From 42fd92f959122f638fa03a8a403de29d39354c30 Mon Sep 17 00:00:00 2001 From: Alex de Mulder Date: Wed, 28 Jan 2015 11:22:03 +0100 Subject: [PATCH 1/7] - Improved images handling so broken images are shown on all references of images that are broken. --- HISTORY.md | 1 + dist/vis.js | 4247 +++++++++++++++++++++-------------------- lib/network/Images.js | 27 +- 3 files changed, 2147 insertions(+), 2128 deletions(-) diff --git a/HISTORY.md b/HISTORY.md index 119939e2..34fb5312 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -7,6 +7,7 @@ http://visjs.org ### Network - Added option bindToWindow (default true) to choose whether the keyboard binds are global or to the network div. +- Improved images handling so broken images are shown on all references of images that are broken. ### DataSet diff --git a/dist/vis.js b/dist/vis.js index 73ff4cfd..e32b2115 100644 --- a/dist/vis.js +++ b/dist/vis.js @@ -5,7 +5,7 @@ * A dynamic, browser-based visualization library. * * @version 3.9.2-SNAPSHOT - * @date 2015-01-23 + * @date 2015-01-28 * * @license * Copyright (C) 2011-2014 Almende B.V, http://almende.com @@ -137,13 +137,13 @@ return /******/ (function(modules) { // webpackBootstrap // Network exports.Network = __webpack_require__(51); exports.network = { - Edge: __webpack_require__(52), + Edge: __webpack_require__(57), Groups: __webpack_require__(54), Images: __webpack_require__(55), - Node: __webpack_require__(53), - Popup: __webpack_require__(56), - dotparser: __webpack_require__(57), - gephiParser: __webpack_require__(58) + Node: __webpack_require__(56), + Popup: __webpack_require__(58), + dotparser: __webpack_require__(52), + gephiParser: __webpack_require__(53) }; // Deprecated since v3.0.0 @@ -22658,13 +22658,13 @@ return /******/ (function(modules) { // webpackBootstrap var hammerUtil = __webpack_require__(22); var DataSet = __webpack_require__(7); var DataView = __webpack_require__(9); - var dotparser = __webpack_require__(57); - var gephiParser = __webpack_require__(58); + var dotparser = __webpack_require__(52); + var gephiParser = __webpack_require__(53); var Groups = __webpack_require__(54); var Images = __webpack_require__(55); - var Node = __webpack_require__(53); - var Edge = __webpack_require__(52); - var Popup = __webpack_require__(56); + var Node = __webpack_require__(56); + var Edge = __webpack_require__(57); + var Popup = __webpack_require__(58); var MixinLoader = __webpack_require__(59); var Activator = __webpack_require__(35); var locales = __webpack_require__(70); @@ -25397,1360 +25397,1076 @@ return /******/ (function(modules) { // webpackBootstrap /* 52 */ /***/ function(module, exports, __webpack_require__) { - var util = __webpack_require__(1); - var Node = __webpack_require__(53); - /** - * @class Edge + * Parse a text source containing data in DOT language into a JSON object. + * The object contains two lists: one with nodes and one with edges. * - * A edge connects two nodes - * @param {Object} properties Object with properties. Must contain - * At least properties from and to. - * Available properties: 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 + * DOT language reference: http://www.graphviz.org/doc/info/lang.html + * + * @param {String} data Text containing a graph in DOT-notation + * @return {Object} graph An object containing two parameters: + * {Object[]} nodes + * {Object[]} edges */ - function Edge (properties, network, networkConstants) { - if (!network) { - throw "No network provided"; - } - var fields = ['edges','physics']; - var constants = util.selectiveBridgeObject(fields,networkConstants); - this.options = constants.edges; - this.physics = constants.physics; - this.options['smoothCurves'] = networkConstants['smoothCurves']; - - - this.network = network; + function parseDOT (data) { + dot = data; + return parseGraph(); + } - // initialize variables - this.id = undefined; - this.fromId = undefined; - this.toId = undefined; - this.title = undefined; - this.widthSelected = this.options.width * this.options.widthSelectionMultiplier; - this.value = undefined; - this.selected = false; - this.hover = false; - this.labelDimensions = {top:0,left:0,width:0,height:0,yLine:0}; // could be cached - this.dirtyLabel = true; + // token types enumeration + var TOKENTYPE = { + NULL : 0, + DELIMITER : 1, + IDENTIFIER: 2, + UNKNOWN : 3 + }; - this.from = null; // a node - this.to = null; // a node - this.via = null; // a temp node + // map with all delimiters + var DELIMITERS = { + '{': true, + '}': true, + '[': true, + ']': true, + ';': true, + '=': true, + ',': true, - this.fromBackup = null; // used to clean up after reconnect - this.toBackup = null;; // used to clean up after reconnect + '->': true, + '--': true + }; - // we use this to be able to reconnect the edge to a cluster if its node is put into a cluster - // by storing the original information we can revert to the original connection when the cluser is opened. - this.originalFromId = []; - this.originalToId = []; + var dot = ''; // current dot file + var index = 0; // current index in dot file + var c = ''; // current token character in expr + var token = ''; // current token + var tokenType = TOKENTYPE.NULL; // type of the token - this.connected = false; + /** + * Get the first character from the dot file. + * The character is stored into the char c. If the end of the dot file is + * reached, the function puts an empty string in c. + */ + function first() { + index = 0; + c = dot.charAt(0); + } - this.widthFixed = false; - this.lengthFixed = false; + /** + * Get the next character from the dot file. + * The character is stored into the char c. If the end of the dot file is + * reached, the function puts an empty string in c. + */ + function next() { + index++; + c = dot.charAt(index); + } - this.setProperties(properties); + /** + * Preview the next character from the dot file. + * @return {String} cNext + */ + function nextPreview() { + return dot.charAt(index + 1); + } - this.controlNodesEnabled = false; - this.controlNodes = {from:null, to:null, positions:{}}; - this.connectedNode = null; + /** + * Test whether given character is alphabetic or numeric + * @param {String} c + * @return {Boolean} isAlphaNumeric + */ + var regexAlphaNumeric = /[a-zA-Z_0-9.:#]/; + function isAlphaNumeric(c) { + return regexAlphaNumeric.test(c); } /** - * Set or overwrite properties for the edge - * @param {Object} properties an object with properties - * @param {Object} constants and object with default, global properties + * Merge all properties of object b into object b + * @param {Object} a + * @param {Object} b + * @return {Object} a */ - Edge.prototype.setProperties = function(properties) { - if (!properties) { - return; + function merge (a, b) { + if (!a) { + a = {}; } - var fields = ['style','fontSize','fontFace','fontColor','fontFill','fontStrokeWidth','fontStrokeColor','width', - 'widthSelectionMultiplier','hoverWidth','arrowScaleFactor','dash','inheritColor','labelAlignment' - ]; - util.selectiveDeepExtend(fields, this.options, properties); - - if (properties.from !== undefined) {this.fromId = properties.from;} - if (properties.to !== undefined) {this.toId = properties.to;} - - if (properties.id !== undefined) {this.id = properties.id;} - if (properties.label !== undefined) {this.label = properties.label; this.dirtyLabel = true;} - - if (properties.title !== undefined) {this.title = properties.title;} - if (properties.value !== undefined) {this.value = properties.value;} - if (properties.length !== undefined) {this.physics.springLength = properties.length;} + if (b) { + for (var name in b) { + if (b.hasOwnProperty(name)) { + a[name] = b[name]; + } + } + } + return a; + } - if (properties.color !== undefined) { - this.options.inheritColor = false; - if (util.isString(properties.color)) { - this.options.color.color = properties.color; - this.options.color.highlight = properties.color; + /** + * Set a value in an object, where the provided parameter name can be a + * path with nested parameters. For example: + * + * var obj = {a: 2}; + * setValue(obj, 'b.c', 3); // obj = {a: 2, b: {c: 3}} + * + * @param {Object} obj + * @param {String} path A parameter name or dot-separated parameter path, + * like "color.highlight.border". + * @param {*} value + */ + function setValue(obj, path, value) { + var keys = path.split('.'); + var o = obj; + while (keys.length) { + var key = keys.shift(); + if (keys.length) { + // this isn't the end point + if (!o[key]) { + o[key] = {}; + } + o = o[key]; } else { - if (properties.color.color !== undefined) {this.options.color.color = properties.color.color;} - if (properties.color.highlight !== undefined) {this.options.color.highlight = properties.color.highlight;} - if (properties.color.hover !== undefined) {this.options.color.hover = properties.color.hover;} + // this is the end point + o[key] = value; } } + } - // A node is connected when it has a from and to node. - this.connect(); - - this.widthFixed = this.widthFixed || (properties.width !== undefined); - this.lengthFixed = this.lengthFixed || (properties.length !== undefined); + /** + * Add a node to a graph object. If there is already a node with + * the same id, their attributes will be merged. + * @param {Object} graph + * @param {Object} node + */ + function addNode(graph, node) { + var i, len; + var current = null; - this.widthSelected = this.options.width* this.options.widthSelectionMultiplier; + // find root graph (in case of subgraph) + var graphs = [graph]; // list with all graphs from current graph to root graph + var root = graph; + while (root.parent) { + graphs.push(root.parent); + root = root.parent; + } - // set draw method based on style - switch (this.options.style) { - case 'line': this.draw = this._drawLine; break; - case 'arrow': this.draw = this._drawArrow; break; - case 'arrow-center': this.draw = this._drawArrowCenter; break; - case 'dash-line': this.draw = this._drawDashLine; break; - default: this.draw = this._drawLine; break; + // find existing node (at root level) by its id + if (root.nodes) { + for (i = 0, len = root.nodes.length; i < len; i++) { + if (node.id === root.nodes[i].id) { + current = root.nodes[i]; + break; + } + } } - - }; - /** - * Connect an edge to its nodes - */ - Edge.prototype.connect = function () { - this.disconnect(); + if (!current) { + // this is a new node + current = { + id: node.id + }; + if (graph.node) { + // clone default attributes + current.attr = merge(current.attr, graph.node); + } + } - this.from = this.network.nodes[this.fromId] || null; - this.to = this.network.nodes[this.toId] || null; - this.connected = (this.from && this.to); + // add node to this (sub)graph and all its parent graphs + for (i = graphs.length - 1; i >= 0; i--) { + var g = graphs[i]; - if (this.connected) { - this.from.attachEdge(this); - this.to.attachEdge(this); - } - else { - if (this.from) { - this.from.detachEdge(this); + if (!g.nodes) { + g.nodes = []; } - if (this.to) { - this.to.detachEdge(this); + if (g.nodes.indexOf(current) == -1) { + g.nodes.push(current); } } - }; - /** - * Disconnect an edge from its nodes - */ - Edge.prototype.disconnect = function () { - if (this.from) { - this.from.detachEdge(this); - this.from = null; - } - if (this.to) { - this.to.detachEdge(this); - this.to = null; + // merge attributes + if (node.attr) { + current.attr = merge(current.attr, node.attr); } - - this.connected = false; - }; + } /** - * get the title of this edge. - * @return {string} title The title of the edge, or undefined when no title - * has been set. + * Add an edge to a graph object + * @param {Object} graph + * @param {Object} edge */ - Edge.prototype.getTitle = function() { - return typeof this.title === "function" ? this.title() : this.title; - }; - + function addEdge(graph, edge) { + if (!graph.edges) { + graph.edges = []; + } + graph.edges.push(edge); + if (graph.edge) { + var attr = merge({}, graph.edge); // clone default attributes + edge.attr = merge(attr, edge.attr); // merge attributes + } + } /** - * Retrieve the value of the edge. Can be undefined - * @return {Number} value + * Create an edge to a graph object + * @param {Object} graph + * @param {String | Number | Object} from + * @param {String | Number | Object} to + * @param {String} type + * @param {Object | null} attr + * @return {Object} edge */ - Edge.prototype.getValue = function() { - return this.value; - }; + function createEdge(graph, from, to, type, attr) { + var edge = { + from: from, + to: to, + type: type + }; - /** - * Adjust the value range of the edge. The edge will adjust it's width - * based on its value. - * @param {Number} min - * @param {Number} max - */ - Edge.prototype.setValueRange = function(min, max) { - if (!this.widthFixed && this.value !== undefined) { - var scale = (this.options.widthMax - this.options.widthMin) / (max - min); - this.options.width= (this.value - min) * scale + this.options.widthMin; - this.widthSelected = this.options.width* this.options.widthSelectionMultiplier; + if (graph.edge) { + edge.attr = merge({}, graph.edge); // clone default attributes } - }; + edge.attr = merge(edge.attr || {}, attr); // merge attributes - /** - * 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 - */ - Edge.prototype.draw = function(ctx) { - throw "Method draw not initialized in edge"; - }; + return edge; + } /** - * 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 + * Get next token in the current dot file. + * The token and token type are available as token and tokenType */ - Edge.prototype.isOverlappingWith = function(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._getDistanceToEdge(xFrom, yFrom, xTo, yTo, xObj, yObj); - - return (dist < distMax); - } - else { - return false - } - }; + function getToken() { + tokenType = TOKENTYPE.NULL; + token = ''; - Edge.prototype._getColor = function() { - var colorObj = this.options.color; - if (this.options.inheritColor == "to") { - colorObj = { - highlight: this.to.options.color.highlight.border, - hover: this.to.options.color.hover.border, - color: this.to.options.color.border - }; - } - else if (this.options.inheritColor == "from" || this.options.inheritColor == true) { - colorObj = { - highlight: this.from.options.color.highlight.border, - hover: this.from.options.color.hover.border, - color: this.from.options.color.border - }; + // skip over whitespaces + while (c == ' ' || c == '\t' || c == '\n' || c == '\r') { // space, tab, enter + next(); } - if (this.selected == true) {return colorObj.highlight;} - else if (this.hover == true) {return colorObj.hover;} - else {return colorObj.color;} - }; - - - /** - * Redraw a edge as a line - * Draw this edge in the given canvas - * The 2d context of a HTML canvas can be retrieved by canvas.getContext("2d"); - * @param {CanvasRenderingContext2D} ctx - * @private - */ - Edge.prototype._drawLine = function(ctx) { - // set style - ctx.strokeStyle = this._getColor(); - ctx.lineWidth = this._getLineWidth(); - - if (this.from != this.to) { - // draw line - var via = this._line(ctx); + do { + var isComment = false; - // draw label - var point; - if (this.label) { - if (this.options.smoothCurves.enabled == true && via != null) { - var midpointX = 0.5*(0.5*(this.from.x + via.x) + 0.5*(this.to.x + via.x)); - var midpointY = 0.5*(0.5*(this.from.y + via.y) + 0.5*(this.to.y + via.y)); - point = {x:midpointX, y:midpointY}; + // skip comment + if (c == '#') { + // find the previous non-space character + var i = index - 1; + while (dot.charAt(i) == ' ' || dot.charAt(i) == '\t') { + i--; } - else { - point = this._pointOnLine(0.5); + if (dot.charAt(i) == '\n' || dot.charAt(i) == '') { + // the # is at the start of a line, this is indeed a line comment + while (c != '' && c != '\n') { + next(); + } + isComment = true; } - this._label(ctx, this.label, point.x, point.y); } - } - else { - var x, y; - var radius = this.physics.springLength / 4; - var node = this.from; - if (!node.width) { - node.resize(ctx); + if (c == '/' && nextPreview() == '/') { + // skip line comment + while (c != '' && c != '\n') { + next(); + } + isComment = true; } - if (node.width > node.height) { - x = node.x + node.width / 2; - y = node.y - radius; + if (c == '/' && nextPreview() == '*') { + // skip block comment + while (c != '') { + if (c == '*' && nextPreview() == '/') { + // end of block comment found. skip these last two characters + next(); + next(); + break; + } + else { + next(); + } + } + isComment = true; } - else { - x = node.x + radius; - y = node.y - node.height / 2; + + // skip over whitespaces + while (c == ' ' || c == '\t' || c == '\n' || c == '\r') { // space, tab, enter + next(); } - this._circle(ctx, x, y, radius); - point = this._pointOnCircle(x, y, radius, 0.5); - this._label(ctx, this.label, point.x, point.y); } - }; + while (isComment); - /** - * Get the line width of the edge. Depends on width and whether one of the - * connected nodes is selected. - * @return {Number} width - * @private - */ - Edge.prototype._getLineWidth = function() { - if (this.selected == true) { - return Math.max(Math.min(this.widthSelected, this.options.widthMax), 0.3*this.networkScaleInv); + // check for end of dot file + if (c == '') { + // token is still empty + tokenType = TOKENTYPE.DELIMITER; + return; } - else { - if (this.hover == true) { - return Math.max(Math.min(this.options.hoverWidth, this.options.widthMax), 0.3*this.networkScaleInv); - } - else { - return Math.max(this.options.width, 0.3*this.networkScaleInv); - } + + // check for delimiters consisting of 2 characters + var c2 = c + nextPreview(); + if (DELIMITERS[c2]) { + tokenType = TOKENTYPE.DELIMITER; + token = c2; + next(); + next(); + return; } - }; - Edge.prototype._getViaCoordinates = function () { - if (this.options.smoothCurves.dynamic == true && this.options.smoothCurves.enabled == true ) { - return this.via; - } - else if (this.options.smoothCurves.enabled == false) { - return {x:0,y:0}; + // check for delimiters consisting of 1 character + if (DELIMITERS[c]) { + tokenType = TOKENTYPE.DELIMITER; + token = c; + next(); + return; } - else { - var xVia = null; - var yVia = null; - var factor = this.options.smoothCurves.roundness; - var type = this.options.smoothCurves.type; - var dx = Math.abs(this.from.x - this.to.x); - var dy = Math.abs(this.from.y - this.to.y); - if (type == 'discrete' || type == 'diagonalCross') { - if (Math.abs(this.from.x - this.to.x) < Math.abs(this.from.y - this.to.y)) { - if (this.from.y > this.to.y) { - if (this.from.x < this.to.x) { - xVia = this.from.x + factor * dy; - yVia = this.from.y - factor * dy; - } - else if (this.from.x > this.to.x) { - xVia = this.from.x - factor * dy; - yVia = this.from.y - factor * dy; - } - } - else if (this.from.y < this.to.y) { - if (this.from.x < this.to.x) { - xVia = this.from.x + factor * dy; - yVia = this.from.y + factor * dy; - } - else if (this.from.x > this.to.x) { - xVia = this.from.x - factor * dy; - yVia = this.from.y + factor * dy; - } - } - if (type == "discrete") { - xVia = dx < factor * dy ? this.from.x : xVia; - } - } - else if (Math.abs(this.from.x - this.to.x) > Math.abs(this.from.y - this.to.y)) { - if (this.from.y > this.to.y) { - if (this.from.x < this.to.x) { - xVia = this.from.x + factor * dx; - yVia = this.from.y - factor * dx; - } - else if (this.from.x > this.to.x) { - xVia = this.from.x - factor * dx; - yVia = this.from.y - factor * dx; - } - } - else if (this.from.y < this.to.y) { - if (this.from.x < this.to.x) { - xVia = this.from.x + factor * dx; - yVia = this.from.y + factor * dx; - } - else if (this.from.x > this.to.x) { - xVia = this.from.x - factor * dx; - yVia = this.from.y + factor * dx; - } - } - if (type == "discrete") { - yVia = dy < factor * dx ? this.from.y : yVia; - } - } - } - else if (type == "straightCross") { - if (Math.abs(this.from.x - this.to.x) < Math.abs(this.from.y - this.to.y)) { // up - down - xVia = this.from.x; - if (this.from.y < this.to.y) { - yVia = this.to.y - (1 - factor) * dy; - } - else { - yVia = this.to.y + (1 - factor) * dy; - } - } - else if (Math.abs(this.from.x - this.to.x) > Math.abs(this.from.y - this.to.y)) { // left - right - if (this.from.x < this.to.x) { - xVia = this.to.x - (1 - factor) * dx; - } - else { - xVia = this.to.x + (1 - factor) * dx; - } - yVia = this.from.y; - } + // check for an identifier (number or string) + // TODO: more precise parsing of numbers/strings (and the port separator ':') + if (isAlphaNumeric(c) || c == '-') { + token += c; + next(); + + while (isAlphaNumeric(c)) { + token += c; + next(); } - else if (type == 'horizontal') { - if (this.from.x < this.to.x) { - xVia = this.to.x - (1 - factor) * dx; - } - else { - xVia = this.to.x + (1 - factor) * dx; - } - yVia = this.from.y; + if (token == 'false') { + token = false; // convert to boolean } - else if (type == 'vertical') { - xVia = this.from.x; - if (this.from.y < this.to.y) { - yVia = this.to.y - (1 - factor) * dy; - } - else { - yVia = this.to.y + (1 - factor) * dy; - } + else if (token == 'true') { + token = true; // convert to boolean } - else { // continuous - if (Math.abs(this.from.x - this.to.x) < Math.abs(this.from.y - this.to.y)) { - if (this.from.y > this.to.y) { - if (this.from.x < this.to.x) { - // console.log(1) - xVia = this.from.x + factor * dy; - yVia = this.from.y - factor * dy; - xVia = this.to.x < xVia ? this.to.x : xVia; - } - else if (this.from.x > this.to.x) { - // console.log(2) - xVia = this.from.x - factor * dy; - yVia = this.from.y - factor * dy; - xVia = this.to.x > xVia ? this.to.x : xVia; - } - } - else if (this.from.y < this.to.y) { - if (this.from.x < this.to.x) { - // console.log(3) - xVia = this.from.x + factor * dy; - yVia = this.from.y + factor * dy; - xVia = this.to.x < xVia ? this.to.x : xVia; - } - else if (this.from.x > this.to.x) { - // console.log(4, this.from.x, this.to.x) - xVia = this.from.x - factor * dy; - yVia = this.from.y + factor * dy; - xVia = this.to.x > xVia ? this.to.x : xVia; - } - } - } - else if (Math.abs(this.from.x - this.to.x) > Math.abs(this.from.y - this.to.y)) { - if (this.from.y > this.to.y) { - if (this.from.x < this.to.x) { - // console.log(5) - xVia = this.from.x + factor * dx; - yVia = this.from.y - factor * dx; - yVia = this.to.y > yVia ? this.to.y : yVia; - } - else if (this.from.x > this.to.x) { - // console.log(6) - xVia = this.from.x - factor * dx; - yVia = this.from.y - factor * dx; - yVia = this.to.y > yVia ? this.to.y : yVia; - } - } - else if (this.from.y < this.to.y) { - if (this.from.x < this.to.x) { - // console.log(7) - xVia = this.from.x + factor * dx; - yVia = this.from.y + factor * dx; - yVia = this.to.y < yVia ? this.to.y : yVia; - } - else if (this.from.x > this.to.x) { - // console.log(8) - xVia = this.from.x - factor * dx; - yVia = this.from.y + factor * dx; - yVia = this.to.y < yVia ? this.to.y : yVia; - } - } - } + else if (!isNaN(Number(token))) { + token = Number(token); // convert to number } - - - return {x: xVia, y: yVia}; + tokenType = TOKENTYPE.IDENTIFIER; + return; } - }; - /** - * Draw a line between two nodes - * @param {CanvasRenderingContext2D} ctx - * @private - */ - Edge.prototype._line = function (ctx) { - // draw a straight line - ctx.beginPath(); - ctx.moveTo(this.from.x, this.from.y); - if (this.options.smoothCurves.enabled == true) { - if (this.options.smoothCurves.dynamic == false) { - var via = this._getViaCoordinates(); - if (via.x == null) { - ctx.lineTo(this.to.x, this.to.y); - ctx.stroke(); - return null; - } - else { - // this.via.x = via.x; - // this.via.y = via.y; - ctx.quadraticCurveTo(via.x,via.y,this.to.x, this.to.y); - ctx.stroke(); - return via; + // check for a string enclosed by double quotes + if (c == '"') { + next(); + while (c != '' && (c != '"' || (c == '"' && nextPreview() == '"'))) { + token += c; + if (c == '"') { // skip the escape character + next(); } + next(); } - else { - ctx.quadraticCurveTo(this.via.x,this.via.y,this.to.x, this.to.y); - ctx.stroke(); - return this.via; + if (c != '"') { + throw newSyntaxError('End of string " expected'); } + next(); + tokenType = TOKENTYPE.IDENTIFIER; + return; } - else { - ctx.lineTo(this.to.x, this.to.y); - ctx.stroke(); - return null; + + // something unknown is found, wrong characters, a syntax error + tokenType = TOKENTYPE.UNKNOWN; + while (c != '') { + token += c; + next(); } - }; + throw new SyntaxError('Syntax error in part "' + chop(token, 30) + '"'); + } /** - * Draw a line from a node to itself, a circle - * @param {CanvasRenderingContext2D} ctx - * @param {Number} x - * @param {Number} y - * @param {Number} radius - * @private + * Parse a graph. + * @returns {Object} graph */ - Edge.prototype._circle = function (ctx, x, y, radius) { - // draw a circle - ctx.beginPath(); - ctx.arc(x, y, radius, 0, 2 * Math.PI, false); - ctx.stroke(); - }; + function parseGraph() { + var graph = {}; - /** - * Draw label with white background and with the middle at (x, y) - * @param {CanvasRenderingContext2D} ctx - * @param {String} text - * @param {Number} x - * @param {Number} y - * @private - */ - Edge.prototype._label = function (ctx, text, x, y) { - if (text) { - ctx.font = ((this.from.selected || this.to.selected) ? "bold " : "") + - this.options.fontSize + "px " + this.options.fontFace; - var yLine; + first(); + getToken(); - if (this.dirtyLabel == true) { - var lines = String(text).split('\n'); - var lineCount = lines.length; - var fontSize = Number(this.options.fontSize); - yLine = y + (1 - lineCount) / 2 * fontSize; + // optional strict keyword + if (token == 'strict') { + graph.strict = true; + getToken(); + } - var 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; - } - var height = this.options.fontSize * lineCount; - var left = x - width / 2; - var top = y - height / 2; + // graph or digraph keyword + if (token == 'graph' || token == 'digraph') { + graph.type = token; + getToken(); + } - // cache - this.labelDimensions = {top:top,left:left,width:width,height:height,yLine:yLine}; - } + // optional graph id + if (tokenType == TOKENTYPE.IDENTIFIER) { + graph.id = token; + getToken(); + } - var yLine = this.labelDimensions.yLine; - - ctx.save(); - - if (this.options.labelAlignment != "horizontal"){ - ctx.translate(x, yLine); - this._rotateForLabelAlignment(ctx); - x = 0; - yLine = 0; - } + // open angle bracket + if (token != '{') { + throw newSyntaxError('Angle bracket { expected'); + } + getToken(); - - this._drawLabelRect(ctx); - this._drawLabelText(ctx,x,yLine, lines, lineCount, fontSize); - - ctx.restore(); + // statements + parseStatements(graph); + + // close angle bracket + if (token != '}') { + throw newSyntaxError('Angle bracket } expected'); } - }; + getToken(); - /** - * Rotates the canvas so the text is most readable - * @param {CanvasRenderingContext2D} ctx - * @private - */ - Edge.prototype._rotateForLabelAlignment = function(ctx) { - var dy = this.from.y - this.to.y; - var dx = this.from.x - this.to.x; - var angleInDegrees = Math.atan2(dy, dx); + // end of file + if (token !== '') { + throw newSyntaxError('End of file expected'); + } + getToken(); - // rotate so label it is readable - if((angleInDegrees < -1 && dx < 0) || (angleInDegrees > 0 && dx < 0)){ - angleInDegrees = angleInDegrees + Math.PI; - } - - ctx.rotate(angleInDegrees); - }; + // remove temporary default properties + delete graph.node; + delete graph.edge; + delete graph.graph; + + return graph; + } /** - * Draws the label rectangle - * @param {CanvasRenderingContext2D} ctx - * @param {String} labelAlignment - * @private + * Parse a list with statements. + * @param {Object} graph */ - Edge.prototype._drawLabelRect = function(ctx) { - if (this.options.fontFill !== undefined && this.options.fontFill !== null && this.options.fontFill !== "none") { - ctx.fillStyle = this.options.fontFill; - - var lineMargin = 2; - - if (this.options.labelAlignment == 'line-center') { - ctx.fillRect(-this.labelDimensions.width * 0.5, -this.labelDimensions.height * 0.5, this.labelDimensions.width, this.labelDimensions.height); - } - else if (this.options.labelAlignment == 'line-above') { - ctx.fillRect(-this.labelDimensions.width * 0.5, -(this.labelDimensions.height + lineMargin), this.labelDimensions.width, this.labelDimensions.height); - } - else if (this.options.labelAlignment == 'line-below') { - ctx.fillRect(-this.labelDimensions.width * 0.5, lineMargin, this.labelDimensions.width, this.labelDimensions.height); - } - else { - ctx.fillRect(this.labelDimensions.left, this.labelDimensions.top, this.labelDimensions.width, this.labelDimensions.height); + function parseStatements (graph) { + while (token !== '' && token != '}') { + parseStatement(graph); + if (token == ';') { + getToken(); } } - }; + } /** - * Draws the label text - * @param {CanvasRenderingContext2D} ctx - * @param {Number} x - * @param {Number} yLine - * @param {Array} lines - * @param {Number} lineCount - * @param {Number} fontSize - * @private + * Parse a single statement. Can be a an attribute statement, node + * statement, a series of node statements and edge statements, or a + * parameter. + * @param {Object} graph */ - Edge.prototype._drawLabelText = function(ctx, x, yLine, lines, lineCount, fontSize) { - // draw text - ctx.fillStyle = this.options.fontColor || "black"; - ctx.textAlign = "center"; + function parseStatement(graph) { + // parse subgraph + var subgraph = parseSubgraph(graph); + if (subgraph) { + // edge statements + parseEdge(graph, subgraph); - // check for label alignment - if (this.options.labelAlignment != 'horizontal') { - var lineMargin = 2; - if (this.options.labelAlignment == 'line-above') { - ctx.textBaseline = "alphabetic"; - yLine -= 2 * lineMargin; // distance from edge, required because we use alphabetic. Alphabetic has less difference between browsers - } - else if (this.options.labelAlignment == 'line-below') { - 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 = "middle"; + return; } - // check for strokeWidth - if (this.options.fontStrokeWidth > 0){ - ctx.lineWidth = this.options.fontStrokeWidth; - ctx.strokeStyle = this.options.fontStrokeColor; - ctx.lineJoin = 'round'; + // parse an attribute statement + var attr = parseAttributeStatement(graph); + if (attr) { + return; } - for (var i = 0; i < lineCount; i++) { - if(this.options.fontStrokeWidth > 0){ - ctx.strokeText(lines[i], x, yLine); - } - ctx.fillText(lines[i], x, yLine); - yLine += fontSize; - } - }; - - /** - * Redraw a edge as a dashed line - * Draw this edge in the given canvas - * @author David Jordan - * @date 2012-08-08 - * The 2d context of a HTML canvas can be retrieved by canvas.getContext("2d"); - * @param {CanvasRenderingContext2D} ctx - * @private - */ - Edge.prototype._drawDashLine = function(ctx) { - // set style - ctx.strokeStyle = this._getColor(); - ctx.lineWidth = this._getLineWidth(); - - var via = null; - // only firefox and chrome support this method, else we use the legacy one. - if (ctx.setLineDash !== undefined) { - ctx.save(); - // configure the dash pattern - var pattern = [0]; - if (this.options.dash.length !== undefined && this.options.dash.gap !== undefined) { - pattern = [this.options.dash.length,this.options.dash.gap]; - } - else { - pattern = [5,5]; - } - - // set dash settings for chrome or firefox - ctx.setLineDash(pattern); - ctx.lineDashOffset = 0; - - // draw the line - via = this._line(ctx); - // restore the dash settings. - ctx.setLineDash([0]); - ctx.lineDashOffset = 0; - ctx.restore(); - } - else { // unsupporting smooth lines - // draw dashed line - ctx.beginPath(); - ctx.lineCap = 'round'; - if (this.options.dash.altLength !== undefined) //If an alt dash value has been set add to the array this value - { - ctx.dashedLine(this.from.x,this.from.y,this.to.x,this.to.y, - [this.options.dash.length,this.options.dash.gap,this.options.dash.altLength,this.options.dash.gap]); - } - else if (this.options.dash.length !== undefined && this.options.dash.gap !== undefined) //If a dash and gap value has been set add to the array this value - { - ctx.dashedLine(this.from.x,this.from.y,this.to.x,this.to.y, - [this.options.dash.length,this.options.dash.gap]); - } - else //If all else fails draw a line - { - ctx.moveTo(this.from.x, this.from.y); - ctx.lineTo(this.to.x, this.to.y); - } - ctx.stroke(); + // parse node + if (tokenType != TOKENTYPE.IDENTIFIER) { + throw newSyntaxError('Identifier expected'); } + var id = token; // id can be a string or a number + getToken(); - // draw label - if (this.label) { - var point; - if (this.options.smoothCurves.enabled == true && via != null) { - var midpointX = 0.5*(0.5*(this.from.x + via.x) + 0.5*(this.to.x + via.x)); - var midpointY = 0.5*(0.5*(this.from.y + via.y) + 0.5*(this.to.y + via.y)); - point = {x:midpointX, y:midpointY}; - } - else { - point = this._pointOnLine(0.5); + if (token == '=') { + // id statement + getToken(); + if (tokenType != TOKENTYPE.IDENTIFIER) { + throw newSyntaxError('Identifier expected'); } - this._label(ctx, this.label, point.x, point.y); + graph[id] = token; + getToken(); + // TODO: implement comma separated list with "a_list: ID=ID [','] [a_list] " } - }; - - /** - * Get a point on a line - * @param {Number} percentage. Value between 0 (line start) and 1 (line end) - * @return {Object} point - * @private - */ - Edge.prototype._pointOnLine = function (percentage) { - return { - x: (1 - percentage) * this.from.x + percentage * this.to.x, - y: (1 - percentage) * this.from.y + percentage * this.to.y + else { + parseNodeStatement(graph, id); } - }; + } /** - * 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 + * Parse a subgraph + * @param {Object} graph parent graph object + * @return {Object | null} subgraph */ - Edge.prototype._pointOnCircle = function (x, y, radius, percentage) { - var angle = (percentage - 3/8) * 2 * Math.PI; - return { - x: x + radius * Math.cos(angle), - y: y - radius * Math.sin(angle) - } - }; + function parseSubgraph (graph) { + var subgraph = null; - /** - * Redraw a edge as a line with an arrow halfway the line - * Draw this edge in the given canvas - * The 2d context of a HTML canvas can be retrieved by canvas.getContext("2d"); - * @param {CanvasRenderingContext2D} ctx - * @private - */ - Edge.prototype._drawArrowCenter = function(ctx) { - var point; - // set style - ctx.strokeStyle = this._getColor(); - ctx.fillStyle = ctx.strokeStyle; - ctx.lineWidth = this._getLineWidth(); + // optional subgraph keyword + if (token == 'subgraph') { + subgraph = {}; + subgraph.type = 'subgraph'; + getToken(); - if (this.from != this.to) { - // draw line - var via = this._line(ctx); + // optional graph id + if (tokenType == TOKENTYPE.IDENTIFIER) { + subgraph.id = token; + getToken(); + } + } - var angle = Math.atan2((this.to.y - this.from.y), (this.to.x - this.from.x)); - var length = (10 + 5 * this.options.width) * this.options.arrowScaleFactor; - // draw an arrow halfway the line - if (this.options.smoothCurves.enabled == true && via != null) { - var midpointX = 0.5*(0.5*(this.from.x + via.x) + 0.5*(this.to.x + via.x)); - var midpointY = 0.5*(0.5*(this.from.y + via.y) + 0.5*(this.to.y + via.y)); - point = {x:midpointX, y:midpointY}; + // open angle bracket + if (token == '{') { + getToken(); + + if (!subgraph) { + subgraph = {}; } - else { - point = this._pointOnLine(0.5); + subgraph.parent = graph; + subgraph.node = graph.node; + subgraph.edge = graph.edge; + subgraph.graph = graph.graph; + + // statements + parseStatements(subgraph); + + // close angle bracket + if (token != '}') { + throw newSyntaxError('Angle bracket } expected'); } + getToken(); - ctx.arrow(point.x, point.y, angle, length); - ctx.fill(); - ctx.stroke(); + // remove temporary default properties + delete subgraph.node; + delete subgraph.edge; + delete subgraph.graph; + delete subgraph.parent; - // draw label - if (this.label) { - this._label(ctx, this.label, point.x, point.y); + // register at the parent graph + if (!graph.subgraphs) { + graph.subgraphs = []; } + graph.subgraphs.push(subgraph); } - else { - // draw circle - var x, y; - var radius = 0.25 * Math.max(100,this.physics.springLength); - var node = this.from; - if (!node.width) { - node.resize(ctx); - } - if (node.width > node.height) { - x = node.x + node.width * 0.5; - y = node.y - radius; - } - else { - x = node.x + radius; - y = node.y - node.height * 0.5; - } - this._circle(ctx, x, y, radius); - // draw all arrows - var angle = 0.2 * Math.PI; - var length = (10 + 5 * this.options.width) * this.options.arrowScaleFactor; - point = this._pointOnCircle(x, y, radius, 0.5); - ctx.arrow(point.x, point.y, angle, length); - ctx.fill(); - ctx.stroke(); + return subgraph; + } - // draw label - if (this.label) { - point = this._pointOnCircle(x, y, radius, 0.5); - this._label(ctx, this.label, point.x, point.y); - } + /** + * parse an attribute statement like "node [shape=circle fontSize=16]". + * Available keywords are 'node', 'edge', 'graph'. + * The previous list with default attributes will be replaced + * @param {Object} graph + * @returns {String | null} keyword Returns the name of the parsed attribute + * (node, edge, graph), or null if nothing + * is parsed. + */ + function parseAttributeStatement (graph) { + // attribute statements + if (token == 'node') { + getToken(); + + // node attributes + graph.node = parseAttributeList(); + return 'node'; } - }; + else if (token == 'edge') { + getToken(); - Edge.prototype._pointOnBezier = function(t) { - var via = this._getViaCoordinates(); + // edge attributes + graph.edge = parseAttributeList(); + return 'edge'; + } + else if (token == 'graph') { + getToken(); - var x = Math.pow(1-t,2)*this.from.x + (2*t*(1 - t))*via.x + Math.pow(t,2)*this.to.x; - var y = Math.pow(1-t,2)*this.from.y + (2*t*(1 - t))*via.y + Math.pow(t,2)*this.to.y; + // graph attributes + graph.graph = parseAttributeList(); + return 'graph'; + } - return {x:x,y:y}; + return null; } /** - * This function uses binary search to look for the point where the bezier curve crosses the border of the node. - * - * @param from - * @param ctx - * @returns {*} - * @private + * parse a node statement + * @param {Object} graph + * @param {String | Number} id */ - Edge.prototype._findBorderPosition = function(from,ctx) { - var maxIterations = 10; - var iteration = 0; - var low = 0; - var high = 1; - var pos,angle,distanceToBorder, distanceToNodes, difference; - var threshold = 0.2; - var node = this.to; - if (from == true) { - node = this.from; + function parseNodeStatement(graph, id) { + // node statement + var node = { + id: id + }; + var attr = parseAttributeList(); + if (attr) { + node.attr = attr; } + addNode(graph, node); - while (low <= high && iteration < maxIterations) { - var middle = (low + high) * 0.5; + // edge statements + parseEdge(graph, id); + } - pos = this._pointOnBezier(middle); - angle = Math.atan2((node.y - pos.y), (node.x - pos.x)); - distanceToBorder = node.distanceToBorder(ctx,angle); - distanceToNodes = Math.sqrt(Math.pow(pos.x-node.x,2) + Math.pow(pos.y-node.y,2)); - difference = distanceToBorder - distanceToNodes; - if (Math.abs(difference) < threshold) { - break; // found - } - else if (difference < 0) { // distance to nodes is larger than distance to border --> t needs to be bigger if we're looking at the to node. - if (from == false) { - low = middle; - } - else { - high = middle; - } + /** + * Parse an edge or a series of edges + * @param {Object} graph + * @param {String | Number} from Id of the from node + */ + function parseEdge(graph, from) { + while (token == '->' || token == '--') { + var to; + var type = token; + getToken(); + + var subgraph = parseSubgraph(graph); + if (subgraph) { + to = subgraph; } else { - if (from == false) { - high = middle; - } - else { - low = middle; + if (tokenType != TOKENTYPE.IDENTIFIER) { + throw newSyntaxError('Identifier or subgraph expected'); } + to = token; + addNode(graph, { + id: to + }); + getToken(); } - iteration++; - } - pos.t = middle; + // parse edge attributes + var attr = parseAttributeList(); - return pos; - }; + // create edge + var edge = createEdge(graph, from, to, type, attr); + addEdge(graph, edge); + + from = to; + } + } /** - * Redraw a edge as a line with an arrow - * Draw this edge in the given canvas - * The 2d context of a HTML canvas can be retrieved by canvas.getContext("2d"); - * @param {CanvasRenderingContext2D} ctx - * @private + * Parse a set with attributes, + * for example [label="1.000", shape=solid] + * @return {Object | null} attr */ - Edge.prototype._drawArrow = function(ctx) { - // set style - ctx.strokeStyle = this._getColor(); - ctx.fillStyle = ctx.strokeStyle; - ctx.lineWidth = this._getLineWidth(); + function parseAttributeList() { + var attr = null; - // set vars - var angle, length, arrowPos; + while (token == '[') { + getToken(); + attr = {}; + while (token !== '' && token != ']') { + if (tokenType != TOKENTYPE.IDENTIFIER) { + throw newSyntaxError('Attribute name expected'); + } + var name = token; - // if not connected to itself - if (this.from != this.to) { - // draw line - this._line(ctx); + getToken(); + if (token != '=') { + throw newSyntaxError('Equal sign = expected'); + } + getToken(); - // draw arrow head - if (this.options.smoothCurves.enabled == true) { - var via = this._getViaCoordinates(); - arrowPos = this._findBorderPosition(false, ctx); - var guidePos = this._pointOnBezier(Math.max(0.0, arrowPos.t - 0.1)) - angle = Math.atan2((arrowPos.y - guidePos.y), (arrowPos.x - guidePos.x)); + if (tokenType != TOKENTYPE.IDENTIFIER) { + throw newSyntaxError('Attribute value expected'); + } + var value = token; + setValue(attr, name, value); // name can be a path + + getToken(); + if (token ==',') { + getToken(); + } } - else { - angle = Math.atan2((this.to.y - this.from.y), (this.to.x - this.from.x)); - var dx = (this.to.x - this.from.x); - var dy = (this.to.y - this.from.y); - var edgeSegmentLength = Math.sqrt(dx * dx + dy * dy); - var toBorderDist = this.to.distanceToBorder(ctx, angle); - var toBorderPoint = (edgeSegmentLength - toBorderDist) / edgeSegmentLength; - arrowPos = {}; - arrowPos.x = (1 - toBorderPoint) * this.from.x + toBorderPoint * this.to.x; - arrowPos.y = (1 - toBorderPoint) * this.from.y + toBorderPoint * this.to.y; + if (token != ']') { + throw newSyntaxError('Bracket ] expected'); } + getToken(); + } - // draw arrow at the end of the line - length = (10 + 5 * this.options.width) * this.options.arrowScaleFactor; - ctx.arrow(arrowPos.x,arrowPos.y, angle, length); - ctx.fill(); - ctx.stroke(); + return attr; + } - // draw label - if (this.label) { - var point; - if (this.options.smoothCurves.enabled == true && via != null) { - point = this._pointOnBezier(0.5); + /** + * Create a syntax error with extra information on current token and index. + * @param {String} message + * @returns {SyntaxError} err + */ + function newSyntaxError(message) { + return new SyntaxError(message + ', got "' + chop(token, 30) + '" (char ' + index + ')'); + } + + /** + * Chop off text after a maximum length + * @param {String} text + * @param {Number} maxLength + * @returns {String} + */ + function chop (text, maxLength) { + return (text.length <= maxLength) ? text : (text.substr(0, 27) + '...'); + } + + /** + * Execute a function fn for each pair of elements in two arrays + * @param {Array | *} array1 + * @param {Array | *} array2 + * @param {function} fn + */ + function forEach2(array1, array2, fn) { + if (Array.isArray(array1)) { + array1.forEach(function (elem1) { + if (Array.isArray(array2)) { + array2.forEach(function (elem2) { + fn(elem1, elem2); + }); } else { - point = this._pointOnLine(0.5); + fn(elem1, array2); } - this._label(ctx, this.label, point.x, point.y); - } + }); } else { - // draw circle - var node = this.from; - var x, y, arrow; - var radius = 0.25 * Math.max(100,this.physics.springLength); - if (!node.width) { - node.resize(ctx); - } - if (node.width > node.height) { - x = node.x + node.width * 0.5; - y = node.y - radius; - arrow = { - x: x, - y: node.y, - angle: 0.9 * Math.PI - }; + if (Array.isArray(array2)) { + array2.forEach(function (elem2) { + fn(array1, elem2); + }); } else { - x = node.x + radius; - y = node.y - node.height * 0.5; - arrow = { - x: node.x, - y: y, - angle: 0.6 * Math.PI - }; - } - ctx.beginPath(); - // TODO: similarly, for a line without arrows, draw to the border of the nodes instead of the center - ctx.arc(x, y, radius, 0, 2 * Math.PI, false); - ctx.stroke(); - - // draw all arrows - var length = (10 + 5 * this.options.width) * this.options.arrowScaleFactor; - ctx.arrow(arrow.x, arrow.y, arrow.angle, length); - ctx.fill(); - ctx.stroke(); - - // draw label - if (this.label) { - point = this._pointOnCircle(x, y, radius, 0.5); - this._label(ctx, this.label, point.x, point.y); + fn(array1, array2); } } - }; + } /** - * Calculate the distance between a point (x3,y3) and a line segment from - * (x1,y1) to (x2,y2). - * http://stackoverflow.com/questions/849211/shortest-distancae-between-a-point-and-a-line-segment - * @param {number} x1 - * @param {number} y1 - * @param {number} x2 - * @param {number} y2 - * @param {number} x3 - * @param {number} y3 - * @private + * Convert a string containing a graph in DOT language into a map containing + * with nodes and edges in the format of graph. + * @param {String} data Text containing a graph in DOT-notation + * @return {Object} graphData */ - Edge.prototype._getDistanceToEdge = function (x1,y1, x2,y2, x3,y3) { // x3,y3 is the point - var returnValue = 0; - if (this.from != this.to) { - if (this.options.smoothCurves.enabled == true) { - var xVia, yVia; - if (this.options.smoothCurves.enabled == true && this.options.smoothCurves.dynamic == true) { - xVia = this.via.x; - yVia = this.via.y; + function DOTToGraph (data) { + // parse the DOT file + var dotData = parseDOT(data); + var graphData = { + nodes: [], + edges: [], + options: {} + }; + + // copy the nodes + if (dotData.nodes) { + dotData.nodes.forEach(function (dotNode) { + var graphNode = { + id: dotNode.id, + label: String(dotNode.label || dotNode.id) + }; + merge(graphNode, dotNode.attr); + if (graphNode.image) { + graphNode.shape = 'image'; + } + graphData.nodes.push(graphNode); + }); + } + + // copy the edges + if (dotData.edges) { + /** + * Convert an edge in DOT format to an edge with VisGraph format + * @param {Object} dotEdge + * @returns {Object} graphEdge + */ + var convertEdge = function (dotEdge) { + var graphEdge = { + from: dotEdge.from, + to: dotEdge.to + }; + merge(graphEdge, dotEdge.attr); + graphEdge.style = (dotEdge.type == '->') ? 'arrow' : 'line'; + return graphEdge; + } + + dotData.edges.forEach(function (dotEdge) { + var from, to; + if (dotEdge.from instanceof Object) { + from = dotEdge.from.nodes; } else { - var via = this._getViaCoordinates(); - xVia = via.x; - yVia = via.y; + from = { + id: dotEdge.from + } } - var minDistance = 1e9; - var distance; - var i,t,x,y, lastX, lastY; - for (i = 0; i < 10; i++) { - t = 0.1*i; - x = Math.pow(1-t,2)*x1 + (2*t*(1 - t))*xVia + Math.pow(t,2)*x2; - y = Math.pow(1-t,2)*y1 + (2*t*(1 - t))*yVia + Math.pow(t,2)*y2; - if (i > 0) { - distance = this._getDistanceToLine(lastX,lastY,x,y, x3,y3); - minDistance = distance < minDistance ? distance : minDistance; + + if (dotEdge.to instanceof Object) { + to = dotEdge.to.nodes; + } + else { + to = { + id: dotEdge.to } - lastX = x; lastY = y; } - returnValue = minDistance; - } - else { - returnValue = this._getDistanceToLine(x1,y1,x2,y2,x3,y3); + + if (dotEdge.from instanceof Object && dotEdge.from.edges) { + dotEdge.from.edges.forEach(function (subEdge) { + var graphEdge = convertEdge(subEdge); + graphData.edges.push(graphEdge); + }); + } + + forEach2(from, to, function (from, to) { + var subEdge = createEdge(graphData, from.id, to.id, dotEdge.type, dotEdge.attr); + var graphEdge = convertEdge(subEdge); + graphData.edges.push(graphEdge); + }); + + if (dotEdge.to instanceof Object && dotEdge.to.edges) { + dotEdge.to.edges.forEach(function (subEdge) { + var graphEdge = convertEdge(subEdge); + graphData.edges.push(graphEdge); + }); + } + }); + } + + // copy the options + if (dotData.attr) { + graphData.options = dotData.attr; + } + + return graphData; + } + + // exports + exports.parseDOT = parseDOT; + exports.DOTToGraph = DOTToGraph; + + +/***/ }, +/* 53 */ +/***/ function(module, exports, __webpack_require__) { + + + function parseGephi(gephiJSON, options) { + var edges = []; + var nodes = []; + this.options = { + edges: { + inheritColor: true + }, + nodes: { + allowedToMove: false, + parseColor: false } + }; + + if (options !== undefined) { + this.options.nodes['allowedToMove'] = options.allowedToMove | false; + this.options.nodes['parseColor'] = options.parseColor | false; + this.options.edges['inheritColor'] = options.inheritColor | true; } - else { - var x, y, dx, dy; - var radius = 0.25 * this.physics.springLength; - var node = this.from; - if (node.width > node.height) { - x = node.x + 0.5 * node.width; - y = node.y - radius; + + var gEdges = gephiJSON.edges; + var gNodes = gephiJSON.nodes; + for (var i = 0; i < gEdges.length; i++) { + var edge = {}; + var gEdge = gEdges[i]; + edge['id'] = gEdge.id; + edge['from'] = gEdge.source; + edge['to'] = gEdge.target; + edge['attributes'] = gEdge.attributes; + // edge['value'] = gEdge.attributes !== undefined ? gEdge.attributes.Weight : undefined; + // edge['width'] = edge['value'] !== undefined ? undefined : edgegEdge.size; + edge['color'] = gEdge.color; + edge['inheritColor'] = edge['color'] !== undefined ? false : this.options.inheritColor; + edges.push(edge); + } + + for (var i = 0; i < gNodes.length; i++) { + var node = {}; + var gNode = gNodes[i]; + node['id'] = gNode.id; + node['attributes'] = gNode.attributes; + node['x'] = gNode.x; + node['y'] = gNode.y; + node['label'] = gNode.label; + if (this.options.nodes.parseColor == true) { + node['color'] = gNode.color; } else { - x = node.x + radius; - y = node.y - 0.5 * node.height; + node['color'] = gNode.color !== undefined ? {background:gNode.color, border:gNode.color} : undefined; } - dx = x - x3; - dy = y - y3; - returnValue = Math.abs(Math.sqrt(dx*dx + dy*dy) - radius); - } - - if (this.labelDimensions.left < x3 && - this.labelDimensions.left + this.labelDimensions.width > x3 && - this.labelDimensions.top < y3 && - this.labelDimensions.top + this.labelDimensions.height > y3) { - return 0; - } - else { - return returnValue; + node['radius'] = gNode.size; + node['allowedToMoveX'] = this.options.nodes.allowedToMove; + node['allowedToMoveY'] = this.options.nodes.allowedToMove; + nodes.push(node); } - }; - - Edge.prototype._getDistanceToLine = function(x1,y1,x2,y2,x3,y3) { - var px = x2-x1, - py = y2-y1, - something = px*px + py*py, - u = ((x3 - x1) * px + (y3 - y1) * py) / something; - if (u > 1) { - u = 1; - } - else if (u < 0) { - u = 0; - } + return {nodes:nodes, edges:edges}; + } - var x = x1 + u * px, - y = y1 + u * py, - dx = x - x3, - dy = y - y3; + exports.parseGephi = parseGephi; - //# Note: If the actual distance does not matter, - //# if you only want to compare what this function - //# returns to other results of this function, you - //# can just return the squared distance instead - //# (i.e. remove the sqrt) to gain a little performance +/***/ }, +/* 54 */ +/***/ function(module, exports, __webpack_require__) { - return Math.sqrt(dx*dx + dy*dy); - }; + var util = __webpack_require__(1); /** - * This allows the zoom level of the network to influence the rendering - * - * @param scale + * @class Groups + * This class can store groups and properties specific for groups. */ - Edge.prototype.setScale = function(scale) { - this.networkScaleInv = 1.0/scale; - }; - + function Groups() { + this.clear(); + this.defaultIndex = 0; + } - Edge.prototype.select = function() { - this.selected = true; - }; - Edge.prototype.unselect = function() { - this.selected = false; - }; + /** + * default constants for group colors + */ + Groups.DEFAULT = [ + {border: "#2B7CE9", background: "#97C2FC", highlight: {border: "#2B7CE9", background: "#D2E5FF"}, hover: {border: "#2B7CE9", background: "#D2E5FF"}}, // blue + {border: "#FFA500", background: "#FFFF00", highlight: {border: "#FFA500", background: "#FFFFA3"}, hover: {border: "#FFA500", background: "#FFFFA3"}}, // yellow + {border: "#FA0A10", background: "#FB7E81", highlight: {border: "#FA0A10", background: "#FFAFB1"}, hover: {border: "#FA0A10", background: "#FFAFB1"}}, // red + {border: "#41A906", background: "#7BE141", highlight: {border: "#41A906", background: "#A1EC76"}, hover: {border: "#41A906", background: "#A1EC76"}}, // green + {border: "#E129F0", background: "#EB7DF4", highlight: {border: "#E129F0", background: "#F0B3F5"}, hover: {border: "#E129F0", background: "#F0B3F5"}}, // magenta + {border: "#7C29F0", background: "#AD85E4", highlight: {border: "#7C29F0", background: "#D3BDF0"}, hover: {border: "#7C29F0", background: "#D3BDF0"}}, // purple + {border: "#C37F00", background: "#FFA807", highlight: {border: "#C37F00", background: "#FFCA66"}, hover: {border: "#C37F00", background: "#FFCA66"}}, // orange + {border: "#4220FB", background: "#6E6EFD", highlight: {border: "#4220FB", background: "#9B9BFD"}, hover: {border: "#4220FB", background: "#9B9BFD"}}, // darkblue + {border: "#FD5A77", background: "#FFC0CB", highlight: {border: "#FD5A77", background: "#FFD1D9"}, hover: {border: "#FD5A77", background: "#FFD1D9"}}, // pink + {border: "#4AD63A", background: "#C2FABC", highlight: {border: "#4AD63A", background: "#E6FFE3"}, hover: {border: "#4AD63A", background: "#E6FFE3"}} // mint + ]; - Edge.prototype.positionBezierNode = function() { - if (this.via !== null && this.from !== null && this.to !== null) { - 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 !== null) { - this.via.x = 0; - this.via.y = 0; - } - }; /** - * This function draws the control nodes for the manipulator. - * In order to enable this, only set the this.controlNodesEnabled to true. - * @param ctx + * Clear all groups */ - Edge.prototype._drawControlNodes = function(ctx) { - if (this.controlNodesEnabled == true) { - if (this.controlNodes.from === null && this.controlNodes.to === null) { - var nodeIdFrom = "edgeIdFrom:".concat(this.id); - var nodeIdTo = "edgeIdTo:".concat(this.id); - var constants = { - nodes:{group:'', radius:7, borderWidth:2, borderWidthSelected: 2}, - physics:{damping:0}, - clustering: {maxNodeSizeIncrements: 0 ,nodeScaling: {width:0, height: 0, radius:0}} - }; - this.controlNodes.from = new Node( - {id:nodeIdFrom, - shape:'dot', - color:{background:'#ff0000', border:'#3c3c3c', highlight: {background:'#07f968'}} - },{},{},constants); - this.controlNodes.to = new Node( - {id:nodeIdTo, - shape:'dot', - color:{background:'#ff0000', border:'#3c3c3c', highlight: {background:'#07f968'}} - },{},{},constants); - } - - this.controlNodes.positions = {}; - if (this.controlNodes.from.selected == false) { - this.controlNodes.positions.from = this.getControlNodeFromPosition(ctx); - this.controlNodes.from.x = this.controlNodes.positions.from.x; - this.controlNodes.from.y = this.controlNodes.positions.from.y; - } - if (this.controlNodes.to.selected == false) { - this.controlNodes.positions.to = this.getControlNodeToPosition(ctx); - this.controlNodes.to.x = this.controlNodes.positions.to.x; - this.controlNodes.to.y = this.controlNodes.positions.to.y; + Groups.prototype.clear = function () { + this.groups = {}; + this.groups.length = function() + { + var i = 0; + for ( var p in this ) { + if (this.hasOwnProperty(p)) { + i++; + } } - - this.controlNodes.from.draw(ctx); - this.controlNodes.to.draw(ctx); - } - else { - this.controlNodes = {from:null, to:null, positions:{}}; + return i; } }; - /** - * Enable control nodes. - * @private - */ - Edge.prototype._enableControlNodes = function() { - this.fromBackup = this.from; - this.toBackup = this.to; - this.controlNodesEnabled = true; - }; /** - * disable control nodes and remove from dynamicEdges from old node - * @private + * get group properties of a groupname. If groupname is not found, a new group + * is added. + * @param {*} groupname Can be a number, string, Date, etc. + * @return {Object} group The created group, containing all group properties */ - Edge.prototype._disableControlNodes = function() { - this.fromId = this.from.id; - this.toId = this.to.id; - if (this.fromId != this.fromBackup.id) { // from was changed, remove edge from old 'from' node dynamic edges - this.fromBackup.detachEdge(this); - } - else if (this.toId != this.toBackup.id) { // to was changed, remove edge from old 'to' node dynamic edges - this.toBackup.detachEdge(this); + Groups.prototype.get = function (groupname) { + var group = this.groups[groupname]; + if (group == undefined) { + // create new group + var index = this.defaultIndex % Groups.DEFAULT.length; + this.defaultIndex++; + group = {}; + group.color = Groups.DEFAULT[index]; + this.groups[groupname] = group; } - this.fromBackup = null; - this.toBackup = null; - this.controlNodesEnabled = false; + return group; }; - /** - * This checks if one of the control nodes is selected and if so, returns the control node object. Else it returns null. - * @param x - * @param y - * @returns {null} - * @private + * Add a custom group style + * @param {String} groupname + * @param {Object} style An object containing borderColor, + * backgroundColor, etc. + * @return {Object} group The created group object */ - Edge.prototype._getSelectedControlNode = function(x,y) { - var positions = this.controlNodes.positions; - var fromDistance = Math.sqrt(Math.pow(x - positions.from.x,2) + Math.pow(y - positions.from.y,2)); - var toDistance = Math.sqrt(Math.pow(x - positions.to.x ,2) + Math.pow(y - positions.to.y ,2)); - - if (fromDistance < 15) { - this.connectedNode = this.from; - this.from = this.controlNodes.from; - return this.controlNodes.from; - } - else if (toDistance < 15) { - this.connectedNode = this.to; - this.to = this.controlNodes.to; - return this.controlNodes.to; - } - else { - return null; - } + Groups.prototype.add = function (groupname, style) { + this.groups[groupname] = style; + return style; }; + module.exports = Groups; + + +/***/ }, +/* 55 */ +/***/ function(module, exports, __webpack_require__) { /** - * this resets the control nodes to their original position. - * @private + * @class Images + * This class loads images and keeps them stored. */ - Edge.prototype._restoreControlNodes = function() { - if (this.controlNodes.from.selected == true) { - this.from = this.connectedNode; - this.connectedNode = null; - this.controlNodes.from.unselect(); - } - else if (this.controlNodes.to.selected == true) { - this.to = this.connectedNode; - this.connectedNode = null; - this.controlNodes.to.unselect(); - } - }; + function Images() { + this.images = {}; + this.imageBroken = {}; + this.callback = undefined; + } /** - * this calculates the position of the control nodes on the edges of the parent nodes. - * - * @param ctx - * @returns {x: *, y: *} + * Set an onload callback function. This will be called each time an image + * is loaded + * @param {function} callback */ - Edge.prototype.getControlNodeFromPosition = function(ctx) { - // draw arrow head - var controlnodeFromPos; - if (this.options.smoothCurves.enabled == true) { - controlnodeFromPos = this._findBorderPosition(true, ctx); - } - else { - var angle = Math.atan2((this.to.y - this.from.y), (this.to.x - this.from.x)); - var dx = (this.to.x - this.from.x); - var dy = (this.to.y - this.from.y); - var edgeSegmentLength = Math.sqrt(dx * dx + dy * dy); - - var fromBorderDist = this.from.distanceToBorder(ctx, angle + Math.PI); - var fromBorderPoint = (edgeSegmentLength - fromBorderDist) / edgeSegmentLength; - controlnodeFromPos = {}; - controlnodeFromPos.x = (fromBorderPoint) * this.from.x + (1 - fromBorderPoint) * this.to.x; - controlnodeFromPos.y = (fromBorderPoint) * this.from.y + (1 - fromBorderPoint) * this.to.y; - } - - return controlnodeFromPos; + Images.prototype.setOnloadCallback = function(callback) { + this.callback = callback; }; /** - * this calculates the position of the control nodes on the edges of the parent nodes. * - * @param ctx - * @returns {{from: {x: number, y: number}, to: {x: *, y: *}}} + * @param {string} url Url of the image + * @param {string} url Url of an image to use if the url image is not found + * @return {Image} img The image object */ - Edge.prototype.getControlNodeToPosition = function(ctx) { - // draw arrow head - var controlnodeFromPos,controlnodeToPos; - if (this.options.smoothCurves.enabled == true) { - controlnodeToPos = this._findBorderPosition(false, ctx); - } - else { - var angle = Math.atan2((this.to.y - this.from.y), (this.to.x - this.from.x)); - var dx = (this.to.x - this.from.x); - var dy = (this.to.y - this.from.y); - var edgeSegmentLength = Math.sqrt(dx * dx + dy * dy); - var toBorderDist = this.to.distanceToBorder(ctx, angle); - var toBorderPoint = (edgeSegmentLength - toBorderDist) / edgeSegmentLength; + Images.prototype.load = function(url, brokenUrl) { + var img = this.images[url]; // make a pointer + if (img === undefined) { + // create the image + var me = this; + img = new Image(); + img.onload = function () { + // IE11 fix -- thanks dponch! + if (this.width == 0) { + document.body.appendChild(this); + this.width = this.offsetWidth; + this.height = this.offsetHeight; + document.body.removeChild(this); + } - controlnodeToPos = {}; - controlnodeToPos.x = (1 - toBorderPoint) * this.from.x + toBorderPoint * this.to.x; - controlnodeToPos.y = (1 - toBorderPoint) * this.from.y + toBorderPoint * this.to.y; + if (me.callback) { + me.images[url] = img; + me.callback(this); + } + }; + + img.onerror = function () { + if (brokenUrl === undefined) { + console.error("Could not load image:", url); + delete this.src; + if (me.callback) { + me.callback(this); + } + } + else { + if (me.imageBroken[url] === true) { + if (this.src == brokenUrl) { + console.error("Could not load brokenImage:", brokenUrl); + delete this.src; + if (me.callback) { + me.callback(this); + } + } + else { + console.error("Could not load image:", url); + this.src = brokenUrl; + } + } + else { + console.error("Could not load image:", url); + this.src = brokenUrl; + me.imageBroken[url] = true; + } + } + }; + + img.src = url; } - return controlnodeToPos; + return img; }; - module.exports = Edge; + module.exports = Images; + /***/ }, -/* 53 */ +/* 56 */ /***/ function(module, exports, __webpack_require__) { var util = __webpack_require__(1); @@ -27926,1213 +27642,1506 @@ return /******/ (function(modules) { // webpackBootstrap /***/ }, -/* 54 */ +/* 57 */ /***/ function(module, exports, __webpack_require__) { var util = __webpack_require__(1); + var Node = __webpack_require__(56); /** - * @class Groups - * This class can store groups and properties specific for groups. + * @class Edge + * + * A edge connects two nodes + * @param {Object} properties Object with properties. Must contain + * At least properties from and to. + * Available properties: 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 */ - function Groups() { - this.clear(); - this.defaultIndex = 0; - } + function Edge (properties, network, networkConstants) { + if (!network) { + throw "No network provided"; + } + var fields = ['edges','physics']; + var constants = util.selectiveBridgeObject(fields,networkConstants); + this.options = constants.edges; + this.physics = constants.physics; + this.options['smoothCurves'] = networkConstants['smoothCurves']; - /** - * default constants for group colors - */ - Groups.DEFAULT = [ - {border: "#2B7CE9", background: "#97C2FC", highlight: {border: "#2B7CE9", background: "#D2E5FF"}, hover: {border: "#2B7CE9", background: "#D2E5FF"}}, // blue - {border: "#FFA500", background: "#FFFF00", highlight: {border: "#FFA500", background: "#FFFFA3"}, hover: {border: "#FFA500", background: "#FFFFA3"}}, // yellow - {border: "#FA0A10", background: "#FB7E81", highlight: {border: "#FA0A10", background: "#FFAFB1"}, hover: {border: "#FA0A10", background: "#FFAFB1"}}, // red - {border: "#41A906", background: "#7BE141", highlight: {border: "#41A906", background: "#A1EC76"}, hover: {border: "#41A906", background: "#A1EC76"}}, // green - {border: "#E129F0", background: "#EB7DF4", highlight: {border: "#E129F0", background: "#F0B3F5"}, hover: {border: "#E129F0", background: "#F0B3F5"}}, // magenta - {border: "#7C29F0", background: "#AD85E4", highlight: {border: "#7C29F0", background: "#D3BDF0"}, hover: {border: "#7C29F0", background: "#D3BDF0"}}, // purple - {border: "#C37F00", background: "#FFA807", highlight: {border: "#C37F00", background: "#FFCA66"}, hover: {border: "#C37F00", background: "#FFCA66"}}, // orange - {border: "#4220FB", background: "#6E6EFD", highlight: {border: "#4220FB", background: "#9B9BFD"}, hover: {border: "#4220FB", background: "#9B9BFD"}}, // darkblue - {border: "#FD5A77", background: "#FFC0CB", highlight: {border: "#FD5A77", background: "#FFD1D9"}, hover: {border: "#FD5A77", background: "#FFD1D9"}}, // pink - {border: "#4AD63A", background: "#C2FABC", highlight: {border: "#4AD63A", background: "#E6FFE3"}, hover: {border: "#4AD63A", background: "#E6FFE3"}} // mint - ]; + this.network = network; + + // initialize variables + this.id = undefined; + this.fromId = undefined; + this.toId = undefined; + this.title = undefined; + this.widthSelected = this.options.width * this.options.widthSelectionMultiplier; + this.value = undefined; + this.selected = false; + this.hover = false; + this.labelDimensions = {top:0,left:0,width:0,height:0,yLine:0}; // could be cached + this.dirtyLabel = true; + + this.from = null; // a node + this.to = null; // a node + this.via = null; // a temp node + + this.fromBackup = null; // used to clean up after reconnect + this.toBackup = null;; // used to clean up after reconnect + + // we use this to be able to reconnect the edge to a cluster if its node is put into a cluster + // by storing the original information we can revert to the original connection when the cluser is opened. + this.originalFromId = []; + this.originalToId = []; + + this.connected = false; + + this.widthFixed = false; + this.lengthFixed = false; + + this.setProperties(properties); + this.controlNodesEnabled = false; + this.controlNodes = {from:null, to:null, positions:{}}; + this.connectedNode = null; + } /** - * Clear all groups + * Set or overwrite properties for the edge + * @param {Object} properties an object with properties + * @param {Object} constants and object with default, global properties */ - Groups.prototype.clear = function () { - this.groups = {}; - this.groups.length = function() - { - var i = 0; - for ( var p in this ) { - if (this.hasOwnProperty(p)) { - i++; - } + Edge.prototype.setProperties = function(properties) { + if (!properties) { + return; + } + + var fields = ['style','fontSize','fontFace','fontColor','fontFill','fontStrokeWidth','fontStrokeColor','width', + 'widthSelectionMultiplier','hoverWidth','arrowScaleFactor','dash','inheritColor','labelAlignment' + ]; + util.selectiveDeepExtend(fields, this.options, properties); + + if (properties.from !== undefined) {this.fromId = properties.from;} + if (properties.to !== undefined) {this.toId = properties.to;} + + if (properties.id !== undefined) {this.id = properties.id;} + if (properties.label !== undefined) {this.label = properties.label; this.dirtyLabel = true;} + + if (properties.title !== undefined) {this.title = properties.title;} + if (properties.value !== undefined) {this.value = properties.value;} + if (properties.length !== undefined) {this.physics.springLength = properties.length;} + + if (properties.color !== undefined) { + this.options.inheritColor = false; + if (util.isString(properties.color)) { + this.options.color.color = properties.color; + this.options.color.highlight = properties.color; + } + else { + if (properties.color.color !== undefined) {this.options.color.color = properties.color.color;} + if (properties.color.highlight !== undefined) {this.options.color.highlight = properties.color.highlight;} + if (properties.color.hover !== undefined) {this.options.color.hover = properties.color.hover;} } - return i; } - }; + // A node is connected when it has a from and to node. + this.connect(); + + this.widthFixed = this.widthFixed || (properties.width !== undefined); + this.lengthFixed = this.lengthFixed || (properties.length !== undefined); + + this.widthSelected = this.options.width* this.options.widthSelectionMultiplier; + + // set draw method based on style + switch (this.options.style) { + case 'line': this.draw = this._drawLine; break; + case 'arrow': this.draw = this._drawArrow; break; + case 'arrow-center': this.draw = this._drawArrowCenter; break; + case 'dash-line': this.draw = this._drawDashLine; break; + default: this.draw = this._drawLine; break; + } + + }; /** - * get group properties of a groupname. If groupname is not found, a new group - * is added. - * @param {*} groupname Can be a number, string, Date, etc. - * @return {Object} group The created group, containing all group properties + * Connect an edge to its nodes + */ + Edge.prototype.connect = function () { + this.disconnect(); + + this.from = this.network.nodes[this.fromId] || null; + this.to = this.network.nodes[this.toId] || null; + this.connected = (this.from && this.to); + + if (this.connected) { + this.from.attachEdge(this); + this.to.attachEdge(this); + } + else { + if (this.from) { + this.from.detachEdge(this); + } + if (this.to) { + this.to.detachEdge(this); + } + } + }; + + /** + * Disconnect an edge from its nodes */ - Groups.prototype.get = function (groupname) { - var group = this.groups[groupname]; - if (group == undefined) { - // create new group - var index = this.defaultIndex % Groups.DEFAULT.length; - this.defaultIndex++; - group = {}; - group.color = Groups.DEFAULT[index]; - this.groups[groupname] = group; + Edge.prototype.disconnect = function () { + if (this.from) { + this.from.detachEdge(this); + this.from = null; + } + if (this.to) { + this.to.detachEdge(this); + this.to = null; } - return group; + this.connected = false; }; /** - * Add a custom group style - * @param {String} groupname - * @param {Object} style An object containing borderColor, - * backgroundColor, etc. - * @return {Object} group The created group object + * get the title of this edge. + * @return {string} title The title of the edge, or undefined when no title + * has been set. */ - Groups.prototype.add = function (groupname, style) { - this.groups[groupname] = style; - return style; + Edge.prototype.getTitle = function() { + return typeof this.title === "function" ? this.title() : this.title; }; - module.exports = Groups; - - -/***/ }, -/* 55 */ -/***/ function(module, exports, __webpack_require__) { /** - * @class Images - * This class loads images and keeps them stored. + * Retrieve the value of the edge. Can be undefined + * @return {Number} value */ - function Images() { - this.images = {}; - this.imageBroken = {}; - this.callback = undefined; - } + Edge.prototype.getValue = function() { + return this.value; + }; /** - * Set an onload callback function. This will be called each time an image - * is loaded - * @param {function} callback + * Adjust the value range of the edge. The edge will adjust it's width + * based on its value. + * @param {Number} min + * @param {Number} max */ - Images.prototype.setOnloadCallback = function(callback) { - this.callback = callback; + Edge.prototype.setValueRange = function(min, max) { + if (!this.widthFixed && this.value !== undefined) { + var scale = (this.options.widthMax - this.options.widthMin) / (max - min); + this.options.width= (this.value - min) * scale + this.options.widthMin; + this.widthSelected = this.options.width* this.options.widthSelectionMultiplier; + } }; /** - * - * @param {string} url Url of the image - * @param {string} url Url of an image to use if the url image is not found - * @return {Image} img The image object + * 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 */ - Images.prototype.load = function(url, brokenUrl) { - var img = this.images[url]; // make a pointer - if (img === undefined) { - // create the image - var me = this; - img = new Image(); - img.onload = function () { - // IE11 fix -- thanks dponch! - if (this.width == 0) { - document.body.appendChild(this); - this.width = this.offsetWidth; - this.height = this.offsetHeight; - document.body.removeChild(this); - } + Edge.prototype.draw = function(ctx) { + throw "Method draw not initialized in edge"; + }; - if (me.callback) { - me.images[url] = img; - me.callback(this); - } - }; + /** + * 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 + */ + Edge.prototype.isOverlappingWith = function(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; - img.onerror = function () { - if (brokenUrl === undefined) { - console.error("Could not load image:", url); - delete this.src; - if (me.callback) { - me.callback(this); - } - } - else if (me.imageBroken[url] === true) { - console.error("Could not load brokenImage:", brokenUrl); - delete this.src; - if (me.callback) { - me.callback(this); - } - } - else { - this.src = brokenUrl; - me.imageBroken[url] = true; - } - }; + var dist = this._getDistanceToEdge(xFrom, yFrom, xTo, yTo, xObj, yObj); - img.src = url; + return (dist < distMax); + } + else { + return false } - - return img; }; - module.exports = Images; + Edge.prototype._getColor = function() { + var colorObj = this.options.color; + if (this.options.inheritColor == "to") { + colorObj = { + highlight: this.to.options.color.highlight.border, + hover: this.to.options.color.hover.border, + color: this.to.options.color.border + }; + } + else if (this.options.inheritColor == "from" || this.options.inheritColor == true) { + colorObj = { + highlight: this.from.options.color.highlight.border, + hover: this.from.options.color.hover.border, + color: this.from.options.color.border + }; + } + if (this.selected == true) {return colorObj.highlight;} + else if (this.hover == true) {return colorObj.hover;} + else {return colorObj.color;} + }; -/***/ }, -/* 56 */ -/***/ function(module, exports, __webpack_require__) { /** - * 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. + * Redraw a edge as a line + * Draw this edge in the given canvas + * The 2d context of a HTML canvas can be retrieved by canvas.getContext("2d"); + * @param {CanvasRenderingContext2D} ctx + * @private */ - function Popup(container, x, y, text, style) { - if (container) { - this.container = container; - } - else { - this.container = document.body; - } + Edge.prototype._drawLine = function(ctx) { + // set style + ctx.strokeStyle = this._getColor(); + ctx.lineWidth = this._getLineWidth(); - // x, y and text are optional, see if a style object was passed in their place - if (style === undefined) { - if (typeof x === "object") { - style = x; - x = undefined; - } else if (typeof text === "object") { - style = text; - text = undefined; - } else { - // for backwards compatibility, in case clients other than Network are creating Popup directly - style = { - fontColor: 'black', - fontSize: 14, // px - fontFace: 'verdana', - color: { - border: '#666', - background: '#FFFFC6' - } + if (this.from != this.to) { + // draw line + var via = this._line(ctx); + + // draw label + var point; + if (this.label) { + if (this.options.smoothCurves.enabled == true && via != null) { + var midpointX = 0.5*(0.5*(this.from.x + via.x) + 0.5*(this.to.x + via.x)); + var midpointY = 0.5*(0.5*(this.from.y + via.y) + 0.5*(this.to.y + via.y)); + point = {x:midpointX, y:midpointY}; } + else { + point = this._pointOnLine(0.5); + } + this._label(ctx, this.label, point.x, point.y); } } - - this.x = 0; - this.y = 0; - this.padding = 5; - - if (x !== undefined && y !== undefined ) { - this.setPosition(x, y); - } - if (text !== undefined) { - this.setText(text); + else { + var x, y; + var radius = this.physics.springLength / 4; + var node = this.from; + if (!node.width) { + node.resize(ctx); + } + if (node.width > node.height) { + x = node.x + node.width / 2; + y = node.y - radius; + } + else { + x = node.x + radius; + y = node.y - node.height / 2; + } + this._circle(ctx, x, y, radius); + point = this._pointOnCircle(x, y, radius, 0.5); + this._label(ctx, this.label, point.x, point.y); } - - // create the frame - this.frame = document.createElement("div"); - var styleAttr = this.frame.style; - styleAttr.position = "absolute"; - styleAttr.visibility = "hidden"; - styleAttr.border = "1px solid " + style.color.border; - styleAttr.color = style.fontColor; - styleAttr.fontSize = style.fontSize + "px"; - styleAttr.fontFamily = style.fontFace; - styleAttr.padding = this.padding + "px"; - styleAttr.backgroundColor = style.color.background; - styleAttr.borderRadius = "3px"; - styleAttr.MozBorderRadius = "3px"; - styleAttr.WebkitBorderRadius = "3px"; - styleAttr.boxShadow = "3px 3px 10px rgba(128, 128, 128, 0.5)"; - styleAttr.whiteSpace = "nowrap"; - this.container.appendChild(this.frame); - } - - /** - * @param {number} x Horizontal position of the popup window - * @param {number} y Vertical position of the popup window - */ - Popup.prototype.setPosition = function(x, y) { - this.x = parseInt(x); - this.y = parseInt(y); }; /** - * Set the content for the popup window. This can be HTML code or text. - * @param {string | Element} content + * Get the line width of the edge. Depends on width and whether one of the + * connected nodes is selected. + * @return {Number} width + * @private */ - Popup.prototype.setText = function(content) { - if (content instanceof Element) { - this.frame.innerHTML = ''; - this.frame.appendChild(content); + Edge.prototype._getLineWidth = function() { + if (this.selected == true) { + return Math.max(Math.min(this.widthSelected, this.options.widthMax), 0.3*this.networkScaleInv); } else { - this.frame.innerHTML = content; // string containing text or HTML + if (this.hover == true) { + return Math.max(Math.min(this.options.hoverWidth, this.options.widthMax), 0.3*this.networkScaleInv); + } + else { + return Math.max(this.options.width, 0.3*this.networkScaleInv); + } } }; - /** - * Show the popup window - * @param {boolean} show Optional. Show or hide the window - */ - Popup.prototype.show = function (show) { - if (show === undefined) { - show = true; + Edge.prototype._getViaCoordinates = function () { + if (this.options.smoothCurves.dynamic == true && this.options.smoothCurves.enabled == true ) { + return this.via; } + else if (this.options.smoothCurves.enabled == false) { + return {x:0,y:0}; + } + else { + var xVia = null; + var yVia = null; + var factor = this.options.smoothCurves.roundness; + var type = this.options.smoothCurves.type; - if (show) { - 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; + var dx = Math.abs(this.from.x - this.to.x); + var dy = Math.abs(this.from.y - this.to.y); + if (type == 'discrete' || type == 'diagonalCross') { + if (Math.abs(this.from.x - this.to.x) < Math.abs(this.from.y - this.to.y)) { + if (this.from.y > this.to.y) { + if (this.from.x < this.to.x) { + xVia = this.from.x + factor * dy; + yVia = this.from.y - factor * dy; + } + else if (this.from.x > this.to.x) { + xVia = this.from.x - factor * dy; + yVia = this.from.y - factor * dy; + } + } + else if (this.from.y < this.to.y) { + if (this.from.x < this.to.x) { + xVia = this.from.x + factor * dy; + yVia = this.from.y + factor * dy; + } + else if (this.from.x > this.to.x) { + xVia = this.from.x - factor * dy; + yVia = this.from.y + factor * dy; + } + } + if (type == "discrete") { + xVia = dx < factor * dy ? this.from.x : xVia; + } + } + else if (Math.abs(this.from.x - this.to.x) > Math.abs(this.from.y - this.to.y)) { + if (this.from.y > this.to.y) { + if (this.from.x < this.to.x) { + xVia = this.from.x + factor * dx; + yVia = this.from.y - factor * dx; + } + else if (this.from.x > this.to.x) { + xVia = this.from.x - factor * dx; + yVia = this.from.y - factor * dx; + } + } + else if (this.from.y < this.to.y) { + if (this.from.x < this.to.x) { + xVia = this.from.x + factor * dx; + yVia = this.from.y + factor * dx; + } + else if (this.from.x > this.to.x) { + xVia = this.from.x - factor * dx; + yVia = this.from.y + factor * dx; + } + } + if (type == "discrete") { + yVia = dy < factor * dx ? this.from.y : yVia; + } + } } - if (top < this.padding) { - top = this.padding; + else if (type == "straightCross") { + if (Math.abs(this.from.x - this.to.x) < Math.abs(this.from.y - this.to.y)) { // up - down + xVia = this.from.x; + if (this.from.y < this.to.y) { + yVia = this.to.y - (1 - factor) * dy; + } + else { + yVia = this.to.y + (1 - factor) * dy; + } + } + else if (Math.abs(this.from.x - this.to.x) > Math.abs(this.from.y - this.to.y)) { // left - right + if (this.from.x < this.to.x) { + xVia = this.to.x - (1 - factor) * dx; + } + else { + xVia = this.to.x + (1 - factor) * dx; + } + yVia = this.from.y; + } } - - var left = this.x; - if (left + width + this.padding > maxWidth) { - left = maxWidth - width - this.padding; + else if (type == 'horizontal') { + if (this.from.x < this.to.x) { + xVia = this.to.x - (1 - factor) * dx; + } + else { + xVia = this.to.x + (1 - factor) * dx; + } + yVia = this.from.y; } - if (left < this.padding) { - left = this.padding; + else if (type == 'vertical') { + xVia = this.from.x; + if (this.from.y < this.to.y) { + yVia = this.to.y - (1 - factor) * dy; + } + else { + yVia = this.to.y + (1 - factor) * dy; + } + } + else { // continuous + if (Math.abs(this.from.x - this.to.x) < Math.abs(this.from.y - this.to.y)) { + if (this.from.y > this.to.y) { + if (this.from.x < this.to.x) { + // console.log(1) + xVia = this.from.x + factor * dy; + yVia = this.from.y - factor * dy; + xVia = this.to.x < xVia ? this.to.x : xVia; + } + else if (this.from.x > this.to.x) { + // console.log(2) + xVia = this.from.x - factor * dy; + yVia = this.from.y - factor * dy; + xVia = this.to.x > xVia ? this.to.x : xVia; + } + } + else if (this.from.y < this.to.y) { + if (this.from.x < this.to.x) { + // console.log(3) + xVia = this.from.x + factor * dy; + yVia = this.from.y + factor * dy; + xVia = this.to.x < xVia ? this.to.x : xVia; + } + else if (this.from.x > this.to.x) { + // console.log(4, this.from.x, this.to.x) + xVia = this.from.x - factor * dy; + yVia = this.from.y + factor * dy; + xVia = this.to.x > xVia ? this.to.x : xVia; + } + } + } + else if (Math.abs(this.from.x - this.to.x) > Math.abs(this.from.y - this.to.y)) { + if (this.from.y > this.to.y) { + if (this.from.x < this.to.x) { + // console.log(5) + xVia = this.from.x + factor * dx; + yVia = this.from.y - factor * dx; + yVia = this.to.y > yVia ? this.to.y : yVia; + } + else if (this.from.x > this.to.x) { + // console.log(6) + xVia = this.from.x - factor * dx; + yVia = this.from.y - factor * dx; + yVia = this.to.y > yVia ? this.to.y : yVia; + } + } + else if (this.from.y < this.to.y) { + if (this.from.x < this.to.x) { + // console.log(7) + xVia = this.from.x + factor * dx; + yVia = this.from.y + factor * dx; + yVia = this.to.y < yVia ? this.to.y : yVia; + } + else if (this.from.x > this.to.x) { + // console.log(8) + xVia = this.from.x - factor * dx; + yVia = this.from.y + factor * dx; + yVia = this.to.y < yVia ? this.to.y : yVia; + } + } + } } - this.frame.style.left = left + "px"; - this.frame.style.top = top + "px"; - this.frame.style.visibility = "visible"; + + return {x: xVia, y: yVia}; + } + }; + + /** + * Draw a line between two nodes + * @param {CanvasRenderingContext2D} ctx + * @private + */ + Edge.prototype._line = function (ctx) { + // draw a straight line + ctx.beginPath(); + ctx.moveTo(this.from.x, this.from.y); + if (this.options.smoothCurves.enabled == true) { + if (this.options.smoothCurves.dynamic == false) { + var via = this._getViaCoordinates(); + if (via.x == null) { + ctx.lineTo(this.to.x, this.to.y); + ctx.stroke(); + return null; + } + else { + // this.via.x = via.x; + // this.via.y = via.y; + ctx.quadraticCurveTo(via.x,via.y,this.to.x, this.to.y); + ctx.stroke(); + return via; + } + } + else { + ctx.quadraticCurveTo(this.via.x,this.via.y,this.to.x, this.to.y); + ctx.stroke(); + return this.via; + } } else { - this.hide(); + ctx.lineTo(this.to.x, this.to.y); + ctx.stroke(); + return null; } }; /** - * Hide the popup window + * Draw a line from a node to itself, a circle + * @param {CanvasRenderingContext2D} ctx + * @param {Number} x + * @param {Number} y + * @param {Number} radius + * @private */ - Popup.prototype.hide = function () { - this.frame.style.visibility = "hidden"; + Edge.prototype._circle = function (ctx, x, y, radius) { + // draw a circle + ctx.beginPath(); + ctx.arc(x, y, radius, 0, 2 * Math.PI, false); + ctx.stroke(); }; - module.exports = Popup; - - -/***/ }, -/* 57 */ -/***/ function(module, exports, __webpack_require__) { - /** - * Parse a text source containing data in DOT language into a JSON object. - * The object contains two lists: one with nodes and one with edges. - * - * DOT language reference: http://www.graphviz.org/doc/info/lang.html - * - * @param {String} data Text containing a graph in DOT-notation - * @return {Object} graph An object containing two parameters: - * {Object[]} nodes - * {Object[]} edges + * Draw label with white background and with the middle at (x, y) + * @param {CanvasRenderingContext2D} ctx + * @param {String} text + * @param {Number} x + * @param {Number} y + * @private */ - function parseDOT (data) { - dot = data; - return parseGraph(); - } - - // token types enumeration - var TOKENTYPE = { - NULL : 0, - DELIMITER : 1, - IDENTIFIER: 2, - UNKNOWN : 3 - }; + Edge.prototype._label = function (ctx, text, x, y) { + if (text) { + ctx.font = ((this.from.selected || this.to.selected) ? "bold " : "") + + this.options.fontSize + "px " + this.options.fontFace; + var yLine; - // map with all delimiters - var DELIMITERS = { - '{': true, - '}': true, - '[': true, - ']': true, - ';': true, - '=': true, - ',': true, + if (this.dirtyLabel == true) { + var lines = String(text).split('\n'); + var lineCount = lines.length; + var fontSize = Number(this.options.fontSize); + yLine = y + (1 - lineCount) / 2 * fontSize; - '->': true, - '--': true - }; + var 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; + } + var height = this.options.fontSize * lineCount; + var left = x - width / 2; + var top = y - height / 2; - var dot = ''; // current dot file - var index = 0; // current index in dot file - var c = ''; // current token character in expr - var token = ''; // current token - var tokenType = TOKENTYPE.NULL; // type of the token + // cache + this.labelDimensions = {top:top,left:left,width:width,height:height,yLine:yLine}; + } - /** - * Get the first character from the dot file. - * The character is stored into the char c. If the end of the dot file is - * reached, the function puts an empty string in c. - */ - function first() { - index = 0; - c = dot.charAt(0); - } + var yLine = this.labelDimensions.yLine; + + ctx.save(); + + if (this.options.labelAlignment != "horizontal"){ + ctx.translate(x, yLine); + this._rotateForLabelAlignment(ctx); + x = 0; + yLine = 0; + } - /** - * Get the next character from the dot file. - * The character is stored into the char c. If the end of the dot file is - * reached, the function puts an empty string in c. - */ - function next() { - index++; - c = dot.charAt(index); - } + + this._drawLabelRect(ctx); + this._drawLabelText(ctx,x,yLine, lines, lineCount, fontSize); + + ctx.restore(); + } + }; /** - * Preview the next character from the dot file. - * @return {String} cNext + * Rotates the canvas so the text is most readable + * @param {CanvasRenderingContext2D} ctx + * @private */ - function nextPreview() { - return dot.charAt(index + 1); - } + Edge.prototype._rotateForLabelAlignment = function(ctx) { + var dy = this.from.y - this.to.y; + var dx = this.from.x - this.to.x; + var angleInDegrees = Math.atan2(dy, dx); - /** - * Test whether given character is alphabetic or numeric - * @param {String} c - * @return {Boolean} isAlphaNumeric - */ - var regexAlphaNumeric = /[a-zA-Z_0-9.:#]/; - function isAlphaNumeric(c) { - return regexAlphaNumeric.test(c); - } + // rotate so label it is readable + if((angleInDegrees < -1 && dx < 0) || (angleInDegrees > 0 && dx < 0)){ + angleInDegrees = angleInDegrees + Math.PI; + } + + ctx.rotate(angleInDegrees); + }; /** - * Merge all properties of object b into object b - * @param {Object} a - * @param {Object} b - * @return {Object} a + * Draws the label rectangle + * @param {CanvasRenderingContext2D} ctx + * @param {String} labelAlignment + * @private */ - function merge (a, b) { - if (!a) { - a = {}; - } + Edge.prototype._drawLabelRect = function(ctx) { + if (this.options.fontFill !== undefined && this.options.fontFill !== null && this.options.fontFill !== "none") { + ctx.fillStyle = this.options.fontFill; + + var lineMargin = 2; - if (b) { - for (var name in b) { - if (b.hasOwnProperty(name)) { - a[name] = b[name]; - } + if (this.options.labelAlignment == 'line-center') { + ctx.fillRect(-this.labelDimensions.width * 0.5, -this.labelDimensions.height * 0.5, this.labelDimensions.width, this.labelDimensions.height); + } + else if (this.options.labelAlignment == 'line-above') { + ctx.fillRect(-this.labelDimensions.width * 0.5, -(this.labelDimensions.height + lineMargin), this.labelDimensions.width, this.labelDimensions.height); + } + else if (this.options.labelAlignment == 'line-below') { + ctx.fillRect(-this.labelDimensions.width * 0.5, lineMargin, this.labelDimensions.width, this.labelDimensions.height); + } + else { + ctx.fillRect(this.labelDimensions.left, this.labelDimensions.top, this.labelDimensions.width, this.labelDimensions.height); } } - return a; - } + }; /** - * Set a value in an object, where the provided parameter name can be a - * path with nested parameters. For example: - * - * var obj = {a: 2}; - * setValue(obj, 'b.c', 3); // obj = {a: 2, b: {c: 3}} - * - * @param {Object} obj - * @param {String} path A parameter name or dot-separated parameter path, - * like "color.highlight.border". - * @param {*} value + * Draws the label text + * @param {CanvasRenderingContext2D} ctx + * @param {Number} x + * @param {Number} yLine + * @param {Array} lines + * @param {Number} lineCount + * @param {Number} fontSize + * @private */ - function setValue(obj, path, value) { - var keys = path.split('.'); - var o = obj; - while (keys.length) { - var key = keys.shift(); - if (keys.length) { - // this isn't the end point - if (!o[key]) { - o[key] = {}; - } - o = o[key]; + Edge.prototype._drawLabelText = function(ctx, x, yLine, lines, lineCount, fontSize) { + // draw text + ctx.fillStyle = this.options.fontColor || "black"; + ctx.textAlign = "center"; + + // check for label alignment + if (this.options.labelAlignment != 'horizontal') { + var lineMargin = 2; + if (this.options.labelAlignment == 'line-above') { + ctx.textBaseline = "alphabetic"; + yLine -= 2 * lineMargin; // distance from edge, required because we use alphabetic. Alphabetic has less difference between browsers + } + else if (this.options.labelAlignment == 'line-below') { + ctx.textBaseline = "hanging"; + yLine += 2 * lineMargin;// distance from edge, required because we use hanging. Hanging has less difference between browsers } else { - // this is the end point - o[key] = value; + ctx.textBaseline = "middle"; } } - } + else { + ctx.textBaseline = "middle"; + } + + // check for strokeWidth + if (this.options.fontStrokeWidth > 0){ + ctx.lineWidth = this.options.fontStrokeWidth; + ctx.strokeStyle = this.options.fontStrokeColor; + ctx.lineJoin = 'round'; + } + for (var i = 0; i < lineCount; i++) { + if(this.options.fontStrokeWidth > 0){ + ctx.strokeText(lines[i], x, yLine); + } + ctx.fillText(lines[i], x, yLine); + yLine += fontSize; + } + }; /** - * Add a node to a graph object. If there is already a node with - * the same id, their attributes will be merged. - * @param {Object} graph - * @param {Object} node + * Redraw a edge as a dashed line + * Draw this edge in the given canvas + * @author David Jordan + * @date 2012-08-08 + * The 2d context of a HTML canvas can be retrieved by canvas.getContext("2d"); + * @param {CanvasRenderingContext2D} ctx + * @private */ - function addNode(graph, node) { - var i, len; - var current = null; - - // find root graph (in case of subgraph) - var graphs = [graph]; // list with all graphs from current graph to root graph - var root = graph; - while (root.parent) { - graphs.push(root.parent); - root = root.parent; - } + Edge.prototype._drawDashLine = function(ctx) { + // set style + ctx.strokeStyle = this._getColor(); + ctx.lineWidth = this._getLineWidth(); - // find existing node (at root level) by its id - if (root.nodes) { - for (i = 0, len = root.nodes.length; i < len; i++) { - if (node.id === root.nodes[i].id) { - current = root.nodes[i]; - break; - } + var via = null; + // only firefox and chrome support this method, else we use the legacy one. + if (ctx.setLineDash !== undefined) { + ctx.save(); + // configure the dash pattern + var pattern = [0]; + if (this.options.dash.length !== undefined && this.options.dash.gap !== undefined) { + pattern = [this.options.dash.length,this.options.dash.gap]; } - } - - if (!current) { - // this is a new node - current = { - id: node.id - }; - if (graph.node) { - // clone default attributes - current.attr = merge(current.attr, graph.node); + else { + pattern = [5,5]; } - } - // add node to this (sub)graph and all its parent graphs - for (i = graphs.length - 1; i >= 0; i--) { - var g = graphs[i]; + // set dash settings for chrome or firefox + ctx.setLineDash(pattern); + ctx.lineDashOffset = 0; - if (!g.nodes) { - g.nodes = []; + // draw the line + via = this._line(ctx); + + // restore the dash settings. + ctx.setLineDash([0]); + ctx.lineDashOffset = 0; + ctx.restore(); + } + else { // unsupporting smooth lines + // draw dashed line + ctx.beginPath(); + ctx.lineCap = 'round'; + if (this.options.dash.altLength !== undefined) //If an alt dash value has been set add to the array this value + { + ctx.dashedLine(this.from.x,this.from.y,this.to.x,this.to.y, + [this.options.dash.length,this.options.dash.gap,this.options.dash.altLength,this.options.dash.gap]); } - if (g.nodes.indexOf(current) == -1) { - g.nodes.push(current); + else if (this.options.dash.length !== undefined && this.options.dash.gap !== undefined) //If a dash and gap value has been set add to the array this value + { + ctx.dashedLine(this.from.x,this.from.y,this.to.x,this.to.y, + [this.options.dash.length,this.options.dash.gap]); + } + else //If all else fails draw a line + { + ctx.moveTo(this.from.x, this.from.y); + ctx.lineTo(this.to.x, this.to.y); } + ctx.stroke(); } - // merge attributes - if (node.attr) { - current.attr = merge(current.attr, node.attr); + // draw label + if (this.label) { + var point; + if (this.options.smoothCurves.enabled == true && via != null) { + var midpointX = 0.5*(0.5*(this.from.x + via.x) + 0.5*(this.to.x + via.x)); + var midpointY = 0.5*(0.5*(this.from.y + via.y) + 0.5*(this.to.y + via.y)); + point = {x:midpointX, y:midpointY}; + } + else { + point = this._pointOnLine(0.5); + } + this._label(ctx, this.label, point.x, point.y); } - } + }; /** - * Add an edge to a graph object - * @param {Object} graph - * @param {Object} edge + * Get a point on a line + * @param {Number} percentage. Value between 0 (line start) and 1 (line end) + * @return {Object} point + * @private */ - function addEdge(graph, edge) { - if (!graph.edges) { - graph.edges = []; + Edge.prototype._pointOnLine = function (percentage) { + return { + x: (1 - percentage) * this.from.x + percentage * this.to.x, + y: (1 - percentage) * this.from.y + percentage * this.to.y } - graph.edges.push(edge); - if (graph.edge) { - var attr = merge({}, graph.edge); // clone default attributes - edge.attr = merge(attr, edge.attr); // merge attributes + }; + + /** + * 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 + */ + Edge.prototype._pointOnCircle = function (x, y, radius, percentage) { + var angle = (percentage - 3/8) * 2 * Math.PI; + return { + x: x + radius * Math.cos(angle), + y: y - radius * Math.sin(angle) } - } + }; /** - * Create an edge to a graph object - * @param {Object} graph - * @param {String | Number | Object} from - * @param {String | Number | Object} to - * @param {String} type - * @param {Object | null} attr - * @return {Object} edge + * Redraw a edge as a line with an arrow halfway the line + * Draw this edge in the given canvas + * The 2d context of a HTML canvas can be retrieved by canvas.getContext("2d"); + * @param {CanvasRenderingContext2D} ctx + * @private */ - function createEdge(graph, from, to, type, attr) { - var edge = { - from: from, - to: to, - type: type - }; + Edge.prototype._drawArrowCenter = function(ctx) { + var point; + // set style + ctx.strokeStyle = this._getColor(); + ctx.fillStyle = ctx.strokeStyle; + ctx.lineWidth = this._getLineWidth(); - if (graph.edge) { - edge.attr = merge({}, graph.edge); // clone default attributes + if (this.from != this.to) { + // draw line + var via = this._line(ctx); + + var angle = Math.atan2((this.to.y - this.from.y), (this.to.x - this.from.x)); + var length = (10 + 5 * this.options.width) * this.options.arrowScaleFactor; + // draw an arrow halfway the line + if (this.options.smoothCurves.enabled == true && via != null) { + var midpointX = 0.5*(0.5*(this.from.x + via.x) + 0.5*(this.to.x + via.x)); + var midpointY = 0.5*(0.5*(this.from.y + via.y) + 0.5*(this.to.y + via.y)); + point = {x:midpointX, y:midpointY}; + } + else { + point = this._pointOnLine(0.5); + } + + ctx.arrow(point.x, point.y, angle, length); + ctx.fill(); + ctx.stroke(); + + // draw label + if (this.label) { + this._label(ctx, this.label, point.x, point.y); + } } - edge.attr = merge(edge.attr || {}, attr); // merge attributes + else { + // draw circle + var x, y; + var radius = 0.25 * Math.max(100,this.physics.springLength); + var node = this.from; + if (!node.width) { + node.resize(ctx); + } + if (node.width > node.height) { + x = node.x + node.width * 0.5; + y = node.y - radius; + } + else { + x = node.x + radius; + y = node.y - node.height * 0.5; + } + this._circle(ctx, x, y, radius); - return edge; + // draw all arrows + var angle = 0.2 * Math.PI; + var length = (10 + 5 * this.options.width) * this.options.arrowScaleFactor; + point = this._pointOnCircle(x, y, radius, 0.5); + ctx.arrow(point.x, point.y, angle, length); + ctx.fill(); + ctx.stroke(); + + // draw label + if (this.label) { + point = this._pointOnCircle(x, y, radius, 0.5); + this._label(ctx, this.label, point.x, point.y); + } + } + }; + + Edge.prototype._pointOnBezier = function(t) { + var via = this._getViaCoordinates(); + + var x = Math.pow(1-t,2)*this.from.x + (2*t*(1 - t))*via.x + Math.pow(t,2)*this.to.x; + var y = Math.pow(1-t,2)*this.from.y + (2*t*(1 - t))*via.y + Math.pow(t,2)*this.to.y; + + return {x:x,y:y}; } /** - * Get next token in the current dot file. - * The token and token type are available as token and tokenType + * This function uses binary search to look for the point where the bezier curve crosses the border of the node. + * + * @param from + * @param ctx + * @returns {*} + * @private */ - function getToken() { - tokenType = TOKENTYPE.NULL; - token = ''; - - // skip over whitespaces - while (c == ' ' || c == '\t' || c == '\n' || c == '\r') { // space, tab, enter - next(); + Edge.prototype._findBorderPosition = function(from,ctx) { + var maxIterations = 10; + var iteration = 0; + var low = 0; + var high = 1; + var pos,angle,distanceToBorder, distanceToNodes, difference; + var threshold = 0.2; + var node = this.to; + if (from == true) { + node = this.from; } - do { - var isComment = false; + while (low <= high && iteration < maxIterations) { + var middle = (low + high) * 0.5; - // skip comment - if (c == '#') { - // find the previous non-space character - var i = index - 1; - while (dot.charAt(i) == ' ' || dot.charAt(i) == '\t') { - i--; + pos = this._pointOnBezier(middle); + angle = Math.atan2((node.y - pos.y), (node.x - pos.x)); + distanceToBorder = node.distanceToBorder(ctx,angle); + distanceToNodes = Math.sqrt(Math.pow(pos.x-node.x,2) + Math.pow(pos.y-node.y,2)); + difference = distanceToBorder - distanceToNodes; + if (Math.abs(difference) < threshold) { + break; // found + } + else if (difference < 0) { // distance to nodes is larger than distance to border --> t needs to be bigger if we're looking at the to node. + if (from == false) { + low = middle; } - if (dot.charAt(i) == '\n' || dot.charAt(i) == '') { - // the # is at the start of a line, this is indeed a line comment - while (c != '' && c != '\n') { - next(); - } - isComment = true; + else { + high = middle; } } - if (c == '/' && nextPreview() == '/') { - // skip line comment - while (c != '' && c != '\n') { - next(); + else { + if (from == false) { + high = middle; } - isComment = true; - } - if (c == '/' && nextPreview() == '*') { - // skip block comment - while (c != '') { - if (c == '*' && nextPreview() == '/') { - // end of block comment found. skip these last two characters - next(); - next(); - break; - } - else { - next(); - } + else { + low = middle; } - isComment = true; } - // skip over whitespaces - while (c == ' ' || c == '\t' || c == '\n' || c == '\r') { // space, tab, enter - next(); - } + iteration++; } - while (isComment); + pos.t = middle; - // check for end of dot file - if (c == '') { - // token is still empty - tokenType = TOKENTYPE.DELIMITER; - return; - } + return pos; + }; - // check for delimiters consisting of 2 characters - var c2 = c + nextPreview(); - if (DELIMITERS[c2]) { - tokenType = TOKENTYPE.DELIMITER; - token = c2; - next(); - next(); - return; - } + /** + * Redraw a edge as a line with an arrow + * Draw this edge in the given canvas + * The 2d context of a HTML canvas can be retrieved by canvas.getContext("2d"); + * @param {CanvasRenderingContext2D} ctx + * @private + */ + Edge.prototype._drawArrow = function(ctx) { + // set style + ctx.strokeStyle = this._getColor(); + ctx.fillStyle = ctx.strokeStyle; + ctx.lineWidth = this._getLineWidth(); - // check for delimiters consisting of 1 character - if (DELIMITERS[c]) { - tokenType = TOKENTYPE.DELIMITER; - token = c; - next(); - return; - } + // set vars + var angle, length, arrowPos; - // check for an identifier (number or string) - // TODO: more precise parsing of numbers/strings (and the port separator ':') - if (isAlphaNumeric(c) || c == '-') { - token += c; - next(); + // if not connected to itself + if (this.from != this.to) { + // draw line + this._line(ctx); - while (isAlphaNumeric(c)) { - token += c; - next(); - } - if (token == 'false') { - token = false; // convert to boolean - } - else if (token == 'true') { - token = true; // convert to boolean + // draw arrow head + if (this.options.smoothCurves.enabled == true) { + var via = this._getViaCoordinates(); + arrowPos = this._findBorderPosition(false, ctx); + var guidePos = this._pointOnBezier(Math.max(0.0, arrowPos.t - 0.1)) + angle = Math.atan2((arrowPos.y - guidePos.y), (arrowPos.x - guidePos.x)); } - else if (!isNaN(Number(token))) { - token = Number(token); // convert to number + else { + angle = Math.atan2((this.to.y - this.from.y), (this.to.x - this.from.x)); + var dx = (this.to.x - this.from.x); + var dy = (this.to.y - this.from.y); + var edgeSegmentLength = Math.sqrt(dx * dx + dy * dy); + var toBorderDist = this.to.distanceToBorder(ctx, angle); + var toBorderPoint = (edgeSegmentLength - toBorderDist) / edgeSegmentLength; + + arrowPos = {}; + arrowPos.x = (1 - toBorderPoint) * this.from.x + toBorderPoint * this.to.x; + arrowPos.y = (1 - toBorderPoint) * this.from.y + toBorderPoint * this.to.y; } - tokenType = TOKENTYPE.IDENTIFIER; - return; - } - // check for a string enclosed by double quotes - if (c == '"') { - next(); - while (c != '' && (c != '"' || (c == '"' && nextPreview() == '"'))) { - token += c; - if (c == '"') { // skip the escape character - next(); + // draw arrow at the end of the line + length = (10 + 5 * this.options.width) * this.options.arrowScaleFactor; + ctx.arrow(arrowPos.x,arrowPos.y, angle, length); + ctx.fill(); + ctx.stroke(); + + // draw label + if (this.label) { + var point; + if (this.options.smoothCurves.enabled == true && via != null) { + point = this._pointOnBezier(0.5); } - next(); - } - if (c != '"') { - throw newSyntaxError('End of string " expected'); + else { + point = this._pointOnLine(0.5); + } + this._label(ctx, this.label, point.x, point.y); } - next(); - tokenType = TOKENTYPE.IDENTIFIER; - return; } + else { + // draw circle + var node = this.from; + var x, y, arrow; + var radius = 0.25 * Math.max(100,this.physics.springLength); + if (!node.width) { + node.resize(ctx); + } + if (node.width > node.height) { + x = node.x + node.width * 0.5; + y = node.y - radius; + arrow = { + x: x, + y: node.y, + angle: 0.9 * Math.PI + }; + } + else { + x = node.x + radius; + y = node.y - node.height * 0.5; + arrow = { + x: node.x, + y: y, + angle: 0.6 * Math.PI + }; + } + ctx.beginPath(); + // TODO: similarly, for a line without arrows, draw to the border of the nodes instead of the center + ctx.arc(x, y, radius, 0, 2 * Math.PI, false); + ctx.stroke(); - // something unknown is found, wrong characters, a syntax error - tokenType = TOKENTYPE.UNKNOWN; - while (c != '') { - token += c; - next(); + // draw all arrows + var length = (10 + 5 * this.options.width) * this.options.arrowScaleFactor; + ctx.arrow(arrow.x, arrow.y, arrow.angle, length); + ctx.fill(); + ctx.stroke(); + + // draw label + if (this.label) { + point = this._pointOnCircle(x, y, radius, 0.5); + this._label(ctx, this.label, point.x, point.y); + } } - throw new SyntaxError('Syntax error in part "' + chop(token, 30) + '"'); - } + }; /** - * Parse a graph. - * @returns {Object} graph + * Calculate the distance between a point (x3,y3) and a line segment from + * (x1,y1) to (x2,y2). + * http://stackoverflow.com/questions/849211/shortest-distancae-between-a-point-and-a-line-segment + * @param {number} x1 + * @param {number} y1 + * @param {number} x2 + * @param {number} y2 + * @param {number} x3 + * @param {number} y3 + * @private */ - function parseGraph() { - var graph = {}; - - first(); - getToken(); - - // optional strict keyword - if (token == 'strict') { - graph.strict = true; - getToken(); + Edge.prototype._getDistanceToEdge = function (x1,y1, x2,y2, x3,y3) { // x3,y3 is the point + var returnValue = 0; + if (this.from != this.to) { + if (this.options.smoothCurves.enabled == true) { + var xVia, yVia; + if (this.options.smoothCurves.enabled == true && this.options.smoothCurves.dynamic == true) { + xVia = this.via.x; + yVia = this.via.y; + } + else { + var via = this._getViaCoordinates(); + xVia = via.x; + yVia = via.y; + } + var minDistance = 1e9; + var distance; + var i,t,x,y, lastX, lastY; + for (i = 0; i < 10; i++) { + t = 0.1*i; + x = Math.pow(1-t,2)*x1 + (2*t*(1 - t))*xVia + Math.pow(t,2)*x2; + y = Math.pow(1-t,2)*y1 + (2*t*(1 - t))*yVia + Math.pow(t,2)*y2; + if (i > 0) { + distance = this._getDistanceToLine(lastX,lastY,x,y, x3,y3); + minDistance = distance < minDistance ? distance : minDistance; + } + lastX = x; lastY = y; + } + returnValue = minDistance; + } + else { + returnValue = this._getDistanceToLine(x1,y1,x2,y2,x3,y3); + } } - - // graph or digraph keyword - if (token == 'graph' || token == 'digraph') { - graph.type = token; - getToken(); + else { + var x, y, dx, dy; + var radius = 0.25 * this.physics.springLength; + var node = this.from; + if (node.width > node.height) { + x = node.x + 0.5 * node.width; + y = node.y - radius; + } + else { + x = node.x + radius; + y = node.y - 0.5 * node.height; + } + dx = x - x3; + dy = y - y3; + returnValue = Math.abs(Math.sqrt(dx*dx + dy*dy) - radius); } - // optional graph id - if (tokenType == TOKENTYPE.IDENTIFIER) { - graph.id = token; - getToken(); + if (this.labelDimensions.left < x3 && + this.labelDimensions.left + this.labelDimensions.width > x3 && + this.labelDimensions.top < y3 && + this.labelDimensions.top + this.labelDimensions.height > y3) { + return 0; } - - // open angle bracket - if (token != '{') { - throw newSyntaxError('Angle bracket { expected'); + else { + return returnValue; } - getToken(); + }; - // statements - parseStatements(graph); + Edge.prototype._getDistanceToLine = function(x1,y1,x2,y2,x3,y3) { + var px = x2-x1, + py = y2-y1, + something = px*px + py*py, + u = ((x3 - x1) * px + (y3 - y1) * py) / something; - // close angle bracket - if (token != '}') { - throw newSyntaxError('Angle bracket } expected'); + if (u > 1) { + u = 1; } - getToken(); - - // end of file - if (token !== '') { - throw newSyntaxError('End of file expected'); + else if (u < 0) { + u = 0; } - getToken(); - // remove temporary default properties - delete graph.node; - delete graph.edge; - delete graph.graph; + var x = x1 + u * px, + y = y1 + u * py, + dx = x - x3, + dy = y - y3; - return graph; - } + //# Note: If the actual distance does not matter, + //# if you only want to compare what this function + //# returns to other results of this function, you + //# can just return the squared distance instead + //# (i.e. remove the sqrt) to gain a little performance - /** - * Parse a list with statements. - * @param {Object} graph - */ - function parseStatements (graph) { - while (token !== '' && token != '}') { - parseStatement(graph); - if (token == ';') { - getToken(); - } - } - } + return Math.sqrt(dx*dx + dy*dy); + }; /** - * Parse a single statement. Can be a an attribute statement, node - * statement, a series of node statements and edge statements, or a - * parameter. - * @param {Object} graph + * This allows the zoom level of the network to influence the rendering + * + * @param scale */ - function parseStatement(graph) { - // parse subgraph - var subgraph = parseSubgraph(graph); - if (subgraph) { - // edge statements - parseEdge(graph, subgraph); + Edge.prototype.setScale = function(scale) { + this.networkScaleInv = 1.0/scale; + }; - return; - } - // parse an attribute statement - var attr = parseAttributeStatement(graph); - if (attr) { - return; - } + Edge.prototype.select = function() { + this.selected = true; + }; - // parse node - if (tokenType != TOKENTYPE.IDENTIFIER) { - throw newSyntaxError('Identifier expected'); - } - var id = token; // id can be a string or a number - getToken(); + Edge.prototype.unselect = function() { + this.selected = false; + }; - if (token == '=') { - // id statement - getToken(); - if (tokenType != TOKENTYPE.IDENTIFIER) { - throw newSyntaxError('Identifier expected'); - } - graph[id] = token; - getToken(); - // TODO: implement comma separated list with "a_list: ID=ID [','] [a_list] " + Edge.prototype.positionBezierNode = function() { + if (this.via !== null && this.from !== null && this.to !== null) { + this.via.x = 0.5 * (this.from.x + this.to.x); + this.via.y = 0.5 * (this.from.y + this.to.y); } - else { - parseNodeStatement(graph, id); + else if (this.via !== null) { + this.via.x = 0; + this.via.y = 0; } - } + }; /** - * Parse a subgraph - * @param {Object} graph parent graph object - * @return {Object | null} subgraph + * This function draws the control nodes for the manipulator. + * In order to enable this, only set the this.controlNodesEnabled to true. + * @param ctx */ - function parseSubgraph (graph) { - var subgraph = null; - - // optional subgraph keyword - if (token == 'subgraph') { - subgraph = {}; - subgraph.type = 'subgraph'; - getToken(); - - // optional graph id - if (tokenType == TOKENTYPE.IDENTIFIER) { - subgraph.id = token; - getToken(); + Edge.prototype._drawControlNodes = function(ctx) { + if (this.controlNodesEnabled == true) { + if (this.controlNodes.from === null && this.controlNodes.to === null) { + var nodeIdFrom = "edgeIdFrom:".concat(this.id); + var nodeIdTo = "edgeIdTo:".concat(this.id); + var constants = { + nodes:{group:'', radius:7, borderWidth:2, borderWidthSelected: 2}, + physics:{damping:0}, + clustering: {maxNodeSizeIncrements: 0 ,nodeScaling: {width:0, height: 0, radius:0}} + }; + this.controlNodes.from = new Node( + {id:nodeIdFrom, + shape:'dot', + color:{background:'#ff0000', border:'#3c3c3c', highlight: {background:'#07f968'}} + },{},{},constants); + this.controlNodes.to = new Node( + {id:nodeIdTo, + shape:'dot', + color:{background:'#ff0000', border:'#3c3c3c', highlight: {background:'#07f968'}} + },{},{},constants); } - } - // open angle bracket - if (token == '{') { - getToken(); - - if (!subgraph) { - subgraph = {}; + this.controlNodes.positions = {}; + if (this.controlNodes.from.selected == false) { + this.controlNodes.positions.from = this.getControlNodeFromPosition(ctx); + this.controlNodes.from.x = this.controlNodes.positions.from.x; + this.controlNodes.from.y = this.controlNodes.positions.from.y; } - subgraph.parent = graph; - subgraph.node = graph.node; - subgraph.edge = graph.edge; - subgraph.graph = graph.graph; - - // statements - parseStatements(subgraph); - - // close angle bracket - if (token != '}') { - throw newSyntaxError('Angle bracket } expected'); + if (this.controlNodes.to.selected == false) { + this.controlNodes.positions.to = this.getControlNodeToPosition(ctx); + this.controlNodes.to.x = this.controlNodes.positions.to.x; + this.controlNodes.to.y = this.controlNodes.positions.to.y; } - getToken(); - // remove temporary default properties - delete subgraph.node; - delete subgraph.edge; - delete subgraph.graph; - delete subgraph.parent; + this.controlNodes.from.draw(ctx); + this.controlNodes.to.draw(ctx); + } + else { + this.controlNodes = {from:null, to:null, positions:{}}; + } + }; - // register at the parent graph - if (!graph.subgraphs) { - graph.subgraphs = []; - } - graph.subgraphs.push(subgraph); + /** + * Enable control nodes. + * @private + */ + Edge.prototype._enableControlNodes = function() { + this.fromBackup = this.from; + this.toBackup = this.to; + this.controlNodesEnabled = true; + }; + + /** + * disable control nodes and remove from dynamicEdges from old node + * @private + */ + Edge.prototype._disableControlNodes = function() { + this.fromId = this.from.id; + this.toId = this.to.id; + if (this.fromId != this.fromBackup.id) { // from was changed, remove edge from old 'from' node dynamic edges + this.fromBackup.detachEdge(this); + } + else if (this.toId != this.toBackup.id) { // to was changed, remove edge from old 'to' node dynamic edges + this.toBackup.detachEdge(this); } - return subgraph; - } + this.fromBackup = null; + this.toBackup = null; + this.controlNodesEnabled = false; + }; + /** - * parse an attribute statement like "node [shape=circle fontSize=16]". - * Available keywords are 'node', 'edge', 'graph'. - * The previous list with default attributes will be replaced - * @param {Object} graph - * @returns {String | null} keyword Returns the name of the parsed attribute - * (node, edge, graph), or null if nothing - * is parsed. + * This checks if one of the control nodes is selected and if so, returns the control node object. Else it returns null. + * @param x + * @param y + * @returns {null} + * @private */ - function parseAttributeStatement (graph) { - // attribute statements - if (token == 'node') { - getToken(); + Edge.prototype._getSelectedControlNode = function(x,y) { + var positions = this.controlNodes.positions; + var fromDistance = Math.sqrt(Math.pow(x - positions.from.x,2) + Math.pow(y - positions.from.y,2)); + var toDistance = Math.sqrt(Math.pow(x - positions.to.x ,2) + Math.pow(y - positions.to.y ,2)); - // node attributes - graph.node = parseAttributeList(); - return 'node'; + if (fromDistance < 15) { + this.connectedNode = this.from; + this.from = this.controlNodes.from; + return this.controlNodes.from; } - else if (token == 'edge') { - getToken(); - - // edge attributes - graph.edge = parseAttributeList(); - return 'edge'; + else if (toDistance < 15) { + this.connectedNode = this.to; + this.to = this.controlNodes.to; + return this.controlNodes.to; } - else if (token == 'graph') { - getToken(); - - // graph attributes - graph.graph = parseAttributeList(); - return 'graph'; + else { + return null; } + }; - return null; - } /** - * parse a node statement - * @param {Object} graph - * @param {String | Number} id + * this resets the control nodes to their original position. + * @private */ - function parseNodeStatement(graph, id) { - // node statement - var node = { - id: id - }; - var attr = parseAttributeList(); - if (attr) { - node.attr = attr; + Edge.prototype._restoreControlNodes = function() { + if (this.controlNodes.from.selected == true) { + this.from = this.connectedNode; + this.connectedNode = null; + this.controlNodes.from.unselect(); } - addNode(graph, node); - - // edge statements - parseEdge(graph, id); - } + else if (this.controlNodes.to.selected == true) { + this.to = this.connectedNode; + this.connectedNode = null; + this.controlNodes.to.unselect(); + } + }; /** - * Parse an edge or a series of edges - * @param {Object} graph - * @param {String | Number} from Id of the from node + * this calculates the position of the control nodes on the edges of the parent nodes. + * + * @param ctx + * @returns {x: *, y: *} */ - function parseEdge(graph, from) { - while (token == '->' || token == '--') { - var to; - var type = token; - getToken(); + Edge.prototype.getControlNodeFromPosition = function(ctx) { + // draw arrow head + var controlnodeFromPos; + if (this.options.smoothCurves.enabled == true) { + controlnodeFromPos = this._findBorderPosition(true, ctx); + } + else { + var angle = Math.atan2((this.to.y - this.from.y), (this.to.x - this.from.x)); + var dx = (this.to.x - this.from.x); + var dy = (this.to.y - this.from.y); + var edgeSegmentLength = Math.sqrt(dx * dx + dy * dy); - var subgraph = parseSubgraph(graph); - if (subgraph) { - to = subgraph; - } - else { - if (tokenType != TOKENTYPE.IDENTIFIER) { - throw newSyntaxError('Identifier or subgraph expected'); - } - to = token; - addNode(graph, { - id: to - }); - getToken(); - } + var fromBorderDist = this.from.distanceToBorder(ctx, angle + Math.PI); + var fromBorderPoint = (edgeSegmentLength - fromBorderDist) / edgeSegmentLength; + controlnodeFromPos = {}; + controlnodeFromPos.x = (fromBorderPoint) * this.from.x + (1 - fromBorderPoint) * this.to.x; + controlnodeFromPos.y = (fromBorderPoint) * this.from.y + (1 - fromBorderPoint) * this.to.y; + } - // parse edge attributes - var attr = parseAttributeList(); + return controlnodeFromPos; + }; - // create edge - var edge = createEdge(graph, from, to, type, attr); - addEdge(graph, edge); + /** + * this calculates the position of the control nodes on the edges of the parent nodes. + * + * @param ctx + * @returns {{from: {x: number, y: number}, to: {x: *, y: *}}} + */ + Edge.prototype.getControlNodeToPosition = function(ctx) { + // draw arrow head + var controlnodeFromPos,controlnodeToPos; + if (this.options.smoothCurves.enabled == true) { + controlnodeToPos = this._findBorderPosition(false, ctx); + } + else { + var angle = Math.atan2((this.to.y - this.from.y), (this.to.x - this.from.x)); + var dx = (this.to.x - this.from.x); + var dy = (this.to.y - this.from.y); + var edgeSegmentLength = Math.sqrt(dx * dx + dy * dy); + var toBorderDist = this.to.distanceToBorder(ctx, angle); + var toBorderPoint = (edgeSegmentLength - toBorderDist) / edgeSegmentLength; - from = to; + controlnodeToPos = {}; + controlnodeToPos.x = (1 - toBorderPoint) * this.from.x + toBorderPoint * this.to.x; + controlnodeToPos.y = (1 - toBorderPoint) * this.from.y + toBorderPoint * this.to.y; } - } - /** - * Parse a set with attributes, - * for example [label="1.000", shape=solid] - * @return {Object | null} attr - */ - function parseAttributeList() { - var attr = null; + return controlnodeToPos; + }; - while (token == '[') { - getToken(); - attr = {}; - while (token !== '' && token != ']') { - if (tokenType != TOKENTYPE.IDENTIFIER) { - throw newSyntaxError('Attribute name expected'); - } - var name = token; + module.exports = Edge; - getToken(); - if (token != '=') { - throw newSyntaxError('Equal sign = expected'); - } - getToken(); +/***/ }, +/* 58 */ +/***/ function(module, exports, __webpack_require__) { - if (tokenType != TOKENTYPE.IDENTIFIER) { - throw newSyntaxError('Attribute value expected'); - } - var value = token; - setValue(attr, name, value); // name can be a path + /** + * 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 Popup(container, x, y, text, style) { + if (container) { + this.container = container; + } + else { + this.container = document.body; + } - getToken(); - if (token ==',') { - getToken(); + // x, y and text are optional, see if a style object was passed in their place + if (style === undefined) { + if (typeof x === "object") { + style = x; + x = undefined; + } else if (typeof text === "object") { + style = text; + text = undefined; + } else { + // for backwards compatibility, in case clients other than Network are creating Popup directly + style = { + fontColor: 'black', + fontSize: 14, // px + fontFace: 'verdana', + color: { + border: '#666', + background: '#FFFFC6' + } } } - - if (token != ']') { - throw newSyntaxError('Bracket ] expected'); - } - getToken(); } - return attr; - } + this.x = 0; + this.y = 0; + this.padding = 5; - /** - * Create a syntax error with extra information on current token and index. - * @param {String} message - * @returns {SyntaxError} err - */ - function newSyntaxError(message) { - return new SyntaxError(message + ', got "' + chop(token, 30) + '" (char ' + index + ')'); + if (x !== undefined && y !== undefined ) { + this.setPosition(x, y); + } + if (text !== undefined) { + this.setText(text); + } + + // create the frame + this.frame = document.createElement("div"); + var styleAttr = this.frame.style; + styleAttr.position = "absolute"; + styleAttr.visibility = "hidden"; + styleAttr.border = "1px solid " + style.color.border; + styleAttr.color = style.fontColor; + styleAttr.fontSize = style.fontSize + "px"; + styleAttr.fontFamily = style.fontFace; + styleAttr.padding = this.padding + "px"; + styleAttr.backgroundColor = style.color.background; + styleAttr.borderRadius = "3px"; + styleAttr.MozBorderRadius = "3px"; + styleAttr.WebkitBorderRadius = "3px"; + styleAttr.boxShadow = "3px 3px 10px rgba(128, 128, 128, 0.5)"; + styleAttr.whiteSpace = "nowrap"; + this.container.appendChild(this.frame); } /** - * Chop off text after a maximum length - * @param {String} text - * @param {Number} maxLength - * @returns {String} + * @param {number} x Horizontal position of the popup window + * @param {number} y Vertical position of the popup window */ - function chop (text, maxLength) { - return (text.length <= maxLength) ? text : (text.substr(0, 27) + '...'); - } + Popup.prototype.setPosition = function(x, y) { + this.x = parseInt(x); + this.y = parseInt(y); + }; /** - * Execute a function fn for each pair of elements in two arrays - * @param {Array | *} array1 - * @param {Array | *} array2 - * @param {function} fn + * Set the content for the popup window. This can be HTML code or text. + * @param {string | Element} content */ - function forEach2(array1, array2, fn) { - if (Array.isArray(array1)) { - array1.forEach(function (elem1) { - if (Array.isArray(array2)) { - array2.forEach(function (elem2) { - fn(elem1, elem2); - }); - } - else { - fn(elem1, array2); - } - }); + Popup.prototype.setText = function(content) { + if (content instanceof Element) { + this.frame.innerHTML = ''; + this.frame.appendChild(content); } else { - if (Array.isArray(array2)) { - array2.forEach(function (elem2) { - fn(array1, elem2); - }); - } - else { - fn(array1, array2); - } + this.frame.innerHTML = content; // string containing text or HTML } - } + }; /** - * Convert a string containing a graph in DOT language into a map containing - * with nodes and edges in the format of graph. - * @param {String} data Text containing a graph in DOT-notation - * @return {Object} graphData + * Show the popup window + * @param {boolean} show Optional. Show or hide the window */ - function DOTToGraph (data) { - // parse the DOT file - var dotData = parseDOT(data); - var graphData = { - nodes: [], - edges: [], - options: {} - }; - - // copy the nodes - if (dotData.nodes) { - dotData.nodes.forEach(function (dotNode) { - var graphNode = { - id: dotNode.id, - label: String(dotNode.label || dotNode.id) - }; - merge(graphNode, dotNode.attr); - if (graphNode.image) { - graphNode.shape = 'image'; - } - graphData.nodes.push(graphNode); - }); - } - - // copy the edges - if (dotData.edges) { - /** - * Convert an edge in DOT format to an edge with VisGraph format - * @param {Object} dotEdge - * @returns {Object} graphEdge - */ - var convertEdge = function (dotEdge) { - var graphEdge = { - from: dotEdge.from, - to: dotEdge.to - }; - merge(graphEdge, dotEdge.attr); - graphEdge.style = (dotEdge.type == '->') ? 'arrow' : 'line'; - return graphEdge; - } - - dotData.edges.forEach(function (dotEdge) { - var from, to; - if (dotEdge.from instanceof Object) { - from = dotEdge.from.nodes; - } - else { - from = { - id: dotEdge.from - } - } - - if (dotEdge.to instanceof Object) { - to = dotEdge.to.nodes; - } - else { - to = { - id: dotEdge.to - } - } - - if (dotEdge.from instanceof Object && dotEdge.from.edges) { - dotEdge.from.edges.forEach(function (subEdge) { - var graphEdge = convertEdge(subEdge); - graphData.edges.push(graphEdge); - }); - } - - forEach2(from, to, function (from, to) { - var subEdge = createEdge(graphData, from.id, to.id, dotEdge.type, dotEdge.attr); - var graphEdge = convertEdge(subEdge); - graphData.edges.push(graphEdge); - }); - - if (dotEdge.to instanceof Object && dotEdge.to.edges) { - dotEdge.to.edges.forEach(function (subEdge) { - var graphEdge = convertEdge(subEdge); - graphData.edges.push(graphEdge); - }); - } - }); - } - - // copy the options - if (dotData.attr) { - graphData.options = dotData.attr; + Popup.prototype.show = function (show) { + if (show === undefined) { + show = true; } - return graphData; - } - - // exports - exports.parseDOT = parseDOT; - exports.DOTToGraph = DOTToGraph; - + if (show) { + var height = this.frame.clientHeight; + var width = this.frame.clientWidth; + var maxHeight = this.frame.parentNode.clientHeight; + var maxWidth = this.frame.parentNode.clientWidth; -/***/ }, -/* 58 */ -/***/ function(module, exports, __webpack_require__) { + var top = (this.y - height); + if (top + height + this.padding > maxHeight) { + top = maxHeight - height - this.padding; + } + if (top < this.padding) { + top = this.padding; + } - - function parseGephi(gephiJSON, options) { - var edges = []; - var nodes = []; - this.options = { - edges: { - inheritColor: true - }, - nodes: { - allowedToMove: false, - parseColor: false + var left = this.x; + if (left + width + this.padding > maxWidth) { + left = maxWidth - width - this.padding; + } + if (left < this.padding) { + left = this.padding; } - }; - if (options !== undefined) { - this.options.nodes['allowedToMove'] = options.allowedToMove | false; - this.options.nodes['parseColor'] = options.parseColor | false; - this.options.edges['inheritColor'] = options.inheritColor | true; + this.frame.style.left = left + "px"; + this.frame.style.top = top + "px"; + this.frame.style.visibility = "visible"; } - - var gEdges = gephiJSON.edges; - var gNodes = gephiJSON.nodes; - for (var i = 0; i < gEdges.length; i++) { - var edge = {}; - var gEdge = gEdges[i]; - edge['id'] = gEdge.id; - edge['from'] = gEdge.source; - edge['to'] = gEdge.target; - edge['attributes'] = gEdge.attributes; - // edge['value'] = gEdge.attributes !== undefined ? gEdge.attributes.Weight : undefined; - // edge['width'] = edge['value'] !== undefined ? undefined : edgegEdge.size; - edge['color'] = gEdge.color; - edge['inheritColor'] = edge['color'] !== undefined ? false : this.options.inheritColor; - edges.push(edge); + else { + this.hide(); } + }; - for (var i = 0; i < gNodes.length; i++) { - var node = {}; - var gNode = gNodes[i]; - node['id'] = gNode.id; - node['attributes'] = gNode.attributes; - node['x'] = gNode.x; - node['y'] = gNode.y; - node['label'] = gNode.label; - if (this.options.nodes.parseColor == true) { - node['color'] = gNode.color; - } - else { - node['color'] = gNode.color !== undefined ? {background:gNode.color, border:gNode.color} : undefined; - } - node['radius'] = gNode.size; - node['allowedToMoveX'] = this.options.nodes.allowedToMove; - node['allowedToMoveY'] = this.options.nodes.allowedToMove; - nodes.push(node); - } + /** + * Hide the popup window + */ + Popup.prototype.hide = function () { + this.frame.style.visibility = "hidden"; + }; - return {nodes:nodes, edges:edges}; - } + module.exports = Popup; - exports.parseGephi = parseGephi; /***/ }, /* 59 */ @@ -31847,7 +31856,7 @@ return /******/ (function(modules) { // webpackBootstrap /***/ function(module, exports, __webpack_require__) { var util = __webpack_require__(1); - var Node = __webpack_require__(53); + var Node = __webpack_require__(56); /** * Creation of the SectorMixin var. @@ -32405,7 +32414,7 @@ return /******/ (function(modules) { // webpackBootstrap /* 66 */ /***/ function(module, exports, __webpack_require__) { - var Node = __webpack_require__(53); + var Node = __webpack_require__(56); /** * This function can be called from the _doInAllSectors function @@ -33120,8 +33129,8 @@ return /******/ (function(modules) { // webpackBootstrap /***/ function(module, exports, __webpack_require__) { var util = __webpack_require__(1); - var Node = __webpack_require__(53); - var Edge = __webpack_require__(52); + var Node = __webpack_require__(56); + var Edge = __webpack_require__(57); /** * clears the toolbar div element of children diff --git a/lib/network/Images.js b/lib/network/Images.js index 0d3135ca..70d83ed9 100644 --- a/lib/network/Images.js +++ b/lib/network/Images.js @@ -52,16 +52,25 @@ Images.prototype.load = function(url, brokenUrl) { me.callback(this); } } - else if (me.imageBroken[url] === true) { - console.error("Could not load brokenImage:", brokenUrl); - delete this.src; - if (me.callback) { - me.callback(this); - } - } else { - this.src = brokenUrl; - me.imageBroken[url] = true; + if (me.imageBroken[url] === true) { + if (this.src == brokenUrl) { + console.error("Could not load brokenImage:", brokenUrl); + delete this.src; + if (me.callback) { + me.callback(this); + } + } + else { + console.error("Could not load image:", url); + this.src = brokenUrl; + } + } + else { + console.error("Could not load image:", url); + this.src = brokenUrl; + me.imageBroken[url] = true; + } } }; From dcaa31214b965f262671c40bbaf6d695461921ee Mon Sep 17 00:00:00 2001 From: Alex de Mulder Date: Wed, 28 Jan 2015 11:23:53 +0100 Subject: [PATCH 2/7] rebuilt vis.js --- dist/vis.js | 53864 +++++++++++++++++++++++++------------------------- 1 file changed, 26934 insertions(+), 26930 deletions(-) diff --git a/dist/vis.js b/dist/vis.js index e32b2115..76672da9 100644 --- a/dist/vis.js +++ b/dist/vis.js @@ -83,67 +83,67 @@ return /******/ (function(modules) { // webpackBootstrap // utils exports.util = __webpack_require__(1); - exports.DOMutil = __webpack_require__(6); + exports.DOMutil = __webpack_require__(2); // data - exports.DataSet = __webpack_require__(7); - exports.DataView = __webpack_require__(9); - exports.Queue = __webpack_require__(8); + exports.DataSet = __webpack_require__(3); + exports.DataView = __webpack_require__(4); + exports.Queue = __webpack_require__(5); // Graph3d - exports.Graph3d = __webpack_require__(10); + exports.Graph3d = __webpack_require__(6); exports.graph3d = { - Camera: __webpack_require__(14), - Filter: __webpack_require__(15), - Point2d: __webpack_require__(13), - Point3d: __webpack_require__(12), - Slider: __webpack_require__(16), - StepNumber: __webpack_require__(17) + Camera: __webpack_require__(7), + Filter: __webpack_require__(8), + Point2d: __webpack_require__(9), + Point3d: __webpack_require__(10), + Slider: __webpack_require__(11), + StepNumber: __webpack_require__(12) }; // Timeline - exports.Timeline = __webpack_require__(18); - exports.Graph2d = __webpack_require__(42); + exports.Timeline = __webpack_require__(13); + exports.Graph2d = __webpack_require__(14); exports.timeline = { - DateUtil: __webpack_require__(24), - DataStep: __webpack_require__(45), - Range: __webpack_require__(21), - stack: __webpack_require__(28), - TimeStep: __webpack_require__(38), + DateUtil: __webpack_require__(15), + DataStep: __webpack_require__(16), + Range: __webpack_require__(17), + stack: __webpack_require__(18), + TimeStep: __webpack_require__(19), components: { items: { - Item: __webpack_require__(30), - BackgroundItem: __webpack_require__(34), - BoxItem: __webpack_require__(32), - PointItem: __webpack_require__(33), - RangeItem: __webpack_require__(29) + Item: __webpack_require__(31), + BackgroundItem: __webpack_require__(32), + BoxItem: __webpack_require__(33), + PointItem: __webpack_require__(34), + RangeItem: __webpack_require__(35) }, - Component: __webpack_require__(23), - CurrentTime: __webpack_require__(39), - CustomTime: __webpack_require__(41), - DataAxis: __webpack_require__(44), - GraphGroup: __webpack_require__(46), - Group: __webpack_require__(27), - BackgroundGroup: __webpack_require__(31), - ItemSet: __webpack_require__(26), - Legend: __webpack_require__(50), - LineGraph: __webpack_require__(43), - TimeAxis: __webpack_require__(37) + Component: __webpack_require__(20), + CurrentTime: __webpack_require__(21), + CustomTime: __webpack_require__(22), + DataAxis: __webpack_require__(23), + GraphGroup: __webpack_require__(24), + Group: __webpack_require__(25), + BackgroundGroup: __webpack_require__(26), + ItemSet: __webpack_require__(27), + Legend: __webpack_require__(28), + LineGraph: __webpack_require__(29), + TimeAxis: __webpack_require__(30) } }; // Network - exports.Network = __webpack_require__(51); + exports.Network = __webpack_require__(36); exports.network = { - Edge: __webpack_require__(57), - Groups: __webpack_require__(54), - Images: __webpack_require__(55), - Node: __webpack_require__(56), - Popup: __webpack_require__(58), - dotparser: __webpack_require__(52), - gephiParser: __webpack_require__(53) + Edge: __webpack_require__(37), + Groups: __webpack_require__(38), + Images: __webpack_require__(39), + Node: __webpack_require__(40), + Popup: __webpack_require__(41), + dotparser: __webpack_require__(42), + gephiParser: __webpack_require__(43) }; // Deprecated since v3.0.0 @@ -152,8 +152,8 @@ return /******/ (function(modules) { // webpackBootstrap }; // bundled external libraries - exports.moment = __webpack_require__(2); - exports.hammer = __webpack_require__(19); + exports.moment = __webpack_require__(44); + exports.hammer = __webpack_require__(45); /***/ }, @@ -164,7 +164,7 @@ return /******/ (function(modules) { // webpackBootstrap // first check if moment.js is already loaded in the browser window, if so, // use this instance. Else, load via commonjs. - var moment = __webpack_require__(2); + var moment = __webpack_require__(44); /** * Test whether given object is a number @@ -1396,28810 +1396,28475 @@ return /******/ (function(modules) { // webpackBootstrap /* 2 */ /***/ function(module, exports, __webpack_require__) { - // first check if moment.js is already loaded in the browser window, if so, - // use this instance. Else, load via commonjs. - module.exports = (typeof window !== 'undefined') && window['moment'] || __webpack_require__(3); + // DOM utility methods + + /** + * this prepares the JSON container for allocating SVG elements + * @param JSONcontainer + * @private + */ + exports.prepareElements = function(JSONcontainer) { + // cleanup the redundant svgElements; + for (var elementType in JSONcontainer) { + if (JSONcontainer.hasOwnProperty(elementType)) { + JSONcontainer[elementType].redundant = JSONcontainer[elementType].used; + JSONcontainer[elementType].used = []; + } + } + }; + + /** + * this cleans up all the unused SVG elements. By asking for the parentNode, we only need to supply the JSON container from + * which to remove the redundant elements. + * + * @param JSONcontainer + * @private + */ + exports.cleanupElements = function(JSONcontainer) { + // cleanup the redundant svgElements; + for (var elementType in JSONcontainer) { + if (JSONcontainer.hasOwnProperty(elementType)) { + if (JSONcontainer[elementType].redundant) { + for (var i = 0; i < JSONcontainer[elementType].redundant.length; i++) { + JSONcontainer[elementType].redundant[i].parentNode.removeChild(JSONcontainer[elementType].redundant[i]); + } + JSONcontainer[elementType].redundant = []; + } + } + } + }; + + /** + * Allocate or generate an SVG element if needed. Store a reference to it in the JSON container and draw it in the svgContainer + * the JSON container and the SVG container have to be supplied so other svg containers (like the legend) can use this. + * + * @param elementType + * @param JSONcontainer + * @param svgContainer + * @returns {*} + * @private + */ + exports.getSVGElement = function (elementType, JSONcontainer, svgContainer) { + var element; + // allocate SVG element, if it doesnt yet exist, create one. + if (JSONcontainer.hasOwnProperty(elementType)) { // this element has been created before + // check if there is an redundant element + if (JSONcontainer[elementType].redundant.length > 0) { + element = JSONcontainer[elementType].redundant[0]; + JSONcontainer[elementType].redundant.shift(); + } + else { + // create a new element and add it to the SVG + element = document.createElementNS('http://www.w3.org/2000/svg', elementType); + svgContainer.appendChild(element); + } + } + else { + // create a new element and add it to the SVG, also create a new object in the svgElements to keep track of it. + element = document.createElementNS('http://www.w3.org/2000/svg', elementType); + JSONcontainer[elementType] = {used: [], redundant: []}; + svgContainer.appendChild(element); + } + JSONcontainer[elementType].used.push(element); + return element; + }; + + + /** + * Allocate or generate an SVG element if needed. Store a reference to it in the JSON container and draw it in the svgContainer + * the JSON container and the SVG container have to be supplied so other svg containers (like the legend) can use this. + * + * @param elementType + * @param JSONcontainer + * @param DOMContainer + * @returns {*} + * @private + */ + exports.getDOMElement = function (elementType, JSONcontainer, DOMContainer, insertBefore) { + var element; + // allocate DOM element, if it doesnt yet exist, create one. + if (JSONcontainer.hasOwnProperty(elementType)) { // this element has been created before + // check if there is an redundant element + if (JSONcontainer[elementType].redundant.length > 0) { + element = JSONcontainer[elementType].redundant[0]; + JSONcontainer[elementType].redundant.shift(); + } + else { + // create a new element and add it to the SVG + element = document.createElement(elementType); + if (insertBefore !== undefined) { + DOMContainer.insertBefore(element, insertBefore); + } + else { + DOMContainer.appendChild(element); + } + } + } + else { + // create a new element and add it to the SVG, also create a new object in the svgElements to keep track of it. + element = document.createElement(elementType); + JSONcontainer[elementType] = {used: [], redundant: []}; + if (insertBefore !== undefined) { + DOMContainer.insertBefore(element, insertBefore); + } + else { + DOMContainer.appendChild(element); + } + } + JSONcontainer[elementType].used.push(element); + return element; + }; + + + + + /** + * draw a point object. this is a seperate function because it can also be called by the legend. + * The reason the JSONcontainer and the target SVG svgContainer have to be supplied is so the legend can use these functions + * as well. + * + * @param x + * @param y + * @param group + * @param JSONcontainer + * @param svgContainer + * @returns {*} + */ + exports.drawPoint = function(x, y, group, JSONcontainer, svgContainer) { + var point; + if (group.options.drawPoints.style == 'circle') { + point = exports.getSVGElement('circle',JSONcontainer,svgContainer); + point.setAttributeNS(null, "cx", x); + point.setAttributeNS(null, "cy", y); + point.setAttributeNS(null, "r", 0.5 * group.options.drawPoints.size); + } + else { + point = exports.getSVGElement('rect',JSONcontainer,svgContainer); + point.setAttributeNS(null, "x", x - 0.5*group.options.drawPoints.size); + point.setAttributeNS(null, "y", y - 0.5*group.options.drawPoints.size); + point.setAttributeNS(null, "width", group.options.drawPoints.size); + point.setAttributeNS(null, "height", group.options.drawPoints.size); + } + + if(group.options.drawPoints.styles !== undefined) { + point.setAttributeNS(null, "style", group.group.options.drawPoints.styles); + } + point.setAttributeNS(null, "class", group.className + " point"); + return point; + }; + /** + * draw a bar SVG element centered on the X coordinate + * + * @param x + * @param y + * @param className + */ + exports.drawBar = function (x, y, width, height, className, JSONcontainer, svgContainer) { + if (height != 0) { + if (height < 0) { + height *= -1; + y -= height; + } + var rect = exports.getSVGElement('rect',JSONcontainer, svgContainer); + rect.setAttributeNS(null, "x", x - 0.5 * width); + rect.setAttributeNS(null, "y", y); + rect.setAttributeNS(null, "width", width); + rect.setAttributeNS(null, "height", height); + rect.setAttributeNS(null, "class", className); + } + }; /***/ }, /* 3 */ /***/ function(module, exports, __webpack_require__) { - var __WEBPACK_AMD_DEFINE_RESULT__;/* WEBPACK VAR INJECTION */(function(global, module) {//! moment.js - //! version : 2.9.0 - //! authors : Tim Wood, Iskren Chernev, Moment.js contributors - //! license : MIT - //! momentjs.com + var util = __webpack_require__(1); + var Queue = __webpack_require__(5); - (function (undefined) { - /************************************ - Constants - ************************************/ + /** + * DataSet + * + * Usage: + * var dataSet = new DataSet({ + * fieldId: '_id', + * type: { + * // ... + * } + * }); + * + * dataSet.add(item); + * dataSet.add(data); + * dataSet.update(item); + * dataSet.update(data); + * dataSet.remove(id); + * dataSet.remove(ids); + * var data = dataSet.get(); + * var data = dataSet.get(id); + * var data = dataSet.get(ids); + * var data = dataSet.get(ids, options, data); + * dataSet.clear(); + * + * A data set can: + * - add/remove/update data + * - gives triggers upon changes in the data + * - can import/export data in various data formats + * + * @param {Array | DataTable} [data] Optional array with initial data + * @param {Object} [options] Available options: + * {String} fieldId Field name of the id in the + * items, 'id' by default. + * {Object. ['10', '00'] or '-1530' > ['-', '15', '30'] - parseTimezoneChunker = /([\+\-]|\d\d)/gi, + var subscribers = []; + if (event in this._subscribers) { + subscribers = subscribers.concat(this._subscribers[event]); + } + if ('*' in this._subscribers) { + subscribers = subscribers.concat(this._subscribers['*']); + } - // getter and setter names - proxyGettersAndSetters = 'Date|Hours|Minutes|Seconds|Milliseconds'.split('|'), - unitMillisecondFactors = { - 'Milliseconds' : 1, - 'Seconds' : 1e3, - 'Minutes' : 6e4, - 'Hours' : 36e5, - 'Days' : 864e5, - 'Months' : 2592e6, - 'Years' : 31536e6 - }, + for (var i = 0; i < subscribers.length; i++) { + var subscriber = subscribers[i]; + if (subscriber.callback) { + subscriber.callback(event, params, senderId || null); + } + } + }; - unitAliases = { - ms : 'millisecond', - s : 'second', - m : 'minute', - h : 'hour', - d : 'day', - D : 'date', - w : 'week', - W : 'isoWeek', - M : 'month', - Q : 'quarter', - y : 'year', - DDD : 'dayOfYear', - e : 'weekday', - E : 'isoWeekday', - gg: 'weekYear', - GG: 'isoWeekYear' - }, + /** + * Add data. + * Adding an item will fail when there already is an item with the same id. + * @param {Object | Array | DataTable} data + * @param {String} [senderId] Optional sender id + * @return {Array} addedIds Array with the ids of the added items + */ + DataSet.prototype.add = function (data, senderId) { + var addedIds = [], + id, + me = this; - camelFunctions = { - dayofyear : 'dayOfYear', - isoweekday : 'isoWeekday', - isoweek : 'isoWeek', - weekyear : 'weekYear', - isoweekyear : 'isoWeekYear' - }, + if (Array.isArray(data)) { + // Array + for (var i = 0, len = data.length; i < len; i++) { + id = me._addItem(data[i]); + addedIds.push(id); + } + } + else if (util.isDataTable(data)) { + // Google DataTable + var columns = this._getColumnNames(data); + for (var row = 0, rows = data.getNumberOfRows(); row < rows; row++) { + var item = {}; + for (var col = 0, cols = columns.length; col < cols; col++) { + var field = columns[col]; + item[field] = data.getValue(row, col); + } - // format function strings - formatFunctions = {}, + id = me._addItem(item); + addedIds.push(id); + } + } + else if (data instanceof Object) { + // Single item + id = me._addItem(data); + addedIds.push(id); + } + else { + throw new Error('Unknown dataType'); + } - // default relative time thresholds - relativeTimeThresholds = { - s: 45, // seconds to minute - m: 45, // minutes to hour - h: 22, // hours to day - d: 26, // days to month - M: 11 // months to year - }, - - // tokens to ordinalize and pad - ordinalizeTokens = 'DDD w W M D d'.split(' '), - paddedTokens = 'M D H h m s w W'.split(' '), - - formatTokenFunctions = { - M : function () { - return this.month() + 1; - }, - MMM : function (format) { - return this.localeData().monthsShort(this, format); - }, - MMMM : function (format) { - return this.localeData().months(this, format); - }, - D : function () { - return this.date(); - }, - DDD : function () { - return this.dayOfYear(); - }, - d : function () { - return this.day(); - }, - dd : function (format) { - return this.localeData().weekdaysMin(this, format); - }, - ddd : function (format) { - return this.localeData().weekdaysShort(this, format); - }, - dddd : function (format) { - return this.localeData().weekdays(this, format); - }, - w : function () { - return this.week(); - }, - W : function () { - return this.isoWeek(); - }, - YY : function () { - return leftZeroFill(this.year() % 100, 2); - }, - YYYY : function () { - return leftZeroFill(this.year(), 4); - }, - YYYYY : function () { - return leftZeroFill(this.year(), 5); - }, - YYYYYY : function () { - var y = this.year(), sign = y >= 0 ? '+' : '-'; - return sign + leftZeroFill(Math.abs(y), 6); - }, - gg : function () { - return leftZeroFill(this.weekYear() % 100, 2); - }, - gggg : function () { - return leftZeroFill(this.weekYear(), 4); - }, - ggggg : function () { - return leftZeroFill(this.weekYear(), 5); - }, - GG : function () { - return leftZeroFill(this.isoWeekYear() % 100, 2); - }, - GGGG : function () { - return leftZeroFill(this.isoWeekYear(), 4); - }, - GGGGG : function () { - return leftZeroFill(this.isoWeekYear(), 5); - }, - e : function () { - return this.weekday(); - }, - E : function () { - return this.isoWeekday(); - }, - a : function () { - return this.localeData().meridiem(this.hours(), this.minutes(), true); - }, - A : function () { - return this.localeData().meridiem(this.hours(), this.minutes(), false); - }, - H : function () { - return this.hours(); - }, - h : function () { - return this.hours() % 12 || 12; - }, - m : function () { - return this.minutes(); - }, - s : function () { - return this.seconds(); - }, - S : function () { - return toInt(this.milliseconds() / 100); - }, - SS : function () { - return leftZeroFill(toInt(this.milliseconds() / 10), 2); - }, - SSS : function () { - return leftZeroFill(this.milliseconds(), 3); - }, - SSSS : function () { - return leftZeroFill(this.milliseconds(), 3); - }, - Z : function () { - var a = this.utcOffset(), - b = '+'; - if (a < 0) { - a = -a; - b = '-'; - } - return b + leftZeroFill(toInt(a / 60), 2) + ':' + leftZeroFill(toInt(a) % 60, 2); - }, - ZZ : function () { - var a = this.utcOffset(), - b = '+'; - if (a < 0) { - a = -a; - b = '-'; - } - return b + leftZeroFill(toInt(a / 60), 2) + leftZeroFill(toInt(a) % 60, 2); - }, - z : function () { - return this.zoneAbbr(); - }, - zz : function () { - return this.zoneName(); - }, - x : function () { - return this.valueOf(); - }, - X : function () { - return this.unix(); - }, - Q : function () { - return this.quarter(); - } - }, - - deprecations = {}, - - lists = ['months', 'monthsShort', 'weekdays', 'weekdaysShort', 'weekdaysMin'], + if (addedIds.length) { + this._trigger('add', {items: addedIds}, senderId); + } - updateInProgress = false; + return addedIds; + }; - // Pick the first defined of two or three arguments. dfl comes from - // default. - function dfl(a, b, c) { - switch (arguments.length) { - case 2: return a != null ? a : b; - case 3: return a != null ? a : b != null ? b : c; - default: throw new Error('Implement me'); - } - } + /** + * Update existing items. When an item does not exist, it will be created + * @param {Object | Array | DataTable} data + * @param {String} [senderId] Optional sender id + * @return {Array} updatedIds The ids of the added or updated items + */ + DataSet.prototype.update = function (data, senderId) { + var addedIds = []; + var updatedIds = []; + var updatedData = []; + var me = this; + var fieldId = me._fieldId; - function hasOwnProp(a, b) { - return hasOwnProperty.call(a, b); + var addOrUpdate = function (item) { + var id = item[fieldId]; + if (me._data[id]) { + // update item + id = me._updateItem(item); + updatedIds.push(id); + updatedData.push(item); } - - function defaultParsingFlags() { - // We need to deep clone this object, and es5 standard is not very - // helpful. - return { - empty : false, - unusedTokens : [], - unusedInput : [], - overflow : -2, - charsLeftOver : 0, - nullInput : false, - invalidMonth : null, - invalidFormat : false, - userInvalidated : false, - iso: false - }; + else { + // add new item + id = me._addItem(item); + addedIds.push(id); } + }; - function printMsg(msg) { - if (moment.suppressDeprecationWarnings === false && - typeof console !== 'undefined' && console.warn) { - console.warn('Deprecation warning: ' + msg); - } + if (Array.isArray(data)) { + // Array + for (var i = 0, len = data.length; i < len; i++) { + addOrUpdate(data[i]); } + } + else if (util.isDataTable(data)) { + // Google DataTable + var columns = this._getColumnNames(data); + for (var row = 0, rows = data.getNumberOfRows(); row < rows; row++) { + var item = {}; + for (var col = 0, cols = columns.length; col < cols; col++) { + var field = columns[col]; + item[field] = data.getValue(row, col); + } - function deprecate(msg, fn) { - var firstTime = true; - return extend(function () { - if (firstTime) { - printMsg(msg); - firstTime = false; - } - return fn.apply(this, arguments); - }, fn); + addOrUpdate(item); } + } + else if (data instanceof Object) { + // Single item + addOrUpdate(data); + } + else { + throw new Error('Unknown dataType'); + } - function deprecateSimple(name, msg) { - if (!deprecations[name]) { - printMsg(msg); - deprecations[name] = true; - } - } + if (addedIds.length) { + this._trigger('add', {items: addedIds}, senderId); + } + if (updatedIds.length) { + this._trigger('update', {items: updatedIds, data: updatedData}, senderId); + } - function padToken(func, count) { - return function (a) { - return leftZeroFill(func.call(this, a), count); - }; - } - function ordinalizeToken(func, period) { - return function (a) { - return this.localeData().ordinal(func.call(this, a), period); - }; - } + return addedIds.concat(updatedIds); + }; - function monthDiff(a, b) { - // difference in months - var wholeMonthDiff = ((b.year() - a.year()) * 12) + (b.month() - a.month()), - // b is in (anchor - 1 month, anchor + 1 month) - anchor = a.clone().add(wholeMonthDiff, 'months'), - anchor2, adjust; + /** + * Get a data item or multiple items. + * + * Usage: + * + * get() + * get(options: Object) + * get(options: Object, data: Array | DataTable) + * + * get(id: Number | String) + * get(id: Number | String, options: Object) + * get(id: Number | String, options: Object, data: Array | DataTable) + * + * get(ids: Number[] | String[]) + * get(ids: Number[] | String[], options: Object) + * get(ids: Number[] | String[], options: Object, data: Array | DataTable) + * + * Where: + * + * {Number | String} id The id of an item + * {Number[] | String{}} ids An array with ids of items + * {Object} options An Object with options. Available options: + * {String} [returnType] Type of data to be + * returned. Can be 'DataTable' or 'Array' (default) + * {Object.} [type] + * {String[]} [fields] field names to be returned + * {function} [filter] filter items + * {String | function} [order] Order the items by + * a field name or custom sort function. + * {Array | DataTable} [data] If provided, items will be appended to this + * array or table. Required in case of Google + * DataTable. + * + * @throws Error + */ + DataSet.prototype.get = function (args) { + var me = this; - if (b - anchor < 0) { - anchor2 = a.clone().add(wholeMonthDiff - 1, 'months'); - // linear across the month - adjust = (b - anchor) / (anchor - anchor2); - } else { - anchor2 = a.clone().add(wholeMonthDiff + 1, 'months'); - // linear across the month - adjust = (b - anchor) / (anchor2 - anchor); - } + // parse the arguments + var id, ids, options, data; + var firstType = util.getType(arguments[0]); + if (firstType == 'String' || firstType == 'Number') { + // get(id [, options] [, data]) + id = arguments[0]; + options = arguments[1]; + data = arguments[2]; + } + else if (firstType == 'Array') { + // get(ids [, options] [, data]) + ids = arguments[0]; + options = arguments[1]; + data = arguments[2]; + } + else { + // get([, options] [, data]) + options = arguments[0]; + data = arguments[1]; + } - return -(wholeMonthDiff + adjust); - } + // determine the return type + var returnType; + if (options && options.returnType) { + var allowedValues = ["DataTable", "Array", "Object"]; + returnType = allowedValues.indexOf(options.returnType) == -1 ? "Array" : options.returnType; - while (ordinalizeTokens.length) { - i = ordinalizeTokens.pop(); - formatTokenFunctions[i + 'o'] = ordinalizeToken(formatTokenFunctions[i], i); + if (data && (returnType != util.getType(data))) { + throw new Error('Type of parameter "data" (' + util.getType(data) + ') ' + + 'does not correspond with specified options.type (' + options.type + ')'); } - while (paddedTokens.length) { - i = paddedTokens.pop(); - formatTokenFunctions[i + i] = padToken(formatTokenFunctions[i], 2); + if (returnType == 'DataTable' && !util.isDataTable(data)) { + throw new Error('Parameter "data" must be a DataTable ' + + 'when options.type is "DataTable"'); } - formatTokenFunctions.DDDD = padToken(formatTokenFunctions.DDD, 3); - + } + else if (data) { + returnType = (util.getType(data) == 'DataTable') ? 'DataTable' : 'Array'; + } + else { + returnType = 'Array'; + } - function meridiemFixWrap(locale, hour, meridiem) { - var isPm; + // build options + var type = options && options.type || this._options.type; + var filter = options && options.filter; + var items = [], item, itemId, i, len; - if (meridiem == null) { - // nothing to do - return hour; - } - if (locale.meridiemHour != null) { - return locale.meridiemHour(hour, meridiem); - } else if (locale.isPM != null) { - // Fallback - isPm = locale.isPM(meridiem); - if (isPm && hour < 12) { - hour += 12; - } - if (!isPm && hour === 12) { - hour = 0; - } - return hour; - } else { - // thie is not supposed to happen - return hour; - } + // convert items + if (id != undefined) { + // return a single item + item = me._getItem(id, type); + if (filter && !filter(item)) { + item = null; } - - /************************************ - Constructors - ************************************/ - - function Locale() { + } + else if (ids != undefined) { + // return a subset of items + for (i = 0, len = ids.length; i < len; i++) { + item = me._getItem(ids[i], type); + if (!filter || filter(item)) { + items.push(item); + } } - - // Moment prototype object - function Moment(config, skipOverflow) { - if (skipOverflow !== false) { - checkOverflow(config); - } - copyConfig(this, config); - this._d = new Date(+config._d); - // Prevent infinite loop in case updateOffset creates new moment - // objects. - if (updateInProgress === false) { - updateInProgress = true; - moment.updateOffset(this); - updateInProgress = false; + } + else { + // return all items + for (itemId in this._data) { + if (this._data.hasOwnProperty(itemId)) { + item = me._getItem(itemId, type); + if (!filter || filter(item)) { + items.push(item); } + } } + } - // Duration Constructor - function Duration(duration) { - var normalizedInput = normalizeObjectUnits(duration), - years = normalizedInput.year || 0, - quarters = normalizedInput.quarter || 0, - months = normalizedInput.month || 0, - weeks = normalizedInput.week || 0, - days = normalizedInput.day || 0, - hours = normalizedInput.hour || 0, - minutes = normalizedInput.minute || 0, - seconds = normalizedInput.second || 0, - milliseconds = normalizedInput.millisecond || 0; - - // representation for dateAddRemove - this._milliseconds = +milliseconds + - seconds * 1e3 + // 1000 - minutes * 6e4 + // 1000 * 60 - hours * 36e5; // 1000 * 60 * 60 - // Because of dateAddRemove treats 24 hours as different from a - // day when working around DST, we need to store them separately - this._days = +days + - weeks * 7; - // It is impossible translate months into days without knowing - // which months you are are talking about, so we have to store - // it separately. - this._months = +months + - quarters * 3 + - years * 12; - - this._data = {}; - - this._locale = moment.localeData(); + // order the results + if (options && options.order && id == undefined) { + this._sort(items, options.order); + } - this._bubble(); + // filter fields of the items + if (options && options.fields) { + var fields = options.fields; + if (id != undefined) { + item = this._filterFields(item, fields); } - - /************************************ - Helpers - ************************************/ - - - function extend(a, b) { - for (var i in b) { - if (hasOwnProp(b, i)) { - a[i] = b[i]; - } - } - - if (hasOwnProp(b, 'toString')) { - a.toString = b.toString; - } - - if (hasOwnProp(b, 'valueOf')) { - a.valueOf = b.valueOf; - } - - return a; + else { + for (i = 0, len = items.length; i < len; i++) { + items[i] = this._filterFields(items[i], fields); + } } + } - function copyConfig(to, from) { - var i, prop, val; - - if (typeof from._isAMomentObject !== 'undefined') { - to._isAMomentObject = from._isAMomentObject; - } - if (typeof from._i !== 'undefined') { - to._i = from._i; - } - if (typeof from._f !== 'undefined') { - to._f = from._f; - } - if (typeof from._l !== 'undefined') { - to._l = from._l; - } - if (typeof from._strict !== 'undefined') { - to._strict = from._strict; - } - if (typeof from._tzm !== 'undefined') { - to._tzm = from._tzm; - } - if (typeof from._isUTC !== 'undefined') { - to._isUTC = from._isUTC; - } - if (typeof from._offset !== 'undefined') { - to._offset = from._offset; - } - if (typeof from._pf !== 'undefined') { - to._pf = from._pf; - } - if (typeof from._locale !== 'undefined') { - to._locale = from._locale; - } - - if (momentProperties.length > 0) { - for (i in momentProperties) { - prop = momentProperties[i]; - val = from[prop]; - if (typeof val !== 'undefined') { - to[prop] = val; - } - } - } - - return to; + // return the results + if (returnType == 'DataTable') { + var columns = this._getColumnNames(data); + if (id != undefined) { + // append a single item to the data table + me._appendRow(data, columns, item); } - - function absRound(number) { - if (number < 0) { - return Math.ceil(number); - } else { - return Math.floor(number); - } + else { + // copy the items to the provided data table + for (i = 0; i < items.length; i++) { + me._appendRow(data, columns, items[i]); + } } - - // left zero fill a number - // see http://jsperf.com/left-zero-filling for performance comparison - function leftZeroFill(number, targetLength, forceSign) { - var output = '' + Math.abs(number), - sign = number >= 0; - - while (output.length < targetLength) { - output = '0' + output; + return data; + } + else if (returnType == "Object") { + var result = {}; + for (i = 0; i < items.length; i++) { + result[items[i].id] = items[i]; + } + return result; + } + else { + // return an array + if (id != undefined) { + // a single item + return item; + } + else { + // multiple items + if (data) { + // copy the items to the provided array + for (i = 0, len = items.length; i < len; i++) { + data.push(items[i]); } - return (sign ? (forceSign ? '+' : '') : '-') + output; + return data; + } + else { + // just return our array + return items; + } } + } + }; - function positiveMomentsDifference(base, other) { - var res = {milliseconds: 0, months: 0}; + /** + * Get ids of all items or from a filtered set of items. + * @param {Object} [options] An Object with options. Available options: + * {function} [filter] filter items + * {String | function} [order] Order the items by + * a field name or custom sort function. + * @return {Array} ids + */ + DataSet.prototype.getIds = function (options) { + var data = this._data, + filter = options && options.filter, + order = options && options.order, + type = options && options.type || this._options.type, + i, + len, + id, + item, + items, + ids = []; - res.months = other.month() - base.month() + - (other.year() - base.year()) * 12; - if (base.clone().add(res.months, 'M').isAfter(other)) { - --res.months; + if (filter) { + // get filtered items + if (order) { + // create ordered list + items = []; + for (id in data) { + if (data.hasOwnProperty(id)) { + item = this._getItem(id, type); + if (filter(item)) { + items.push(item); + } } + } - res.milliseconds = +other - +(base.clone().add(res.months, 'M')); + this._sort(items, order); - return res; + for (i = 0, len = items.length; i < len; i++) { + ids[i] = items[i][this._fieldId]; + } } - - function momentsDifference(base, other) { - var res; - other = makeAs(other, base); - if (base.isBefore(other)) { - res = positiveMomentsDifference(base, other); - } else { - res = positiveMomentsDifference(other, base); - res.milliseconds = -res.milliseconds; - res.months = -res.months; + else { + // create unordered list + for (id in data) { + if (data.hasOwnProperty(id)) { + item = this._getItem(id, type); + if (filter(item)) { + ids.push(item[this._fieldId]); + } } - - return res; + } } + } + else { + // get all items + if (order) { + // create an ordered list + items = []; + for (id in data) { + if (data.hasOwnProperty(id)) { + items.push(data[id]); + } + } - // TODO: remove 'name' arg after deprecation is removed - function createAdder(direction, name) { - return function (val, period) { - var dur, tmp; - //invert the arguments, but complain about it - if (period !== null && !isNaN(+period)) { - deprecateSimple(name, 'moment().' + name + '(period, number) is deprecated. Please use moment().' + name + '(number, period).'); - tmp = val; val = period; period = tmp; - } + this._sort(items, order); - val = typeof val === 'string' ? +val : val; - dur = moment.duration(val, period); - addOrSubtractDurationFromMoment(this, dur, direction); - return this; - }; + for (i = 0, len = items.length; i < len; i++) { + ids[i] = items[i][this._fieldId]; + } } - - function addOrSubtractDurationFromMoment(mom, duration, isAdding, updateOffset) { - var milliseconds = duration._milliseconds, - days = duration._days, - months = duration._months; - updateOffset = updateOffset == null ? true : updateOffset; - - if (milliseconds) { - mom._d.setTime(+mom._d + milliseconds * isAdding); - } - if (days) { - rawSetter(mom, 'Date', rawGetter(mom, 'Date') + days * isAdding); - } - if (months) { - rawMonthSetter(mom, rawGetter(mom, 'Month') + months * isAdding); - } - if (updateOffset) { - moment.updateOffset(mom, days || months); + else { + // create unordered list + for (id in data) { + if (data.hasOwnProperty(id)) { + item = data[id]; + ids.push(item[this._fieldId]); } + } } + } - // check if is an array - function isArray(input) { - return Object.prototype.toString.call(input) === '[object Array]'; - } - - function isDate(input) { - return Object.prototype.toString.call(input) === '[object Date]' || - input instanceof Date; - } + return ids; + }; - // compare two arrays, return the number of differences - function compareArrays(array1, array2, dontConvert) { - var len = Math.min(array1.length, array2.length), - lengthDiff = Math.abs(array1.length - array2.length), - diffs = 0, - i; - for (i = 0; i < len; i++) { - if ((dontConvert && array1[i] !== array2[i]) || - (!dontConvert && toInt(array1[i]) !== toInt(array2[i]))) { - diffs++; - } - } - return diffs + lengthDiff; - } + /** + * Returns the DataSet itself. Is overwritten for example by the DataView, + * which returns the DataSet it is connected to instead. + */ + DataSet.prototype.getDataSet = function () { + return this; + }; - function normalizeUnits(units) { - if (units) { - var lowered = units.toLowerCase().replace(/(.)s$/, '$1'); - units = unitAliases[units] || camelFunctions[lowered] || lowered; - } - return units; - } + /** + * Execute a callback function for every item in the dataset. + * @param {function} callback + * @param {Object} [options] Available options: + * {Object.} [type] + * {String[]} [fields] filter fields + * {function} [filter] filter items + * {String | function} [order] Order the items by + * a field name or custom sort function. + */ + DataSet.prototype.forEach = function (callback, options) { + var filter = options && options.filter, + type = options && options.type || this._options.type, + data = this._data, + item, + id; - function normalizeObjectUnits(inputObject) { - var normalizedInput = {}, - normalizedProp, - prop; + if (options && options.order) { + // execute forEach on ordered list + var items = this.get(options); - for (prop in inputObject) { - if (hasOwnProp(inputObject, prop)) { - normalizedProp = normalizeUnits(prop); - if (normalizedProp) { - normalizedInput[normalizedProp] = inputObject[prop]; - } - } + for (var i = 0, len = items.length; i < len; i++) { + item = items[i]; + id = item[this._fieldId]; + callback(item, id); + } + } + else { + // unordered + for (id in data) { + if (data.hasOwnProperty(id)) { + item = this._getItem(id, type); + if (!filter || filter(item)) { + callback(item, id); } - - return normalizedInput; + } } + } + }; - function makeList(field) { - var count, setter; + /** + * Map every item in the dataset. + * @param {function} callback + * @param {Object} [options] Available options: + * {Object.} [type] + * {String[]} [fields] filter fields + * {function} [filter] filter items + * {String | function} [order] Order the items by + * a field name or custom sort function. + * @return {Object[]} mappedItems + */ + DataSet.prototype.map = function (callback, options) { + var filter = options && options.filter, + type = options && options.type || this._options.type, + mappedItems = [], + data = this._data, + item; - if (field.indexOf('week') === 0) { - count = 7; - setter = 'day'; - } - else if (field.indexOf('month') === 0) { - count = 12; - setter = 'month'; - } - else { - return; - } + // convert and filter items + for (var id in data) { + if (data.hasOwnProperty(id)) { + item = this._getItem(id, type); + if (!filter || filter(item)) { + mappedItems.push(callback(item, id)); + } + } + } - moment[field] = function (format, index) { - var i, getter, - method = moment._locale[field], - results = []; + // order items + if (options && options.order) { + this._sort(mappedItems, options.order); + } - if (typeof format === 'number') { - index = format; - format = undefined; - } + return mappedItems; + }; - getter = function (i) { - var m = moment().utc().set(setter, i); - return method.call(moment._locale, m, format || ''); - }; + /** + * Filter the fields of an item + * @param {Object} item + * @param {String[]} fields Field names + * @return {Object} filteredItem + * @private + */ + DataSet.prototype._filterFields = function (item, fields) { + var filteredItem = {}; - if (index != null) { - return getter(index); - } - else { - for (i = 0; i < count; i++) { - results.push(getter(i)); - } - return results; - } - }; + for (var field in item) { + if (item.hasOwnProperty(field) && (fields.indexOf(field) != -1)) { + filteredItem[field] = item[field]; } + } - function toInt(argumentForCoercion) { - var coercedNumber = +argumentForCoercion, - value = 0; + return filteredItem; + }; - if (coercedNumber !== 0 && isFinite(coercedNumber)) { - if (coercedNumber >= 0) { - value = Math.floor(coercedNumber); - } else { - value = Math.ceil(coercedNumber); - } - } + /** + * Sort the provided array with items + * @param {Object[]} items + * @param {String | function} order A field name or custom sort function. + * @private + */ + DataSet.prototype._sort = function (items, order) { + if (util.isString(order)) { + // order by provided field name + var name = order; // field name + items.sort(function (a, b) { + var av = a[name]; + var bv = b[name]; + return (av > bv) ? 1 : ((av < bv) ? -1 : 0); + }); + } + else if (typeof order === 'function') { + // order by sort function + items.sort(order); + } + // TODO: extend order by an Object {field:String, direction:String} + // where direction can be 'asc' or 'desc' + else { + throw new TypeError('Order must be a function or a string'); + } + }; - return value; - } + /** + * Remove an object by pointer or by id + * @param {String | Number | Object | Array} id Object or id, or an array with + * objects or ids to be removed + * @param {String} [senderId] Optional sender id + * @return {Array} removedIds + */ + DataSet.prototype.remove = function (id, senderId) { + var removedIds = [], + i, len, removedId; - function daysInMonth(year, month) { - return new Date(Date.UTC(year, month + 1, 0)).getUTCDate(); + if (Array.isArray(id)) { + for (i = 0, len = id.length; i < len; i++) { + removedId = this._remove(id[i]); + if (removedId != null) { + removedIds.push(removedId); + } } - - function weeksInYear(year, dow, doy) { - return weekOfYear(moment([year, 11, 31 + dow - doy]), dow, doy).week; + } + else { + removedId = this._remove(id); + if (removedId != null) { + removedIds.push(removedId); } + } - function daysInYear(year) { - return isLeapYear(year) ? 366 : 365; - } + if (removedIds.length) { + this._trigger('remove', {items: removedIds}, senderId); + } - function isLeapYear(year) { - return (year % 4 === 0 && year % 100 !== 0) || year % 400 === 0; + return removedIds; + }; + + /** + * Remove an item by its id + * @param {Number | String | Object} id id or item + * @returns {Number | String | null} id + * @private + */ + DataSet.prototype._remove = function (id) { + if (util.isNumber(id) || util.isString(id)) { + if (this._data[id]) { + delete this._data[id]; + this.length--; + return id; + } + } + else if (id instanceof Object) { + var itemId = id[this._fieldId]; + if (itemId && this._data[itemId]) { + delete this._data[itemId]; + this.length--; + return itemId; } + } + return null; + }; - function checkOverflow(m) { - var overflow; - if (m._a && m._pf.overflow === -2) { - overflow = - m._a[MONTH] < 0 || m._a[MONTH] > 11 ? MONTH : - m._a[DATE] < 1 || m._a[DATE] > daysInMonth(m._a[YEAR], m._a[MONTH]) ? DATE : - m._a[HOUR] < 0 || m._a[HOUR] > 24 || - (m._a[HOUR] === 24 && (m._a[MINUTE] !== 0 || - m._a[SECOND] !== 0 || - m._a[MILLISECOND] !== 0)) ? HOUR : - m._a[MINUTE] < 0 || m._a[MINUTE] > 59 ? MINUTE : - m._a[SECOND] < 0 || m._a[SECOND] > 59 ? SECOND : - m._a[MILLISECOND] < 0 || m._a[MILLISECOND] > 999 ? MILLISECOND : - -1; + /** + * Clear the data + * @param {String} [senderId] Optional sender id + * @return {Array} removedIds The ids of all removed items + */ + DataSet.prototype.clear = function (senderId) { + var ids = Object.keys(this._data); - if (m._pf._overflowDayOfYear && (overflow < YEAR || overflow > DATE)) { - overflow = DATE; - } + this._data = {}; + this.length = 0; - m._pf.overflow = overflow; - } - } + this._trigger('remove', {items: ids}, senderId); - function isValid(m) { - if (m._isValid == null) { - m._isValid = !isNaN(m._d.getTime()) && - m._pf.overflow < 0 && - !m._pf.empty && - !m._pf.invalidMonth && - !m._pf.nullInput && - !m._pf.invalidFormat && - !m._pf.userInvalidated; + return ids; + }; - if (m._strict) { - m._isValid = m._isValid && - m._pf.charsLeftOver === 0 && - m._pf.unusedTokens.length === 0 && - m._pf.bigHour === undefined; - } - } - return m._isValid; - } + /** + * Find the item with maximum value of a specified field + * @param {String} field + * @return {Object | null} item Item containing max value, or null if no items + */ + DataSet.prototype.max = function (field) { + var data = this._data, + max = null, + maxField = null; - function normalizeLocale(key) { - return key ? key.toLowerCase().replace('_', '-') : key; + for (var id in data) { + if (data.hasOwnProperty(id)) { + var item = data[id]; + var itemField = item[field]; + if (itemField != null && (!max || itemField > maxField)) { + max = item; + maxField = itemField; + } } + } - // pick the locale from the array - // try ['en-au', 'en-gb'] as 'en-au', 'en-gb', 'en', as in move through the list trying each - // substring from most specific to least, but move to the next array item if it's a more specific variant than the current root - function chooseLocale(names) { - var i = 0, j, next, locale, split; + return max; + }; - while (i < names.length) { - split = normalizeLocale(names[i]).split('-'); - j = split.length; - next = normalizeLocale(names[i + 1]); - next = next ? next.split('-') : null; - while (j > 0) { - locale = loadLocale(split.slice(0, j).join('-')); - if (locale) { - return locale; - } - if (next && next.length >= j && compareArrays(split, next, true) >= j - 1) { - //the next array item is better than a shallower substring of this one - break; - } - j--; - } - i++; - } - return null; + /** + * Find the item with minimum value of a specified field + * @param {String} field + * @return {Object | null} item Item containing max value, or null if no items + */ + DataSet.prototype.min = function (field) { + var data = this._data, + min = null, + minField = null; + + for (var id in data) { + if (data.hasOwnProperty(id)) { + var item = data[id]; + var itemField = item[field]; + if (itemField != null && (!min || itemField < minField)) { + min = item; + minField = itemField; + } } + } - function loadLocale(name) { - var oldLocale = null; - if (!locales[name] && hasModule) { - try { - oldLocale = moment.locale(); - !(function webpackMissingModule() { var e = new Error("Cannot find module \"./locale\""); e.code = 'MODULE_NOT_FOUND'; throw e; }()); - // because defineLocale currently also sets the global locale, we want to undo that for lazy loaded locales - moment.locale(oldLocale); - } catch (e) { } + return min; + }; + + /** + * Find all distinct values of a specified field + * @param {String} field + * @return {Array} values Array containing all distinct values. If data items + * do not contain the specified field are ignored. + * The returned array is unordered. + */ + DataSet.prototype.distinct = function (field) { + var data = this._data; + var values = []; + var fieldType = this._options.type && this._options.type[field] || null; + var count = 0; + var i; + + for (var prop in data) { + if (data.hasOwnProperty(prop)) { + var item = data[prop]; + var value = item[field]; + var exists = false; + for (i = 0; i < count; i++) { + if (values[i] == value) { + exists = true; + break; } - return locales[name]; + } + if (!exists && (value !== undefined)) { + values[count] = value; + count++; + } } + } - // Return a moment from input, that is local/utc/utcOffset equivalent to - // model. - function makeAs(input, model) { - var res, diff; - if (model._isUTC) { - res = model.clone(); - diff = (moment.isMoment(input) || isDate(input) ? - +input : +moment(input)) - (+res); - // Use low-level api, because this fn is low-level api. - res._d.setTime(+res._d + diff); - moment.updateOffset(res, false); - return res; - } else { - return moment(input).local(); - } + if (fieldType) { + for (i = 0; i < values.length; i++) { + values[i] = util.convert(values[i], fieldType); } + } - /************************************ - Locale - ************************************/ + return values; + }; + /** + * Add a single item. Will fail when an item with the same id already exists. + * @param {Object} item + * @return {String} id + * @private + */ + DataSet.prototype._addItem = function (item) { + var id = item[this._fieldId]; - extend(Locale.prototype, { + if (id != undefined) { + // check whether this id is already taken + if (this._data[id]) { + // item already exists + throw new Error('Cannot add item: item with id ' + id + ' already exists'); + } + } + else { + // generate an id + id = util.randomUUID(); + item[this._fieldId] = id; + } - set : function (config) { - var prop, i; - for (i in config) { - prop = config[i]; - if (typeof prop === 'function') { - this[i] = prop; - } else { - this['_' + i] = prop; - } - } - // Lenient ordinal parsing accepts just a number in addition to - // number + (possibly) stuff coming from _ordinalParseLenient. - this._ordinalParseLenient = new RegExp(this._ordinalParse.source + '|' + /\d{1,2}/.source); - }, + var d = {}; + for (var field in item) { + if (item.hasOwnProperty(field)) { + var fieldType = this._type[field]; // type may be undefined + d[field] = util.convert(item[field], fieldType); + } + } + this._data[id] = d; + this.length++; - _months : 'January_February_March_April_May_June_July_August_September_October_November_December'.split('_'), - months : function (m) { - return this._months[m.month()]; - }, + return id; + }; - _monthsShort : 'Jan_Feb_Mar_Apr_May_Jun_Jul_Aug_Sep_Oct_Nov_Dec'.split('_'), - monthsShort : function (m) { - return this._monthsShort[m.month()]; - }, + /** + * Get an item. Fields can be converted to a specific type + * @param {String} id + * @param {Object.} [types] field types to convert + * @return {Object | null} item + * @private + */ + DataSet.prototype._getItem = function (id, types) { + var field, value; - monthsParse : function (monthName, format, strict) { - var i, mom, regex; + // get the item from the dataset + var raw = this._data[id]; + if (!raw) { + return null; + } - if (!this._monthsParse) { - this._monthsParse = []; - this._longMonthsParse = []; - this._shortMonthsParse = []; - } + // convert the items field types + var converted = {}; + if (types) { + for (field in raw) { + if (raw.hasOwnProperty(field)) { + value = raw[field]; + converted[field] = util.convert(value, types[field]); + } + } + } + else { + // no field types specified, no converting needed + for (field in raw) { + if (raw.hasOwnProperty(field)) { + value = raw[field]; + converted[field] = value; + } + } + } + return converted; + }; - for (i = 0; i < 12; i++) { - // make the regex if we don't have it already - mom = moment.utc([2000, i]); - if (strict && !this._longMonthsParse[i]) { - this._longMonthsParse[i] = new RegExp('^' + this.months(mom, '').replace('.', '') + '$', 'i'); - this._shortMonthsParse[i] = new RegExp('^' + this.monthsShort(mom, '').replace('.', '') + '$', 'i'); - } - if (!strict && !this._monthsParse[i]) { - regex = '^' + this.months(mom, '') + '|^' + this.monthsShort(mom, ''); - this._monthsParse[i] = new RegExp(regex.replace('.', ''), 'i'); - } - // test the regex - if (strict && format === 'MMMM' && this._longMonthsParse[i].test(monthName)) { - return i; - } else if (strict && format === 'MMM' && this._shortMonthsParse[i].test(monthName)) { - return i; - } else if (!strict && this._monthsParse[i].test(monthName)) { - return i; - } - } - }, + /** + * Update a single item: merge with existing item. + * Will fail when the item has no id, or when there does not exist an item + * with the same id. + * @param {Object} item + * @return {String} id + * @private + */ + DataSet.prototype._updateItem = function (item) { + var id = item[this._fieldId]; + if (id == undefined) { + throw new Error('Cannot update item: item has no id (item: ' + JSON.stringify(item) + ')'); + } + var d = this._data[id]; + if (!d) { + // item doesn't exist + throw new Error('Cannot update item: no item with id ' + id + ' found'); + } - _weekdays : 'Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday'.split('_'), - weekdays : function (m) { - return this._weekdays[m.day()]; - }, + // merge with current item + for (var field in item) { + if (item.hasOwnProperty(field)) { + var fieldType = this._type[field]; // type may be undefined + d[field] = util.convert(item[field], fieldType); + } + } - _weekdaysShort : 'Sun_Mon_Tue_Wed_Thu_Fri_Sat'.split('_'), - weekdaysShort : function (m) { - return this._weekdaysShort[m.day()]; - }, + return id; + }; - _weekdaysMin : 'Su_Mo_Tu_We_Th_Fr_Sa'.split('_'), - weekdaysMin : function (m) { - return this._weekdaysMin[m.day()]; - }, + /** + * Get an array with the column names of a Google DataTable + * @param {DataTable} dataTable + * @return {String[]} columnNames + * @private + */ + DataSet.prototype._getColumnNames = function (dataTable) { + var columns = []; + for (var col = 0, cols = dataTable.getNumberOfColumns(); col < cols; col++) { + columns[col] = dataTable.getColumnId(col) || dataTable.getColumnLabel(col); + } + return columns; + }; - weekdaysParse : function (weekdayName) { - var i, mom, regex; + /** + * Append an item as a row to the dataTable + * @param dataTable + * @param columns + * @param item + * @private + */ + DataSet.prototype._appendRow = function (dataTable, columns, item) { + var row = dataTable.addRow(); - if (!this._weekdaysParse) { - this._weekdaysParse = []; - } + for (var col = 0, cols = columns.length; col < cols; col++) { + var field = columns[col]; + dataTable.setValue(row, col, item[field]); + } + }; - for (i = 0; i < 7; i++) { - // make the regex if we don't have it already - if (!this._weekdaysParse[i]) { - mom = moment([2000, 1]).day(i); - regex = '^' + this.weekdays(mom, '') + '|^' + this.weekdaysShort(mom, '') + '|^' + this.weekdaysMin(mom, ''); - this._weekdaysParse[i] = new RegExp(regex.replace('.', ''), 'i'); - } - // test the regex - if (this._weekdaysParse[i].test(weekdayName)) { - return i; - } - } - }, + module.exports = DataSet; - _longDateFormat : { - LTS : 'h:mm:ss A', - LT : 'h:mm A', - L : 'MM/DD/YYYY', - LL : 'MMMM D, YYYY', - LLL : 'MMMM D, YYYY LT', - LLLL : 'dddd, MMMM D, YYYY LT' - }, - longDateFormat : function (key) { - var output = this._longDateFormat[key]; - if (!output && this._longDateFormat[key.toUpperCase()]) { - output = this._longDateFormat[key.toUpperCase()].replace(/MMMM|MM|DD|dddd/g, function (val) { - return val.slice(1); - }); - this._longDateFormat[key] = output; - } - return output; - }, - isPM : function (input) { - // IE8 Quirks Mode & IE7 Standards Mode do not allow accessing strings like arrays - // Using charAt should be more compatible. - return ((input + '').toLowerCase().charAt(0) === 'p'); - }, +/***/ }, +/* 4 */ +/***/ function(module, exports, __webpack_require__) { - _meridiemParse : /[ap]\.?m?\.?/i, - meridiem : function (hours, minutes, isLower) { - if (hours > 11) { - return isLower ? 'pm' : 'PM'; - } else { - return isLower ? 'am' : 'AM'; - } - }, + var util = __webpack_require__(1); + var DataSet = __webpack_require__(3); + /** + * DataView + * + * a dataview offers a filtered view on a dataset or an other dataview. + * + * @param {DataSet | DataView} data + * @param {Object} [options] Available options: see method get + * + * @constructor DataView + */ + function DataView (data, options) { + this._data = null; + this._ids = {}; // ids of the items currently in memory (just contains a boolean true) + this.length = 0; // number of items in the DataView + this._options = options || {}; + this._fieldId = 'id'; // name of the field containing id + this._subscribers = {}; // event subscribers - _calendar : { - sameDay : '[Today at] LT', - nextDay : '[Tomorrow at] LT', - nextWeek : 'dddd [at] LT', - lastDay : '[Yesterday at] LT', - lastWeek : '[Last] dddd [at] LT', - sameElse : 'L' - }, - calendar : function (key, mom, now) { - var output = this._calendar[key]; - return typeof output === 'function' ? output.apply(mom, [now]) : output; - }, + var me = this; + this.listener = function () { + me._onEvent.apply(me, arguments); + }; - _relativeTime : { - future : 'in %s', - past : '%s ago', - s : 'a few seconds', - m : 'a minute', - mm : '%d minutes', - h : 'an hour', - hh : '%d hours', - d : 'a day', - dd : '%d days', - M : 'a month', - MM : '%d months', - y : 'a year', - yy : '%d years' - }, + this.setData(data); + } - relativeTime : function (number, withoutSuffix, string, isFuture) { - var output = this._relativeTime[string]; - return (typeof output === 'function') ? - output(number, withoutSuffix, string, isFuture) : - output.replace(/%d/i, number); - }, + // TODO: implement a function .config() to dynamically update things like configured filter + // and trigger changes accordingly - pastFuture : function (diff, output) { - var format = this._relativeTime[diff > 0 ? 'future' : 'past']; - return typeof format === 'function' ? format(output) : format.replace(/%s/i, output); - }, + /** + * Set a data source for the view + * @param {DataSet | DataView} data + */ + DataView.prototype.setData = function (data) { + var ids, i, len; - ordinal : function (number) { - return this._ordinal.replace('%d', number); - }, - _ordinal : '%d', - _ordinalParse : /\d{1,2}/, + if (this._data) { + // unsubscribe from current dataset + if (this._data.unsubscribe) { + this._data.unsubscribe('*', this.listener); + } - preparse : function (string) { - return string; - }, + // trigger a remove of all items in memory + ids = []; + for (var id in this._ids) { + if (this._ids.hasOwnProperty(id)) { + ids.push(id); + } + } + this._ids = {}; + this.length = 0; + this._trigger('remove', {items: ids}); + } - postformat : function (string) { - return string; - }, + this._data = data; - week : function (mom) { - return weekOfYear(mom, this._week.dow, this._week.doy).week; - }, + if (this._data) { + // update fieldId + this._fieldId = this._options.fieldId || + (this._data && this._data.options && this._data.options.fieldId) || + 'id'; - _week : { - dow : 0, // Sunday is the first day of the week. - doy : 6 // The week that contains Jan 1st is the first week of the year. - }, + // trigger an add of all added items + ids = this._data.getIds({filter: this._options && this._options.filter}); + for (i = 0, len = ids.length; i < len; i++) { + id = ids[i]; + this._ids[id] = true; + } + this.length = ids.length; + this._trigger('add', {items: ids}); - firstDayOfWeek : function () { - return this._week.dow; - }, + // subscribe to new dataset + if (this._data.on) { + this._data.on('*', this.listener); + } + } + }; - firstDayOfYear : function () { - return this._week.doy; - }, - - _invalidDate: 'Invalid date', - invalidDate: function () { - return this._invalidDate; - } - }); + /** + * Get data from the data view + * + * Usage: + * + * get() + * get(options: Object) + * get(options: Object, data: Array | DataTable) + * + * get(id: Number) + * get(id: Number, options: Object) + * get(id: Number, options: Object, data: Array | DataTable) + * + * get(ids: Number[]) + * get(ids: Number[], options: Object) + * get(ids: Number[], options: Object, data: Array | DataTable) + * + * Where: + * + * {Number | String} id The id of an item + * {Number[] | String{}} ids An array with ids of items + * {Object} options An Object with options. Available options: + * {String} [type] Type of data to be returned. Can + * be 'DataTable' or 'Array' (default) + * {Object.} [convert] + * {String[]} [fields] field names to be returned + * {function} [filter] filter items + * {String | function} [order] Order the items by + * a field name or custom sort function. + * {Array | DataTable} [data] If provided, items will be appended to this + * array or table. Required in case of Google + * DataTable. + * @param args + */ + DataView.prototype.get = function (args) { + var me = this; - /************************************ - Formatting - ************************************/ + // parse the arguments + var ids, options, data; + var firstType = util.getType(arguments[0]); + if (firstType == 'String' || firstType == 'Number' || firstType == 'Array') { + // get(id(s) [, options] [, data]) + ids = arguments[0]; // can be a single id or an array with ids + options = arguments[1]; + data = arguments[2]; + } + else { + // get([, options] [, data]) + options = arguments[0]; + data = arguments[1]; + } + // extend the options with the default options and provided options + var viewOptions = util.extend({}, this._options, options); - function removeFormattingTokens(input) { - if (input.match(/\[[\s\S]/)) { - return input.replace(/^\[|\]$/g, ''); - } - return input.replace(/\\/g, ''); + // create a combined filter method when needed + if (this._options.filter && options && options.filter) { + viewOptions.filter = function (item) { + return me._options.filter(item) && options.filter(item); } + } - function makeFormatFunction(format) { - var array = format.match(formattingTokens), i, length; - - for (i = 0, length = array.length; i < length; i++) { - if (formatTokenFunctions[array[i]]) { - array[i] = formatTokenFunctions[array[i]]; - } else { - array[i] = removeFormattingTokens(array[i]); - } - } + // build up the call to the linked data set + var getArguments = []; + if (ids != undefined) { + getArguments.push(ids); + } + getArguments.push(viewOptions); + getArguments.push(data); - return function (mom) { - var output = ''; - for (i = 0; i < length; i++) { - output += array[i] instanceof Function ? array[i].call(mom, format) : array[i]; - } - return output; - }; - } + return this._data && this._data.get.apply(this._data, getArguments); + }; - // format date using native date object - function formatMoment(m, format) { - if (!m.isValid()) { - return m.localeData().invalidDate(); - } + /** + * Get ids of all items or from a filtered set of items. + * @param {Object} [options] An Object with options. Available options: + * {function} [filter] filter items + * {String | function} [order] Order the items by + * a field name or custom sort function. + * @return {Array} ids + */ + DataView.prototype.getIds = function (options) { + var ids; - format = expandFormat(format, m.localeData()); + if (this._data) { + var defaultFilter = this._options.filter; + var filter; - if (!formatFunctions[format]) { - formatFunctions[format] = makeFormatFunction(format); + if (options && options.filter) { + if (defaultFilter) { + filter = function (item) { + return defaultFilter(item) && options.filter(item); } - - return formatFunctions[format](m); + } + else { + filter = options.filter; + } + } + else { + filter = defaultFilter; } - function expandFormat(format, locale) { - var i = 5; + ids = this._data.getIds({ + filter: filter, + order: options && options.order + }); + } + else { + ids = []; + } - function replaceLongDateFormatTokens(input) { - return locale.longDateFormat(input) || input; - } + return ids; + }; - localFormattingTokens.lastIndex = 0; - while (i >= 0 && localFormattingTokens.test(format)) { - format = format.replace(localFormattingTokens, replaceLongDateFormatTokens); - localFormattingTokens.lastIndex = 0; - i -= 1; - } + /** + * Get the DataSet to which this DataView is connected. In case there is a chain + * of multiple DataViews, the root DataSet of this chain is returned. + * @return {DataSet} dataSet + */ + DataView.prototype.getDataSet = function () { + var dataSet = this; + while (dataSet instanceof DataView) { + dataSet = dataSet._data; + } + return dataSet || null; + }; - return format; - } + /** + * Event listener. Will propagate all events from the connected data set to + * the subscribers of the DataView, but will filter the items and only trigger + * when there are changes in the filtered data set. + * @param {String} event + * @param {Object | null} params + * @param {String} senderId + * @private + */ + DataView.prototype._onEvent = function (event, params, senderId) { + var i, len, id, item, + ids = params && params.items, + data = this._data, + added = [], + updated = [], + removed = []; + if (ids && data) { + switch (event) { + case 'add': + // filter the ids of the added items + for (i = 0, len = ids.length; i < len; i++) { + id = ids[i]; + item = this.get(id); + if (item) { + this._ids[id] = true; + added.push(id); + } + } - /************************************ - Parsing - ************************************/ + break; + case 'update': + // determine the event from the views viewpoint: an updated + // item can be added, updated, or removed from this view. + for (i = 0, len = ids.length; i < len; i++) { + id = ids[i]; + item = this.get(id); - // get the regex to find the next token - function getParseRegexForToken(token, config) { - var a, strict = config._strict; - switch (token) { - case 'Q': - return parseTokenOneDigit; - case 'DDDD': - return parseTokenThreeDigits; - case 'YYYY': - case 'GGGG': - case 'gggg': - return strict ? parseTokenFourDigits : parseTokenOneToFourDigits; - case 'Y': - case 'G': - case 'g': - return parseTokenSignedNumber; - case 'YYYYYY': - case 'YYYYY': - case 'GGGGG': - case 'ggggg': - return strict ? parseTokenSixDigits : parseTokenOneToSixDigits; - case 'S': - if (strict) { - return parseTokenOneDigit; + if (item) { + if (this._ids[id]) { + updated.push(id); } - /* falls through */ - case 'SS': - if (strict) { - return parseTokenTwoDigits; + else { + this._ids[id] = true; + added.push(id); } - /* falls through */ - case 'SSS': - if (strict) { - return parseTokenThreeDigits; + } + else { + if (this._ids[id]) { + delete this._ids[id]; + removed.push(id); } - /* falls through */ - case 'DDD': - return parseTokenOneToThreeDigits; - case 'MMM': - case 'MMMM': - case 'dd': - case 'ddd': - case 'dddd': - return parseTokenWord; - case 'a': - case 'A': - return config._locale._meridiemParse; - case 'x': - return parseTokenOffsetMs; - case 'X': - return parseTokenTimestampMs; - case 'Z': - case 'ZZ': - return parseTokenTimezone; - case 'T': - return parseTokenT; - case 'SSSS': - return parseTokenDigits; - case 'MM': - case 'DD': - case 'YY': - case 'GG': - case 'gg': - case 'HH': - case 'hh': - case 'mm': - case 'ss': - case 'ww': - case 'WW': - return strict ? parseTokenTwoDigits : parseTokenOneOrTwoDigits; - case 'M': - case 'D': - case 'd': - case 'H': - case 'h': - case 'm': - case 's': - case 'w': - case 'W': - case 'e': - case 'E': - return parseTokenOneOrTwoDigits; - case 'Do': - return strict ? config._locale._ordinalParse : config._locale._ordinalParseLenient; - default : - a = new RegExp(regexpEscape(unescapeFormat(token.replace('\\', '')), 'i')); - return a; + else { + // nothing interesting for me :-( + } + } } - } - function utcOffsetFromString(string) { - string = string || ''; - var possibleTzMatches = (string.match(parseTokenTimezone) || []), - tzChunk = possibleTzMatches[possibleTzMatches.length - 1] || [], - parts = (tzChunk + '').match(parseTimezoneChunker) || ['-', 0, 0], - minutes = +(parts[1] * 60) + toInt(parts[2]); + break; - return parts[0] === '+' ? minutes : -minutes; - } + case 'remove': + // filter the ids of the removed items + for (i = 0, len = ids.length; i < len; i++) { + id = ids[i]; + if (this._ids[id]) { + delete this._ids[id]; + removed.push(id); + } + } - // function to convert string input to date - function addTimeToArrayFromToken(token, input, config) { - var a, datePartArray = config._a; + break; + } - switch (token) { - // QUARTER - case 'Q': - if (input != null) { - datePartArray[MONTH] = (toInt(input) - 1) * 3; - } - break; - // MONTH - case 'M' : // fall through to MM - case 'MM' : - if (input != null) { - datePartArray[MONTH] = toInt(input) - 1; - } - break; - case 'MMM' : // fall through to MMMM - case 'MMMM' : - a = config._locale.monthsParse(input, token, config._strict); - // if we didn't find a month name, mark the date as invalid. - if (a != null) { - datePartArray[MONTH] = a; - } else { - config._pf.invalidMonth = input; - } - break; - // DAY OF MONTH - case 'D' : // fall through to DD - case 'DD' : - if (input != null) { - datePartArray[DATE] = toInt(input); - } - break; - case 'Do' : - if (input != null) { - datePartArray[DATE] = toInt(parseInt( - input.match(/\d{1,2}/)[0], 10)); - } - break; - // DAY OF YEAR - case 'DDD' : // fall through to DDDD - case 'DDDD' : - if (input != null) { - config._dayOfYear = toInt(input); - } + this.length += added.length - removed.length; - break; - // YEAR - case 'YY' : - datePartArray[YEAR] = moment.parseTwoDigitYear(input); - break; - case 'YYYY' : - case 'YYYYY' : - case 'YYYYYY' : - datePartArray[YEAR] = toInt(input); - break; - // AM / PM - case 'a' : // fall through to A - case 'A' : - config._meridiem = input; - // config._isPm = config._locale.isPM(input); - break; - // HOUR - case 'h' : // fall through to hh - case 'hh' : - config._pf.bigHour = true; - /* falls through */ - case 'H' : // fall through to HH - case 'HH' : - datePartArray[HOUR] = toInt(input); - break; - // MINUTE - case 'm' : // fall through to mm - case 'mm' : - datePartArray[MINUTE] = toInt(input); - break; - // SECOND - case 's' : // fall through to ss - case 'ss' : - datePartArray[SECOND] = toInt(input); - break; - // MILLISECOND - case 'S' : - case 'SS' : - case 'SSS' : - case 'SSSS' : - datePartArray[MILLISECOND] = toInt(('0.' + input) * 1000); - break; - // UNIX OFFSET (MILLISECONDS) - case 'x': - config._d = new Date(toInt(input)); - break; - // UNIX TIMESTAMP WITH MS - case 'X': - config._d = new Date(parseFloat(input) * 1000); - break; - // TIMEZONE - case 'Z' : // fall through to ZZ - case 'ZZ' : - config._useUTC = true; - config._tzm = utcOffsetFromString(input); - break; - // WEEKDAY - human - case 'dd': - case 'ddd': - case 'dddd': - a = config._locale.weekdaysParse(input); - // if we didn't get a weekday name, mark the date as invalid - if (a != null) { - config._w = config._w || {}; - config._w['d'] = a; - } else { - config._pf.invalidWeekday = input; - } - break; - // WEEK, WEEK DAY - numeric - case 'w': - case 'ww': - case 'W': - case 'WW': - case 'd': - case 'e': - case 'E': - token = token.substr(0, 1); - /* falls through */ - case 'gggg': - case 'GGGG': - case 'GGGGG': - token = token.substr(0, 2); - if (input) { - config._w = config._w || {}; - config._w[token] = toInt(input); - } - break; - case 'gg': - case 'GG': - config._w = config._w || {}; - config._w[token] = moment.parseTwoDigitYear(input); - } + if (added.length) { + this._trigger('add', {items: added}, senderId); + } + if (updated.length) { + this._trigger('update', {items: updated}, senderId); } + if (removed.length) { + this._trigger('remove', {items: removed}, senderId); + } + } + }; - function dayOfYearFromWeekInfo(config) { - var w, weekYear, week, weekday, dow, doy, temp; + // copy subscription functionality from DataSet + DataView.prototype.on = DataSet.prototype.on; + DataView.prototype.off = DataSet.prototype.off; + DataView.prototype._trigger = DataSet.prototype._trigger; - w = config._w; - if (w.GG != null || w.W != null || w.E != null) { - dow = 1; - doy = 4; + // TODO: make these functions deprecated (replaced with `on` and `off` since version 0.5) + DataView.prototype.subscribe = DataView.prototype.on; + DataView.prototype.unsubscribe = DataView.prototype.off; - // TODO: We need to take the current isoWeekYear, but that depends on - // how we interpret now (local, utc, fixed offset). So create - // a now version of current config (take local/utc/offset flags, and - // create now). - weekYear = dfl(w.GG, config._a[YEAR], weekOfYear(moment(), 1, 4).year); - week = dfl(w.W, 1); - weekday = dfl(w.E, 1); - } else { - dow = config._locale._week.dow; - doy = config._locale._week.doy; + module.exports = DataView; - weekYear = dfl(w.gg, config._a[YEAR], weekOfYear(moment(), dow, doy).year); - week = dfl(w.w, 1); +/***/ }, +/* 5 */ +/***/ function(module, exports, __webpack_require__) { - if (w.d != null) { - // weekday -- low day numbers are considered next week - weekday = w.d; - if (weekday < dow) { - ++week; - } - } else if (w.e != null) { - // local weekday -- counting starts from begining of week - weekday = w.e + dow; - } else { - // default to begining of week - weekday = dow; - } - } - temp = dayOfYearFromWeeks(weekYear, week, weekday, doy, dow); + /** + * A queue + * @param {Object} options + * Available options: + * - delay: number When provided, the queue will be flushed + * automatically after an inactivity of this delay + * in milliseconds. + * Default value is null. + * - max: number When the queue exceeds the given maximum number + * of entries, the queue is flushed automatically. + * Default value of max is Infinity. + * @constructor + */ + function Queue(options) { + // options + this.delay = null; + this.max = Infinity; - config._a[YEAR] = temp.year; - config._dayOfYear = temp.dayOfYear; - } + // properties + this._queue = []; + this._timeout = null; + this._extended = null; - // convert an array to a date. - // the array should mirror the parameters below - // note: all values past the year are optional and will default to the lowest possible value. - // [year, month, day , hour, minute, second, millisecond] - function dateFromConfig(config) { - var i, date, input = [], currentDate, yearToUse; + this.setOptions(options); + } - if (config._d) { - return; - } + /** + * Update the configuration of the queue + * @param {Object} options + * Available options: + * - delay: number When provided, the queue will be flushed + * automatically after an inactivity of this delay + * in milliseconds. + * Default value is null. + * - max: number When the queue exceeds the given maximum number + * of entries, the queue is flushed automatically. + * Default value of max is Infinity. + * @param options + */ + Queue.prototype.setOptions = function (options) { + if (options && typeof options.delay !== 'undefined') { + this.delay = options.delay; + } + if (options && typeof options.max !== 'undefined') { + this.max = options.max; + } - currentDate = currentDateArray(config); + this._flushIfNeeded(); + }; - //compute day of the year from weeks and weekdays - if (config._w && config._a[DATE] == null && config._a[MONTH] == null) { - dayOfYearFromWeekInfo(config); - } + /** + * Extend an object with queuing functionality. + * The object will be extended with a function flush, and the methods provided + * in options.replace will be replaced with queued ones. + * @param {Object} object + * @param {Object} options + * Available options: + * - replace: Array. + * A list with method names of the methods + * on the object to be replaced with queued ones. + * - delay: number When provided, the queue will be flushed + * automatically after an inactivity of this delay + * in milliseconds. + * Default value is null. + * - max: number When the queue exceeds the given maximum number + * of entries, the queue is flushed automatically. + * Default value of max is Infinity. + * @return {Queue} Returns the created queue + */ + Queue.extend = function (object, options) { + var queue = new Queue(options); - //if the day of the year is set, figure out what it is - if (config._dayOfYear) { - yearToUse = dfl(config._a[YEAR], currentDate[YEAR]); + if (object.flush !== undefined) { + throw new Error('Target object already has a property flush'); + } + object.flush = function () { + queue.flush(); + }; - if (config._dayOfYear > daysInYear(yearToUse)) { - config._pf._overflowDayOfYear = true; - } + var methods = [{ + name: 'flush', + original: undefined + }]; - date = makeUTCDate(yearToUse, 0, config._dayOfYear); - config._a[MONTH] = date.getUTCMonth(); - config._a[DATE] = date.getUTCDate(); - } + if (options && options.replace) { + for (var i = 0; i < options.replace.length; i++) { + var name = options.replace[i]; + methods.push({ + name: name, + original: object[name] + }); + queue.replace(object, name); + } + } - // Default to current date. - // * if no year, month, day of month are given, default to today - // * if day of month is given, default month and year - // * if month is given, default only year - // * if year is given, don't default anything - for (i = 0; i < 3 && config._a[i] == null; ++i) { - config._a[i] = input[i] = currentDate[i]; - } + queue._extended = { + object: object, + methods: methods + }; - // Zero out whatever was not defaulted, including time - for (; i < 7; i++) { - config._a[i] = input[i] = (config._a[i] == null) ? (i === 2 ? 1 : 0) : config._a[i]; - } + return queue; + }; - // Check for 24:00:00.000 - if (config._a[HOUR] === 24 && - config._a[MINUTE] === 0 && - config._a[SECOND] === 0 && - config._a[MILLISECOND] === 0) { - config._nextDay = true; - config._a[HOUR] = 0; - } + /** + * Destroy the queue. The queue will first flush all queued actions, and in + * case it has extended an object, will restore the original object. + */ + Queue.prototype.destroy = function () { + this.flush(); - config._d = (config._useUTC ? makeUTCDate : makeDate).apply(null, input); - // Apply timezone offset from input. The actual utcOffset can be changed - // with parseZone. - if (config._tzm != null) { - config._d.setUTCMinutes(config._d.getUTCMinutes() - config._tzm); - } + if (this._extended) { + var object = this._extended.object; + var methods = this._extended.methods; + for (var i = 0; i < methods.length; i++) { + var method = methods[i]; + if (method.original) { + object[method.name] = method.original; + } + else { + delete object[method.name]; + } + } + this._extended = null; + } + }; - if (config._nextDay) { - config._a[HOUR] = 24; - } + /** + * Replace a method on an object with a queued version + * @param {Object} object Object having the method + * @param {string} method The method name + */ + Queue.prototype.replace = function(object, method) { + var me = this; + var original = object[method]; + if (!original) { + throw new Error('Method ' + method + ' undefined'); + } + + object[method] = function () { + // create an Array with the arguments + var args = []; + for (var i = 0; i < arguments.length; i++) { + args[i] = arguments[i]; } - function dateFromObject(config) { - var normalizedInput; + // add this call to the queue + me.queue({ + args: args, + fn: original, + context: this + }); + }; + }; - if (config._d) { - return; - } + /** + * Queue a call + * @param {function | {fn: function, args: Array} | {fn: function, args: Array, context: Object}} entry + */ + Queue.prototype.queue = function(entry) { + if (typeof entry === 'function') { + this._queue.push({fn: entry}); + } + else { + this._queue.push(entry); + } - normalizedInput = normalizeObjectUnits(config._i); - config._a = [ - normalizedInput.year, - normalizedInput.month, - normalizedInput.day || normalizedInput.date, - normalizedInput.hour, - normalizedInput.minute, - normalizedInput.second, - normalizedInput.millisecond - ]; + this._flushIfNeeded(); + }; - dateFromConfig(config); - } + /** + * Check whether the queue needs to be flushed + * @private + */ + Queue.prototype._flushIfNeeded = function () { + // flush when the maximum is exceeded. + if (this._queue.length > this.max) { + this.flush(); + } - function currentDateArray(config) { - var now = new Date(); - if (config._useUTC) { - return [ - now.getUTCFullYear(), - now.getUTCMonth(), - now.getUTCDate() - ]; - } else { - return [now.getFullYear(), now.getMonth(), now.getDate()]; - } - } + // flush after a period of inactivity when a delay is configured + clearTimeout(this._timeout); + if (this.queue.length > 0 && typeof this.delay === 'number') { + var me = this; + this._timeout = setTimeout(function () { + me.flush(); + }, this.delay); + } + }; - // date from string and format string - function makeDateFromStringAndFormat(config) { - if (config._f === moment.ISO_8601) { - parseISO(config); - return; - } + /** + * Flush all queued calls + */ + Queue.prototype.flush = function () { + while (this._queue.length > 0) { + var entry = this._queue.shift(); + entry.fn.apply(entry.context || entry.fn, entry.args || []); + } + }; - config._a = []; - config._pf.empty = true; + module.exports = Queue; - // This array is used to make a Date, either with `new Date` or `Date.UTC` - var string = '' + config._i, - i, parsedInput, tokens, token, skipped, - stringLength = string.length, - totalParsedInputLength = 0; - tokens = expandFormat(config._f, config._locale).match(formattingTokens) || []; +/***/ }, +/* 6 */ +/***/ function(module, exports, __webpack_require__) { - for (i = 0; i < tokens.length; i++) { - token = tokens[i]; - parsedInput = (string.match(getParseRegexForToken(token, config)) || [])[0]; - if (parsedInput) { - skipped = string.substr(0, string.indexOf(parsedInput)); - if (skipped.length > 0) { - config._pf.unusedInput.push(skipped); - } - string = string.slice(string.indexOf(parsedInput) + parsedInput.length); - totalParsedInputLength += parsedInput.length; - } - // don't parse if it's not a known token - if (formatTokenFunctions[token]) { - if (parsedInput) { - config._pf.empty = false; - } - else { - config._pf.unusedTokens.push(token); - } - addTimeToArrayFromToken(token, parsedInput, config); - } - else if (config._strict && !parsedInput) { - config._pf.unusedTokens.push(token); - } - } + var Emitter = __webpack_require__(56); + var DataSet = __webpack_require__(3); + var DataView = __webpack_require__(4); + var util = __webpack_require__(1); + var Point3d = __webpack_require__(10); + var Point2d = __webpack_require__(9); + var Camera = __webpack_require__(7); + var Filter = __webpack_require__(8); + var Slider = __webpack_require__(11); + var StepNumber = __webpack_require__(12); - // add remaining unparsed input length to the string - config._pf.charsLeftOver = stringLength - totalParsedInputLength; - if (string.length > 0) { - config._pf.unusedInput.push(string); - } + /** + * @constructor Graph3d + * Graph3d displays data in 3d. + * + * Graph3d is developed in javascript as a Google Visualization Chart. + * + * @param {Element} container The DOM element in which the Graph3d will + * be created. Normally a div element. + * @param {DataSet | DataView | Array} [data] + * @param {Object} [options] + */ + function Graph3d(container, data, options) { + if (!(this instanceof Graph3d)) { + throw new SyntaxError('Constructor must be called with the new operator'); + } - // clear _12h flag if hour is <= 12 - if (config._pf.bigHour === true && config._a[HOUR] <= 12) { - config._pf.bigHour = undefined; - } - // handle meridiem - config._a[HOUR] = meridiemFixWrap(config._locale, config._a[HOUR], - config._meridiem); - dateFromConfig(config); - checkOverflow(config); - } + // create variables and set default values + this.containerElement = container; + this.width = '400px'; + this.height = '400px'; + this.margin = 10; // px + this.defaultXCenter = '55%'; + this.defaultYCenter = '50%'; - function unescapeFormat(s) { - return s.replace(/\\(\[)|\\(\])|\[([^\]\[]*)\]|\\(.)/g, function (matched, p1, p2, p3, p4) { - return p1 || p2 || p3 || p4; - }); - } + this.xLabel = 'x'; + this.yLabel = 'y'; + this.zLabel = 'z'; - // Code from http://stackoverflow.com/questions/3561493/is-there-a-regexp-escape-function-in-javascript - function regexpEscape(s) { - return s.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&'); - } + var passValueFn = function(v) { return v; }; + this.xValueLabel = passValueFn; + this.yValueLabel = passValueFn; + this.zValueLabel = passValueFn; + + this.filterLabel = 'time'; + this.legendLabel = 'value'; - // date from string and array of format strings - function makeDateFromStringAndArray(config) { - var tempConfig, - bestMoment, + this.style = Graph3d.STYLE.DOT; + this.showPerspective = true; + this.showGrid = true; + this.keepAspectRatio = true; + this.showShadow = false; + this.showGrayBottom = false; // TODO: this does not work correctly + this.showTooltip = false; + this.verticalRatio = 0.5; // 0.1 to 1.0, where 1.0 results in a 'cube' - scoreToBeat, - i, - currentScore; + this.animationInterval = 1000; // milliseconds + this.animationPreload = false; - if (config._f.length === 0) { - config._pf.invalidFormat = true; - config._d = new Date(NaN); - return; - } + this.camera = new Camera(); + this.eye = new Point3d(0, 0, -1); // TODO: set eye.z about 3/4 of the width of the window? - for (i = 0; i < config._f.length; i++) { - currentScore = 0; - tempConfig = copyConfig({}, config); - if (config._useUTC != null) { - tempConfig._useUTC = config._useUTC; - } - tempConfig._pf = defaultParsingFlags(); - tempConfig._f = config._f[i]; - makeDateFromStringAndFormat(tempConfig); + this.dataTable = null; // The original data table + this.dataPoints = null; // The table with point objects - if (!isValid(tempConfig)) { - continue; - } + // the column indexes + this.colX = undefined; + this.colY = undefined; + this.colZ = undefined; + this.colValue = undefined; + this.colFilter = undefined; - // if there is any input that was not parsed add a penalty for that format - currentScore += tempConfig._pf.charsLeftOver; + this.xMin = 0; + this.xStep = undefined; // auto by default + this.xMax = 1; + this.yMin = 0; + this.yStep = undefined; // auto by default + this.yMax = 1; + this.zMin = 0; + this.zStep = undefined; // auto by default + this.zMax = 1; + this.valueMin = 0; + this.valueMax = 1; + this.xBarWidth = 1; + this.yBarWidth = 1; + // TODO: customize axis range - //or tokens - currentScore += tempConfig._pf.unusedTokens.length * 10; + // constants + this.colorAxis = '#4D4D4D'; + this.colorGrid = '#D3D3D3'; + this.colorDot = '#7DC1FF'; + this.colorDotBorder = '#3267D2'; - tempConfig._pf.score = currentScore; + // create a frame and canvas + this.create(); - if (scoreToBeat == null || currentScore < scoreToBeat) { - scoreToBeat = currentScore; - bestMoment = tempConfig; - } - } + // apply options (also when undefined) + this.setOptions(options); - extend(config, bestMoment || tempConfig); - } + // apply data + if (data) { + this.setData(data); + } + } - // date from iso format - function parseISO(config) { - var i, l, - string = config._i, - match = isoRegex.exec(string); + // Extend Graph3d with an Emitter mixin + Emitter(Graph3d.prototype); - if (match) { - config._pf.iso = true; - for (i = 0, l = isoDates.length; i < l; i++) { - if (isoDates[i][1].exec(string)) { - // match[5] should be 'T' or undefined - config._f = isoDates[i][0] + (match[6] || ' '); - break; - } - } - for (i = 0, l = isoTimes.length; i < l; i++) { - if (isoTimes[i][1].exec(string)) { - config._f += isoTimes[i][0]; - break; - } - } - if (string.match(parseTokenTimezone)) { - config._f += 'Z'; - } - makeDateFromStringAndFormat(config); - } else { - config._isValid = false; - } - } + /** + * Calculate the scaling values, dependent on the range in x, y, and z direction + */ + Graph3d.prototype._setScale = function() { + this.scale = new Point3d(1 / (this.xMax - this.xMin), + 1 / (this.yMax - this.yMin), + 1 / (this.zMax - this.zMin)); - // date from iso format or fallback - function makeDateFromString(config) { - parseISO(config); - if (config._isValid === false) { - delete config._isValid; - moment.createFromInputFallback(config); - } + // keep aspect ration between x and y scale if desired + if (this.keepAspectRatio) { + if (this.scale.x < this.scale.y) { + //noinspection JSSuspiciousNameCombination + this.scale.y = this.scale.x; + } + else { + //noinspection JSSuspiciousNameCombination + this.scale.x = this.scale.y; } + } - function map(arr, fn) { - var res = [], i; - for (i = 0; i < arr.length; ++i) { - res.push(fn(arr[i], i)); - } - return res; - } + // scale the vertical axis + this.scale.z *= this.verticalRatio; + // TODO: can this be automated? verticalRatio? - function makeDateFromInput(config) { - var input = config._i, matched; - if (input === undefined) { - config._d = new Date(); - } else if (isDate(input)) { - config._d = new Date(+input); - } else if ((matched = aspNetJsonRegex.exec(input)) !== null) { - config._d = new Date(+matched[1]); - } else if (typeof input === 'string') { - makeDateFromString(config); - } else if (isArray(input)) { - config._a = map(input.slice(0), function (obj) { - return parseInt(obj, 10); - }); - dateFromConfig(config); - } else if (typeof(input) === 'object') { - dateFromObject(config); - } else if (typeof(input) === 'number') { - // from milliseconds - config._d = new Date(input); - } else { - moment.createFromInputFallback(config); - } - } + // determine scale for (optional) value + this.scale.value = 1 / (this.valueMax - this.valueMin); - function makeDate(y, m, d, h, M, s, ms) { - //can't just apply() to create a date: - //http://stackoverflow.com/questions/181348/instantiating-a-javascript-object-by-calling-prototype-constructor-apply - var date = new Date(y, m, d, h, M, s, ms); + // position the camera arm + var xCenter = (this.xMax + this.xMin) / 2 * this.scale.x; + var yCenter = (this.yMax + this.yMin) / 2 * this.scale.y; + var zCenter = (this.zMax + this.zMin) / 2 * this.scale.z; + this.camera.setArmLocation(xCenter, yCenter, zCenter); + }; - //the date constructor doesn't accept years < 1970 - if (y < 1970) { - date.setFullYear(y); - } - return date; - } - function makeUTCDate(y) { - var date = new Date(Date.UTC.apply(null, arguments)); - if (y < 1970) { - date.setUTCFullYear(y); - } - return date; - } + /** + * Convert a 3D location to a 2D location on screen + * http://en.wikipedia.org/wiki/3D_projection + * @param {Point3d} point3d A 3D point with parameters x, y, z + * @return {Point2d} point2d A 2D point with parameters x, y + */ + Graph3d.prototype._convert3Dto2D = function(point3d) { + var translation = this._convertPointToTranslation(point3d); + return this._convertTranslationToScreen(translation); + }; - function parseWeekday(input, locale) { - if (typeof input === 'string') { - if (!isNaN(input)) { - input = parseInt(input, 10); - } - else { - input = locale.weekdaysParse(input); - if (typeof input !== 'number') { - return null; - } - } - } - return input; - } + /** + * Convert a 3D location its translation seen from the camera + * http://en.wikipedia.org/wiki/3D_projection + * @param {Point3d} point3d A 3D point with parameters x, y, z + * @return {Point3d} translation A 3D point with parameters x, y, z This is + * the translation of the point, seen from the + * camera + */ + Graph3d.prototype._convertPointToTranslation = function(point3d) { + var ax = point3d.x * this.scale.x, + ay = point3d.y * this.scale.y, + az = point3d.z * this.scale.z, - /************************************ - Relative Time - ************************************/ + cx = this.camera.getCameraLocation().x, + cy = this.camera.getCameraLocation().y, + cz = this.camera.getCameraLocation().z, + // calculate angles + sinTx = Math.sin(this.camera.getCameraRotation().x), + cosTx = Math.cos(this.camera.getCameraRotation().x), + sinTy = Math.sin(this.camera.getCameraRotation().y), + cosTy = Math.cos(this.camera.getCameraRotation().y), + sinTz = Math.sin(this.camera.getCameraRotation().z), + cosTz = Math.cos(this.camera.getCameraRotation().z), - // helper function for moment.fn.from, moment.fn.fromNow, and moment.duration.fn.humanize - function substituteTimeAgo(string, number, withoutSuffix, isFuture, locale) { - return locale.relativeTime(number || 1, !!withoutSuffix, string, isFuture); - } + // calculate translation + dx = cosTy * (sinTz * (ay - cy) + cosTz * (ax - cx)) - sinTy * (az - cz), + dy = sinTx * (cosTy * (az - cz) + sinTy * (sinTz * (ay - cy) + cosTz * (ax - cx))) + cosTx * (cosTz * (ay - cy) - sinTz * (ax-cx)), + dz = cosTx * (cosTy * (az - cz) + sinTy * (sinTz * (ay - cy) + cosTz * (ax - cx))) - sinTx * (cosTz * (ay - cy) - sinTz * (ax-cx)); - function relativeTime(posNegDuration, withoutSuffix, locale) { - var duration = moment.duration(posNegDuration).abs(), - seconds = round(duration.as('s')), - minutes = round(duration.as('m')), - hours = round(duration.as('h')), - days = round(duration.as('d')), - months = round(duration.as('M')), - years = round(duration.as('y')), + return new Point3d(dx, dy, dz); + }; - args = seconds < relativeTimeThresholds.s && ['s', seconds] || - minutes === 1 && ['m'] || - minutes < relativeTimeThresholds.m && ['mm', minutes] || - hours === 1 && ['h'] || - hours < relativeTimeThresholds.h && ['hh', hours] || - days === 1 && ['d'] || - days < relativeTimeThresholds.d && ['dd', days] || - months === 1 && ['M'] || - months < relativeTimeThresholds.M && ['MM', months] || - years === 1 && ['y'] || ['yy', years]; + /** + * Convert a translation point to a point on the screen + * @param {Point3d} translation A 3D point with parameters x, y, z This is + * the translation of the point, seen from the + * camera + * @return {Point2d} point2d A 2D point with parameters x, y + */ + Graph3d.prototype._convertTranslationToScreen = function(translation) { + var ex = this.eye.x, + ey = this.eye.y, + ez = this.eye.z, + dx = translation.x, + dy = translation.y, + dz = translation.z; - args[2] = withoutSuffix; - args[3] = +posNegDuration > 0; - args[4] = locale; - return substituteTimeAgo.apply({}, args); - } + // calculate position on screen from translation + var bx; + var by; + if (this.showPerspective) { + bx = (dx - ex) * (ez / dz); + by = (dy - ey) * (ez / dz); + } + else { + bx = dx * -(ez / this.camera.getArmLength()); + by = dy * -(ez / this.camera.getArmLength()); + } + // shift and scale the point to the center of the screen + // use the width of the graph to scale both horizontally and vertically. + return new Point2d( + this.xcenter + bx * this.frame.canvas.clientWidth, + this.ycenter - by * this.frame.canvas.clientWidth); + }; - /************************************ - Week of Year - ************************************/ + /** + * Set the background styling for the graph + * @param {string | {fill: string, stroke: string, strokeWidth: string}} backgroundColor + */ + Graph3d.prototype._setBackgroundColor = function(backgroundColor) { + var fill = 'white'; + var stroke = 'gray'; + var strokeWidth = 1; + if (typeof(backgroundColor) === 'string') { + fill = backgroundColor; + stroke = 'none'; + strokeWidth = 0; + } + else if (typeof(backgroundColor) === 'object') { + if (backgroundColor.fill !== undefined) fill = backgroundColor.fill; + if (backgroundColor.stroke !== undefined) stroke = backgroundColor.stroke; + if (backgroundColor.strokeWidth !== undefined) strokeWidth = backgroundColor.strokeWidth; + } + else if (backgroundColor === undefined) { + // use use defaults + } + else { + throw 'Unsupported type of backgroundColor'; + } - // firstDayOfWeek 0 = sun, 6 = sat - // the day of the week that starts the week - // (usually sunday or monday) - // firstDayOfWeekOfYear 0 = sun, 6 = sat - // the first week is the week that contains the first - // of this day of the week - // (eg. ISO weeks use thursday (4)) - function weekOfYear(mom, firstDayOfWeek, firstDayOfWeekOfYear) { - var end = firstDayOfWeekOfYear - firstDayOfWeek, - daysToDayOfWeek = firstDayOfWeekOfYear - mom.day(), - adjustedMoment; + this.frame.style.backgroundColor = fill; + this.frame.style.borderColor = stroke; + this.frame.style.borderWidth = strokeWidth + 'px'; + this.frame.style.borderStyle = 'solid'; + }; - if (daysToDayOfWeek > end) { - daysToDayOfWeek -= 7; - } + /// enumerate the available styles + Graph3d.STYLE = { + BAR: 0, + BARCOLOR: 1, + BARSIZE: 2, + DOT : 3, + DOTLINE : 4, + DOTCOLOR: 5, + DOTSIZE: 6, + GRID : 7, + LINE: 8, + SURFACE : 9 + }; - if (daysToDayOfWeek < end - 7) { - daysToDayOfWeek += 7; - } + /** + * Retrieve the style index from given styleName + * @param {string} styleName Style name such as 'dot', 'grid', 'dot-line' + * @return {Number} styleNumber Enumeration value representing the style, or -1 + * when not found + */ + Graph3d.prototype._getStyleNumber = function(styleName) { + switch (styleName) { + case 'dot': return Graph3d.STYLE.DOT; + case 'dot-line': return Graph3d.STYLE.DOTLINE; + case 'dot-color': return Graph3d.STYLE.DOTCOLOR; + case 'dot-size': return Graph3d.STYLE.DOTSIZE; + case 'line': return Graph3d.STYLE.LINE; + case 'grid': return Graph3d.STYLE.GRID; + case 'surface': return Graph3d.STYLE.SURFACE; + case 'bar': return Graph3d.STYLE.BAR; + case 'bar-color': return Graph3d.STYLE.BARCOLOR; + case 'bar-size': return Graph3d.STYLE.BARSIZE; + } - adjustedMoment = moment(mom).add(daysToDayOfWeek, 'd'); - return { - week: Math.ceil(adjustedMoment.dayOfYear() / 7), - year: adjustedMoment.year() - }; - } + return -1; + }; - //http://en.wikipedia.org/wiki/ISO_week_date#Calculating_a_date_given_the_year.2C_week_number_and_weekday - function dayOfYearFromWeeks(year, week, weekday, firstDayOfWeekOfYear, firstDayOfWeek) { - var d = makeUTCDate(year, 0, 1).getUTCDay(), daysToAdd, dayOfYear; + /** + * Determine the indexes of the data columns, based on the given style and data + * @param {DataSet} data + * @param {Number} style + */ + Graph3d.prototype._determineColumnIndexes = function(data, style) { + if (this.style === Graph3d.STYLE.DOT || + this.style === Graph3d.STYLE.DOTLINE || + this.style === Graph3d.STYLE.LINE || + this.style === Graph3d.STYLE.GRID || + this.style === Graph3d.STYLE.SURFACE || + this.style === Graph3d.STYLE.BAR) { + // 3 columns expected, and optionally a 4th with filter values + this.colX = 0; + this.colY = 1; + this.colZ = 2; + this.colValue = undefined; - d = d === 0 ? 7 : d; - weekday = weekday != null ? weekday : firstDayOfWeek; - daysToAdd = firstDayOfWeek - d + (d > firstDayOfWeekOfYear ? 7 : 0) - (d < firstDayOfWeek ? 7 : 0); - dayOfYear = 7 * (week - 1) + (weekday - firstDayOfWeek) + daysToAdd + 1; + if (data.getNumberOfColumns() > 3) { + this.colFilter = 3; + } + } + else if (this.style === Graph3d.STYLE.DOTCOLOR || + this.style === Graph3d.STYLE.DOTSIZE || + this.style === Graph3d.STYLE.BARCOLOR || + this.style === Graph3d.STYLE.BARSIZE) { + // 4 columns expected, and optionally a 5th with filter values + this.colX = 0; + this.colY = 1; + this.colZ = 2; + this.colValue = 3; - return { - year: dayOfYear > 0 ? year : year - 1, - dayOfYear: dayOfYear > 0 ? dayOfYear : daysInYear(year - 1) + dayOfYear - }; + if (data.getNumberOfColumns() > 4) { + this.colFilter = 4; } + } + else { + throw 'Unknown style "' + this.style + '"'; + } + }; - /************************************ - Top Level Functions - ************************************/ + Graph3d.prototype.getNumberOfRows = function(data) { + return data.length; + } - function makeMoment(config) { - var input = config._i, - format = config._f, - res; - config._locale = config._locale || moment.localeData(config._l); + Graph3d.prototype.getNumberOfColumns = function(data) { + var counter = 0; + for (var column in data[0]) { + if (data[0].hasOwnProperty(column)) { + counter++; + } + } + return counter; + } - if (input === null || (format === undefined && input === '')) { - return moment.invalid({nullInput: true}); - } - if (typeof input === 'string') { - config._i = input = config._locale.preparse(input); - } + Graph3d.prototype.getDistinctValues = function(data, column) { + var distinctValues = []; + for (var i = 0; i < data.length; i++) { + if (distinctValues.indexOf(data[i][column]) == -1) { + distinctValues.push(data[i][column]); + } + } + return distinctValues; + } - if (moment.isMoment(input)) { - return new Moment(input, true); - } else if (format) { - if (isArray(format)) { - makeDateFromStringAndArray(config); - } else { - makeDateFromStringAndFormat(config); - } - } else { - makeDateFromInput(config); - } - res = new Moment(config); - if (res._nextDay) { - // Adding is smart enough around DST - res.add(1, 'd'); - res._nextDay = undefined; - } + Graph3d.prototype.getColumnRange = function(data,column) { + var minMax = {min:data[0][column],max:data[0][column]}; + for (var i = 0; i < data.length; i++) { + if (minMax.min > data[i][column]) { minMax.min = data[i][column]; } + if (minMax.max < data[i][column]) { minMax.max = data[i][column]; } + } + return minMax; + }; - return res; - } + /** + * Initialize the data from the data table. Calculate minimum and maximum values + * and column index values + * @param {Array | DataSet | DataView} rawData The data containing the items for the Graph. + * @param {Number} style Style Number + */ + Graph3d.prototype._dataInitialize = function (rawData, style) { + var me = this; - moment = function (input, format, locale, strict) { - var c; + // unsubscribe from the dataTable + if (this.dataSet) { + this.dataSet.off('*', this._onChange); + } - if (typeof(locale) === 'boolean') { - strict = locale; - locale = undefined; - } - // object construction must be done this way. - // https://github.com/moment/moment/issues/1423 - c = {}; - c._isAMomentObject = true; - c._i = input; - c._f = format; - c._l = locale; - c._strict = strict; - c._isUTC = false; - c._pf = defaultParsingFlags(); + if (rawData === undefined) + return; - return makeMoment(c); - }; + if (Array.isArray(rawData)) { + rawData = new DataSet(rawData); + } - moment.suppressDeprecationWarnings = false; + var data; + if (rawData instanceof DataSet || rawData instanceof DataView) { + data = rawData.get(); + } + else { + throw new Error('Array, DataSet, or DataView expected'); + } - moment.createFromInputFallback = deprecate( - 'moment construction falls back to js Date. This is ' + - 'discouraged and will be removed in upcoming major ' + - 'release. Please refer to ' + - 'https://github.com/moment/moment/issues/1407 for more info.', - function (config) { - config._d = new Date(config._i + (config._useUTC ? ' UTC' : '')); - } - ); + if (data.length == 0) + return; - // Pick a moment m from moments so that m[fn](other) is true for all - // other. This relies on the function fn to be transitive. - // - // moments should either be an array of moment objects or an array, whose - // first element is an array of moment objects. - function pickBy(fn, moments) { - var res, i; - if (moments.length === 1 && isArray(moments[0])) { - moments = moments[0]; - } - if (!moments.length) { - return moment(); - } - res = moments[0]; - for (i = 1; i < moments.length; ++i) { - if (moments[i][fn](res)) { - res = moments[i]; - } - } - return res; - } + this.dataSet = rawData; + this.dataTable = data; - moment.min = function () { - var args = [].slice.call(arguments, 0); + // subscribe to changes in the dataset + this._onChange = function () { + me.setData(me.dataSet); + }; + this.dataSet.on('*', this._onChange); - return pickBy('isBefore', args); - }; + // _determineColumnIndexes + // getNumberOfRows (points) + // getNumberOfColumns (x,y,z,v,t,t1,t2...) + // getDistinctValues (unique values?) + // getColumnRange - moment.max = function () { - var args = [].slice.call(arguments, 0); + // determine the location of x,y,z,value,filter columns + this.colX = 'x'; + this.colY = 'y'; + this.colZ = 'z'; + this.colValue = 'style'; + this.colFilter = 'filter'; - return pickBy('isAfter', args); - }; - // creating with utc - moment.utc = function (input, format, locale, strict) { - var c; - if (typeof(locale) === 'boolean') { - strict = locale; - locale = undefined; - } - // object construction must be done this way. - // https://github.com/moment/moment/issues/1423 - c = {}; - c._isAMomentObject = true; - c._useUTC = true; - c._isUTC = true; - c._l = locale; - c._i = input; - c._f = format; - c._strict = strict; - c._pf = defaultParsingFlags(); + // check if a filter column is provided + if (data[0].hasOwnProperty('filter')) { + if (this.dataFilter === undefined) { + this.dataFilter = new Filter(rawData, this.colFilter, this); + this.dataFilter.setOnLoadCallback(function() {me.redraw();}); + } + } - return makeMoment(c).utc(); - }; - // creating with unix timestamp (in seconds) - moment.unix = function (input) { - return moment(input * 1000); - }; + var withBars = this.style == Graph3d.STYLE.BAR || + this.style == Graph3d.STYLE.BARCOLOR || + this.style == Graph3d.STYLE.BARSIZE; - // duration - moment.duration = function (input, key) { - var duration = input, - // matching against regexp is expensive, do it on demand - match = null, - sign, - ret, - parseIso, - diffRes; - - if (moment.isDuration(input)) { - duration = { - ms: input._milliseconds, - d: input._days, - M: input._months - }; - } else if (typeof input === 'number') { - duration = {}; - if (key) { - duration[key] = input; - } else { - duration.milliseconds = input; - } - } else if (!!(match = aspNetTimeSpanJsonRegex.exec(input))) { - sign = (match[1] === '-') ? -1 : 1; - duration = { - y: 0, - d: toInt(match[DATE]) * sign, - h: toInt(match[HOUR]) * sign, - m: toInt(match[MINUTE]) * sign, - s: toInt(match[SECOND]) * sign, - ms: toInt(match[MILLISECOND]) * sign - }; - } else if (!!(match = isoDurationRegex.exec(input))) { - sign = (match[1] === '-') ? -1 : 1; - parseIso = function (inp) { - // We'd normally use ~~inp for this, but unfortunately it also - // converts floats to ints. - // inp may be undefined, so careful calling replace on it. - var res = inp && parseFloat(inp.replace(',', '.')); - // apply sign while we're at it - return (isNaN(res) ? 0 : res) * sign; - }; - duration = { - y: parseIso(match[2]), - M: parseIso(match[3]), - d: parseIso(match[4]), - h: parseIso(match[5]), - m: parseIso(match[6]), - s: parseIso(match[7]), - w: parseIso(match[8]) - }; - } else if (duration == null) {// checks for null or undefined - duration = {}; - } else if (typeof duration === 'object' && - ('from' in duration || 'to' in duration)) { - diffRes = momentsDifference(moment(duration.from), moment(duration.to)); + // determine barWidth from data + if (withBars) { + if (this.defaultXBarWidth !== undefined) { + this.xBarWidth = this.defaultXBarWidth; + } + else { + var dataX = this.getDistinctValues(data,this.colX); + this.xBarWidth = (dataX[1] - dataX[0]) || 1; + } - duration = {}; - duration.ms = diffRes.milliseconds; - duration.M = diffRes.months; - } + if (this.defaultYBarWidth !== undefined) { + this.yBarWidth = this.defaultYBarWidth; + } + else { + var dataY = this.getDistinctValues(data,this.colY); + this.yBarWidth = (dataY[1] - dataY[0]) || 1; + } + } - ret = new Duration(duration); + // calculate minimums and maximums + var xRange = this.getColumnRange(data,this.colX); + if (withBars) { + xRange.min -= this.xBarWidth / 2; + xRange.max += this.xBarWidth / 2; + } + this.xMin = (this.defaultXMin !== undefined) ? this.defaultXMin : xRange.min; + this.xMax = (this.defaultXMax !== undefined) ? this.defaultXMax : xRange.max; + if (this.xMax <= this.xMin) this.xMax = this.xMin + 1; + this.xStep = (this.defaultXStep !== undefined) ? this.defaultXStep : (this.xMax-this.xMin)/5; - if (moment.isDuration(input) && hasOwnProp(input, '_locale')) { - ret._locale = input._locale; - } + var yRange = this.getColumnRange(data,this.colY); + if (withBars) { + yRange.min -= this.yBarWidth / 2; + yRange.max += this.yBarWidth / 2; + } + this.yMin = (this.defaultYMin !== undefined) ? this.defaultYMin : yRange.min; + this.yMax = (this.defaultYMax !== undefined) ? this.defaultYMax : yRange.max; + if (this.yMax <= this.yMin) this.yMax = this.yMin + 1; + this.yStep = (this.defaultYStep !== undefined) ? this.defaultYStep : (this.yMax-this.yMin)/5; - return ret; - }; + var zRange = this.getColumnRange(data,this.colZ); + this.zMin = (this.defaultZMin !== undefined) ? this.defaultZMin : zRange.min; + this.zMax = (this.defaultZMax !== undefined) ? this.defaultZMax : zRange.max; + if (this.zMax <= this.zMin) this.zMax = this.zMin + 1; + this.zStep = (this.defaultZStep !== undefined) ? this.defaultZStep : (this.zMax-this.zMin)/5; - // version number - moment.version = VERSION; + if (this.colValue !== undefined) { + var valueRange = this.getColumnRange(data,this.colValue); + this.valueMin = (this.defaultValueMin !== undefined) ? this.defaultValueMin : valueRange.min; + this.valueMax = (this.defaultValueMax !== undefined) ? this.defaultValueMax : valueRange.max; + if (this.valueMax <= this.valueMin) this.valueMax = this.valueMin + 1; + } - // default format - moment.defaultFormat = isoFormat; + // set the scale dependent on the ranges. + this._setScale(); + }; - // constant that refers to the ISO standard - moment.ISO_8601 = function () {}; - // Plugins that add properties should also add the key here (null value), - // so we can properly clone ourselves. - moment.momentProperties = momentProperties; - // This function will be called whenever a moment is mutated. - // It is intended to keep the offset in sync with the timezone. - moment.updateOffset = function () {}; + /** + * Filter the data based on the current filter + * @param {Array} data + * @return {Array} dataPoints Array with point objects which can be drawn on screen + */ + Graph3d.prototype._getDataPoints = function (data) { + // TODO: store the created matrix dataPoints in the filters instead of reloading each time + var x, y, i, z, obj, point; - // This function allows you to set a threshold for relative time strings - moment.relativeTimeThreshold = function (threshold, limit) { - if (relativeTimeThresholds[threshold] === undefined) { - return false; - } - if (limit === undefined) { - return relativeTimeThresholds[threshold]; - } - relativeTimeThresholds[threshold] = limit; - return true; - }; + var dataPoints = []; - moment.lang = deprecate( - 'moment.lang is deprecated. Use moment.locale instead.', - function (key, value) { - return moment.locale(key, value); - } - ); + if (this.style === Graph3d.STYLE.GRID || + this.style === Graph3d.STYLE.SURFACE) { + // copy all values from the google data table to a matrix + // the provided values are supposed to form a grid of (x,y) positions - // This function will load locale and then set the global locale. If - // no arguments are passed in, it will simply return the current global - // locale key. - moment.locale = function (key, values) { - var data; - if (key) { - if (typeof(values) !== 'undefined') { - data = moment.defineLocale(key, values); - } - else { - data = moment.localeData(key); - } + // create two lists with all present x and y values + var dataX = []; + var dataY = []; + for (i = 0; i < this.getNumberOfRows(data); i++) { + x = data[i][this.colX] || 0; + y = data[i][this.colY] || 0; - if (data) { - moment.duration._locale = moment._locale = data; - } - } + if (dataX.indexOf(x) === -1) { + dataX.push(x); + } + if (dataY.indexOf(y) === -1) { + dataY.push(y); + } + } - return moment._locale._abbr; + var sortNumber = function (a, b) { + return a - b; }; + dataX.sort(sortNumber); + dataY.sort(sortNumber); - moment.defineLocale = function (name, values) { - if (values !== null) { - values.abbr = name; - if (!locales[name]) { - locales[name] = new Locale(); - } - locales[name].set(values); + // create a grid, a 2d matrix, with all values. + var dataMatrix = []; // temporary data matrix + for (i = 0; i < data.length; i++) { + x = data[i][this.colX] || 0; + y = data[i][this.colY] || 0; + z = data[i][this.colZ] || 0; - // backwards compat for now: also set the locale - moment.locale(name); + var xIndex = dataX.indexOf(x); // TODO: implement Array().indexOf() for Internet Explorer + var yIndex = dataY.indexOf(y); - return locales[name]; - } else { - // useful for testing - delete locales[name]; - return null; - } - }; + if (dataMatrix[xIndex] === undefined) { + dataMatrix[xIndex] = []; + } - moment.langData = deprecate( - 'moment.langData is deprecated. Use moment.localeData instead.', - function (key) { - return moment.localeData(key); - } - ); + var point3d = new Point3d(); + point3d.x = x; + point3d.y = y; + point3d.z = z; - // returns locale data - moment.localeData = function (key) { - var locale; + obj = {}; + obj.point = point3d; + obj.trans = undefined; + obj.screen = undefined; + obj.bottom = new Point3d(x, y, this.zMin); - if (key && key._locale && key._locale._abbr) { - key = key._locale._abbr; - } + dataMatrix[xIndex][yIndex] = obj; - if (!key) { - return moment._locale; - } + dataPoints.push(obj); + } - if (!isArray(key)) { - //short-circuit everything else - locale = loadLocale(key); - if (locale) { - return locale; - } - key = [key]; + // fill in the pointers to the neighbors. + for (x = 0; x < dataMatrix.length; x++) { + for (y = 0; y < dataMatrix[x].length; y++) { + if (dataMatrix[x][y]) { + dataMatrix[x][y].pointRight = (x < dataMatrix.length-1) ? dataMatrix[x+1][y] : undefined; + dataMatrix[x][y].pointTop = (y < dataMatrix[x].length-1) ? dataMatrix[x][y+1] : undefined; + dataMatrix[x][y].pointCross = + (x < dataMatrix.length-1 && y < dataMatrix[x].length-1) ? + dataMatrix[x+1][y+1] : + undefined; } + } + } + } + else { // 'dot', 'dot-line', etc. + // copy all values from the google data table to a list with Point3d objects + for (i = 0; i < data.length; i++) { + point = new Point3d(); + point.x = data[i][this.colX] || 0; + point.y = data[i][this.colY] || 0; + point.z = data[i][this.colZ] || 0; - return chooseLocale(key); - }; - - // compare moment object - moment.isMoment = function (obj) { - return obj instanceof Moment || - (obj != null && hasOwnProp(obj, '_isAMomentObject')); - }; + if (this.colValue !== undefined) { + point.value = data[i][this.colValue] || 0; + } - // for typechecking Duration objects - moment.isDuration = function (obj) { - return obj instanceof Duration; - }; + obj = {}; + obj.point = point; + obj.bottom = new Point3d(point.x, point.y, this.zMin); + obj.trans = undefined; + obj.screen = undefined; - for (i = lists.length - 1; i >= 0; --i) { - makeList(lists[i]); + dataPoints.push(obj); } + } - moment.normalizeUnits = function (units) { - return normalizeUnits(units); - }; + return dataPoints; + }; - moment.invalid = function (flags) { - var m = moment.utc(NaN); - if (flags != null) { - extend(m._pf, flags); - } - else { - m._pf.userInvalidated = true; - } + /** + * Create the main frame for the Graph3d. + * This function is executed once when a Graph3d object is created. The frame + * contains a canvas, and this canvas contains all objects like the axis and + * nodes. + */ + Graph3d.prototype.create = function () { + // remove all elements from the container element. + while (this.containerElement.hasChildNodes()) { + this.containerElement.removeChild(this.containerElement.firstChild); + } - return m; - }; + this.frame = document.createElement('div'); + this.frame.style.position = 'relative'; + this.frame.style.overflow = 'hidden'; - moment.parseZone = function () { - return moment.apply(null, arguments).parseZone(); - }; + // create the graph canvas (HTML canvas element) + this.frame.canvas = document.createElement( 'canvas' ); + this.frame.canvas.style.position = 'relative'; + this.frame.appendChild(this.frame.canvas); + //if (!this.frame.canvas.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.frame.canvas.appendChild(noCanvas); + } - moment.parseTwoDigitYear = function (input) { - return toInt(input) + (toInt(input) > 68 ? 1900 : 2000); - }; + this.frame.filter = document.createElement( 'div' ); + this.frame.filter.style.position = 'absolute'; + this.frame.filter.style.bottom = '0px'; + this.frame.filter.style.left = '0px'; + this.frame.filter.style.width = '100%'; + this.frame.appendChild(this.frame.filter); - moment.isDate = isDate; + // add event listeners to handle moving and zooming the contents + var me = this; + var onmousedown = function (event) {me._onMouseDown(event);}; + var ontouchstart = function (event) {me._onTouchStart(event);}; + var onmousewheel = function (event) {me._onWheel(event);}; + var ontooltip = function (event) {me._onTooltip(event);}; + // TODO: these events are never cleaned up... can give a 'memory leakage' - /************************************ - Moment Prototype - ************************************/ + util.addEventListener(this.frame.canvas, 'keydown', onkeydown); + util.addEventListener(this.frame.canvas, 'mousedown', onmousedown); + util.addEventListener(this.frame.canvas, 'touchstart', ontouchstart); + util.addEventListener(this.frame.canvas, 'mousewheel', onmousewheel); + util.addEventListener(this.frame.canvas, 'mousemove', ontooltip); + // add the new graph to the container element + this.containerElement.appendChild(this.frame); + }; - extend(moment.fn = Moment.prototype, { - clone : function () { - return moment(this); - }, + /** + * Set a new size for the graph + * @param {string} width Width in pixels or percentage (for example '800px' + * or '50%') + * @param {string} height Height in pixels or percentage (for example '400px' + * or '30%') + */ + Graph3d.prototype.setSize = function(width, height) { + this.frame.style.width = width; + this.frame.style.height = height; - valueOf : function () { - return +this._d - ((this._offset || 0) * 60000); - }, + this._resizeCanvas(); + }; - unix : function () { - return Math.floor(+this / 1000); - }, + /** + * Resize the canvas to the current size of the frame + */ + Graph3d.prototype._resizeCanvas = function() { + this.frame.canvas.style.width = '100%'; + this.frame.canvas.style.height = '100%'; - toString : function () { - return this.clone().locale('en').format('ddd MMM DD YYYY HH:mm:ss [GMT]ZZ'); - }, + this.frame.canvas.width = this.frame.canvas.clientWidth; + this.frame.canvas.height = this.frame.canvas.clientHeight; - toDate : function () { - return this._offset ? new Date(+this) : this._d; - }, + // adjust with for margin + this.frame.filter.style.width = (this.frame.canvas.clientWidth - 2 * 10) + 'px'; + }; - toISOString : function () { - var m = moment(this).utc(); - if (0 < m.year() && m.year() <= 9999) { - if ('function' === typeof Date.prototype.toISOString) { - // native implementation is ~50x faster, use it when we can - return this.toDate().toISOString(); - } else { - return formatMoment(m, 'YYYY-MM-DD[T]HH:mm:ss.SSS[Z]'); - } - } else { - return formatMoment(m, 'YYYYYY-MM-DD[T]HH:mm:ss.SSS[Z]'); - } - }, + /** + * Start animation + */ + Graph3d.prototype.animationStart = function() { + if (!this.frame.filter || !this.frame.filter.slider) + throw 'No animation available'; - toArray : function () { - var m = this; - return [ - m.year(), - m.month(), - m.date(), - m.hours(), - m.minutes(), - m.seconds(), - m.milliseconds() - ]; - }, + this.frame.filter.slider.play(); + }; - isValid : function () { - return isValid(this); - }, - isDSTShifted : function () { - if (this._a) { - return this.isValid() && compareArrays(this._a, (this._isUTC ? moment.utc(this._a) : moment(this._a)).toArray()) > 0; - } + /** + * Stop animation + */ + Graph3d.prototype.animationStop = function() { + if (!this.frame.filter || !this.frame.filter.slider) return; - return false; - }, + this.frame.filter.slider.stop(); + }; - parsingFlags : function () { - return extend({}, this._pf); - }, - invalidAt: function () { - return this._pf.overflow; - }, + /** + * Resize the center position based on the current values in this.defaultXCenter + * and this.defaultYCenter (which are strings with a percentage or a value + * in pixels). The center positions are the variables this.xCenter + * and this.yCenter + */ + Graph3d.prototype._resizeCenter = function() { + // calculate the horizontal center position + if (this.defaultXCenter.charAt(this.defaultXCenter.length-1) === '%') { + this.xcenter = + parseFloat(this.defaultXCenter) / 100 * + this.frame.canvas.clientWidth; + } + else { + this.xcenter = parseFloat(this.defaultXCenter); // supposed to be in px + } - utc : function (keepLocalTime) { - return this.utcOffset(0, keepLocalTime); - }, + // calculate the vertical center position + if (this.defaultYCenter.charAt(this.defaultYCenter.length-1) === '%') { + this.ycenter = + parseFloat(this.defaultYCenter) / 100 * + (this.frame.canvas.clientHeight - this.frame.filter.clientHeight); + } + else { + this.ycenter = parseFloat(this.defaultYCenter); // supposed to be in px + } + }; - local : function (keepLocalTime) { - if (this._isUTC) { - this.utcOffset(0, keepLocalTime); - this._isUTC = false; + /** + * Set the rotation and distance of the camera + * @param {Object} pos An object with the camera position. The object + * contains three parameters: + * - horizontal {Number} + * The horizontal rotation, between 0 and 2*PI. + * Optional, can be left undefined. + * - vertical {Number} + * The vertical rotation, between 0 and 0.5*PI + * if vertical=0.5*PI, the graph is shown from the + * top. Optional, can be left undefined. + * - distance {Number} + * The (normalized) distance of the camera to the + * center of the graph, a value between 0.71 and 5.0. + * Optional, can be left undefined. + */ + Graph3d.prototype.setCameraPosition = function(pos) { + if (pos === undefined) { + return; + } - if (keepLocalTime) { - this.subtract(this._dateUtcOffset(), 'm'); - } - } - return this; - }, - - format : function (inputString) { - var output = formatMoment(this, inputString || moment.defaultFormat); - return this.localeData().postformat(output); - }, - - add : createAdder(1, 'add'), + if (pos.horizontal !== undefined && pos.vertical !== undefined) { + this.camera.setArmRotation(pos.horizontal, pos.vertical); + } - subtract : createAdder(-1, 'subtract'), + if (pos.distance !== undefined) { + this.camera.setArmLength(pos.distance); + } - diff : function (input, units, asFloat) { - var that = makeAs(input, this), - zoneDiff = (that.utcOffset() - this.utcOffset()) * 6e4, - anchor, diff, output, daysAdjust; + this.redraw(); + }; - units = normalizeUnits(units); - if (units === 'year' || units === 'month' || units === 'quarter') { - output = monthDiff(this, that); - if (units === 'quarter') { - output = output / 3; - } else if (units === 'year') { - output = output / 12; - } - } else { - diff = this - that; - output = units === 'second' ? diff / 1e3 : // 1000 - units === 'minute' ? diff / 6e4 : // 1000 * 60 - units === 'hour' ? diff / 36e5 : // 1000 * 60 * 60 - units === 'day' ? (diff - zoneDiff) / 864e5 : // 1000 * 60 * 60 * 24, negate dst - units === 'week' ? (diff - zoneDiff) / 6048e5 : // 1000 * 60 * 60 * 24 * 7, negate dst - diff; - } - return asFloat ? output : absRound(output); - }, + /** + * Retrieve the current camera rotation + * @return {object} An object with parameters horizontal, vertical, and + * distance + */ + Graph3d.prototype.getCameraPosition = function() { + var pos = this.camera.getArmRotation(); + pos.distance = this.camera.getArmLength(); + return pos; + }; - from : function (time, withoutSuffix) { - return moment.duration({to: this, from: time}).locale(this.locale()).humanize(!withoutSuffix); - }, + /** + * Load data into the 3D Graph + */ + Graph3d.prototype._readData = function(data) { + // read the data + this._dataInitialize(data, this.style); - fromNow : function (withoutSuffix) { - return this.from(moment(), withoutSuffix); - }, - calendar : function (time) { - // We want to compare the start of today, vs this. - // Getting start-of-today depends on whether we're locat/utc/offset - // or not. - var now = time || moment(), - sod = makeAs(now, this).startOf('day'), - diff = this.diff(sod, 'days', true), - format = diff < -6 ? 'sameElse' : - diff < -1 ? 'lastWeek' : - diff < 0 ? 'lastDay' : - diff < 1 ? 'sameDay' : - diff < 2 ? 'nextDay' : - diff < 7 ? 'nextWeek' : 'sameElse'; - return this.format(this.localeData().calendar(format, this, moment(now))); - }, + if (this.dataFilter) { + // apply filtering + this.dataPoints = this.dataFilter._getDataPoints(); + } + else { + // no filtering. load all data + this.dataPoints = this._getDataPoints(this.dataTable); + } - isLeapYear : function () { - return isLeapYear(this.year()); - }, + // draw the filter + this._redrawFilter(); + }; - isDST : function () { - return (this.utcOffset() > this.clone().month(0).utcOffset() || - this.utcOffset() > this.clone().month(5).utcOffset()); - }, + /** + * Replace the dataset of the Graph3d + * @param {Array | DataSet | DataView} data + */ + Graph3d.prototype.setData = function (data) { + this._readData(data); + this.redraw(); - day : function (input) { - var day = this._isUTC ? this._d.getUTCDay() : this._d.getDay(); - if (input != null) { - input = parseWeekday(input, this.localeData()); - return this.add(input - day, 'd'); - } else { - return day; - } - }, + // start animation when option is true + if (this.animationAutoStart && this.dataFilter) { + this.animationStart(); + } + }; - month : makeAccessor('Month', true), + /** + * Update the options. Options will be merged with current options + * @param {Object} options + */ + Graph3d.prototype.setOptions = function (options) { + var cameraPosition = undefined; - startOf : function (units) { - units = normalizeUnits(units); - // the following switch intentionally omits break keywords - // to utilize falling through the cases. - switch (units) { - case 'year': - this.month(0); - /* falls through */ - case 'quarter': - case 'month': - this.date(1); - /* falls through */ - case 'week': - case 'isoWeek': - case 'day': - this.hours(0); - /* falls through */ - case 'hour': - this.minutes(0); - /* falls through */ - case 'minute': - this.seconds(0); - /* falls through */ - case 'second': - this.milliseconds(0); - /* falls through */ - } + this.animationStop(); - // weeks are a special case - if (units === 'week') { - this.weekday(0); - } else if (units === 'isoWeek') { - this.isoWeekday(1); - } + if (options !== undefined) { + // retrieve parameter values + if (options.width !== undefined) this.width = options.width; + if (options.height !== undefined) this.height = options.height; - // quarters are also special - if (units === 'quarter') { - this.month(Math.floor(this.month() / 3) * 3); - } + if (options.xCenter !== undefined) this.defaultXCenter = options.xCenter; + if (options.yCenter !== undefined) this.defaultYCenter = options.yCenter; - return this; - }, + if (options.filterLabel !== undefined) this.filterLabel = options.filterLabel; + if (options.legendLabel !== undefined) this.legendLabel = options.legendLabel; + if (options.xLabel !== undefined) this.xLabel = options.xLabel; + if (options.yLabel !== undefined) this.yLabel = options.yLabel; + if (options.zLabel !== undefined) this.zLabel = options.zLabel; - endOf: function (units) { - units = normalizeUnits(units); - if (units === undefined || units === 'millisecond') { - return this; - } - return this.startOf(units).add(1, (units === 'isoWeek' ? 'week' : units)).subtract(1, 'ms'); - }, + if (options.xValueLabel !== undefined) this.xValueLabel = options.xValueLabel; + if (options.yValueLabel !== undefined) this.yValueLabel = options.yValueLabel; + if (options.zValueLabel !== undefined) this.zValueLabel = options.zValueLabel; - isAfter: function (input, units) { - var inputMs; - units = normalizeUnits(typeof units !== 'undefined' ? units : 'millisecond'); - if (units === 'millisecond') { - input = moment.isMoment(input) ? input : moment(input); - return +this > +input; - } else { - inputMs = moment.isMoment(input) ? +input : +moment(input); - return inputMs < +this.clone().startOf(units); - } - }, + if (options.style !== undefined) { + var styleNumber = this._getStyleNumber(options.style); + if (styleNumber !== -1) { + this.style = styleNumber; + } + } + if (options.showGrid !== undefined) this.showGrid = options.showGrid; + if (options.showPerspective !== undefined) this.showPerspective = options.showPerspective; + if (options.showShadow !== undefined) this.showShadow = options.showShadow; + if (options.tooltip !== undefined) this.showTooltip = options.tooltip; + if (options.showAnimationControls !== undefined) this.showAnimationControls = options.showAnimationControls; + if (options.keepAspectRatio !== undefined) this.keepAspectRatio = options.keepAspectRatio; + if (options.verticalRatio !== undefined) this.verticalRatio = options.verticalRatio; - isBefore: function (input, units) { - var inputMs; - units = normalizeUnits(typeof units !== 'undefined' ? units : 'millisecond'); - if (units === 'millisecond') { - input = moment.isMoment(input) ? input : moment(input); - return +this < +input; - } else { - inputMs = moment.isMoment(input) ? +input : +moment(input); - return +this.clone().endOf(units) < inputMs; - } - }, + if (options.animationInterval !== undefined) this.animationInterval = options.animationInterval; + if (options.animationPreload !== undefined) this.animationPreload = options.animationPreload; + if (options.animationAutoStart !== undefined)this.animationAutoStart = options.animationAutoStart; - isBetween: function (from, to, units) { - return this.isAfter(from, units) && this.isBefore(to, units); - }, + if (options.xBarWidth !== undefined) this.defaultXBarWidth = options.xBarWidth; + if (options.yBarWidth !== undefined) this.defaultYBarWidth = options.yBarWidth; - isSame: function (input, units) { - var inputMs; - units = normalizeUnits(units || 'millisecond'); - if (units === 'millisecond') { - input = moment.isMoment(input) ? input : moment(input); - return +this === +input; - } else { - inputMs = +moment(input); - return +(this.clone().startOf(units)) <= inputMs && inputMs <= +(this.clone().endOf(units)); - } - }, + if (options.xMin !== undefined) this.defaultXMin = options.xMin; + if (options.xStep !== undefined) this.defaultXStep = options.xStep; + if (options.xMax !== undefined) this.defaultXMax = options.xMax; + if (options.yMin !== undefined) this.defaultYMin = options.yMin; + if (options.yStep !== undefined) this.defaultYStep = options.yStep; + if (options.yMax !== undefined) this.defaultYMax = options.yMax; + if (options.zMin !== undefined) this.defaultZMin = options.zMin; + if (options.zStep !== undefined) this.defaultZStep = options.zStep; + if (options.zMax !== undefined) this.defaultZMax = options.zMax; + if (options.valueMin !== undefined) this.defaultValueMin = options.valueMin; + if (options.valueMax !== undefined) this.defaultValueMax = options.valueMax; - min: deprecate( - 'moment().min is deprecated, use moment.min instead. https://github.com/moment/moment/issues/1548', - function (other) { - other = moment.apply(null, arguments); - return other < this ? this : other; - } - ), + if (options.cameraPosition !== undefined) cameraPosition = options.cameraPosition; - max: deprecate( - 'moment().max is deprecated, use moment.max instead. https://github.com/moment/moment/issues/1548', - function (other) { - other = moment.apply(null, arguments); - return other > this ? this : other; - } - ), + if (cameraPosition !== undefined) { + this.camera.setArmRotation(cameraPosition.horizontal, cameraPosition.vertical); + this.camera.setArmLength(cameraPosition.distance); + } + else { + this.camera.setArmRotation(1.0, 0.5); + this.camera.setArmLength(1.7); + } + } - zone : deprecate( - 'moment().zone is deprecated, use moment().utcOffset instead. ' + - 'https://github.com/moment/moment/issues/1779', - function (input, keepLocalTime) { - if (input != null) { - if (typeof input !== 'string') { - input = -input; - } + this._setBackgroundColor(options && options.backgroundColor); - this.utcOffset(input, keepLocalTime); + this.setSize(this.width, this.height); - return this; - } else { - return -this.utcOffset(); - } - } - ), + // re-load the data + if (this.dataTable) { + this.setData(this.dataTable); + } - // keepLocalTime = true means only change the timezone, without - // affecting the local hour. So 5:31:26 +0300 --[utcOffset(2, true)]--> - // 5:31:26 +0200 It is possible that 5:31:26 doesn't exist with offset - // +0200, so we adjust the time as needed, to be valid. - // - // Keeping the time actually adds/subtracts (one hour) - // from the actual represented time. That is why we call updateOffset - // a second time. In case it wants us to change the offset again - // _changeInProgress == true case, then we have to adjust, because - // there is no such time in the given timezone. - utcOffset : function (input, keepLocalTime) { - var offset = this._offset || 0, - localAdjust; - if (input != null) { - if (typeof input === 'string') { - input = utcOffsetFromString(input); - } - if (Math.abs(input) < 16) { - input = input * 60; - } - if (!this._isUTC && keepLocalTime) { - localAdjust = this._dateUtcOffset(); - } - this._offset = input; - this._isUTC = true; - if (localAdjust != null) { - this.add(localAdjust, 'm'); - } - if (offset !== input) { - if (!keepLocalTime || this._changeInProgress) { - addOrSubtractDurationFromMoment(this, - moment.duration(input - offset, 'm'), 1, false); - } else if (!this._changeInProgress) { - this._changeInProgress = true; - moment.updateOffset(this, true); - this._changeInProgress = null; - } - } + // start animation when option is true + if (this.animationAutoStart && this.dataFilter) { + this.animationStart(); + } + }; - return this; - } else { - return this._isUTC ? offset : this._dateUtcOffset(); - } - }, + /** + * Redraw the Graph. + */ + Graph3d.prototype.redraw = function() { + if (this.dataPoints === undefined) { + throw 'Error: graph data not initialized'; + } - isLocal : function () { - return !this._isUTC; - }, + this._resizeCanvas(); + this._resizeCenter(); + this._redrawSlider(); + this._redrawClear(); + this._redrawAxis(); - isUtcOffset : function () { - return this._isUTC; - }, + if (this.style === Graph3d.STYLE.GRID || + this.style === Graph3d.STYLE.SURFACE) { + this._redrawDataGrid(); + } + else if (this.style === Graph3d.STYLE.LINE) { + this._redrawDataLine(); + } + else if (this.style === Graph3d.STYLE.BAR || + this.style === Graph3d.STYLE.BARCOLOR || + this.style === Graph3d.STYLE.BARSIZE) { + this._redrawDataBar(); + } + else { + // style is DOT, DOTLINE, DOTCOLOR, DOTSIZE + this._redrawDataDot(); + } - isUtc : function () { - return this._isUTC && this._offset === 0; - }, + this._redrawInfo(); + this._redrawLegend(); + }; - zoneAbbr : function () { - return this._isUTC ? 'UTC' : ''; - }, + /** + * Clear the canvas before redrawing + */ + Graph3d.prototype._redrawClear = function() { + var canvas = this.frame.canvas; + var ctx = canvas.getContext('2d'); - zoneName : function () { - return this._isUTC ? 'Coordinated Universal Time' : ''; - }, + ctx.clearRect(0, 0, canvas.width, canvas.height); + }; - parseZone : function () { - if (this._tzm) { - this.utcOffset(this._tzm); - } else if (typeof this._i === 'string') { - this.utcOffset(utcOffsetFromString(this._i)); - } - return this; - }, - hasAlignedHourOffset : function (input) { - if (!input) { - input = 0; - } - else { - input = moment(input).utcOffset(); - } + /** + * Redraw the legend showing the colors + */ + Graph3d.prototype._redrawLegend = function() { + var y; - return (this.utcOffset() - input) % 60 === 0; - }, + if (this.style === Graph3d.STYLE.DOTCOLOR || + this.style === Graph3d.STYLE.DOTSIZE) { - daysInMonth : function () { - return daysInMonth(this.year(), this.month()); - }, + var dotSize = this.frame.clientWidth * 0.02; - dayOfYear : function (input) { - var dayOfYear = round((moment(this).startOf('day') - moment(this).startOf('year')) / 864e5) + 1; - return input == null ? dayOfYear : this.add((input - dayOfYear), 'd'); - }, + var widthMin, widthMax; + if (this.style === Graph3d.STYLE.DOTSIZE) { + widthMin = dotSize / 2; // px + widthMax = dotSize / 2 + dotSize * 2; // Todo: put this in one function + } + else { + widthMin = 20; // px + widthMax = 20; // px + } - quarter : function (input) { - return input == null ? Math.ceil((this.month() + 1) / 3) : this.month((input - 1) * 3 + this.month() % 3); - }, + var height = Math.max(this.frame.clientHeight * 0.25, 100); + var top = this.margin; + var right = this.frame.clientWidth - this.margin; + var left = right - widthMax; + var bottom = top + height; + } - weekYear : function (input) { - var year = weekOfYear(this, this.localeData()._week.dow, this.localeData()._week.doy).year; - return input == null ? year : this.add((input - year), 'y'); - }, + var canvas = this.frame.canvas; + var ctx = canvas.getContext('2d'); + ctx.lineWidth = 1; + ctx.font = '14px arial'; // TODO: put in options - isoWeekYear : function (input) { - var year = weekOfYear(this, 1, 4).year; - return input == null ? year : this.add((input - year), 'y'); - }, + if (this.style === Graph3d.STYLE.DOTCOLOR) { + // draw the color bar + var ymin = 0; + var ymax = height; // Todo: make height customizable + for (y = ymin; y < ymax; y++) { + var f = (y - ymin) / (ymax - ymin); - week : function (input) { - var week = this.localeData().week(this); - return input == null ? week : this.add((input - week) * 7, 'd'); - }, + //var width = (dotSize / 2 + (1-f) * dotSize * 2); // Todo: put this in one function + var hue = f * 240; + var color = this._hsv2rgb(hue, 1, 1); - isoWeek : function (input) { - var week = weekOfYear(this, 1, 4).week; - return input == null ? week : this.add((input - week) * 7, 'd'); - }, + ctx.strokeStyle = color; + ctx.beginPath(); + ctx.moveTo(left, top + y); + ctx.lineTo(right, top + y); + ctx.stroke(); + } - weekday : function (input) { - var weekday = (this.day() + 7 - this.localeData()._week.dow) % 7; - return input == null ? weekday : this.add(input - weekday, 'd'); - }, + ctx.strokeStyle = this.colorAxis; + ctx.strokeRect(left, top, widthMax, height); + } - isoWeekday : function (input) { - // behaves the same as moment#day except - // as a getter, returns 7 instead of 0 (1-7 range instead of 0-6) - // as a setter, sunday should belong to the previous week. - return input == null ? this.day() || 7 : this.day(this.day() % 7 ? input : input - 7); - }, + if (this.style === Graph3d.STYLE.DOTSIZE) { + // draw border around color bar + ctx.strokeStyle = this.colorAxis; + ctx.fillStyle = this.colorDot; + ctx.beginPath(); + ctx.moveTo(left, top); + ctx.lineTo(right, top); + ctx.lineTo(right - widthMax + widthMin, bottom); + ctx.lineTo(left, bottom); + ctx.closePath(); + ctx.fill(); + ctx.stroke(); + } - isoWeeksInYear : function () { - return weeksInYear(this.year(), 1, 4); - }, + if (this.style === Graph3d.STYLE.DOTCOLOR || + this.style === Graph3d.STYLE.DOTSIZE) { + // print values along the color bar + var gridLineLen = 5; // px + var step = new StepNumber(this.valueMin, this.valueMax, (this.valueMax-this.valueMin)/5, true); + step.start(); + if (step.getCurrent() < this.valueMin) { + step.next(); + } + while (!step.end()) { + y = bottom - (step.getCurrent() - this.valueMin) / (this.valueMax - this.valueMin) * height; - weeksInYear : function () { - var weekInfo = this.localeData()._week; - return weeksInYear(this.year(), weekInfo.dow, weekInfo.doy); - }, + ctx.beginPath(); + ctx.moveTo(left - gridLineLen, y); + ctx.lineTo(left, y); + ctx.stroke(); - get : function (units) { - units = normalizeUnits(units); - return this[units](); - }, + ctx.textAlign = 'right'; + ctx.textBaseline = 'middle'; + ctx.fillStyle = this.colorAxis; + ctx.fillText(step.getCurrent(), left - 2 * gridLineLen, y); - set : function (units, value) { - var unit; - if (typeof units === 'object') { - for (unit in units) { - this.set(unit, units[unit]); - } - } - else { - units = normalizeUnits(units); - if (typeof this[units] === 'function') { - this[units](value); - } - } - return this; - }, + step.next(); + } - // If passed a locale key, it will set the locale for this - // instance. Otherwise, it will return the locale configuration - // variables for this instance. - locale : function (key) { - var newLocaleData; + ctx.textAlign = 'right'; + ctx.textBaseline = 'top'; + var label = this.legendLabel; + ctx.fillText(label, right, bottom + this.margin); + } + }; - if (key === undefined) { - return this._locale._abbr; - } else { - newLocaleData = moment.localeData(key); - if (newLocaleData != null) { - this._locale = newLocaleData; - } - return this; - } - }, + /** + * Redraw the filter + */ + Graph3d.prototype._redrawFilter = function() { + this.frame.filter.innerHTML = ''; - lang : deprecate( - 'moment().lang() is deprecated. Instead, use moment().localeData() to get the language configuration. Use moment().locale() to change languages.', - function (key) { - if (key === undefined) { - return this.localeData(); - } else { - return this.locale(key); - } - } - ), + if (this.dataFilter) { + var options = { + 'visible': this.showAnimationControls + }; + var slider = new Slider(this.frame.filter, options); + this.frame.filter.slider = slider; - localeData : function () { - return this._locale; - }, + // TODO: css here is not nice here... + this.frame.filter.style.padding = '10px'; + //this.frame.filter.style.backgroundColor = '#EFEFEF'; - _dateUtcOffset : function () { - // On Firefox.24 Date#getTimezoneOffset returns a floating point. - // https://github.com/moment/moment/pull/1871 - return -Math.round(this._d.getTimezoneOffset() / 15) * 15; - } + slider.setValues(this.dataFilter.values); + slider.setPlayInterval(this.animationInterval); - }); + // create an event handler + var me = this; + var onchange = function () { + var index = slider.getIndex(); - function rawMonthSetter(mom, value) { - var dayOfMonth; + me.dataFilter.selectValue(index); + me.dataPoints = me.dataFilter._getDataPoints(); - // TODO: Move this out of here! - if (typeof value === 'string') { - value = mom.localeData().monthsParse(value); - // TODO: Another silent failure? - if (typeof value !== 'number') { - return mom; - } - } + me.redraw(); + }; + slider.setOnChangeCallback(onchange); + } + else { + this.frame.filter.slider = undefined; + } + }; - dayOfMonth = Math.min(mom.date(), - daysInMonth(mom.year(), value)); - mom._d['set' + (mom._isUTC ? 'UTC' : '') + 'Month'](value, dayOfMonth); - return mom; - } + /** + * Redraw the slider + */ + Graph3d.prototype._redrawSlider = function() { + if ( this.frame.filter.slider !== undefined) { + this.frame.filter.slider.redraw(); + } + }; - function rawGetter(mom, unit) { - return mom._d['get' + (mom._isUTC ? 'UTC' : '') + unit](); - } - function rawSetter(mom, unit, value) { - if (unit === 'Month') { - return rawMonthSetter(mom, value); - } else { - return mom._d['set' + (mom._isUTC ? 'UTC' : '') + unit](value); - } - } + /** + * Redraw common information + */ + Graph3d.prototype._redrawInfo = function() { + if (this.dataFilter) { + var canvas = this.frame.canvas; + var ctx = canvas.getContext('2d'); - function makeAccessor(unit, keepTime) { - return function (value) { - if (value != null) { - rawSetter(this, unit, value); - moment.updateOffset(this, keepTime); - return this; - } else { - return rawGetter(this, unit); - } - }; - } + ctx.font = '14px arial'; // TODO: put in options + ctx.lineStyle = 'gray'; + ctx.fillStyle = 'gray'; + ctx.textAlign = 'left'; + ctx.textBaseline = 'top'; - moment.fn.millisecond = moment.fn.milliseconds = makeAccessor('Milliseconds', false); - moment.fn.second = moment.fn.seconds = makeAccessor('Seconds', false); - moment.fn.minute = moment.fn.minutes = makeAccessor('Minutes', false); - // Setting the hour should keep the time, because the user explicitly - // specified which hour he wants. So trying to maintain the same hour (in - // a new timezone) makes sense. Adding/subtracting hours does not follow - // this rule. - moment.fn.hour = moment.fn.hours = makeAccessor('Hours', true); - // moment.fn.month is defined separately - moment.fn.date = makeAccessor('Date', true); - moment.fn.dates = deprecate('dates accessor is deprecated. Use date instead.', makeAccessor('Date', true)); - moment.fn.year = makeAccessor('FullYear', true); - moment.fn.years = deprecate('years accessor is deprecated. Use year instead.', makeAccessor('FullYear', true)); + var x = this.margin; + var y = this.margin; + ctx.fillText(this.dataFilter.getLabel() + ': ' + this.dataFilter.getSelectedValue(), x, y); + } + }; - // add plural methods - moment.fn.days = moment.fn.day; - moment.fn.months = moment.fn.month; - moment.fn.weeks = moment.fn.week; - moment.fn.isoWeeks = moment.fn.isoWeek; - moment.fn.quarters = moment.fn.quarter; - // add aliased format methods - moment.fn.toJSON = moment.fn.toISOString; + /** + * Redraw the axis + */ + Graph3d.prototype._redrawAxis = function() { + var canvas = this.frame.canvas, + ctx = canvas.getContext('2d'), + from, to, step, prettyStep, + text, xText, yText, zText, + offset, xOffset, yOffset, + xMin2d, xMax2d; - // alias isUtc for dev-friendliness - moment.fn.isUTC = moment.fn.isUtc; + // TODO: get the actual rendered style of the containerElement + //ctx.font = this.containerElement.style.font; + ctx.font = 24 / this.camera.getArmLength() + 'px arial'; - /************************************ - Duration Prototype - ************************************/ + // calculate the length for the short grid lines + var gridLenX = 0.025 / this.scale.x; + var gridLenY = 0.025 / this.scale.y; + var textMargin = 5 / this.camera.getArmLength(); // px + var armAngle = this.camera.getArmRotation().horizontal; + // draw x-grid lines + ctx.lineWidth = 1; + prettyStep = (this.defaultXStep === undefined); + step = new StepNumber(this.xMin, this.xMax, this.xStep, prettyStep); + step.start(); + if (step.getCurrent() < this.xMin) { + step.next(); + } + while (!step.end()) { + var x = step.getCurrent(); - function daysToYears (days) { - // 400 years have 146097 days (taking into account leap year rules) - return days * 400 / 146097; + if (this.showGrid) { + from = this._convert3Dto2D(new Point3d(x, this.yMin, this.zMin)); + to = this._convert3Dto2D(new Point3d(x, this.yMax, this.zMin)); + ctx.strokeStyle = this.colorGrid; + ctx.beginPath(); + ctx.moveTo(from.x, from.y); + ctx.lineTo(to.x, to.y); + ctx.stroke(); } + else { + from = this._convert3Dto2D(new Point3d(x, this.yMin, this.zMin)); + to = this._convert3Dto2D(new Point3d(x, this.yMin+gridLenX, this.zMin)); + ctx.strokeStyle = this.colorAxis; + ctx.beginPath(); + ctx.moveTo(from.x, from.y); + ctx.lineTo(to.x, to.y); + ctx.stroke(); - function yearsToDays (years) { - // years * 365 + absRound(years / 4) - - // absRound(years / 100) + absRound(years / 400); - return years * 146097 / 400; + from = this._convert3Dto2D(new Point3d(x, this.yMax, this.zMin)); + to = this._convert3Dto2D(new Point3d(x, this.yMax-gridLenX, this.zMin)); + ctx.strokeStyle = this.colorAxis; + ctx.beginPath(); + ctx.moveTo(from.x, from.y); + ctx.lineTo(to.x, to.y); + ctx.stroke(); } - extend(moment.duration.fn = Duration.prototype, { + yText = (Math.cos(armAngle) > 0) ? this.yMin : this.yMax; + text = this._convert3Dto2D(new Point3d(x, yText, this.zMin)); + if (Math.cos(armAngle * 2) > 0) { + ctx.textAlign = 'center'; + ctx.textBaseline = 'top'; + text.y += textMargin; + } + else if (Math.sin(armAngle * 2) < 0){ + ctx.textAlign = 'right'; + ctx.textBaseline = 'middle'; + } + else { + ctx.textAlign = 'left'; + ctx.textBaseline = 'middle'; + } + ctx.fillStyle = this.colorAxis; + ctx.fillText(' ' + this.xValueLabel(step.getCurrent()) + ' ', text.x, text.y); - _bubble : function () { - var milliseconds = this._milliseconds, - days = this._days, - months = this._months, - data = this._data, - seconds, minutes, hours, years = 0; + step.next(); + } - // The following code bubbles up values, see the tests for - // examples of what that means. - data.milliseconds = milliseconds % 1000; + // draw y-grid lines + ctx.lineWidth = 1; + prettyStep = (this.defaultYStep === undefined); + step = new StepNumber(this.yMin, this.yMax, this.yStep, prettyStep); + step.start(); + if (step.getCurrent() < this.yMin) { + step.next(); + } + while (!step.end()) { + if (this.showGrid) { + from = this._convert3Dto2D(new Point3d(this.xMin, step.getCurrent(), this.zMin)); + to = this._convert3Dto2D(new Point3d(this.xMax, step.getCurrent(), this.zMin)); + ctx.strokeStyle = this.colorGrid; + ctx.beginPath(); + ctx.moveTo(from.x, from.y); + ctx.lineTo(to.x, to.y); + ctx.stroke(); + } + else { + from = this._convert3Dto2D(new Point3d(this.xMin, step.getCurrent(), this.zMin)); + to = this._convert3Dto2D(new Point3d(this.xMin+gridLenY, step.getCurrent(), this.zMin)); + ctx.strokeStyle = this.colorAxis; + ctx.beginPath(); + ctx.moveTo(from.x, from.y); + ctx.lineTo(to.x, to.y); + ctx.stroke(); - seconds = absRound(milliseconds / 1000); - data.seconds = seconds % 60; + from = this._convert3Dto2D(new Point3d(this.xMax, step.getCurrent(), this.zMin)); + to = this._convert3Dto2D(new Point3d(this.xMax-gridLenY, step.getCurrent(), this.zMin)); + ctx.strokeStyle = this.colorAxis; + ctx.beginPath(); + ctx.moveTo(from.x, from.y); + ctx.lineTo(to.x, to.y); + ctx.stroke(); + } - minutes = absRound(seconds / 60); - data.minutes = minutes % 60; + xText = (Math.sin(armAngle ) > 0) ? this.xMin : this.xMax; + text = this._convert3Dto2D(new Point3d(xText, step.getCurrent(), this.zMin)); + if (Math.cos(armAngle * 2) < 0) { + ctx.textAlign = 'center'; + ctx.textBaseline = 'top'; + text.y += textMargin; + } + else if (Math.sin(armAngle * 2) > 0){ + ctx.textAlign = 'right'; + ctx.textBaseline = 'middle'; + } + else { + ctx.textAlign = 'left'; + ctx.textBaseline = 'middle'; + } + ctx.fillStyle = this.colorAxis; + ctx.fillText(' ' + this.yValueLabel(step.getCurrent()) + ' ', text.x, text.y); - hours = absRound(minutes / 60); - data.hours = hours % 24; + step.next(); + } - days += absRound(hours / 24); + // draw z-grid lines and axis + ctx.lineWidth = 1; + prettyStep = (this.defaultZStep === undefined); + step = new StepNumber(this.zMin, this.zMax, this.zStep, prettyStep); + step.start(); + if (step.getCurrent() < this.zMin) { + step.next(); + } + xText = (Math.cos(armAngle ) > 0) ? this.xMin : this.xMax; + yText = (Math.sin(armAngle ) < 0) ? this.yMin : this.yMax; + while (!step.end()) { + // TODO: make z-grid lines really 3d? + from = this._convert3Dto2D(new Point3d(xText, yText, step.getCurrent())); + ctx.strokeStyle = this.colorAxis; + ctx.beginPath(); + ctx.moveTo(from.x, from.y); + ctx.lineTo(from.x - textMargin, from.y); + ctx.stroke(); - // Accurately convert days to years, assume start from year 0. - years = absRound(daysToYears(days)); - days -= absRound(yearsToDays(years)); + ctx.textAlign = 'right'; + ctx.textBaseline = 'middle'; + ctx.fillStyle = this.colorAxis; + ctx.fillText(this.zValueLabel(step.getCurrent()) + ' ', from.x - 5, from.y); - // 30 days to a month - // TODO (iskren): Use anchor date (like 1st Jan) to compute this. - months += absRound(days / 30); - days %= 30; + step.next(); + } + ctx.lineWidth = 1; + from = this._convert3Dto2D(new Point3d(xText, yText, this.zMin)); + to = this._convert3Dto2D(new Point3d(xText, yText, this.zMax)); + ctx.strokeStyle = this.colorAxis; + ctx.beginPath(); + ctx.moveTo(from.x, from.y); + ctx.lineTo(to.x, to.y); + ctx.stroke(); - // 12 months -> 1 year - years += absRound(months / 12); - months %= 12; + // draw x-axis + ctx.lineWidth = 1; + // line at yMin + xMin2d = this._convert3Dto2D(new Point3d(this.xMin, this.yMin, this.zMin)); + xMax2d = this._convert3Dto2D(new Point3d(this.xMax, this.yMin, this.zMin)); + ctx.strokeStyle = this.colorAxis; + ctx.beginPath(); + ctx.moveTo(xMin2d.x, xMin2d.y); + ctx.lineTo(xMax2d.x, xMax2d.y); + ctx.stroke(); + // line at ymax + xMin2d = this._convert3Dto2D(new Point3d(this.xMin, this.yMax, this.zMin)); + xMax2d = this._convert3Dto2D(new Point3d(this.xMax, this.yMax, this.zMin)); + ctx.strokeStyle = this.colorAxis; + ctx.beginPath(); + ctx.moveTo(xMin2d.x, xMin2d.y); + ctx.lineTo(xMax2d.x, xMax2d.y); + ctx.stroke(); - data.days = days; - data.months = months; - data.years = years; - }, + // draw y-axis + ctx.lineWidth = 1; + // line at xMin + from = this._convert3Dto2D(new Point3d(this.xMin, this.yMin, this.zMin)); + to = this._convert3Dto2D(new Point3d(this.xMin, this.yMax, this.zMin)); + ctx.strokeStyle = this.colorAxis; + ctx.beginPath(); + ctx.moveTo(from.x, from.y); + ctx.lineTo(to.x, to.y); + ctx.stroke(); + // line at xMax + from = this._convert3Dto2D(new Point3d(this.xMax, this.yMin, this.zMin)); + to = this._convert3Dto2D(new Point3d(this.xMax, this.yMax, this.zMin)); + ctx.strokeStyle = this.colorAxis; + ctx.beginPath(); + ctx.moveTo(from.x, from.y); + ctx.lineTo(to.x, to.y); + ctx.stroke(); - abs : function () { - this._milliseconds = Math.abs(this._milliseconds); - this._days = Math.abs(this._days); - this._months = Math.abs(this._months); + // draw x-label + var xLabel = this.xLabel; + if (xLabel.length > 0) { + yOffset = 0.1 / this.scale.y; + xText = (this.xMin + this.xMax) / 2; + yText = (Math.cos(armAngle) > 0) ? this.yMin - yOffset: this.yMax + yOffset; + text = this._convert3Dto2D(new Point3d(xText, yText, this.zMin)); + if (Math.cos(armAngle * 2) > 0) { + ctx.textAlign = 'center'; + ctx.textBaseline = 'top'; + } + else if (Math.sin(armAngle * 2) < 0){ + ctx.textAlign = 'right'; + ctx.textBaseline = 'middle'; + } + else { + ctx.textAlign = 'left'; + ctx.textBaseline = 'middle'; + } + ctx.fillStyle = this.colorAxis; + ctx.fillText(xLabel, text.x, text.y); + } - this._data.milliseconds = Math.abs(this._data.milliseconds); - this._data.seconds = Math.abs(this._data.seconds); - this._data.minutes = Math.abs(this._data.minutes); - this._data.hours = Math.abs(this._data.hours); - this._data.months = Math.abs(this._data.months); - this._data.years = Math.abs(this._data.years); + // draw y-label + var yLabel = this.yLabel; + if (yLabel.length > 0) { + xOffset = 0.1 / this.scale.x; + xText = (Math.sin(armAngle ) > 0) ? this.xMin - xOffset : this.xMax + xOffset; + yText = (this.yMin + this.yMax) / 2; + text = this._convert3Dto2D(new Point3d(xText, yText, this.zMin)); + if (Math.cos(armAngle * 2) < 0) { + ctx.textAlign = 'center'; + ctx.textBaseline = 'top'; + } + else if (Math.sin(armAngle * 2) > 0){ + ctx.textAlign = 'right'; + ctx.textBaseline = 'middle'; + } + else { + ctx.textAlign = 'left'; + ctx.textBaseline = 'middle'; + } + ctx.fillStyle = this.colorAxis; + ctx.fillText(yLabel, text.x, text.y); + } - return this; - }, + // draw z-label + var zLabel = this.zLabel; + if (zLabel.length > 0) { + offset = 30; // pixels. // TODO: relate to the max width of the values on the z axis? + xText = (Math.cos(armAngle ) > 0) ? this.xMin : this.xMax; + yText = (Math.sin(armAngle ) < 0) ? this.yMin : this.yMax; + zText = (this.zMin + this.zMax) / 2; + text = this._convert3Dto2D(new Point3d(xText, yText, zText)); + ctx.textAlign = 'right'; + ctx.textBaseline = 'middle'; + ctx.fillStyle = this.colorAxis; + ctx.fillText(zLabel, text.x - offset, text.y); + } + }; - weeks : function () { - return absRound(this.days() / 7); - }, + /** + * Calculate the color based on the given value. + * @param {Number} H Hue, a value be between 0 and 360 + * @param {Number} S Saturation, a value between 0 and 1 + * @param {Number} V Value, a value between 0 and 1 + */ + Graph3d.prototype._hsv2rgb = function(H, S, V) { + var R, G, B, C, Hi, X; - valueOf : function () { - return this._milliseconds + - this._days * 864e5 + - (this._months % 12) * 2592e6 + - toInt(this._months / 12) * 31536e6; - }, - - humanize : function (withSuffix) { - var output = relativeTime(this, !withSuffix, this.localeData()); - - if (withSuffix) { - output = this.localeData().pastFuture(+this, output); - } - - return this.localeData().postformat(output); - }, - - add : function (input, val) { - // supports only 2.0-style add(1, 's') or add(moment) - var dur = moment.duration(input, val); + C = V * S; + Hi = Math.floor(H/60); // hi = 0,1,2,3,4,5 + X = C * (1 - Math.abs(((H/60) % 2) - 1)); - this._milliseconds += dur._milliseconds; - this._days += dur._days; - this._months += dur._months; + switch (Hi) { + case 0: R = C; G = X; B = 0; break; + case 1: R = X; G = C; B = 0; break; + case 2: R = 0; G = C; B = X; break; + case 3: R = 0; G = X; B = C; break; + case 4: R = X; G = 0; B = C; break; + case 5: R = C; G = 0; B = X; break; - this._bubble(); + default: R = 0; G = 0; B = 0; break; + } - return this; - }, + return 'RGB(' + parseInt(R*255) + ',' + parseInt(G*255) + ',' + parseInt(B*255) + ')'; + }; - subtract : function (input, val) { - var dur = moment.duration(input, val); - this._milliseconds -= dur._milliseconds; - this._days -= dur._days; - this._months -= dur._months; + /** + * Draw all datapoints as a grid + * This function can be used when the style is 'grid' + */ + Graph3d.prototype._redrawDataGrid = function() { + var canvas = this.frame.canvas, + ctx = canvas.getContext('2d'), + point, right, top, cross, + i, + topSideVisible, fillStyle, strokeStyle, lineWidth, + h, s, v, zAvg; - this._bubble(); - return this; - }, + if (this.dataPoints === undefined || this.dataPoints.length <= 0) + return; // TODO: throw exception? - get : function (units) { - units = normalizeUnits(units); - return this[units.toLowerCase() + 's'](); - }, + // calculate the translations and screen position of all points + for (i = 0; i < this.dataPoints.length; i++) { + var trans = this._convertPointToTranslation(this.dataPoints[i].point); + var screen = this._convertTranslationToScreen(trans); - as : function (units) { - var days, months; - units = normalizeUnits(units); + this.dataPoints[i].trans = trans; + this.dataPoints[i].screen = screen; - if (units === 'month' || units === 'year') { - days = this._days + this._milliseconds / 864e5; - months = this._months + daysToYears(days) * 12; - return units === 'month' ? months : months / 12; - } else { - // handle milliseconds separately because of floating point math errors (issue #1867) - days = this._days + Math.round(yearsToDays(this._months / 12)); - switch (units) { - case 'week': return days / 7 + this._milliseconds / 6048e5; - case 'day': return days + this._milliseconds / 864e5; - case 'hour': return days * 24 + this._milliseconds / 36e5; - case 'minute': return days * 24 * 60 + this._milliseconds / 6e4; - case 'second': return days * 24 * 60 * 60 + this._milliseconds / 1000; - // Math.floor prevents floating point math errors here - case 'millisecond': return Math.floor(days * 24 * 60 * 60 * 1000) + this._milliseconds; - default: throw new Error('Unknown unit ' + units); - } - } - }, + // calculate the translation of the point at the bottom (needed for sorting) + var transBottom = this._convertPointToTranslation(this.dataPoints[i].bottom); + this.dataPoints[i].dist = this.showPerspective ? transBottom.length() : -transBottom.z; + } - lang : moment.fn.lang, - locale : moment.fn.locale, + // sort the points on depth of their (x,y) position (not on z) + var sortDepth = function (a, b) { + return b.dist - a.dist; + }; + this.dataPoints.sort(sortDepth); - toIsoString : deprecate( - 'toIsoString() is deprecated. Please use toISOString() instead ' + - '(notice the capitals)', - function () { - return this.toISOString(); - } - ), + if (this.style === Graph3d.STYLE.SURFACE) { + for (i = 0; i < this.dataPoints.length; i++) { + point = this.dataPoints[i]; + right = this.dataPoints[i].pointRight; + top = this.dataPoints[i].pointTop; + cross = this.dataPoints[i].pointCross; - toISOString : function () { - // inspired by https://github.com/dordille/moment-isoduration/blob/master/moment.isoduration.js - var years = Math.abs(this.years()), - months = Math.abs(this.months()), - days = Math.abs(this.days()), - hours = Math.abs(this.hours()), - minutes = Math.abs(this.minutes()), - seconds = Math.abs(this.seconds() + this.milliseconds() / 1000); + if (point !== undefined && right !== undefined && top !== undefined && cross !== undefined) { - if (!this.asSeconds()) { - // this is the same as C#'s (Noda) and python (isodate)... - // but not other JS (goog.date) - return 'P0D'; - } + if (this.showGrayBottom || this.showShadow) { + // calculate the cross product of the two vectors from center + // to left and right, in order to know whether we are looking at the + // bottom or at the top side. We can also use the cross product + // for calculating light intensity + var aDiff = Point3d.subtract(cross.trans, point.trans); + var bDiff = Point3d.subtract(top.trans, right.trans); + var crossproduct = Point3d.crossProduct(aDiff, bDiff); + var len = crossproduct.length(); + // FIXME: there is a bug with determining the surface side (shadow or colored) - return (this.asSeconds() < 0 ? '-' : '') + - 'P' + - (years ? years + 'Y' : '') + - (months ? months + 'M' : '') + - (days ? days + 'D' : '') + - ((hours || minutes || seconds) ? 'T' : '') + - (hours ? hours + 'H' : '') + - (minutes ? minutes + 'M' : '') + - (seconds ? seconds + 'S' : ''); - }, + topSideVisible = (crossproduct.z > 0); + } + else { + topSideVisible = true; + } - localeData : function () { - return this._locale; - }, + if (topSideVisible) { + // calculate Hue from the current value. At zMin the hue is 240, at zMax the hue is 0 + zAvg = (point.point.z + right.point.z + top.point.z + cross.point.z) / 4; + h = (1 - (zAvg - this.zMin) * this.scale.z / this.verticalRatio) * 240; + s = 1; // saturation - toJSON : function () { - return this.toISOString(); + if (this.showShadow) { + v = Math.min(1 + (crossproduct.x / len) / 2, 1); // value. TODO: scale + fillStyle = this._hsv2rgb(h, s, v); + strokeStyle = fillStyle; + } + else { + v = 1; + fillStyle = this._hsv2rgb(h, s, v); + strokeStyle = this.colorAxis; + } } - }); - - moment.duration.fn.toString = moment.duration.fn.toISOString; + else { + fillStyle = 'gray'; + strokeStyle = this.colorAxis; + } + lineWidth = 0.5; - function makeDurationGetter(name) { - moment.duration.fn[name] = function () { - return this._data[name]; - }; + ctx.lineWidth = lineWidth; + ctx.fillStyle = fillStyle; + ctx.strokeStyle = strokeStyle; + ctx.beginPath(); + ctx.moveTo(point.screen.x, point.screen.y); + ctx.lineTo(right.screen.x, right.screen.y); + ctx.lineTo(cross.screen.x, cross.screen.y); + ctx.lineTo(top.screen.x, top.screen.y); + ctx.closePath(); + ctx.fill(); + ctx.stroke(); + } } + } + else { // grid style + for (i = 0; i < this.dataPoints.length; i++) { + point = this.dataPoints[i]; + right = this.dataPoints[i].pointRight; + top = this.dataPoints[i].pointTop; - for (i in unitMillisecondFactors) { - if (hasOwnProp(unitMillisecondFactors, i)) { - makeDurationGetter(i.toLowerCase()); + if (point !== undefined) { + if (this.showPerspective) { + lineWidth = 2 / -point.trans.z; } - } - - moment.duration.fn.asMilliseconds = function () { - return this.as('ms'); - }; - moment.duration.fn.asSeconds = function () { - return this.as('s'); - }; - moment.duration.fn.asMinutes = function () { - return this.as('m'); - }; - moment.duration.fn.asHours = function () { - return this.as('h'); - }; - moment.duration.fn.asDays = function () { - return this.as('d'); - }; - moment.duration.fn.asWeeks = function () { - return this.as('weeks'); - }; - moment.duration.fn.asMonths = function () { - return this.as('M'); - }; - moment.duration.fn.asYears = function () { - return this.as('y'); - }; - - /************************************ - Default Locale - ************************************/ - - - // Set default locale, other locale will inherit from English. - moment.locale('en', { - ordinalParse: /\d{1,2}(th|st|nd|rd)/, - ordinal : function (number) { - var b = number % 10, - output = (toInt(number % 100 / 10) === 1) ? 'th' : - (b === 1) ? 'st' : - (b === 2) ? 'nd' : - (b === 3) ? 'rd' : 'th'; - return number + output; + else { + lineWidth = 2 * -(this.eye.z / this.camera.getArmLength()); } - }); - - /* EMBED_LOCALES */ + } - /************************************ - Exposing Moment - ************************************/ + if (point !== undefined && right !== undefined) { + // calculate Hue from the current value. At zMin the hue is 240, at zMax the hue is 0 + zAvg = (point.point.z + right.point.z) / 2; + h = (1 - (zAvg - this.zMin) * this.scale.z / this.verticalRatio) * 240; - function makeGlobal(shouldDeprecate) { - /*global ender:false */ - if (typeof ender !== 'undefined') { - return; - } - oldGlobalMoment = globalScope.moment; - if (shouldDeprecate) { - globalScope.moment = deprecate( - 'Accessing Moment through the global scope is ' + - 'deprecated, and will be removed in an upcoming ' + - 'release.', - moment); - } else { - globalScope.moment = moment; - } - } + ctx.lineWidth = lineWidth; + ctx.strokeStyle = this._hsv2rgb(h, 1, 1); + ctx.beginPath(); + ctx.moveTo(point.screen.x, point.screen.y); + ctx.lineTo(right.screen.x, right.screen.y); + ctx.stroke(); + } - // CommonJS module is defined - if (hasModule) { - module.exports = moment; - } else if (true) { - !(__WEBPACK_AMD_DEFINE_RESULT__ = function (require, exports, module) { - if (module.config && module.config() && module.config().noGlobal === true) { - // release the global variable - globalScope.moment = oldGlobalMoment; - } + if (point !== undefined && top !== undefined) { + // calculate Hue from the current value. At zMin the hue is 240, at zMax the hue is 0 + zAvg = (point.point.z + top.point.z) / 2; + h = (1 - (zAvg - this.zMin) * this.scale.z / this.verticalRatio) * 240; - return moment; - }.call(exports, __webpack_require__, exports, module), __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__)); - makeGlobal(true); - } else { - makeGlobal(); + ctx.lineWidth = lineWidth; + ctx.strokeStyle = this._hsv2rgb(h, 1, 1); + ctx.beginPath(); + ctx.moveTo(point.screen.x, point.screen.y); + ctx.lineTo(top.screen.x, top.screen.y); + ctx.stroke(); + } } - }).call(this); - - /* WEBPACK VAR INJECTION */}.call(exports, (function() { return this; }()), __webpack_require__(5)(module))) + } + }; -/***/ }, -/* 4 */ -/***/ function(module, exports, __webpack_require__) { - function webpackContext(req) { - throw new Error("Cannot find module '" + req + "'."); - } - webpackContext.keys = function() { return []; }; - webpackContext.resolve = webpackContext; - module.exports = webpackContext; - webpackContext.id = 4; + /** + * Draw all datapoints as dots. + * This function can be used when the style is 'dot' or 'dot-line' + */ + Graph3d.prototype._redrawDataDot = function() { + var canvas = this.frame.canvas; + var ctx = canvas.getContext('2d'); + var i; + if (this.dataPoints === undefined || this.dataPoints.length <= 0) + return; // TODO: throw exception? -/***/ }, -/* 5 */ -/***/ function(module, exports, __webpack_require__) { + // calculate the translations of all points + for (i = 0; i < this.dataPoints.length; i++) { + var trans = this._convertPointToTranslation(this.dataPoints[i].point); + var screen = this._convertTranslationToScreen(trans); + this.dataPoints[i].trans = trans; + this.dataPoints[i].screen = screen; - module.exports = function(module) { - if(!module.webpackPolyfill) { - module.deprecate = function() {}; - module.paths = []; - // module.parent = undefined by default - module.children = []; - module.webpackPolyfill = 1; - } - return module; - } + // calculate the distance from the point at the bottom to the camera + var transBottom = this._convertPointToTranslation(this.dataPoints[i].bottom); + this.dataPoints[i].dist = this.showPerspective ? transBottom.length() : -transBottom.z; + } + // order the translated points by depth + var sortDepth = function (a, b) { + return b.dist - a.dist; + }; + this.dataPoints.sort(sortDepth); -/***/ }, -/* 6 */ -/***/ function(module, exports, __webpack_require__) { + // draw the datapoints as colored circles + var dotSize = this.frame.clientWidth * 0.02; // px + for (i = 0; i < this.dataPoints.length; i++) { + var point = this.dataPoints[i]; - // DOM utility methods + if (this.style === Graph3d.STYLE.DOTLINE) { + // draw a vertical line from the bottom to the graph value + //var from = this._convert3Dto2D(new Point3d(point.point.x, point.point.y, this.zMin)); + var from = this._convert3Dto2D(point.bottom); + ctx.lineWidth = 1; + ctx.strokeStyle = this.colorGrid; + ctx.beginPath(); + ctx.moveTo(from.x, from.y); + ctx.lineTo(point.screen.x, point.screen.y); + ctx.stroke(); + } - /** - * this prepares the JSON container for allocating SVG elements - * @param JSONcontainer - * @private - */ - exports.prepareElements = function(JSONcontainer) { - // cleanup the redundant svgElements; - for (var elementType in JSONcontainer) { - if (JSONcontainer.hasOwnProperty(elementType)) { - JSONcontainer[elementType].redundant = JSONcontainer[elementType].used; - JSONcontainer[elementType].used = []; + // calculate radius for the circle + var size; + if (this.style === Graph3d.STYLE.DOTSIZE) { + size = dotSize/2 + 2*dotSize * (point.point.value - this.valueMin) / (this.valueMax - this.valueMin); + } + else { + size = dotSize; } - } - }; - /** - * this cleans up all the unused SVG elements. By asking for the parentNode, we only need to supply the JSON container from - * which to remove the redundant elements. - * - * @param JSONcontainer - * @private - */ - exports.cleanupElements = function(JSONcontainer) { - // cleanup the redundant svgElements; - for (var elementType in JSONcontainer) { - if (JSONcontainer.hasOwnProperty(elementType)) { - if (JSONcontainer[elementType].redundant) { - for (var i = 0; i < JSONcontainer[elementType].redundant.length; i++) { - JSONcontainer[elementType].redundant[i].parentNode.removeChild(JSONcontainer[elementType].redundant[i]); - } - JSONcontainer[elementType].redundant = []; - } + var radius; + if (this.showPerspective) { + radius = size / -point.trans.z; + } + else { + radius = size * -(this.eye.z / this.camera.getArmLength()); + } + if (radius < 0) { + radius = 0; } - } - }; - /** - * Allocate or generate an SVG element if needed. Store a reference to it in the JSON container and draw it in the svgContainer - * the JSON container and the SVG container have to be supplied so other svg containers (like the legend) can use this. - * - * @param elementType - * @param JSONcontainer - * @param svgContainer - * @returns {*} - * @private - */ - exports.getSVGElement = function (elementType, JSONcontainer, svgContainer) { - var element; - // allocate SVG element, if it doesnt yet exist, create one. - if (JSONcontainer.hasOwnProperty(elementType)) { // this element has been created before - // check if there is an redundant element - if (JSONcontainer[elementType].redundant.length > 0) { - element = JSONcontainer[elementType].redundant[0]; - JSONcontainer[elementType].redundant.shift(); + var hue, color, borderColor; + if (this.style === Graph3d.STYLE.DOTCOLOR ) { + // calculate the color based on the value + hue = (1 - (point.point.value - this.valueMin) * this.scale.value) * 240; + color = this._hsv2rgb(hue, 1, 1); + borderColor = this._hsv2rgb(hue, 1, 0.8); + } + else if (this.style === Graph3d.STYLE.DOTSIZE) { + color = this.colorDot; + borderColor = this.colorDotBorder; } else { - // create a new element and add it to the SVG - element = document.createElementNS('http://www.w3.org/2000/svg', elementType); - svgContainer.appendChild(element); + // calculate Hue from the current value. At zMin the hue is 240, at zMax the hue is 0 + hue = (1 - (point.point.z - this.zMin) * this.scale.z / this.verticalRatio) * 240; + color = this._hsv2rgb(hue, 1, 1); + borderColor = this._hsv2rgb(hue, 1, 0.8); } + + // draw the circle + ctx.lineWidth = 1.0; + ctx.strokeStyle = borderColor; + ctx.fillStyle = color; + ctx.beginPath(); + ctx.arc(point.screen.x, point.screen.y, radius, 0, Math.PI*2, true); + ctx.fill(); + ctx.stroke(); } - else { - // create a new element and add it to the SVG, also create a new object in the svgElements to keep track of it. - element = document.createElementNS('http://www.w3.org/2000/svg', elementType); - JSONcontainer[elementType] = {used: [], redundant: []}; - svgContainer.appendChild(element); - } - JSONcontainer[elementType].used.push(element); - return element; }; - /** - * Allocate or generate an SVG element if needed. Store a reference to it in the JSON container and draw it in the svgContainer - * the JSON container and the SVG container have to be supplied so other svg containers (like the legend) can use this. - * - * @param elementType - * @param JSONcontainer - * @param DOMContainer - * @returns {*} - * @private + * Draw all datapoints as bars. + * This function can be used when the style is 'bar', 'bar-color', or 'bar-size' */ - exports.getDOMElement = function (elementType, JSONcontainer, DOMContainer, insertBefore) { - var element; - // allocate DOM element, if it doesnt yet exist, create one. - if (JSONcontainer.hasOwnProperty(elementType)) { // this element has been created before - // check if there is an redundant element - if (JSONcontainer[elementType].redundant.length > 0) { - element = JSONcontainer[elementType].redundant[0]; - JSONcontainer[elementType].redundant.shift(); - } - else { - // create a new element and add it to the SVG - element = document.createElement(elementType); - if (insertBefore !== undefined) { - DOMContainer.insertBefore(element, insertBefore); - } - else { - DOMContainer.appendChild(element); - } - } + Graph3d.prototype._redrawDataBar = function() { + var canvas = this.frame.canvas; + var ctx = canvas.getContext('2d'); + var i, j, surface, corners; + + if (this.dataPoints === undefined || this.dataPoints.length <= 0) + return; // TODO: throw exception? + + // calculate the translations of all points + for (i = 0; i < this.dataPoints.length; i++) { + var trans = this._convertPointToTranslation(this.dataPoints[i].point); + var screen = this._convertTranslationToScreen(trans); + this.dataPoints[i].trans = trans; + this.dataPoints[i].screen = screen; + + // calculate the distance from the point at the bottom to the camera + var transBottom = this._convertPointToTranslation(this.dataPoints[i].bottom); + this.dataPoints[i].dist = this.showPerspective ? transBottom.length() : -transBottom.z; } - else { - // create a new element and add it to the SVG, also create a new object in the svgElements to keep track of it. - element = document.createElement(elementType); - JSONcontainer[elementType] = {used: [], redundant: []}; - if (insertBefore !== undefined) { - DOMContainer.insertBefore(element, insertBefore); + + // order the translated points by depth + var sortDepth = function (a, b) { + return b.dist - a.dist; + }; + this.dataPoints.sort(sortDepth); + + // draw the datapoints as bars + var xWidth = this.xBarWidth / 2; + var yWidth = this.yBarWidth / 2; + for (i = 0; i < this.dataPoints.length; i++) { + var point = this.dataPoints[i]; + + // determine color + var hue, color, borderColor; + if (this.style === Graph3d.STYLE.BARCOLOR ) { + // calculate the color based on the value + hue = (1 - (point.point.value - this.valueMin) * this.scale.value) * 240; + color = this._hsv2rgb(hue, 1, 1); + borderColor = this._hsv2rgb(hue, 1, 0.8); + } + else if (this.style === Graph3d.STYLE.BARSIZE) { + color = this.colorDot; + borderColor = this.colorDotBorder; } else { - DOMContainer.appendChild(element); + // calculate Hue from the current value. At zMin the hue is 240, at zMax the hue is 0 + hue = (1 - (point.point.z - this.zMin) * this.scale.z / this.verticalRatio) * 240; + color = this._hsv2rgb(hue, 1, 1); + borderColor = this._hsv2rgb(hue, 1, 0.8); } - } - JSONcontainer[elementType].used.push(element); - return element; - }; + // calculate size for the bar + if (this.style === Graph3d.STYLE.BARSIZE) { + xWidth = (this.xBarWidth / 2) * ((point.point.value - this.valueMin) / (this.valueMax - this.valueMin) * 0.8 + 0.2); + yWidth = (this.yBarWidth / 2) * ((point.point.value - this.valueMin) / (this.valueMax - this.valueMin) * 0.8 + 0.2); + } + // calculate all corner points + var me = this; + var point3d = point.point; + var top = [ + {point: new Point3d(point3d.x - xWidth, point3d.y - yWidth, point3d.z)}, + {point: new Point3d(point3d.x + xWidth, point3d.y - yWidth, point3d.z)}, + {point: new Point3d(point3d.x + xWidth, point3d.y + yWidth, point3d.z)}, + {point: new Point3d(point3d.x - xWidth, point3d.y + yWidth, point3d.z)} + ]; + var bottom = [ + {point: new Point3d(point3d.x - xWidth, point3d.y - yWidth, this.zMin)}, + {point: new Point3d(point3d.x + xWidth, point3d.y - yWidth, this.zMin)}, + {point: new Point3d(point3d.x + xWidth, point3d.y + yWidth, this.zMin)}, + {point: new Point3d(point3d.x - xWidth, point3d.y + yWidth, this.zMin)} + ]; + // calculate screen location of the points + top.forEach(function (obj) { + obj.screen = me._convert3Dto2D(obj.point); + }); + bottom.forEach(function (obj) { + obj.screen = me._convert3Dto2D(obj.point); + }); - /** - * draw a point object. this is a seperate function because it can also be called by the legend. - * The reason the JSONcontainer and the target SVG svgContainer have to be supplied is so the legend can use these functions - * as well. - * - * @param x - * @param y - * @param group - * @param JSONcontainer - * @param svgContainer - * @returns {*} - */ - exports.drawPoint = function(x, y, group, JSONcontainer, svgContainer) { - var point; - if (group.options.drawPoints.style == 'circle') { - point = exports.getSVGElement('circle',JSONcontainer,svgContainer); - point.setAttributeNS(null, "cx", x); - point.setAttributeNS(null, "cy", y); - point.setAttributeNS(null, "r", 0.5 * group.options.drawPoints.size); - } - else { - point = exports.getSVGElement('rect',JSONcontainer,svgContainer); - point.setAttributeNS(null, "x", x - 0.5*group.options.drawPoints.size); - point.setAttributeNS(null, "y", y - 0.5*group.options.drawPoints.size); - point.setAttributeNS(null, "width", group.options.drawPoints.size); - point.setAttributeNS(null, "height", group.options.drawPoints.size); - } + // create five sides, calculate both corner points and center points + var surfaces = [ + {corners: top, center: Point3d.avg(bottom[0].point, bottom[2].point)}, + {corners: [top[0], top[1], bottom[1], bottom[0]], center: Point3d.avg(bottom[1].point, bottom[0].point)}, + {corners: [top[1], top[2], bottom[2], bottom[1]], center: Point3d.avg(bottom[2].point, bottom[1].point)}, + {corners: [top[2], top[3], bottom[3], bottom[2]], center: Point3d.avg(bottom[3].point, bottom[2].point)}, + {corners: [top[3], top[0], bottom[0], bottom[3]], center: Point3d.avg(bottom[0].point, bottom[3].point)} + ]; + point.surfaces = surfaces; - if(group.options.drawPoints.styles !== undefined) { - point.setAttributeNS(null, "style", group.group.options.drawPoints.styles); - } - point.setAttributeNS(null, "class", group.className + " point"); - return point; - }; + // calculate the distance of each of the surface centers to the camera + for (j = 0; j < surfaces.length; j++) { + surface = surfaces[j]; + var transCenter = this._convertPointToTranslation(surface.center); + surface.dist = this.showPerspective ? transCenter.length() : -transCenter.z; + // TODO: this dept calculation doesn't work 100% of the cases due to perspective, + // but the current solution is fast/simple and works in 99.9% of all cases + // the issue is visible in example 14, with graph.setCameraPosition({horizontal: 2.97, vertical: 0.5, distance: 0.9}) + } - /** - * draw a bar SVG element centered on the X coordinate - * - * @param x - * @param y - * @param className - */ - exports.drawBar = function (x, y, width, height, className, JSONcontainer, svgContainer) { - if (height != 0) { - if (height < 0) { - height *= -1; - y -= height; + // order the surfaces by their (translated) depth + surfaces.sort(function (a, b) { + var diff = b.dist - a.dist; + if (diff) return diff; + + // if equal depth, sort the top surface last + if (a.corners === top) return 1; + if (b.corners === top) return -1; + + // both are equal + return 0; + }); + + // draw the ordered surfaces + ctx.lineWidth = 1; + ctx.strokeStyle = borderColor; + ctx.fillStyle = color; + // NOTE: we start at j=2 instead of j=0 as we don't need to draw the two surfaces at the backside + for (j = 2; j < surfaces.length; j++) { + surface = surfaces[j]; + corners = surface.corners; + ctx.beginPath(); + ctx.moveTo(corners[3].screen.x, corners[3].screen.y); + ctx.lineTo(corners[0].screen.x, corners[0].screen.y); + ctx.lineTo(corners[1].screen.x, corners[1].screen.y); + ctx.lineTo(corners[2].screen.x, corners[2].screen.y); + ctx.lineTo(corners[3].screen.x, corners[3].screen.y); + ctx.fill(); + ctx.stroke(); } - var rect = exports.getSVGElement('rect',JSONcontainer, svgContainer); - rect.setAttributeNS(null, "x", x - 0.5 * width); - rect.setAttributeNS(null, "y", y); - rect.setAttributeNS(null, "width", width); - rect.setAttributeNS(null, "height", height); - rect.setAttributeNS(null, "class", className); } }; -/***/ }, -/* 7 */ -/***/ function(module, exports, __webpack_require__) { - - var util = __webpack_require__(1); - var Queue = __webpack_require__(8); /** - * DataSet - * - * Usage: - * var dataSet = new DataSet({ - * fieldId: '_id', - * type: { - * // ... - * } - * }); - * - * dataSet.add(item); - * dataSet.add(data); - * dataSet.update(item); - * dataSet.update(data); - * dataSet.remove(id); - * dataSet.remove(ids); - * var data = dataSet.get(); - * var data = dataSet.get(id); - * var data = dataSet.get(ids); - * var data = dataSet.get(ids, options, data); - * dataSet.clear(); - * - * A data set can: - * - add/remove/update data - * - gives triggers upon changes in the data - * - can import/export data in various data formats - * - * @param {Array | DataTable} [data] Optional array with initial data - * @param {Object} [options] Available options: - * {String} fieldId Field name of the id in the - * items, 'id' by default. - * {Object. 0) { + point = this.dataPoints[0]; - // add initial data when provided - if (data) { - this.add(data); + ctx.lineWidth = 1; // TODO: make customizable + ctx.strokeStyle = 'blue'; // TODO: make customizable + ctx.beginPath(); + ctx.moveTo(point.screen.x, point.screen.y); } - this.setOptions(options); - } + // draw the datapoints as colored circles + for (i = 1; i < this.dataPoints.length; i++) { + point = this.dataPoints[i]; + ctx.lineTo(point.screen.x, point.screen.y); + } + + // finish the line + if (this.dataPoints.length > 0) { + ctx.stroke(); + } + }; /** - * @param {Object} [options] Available options: - * {Object} queue Queue changes to the DataSet, - * flush them all at once. - * Queue options: - * - {number} delay Delay in ms, null by default - * - {number} max Maximum number of entries in the queue, Infinity by default - * @param options + * Start a moving operation inside the provided parent element + * @param {Event} event The event that occurred (required for + * retrieving the mouse position) */ - DataSet.prototype.setOptions = function(options) { - if (options && options.queue !== undefined) { - if (options.queue === false) { - // delete queue if loaded - if (this._queue) { - this._queue.destroy(); - delete this._queue; - } - } - else { - // create queue and update its options - if (!this._queue) { - this._queue = Queue.extend(this, { - replace: ['add', 'update', 'remove'] - }); - } + Graph3d.prototype._onMouseDown = function(event) { + event = event || window.event; - if (typeof options.queue === 'object') { - this._queue.setOptions(options.queue); - } - } + // check if mouse is still down (may be up when focus is lost for example + // in an iframe) + if (this.leftButtonDown) { + this._onMouseUp(event); } + + // only react on left mouse button down + this.leftButtonDown = event.which ? (event.which === 1) : (event.button === 1); + if (!this.leftButtonDown && !this.touchDown) return; + + // get mouse position (different code for IE and all other browsers) + this.startMouseX = getMouseX(event); + this.startMouseY = getMouseY(event); + + this.startStart = new Date(this.start); + this.startEnd = new Date(this.end); + this.startArmRotation = this.camera.getArmRotation(); + + this.frame.style.cursor = 'move'; + + // add event listeners to handle moving the contents + // we store the function onmousemove and onmouseup in the graph, so we can + // remove the eventlisteners lateron in the function mouseUp() + var me = this; + this.onmousemove = function (event) {me._onMouseMove(event);}; + this.onmouseup = function (event) {me._onMouseUp(event);}; + util.addEventListener(document, 'mousemove', me.onmousemove); + util.addEventListener(document, 'mouseup', me.onmouseup); + util.preventDefault(event); }; + /** - * Subscribe to an event, add an event listener - * @param {String} event Event name. Available events: 'put', 'update', - * 'remove' - * @param {function} callback Callback method. Called with three parameters: - * {String} event - * {Object | null} params - * {String | Number} senderId + * Perform moving operating. + * This function activated from within the funcion Graph.mouseDown(). + * @param {Event} event Well, eehh, the event */ - DataSet.prototype.on = function(event, callback) { - var subscribers = this._subscribers[event]; - if (!subscribers) { - subscribers = []; - this._subscribers[event] = subscribers; + Graph3d.prototype._onMouseMove = function (event) { + event = event || window.event; + + // calculate change in mouse position + var diffX = parseFloat(getMouseX(event)) - this.startMouseX; + var diffY = parseFloat(getMouseY(event)) - this.startMouseY; + + var horizontalNew = this.startArmRotation.horizontal + diffX / 200; + var verticalNew = this.startArmRotation.vertical + diffY / 200; + + var snapAngle = 4; // degrees + var snapValue = Math.sin(snapAngle / 360 * 2 * Math.PI); + + // snap horizontally to nice angles at 0pi, 0.5pi, 1pi, 1.5pi, etc... + // the -0.001 is to take care that the vertical axis is always drawn at the left front corner + if (Math.abs(Math.sin(horizontalNew)) < snapValue) { + horizontalNew = Math.round((horizontalNew / Math.PI)) * Math.PI - 0.001; + } + if (Math.abs(Math.cos(horizontalNew)) < snapValue) { + horizontalNew = (Math.round((horizontalNew/ Math.PI - 0.5)) + 0.5) * Math.PI - 0.001; } - subscribers.push({ - callback: callback - }); + // snap vertically to nice angles + if (Math.abs(Math.sin(verticalNew)) < snapValue) { + verticalNew = Math.round((verticalNew / Math.PI)) * Math.PI; + } + if (Math.abs(Math.cos(verticalNew)) < snapValue) { + verticalNew = (Math.round((verticalNew/ Math.PI - 0.5)) + 0.5) * Math.PI; + } + + this.camera.setArmRotation(horizontalNew, verticalNew); + this.redraw(); + + // fire a cameraPositionChange event + var parameters = this.getCameraPosition(); + this.emit('cameraPositionChange', parameters); + + util.preventDefault(event); }; - // TODO: make this function deprecated (replaced with `on` since version 0.5) - DataSet.prototype.subscribe = DataSet.prototype.on; /** - * Unsubscribe from an event, remove an event listener - * @param {String} event - * @param {function} callback + * Stop moving operating. + * This function activated from within the funcion Graph.mouseDown(). + * @param {event} event The event */ - DataSet.prototype.off = function(event, callback) { - var subscribers = this._subscribers[event]; - if (subscribers) { - this._subscribers[event] = subscribers.filter(function (listener) { - return (listener.callback != callback); - }); - } - }; + Graph3d.prototype._onMouseUp = function (event) { + this.frame.style.cursor = 'auto'; + this.leftButtonDown = false; - // TODO: make this function deprecated (replaced with `on` since version 0.5) - DataSet.prototype.unsubscribe = DataSet.prototype.off; + // remove event listeners here + util.removeEventListener(document, 'mousemove', this.onmousemove); + util.removeEventListener(document, 'mouseup', this.onmouseup); + util.preventDefault(event); + }; /** - * Trigger an event - * @param {String} event - * @param {Object | null} params - * @param {String} [senderId] Optional id of the sender. - * @private + * After having moved the mouse, a tooltip should pop up when the mouse is resting on a data point + * @param {Event} event A mouse move event */ - DataSet.prototype._trigger = function (event, params, senderId) { - if (event == '*') { - throw new Error('Cannot trigger event *'); + Graph3d.prototype._onTooltip = function (event) { + var delay = 300; // ms + var boundingRect = this.frame.getBoundingClientRect(); + var mouseX = getMouseX(event) - boundingRect.left; + var mouseY = getMouseY(event) - boundingRect.top; + + if (!this.showTooltip) { + return; } - var subscribers = []; - if (event in this._subscribers) { - subscribers = subscribers.concat(this._subscribers[event]); + if (this.tooltipTimeout) { + clearTimeout(this.tooltipTimeout); } - if ('*' in this._subscribers) { - subscribers = subscribers.concat(this._subscribers['*']); + + // (delayed) display of a tooltip only if no mouse button is down + if (this.leftButtonDown) { + this._hideTooltip(); + return; } - for (var i = 0; i < subscribers.length; i++) { - var subscriber = subscribers[i]; - if (subscriber.callback) { - subscriber.callback(event, params, senderId || null); + if (this.tooltip && this.tooltip.dataPoint) { + // tooltip is currently visible + var dataPoint = this._dataPointFromXY(mouseX, mouseY); + if (dataPoint !== this.tooltip.dataPoint) { + // datapoint changed + if (dataPoint) { + this._showTooltip(dataPoint); + } + else { + this._hideTooltip(); + } } } + else { + // tooltip is currently not visible + var me = this; + this.tooltipTimeout = setTimeout(function () { + me.tooltipTimeout = null; + + // show a tooltip if we have a data point + var dataPoint = me._dataPointFromXY(mouseX, mouseY); + if (dataPoint) { + me._showTooltip(dataPoint); + } + }, delay); + } }; /** - * Add data. - * Adding an item will fail when there already is an item with the same id. - * @param {Object | Array | DataTable} data - * @param {String} [senderId] Optional sender id - * @return {Array} addedIds Array with the ids of the added items + * Event handler for touchstart event on mobile devices */ - DataSet.prototype.add = function (data, senderId) { - var addedIds = [], - id, - me = this; + Graph3d.prototype._onTouchStart = function(event) { + this.touchDown = true; - if (Array.isArray(data)) { - // Array - for (var i = 0, len = data.length; i < len; i++) { - id = me._addItem(data[i]); - addedIds.push(id); - } - } - else if (util.isDataTable(data)) { - // Google DataTable - var columns = this._getColumnNames(data); - for (var row = 0, rows = data.getNumberOfRows(); row < rows; row++) { - var item = {}; - for (var col = 0, cols = columns.length; col < cols; col++) { - var field = columns[col]; - item[field] = data.getValue(row, col); - } - - id = me._addItem(item); - addedIds.push(id); - } - } - else if (data instanceof Object) { - // Single item - id = me._addItem(data); - addedIds.push(id); - } - else { - throw new Error('Unknown dataType'); - } - - if (addedIds.length) { - this._trigger('add', {items: addedIds}, senderId); - } + var me = this; + this.ontouchmove = function (event) {me._onTouchMove(event);}; + this.ontouchend = function (event) {me._onTouchEnd(event);}; + util.addEventListener(document, 'touchmove', me.ontouchmove); + util.addEventListener(document, 'touchend', me.ontouchend); - return addedIds; + this._onMouseDown(event); }; /** - * Update existing items. When an item does not exist, it will be created - * @param {Object | Array | DataTable} data - * @param {String} [senderId] Optional sender id - * @return {Array} updatedIds The ids of the added or updated items + * Event handler for touchmove event on mobile devices */ - DataSet.prototype.update = function (data, senderId) { - var addedIds = []; - var updatedIds = []; - var updatedData = []; - var me = this; - var fieldId = me._fieldId; - - var addOrUpdate = function (item) { - var id = item[fieldId]; - if (me._data[id]) { - // update item - id = me._updateItem(item); - updatedIds.push(id); - updatedData.push(item); - } - else { - // add new item - id = me._addItem(item); - addedIds.push(id); - } - }; - - if (Array.isArray(data)) { - // Array - for (var i = 0, len = data.length; i < len; i++) { - addOrUpdate(data[i]); - } - } - else if (util.isDataTable(data)) { - // Google DataTable - var columns = this._getColumnNames(data); - for (var row = 0, rows = data.getNumberOfRows(); row < rows; row++) { - var item = {}; - for (var col = 0, cols = columns.length; col < cols; col++) { - var field = columns[col]; - item[field] = data.getValue(row, col); - } + Graph3d.prototype._onTouchMove = function(event) { + this._onMouseMove(event); + }; - addOrUpdate(item); - } - } - else if (data instanceof Object) { - // Single item - addOrUpdate(data); - } - else { - throw new Error('Unknown dataType'); - } + /** + * Event handler for touchend event on mobile devices + */ + Graph3d.prototype._onTouchEnd = function(event) { + this.touchDown = false; - if (addedIds.length) { - this._trigger('add', {items: addedIds}, senderId); - } - if (updatedIds.length) { - this._trigger('update', {items: updatedIds, data: updatedData}, senderId); - } + util.removeEventListener(document, 'touchmove', this.ontouchmove); + util.removeEventListener(document, 'touchend', this.ontouchend); - return addedIds.concat(updatedIds); + this._onMouseUp(event); }; + /** - * Get a data item or multiple items. - * - * Usage: - * - * get() - * get(options: Object) - * get(options: Object, data: Array | DataTable) - * - * get(id: Number | String) - * get(id: Number | String, options: Object) - * get(id: Number | String, options: Object, data: Array | DataTable) - * - * get(ids: Number[] | String[]) - * get(ids: Number[] | String[], options: Object) - * get(ids: Number[] | String[], options: Object, data: Array | DataTable) - * - * Where: - * - * {Number | String} id The id of an item - * {Number[] | String{}} ids An array with ids of items - * {Object} options An Object with options. Available options: - * {String} [returnType] Type of data to be - * returned. Can be 'DataTable' or 'Array' (default) - * {Object.} [type] - * {String[]} [fields] field names to be returned - * {function} [filter] filter items - * {String | function} [order] Order the items by - * a field name or custom sort function. - * {Array | DataTable} [data] If provided, items will be appended to this - * array or table. Required in case of Google - * DataTable. - * - * @throws Error + * Event handler for mouse wheel event, used to zoom the graph + * Code from http://adomas.org/javascript-mouse-wheel/ + * @param {event} event The event */ - DataSet.prototype.get = function (args) { - var me = this; + Graph3d.prototype._onWheel = function(event) { + if (!event) /* For IE. */ + event = window.event; - // parse the arguments - var id, ids, options, data; - var firstType = util.getType(arguments[0]); - if (firstType == 'String' || firstType == 'Number') { - // get(id [, options] [, data]) - id = arguments[0]; - options = arguments[1]; - data = arguments[2]; - } - else if (firstType == 'Array') { - // get(ids [, options] [, data]) - ids = arguments[0]; - options = arguments[1]; - data = arguments[2]; - } - else { - // get([, options] [, data]) - options = arguments[0]; - data = arguments[1]; + // retrieve delta + var delta = 0; + if (event.wheelDelta) { /* IE/Opera. */ + delta = event.wheelDelta/120; + } else if (event.detail) { /* Mozilla case. */ + // In Mozilla, sign of delta is different than in IE. + // Also, delta is multiple of 3. + delta = -event.detail/3; } - // determine the return type - var returnType; - if (options && options.returnType) { - var allowedValues = ["DataTable", "Array", "Object"]; - returnType = allowedValues.indexOf(options.returnType) == -1 ? "Array" : options.returnType; + // If delta is nonzero, handle it. + // Basically, delta is now positive if wheel was scrolled up, + // and negative, if wheel was scrolled down. + if (delta) { + var oldLength = this.camera.getArmLength(); + var newLength = oldLength * (1 - delta / 10); - if (data && (returnType != util.getType(data))) { - throw new Error('Type of parameter "data" (' + util.getType(data) + ') ' + - 'does not correspond with specified options.type (' + options.type + ')'); - } - if (returnType == 'DataTable' && !util.isDataTable(data)) { - throw new Error('Parameter "data" must be a DataTable ' + - 'when options.type is "DataTable"'); - } - } - else if (data) { - returnType = (util.getType(data) == 'DataTable') ? 'DataTable' : 'Array'; - } - else { - returnType = 'Array'; + this.camera.setArmLength(newLength); + this.redraw(); + + this._hideTooltip(); } - // build options - var type = options && options.type || this._options.type; - var filter = options && options.filter; - var items = [], item, itemId, i, len; + // fire a cameraPositionChange event + var parameters = this.getCameraPosition(); + this.emit('cameraPositionChange', parameters); - // convert items - if (id != undefined) { - // return a single item - item = me._getItem(id, type); - if (filter && !filter(item)) { - item = null; - } - } - else if (ids != undefined) { - // return a subset of items - for (i = 0, len = ids.length; i < len; i++) { - item = me._getItem(ids[i], type); - if (!filter || filter(item)) { - items.push(item); - } - } - } - else { - // return all items - for (itemId in this._data) { - if (this._data.hasOwnProperty(itemId)) { - item = me._getItem(itemId, type); - if (!filter || filter(item)) { - items.push(item); - } - } - } - } + // Prevent default actions caused by mouse wheel. + // That might be ugly, but we handle scrolls somehow + // anyway, so don't bother here.. + util.preventDefault(event); + }; - // order the results - if (options && options.order && id == undefined) { - this._sort(items, options.order); - } + /** + * Test whether a point lies inside given 2D triangle + * @param {Point2d} point + * @param {Point2d[]} triangle + * @return {boolean} Returns true if given point lies inside or on the edge of the triangle + * @private + */ + Graph3d.prototype._insideTriangle = function (point, triangle) { + var a = triangle[0], + b = triangle[1], + c = triangle[2]; - // filter fields of the items - if (options && options.fields) { - var fields = options.fields; - if (id != undefined) { - item = this._filterFields(item, fields); - } - else { - for (i = 0, len = items.length; i < len; i++) { - items[i] = this._filterFields(items[i], fields); - } - } + function sign (x) { + return x > 0 ? 1 : x < 0 ? -1 : 0; } - // return the results - if (returnType == 'DataTable') { - var columns = this._getColumnNames(data); - if (id != undefined) { - // append a single item to the data table - me._appendRow(data, columns, item); - } - else { - // copy the items to the provided data table - for (i = 0; i < items.length; i++) { - me._appendRow(data, columns, items[i]); - } - } - return data; - } - else if (returnType == "Object") { - var result = {}; - for (i = 0; i < items.length; i++) { - result[items[i].id] = items[i]; - } - return result; - } - else { - // return an array - if (id != undefined) { - // a single item - return item; - } - else { - // multiple items - if (data) { - // copy the items to the provided array - for (i = 0, len = items.length; i < len; i++) { - data.push(items[i]); - } - return data; - } - else { - // just return our array - return items; - } - } - } + var as = sign((b.x - a.x) * (point.y - a.y) - (b.y - a.y) * (point.x - a.x)); + var bs = sign((c.x - b.x) * (point.y - b.y) - (c.y - b.y) * (point.x - b.x)); + var cs = sign((a.x - c.x) * (point.y - c.y) - (a.y - c.y) * (point.x - c.x)); + + // each of the three signs must be either equal to each other or zero + return (as == 0 || bs == 0 || as == bs) && + (bs == 0 || cs == 0 || bs == cs) && + (as == 0 || cs == 0 || as == cs); }; /** - * Get ids of all items or from a filtered set of items. - * @param {Object} [options] An Object with options. Available options: - * {function} [filter] filter items - * {String | function} [order] Order the items by - * a field name or custom sort function. - * @return {Array} ids + * Find a data point close to given screen position (x, y) + * @param {Number} x + * @param {Number} y + * @return {Object | null} The closest data point or null if not close to any data point + * @private */ - DataSet.prototype.getIds = function (options) { - var data = this._data, - filter = options && options.filter, - order = options && options.order, - type = options && options.type || this._options.type, - i, - len, - id, - item, - items, - ids = []; - - if (filter) { - // get filtered items - if (order) { - // create ordered list - items = []; - for (id in data) { - if (data.hasOwnProperty(id)) { - item = this._getItem(id, type); - if (filter(item)) { - items.push(item); - } - } - } - - this._sort(items, order); + Graph3d.prototype._dataPointFromXY = function (x, y) { + var i, + distMax = 100, // px + dataPoint = null, + closestDataPoint = null, + closestDist = null, + center = new Point2d(x, y); - for (i = 0, len = items.length; i < len; i++) { - ids[i] = items[i][this._fieldId]; - } - } - else { - // create unordered list - for (id in data) { - if (data.hasOwnProperty(id)) { - item = this._getItem(id, type); - if (filter(item)) { - ids.push(item[this._fieldId]); + if (this.style === Graph3d.STYLE.BAR || + this.style === Graph3d.STYLE.BARCOLOR || + this.style === Graph3d.STYLE.BARSIZE) { + // the data points are ordered from far away to closest + for (i = this.dataPoints.length - 1; i >= 0; i--) { + dataPoint = this.dataPoints[i]; + var surfaces = dataPoint.surfaces; + if (surfaces) { + for (var s = surfaces.length - 1; s >= 0; s--) { + // split each surface in two triangles, and see if the center point is inside one of these + var surface = surfaces[s]; + var corners = surface.corners; + var triangle1 = [corners[0].screen, corners[1].screen, corners[2].screen]; + var triangle2 = [corners[2].screen, corners[3].screen, corners[0].screen]; + if (this._insideTriangle(center, triangle1) || + this._insideTriangle(center, triangle2)) { + // return immediately at the first hit + return dataPoint; } } } } } else { - // get all items - if (order) { - // create an ordered list - items = []; - for (id in data) { - if (data.hasOwnProperty(id)) { - items.push(data[id]); - } - } - - this._sort(items, order); + // find the closest data point, using distance to the center of the point on 2d screen + for (i = 0; i < this.dataPoints.length; i++) { + dataPoint = this.dataPoints[i]; + var point = dataPoint.screen; + if (point) { + var distX = Math.abs(x - point.x); + var distY = Math.abs(y - point.y); + var dist = Math.sqrt(distX * distX + distY * distY); - for (i = 0, len = items.length; i < len; i++) { - ids[i] = items[i][this._fieldId]; - } - } - else { - // create unordered list - for (id in data) { - if (data.hasOwnProperty(id)) { - item = data[id]; - ids.push(item[this._fieldId]); + if ((closestDist === null || dist < closestDist) && dist < distMax) { + closestDist = dist; + closestDataPoint = dataPoint; } } } } - return ids; - }; - /** - * Returns the DataSet itself. Is overwritten for example by the DataView, - * which returns the DataSet it is connected to instead. - */ - DataSet.prototype.getDataSet = function () { - return this; + return closestDataPoint; }; /** - * Execute a callback function for every item in the dataset. - * @param {function} callback - * @param {Object} [options] Available options: - * {Object.} [type] - * {String[]} [fields] filter fields - * {function} [filter] filter items - * {String | function} [order] Order the items by - * a field name or custom sort function. + * Display a tooltip for given data point + * @param {Object} dataPoint + * @private */ - DataSet.prototype.forEach = function (callback, options) { - var filter = options && options.filter, - type = options && options.type || this._options.type, - data = this._data, - item, - id; + Graph3d.prototype._showTooltip = function (dataPoint) { + var content, line, dot; - if (options && options.order) { - // execute forEach on ordered list - var items = this.get(options); + if (!this.tooltip) { + content = document.createElement('div'); + content.style.position = 'absolute'; + content.style.padding = '10px'; + content.style.border = '1px solid #4d4d4d'; + content.style.color = '#1a1a1a'; + content.style.background = 'rgba(255,255,255,0.7)'; + content.style.borderRadius = '2px'; + content.style.boxShadow = '5px 5px 10px rgba(128,128,128,0.5)'; - for (var i = 0, len = items.length; i < len; i++) { - item = items[i]; - id = item[this._fieldId]; - callback(item, id); - } + line = document.createElement('div'); + line.style.position = 'absolute'; + line.style.height = '40px'; + line.style.width = '0'; + line.style.borderLeft = '1px solid #4d4d4d'; + + dot = document.createElement('div'); + dot.style.position = 'absolute'; + dot.style.height = '0'; + dot.style.width = '0'; + dot.style.border = '5px solid #4d4d4d'; + dot.style.borderRadius = '5px'; + + this.tooltip = { + dataPoint: null, + dom: { + content: content, + line: line, + dot: dot + } + }; } else { - // unordered - for (id in data) { - if (data.hasOwnProperty(id)) { - item = this._getItem(id, type); - if (!filter || filter(item)) { - callback(item, id); - } - } - } + content = this.tooltip.dom.content; + line = this.tooltip.dom.line; + dot = this.tooltip.dom.dot; } - }; - /** - * Map every item in the dataset. - * @param {function} callback - * @param {Object} [options] Available options: - * {Object.} [type] - * {String[]} [fields] filter fields - * {function} [filter] filter items - * {String | function} [order] Order the items by - * a field name or custom sort function. - * @return {Object[]} mappedItems - */ - DataSet.prototype.map = function (callback, options) { - var filter = options && options.filter, - type = options && options.type || this._options.type, - mappedItems = [], - data = this._data, - item; + this._hideTooltip(); - // convert and filter items - for (var id in data) { - if (data.hasOwnProperty(id)) { - item = this._getItem(id, type); - if (!filter || filter(item)) { - mappedItems.push(callback(item, id)); - } - } + this.tooltip.dataPoint = dataPoint; + if (typeof this.showTooltip === 'function') { + content.innerHTML = this.showTooltip(dataPoint.point); } - - // order items - if (options && options.order) { - this._sort(mappedItems, options.order); + else { + content.innerHTML = '' + + '' + + '' + + '' + + '
x:' + dataPoint.point.x + '
y:' + dataPoint.point.y + '
z:' + dataPoint.point.z + '
'; } - return mappedItems; - }; + content.style.left = '0'; + content.style.top = '0'; + this.frame.appendChild(content); + this.frame.appendChild(line); + this.frame.appendChild(dot); - /** - * Filter the fields of an item - * @param {Object} item - * @param {String[]} fields Field names - * @return {Object} filteredItem - * @private - */ - DataSet.prototype._filterFields = function (item, fields) { - var filteredItem = {}; + // calculate sizes + var contentWidth = content.offsetWidth; + var contentHeight = content.offsetHeight; + var lineHeight = line.offsetHeight; + var dotWidth = dot.offsetWidth; + var dotHeight = dot.offsetHeight; - for (var field in item) { - if (item.hasOwnProperty(field) && (fields.indexOf(field) != -1)) { - filteredItem[field] = item[field]; - } - } + var left = dataPoint.screen.x - contentWidth / 2; + left = Math.min(Math.max(left, 10), this.frame.clientWidth - 10 - contentWidth); - return filteredItem; + line.style.left = dataPoint.screen.x + 'px'; + line.style.top = (dataPoint.screen.y - lineHeight) + 'px'; + content.style.left = left + 'px'; + content.style.top = (dataPoint.screen.y - lineHeight - contentHeight) + 'px'; + dot.style.left = (dataPoint.screen.x - dotWidth / 2) + 'px'; + dot.style.top = (dataPoint.screen.y - dotHeight / 2) + 'px'; }; /** - * Sort the provided array with items - * @param {Object[]} items - * @param {String | function} order A field name or custom sort function. + * Hide the tooltip when displayed * @private */ - DataSet.prototype._sort = function (items, order) { - if (util.isString(order)) { - // order by provided field name - var name = order; // field name - items.sort(function (a, b) { - var av = a[name]; - var bv = b[name]; - return (av > bv) ? 1 : ((av < bv) ? -1 : 0); - }); - } - else if (typeof order === 'function') { - // order by sort function - items.sort(order); - } - // TODO: extend order by an Object {field:String, direction:String} - // where direction can be 'asc' or 'desc' - else { - throw new TypeError('Order must be a function or a string'); - } - }; - - /** - * Remove an object by pointer or by id - * @param {String | Number | Object | Array} id Object or id, or an array with - * objects or ids to be removed - * @param {String} [senderId] Optional sender id - * @return {Array} removedIds - */ - DataSet.prototype.remove = function (id, senderId) { - var removedIds = [], - i, len, removedId; + Graph3d.prototype._hideTooltip = function () { + if (this.tooltip) { + this.tooltip.dataPoint = null; - if (Array.isArray(id)) { - for (i = 0, len = id.length; i < len; i++) { - removedId = this._remove(id[i]); - if (removedId != null) { - removedIds.push(removedId); + for (var prop in this.tooltip.dom) { + if (this.tooltip.dom.hasOwnProperty(prop)) { + var elem = this.tooltip.dom[prop]; + if (elem && elem.parentNode) { + elem.parentNode.removeChild(elem); + } } } } - else { - removedId = this._remove(id); - if (removedId != null) { - removedIds.push(removedId); - } - } + }; - if (removedIds.length) { - this._trigger('remove', {items: removedIds}, senderId); - } + /**--------------------------------------------------------------------------**/ - return removedIds; - }; /** - * Remove an item by its id - * @param {Number | String | Object} id id or item - * @returns {Number | String | null} id - * @private + * Get the horizontal mouse position from a mouse event + * @param {Event} event + * @return {Number} mouse x */ - DataSet.prototype._remove = function (id) { - if (util.isNumber(id) || util.isString(id)) { - if (this._data[id]) { - delete this._data[id]; - this.length--; - return id; - } - } - else if (id instanceof Object) { - var itemId = id[this._fieldId]; - if (itemId && this._data[itemId]) { - delete this._data[itemId]; - this.length--; - return itemId; - } - } - return null; - }; + function getMouseX (event) { + if ('clientX' in event) return event.clientX; + return event.targetTouches[0] && event.targetTouches[0].clientX || 0; + } /** - * Clear the data - * @param {String} [senderId] Optional sender id - * @return {Array} removedIds The ids of all removed items + * Get the vertical mouse position from a mouse event + * @param {Event} event + * @return {Number} mouse y */ - DataSet.prototype.clear = function (senderId) { - var ids = Object.keys(this._data); + function getMouseY (event) { + if ('clientY' in event) return event.clientY; + return event.targetTouches[0] && event.targetTouches[0].clientY || 0; + } - this._data = {}; - this.length = 0; + module.exports = Graph3d; - this._trigger('remove', {items: ids}, senderId); - return ids; - }; +/***/ }, +/* 7 */ +/***/ function(module, exports, __webpack_require__) { + + var Point3d = __webpack_require__(10); /** - * Find the item with maximum value of a specified field - * @param {String} field - * @return {Object | null} item Item containing max value, or null if no items + * @class Camera + * The camera is mounted on a (virtual) camera arm. The camera arm can rotate + * The camera is always looking in the direction of the origin of the arm. + * This way, the camera always rotates around one fixed point, the location + * of the camera arm. + * + * Documentation: + * http://en.wikipedia.org/wiki/3D_projection */ - DataSet.prototype.max = function (field) { - var data = this._data, - max = null, - maxField = null; + function Camera() { + this.armLocation = new Point3d(); + this.armRotation = {}; + this.armRotation.horizontal = 0; + this.armRotation.vertical = 0; + this.armLength = 1.7; - for (var id in data) { - if (data.hasOwnProperty(id)) { - var item = data[id]; - var itemField = item[field]; - if (itemField != null && (!max || itemField > maxField)) { - max = item; - maxField = itemField; - } - } - } + this.cameraLocation = new Point3d(); + this.cameraRotation = new Point3d(0.5*Math.PI, 0, 0); - return max; - }; + this.calculateCameraOrientation(); + } /** - * Find the item with minimum value of a specified field - * @param {String} field - * @return {Object | null} item Item containing max value, or null if no items + * Set the location (origin) of the arm + * @param {Number} x Normalized value of x + * @param {Number} y Normalized value of y + * @param {Number} z Normalized value of z */ - DataSet.prototype.min = function (field) { - var data = this._data, - min = null, - minField = null; - - for (var id in data) { - if (data.hasOwnProperty(id)) { - var item = data[id]; - var itemField = item[field]; - if (itemField != null && (!min || itemField < minField)) { - min = item; - minField = itemField; - } - } - } + Camera.prototype.setArmLocation = function(x, y, z) { + this.armLocation.x = x; + this.armLocation.y = y; + this.armLocation.z = z; - return min; + this.calculateCameraOrientation(); }; /** - * Find all distinct values of a specified field - * @param {String} field - * @return {Array} values Array containing all distinct values. If data items - * do not contain the specified field are ignored. - * The returned array is unordered. + * Set the rotation of the camera arm + * @param {Number} horizontal The horizontal rotation, between 0 and 2*PI. + * Optional, can be left undefined. + * @param {Number} vertical The vertical rotation, between 0 and 0.5*PI + * if vertical=0.5*PI, the graph is shown from the + * top. Optional, can be left undefined. */ - DataSet.prototype.distinct = function (field) { - var data = this._data; - var values = []; - var fieldType = this._options.type && this._options.type[field] || null; - var count = 0; - var i; - - for (var prop in data) { - if (data.hasOwnProperty(prop)) { - var item = data[prop]; - var value = item[field]; - var exists = false; - for (i = 0; i < count; i++) { - if (values[i] == value) { - exists = true; - break; - } - } - if (!exists && (value !== undefined)) { - values[count] = value; - count++; - } - } + Camera.prototype.setArmRotation = function(horizontal, vertical) { + if (horizontal !== undefined) { + this.armRotation.horizontal = horizontal; } - if (fieldType) { - for (i = 0; i < values.length; i++) { - values[i] = util.convert(values[i], fieldType); - } + if (vertical !== undefined) { + this.armRotation.vertical = vertical; + if (this.armRotation.vertical < 0) this.armRotation.vertical = 0; + if (this.armRotation.vertical > 0.5*Math.PI) this.armRotation.vertical = 0.5*Math.PI; } - return values; + if (horizontal !== undefined || vertical !== undefined) { + this.calculateCameraOrientation(); + } }; /** - * Add a single item. Will fail when an item with the same id already exists. - * @param {Object} item - * @return {String} id - * @private + * Retrieve the current arm rotation + * @return {object} An object with parameters horizontal and vertical */ - DataSet.prototype._addItem = function (item) { - var id = item[this._fieldId]; - - if (id != undefined) { - // check whether this id is already taken - if (this._data[id]) { - // item already exists - throw new Error('Cannot add item: item with id ' + id + ' already exists'); - } - } - else { - // generate an id - id = util.randomUUID(); - item[this._fieldId] = id; - } - - var d = {}; - for (var field in item) { - if (item.hasOwnProperty(field)) { - var fieldType = this._type[field]; // type may be undefined - d[field] = util.convert(item[field], fieldType); - } - } - this._data[id] = d; - this.length++; + Camera.prototype.getArmRotation = function() { + var rot = {}; + rot.horizontal = this.armRotation.horizontal; + rot.vertical = this.armRotation.vertical; - return id; + return rot; }; /** - * Get an item. Fields can be converted to a specific type - * @param {String} id - * @param {Object.} [types] field types to convert - * @return {Object | null} item - * @private + * Set the (normalized) length of the camera arm. + * @param {Number} length A length between 0.71 and 5.0 */ - DataSet.prototype._getItem = function (id, types) { - var field, value; + Camera.prototype.setArmLength = function(length) { + if (length === undefined) + return; - // get the item from the dataset - var raw = this._data[id]; - if (!raw) { - return null; - } + this.armLength = length; - // convert the items field types - var converted = {}; - if (types) { - for (field in raw) { - if (raw.hasOwnProperty(field)) { - value = raw[field]; - converted[field] = util.convert(value, types[field]); - } - } - } - else { - // no field types specified, no converting needed - for (field in raw) { - if (raw.hasOwnProperty(field)) { - value = raw[field]; - converted[field] = value; - } - } - } - return converted; + // Radius must be larger than the corner of the graph, + // which has a distance of sqrt(0.5^2+0.5^2) = 0.71 from the center of the + // graph + if (this.armLength < 0.71) this.armLength = 0.71; + if (this.armLength > 5.0) this.armLength = 5.0; + + this.calculateCameraOrientation(); }; /** - * Update a single item: merge with existing item. - * Will fail when the item has no id, or when there does not exist an item - * with the same id. - * @param {Object} item - * @return {String} id - * @private + * Retrieve the arm length + * @return {Number} length */ - DataSet.prototype._updateItem = function (item) { - var id = item[this._fieldId]; - if (id == undefined) { - throw new Error('Cannot update item: item has no id (item: ' + JSON.stringify(item) + ')'); - } - var d = this._data[id]; - if (!d) { - // item doesn't exist - throw new Error('Cannot update item: no item with id ' + id + ' found'); - } - - // merge with current item - for (var field in item) { - if (item.hasOwnProperty(field)) { - var fieldType = this._type[field]; // type may be undefined - d[field] = util.convert(item[field], fieldType); - } - } + Camera.prototype.getArmLength = function() { + return this.armLength; + }; - return id; + /** + * Retrieve the camera location + * @return {Point3d} cameraLocation + */ + Camera.prototype.getCameraLocation = function() { + return this.cameraLocation; }; /** - * Get an array with the column names of a Google DataTable - * @param {DataTable} dataTable - * @return {String[]} columnNames - * @private + * Retrieve the camera rotation + * @return {Point3d} cameraRotation */ - DataSet.prototype._getColumnNames = function (dataTable) { - var columns = []; - for (var col = 0, cols = dataTable.getNumberOfColumns(); col < cols; col++) { - columns[col] = dataTable.getColumnId(col) || dataTable.getColumnLabel(col); - } - return columns; + Camera.prototype.getCameraRotation = function() { + return this.cameraRotation; }; /** - * Append an item as a row to the dataTable - * @param dataTable - * @param columns - * @param item - * @private + * Calculate the location and rotation of the camera based on the + * position and orientation of the camera arm */ - DataSet.prototype._appendRow = function (dataTable, columns, item) { - var row = dataTable.addRow(); + Camera.prototype.calculateCameraOrientation = function() { + // calculate location of the camera + this.cameraLocation.x = this.armLocation.x - this.armLength * Math.sin(this.armRotation.horizontal) * Math.cos(this.armRotation.vertical); + this.cameraLocation.y = this.armLocation.y - this.armLength * Math.cos(this.armRotation.horizontal) * Math.cos(this.armRotation.vertical); + this.cameraLocation.z = this.armLocation.z + this.armLength * Math.sin(this.armRotation.vertical); - for (var col = 0, cols = columns.length; col < cols; col++) { - var field = columns[col]; - dataTable.setValue(row, col, item[field]); - } + // calculate rotation of the camera + this.cameraRotation.x = Math.PI/2 - this.armRotation.vertical; + this.cameraRotation.y = 0; + this.cameraRotation.z = -this.armRotation.horizontal; }; - module.exports = DataSet; - + module.exports = Camera; /***/ }, /* 8 */ /***/ function(module, exports, __webpack_require__) { - /** - * A queue - * @param {Object} options - * Available options: - * - delay: number When provided, the queue will be flushed - * automatically after an inactivity of this delay - * in milliseconds. - * Default value is null. - * - max: number When the queue exceeds the given maximum number - * of entries, the queue is flushed automatically. - * Default value of max is Infinity. - * @constructor - */ - function Queue(options) { - // options - this.delay = null; - this.max = Infinity; - - // properties - this._queue = []; - this._timeout = null; - this._extended = null; - - this.setOptions(options); - } + var DataView = __webpack_require__(4); /** - * Update the configuration of the queue - * @param {Object} options - * Available options: - * - delay: number When provided, the queue will be flushed - * automatically after an inactivity of this delay - * in milliseconds. - * Default value is null. - * - max: number When the queue exceeds the given maximum number - * of entries, the queue is flushed automatically. - * Default value of max is Infinity. - * @param options + * @class Filter + * + * @param {DataSet} data The google data table + * @param {Number} column The index of the column to be filtered + * @param {Graph} graph The graph */ - Queue.prototype.setOptions = function (options) { - if (options && typeof options.delay !== 'undefined') { - this.delay = options.delay; - } - if (options && typeof options.max !== 'undefined') { - this.max = options.max; - } + function Filter (data, column, graph) { + this.data = data; + this.column = column; + this.graph = graph; // the parent graph - this._flushIfNeeded(); - }; + this.index = undefined; + this.value = undefined; - /** - * Extend an object with queuing functionality. - * The object will be extended with a function flush, and the methods provided - * in options.replace will be replaced with queued ones. - * @param {Object} object - * @param {Object} options - * Available options: - * - replace: Array. - * A list with method names of the methods - * on the object to be replaced with queued ones. - * - delay: number When provided, the queue will be flushed - * automatically after an inactivity of this delay - * in milliseconds. - * Default value is null. - * - max: number When the queue exceeds the given maximum number - * of entries, the queue is flushed automatically. - * Default value of max is Infinity. - * @return {Queue} Returns the created queue - */ - Queue.extend = function (object, options) { - var queue = new Queue(options); + // read all distinct values and select the first one + this.values = graph.getDistinctValues(data.get(), this.column); - if (object.flush !== undefined) { - throw new Error('Target object already has a property flush'); + // sort both numeric and string values correctly + this.values.sort(function (a, b) { + return a > b ? 1 : a < b ? -1 : 0; + }); + + if (this.values.length > 0) { + this.selectValue(0); } - object.flush = function () { - queue.flush(); - }; - var methods = [{ - name: 'flush', - original: undefined - }]; + // create an array with the filtered datapoints. this will be loaded afterwards + this.dataPoints = []; - if (options && options.replace) { - for (var i = 0; i < options.replace.length; i++) { - var name = options.replace[i]; - methods.push({ - name: name, - original: object[name] - }); - queue.replace(object, name); - } + this.loaded = false; + this.onLoadCallback = undefined; + + if (graph.animationPreload) { + this.loaded = false; + this.loadInBackground(); } + else { + this.loaded = true; + } + }; - queue._extended = { - object: object, - methods: methods - }; - return queue; + /** + * Return the label + * @return {string} label + */ + Filter.prototype.isLoaded = function() { + return this.loaded; }; + /** - * Destroy the queue. The queue will first flush all queued actions, and in - * case it has extended an object, will restore the original object. + * Return the loaded progress + * @return {Number} percentage between 0 and 100 */ - Queue.prototype.destroy = function () { - this.flush(); + Filter.prototype.getLoadedProgress = function() { + var len = this.values.length; - if (this._extended) { - var object = this._extended.object; - var methods = this._extended.methods; - for (var i = 0; i < methods.length; i++) { - var method = methods[i]; - if (method.original) { - object[method.name] = method.original; - } - else { - delete object[method.name]; - } - } - this._extended = null; + var i = 0; + while (this.dataPoints[i]) { + i++; } + + return Math.round(i / len * 100); }; + /** - * Replace a method on an object with a queued version - * @param {Object} object Object having the method - * @param {string} method The method name + * Return the label + * @return {string} label */ - Queue.prototype.replace = function(object, method) { - var me = this; - var original = object[method]; - if (!original) { - throw new Error('Method ' + method + ' undefined'); - } + Filter.prototype.getLabel = function() { + return this.graph.filterLabel; + }; - object[method] = function () { - // create an Array with the arguments - var args = []; - for (var i = 0; i < arguments.length; i++) { - args[i] = arguments[i]; - } - // add this call to the queue - me.queue({ - args: args, - fn: original, - context: this - }); - }; + /** + * Return the columnIndex of the filter + * @return {Number} columnIndex + */ + Filter.prototype.getColumn = function() { + return this.column; }; /** - * Queue a call - * @param {function | {fn: function, args: Array} | {fn: function, args: Array, context: Object}} entry + * Return the currently selected value. Returns undefined if there is no selection + * @return {*} value */ - Queue.prototype.queue = function(entry) { - if (typeof entry === 'function') { - this._queue.push({fn: entry}); - } - else { - this._queue.push(entry); - } + Filter.prototype.getSelectedValue = function() { + if (this.index === undefined) + return undefined; - this._flushIfNeeded(); + return this.values[this.index]; }; /** - * Check whether the queue needs to be flushed - * @private + * Retrieve all values of the filter + * @return {Array} values */ - Queue.prototype._flushIfNeeded = function () { - // flush when the maximum is exceeded. - if (this._queue.length > this.max) { - this.flush(); - } + Filter.prototype.getValues = function() { + return this.values; + }; - // flush after a period of inactivity when a delay is configured - clearTimeout(this._timeout); - if (this.queue.length > 0 && typeof this.delay === 'number') { - var me = this; - this._timeout = setTimeout(function () { - me.flush(); - }, this.delay); - } + /** + * Retrieve one value of the filter + * @param {Number} index + * @return {*} value + */ + Filter.prototype.getValue = function(index) { + if (index >= this.values.length) + throw 'Error: index out of range'; + + return this.values[index]; }; + /** - * Flush all queued calls + * Retrieve the (filtered) dataPoints for the currently selected filter index + * @param {Number} [index] (optional) + * @return {Array} dataPoints */ - Queue.prototype.flush = function () { - while (this._queue.length > 0) { - var entry = this._queue.shift(); - entry.fn.apply(entry.context || entry.fn, entry.args || []); + Filter.prototype._getDataPoints = function(index) { + if (index === undefined) + index = this.index; + + if (index === undefined) + return []; + + var dataPoints; + if (this.dataPoints[index]) { + dataPoints = this.dataPoints[index]; } - }; + else { + var f = {}; + f.column = this.column; + f.value = this.values[index]; - module.exports = Queue; + var dataView = new DataView(this.data,{filter: function (item) {return (item[f.column] == f.value);}}).get(); + dataPoints = this.graph._getDataPoints(dataView); + this.dataPoints[index] = dataPoints; + } + + return dataPoints; + }; -/***/ }, -/* 9 */ -/***/ function(module, exports, __webpack_require__) { - var util = __webpack_require__(1); - var DataSet = __webpack_require__(7); /** - * DataView - * - * a dataview offers a filtered view on a dataset or an other dataview. - * - * @param {DataSet | DataView} data - * @param {Object} [options] Available options: see method get - * - * @constructor DataView + * Set a callback function when the filter is fully loaded. */ - function DataView (data, options) { - this._data = null; - this._ids = {}; // ids of the items currently in memory (just contains a boolean true) - this.length = 0; // number of items in the DataView - this._options = options || {}; - this._fieldId = 'id'; // name of the field containing id - this._subscribers = {}; // event subscribers + Filter.prototype.setOnLoadCallback = function(callback) { + this.onLoadCallback = callback; + }; - var me = this; - this.listener = function () { - me._onEvent.apply(me, arguments); - }; - this.setData(data); - } + /** + * Add a value to the list with available values for this filter + * No double entries will be created. + * @param {Number} index + */ + Filter.prototype.selectValue = function(index) { + if (index >= this.values.length) + throw 'Error: index out of range'; - // TODO: implement a function .config() to dynamically update things like configured filter - // and trigger changes accordingly + this.index = index; + this.value = this.values[index]; + }; /** - * Set a data source for the view - * @param {DataSet | DataView} data + * Load all filtered rows in the background one by one + * Start this method without providing an index! */ - DataView.prototype.setData = function (data) { - var ids, i, len; + Filter.prototype.loadInBackground = function(index) { + if (index === undefined) + index = 0; - if (this._data) { - // unsubscribe from current dataset - if (this._data.unsubscribe) { - this._data.unsubscribe('*', this.listener); - } + var frame = this.graph.frame; - // trigger a remove of all items in memory - ids = []; - for (var id in this._ids) { - if (this._ids.hasOwnProperty(id)) { - ids.push(id); - } - } - this._ids = {}; - this.length = 0; - this._trigger('remove', {items: ids}); - } + if (index < this.values.length) { + var dataPointsTemp = this._getDataPoints(index); + //this.graph.redrawInfo(); // TODO: not neat - this._data = data; + // create a progress box + if (frame.progress === undefined) { + frame.progress = document.createElement('DIV'); + frame.progress.style.position = 'absolute'; + frame.progress.style.color = 'gray'; + frame.appendChild(frame.progress); + } + var progress = this.getLoadedProgress(); + frame.progress.innerHTML = 'Loading animation... ' + progress + '%'; + // TODO: this is no nice solution... + frame.progress.style.bottom = 60 + 'px'; // TODO: use height of slider + frame.progress.style.left = 10 + 'px'; - if (this._data) { - // update fieldId - this._fieldId = this._options.fieldId || - (this._data && this._data.options && this._data.options.fieldId) || - 'id'; + var me = this; + setTimeout(function() {me.loadInBackground(index+1);}, 10); + this.loaded = false; + } + else { + this.loaded = true; - // trigger an add of all added items - ids = this._data.getIds({filter: this._options && this._options.filter}); - for (i = 0, len = ids.length; i < len; i++) { - id = ids[i]; - this._ids[id] = true; + // remove the progress box + if (frame.progress !== undefined) { + frame.removeChild(frame.progress); + frame.progress = undefined; } - this.length = ids.length; - this._trigger('add', {items: ids}); - // subscribe to new dataset - if (this._data.on) { - this._data.on('*', this.listener); - } + if (this.onLoadCallback) + this.onLoadCallback(); } }; + module.exports = Filter; + + +/***/ }, +/* 9 */ +/***/ function(module, exports, __webpack_require__) { + /** - * Get data from the data view - * - * Usage: - * - * get() - * get(options: Object) - * get(options: Object, data: Array | DataTable) - * - * get(id: Number) - * get(id: Number, options: Object) - * get(id: Number, options: Object, data: Array | DataTable) - * - * get(ids: Number[]) - * get(ids: Number[], options: Object) - * get(ids: Number[], options: Object, data: Array | DataTable) - * - * Where: - * - * {Number | String} id The id of an item - * {Number[] | String{}} ids An array with ids of items - * {Object} options An Object with options. Available options: - * {String} [type] Type of data to be returned. Can - * be 'DataTable' or 'Array' (default) - * {Object.} [convert] - * {String[]} [fields] field names to be returned - * {function} [filter] filter items - * {String | function} [order] Order the items by - * a field name or custom sort function. - * {Array | DataTable} [data] If provided, items will be appended to this - * array or table. Required in case of Google - * DataTable. - * @param args + * @prototype Point2d + * @param {Number} [x] + * @param {Number} [y] */ - DataView.prototype.get = function (args) { - var me = this; - - // parse the arguments - var ids, options, data; - var firstType = util.getType(arguments[0]); - if (firstType == 'String' || firstType == 'Number' || firstType == 'Array') { - // get(id(s) [, options] [, data]) - ids = arguments[0]; // can be a single id or an array with ids - options = arguments[1]; - data = arguments[2]; - } - else { - // get([, options] [, data]) - options = arguments[0]; - data = arguments[1]; - } + function Point2d (x, y) { + this.x = x !== undefined ? x : 0; + this.y = y !== undefined ? y : 0; + } - // extend the options with the default options and provided options - var viewOptions = util.extend({}, this._options, options); + module.exports = Point2d; - // create a combined filter method when needed - if (this._options.filter && options && options.filter) { - viewOptions.filter = function (item) { - return me._options.filter(item) && options.filter(item); - } - } - // build up the call to the linked data set - var getArguments = []; - if (ids != undefined) { - getArguments.push(ids); - } - getArguments.push(viewOptions); - getArguments.push(data); +/***/ }, +/* 10 */ +/***/ function(module, exports, __webpack_require__) { - return this._data && this._data.get.apply(this._data, getArguments); + /** + * @prototype Point3d + * @param {Number} [x] + * @param {Number} [y] + * @param {Number} [z] + */ + function Point3d(x, y, z) { + this.x = x !== undefined ? x : 0; + this.y = y !== undefined ? y : 0; + this.z = z !== undefined ? z : 0; }; /** - * Get ids of all items or from a filtered set of items. - * @param {Object} [options] An Object with options. Available options: - * {function} [filter] filter items - * {String | function} [order] Order the items by - * a field name or custom sort function. - * @return {Array} ids + * Subtract the two provided points, returns a-b + * @param {Point3d} a + * @param {Point3d} b + * @return {Point3d} a-b */ - DataView.prototype.getIds = function (options) { - var ids; - - if (this._data) { - var defaultFilter = this._options.filter; - var filter; - - if (options && options.filter) { - if (defaultFilter) { - filter = function (item) { - return defaultFilter(item) && options.filter(item); - } - } - else { - filter = options.filter; - } - } - else { - filter = defaultFilter; - } - - ids = this._data.getIds({ - filter: filter, - order: options && options.order - }); - } - else { - ids = []; - } - - return ids; + Point3d.subtract = function(a, b) { + var sub = new Point3d(); + sub.x = a.x - b.x; + sub.y = a.y - b.y; + sub.z = a.z - b.z; + return sub; }; /** - * Get the DataSet to which this DataView is connected. In case there is a chain - * of multiple DataViews, the root DataSet of this chain is returned. - * @return {DataSet} dataSet + * Add the two provided points, returns a+b + * @param {Point3d} a + * @param {Point3d} b + * @return {Point3d} a+b */ - DataView.prototype.getDataSet = function () { - var dataSet = this; - while (dataSet instanceof DataView) { - dataSet = dataSet._data; - } - return dataSet || null; + Point3d.add = function(a, b) { + var sum = new Point3d(); + sum.x = a.x + b.x; + sum.y = a.y + b.y; + sum.z = a.z + b.z; + return sum; }; /** - * Event listener. Will propagate all events from the connected data set to - * the subscribers of the DataView, but will filter the items and only trigger - * when there are changes in the filtered data set. - * @param {String} event - * @param {Object | null} params - * @param {String} senderId - * @private + * Calculate the average of two 3d points + * @param {Point3d} a + * @param {Point3d} b + * @return {Point3d} The average, (a+b)/2 */ - DataView.prototype._onEvent = function (event, params, senderId) { - var i, len, id, item, - ids = params && params.items, - data = this._data, - added = [], - updated = [], - removed = []; + Point3d.avg = function(a, b) { + return new Point3d( + (a.x + b.x) / 2, + (a.y + b.y) / 2, + (a.z + b.z) / 2 + ); + }; - if (ids && data) { - switch (event) { - case 'add': - // filter the ids of the added items - for (i = 0, len = ids.length; i < len; i++) { - id = ids[i]; - item = this.get(id); - if (item) { - this._ids[id] = true; - added.push(id); - } - } - - break; - - case 'update': - // determine the event from the views viewpoint: an updated - // item can be added, updated, or removed from this view. - for (i = 0, len = ids.length; i < len; i++) { - id = ids[i]; - item = this.get(id); - - if (item) { - if (this._ids[id]) { - updated.push(id); - } - else { - this._ids[id] = true; - added.push(id); - } - } - else { - if (this._ids[id]) { - delete this._ids[id]; - removed.push(id); - } - else { - // nothing interesting for me :-( - } - } - } - - break; + /** + * Calculate the cross product of the two provided points, returns axb + * Documentation: http://en.wikipedia.org/wiki/Cross_product + * @param {Point3d} a + * @param {Point3d} b + * @return {Point3d} cross product axb + */ + Point3d.crossProduct = function(a, b) { + var crossproduct = new Point3d(); - case 'remove': - // filter the ids of the removed items - for (i = 0, len = ids.length; i < len; i++) { - id = ids[i]; - if (this._ids[id]) { - delete this._ids[id]; - removed.push(id); - } - } + crossproduct.x = a.y * b.z - a.z * b.y; + crossproduct.y = a.z * b.x - a.x * b.z; + crossproduct.z = a.x * b.y - a.y * b.x; - break; - } + return crossproduct; + }; - this.length += added.length - removed.length; - if (added.length) { - this._trigger('add', {items: added}, senderId); - } - if (updated.length) { - this._trigger('update', {items: updated}, senderId); - } - if (removed.length) { - this._trigger('remove', {items: removed}, senderId); - } - } + /** + * Rtrieve the length of the vector (or the distance from this point to the origin + * @return {Number} length + */ + Point3d.prototype.length = function() { + return Math.sqrt( + this.x * this.x + + this.y * this.y + + this.z * this.z + ); }; - // copy subscription functionality from DataSet - DataView.prototype.on = DataSet.prototype.on; - DataView.prototype.off = DataSet.prototype.off; - DataView.prototype._trigger = DataSet.prototype._trigger; - - // TODO: make these functions deprecated (replaced with `on` and `off` since version 0.5) - DataView.prototype.subscribe = DataView.prototype.on; - DataView.prototype.unsubscribe = DataView.prototype.off; + module.exports = Point3d; - module.exports = DataView; /***/ }, -/* 10 */ +/* 11 */ /***/ function(module, exports, __webpack_require__) { - var Emitter = __webpack_require__(11); - var DataSet = __webpack_require__(7); - var DataView = __webpack_require__(9); var util = __webpack_require__(1); - var Point3d = __webpack_require__(12); - var Point2d = __webpack_require__(13); - var Camera = __webpack_require__(14); - var Filter = __webpack_require__(15); - var Slider = __webpack_require__(16); - var StepNumber = __webpack_require__(17); /** - * @constructor Graph3d - * Graph3d displays data in 3d. - * - * Graph3d is developed in javascript as a Google Visualization Chart. + * @constructor Slider * - * @param {Element} container The DOM element in which the Graph3d will - * be created. Normally a div element. - * @param {DataSet | DataView | Array} [data] - * @param {Object} [options] + * An html slider control with start/stop/prev/next buttons + * @param {Element} container The element where the slider will be created + * @param {Object} options Available options: + * {boolean} visible If true (default) the + * slider is visible. */ - function Graph3d(container, data, options) { - if (!(this instanceof Graph3d)) { - throw new SyntaxError('Constructor must be called with the new operator'); + function Slider(container, options) { + if (container === undefined) { + throw 'Error: No container element defined'; } + this.container = container; + this.visible = (options && options.visible != undefined) ? options.visible : true; - // create variables and set default values - this.containerElement = container; - this.width = '400px'; - this.height = '400px'; - this.margin = 10; // px - this.defaultXCenter = '55%'; - this.defaultYCenter = '50%'; - - this.xLabel = 'x'; - this.yLabel = 'y'; - this.zLabel = 'z'; - - var passValueFn = function(v) { return v; }; - this.xValueLabel = passValueFn; - this.yValueLabel = passValueFn; - this.zValueLabel = passValueFn; - - this.filterLabel = 'time'; - this.legendLabel = 'value'; + if (this.visible) { + this.frame = document.createElement('DIV'); + //this.frame.style.backgroundColor = '#E5E5E5'; + this.frame.style.width = '100%'; + this.frame.style.position = 'relative'; + this.container.appendChild(this.frame); - this.style = Graph3d.STYLE.DOT; - this.showPerspective = true; - this.showGrid = true; - this.keepAspectRatio = true; - this.showShadow = false; - this.showGrayBottom = false; // TODO: this does not work correctly - this.showTooltip = false; - this.verticalRatio = 0.5; // 0.1 to 1.0, where 1.0 results in a 'cube' + this.frame.prev = document.createElement('INPUT'); + this.frame.prev.type = 'BUTTON'; + this.frame.prev.value = 'Prev'; + this.frame.appendChild(this.frame.prev); - this.animationInterval = 1000; // milliseconds - this.animationPreload = false; + this.frame.play = document.createElement('INPUT'); + this.frame.play.type = 'BUTTON'; + this.frame.play.value = 'Play'; + this.frame.appendChild(this.frame.play); - this.camera = new Camera(); - this.eye = new Point3d(0, 0, -1); // TODO: set eye.z about 3/4 of the width of the window? + this.frame.next = document.createElement('INPUT'); + this.frame.next.type = 'BUTTON'; + this.frame.next.value = 'Next'; + this.frame.appendChild(this.frame.next); - this.dataTable = null; // The original data table - this.dataPoints = null; // The table with point objects + this.frame.bar = document.createElement('INPUT'); + this.frame.bar.type = 'BUTTON'; + this.frame.bar.style.position = 'absolute'; + this.frame.bar.style.border = '1px solid red'; + this.frame.bar.style.width = '100px'; + this.frame.bar.style.height = '6px'; + this.frame.bar.style.borderRadius = '2px'; + this.frame.bar.style.MozBorderRadius = '2px'; + this.frame.bar.style.border = '1px solid #7F7F7F'; + this.frame.bar.style.backgroundColor = '#E5E5E5'; + this.frame.appendChild(this.frame.bar); - // the column indexes - this.colX = undefined; - this.colY = undefined; - this.colZ = undefined; - this.colValue = undefined; - this.colFilter = undefined; + this.frame.slide = document.createElement('INPUT'); + this.frame.slide.type = 'BUTTON'; + this.frame.slide.style.margin = '0px'; + this.frame.slide.value = ' '; + this.frame.slide.style.position = 'relative'; + this.frame.slide.style.left = '-100px'; + this.frame.appendChild(this.frame.slide); - this.xMin = 0; - this.xStep = undefined; // auto by default - this.xMax = 1; - this.yMin = 0; - this.yStep = undefined; // auto by default - this.yMax = 1; - this.zMin = 0; - this.zStep = undefined; // auto by default - this.zMax = 1; - this.valueMin = 0; - this.valueMax = 1; - this.xBarWidth = 1; - this.yBarWidth = 1; - // TODO: customize axis range + // create events + var me = this; + this.frame.slide.onmousedown = function (event) {me._onMouseDown(event);}; + this.frame.prev.onclick = function (event) {me.prev(event);}; + this.frame.play.onclick = function (event) {me.togglePlay(event);}; + this.frame.next.onclick = function (event) {me.next(event);}; + } - // constants - this.colorAxis = '#4D4D4D'; - this.colorGrid = '#D3D3D3'; - this.colorDot = '#7DC1FF'; - this.colorDotBorder = '#3267D2'; + this.onChangeCallback = undefined; - // create a frame and canvas - this.create(); + this.values = []; + this.index = undefined; - // apply options (also when undefined) - this.setOptions(options); + this.playTimeout = undefined; + this.playInterval = 1000; // milliseconds + this.playLoop = true; + } - // apply data - if (data) { - this.setData(data); + /** + * Select the previous index + */ + Slider.prototype.prev = function() { + var index = this.getIndex(); + if (index > 0) { + index--; + this.setIndex(index); } - } + }; - // Extend Graph3d with an Emitter mixin - Emitter(Graph3d.prototype); + /** + * Select the next index + */ + Slider.prototype.next = function() { + var index = this.getIndex(); + if (index < this.values.length - 1) { + index++; + this.setIndex(index); + } + }; /** - * Calculate the scaling values, dependent on the range in x, y, and z direction + * Select the next index */ - Graph3d.prototype._setScale = function() { - this.scale = new Point3d(1 / (this.xMax - this.xMin), - 1 / (this.yMax - this.yMin), - 1 / (this.zMax - this.zMin)); + Slider.prototype.playNext = function() { + var start = new Date(); - // keep aspect ration between x and y scale if desired - if (this.keepAspectRatio) { - if (this.scale.x < this.scale.y) { - //noinspection JSSuspiciousNameCombination - this.scale.y = this.scale.x; - } - else { - //noinspection JSSuspiciousNameCombination - this.scale.x = this.scale.y; - } + var index = this.getIndex(); + if (index < this.values.length - 1) { + index++; + this.setIndex(index); + } + else if (this.playLoop) { + // jump to the start + index = 0; + this.setIndex(index); } - // scale the vertical axis - this.scale.z *= this.verticalRatio; - // TODO: can this be automated? verticalRatio? + var end = new Date(); + var diff = (end - start); - // determine scale for (optional) value - this.scale.value = 1 / (this.valueMax - this.valueMin); + // calculate how much time it to to set the index and to execute the callback + // function. + var interval = Math.max(this.playInterval - diff, 0); + // document.title = diff // TODO: cleanup - // position the camera arm - var xCenter = (this.xMax + this.xMin) / 2 * this.scale.x; - var yCenter = (this.yMax + this.yMin) / 2 * this.scale.y; - var zCenter = (this.zMax + this.zMin) / 2 * this.scale.z; - this.camera.setArmLocation(xCenter, yCenter, zCenter); + var me = this; + this.playTimeout = setTimeout(function() {me.playNext();}, interval); }; - /** - * Convert a 3D location to a 2D location on screen - * http://en.wikipedia.org/wiki/3D_projection - * @param {Point3d} point3d A 3D point with parameters x, y, z - * @return {Point2d} point2d A 2D point with parameters x, y + * Toggle start or stop playing */ - Graph3d.prototype._convert3Dto2D = function(point3d) { - var translation = this._convertPointToTranslation(point3d); - return this._convertTranslationToScreen(translation); + Slider.prototype.togglePlay = function() { + if (this.playTimeout === undefined) { + this.play(); + } else { + this.stop(); + } }; /** - * Convert a 3D location its translation seen from the camera - * http://en.wikipedia.org/wiki/3D_projection - * @param {Point3d} point3d A 3D point with parameters x, y, z - * @return {Point3d} translation A 3D point with parameters x, y, z This is - * the translation of the point, seen from the - * camera + * Start playing */ - Graph3d.prototype._convertPointToTranslation = function(point3d) { - var ax = point3d.x * this.scale.x, - ay = point3d.y * this.scale.y, - az = point3d.z * this.scale.z, - - cx = this.camera.getCameraLocation().x, - cy = this.camera.getCameraLocation().y, - cz = this.camera.getCameraLocation().z, - - // calculate angles - sinTx = Math.sin(this.camera.getCameraRotation().x), - cosTx = Math.cos(this.camera.getCameraRotation().x), - sinTy = Math.sin(this.camera.getCameraRotation().y), - cosTy = Math.cos(this.camera.getCameraRotation().y), - sinTz = Math.sin(this.camera.getCameraRotation().z), - cosTz = Math.cos(this.camera.getCameraRotation().z), + Slider.prototype.play = function() { + // Test whether already playing + if (this.playTimeout) return; - // calculate translation - dx = cosTy * (sinTz * (ay - cy) + cosTz * (ax - cx)) - sinTy * (az - cz), - dy = sinTx * (cosTy * (az - cz) + sinTy * (sinTz * (ay - cy) + cosTz * (ax - cx))) + cosTx * (cosTz * (ay - cy) - sinTz * (ax-cx)), - dz = cosTx * (cosTy * (az - cz) + sinTy * (sinTz * (ay - cy) + cosTz * (ax - cx))) - sinTx * (cosTz * (ay - cy) - sinTz * (ax-cx)); + this.playNext(); - return new Point3d(dx, dy, dz); + if (this.frame) { + this.frame.play.value = 'Stop'; + } }; /** - * Convert a translation point to a point on the screen - * @param {Point3d} translation A 3D point with parameters x, y, z This is - * the translation of the point, seen from the - * camera - * @return {Point2d} point2d A 2D point with parameters x, y + * Stop playing */ - Graph3d.prototype._convertTranslationToScreen = function(translation) { - var ex = this.eye.x, - ey = this.eye.y, - ez = this.eye.z, - dx = translation.x, - dy = translation.y, - dz = translation.z; + Slider.prototype.stop = function() { + clearInterval(this.playTimeout); + this.playTimeout = undefined; - // calculate position on screen from translation - var bx; - var by; - if (this.showPerspective) { - bx = (dx - ex) * (ez / dz); - by = (dy - ey) * (ez / dz); - } - else { - bx = dx * -(ez / this.camera.getArmLength()); - by = dy * -(ez / this.camera.getArmLength()); + if (this.frame) { + this.frame.play.value = 'Play'; } - - // shift and scale the point to the center of the screen - // use the width of the graph to scale both horizontally and vertically. - return new Point2d( - this.xcenter + bx * this.frame.canvas.clientWidth, - this.ycenter - by * this.frame.canvas.clientWidth); }; /** - * Set the background styling for the graph - * @param {string | {fill: string, stroke: string, strokeWidth: string}} backgroundColor + * Set a callback function which will be triggered when the value of the + * slider bar has changed. */ - Graph3d.prototype._setBackgroundColor = function(backgroundColor) { - var fill = 'white'; - var stroke = 'gray'; - var strokeWidth = 1; - - if (typeof(backgroundColor) === 'string') { - fill = backgroundColor; - stroke = 'none'; - strokeWidth = 0; - } - else if (typeof(backgroundColor) === 'object') { - if (backgroundColor.fill !== undefined) fill = backgroundColor.fill; - if (backgroundColor.stroke !== undefined) stroke = backgroundColor.stroke; - if (backgroundColor.strokeWidth !== undefined) strokeWidth = backgroundColor.strokeWidth; - } - else if (backgroundColor === undefined) { - // use use defaults - } - else { - throw 'Unsupported type of backgroundColor'; - } + Slider.prototype.setOnChangeCallback = function(callback) { + this.onChangeCallback = callback; + }; - this.frame.style.backgroundColor = fill; - this.frame.style.borderColor = stroke; - this.frame.style.borderWidth = strokeWidth + 'px'; - this.frame.style.borderStyle = 'solid'; + /** + * Set the interval for playing the list + * @param {Number} interval The interval in milliseconds + */ + Slider.prototype.setPlayInterval = function(interval) { + this.playInterval = interval; }; + /** + * Retrieve the current play interval + * @return {Number} interval The interval in milliseconds + */ + Slider.prototype.getPlayInterval = function(interval) { + return this.playInterval; + }; - /// enumerate the available styles - Graph3d.STYLE = { - BAR: 0, - BARCOLOR: 1, - BARSIZE: 2, - DOT : 3, - DOTLINE : 4, - DOTCOLOR: 5, - DOTSIZE: 6, - GRID : 7, - LINE: 8, - SURFACE : 9 + /** + * Set looping on or off + * @pararm {boolean} doLoop If true, the slider will jump to the start when + * the end is passed, and will jump to the end + * when the start is passed. + */ + Slider.prototype.setPlayLoop = function(doLoop) { + this.playLoop = doLoop; }; + /** - * Retrieve the style index from given styleName - * @param {string} styleName Style name such as 'dot', 'grid', 'dot-line' - * @return {Number} styleNumber Enumeration value representing the style, or -1 - * when not found + * Execute the onchange callback function */ - Graph3d.prototype._getStyleNumber = function(styleName) { - switch (styleName) { - case 'dot': return Graph3d.STYLE.DOT; - case 'dot-line': return Graph3d.STYLE.DOTLINE; - case 'dot-color': return Graph3d.STYLE.DOTCOLOR; - case 'dot-size': return Graph3d.STYLE.DOTSIZE; - case 'line': return Graph3d.STYLE.LINE; - case 'grid': return Graph3d.STYLE.GRID; - case 'surface': return Graph3d.STYLE.SURFACE; - case 'bar': return Graph3d.STYLE.BAR; - case 'bar-color': return Graph3d.STYLE.BARCOLOR; - case 'bar-size': return Graph3d.STYLE.BARSIZE; + Slider.prototype.onChange = function() { + if (this.onChangeCallback !== undefined) { + this.onChangeCallback(); } - - return -1; }; /** - * Determine the indexes of the data columns, based on the given style and data - * @param {DataSet} data - * @param {Number} style + * redraw the slider on the correct place */ - Graph3d.prototype._determineColumnIndexes = function(data, style) { - if (this.style === Graph3d.STYLE.DOT || - this.style === Graph3d.STYLE.DOTLINE || - this.style === Graph3d.STYLE.LINE || - this.style === Graph3d.STYLE.GRID || - this.style === Graph3d.STYLE.SURFACE || - this.style === Graph3d.STYLE.BAR) { - // 3 columns expected, and optionally a 4th with filter values - this.colX = 0; - this.colY = 1; - this.colZ = 2; - this.colValue = undefined; + Slider.prototype.redraw = function() { + if (this.frame) { + // resize the bar + this.frame.bar.style.top = (this.frame.clientHeight/2 - + this.frame.bar.offsetHeight/2) + 'px'; + this.frame.bar.style.width = (this.frame.clientWidth - + this.frame.prev.clientWidth - + this.frame.play.clientWidth - + this.frame.next.clientWidth - 30) + 'px'; - if (data.getNumberOfColumns() > 3) { - this.colFilter = 3; - } + // position the slider button + var left = this.indexToLeft(this.index); + this.frame.slide.style.left = (left) + 'px'; } - else if (this.style === Graph3d.STYLE.DOTCOLOR || - this.style === Graph3d.STYLE.DOTSIZE || - this.style === Graph3d.STYLE.BARCOLOR || - this.style === Graph3d.STYLE.BARSIZE) { - // 4 columns expected, and optionally a 5th with filter values - this.colX = 0; - this.colY = 1; - this.colZ = 2; - this.colValue = 3; + }; - if (data.getNumberOfColumns() > 4) { - this.colFilter = 4; - } - } - else { - throw 'Unknown style "' + this.style + '"'; - } - }; - - Graph3d.prototype.getNumberOfRows = function(data) { - return data.length; - } - - - Graph3d.prototype.getNumberOfColumns = function(data) { - var counter = 0; - for (var column in data[0]) { - if (data[0].hasOwnProperty(column)) { - counter++; - } - } - return counter; - } - - - Graph3d.prototype.getDistinctValues = function(data, column) { - var distinctValues = []; - for (var i = 0; i < data.length; i++) { - if (distinctValues.indexOf(data[i][column]) == -1) { - distinctValues.push(data[i][column]); - } - } - return distinctValues; - } + /** + * Set the list with values for the slider + * @param {Array} values A javascript array with values (any type) + */ + Slider.prototype.setValues = function(values) { + this.values = values; - Graph3d.prototype.getColumnRange = function(data,column) { - var minMax = {min:data[0][column],max:data[0][column]}; - for (var i = 0; i < data.length; i++) { - if (minMax.min > data[i][column]) { minMax.min = data[i][column]; } - if (minMax.max < data[i][column]) { minMax.max = data[i][column]; } - } - return minMax; + if (this.values.length > 0) + this.setIndex(0); + else + this.index = undefined; }; /** - * Initialize the data from the data table. Calculate minimum and maximum values - * and column index values - * @param {Array | DataSet | DataView} rawData The data containing the items for the Graph. - * @param {Number} style Style Number + * Select a value by its index + * @param {Number} index */ - Graph3d.prototype._dataInitialize = function (rawData, style) { - var me = this; - - // unsubscribe from the dataTable - if (this.dataSet) { - this.dataSet.off('*', this._onChange); - } - - if (rawData === undefined) - return; - - if (Array.isArray(rawData)) { - rawData = new DataSet(rawData); - } + Slider.prototype.setIndex = function(index) { + if (index < this.values.length) { + this.index = index; - var data; - if (rawData instanceof DataSet || rawData instanceof DataView) { - data = rawData.get(); + this.redraw(); + this.onChange(); } else { - throw new Error('Array, DataSet, or DataView expected'); + throw 'Error: index out of range'; } + }; - if (data.length == 0) - return; - - this.dataSet = rawData; - this.dataTable = data; - - // subscribe to changes in the dataset - this._onChange = function () { - me.setData(me.dataSet); - }; - this.dataSet.on('*', this._onChange); + /** + * retrieve the index of the currently selected vaue + * @return {Number} index + */ + Slider.prototype.getIndex = function() { + return this.index; + }; - // _determineColumnIndexes - // getNumberOfRows (points) - // getNumberOfColumns (x,y,z,v,t,t1,t2...) - // getDistinctValues (unique values?) - // getColumnRange - // determine the location of x,y,z,value,filter columns - this.colX = 'x'; - this.colY = 'y'; - this.colZ = 'z'; - this.colValue = 'style'; - this.colFilter = 'filter'; + /** + * retrieve the currently selected value + * @return {*} value + */ + Slider.prototype.get = function() { + return this.values[this.index]; + }; + Slider.prototype._onMouseDown = function(event) { + // only react on left mouse button down + var leftButtonDown = event.which ? (event.which === 1) : (event.button === 1); + if (!leftButtonDown) return; - // check if a filter column is provided - if (data[0].hasOwnProperty('filter')) { - if (this.dataFilter === undefined) { - this.dataFilter = new Filter(rawData, this.colFilter, this); - this.dataFilter.setOnLoadCallback(function() {me.redraw();}); - } - } + this.startClientX = event.clientX; + this.startSlideX = parseFloat(this.frame.slide.style.left); + this.frame.style.cursor = 'move'; - var withBars = this.style == Graph3d.STYLE.BAR || - this.style == Graph3d.STYLE.BARCOLOR || - this.style == Graph3d.STYLE.BARSIZE; + // add event listeners to handle moving the contents + // we store the function onmousemove and onmouseup in the graph, so we can + // remove the eventlisteners lateron in the function mouseUp() + var me = this; + this.onmousemove = function (event) {me._onMouseMove(event);}; + this.onmouseup = function (event) {me._onMouseUp(event);}; + util.addEventListener(document, 'mousemove', this.onmousemove); + util.addEventListener(document, 'mouseup', this.onmouseup); + util.preventDefault(event); + }; - // determine barWidth from data - if (withBars) { - if (this.defaultXBarWidth !== undefined) { - this.xBarWidth = this.defaultXBarWidth; - } - else { - var dataX = this.getDistinctValues(data,this.colX); - this.xBarWidth = (dataX[1] - dataX[0]) || 1; - } - if (this.defaultYBarWidth !== undefined) { - this.yBarWidth = this.defaultYBarWidth; - } - else { - var dataY = this.getDistinctValues(data,this.colY); - this.yBarWidth = (dataY[1] - dataY[0]) || 1; - } - } + Slider.prototype.leftToIndex = function (left) { + var width = parseFloat(this.frame.bar.style.width) - + this.frame.slide.clientWidth - 10; + var x = left - 3; - // calculate minimums and maximums - var xRange = this.getColumnRange(data,this.colX); - if (withBars) { - xRange.min -= this.xBarWidth / 2; - xRange.max += this.xBarWidth / 2; - } - this.xMin = (this.defaultXMin !== undefined) ? this.defaultXMin : xRange.min; - this.xMax = (this.defaultXMax !== undefined) ? this.defaultXMax : xRange.max; - if (this.xMax <= this.xMin) this.xMax = this.xMin + 1; - this.xStep = (this.defaultXStep !== undefined) ? this.defaultXStep : (this.xMax-this.xMin)/5; + var index = Math.round(x / width * (this.values.length-1)); + if (index < 0) index = 0; + if (index > this.values.length-1) index = this.values.length-1; - var yRange = this.getColumnRange(data,this.colY); - if (withBars) { - yRange.min -= this.yBarWidth / 2; - yRange.max += this.yBarWidth / 2; - } - this.yMin = (this.defaultYMin !== undefined) ? this.defaultYMin : yRange.min; - this.yMax = (this.defaultYMax !== undefined) ? this.defaultYMax : yRange.max; - if (this.yMax <= this.yMin) this.yMax = this.yMin + 1; - this.yStep = (this.defaultYStep !== undefined) ? this.defaultYStep : (this.yMax-this.yMin)/5; + return index; + }; - var zRange = this.getColumnRange(data,this.colZ); - this.zMin = (this.defaultZMin !== undefined) ? this.defaultZMin : zRange.min; - this.zMax = (this.defaultZMax !== undefined) ? this.defaultZMax : zRange.max; - if (this.zMax <= this.zMin) this.zMax = this.zMin + 1; - this.zStep = (this.defaultZStep !== undefined) ? this.defaultZStep : (this.zMax-this.zMin)/5; + Slider.prototype.indexToLeft = function (index) { + var width = parseFloat(this.frame.bar.style.width) - + this.frame.slide.clientWidth - 10; - if (this.colValue !== undefined) { - var valueRange = this.getColumnRange(data,this.colValue); - this.valueMin = (this.defaultValueMin !== undefined) ? this.defaultValueMin : valueRange.min; - this.valueMax = (this.defaultValueMax !== undefined) ? this.defaultValueMax : valueRange.max; - if (this.valueMax <= this.valueMin) this.valueMax = this.valueMin + 1; - } + var x = index / (this.values.length-1) * width; + var left = x + 3; - // set the scale dependent on the ranges. - this._setScale(); + return left; }; - /** - * Filter the data based on the current filter - * @param {Array} data - * @return {Array} dataPoints Array with point objects which can be drawn on screen - */ - Graph3d.prototype._getDataPoints = function (data) { - // TODO: store the created matrix dataPoints in the filters instead of reloading each time - var x, y, i, z, obj, point; + Slider.prototype._onMouseMove = function (event) { + var diff = event.clientX - this.startClientX; + var x = this.startSlideX + diff; - var dataPoints = []; + var index = this.leftToIndex(x); - if (this.style === Graph3d.STYLE.GRID || - this.style === Graph3d.STYLE.SURFACE) { - // copy all values from the google data table to a matrix - // the provided values are supposed to form a grid of (x,y) positions + this.setIndex(index); - // create two lists with all present x and y values - var dataX = []; - var dataY = []; - for (i = 0; i < this.getNumberOfRows(data); i++) { - x = data[i][this.colX] || 0; - y = data[i][this.colY] || 0; + util.preventDefault(); + }; - if (dataX.indexOf(x) === -1) { - dataX.push(x); - } - if (dataY.indexOf(y) === -1) { - dataY.push(y); - } - } - var sortNumber = function (a, b) { - return a - b; - }; - dataX.sort(sortNumber); - dataY.sort(sortNumber); + Slider.prototype._onMouseUp = function (event) { + this.frame.style.cursor = 'auto'; - // create a grid, a 2d matrix, with all values. - var dataMatrix = []; // temporary data matrix - for (i = 0; i < data.length; i++) { - x = data[i][this.colX] || 0; - y = data[i][this.colY] || 0; - z = data[i][this.colZ] || 0; + // remove event listeners + util.removeEventListener(document, 'mousemove', this.onmousemove); + util.removeEventListener(document, 'mouseup', this.onmouseup); - var xIndex = dataX.indexOf(x); // TODO: implement Array().indexOf() for Internet Explorer - var yIndex = dataY.indexOf(y); + util.preventDefault(); + }; - if (dataMatrix[xIndex] === undefined) { - dataMatrix[xIndex] = []; - } + module.exports = Slider; - var point3d = new Point3d(); - point3d.x = x; - point3d.y = y; - point3d.z = z; - obj = {}; - obj.point = point3d; - obj.trans = undefined; - obj.screen = undefined; - obj.bottom = new Point3d(x, y, this.zMin); +/***/ }, +/* 12 */ +/***/ function(module, exports, __webpack_require__) { - dataMatrix[xIndex][yIndex] = obj; + /** + * @prototype StepNumber + * The class StepNumber is an iterator for Numbers. You provide a start and end + * value, and a best step size. StepNumber itself rounds to fixed values and + * a finds the step that best fits the provided step. + * + * If prettyStep is true, the step size is chosen as close as possible to the + * provided step, but being a round value like 1, 2, 5, 10, 20, 50, .... + * + * Example usage: + * var step = new StepNumber(0, 10, 2.5, true); + * step.start(); + * while (!step.end()) { + * alert(step.getCurrent()); + * step.next(); + * } + * + * Version: 1.0 + * + * @param {Number} start The start value + * @param {Number} end The end value + * @param {Number} step Optional. Step size. Must be a positive value. + * @param {boolean} prettyStep Optional. If true, the step size is rounded + * To a pretty step size (like 1, 2, 5, 10, 20, 50, ...) + */ + function StepNumber(start, end, step, prettyStep) { + // set default values + this._start = 0; + this._end = 0; + this._step = 1; + this.prettyStep = true; + this.precision = 5; - dataPoints.push(obj); - } + this._current = 0; + this.setRange(start, end, step, prettyStep); + }; - // fill in the pointers to the neighbors. - for (x = 0; x < dataMatrix.length; x++) { - for (y = 0; y < dataMatrix[x].length; y++) { - if (dataMatrix[x][y]) { - dataMatrix[x][y].pointRight = (x < dataMatrix.length-1) ? dataMatrix[x+1][y] : undefined; - dataMatrix[x][y].pointTop = (y < dataMatrix[x].length-1) ? dataMatrix[x][y+1] : undefined; - dataMatrix[x][y].pointCross = - (x < dataMatrix.length-1 && y < dataMatrix[x].length-1) ? - dataMatrix[x+1][y+1] : - undefined; - } - } - } - } - else { // 'dot', 'dot-line', etc. - // copy all values from the google data table to a list with Point3d objects - for (i = 0; i < data.length; i++) { - point = new Point3d(); - point.x = data[i][this.colX] || 0; - point.y = data[i][this.colY] || 0; - point.z = data[i][this.colZ] || 0; + /** + * Set a new range: start, end and step. + * + * @param {Number} start The start value + * @param {Number} end The end value + * @param {Number} step Optional. Step size. Must be a positive value. + * @param {boolean} prettyStep Optional. If true, the step size is rounded + * To a pretty step size (like 1, 2, 5, 10, 20, 50, ...) + */ + StepNumber.prototype.setRange = function(start, end, step, prettyStep) { + this._start = start ? start : 0; + this._end = end ? end : 0; - if (this.colValue !== undefined) { - point.value = data[i][this.colValue] || 0; - } + this.setStep(step, prettyStep); + }; - obj = {}; - obj.point = point; - obj.bottom = new Point3d(point.x, point.y, this.zMin); - obj.trans = undefined; - obj.screen = undefined; + /** + * Set a new step size + * @param {Number} step New step size. Must be a positive value + * @param {boolean} prettyStep Optional. If true, the provided step is rounded + * to a pretty step size (like 1, 2, 5, 10, 20, 50, ...) + */ + StepNumber.prototype.setStep = function(step, prettyStep) { + if (step === undefined || step <= 0) + return; - dataPoints.push(obj); - } - } + if (prettyStep !== undefined) + this.prettyStep = prettyStep; - return dataPoints; + if (this.prettyStep === true) + this._step = StepNumber.calculatePrettyStep(step); + else + this._step = step; }; /** - * Create the main frame for the Graph3d. - * This function is executed once when a Graph3d object is created. The frame - * contains a canvas, and this canvas contains all objects like the axis and - * nodes. + * Calculate a nice step size, closest to the desired step size. + * Returns a value in one of the ranges 1*10^n, 2*10^n, or 5*10^n, where n is an + * integer Number. For example 1, 2, 5, 10, 20, 50, etc... + * @param {Number} step Desired step size + * @return {Number} Nice step size */ - Graph3d.prototype.create = function () { - // remove all elements from the container element. - while (this.containerElement.hasChildNodes()) { - this.containerElement.removeChild(this.containerElement.firstChild); - } - - this.frame = document.createElement('div'); - this.frame.style.position = 'relative'; - this.frame.style.overflow = 'hidden'; - - // create the graph canvas (HTML canvas element) - this.frame.canvas = document.createElement( 'canvas' ); - this.frame.canvas.style.position = 'relative'; - this.frame.appendChild(this.frame.canvas); - //if (!this.frame.canvas.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.frame.canvas.appendChild(noCanvas); - } + StepNumber.calculatePrettyStep = function (step) { + var log10 = function (x) {return Math.log(x) / Math.LN10;}; - this.frame.filter = document.createElement( 'div' ); - this.frame.filter.style.position = 'absolute'; - this.frame.filter.style.bottom = '0px'; - this.frame.filter.style.left = '0px'; - this.frame.filter.style.width = '100%'; - this.frame.appendChild(this.frame.filter); + // try three steps (multiple of 1, 2, or 5 + var step1 = Math.pow(10, Math.round(log10(step))), + step2 = 2 * Math.pow(10, Math.round(log10(step / 2))), + step5 = 5 * Math.pow(10, Math.round(log10(step / 5))); - // add event listeners to handle moving and zooming the contents - var me = this; - var onmousedown = function (event) {me._onMouseDown(event);}; - var ontouchstart = function (event) {me._onTouchStart(event);}; - var onmousewheel = function (event) {me._onWheel(event);}; - var ontooltip = function (event) {me._onTooltip(event);}; - // TODO: these events are never cleaned up... can give a 'memory leakage' + // choose the best step (closest to minimum step) + var prettyStep = step1; + if (Math.abs(step2 - step) <= Math.abs(prettyStep - step)) prettyStep = step2; + if (Math.abs(step5 - step) <= Math.abs(prettyStep - step)) prettyStep = step5; - util.addEventListener(this.frame.canvas, 'keydown', onkeydown); - util.addEventListener(this.frame.canvas, 'mousedown', onmousedown); - util.addEventListener(this.frame.canvas, 'touchstart', ontouchstart); - util.addEventListener(this.frame.canvas, 'mousewheel', onmousewheel); - util.addEventListener(this.frame.canvas, 'mousemove', ontooltip); + // for safety + if (prettyStep <= 0) { + prettyStep = 1; + } - // add the new graph to the container element - this.containerElement.appendChild(this.frame); + return prettyStep; }; - /** - * Set a new size for the graph - * @param {string} width Width in pixels or percentage (for example '800px' - * or '50%') - * @param {string} height Height in pixels or percentage (for example '400px' - * or '30%') + * returns the current value of the step + * @return {Number} current value */ - Graph3d.prototype.setSize = function(width, height) { - this.frame.style.width = width; - this.frame.style.height = height; - - this._resizeCanvas(); + StepNumber.prototype.getCurrent = function () { + return parseFloat(this._current.toPrecision(this.precision)); }; /** - * Resize the canvas to the current size of the frame + * returns the current step size + * @return {Number} current step size */ - Graph3d.prototype._resizeCanvas = function() { - this.frame.canvas.style.width = '100%'; - this.frame.canvas.style.height = '100%'; - - this.frame.canvas.width = this.frame.canvas.clientWidth; - this.frame.canvas.height = this.frame.canvas.clientHeight; - - // adjust with for margin - this.frame.filter.style.width = (this.frame.canvas.clientWidth - 2 * 10) + 'px'; + StepNumber.prototype.getStep = function () { + return this._step; }; /** - * Start animation + * Set the current value to the largest value smaller than start, which + * is a multiple of the step size */ - Graph3d.prototype.animationStart = function() { - if (!this.frame.filter || !this.frame.filter.slider) - throw 'No animation available'; - - this.frame.filter.slider.play(); + StepNumber.prototype.start = function() { + this._current = this._start - this._start % this._step; }; - /** - * Stop animation + * Do a step, add the step size to the current value */ - Graph3d.prototype.animationStop = function() { - if (!this.frame.filter || !this.frame.filter.slider) return; + StepNumber.prototype.next = function () { + this._current += this._step; + }; - this.frame.filter.slider.stop(); + /** + * Returns true whether the end is reached + * @return {boolean} True if the current value has passed the end value. + */ + StepNumber.prototype.end = function () { + return (this._current > this._end); }; + module.exports = StepNumber; + + +/***/ }, +/* 13 */ +/***/ function(module, exports, __webpack_require__) { + + var Emitter = __webpack_require__(56); + var Hammer = __webpack_require__(45); + var util = __webpack_require__(1); + var DataSet = __webpack_require__(3); + var DataView = __webpack_require__(4); + var Range = __webpack_require__(17); + var Core = __webpack_require__(46); + var TimeAxis = __webpack_require__(30); + var CurrentTime = __webpack_require__(21); + var CustomTime = __webpack_require__(22); + var ItemSet = __webpack_require__(27); /** - * Resize the center position based on the current values in this.defaultXCenter - * and this.defaultYCenter (which are strings with a percentage or a value - * in pixels). The center positions are the variables this.xCenter - * and this.yCenter - */ - Graph3d.prototype._resizeCenter = function() { - // calculate the horizontal center position - if (this.defaultXCenter.charAt(this.defaultXCenter.length-1) === '%') { - this.xcenter = - parseFloat(this.defaultXCenter) / 100 * - this.frame.canvas.clientWidth; - } - else { - this.xcenter = parseFloat(this.defaultXCenter); // supposed to be in px - } - - // calculate the vertical center position - if (this.defaultYCenter.charAt(this.defaultYCenter.length-1) === '%') { - this.ycenter = - parseFloat(this.defaultYCenter) / 100 * - (this.frame.canvas.clientHeight - this.frame.filter.clientHeight); - } - else { - this.ycenter = parseFloat(this.defaultYCenter); // supposed to be in px - } - }; - - /** - * Set the rotation and distance of the camera - * @param {Object} pos An object with the camera position. The object - * contains three parameters: - * - horizontal {Number} - * The horizontal rotation, between 0 and 2*PI. - * Optional, can be left undefined. - * - vertical {Number} - * The vertical rotation, between 0 and 0.5*PI - * if vertical=0.5*PI, the graph is shown from the - * top. Optional, can be left undefined. - * - distance {Number} - * The (normalized) distance of the camera to the - * center of the graph, a value between 0.71 and 5.0. - * Optional, can be left undefined. + * Create a timeline visualization + * @param {HTMLElement} container + * @param {vis.DataSet | Array | google.visualization.DataTable} [items] + * @param {vis.DataSet | Array | google.visualization.DataTable} [groups] + * @param {Object} [options] See Timeline.setOptions for the available options. + * @constructor + * @extends Core */ - Graph3d.prototype.setCameraPosition = function(pos) { - if (pos === undefined) { - return; + function Timeline (container, items, groups, options) { + if (!(this instanceof Timeline)) { + throw new SyntaxError('Constructor must be called with the new operator'); } - if (pos.horizontal !== undefined && pos.vertical !== undefined) { - this.camera.setArmRotation(pos.horizontal, pos.vertical); + // if the third element is options, the forth is groups (optionally); + if (!(Array.isArray(groups) || groups instanceof DataSet) && groups instanceof Object) { + var forthArgument = options; + options = groups; + groups = forthArgument; } - if (pos.distance !== undefined) { - this.camera.setArmLength(pos.distance); - } + var me = this; + this.defaultOptions = { + start: null, + end: null, - this.redraw(); - }; + autoResize: true, + orientation: 'bottom', + width: null, + height: null, + maxHeight: null, + minHeight: null + }; + this.options = util.deepExtend({}, this.defaultOptions); - /** - * Retrieve the current camera rotation - * @return {object} An object with parameters horizontal, vertical, and - * distance - */ - Graph3d.prototype.getCameraPosition = function() { - var pos = this.camera.getArmRotation(); - pos.distance = this.camera.getArmLength(); - return pos; - }; + // Create the DOM, props, and emitter + this._create(container); - /** - * Load data into the 3D Graph - */ - Graph3d.prototype._readData = function(data) { - // read the data - this._dataInitialize(data, this.style); + // all components listed here will be repainted automatically + this.components = []; + this.body = { + dom: this.dom, + domProps: this.props, + emitter: { + on: this.on.bind(this), + off: this.off.bind(this), + emit: this.emit.bind(this) + }, + hiddenDates: [], + util: { + snap: null, // will be specified after TimeAxis is created + toScreen: me._toScreen.bind(me), + toGlobalScreen: me._toGlobalScreen.bind(me), // this refers to the root.width + toTime: me._toTime.bind(me), + toGlobalTime : me._toGlobalTime.bind(me) + } + }; - if (this.dataFilter) { - // apply filtering - this.dataPoints = this.dataFilter._getDataPoints(); - } - else { - // no filtering. load all data - this.dataPoints = this._getDataPoints(this.dataTable); - } + // range + this.range = new Range(this.body); + this.components.push(this.range); + this.body.range = this.range; - // draw the filter - this._redrawFilter(); - }; + // time axis + this.timeAxis = new TimeAxis(this.body); + this.components.push(this.timeAxis); + this.body.util.snap = this.timeAxis.snap.bind(this.timeAxis); - /** - * Replace the dataset of the Graph3d - * @param {Array | DataSet | DataView} data - */ - Graph3d.prototype.setData = function (data) { - this._readData(data); - this.redraw(); + // current time bar + this.currentTime = new CurrentTime(this.body); + this.components.push(this.currentTime); - // start animation when option is true - if (this.animationAutoStart && this.dataFilter) { - this.animationStart(); - } - }; + // custom time bar + // Note: time bar will be attached in this.setOptions when selected + this.customTime = new CustomTime(this.body); + this.components.push(this.customTime); - /** - * Update the options. Options will be merged with current options - * @param {Object} options - */ - Graph3d.prototype.setOptions = function (options) { - var cameraPosition = undefined; + // item set + this.itemSet = new ItemSet(this.body); + this.components.push(this.itemSet); - this.animationStop(); + this.itemsData = null; // DataSet + this.groupsData = null; // DataSet - if (options !== undefined) { - // retrieve parameter values - if (options.width !== undefined) this.width = options.width; - if (options.height !== undefined) this.height = options.height; + // apply options + if (options) { + this.setOptions(options); + } - if (options.xCenter !== undefined) this.defaultXCenter = options.xCenter; - if (options.yCenter !== undefined) this.defaultYCenter = options.yCenter; + // IMPORTANT: THIS HAPPENS BEFORE SET ITEMS! + if (groups) { + this.setGroups(groups); + } - if (options.filterLabel !== undefined) this.filterLabel = options.filterLabel; - if (options.legendLabel !== undefined) this.legendLabel = options.legendLabel; - if (options.xLabel !== undefined) this.xLabel = options.xLabel; - if (options.yLabel !== undefined) this.yLabel = options.yLabel; - if (options.zLabel !== undefined) this.zLabel = options.zLabel; + // create itemset + if (items) { + this.setItems(items); + } + else { + this.redraw(); + } + } - if (options.xValueLabel !== undefined) this.xValueLabel = options.xValueLabel; - if (options.yValueLabel !== undefined) this.yValueLabel = options.yValueLabel; - if (options.zValueLabel !== undefined) this.zValueLabel = options.zValueLabel; + // Extend the functionality from Core + Timeline.prototype = new Core(); - if (options.style !== undefined) { - var styleNumber = this._getStyleNumber(options.style); - if (styleNumber !== -1) { - this.style = styleNumber; - } - } - if (options.showGrid !== undefined) this.showGrid = options.showGrid; - if (options.showPerspective !== undefined) this.showPerspective = options.showPerspective; - if (options.showShadow !== undefined) this.showShadow = options.showShadow; - if (options.tooltip !== undefined) this.showTooltip = options.tooltip; - if (options.showAnimationControls !== undefined) this.showAnimationControls = options.showAnimationControls; - if (options.keepAspectRatio !== undefined) this.keepAspectRatio = options.keepAspectRatio; - if (options.verticalRatio !== undefined) this.verticalRatio = options.verticalRatio; + /** + * Set items + * @param {vis.DataSet | Array | google.visualization.DataTable | null} items + */ + Timeline.prototype.setItems = function(items) { + var initialLoad = (this.itemsData == null); - if (options.animationInterval !== undefined) this.animationInterval = options.animationInterval; - if (options.animationPreload !== undefined) this.animationPreload = options.animationPreload; - if (options.animationAutoStart !== undefined)this.animationAutoStart = options.animationAutoStart; + // convert to type DataSet when needed + var newDataSet; + if (!items) { + newDataSet = null; + } + else if (items instanceof DataSet || items instanceof DataView) { + newDataSet = items; + } + else { + // turn an array into a dataset + newDataSet = new DataSet(items, { + type: { + start: 'Date', + end: 'Date' + } + }); + } - if (options.xBarWidth !== undefined) this.defaultXBarWidth = options.xBarWidth; - if (options.yBarWidth !== undefined) this.defaultYBarWidth = options.yBarWidth; + // set items + this.itemsData = newDataSet; + this.itemSet && this.itemSet.setItems(newDataSet); - if (options.xMin !== undefined) this.defaultXMin = options.xMin; - if (options.xStep !== undefined) this.defaultXStep = options.xStep; - if (options.xMax !== undefined) this.defaultXMax = options.xMax; - if (options.yMin !== undefined) this.defaultYMin = options.yMin; - if (options.yStep !== undefined) this.defaultYStep = options.yStep; - if (options.yMax !== undefined) this.defaultYMax = options.yMax; - if (options.zMin !== undefined) this.defaultZMin = options.zMin; - if (options.zStep !== undefined) this.defaultZStep = options.zStep; - if (options.zMax !== undefined) this.defaultZMax = options.zMax; - if (options.valueMin !== undefined) this.defaultValueMin = options.valueMin; - if (options.valueMax !== undefined) this.defaultValueMax = options.valueMax; + if (initialLoad) { + if (this.options.start != undefined || this.options.end != undefined) { + if (this.options.start == undefined || this.options.end == undefined) { + var dataRange = this._getDataRange(); + } - if (options.cameraPosition !== undefined) cameraPosition = options.cameraPosition; + var start = this.options.start != undefined ? this.options.start : dataRange.start; + var end = this.options.end != undefined ? this.options.end : dataRange.end; - if (cameraPosition !== undefined) { - this.camera.setArmRotation(cameraPosition.horizontal, cameraPosition.vertical); - this.camera.setArmLength(cameraPosition.distance); + this.setWindow(start, end, {animate: false}); } else { - this.camera.setArmRotation(1.0, 0.5); - this.camera.setArmLength(1.7); + this.fit({animate: false}); } } - - this._setBackgroundColor(options && options.backgroundColor); - - this.setSize(this.width, this.height); - - // re-load the data - if (this.dataTable) { - this.setData(this.dataTable); - } - - // start animation when option is true - if (this.animationAutoStart && this.dataFilter) { - this.animationStart(); - } }; /** - * Redraw the Graph. + * Set groups + * @param {vis.DataSet | Array | google.visualization.DataTable} groups */ - Graph3d.prototype.redraw = function() { - if (this.dataPoints === undefined) { - throw 'Error: graph data not initialized'; - } - - this._resizeCanvas(); - this._resizeCenter(); - this._redrawSlider(); - this._redrawClear(); - this._redrawAxis(); - - if (this.style === Graph3d.STYLE.GRID || - this.style === Graph3d.STYLE.SURFACE) { - this._redrawDataGrid(); - } - else if (this.style === Graph3d.STYLE.LINE) { - this._redrawDataLine(); + Timeline.prototype.setGroups = function(groups) { + // convert to type DataSet when needed + var newDataSet; + if (!groups) { + newDataSet = null; } - else if (this.style === Graph3d.STYLE.BAR || - this.style === Graph3d.STYLE.BARCOLOR || - this.style === Graph3d.STYLE.BARSIZE) { - this._redrawDataBar(); + else if (groups instanceof DataSet || groups instanceof DataView) { + newDataSet = groups; } else { - // style is DOT, DOTLINE, DOTCOLOR, DOTSIZE - this._redrawDataDot(); + // turn an array into a dataset + newDataSet = new DataSet(groups); } - this._redrawInfo(); - this._redrawLegend(); + this.groupsData = newDataSet; + this.itemSet.setGroups(newDataSet); }; /** - * Clear the canvas before redrawing + * Set selected items by their id. Replaces the current selection + * Unknown id's are silently ignored. + * @param {string[] | string} [ids] An array with zero or more id's of the items to be + * selected. If ids is an empty array, all items will be + * unselected. + * @param {Object} [options] Available options: + * `focus: boolean` + * If true, focus will be set to the selected item(s) + * `animate: boolean | number` + * If true (default), the range is animated + * smoothly to the new window. + * If a number, the number is taken as duration + * for the animation. Default duration is 500 ms. + * Only applicable when option focus is true. */ - Graph3d.prototype._redrawClear = function() { - var canvas = this.frame.canvas; - var ctx = canvas.getContext('2d'); + Timeline.prototype.setSelection = function(ids, options) { + this.itemSet && this.itemSet.setSelection(ids); - ctx.clearRect(0, 0, canvas.width, canvas.height); + if (options && options.focus) { + this.focus(ids, options); + } }; + /** + * Get the selected items by their id + * @return {Array} ids The ids of the selected items + */ + Timeline.prototype.getSelection = function() { + return this.itemSet && this.itemSet.getSelection() || []; + }; /** - * Redraw the legend showing the colors + * Adjust the visible window such that the selected item (or multiple items) + * are centered on screen. + * @param {String | String[]} id An item id or array with item ids + * @param {Object} [options] Available options: + * `animate: boolean | number` + * If true (default), the range is animated + * smoothly to the new window. + * If a number, the number is taken as duration + * for the animation. Default duration is 500 ms. + * Only applicable when option focus is true */ - Graph3d.prototype._redrawLegend = function() { - var y; + Timeline.prototype.focus = function(id, options) { + if (!this.itemsData || id == undefined) return; - if (this.style === Graph3d.STYLE.DOTCOLOR || - this.style === Graph3d.STYLE.DOTSIZE) { + var ids = Array.isArray(id) ? id : [id]; - var dotSize = this.frame.clientWidth * 0.02; + // get the specified item(s) + var itemsData = this.itemsData.getDataSet().get(ids, { + type: { + start: 'Date', + end: 'Date' + } + }); - var widthMin, widthMax; - if (this.style === Graph3d.STYLE.DOTSIZE) { - widthMin = dotSize / 2; // px - widthMax = dotSize / 2 + dotSize * 2; // Todo: put this in one function + // calculate minimum start and maximum end of specified items + var start = null; + var end = null; + itemsData.forEach(function (itemData) { + var s = itemData.start.valueOf(); + var e = 'end' in itemData ? itemData.end.valueOf() : itemData.start.valueOf(); + + if (start === null || s < start) { + start = s; } - else { - widthMin = 20; // px - widthMax = 20; // px + + if (end === null || e > end) { + end = e; } + }); - var height = Math.max(this.frame.clientHeight * 0.25, 100); - var top = this.margin; - var right = this.frame.clientWidth - this.margin; - var left = right - widthMax; - var bottom = top + height; - } + if (start !== null && end !== null) { + // calculate the new middle and interval for the window + var middle = (start + end) / 2; + var interval = Math.max((this.range.end - this.range.start), (end - start) * 1.1); - var canvas = this.frame.canvas; - var ctx = canvas.getContext('2d'); - ctx.lineWidth = 1; - ctx.font = '14px arial'; // TODO: put in options + var animate = (options && options.animate !== undefined) ? options.animate : true; + this.range.setRange(middle - interval / 2, middle + interval / 2, animate); + } + }; - if (this.style === Graph3d.STYLE.DOTCOLOR) { - // draw the color bar - var ymin = 0; - var ymax = height; // Todo: make height customizable - for (y = ymin; y < ymax; y++) { - var f = (y - ymin) / (ymax - ymin); + /** + * Get the data range of the item set. + * @returns {{min: Date, max: Date}} range A range with a start and end Date. + * When no minimum is found, min==null + * When no maximum is found, max==null + */ + Timeline.prototype.getItemRange = function() { + // calculate min from start filed + var dataset = this.itemsData.getDataSet(), + min = null, + max = null; - //var width = (dotSize / 2 + (1-f) * dotSize * 2); // Todo: put this in one function - var hue = f * 240; - var color = this._hsv2rgb(hue, 1, 1); + if (dataset) { + // calculate the minimum value of the field 'start' + var minItem = dataset.min('start'); + min = minItem ? util.convert(minItem.start, 'Date').valueOf() : null; + // Note: we convert first to Date and then to number because else + // a conversion from ISODate to Number will fail - ctx.strokeStyle = color; - ctx.beginPath(); - ctx.moveTo(left, top + y); - ctx.lineTo(right, top + y); - ctx.stroke(); + // calculate maximum value of fields 'start' and 'end' + var maxStartItem = dataset.max('start'); + if (maxStartItem) { + max = util.convert(maxStartItem.start, 'Date').valueOf(); + } + var maxEndItem = dataset.max('end'); + if (maxEndItem) { + if (max == null) { + max = util.convert(maxEndItem.end, 'Date').valueOf(); + } + else { + max = Math.max(max, util.convert(maxEndItem.end, 'Date').valueOf()); + } } - - ctx.strokeStyle = this.colorAxis; - ctx.strokeRect(left, top, widthMax, height); } - if (this.style === Graph3d.STYLE.DOTSIZE) { - // draw border around color bar - ctx.strokeStyle = this.colorAxis; - ctx.fillStyle = this.colorDot; - ctx.beginPath(); - ctx.moveTo(left, top); - ctx.lineTo(right, top); - ctx.lineTo(right - widthMax + widthMin, bottom); - ctx.lineTo(left, bottom); - ctx.closePath(); - ctx.fill(); - ctx.stroke(); - } + return { + min: (min != null) ? new Date(min) : null, + max: (max != null) ? new Date(max) : null + }; + }; - if (this.style === Graph3d.STYLE.DOTCOLOR || - this.style === Graph3d.STYLE.DOTSIZE) { - // print values along the color bar - var gridLineLen = 5; // px - var step = new StepNumber(this.valueMin, this.valueMax, (this.valueMax-this.valueMin)/5, true); - step.start(); - if (step.getCurrent() < this.valueMin) { - step.next(); - } - while (!step.end()) { - y = bottom - (step.getCurrent() - this.valueMin) / (this.valueMax - this.valueMin) * height; - ctx.beginPath(); - ctx.moveTo(left - gridLineLen, y); - ctx.lineTo(left, y); - ctx.stroke(); + module.exports = Timeline; - ctx.textAlign = 'right'; - ctx.textBaseline = 'middle'; - ctx.fillStyle = this.colorAxis; - ctx.fillText(step.getCurrent(), left - 2 * gridLineLen, y); - step.next(); - } +/***/ }, +/* 14 */ +/***/ function(module, exports, __webpack_require__) { - ctx.textAlign = 'right'; - ctx.textBaseline = 'top'; - var label = this.legendLabel; - ctx.fillText(label, right, bottom + this.margin); - } - }; + var Emitter = __webpack_require__(56); + var Hammer = __webpack_require__(45); + var util = __webpack_require__(1); + var DataSet = __webpack_require__(3); + var DataView = __webpack_require__(4); + var Range = __webpack_require__(17); + var Core = __webpack_require__(46); + var TimeAxis = __webpack_require__(30); + var CurrentTime = __webpack_require__(21); + var CustomTime = __webpack_require__(22); + var LineGraph = __webpack_require__(29); /** - * Redraw the filter - */ - Graph3d.prototype._redrawFilter = function() { - this.frame.filter.innerHTML = ''; - - if (this.dataFilter) { - var options = { - 'visible': this.showAnimationControls - }; - var slider = new Slider(this.frame.filter, options); - this.frame.filter.slider = slider; - - // TODO: css here is not nice here... - this.frame.filter.style.padding = '10px'; - //this.frame.filter.style.backgroundColor = '#EFEFEF'; - - slider.setValues(this.dataFilter.values); - slider.setPlayInterval(this.animationInterval); - - // create an event handler - var me = this; - var onchange = function () { - var index = slider.getIndex(); - - me.dataFilter.selectValue(index); - me.dataPoints = me.dataFilter._getDataPoints(); - - me.redraw(); - }; - slider.setOnChangeCallback(onchange); - } - else { - this.frame.filter.slider = undefined; - } - }; - - /** - * Redraw the slider + * Create a timeline visualization + * @param {HTMLElement} container + * @param {vis.DataSet | Array | google.visualization.DataTable} [items] + * @param {Object} [options] See Graph2d.setOptions for the available options. + * @constructor + * @extends Core */ - Graph3d.prototype._redrawSlider = function() { - if ( this.frame.filter.slider !== undefined) { - this.frame.filter.slider.redraw(); + function Graph2d (container, items, groups, options) { + // if the third element is options, the forth is groups (optionally); + if (!(Array.isArray(groups) || groups instanceof DataSet) && groups instanceof Object) { + var forthArgument = options; + options = groups; + groups = forthArgument; } - }; + var me = this; + this.defaultOptions = { + start: null, + end: null, - /** - * Redraw common information - */ - Graph3d.prototype._redrawInfo = function() { - if (this.dataFilter) { - var canvas = this.frame.canvas; - var ctx = canvas.getContext('2d'); + autoResize: true, - ctx.font = '14px arial'; // TODO: put in options - ctx.lineStyle = 'gray'; - ctx.fillStyle = 'gray'; - ctx.textAlign = 'left'; - ctx.textBaseline = 'top'; + orientation: 'bottom', + width: null, + height: null, + maxHeight: null, + minHeight: null + }; + this.options = util.deepExtend({}, this.defaultOptions); - var x = this.margin; - var y = this.margin; - ctx.fillText(this.dataFilter.getLabel() + ': ' + this.dataFilter.getSelectedValue(), x, y); - } - }; + // Create the DOM, props, and emitter + this._create(container); + // all components listed here will be repainted automatically + this.components = []; - /** - * Redraw the axis - */ - Graph3d.prototype._redrawAxis = function() { - var canvas = this.frame.canvas, - ctx = canvas.getContext('2d'), - from, to, step, prettyStep, - text, xText, yText, zText, - offset, xOffset, yOffset, - xMin2d, xMax2d; + this.body = { + dom: this.dom, + domProps: this.props, + emitter: { + on: this.on.bind(this), + off: this.off.bind(this), + emit: this.emit.bind(this) + }, + hiddenDates: [], + util: { + snap: null, // will be specified after TimeAxis is created + toScreen: me._toScreen.bind(me), + toGlobalScreen: me._toGlobalScreen.bind(me), // this refers to the root.width + toTime: me._toTime.bind(me), + toGlobalTime : me._toGlobalTime.bind(me) + } + }; - // TODO: get the actual rendered style of the containerElement - //ctx.font = this.containerElement.style.font; - ctx.font = 24 / this.camera.getArmLength() + 'px arial'; + // range + this.range = new Range(this.body); + this.components.push(this.range); + this.body.range = this.range; - // calculate the length for the short grid lines - var gridLenX = 0.025 / this.scale.x; - var gridLenY = 0.025 / this.scale.y; - var textMargin = 5 / this.camera.getArmLength(); // px - var armAngle = this.camera.getArmRotation().horizontal; + // time axis + this.timeAxis = new TimeAxis(this.body); + this.components.push(this.timeAxis); + this.body.util.snap = this.timeAxis.snap.bind(this.timeAxis); - // draw x-grid lines - ctx.lineWidth = 1; - prettyStep = (this.defaultXStep === undefined); - step = new StepNumber(this.xMin, this.xMax, this.xStep, prettyStep); - step.start(); - if (step.getCurrent() < this.xMin) { - step.next(); - } - while (!step.end()) { - var x = step.getCurrent(); + // current time bar + this.currentTime = new CurrentTime(this.body); + this.components.push(this.currentTime); - if (this.showGrid) { - from = this._convert3Dto2D(new Point3d(x, this.yMin, this.zMin)); - to = this._convert3Dto2D(new Point3d(x, this.yMax, this.zMin)); - ctx.strokeStyle = this.colorGrid; - ctx.beginPath(); - ctx.moveTo(from.x, from.y); - ctx.lineTo(to.x, to.y); - ctx.stroke(); - } - else { - from = this._convert3Dto2D(new Point3d(x, this.yMin, this.zMin)); - to = this._convert3Dto2D(new Point3d(x, this.yMin+gridLenX, this.zMin)); - ctx.strokeStyle = this.colorAxis; - ctx.beginPath(); - ctx.moveTo(from.x, from.y); - ctx.lineTo(to.x, to.y); - ctx.stroke(); + // custom time bar + // Note: time bar will be attached in this.setOptions when selected + this.customTime = new CustomTime(this.body); + this.components.push(this.customTime); - from = this._convert3Dto2D(new Point3d(x, this.yMax, this.zMin)); - to = this._convert3Dto2D(new Point3d(x, this.yMax-gridLenX, this.zMin)); - ctx.strokeStyle = this.colorAxis; - ctx.beginPath(); - ctx.moveTo(from.x, from.y); - ctx.lineTo(to.x, to.y); - ctx.stroke(); - } + // item set + this.linegraph = new LineGraph(this.body); + this.components.push(this.linegraph); - yText = (Math.cos(armAngle) > 0) ? this.yMin : this.yMax; - text = this._convert3Dto2D(new Point3d(x, yText, this.zMin)); - if (Math.cos(armAngle * 2) > 0) { - ctx.textAlign = 'center'; - ctx.textBaseline = 'top'; - text.y += textMargin; - } - else if (Math.sin(armAngle * 2) < 0){ - ctx.textAlign = 'right'; - ctx.textBaseline = 'middle'; - } - else { - ctx.textAlign = 'left'; - ctx.textBaseline = 'middle'; - } - ctx.fillStyle = this.colorAxis; - ctx.fillText(' ' + this.xValueLabel(step.getCurrent()) + ' ', text.x, text.y); + this.itemsData = null; // DataSet + this.groupsData = null; // DataSet - step.next(); + // apply options + if (options) { + this.setOptions(options); } - // draw y-grid lines - ctx.lineWidth = 1; - prettyStep = (this.defaultYStep === undefined); - step = new StepNumber(this.yMin, this.yMax, this.yStep, prettyStep); - step.start(); - if (step.getCurrent() < this.yMin) { - step.next(); + // IMPORTANT: THIS HAPPENS BEFORE SET ITEMS! + if (groups) { + this.setGroups(groups); } - while (!step.end()) { - if (this.showGrid) { - from = this._convert3Dto2D(new Point3d(this.xMin, step.getCurrent(), this.zMin)); - to = this._convert3Dto2D(new Point3d(this.xMax, step.getCurrent(), this.zMin)); - ctx.strokeStyle = this.colorGrid; - ctx.beginPath(); - ctx.moveTo(from.x, from.y); - ctx.lineTo(to.x, to.y); - ctx.stroke(); - } - else { - from = this._convert3Dto2D(new Point3d(this.xMin, step.getCurrent(), this.zMin)); - to = this._convert3Dto2D(new Point3d(this.xMin+gridLenY, step.getCurrent(), this.zMin)); - ctx.strokeStyle = this.colorAxis; - ctx.beginPath(); - ctx.moveTo(from.x, from.y); - ctx.lineTo(to.x, to.y); - ctx.stroke(); - - from = this._convert3Dto2D(new Point3d(this.xMax, step.getCurrent(), this.zMin)); - to = this._convert3Dto2D(new Point3d(this.xMax-gridLenY, step.getCurrent(), this.zMin)); - ctx.strokeStyle = this.colorAxis; - ctx.beginPath(); - ctx.moveTo(from.x, from.y); - ctx.lineTo(to.x, to.y); - ctx.stroke(); - } - - xText = (Math.sin(armAngle ) > 0) ? this.xMin : this.xMax; - text = this._convert3Dto2D(new Point3d(xText, step.getCurrent(), this.zMin)); - if (Math.cos(armAngle * 2) < 0) { - ctx.textAlign = 'center'; - ctx.textBaseline = 'top'; - text.y += textMargin; - } - else if (Math.sin(armAngle * 2) > 0){ - ctx.textAlign = 'right'; - ctx.textBaseline = 'middle'; - } - else { - ctx.textAlign = 'left'; - ctx.textBaseline = 'middle'; - } - ctx.fillStyle = this.colorAxis; - ctx.fillText(' ' + this.yValueLabel(step.getCurrent()) + ' ', text.x, text.y); - step.next(); + // create itemset + if (items) { + this.setItems(items); } - - // draw z-grid lines and axis - ctx.lineWidth = 1; - prettyStep = (this.defaultZStep === undefined); - step = new StepNumber(this.zMin, this.zMax, this.zStep, prettyStep); - step.start(); - if (step.getCurrent() < this.zMin) { - step.next(); + else { + this.redraw(); } - xText = (Math.cos(armAngle ) > 0) ? this.xMin : this.xMax; - yText = (Math.sin(armAngle ) < 0) ? this.yMin : this.yMax; - while (!step.end()) { - // TODO: make z-grid lines really 3d? - from = this._convert3Dto2D(new Point3d(xText, yText, step.getCurrent())); - ctx.strokeStyle = this.colorAxis; - ctx.beginPath(); - ctx.moveTo(from.x, from.y); - ctx.lineTo(from.x - textMargin, from.y); - ctx.stroke(); + } - ctx.textAlign = 'right'; - ctx.textBaseline = 'middle'; - ctx.fillStyle = this.colorAxis; - ctx.fillText(this.zValueLabel(step.getCurrent()) + ' ', from.x - 5, from.y); + // Extend the functionality from Core + Graph2d.prototype = new Core(); - step.next(); + /** + * Set items + * @param {vis.DataSet | Array | google.visualization.DataTable | null} items + */ + Graph2d.prototype.setItems = function(items) { + var initialLoad = (this.itemsData == null); + + // convert to type DataSet when needed + var newDataSet; + if (!items) { + newDataSet = null; + } + else if (items instanceof DataSet || items instanceof DataView) { + newDataSet = items; + } + else { + // turn an array into a dataset + newDataSet = new DataSet(items, { + type: { + start: 'Date', + end: 'Date' + } + }); } - ctx.lineWidth = 1; - from = this._convert3Dto2D(new Point3d(xText, yText, this.zMin)); - to = this._convert3Dto2D(new Point3d(xText, yText, this.zMax)); - ctx.strokeStyle = this.colorAxis; - ctx.beginPath(); - ctx.moveTo(from.x, from.y); - ctx.lineTo(to.x, to.y); - ctx.stroke(); - // draw x-axis - ctx.lineWidth = 1; - // line at yMin - xMin2d = this._convert3Dto2D(new Point3d(this.xMin, this.yMin, this.zMin)); - xMax2d = this._convert3Dto2D(new Point3d(this.xMax, this.yMin, this.zMin)); - ctx.strokeStyle = this.colorAxis; - ctx.beginPath(); - ctx.moveTo(xMin2d.x, xMin2d.y); - ctx.lineTo(xMax2d.x, xMax2d.y); - ctx.stroke(); - // line at ymax - xMin2d = this._convert3Dto2D(new Point3d(this.xMin, this.yMax, this.zMin)); - xMax2d = this._convert3Dto2D(new Point3d(this.xMax, this.yMax, this.zMin)); - ctx.strokeStyle = this.colorAxis; - ctx.beginPath(); - ctx.moveTo(xMin2d.x, xMin2d.y); - ctx.lineTo(xMax2d.x, xMax2d.y); - ctx.stroke(); + // set items + this.itemsData = newDataSet; + this.linegraph && this.linegraph.setItems(newDataSet); - // draw y-axis - ctx.lineWidth = 1; - // line at xMin - from = this._convert3Dto2D(new Point3d(this.xMin, this.yMin, this.zMin)); - to = this._convert3Dto2D(new Point3d(this.xMin, this.yMax, this.zMin)); - ctx.strokeStyle = this.colorAxis; - ctx.beginPath(); - ctx.moveTo(from.x, from.y); - ctx.lineTo(to.x, to.y); - ctx.stroke(); - // line at xMax - from = this._convert3Dto2D(new Point3d(this.xMax, this.yMin, this.zMin)); - to = this._convert3Dto2D(new Point3d(this.xMax, this.yMax, this.zMin)); - ctx.strokeStyle = this.colorAxis; - ctx.beginPath(); - ctx.moveTo(from.x, from.y); - ctx.lineTo(to.x, to.y); - ctx.stroke(); + if (initialLoad) { + if (this.options.start != undefined || this.options.end != undefined) { + var start = this.options.start != undefined ? this.options.start : null; + var end = this.options.end != undefined ? this.options.end : null; - // draw x-label - var xLabel = this.xLabel; - if (xLabel.length > 0) { - yOffset = 0.1 / this.scale.y; - xText = (this.xMin + this.xMax) / 2; - yText = (Math.cos(armAngle) > 0) ? this.yMin - yOffset: this.yMax + yOffset; - text = this._convert3Dto2D(new Point3d(xText, yText, this.zMin)); - if (Math.cos(armAngle * 2) > 0) { - ctx.textAlign = 'center'; - ctx.textBaseline = 'top'; - } - else if (Math.sin(armAngle * 2) < 0){ - ctx.textAlign = 'right'; - ctx.textBaseline = 'middle'; + this.setWindow(start, end, {animate: false}); } else { - ctx.textAlign = 'left'; - ctx.textBaseline = 'middle'; + this.fit({animate: false}); } - ctx.fillStyle = this.colorAxis; - ctx.fillText(xLabel, text.x, text.y); } + }; - // draw y-label - var yLabel = this.yLabel; - if (yLabel.length > 0) { - xOffset = 0.1 / this.scale.x; - xText = (Math.sin(armAngle ) > 0) ? this.xMin - xOffset : this.xMax + xOffset; - yText = (this.yMin + this.yMax) / 2; - text = this._convert3Dto2D(new Point3d(xText, yText, this.zMin)); - if (Math.cos(armAngle * 2) < 0) { - ctx.textAlign = 'center'; - ctx.textBaseline = 'top'; - } - else if (Math.sin(armAngle * 2) > 0){ - ctx.textAlign = 'right'; - ctx.textBaseline = 'middle'; - } - else { - ctx.textAlign = 'left'; - ctx.textBaseline = 'middle'; - } - ctx.fillStyle = this.colorAxis; - ctx.fillText(yLabel, text.x, text.y); + /** + * Set groups + * @param {vis.DataSet | Array | google.visualization.DataTable} groups + */ + Graph2d.prototype.setGroups = function(groups) { + // convert to type DataSet when needed + var newDataSet; + if (!groups) { + newDataSet = null; } - - // draw z-label - var zLabel = this.zLabel; - if (zLabel.length > 0) { - offset = 30; // pixels. // TODO: relate to the max width of the values on the z axis? - xText = (Math.cos(armAngle ) > 0) ? this.xMin : this.xMax; - yText = (Math.sin(armAngle ) < 0) ? this.yMin : this.yMax; - zText = (this.zMin + this.zMax) / 2; - text = this._convert3Dto2D(new Point3d(xText, yText, zText)); - ctx.textAlign = 'right'; - ctx.textBaseline = 'middle'; - ctx.fillStyle = this.colorAxis; - ctx.fillText(zLabel, text.x - offset, text.y); + else if (groups instanceof DataSet || groups instanceof DataView) { + newDataSet = groups; + } + else { + // turn an array into a dataset + newDataSet = new DataSet(groups); } + + this.groupsData = newDataSet; + this.linegraph.setGroups(newDataSet); }; /** - * Calculate the color based on the given value. - * @param {Number} H Hue, a value be between 0 and 360 - * @param {Number} S Saturation, a value between 0 and 1 - * @param {Number} V Value, a value between 0 and 1 + * Returns an object containing an SVG element with the icon of the group (size determined by iconWidth and iconHeight), the label of the group (content) and the yAxisOrientation of the group (left or right). + * @param groupId + * @param width + * @param height */ - Graph3d.prototype._hsv2rgb = function(H, S, V) { - var R, G, B, C, Hi, X; + Graph2d.prototype.getLegend = function(groupId, width, height) { + if (width === undefined) {width = 15;} + if (height === undefined) {height = 15;} + if (this.linegraph.groups[groupId] !== undefined) { + return this.linegraph.groups[groupId].getLegend(width,height); + } + else { + return "cannot find group:" + groupId; + } + } - C = V * S; - Hi = Math.floor(H/60); // hi = 0,1,2,3,4,5 - X = C * (1 - Math.abs(((H/60) % 2) - 1)); + /** + * This checks if the visible option of the supplied group (by ID) is true or false. + * @param groupId + * @returns {*} + */ + Graph2d.prototype.isGroupVisible = function(groupId) { + if (this.linegraph.groups[groupId] !== undefined) { + return (this.linegraph.groups[groupId].visible && (this.linegraph.options.groups.visibility[groupId] === undefined || this.linegraph.options.groups.visibility[groupId] == true)); + } + else { + return false; + } + } - switch (Hi) { - case 0: R = C; G = X; B = 0; break; - case 1: R = X; G = C; B = 0; break; - case 2: R = 0; G = C; B = X; break; - case 3: R = 0; G = X; B = C; break; - case 4: R = X; G = 0; B = C; break; - case 5: R = C; G = 0; B = X; break; - default: R = 0; G = 0; B = 0; break; + /** + * Get the data range of the item set. + * @returns {{min: Date, max: Date}} range A range with a start and end Date. + * When no minimum is found, min==null + * When no maximum is found, max==null + */ + Graph2d.prototype.getItemRange = function() { + var min = null; + var max = null; + + // calculate min from start filed + for (var groupId in this.linegraph.groups) { + if (this.linegraph.groups.hasOwnProperty(groupId)) { + if (this.linegraph.groups[groupId].visible == true) { + for (var i = 0; i < this.linegraph.groups[groupId].itemsData.length; i++) { + var item = this.linegraph.groups[groupId].itemsData[i]; + var value = util.convert(item.x, 'Date').valueOf(); + min = min == null ? value : min > value ? value : min; + max = max == null ? value : max < value ? value : max; + } + } + } } - return 'RGB(' + parseInt(R*255) + ',' + parseInt(G*255) + ',' + parseInt(B*255) + ')'; + return { + min: (min != null) ? new Date(min) : null, + max: (max != null) ? new Date(max) : null + }; }; - /** - * Draw all datapoints as a grid - * This function can be used when the style is 'grid' - */ - Graph3d.prototype._redrawDataGrid = function() { - var canvas = this.frame.canvas, - ctx = canvas.getContext('2d'), - point, right, top, cross, - i, - topSideVisible, fillStyle, strokeStyle, lineWidth, - h, s, v, zAvg; + module.exports = Graph2d; - if (this.dataPoints === undefined || this.dataPoints.length <= 0) - return; // TODO: throw exception? - // calculate the translations and screen position of all points - for (i = 0; i < this.dataPoints.length; i++) { - var trans = this._convertPointToTranslation(this.dataPoints[i].point); - var screen = this._convertTranslationToScreen(trans); +/***/ }, +/* 15 */ +/***/ function(module, exports, __webpack_require__) { - this.dataPoints[i].trans = trans; - this.dataPoints[i].screen = screen; + /** + * Created by Alex on 10/3/2014. + */ + var moment = __webpack_require__(44); - // calculate the translation of the point at the bottom (needed for sorting) - var transBottom = this._convertPointToTranslation(this.dataPoints[i].bottom); - this.dataPoints[i].dist = this.showPerspective ? transBottom.length() : -transBottom.z; + + /** + * used in Core to convert the options into a volatile variable + * + * @param Core + */ + exports.convertHiddenOptions = function(body, hiddenDates) { + body.hiddenDates = []; + if (hiddenDates) { + if (Array.isArray(hiddenDates) == true) { + for (var i = 0; i < hiddenDates.length; i++) { + if (hiddenDates[i].repeat === undefined) { + var dateItem = {}; + dateItem.start = moment(hiddenDates[i].start).toDate().valueOf(); + dateItem.end = moment(hiddenDates[i].end).toDate().valueOf(); + body.hiddenDates.push(dateItem); + } + } + body.hiddenDates.sort(function (a, b) { + return a.start - b.start; + }); // sort by start time + } } + }; - // sort the points on depth of their (x,y) position (not on z) - var sortDepth = function (a, b) { - return b.dist - a.dist; - }; - this.dataPoints.sort(sortDepth); - if (this.style === Graph3d.STYLE.SURFACE) { - for (i = 0; i < this.dataPoints.length; i++) { - point = this.dataPoints[i]; - right = this.dataPoints[i].pointRight; - top = this.dataPoints[i].pointTop; - cross = this.dataPoints[i].pointCross; + /** + * create new entrees for the repeating hidden dates + * @param body + * @param hiddenDates + */ + exports.updateHiddenDates = function (body, hiddenDates) { + if (hiddenDates && body.domProps.centerContainer.width !== undefined) { + exports.convertHiddenOptions(body, hiddenDates); - if (point !== undefined && right !== undefined && top !== undefined && cross !== undefined) { + var start = moment(body.range.start); + var end = moment(body.range.end); - if (this.showGrayBottom || this.showShadow) { - // calculate the cross product of the two vectors from center - // to left and right, in order to know whether we are looking at the - // bottom or at the top side. We can also use the cross product - // for calculating light intensity - var aDiff = Point3d.subtract(cross.trans, point.trans); - var bDiff = Point3d.subtract(top.trans, right.trans); - var crossproduct = Point3d.crossProduct(aDiff, bDiff); - var len = crossproduct.length(); - // FIXME: there is a bug with determining the surface side (shadow or colored) + var totalRange = (body.range.end - body.range.start); + var pixelTime = totalRange / body.domProps.centerContainer.width; - topSideVisible = (crossproduct.z > 0); + for (var i = 0; i < hiddenDates.length; i++) { + if (hiddenDates[i].repeat !== undefined) { + var startDate = moment(hiddenDates[i].start); + var endDate = moment(hiddenDates[i].end); + + if (startDate._d == "Invalid Date") { + throw new Error("Supplied start date is not valid: " + hiddenDates[i].start); } - else { - topSideVisible = true; + if (endDate._d == "Invalid Date") { + throw new Error("Supplied end date is not valid: " + hiddenDates[i].end); } - if (topSideVisible) { - // calculate Hue from the current value. At zMin the hue is 240, at zMax the hue is 0 - zAvg = (point.point.z + right.point.z + top.point.z + cross.point.z) / 4; - h = (1 - (zAvg - this.zMin) * this.scale.z / this.verticalRatio) * 240; - s = 1; // saturation + var duration = endDate - startDate; + if (duration >= 4 * pixelTime) { - if (this.showShadow) { - v = Math.min(1 + (crossproduct.x / len) / 2, 1); // value. TODO: scale - fillStyle = this._hsv2rgb(h, s, v); - strokeStyle = fillStyle; + var offset = 0; + var runUntil = end.clone(); + switch (hiddenDates[i].repeat) { + case "daily": // case of time + if (startDate.day() != endDate.day()) { + offset = 1; + } + startDate.dayOfYear(start.dayOfYear()); + startDate.year(start.year()); + startDate.subtract(7,'days'); + + endDate.dayOfYear(start.dayOfYear()); + endDate.year(start.year()); + endDate.subtract(7 - offset,'days'); + + runUntil.add(1, 'weeks'); + break; + case "weekly": + var dayOffset = endDate.diff(startDate,'days') + var day = startDate.day(); + + // set the start date to the range.start + startDate.date(start.date()); + startDate.month(start.month()); + startDate.year(start.year()); + endDate = startDate.clone(); + + // force + startDate.day(day); + endDate.day(day); + endDate.add(dayOffset,'days'); + + startDate.subtract(1,'weeks'); + endDate.subtract(1,'weeks'); + + runUntil.add(1, 'weeks'); + break + case "monthly": + if (startDate.month() != endDate.month()) { + offset = 1; + } + startDate.month(start.month()); + startDate.year(start.year()); + startDate.subtract(1,'months'); + + endDate.month(start.month()); + endDate.year(start.year()); + endDate.subtract(1,'months'); + endDate.add(offset,'months'); + + runUntil.add(1, 'months'); + break; + case "yearly": + if (startDate.year() != endDate.year()) { + offset = 1; + } + startDate.year(start.year()); + startDate.subtract(1,'years'); + endDate.year(start.year()); + endDate.subtract(1,'years'); + endDate.add(offset,'years'); + + runUntil.add(1, 'years'); + break; + default: + console.log("Wrong repeat format, allowed are: daily, weekly, monthly, yearly. Given:", hiddenDates[i].repeat); + return; } - else { - v = 1; - fillStyle = this._hsv2rgb(h, s, v); - strokeStyle = this.colorAxis; + while (startDate < runUntil) { + body.hiddenDates.push({start: startDate.valueOf(), end: endDate.valueOf()}); + switch (hiddenDates[i].repeat) { + case "daily": + startDate.add(1, 'days'); + endDate.add(1, 'days'); + break; + case "weekly": + startDate.add(1, 'weeks'); + endDate.add(1, 'weeks'); + break + case "monthly": + startDate.add(1, 'months'); + endDate.add(1, 'months'); + break; + case "yearly": + startDate.add(1, 'y'); + endDate.add(1, 'y'); + break; + default: + console.log("Wrong repeat format, allowed are: daily, weekly, monthly, yearly. Given:", hiddenDates[i].repeat); + return; + } } + body.hiddenDates.push({start: startDate.valueOf(), end: endDate.valueOf()}); } - else { - fillStyle = 'gray'; - strokeStyle = this.colorAxis; - } - lineWidth = 0.5; - - ctx.lineWidth = lineWidth; - ctx.fillStyle = fillStyle; - ctx.strokeStyle = strokeStyle; - ctx.beginPath(); - ctx.moveTo(point.screen.x, point.screen.y); - ctx.lineTo(right.screen.x, right.screen.y); - ctx.lineTo(cross.screen.x, cross.screen.y); - ctx.lineTo(top.screen.x, top.screen.y); - ctx.closePath(); - ctx.fill(); - ctx.stroke(); } } + // remove duplicates, merge where possible + exports.removeDuplicates(body); + // ensure the new positions are not on hidden dates + var startHidden = exports.isHidden(body.range.start, body.hiddenDates); + var endHidden = exports.isHidden(body.range.end,body.hiddenDates); + var rangeStart = body.range.start; + var rangeEnd = body.range.end; + if (startHidden.hidden == true) {rangeStart = body.range.startToFront == true ? startHidden.startDate - 1 : startHidden.endDate + 1;} + if (endHidden.hidden == true) {rangeEnd = body.range.endToFront == true ? endHidden.startDate - 1 : endHidden.endDate + 1;} + if (startHidden.hidden == true || endHidden.hidden == true) { + body.range._applyRange(rangeStart, rangeEnd); + } } - else { // grid style - for (i = 0; i < this.dataPoints.length; i++) { - point = this.dataPoints[i]; - right = this.dataPoints[i].pointRight; - top = this.dataPoints[i].pointTop; - if (point !== undefined) { - if (this.showPerspective) { - lineWidth = 2 / -point.trans.z; - } - else { - lineWidth = 2 * -(this.eye.z / this.camera.getArmLength()); - } - } + } - if (point !== undefined && right !== undefined) { - // calculate Hue from the current value. At zMin the hue is 240, at zMax the hue is 0 - zAvg = (point.point.z + right.point.z) / 2; - h = (1 - (zAvg - this.zMin) * this.scale.z / this.verticalRatio) * 240; - ctx.lineWidth = lineWidth; - ctx.strokeStyle = this._hsv2rgb(h, 1, 1); - ctx.beginPath(); - ctx.moveTo(point.screen.x, point.screen.y); - ctx.lineTo(right.screen.x, right.screen.y); - ctx.stroke(); + /** + * remove duplicates from the hidden dates list. Duplicates are evil. They mess everything up. + * Scales with N^2 + * @param body + */ + exports.removeDuplicates = function(body) { + var hiddenDates = body.hiddenDates; + var safeDates = []; + for (var i = 0; i < hiddenDates.length; i++) { + for (var j = 0; j < hiddenDates.length; j++) { + if (i != j && hiddenDates[j].remove != true && hiddenDates[i].remove != true) { + // j inside i + if (hiddenDates[j].start >= hiddenDates[i].start && hiddenDates[j].end <= hiddenDates[i].end) { + hiddenDates[j].remove = true; + } + // j start inside i + else if (hiddenDates[j].start >= hiddenDates[i].start && hiddenDates[j].start <= hiddenDates[i].end) { + hiddenDates[i].end = hiddenDates[j].end; + hiddenDates[j].remove = true; + } + // j end inside i + else if (hiddenDates[j].end >= hiddenDates[i].start && hiddenDates[j].end <= hiddenDates[i].end) { + hiddenDates[i].start = hiddenDates[j].start; + hiddenDates[j].remove = true; + } } + } + } - if (point !== undefined && top !== undefined) { - // calculate Hue from the current value. At zMin the hue is 240, at zMax the hue is 0 - zAvg = (point.point.z + top.point.z) / 2; - h = (1 - (zAvg - this.zMin) * this.scale.z / this.verticalRatio) * 240; - - ctx.lineWidth = lineWidth; - ctx.strokeStyle = this._hsv2rgb(h, 1, 1); - ctx.beginPath(); - ctx.moveTo(point.screen.x, point.screen.y); - ctx.lineTo(top.screen.x, top.screen.y); - ctx.stroke(); - } + for (var i = 0; i < hiddenDates.length; i++) { + if (hiddenDates[i].remove !== true) { + safeDates.push(hiddenDates[i]); } } - }; + body.hiddenDates = safeDates; + body.hiddenDates.sort(function (a, b) { + return a.start - b.start; + }); // sort by start time + } + + exports.printDates = function(dates) { + for (var i =0; i < dates.length; i++) { + console.log(i, new Date(dates[i].start),new Date(dates[i].end), dates[i].start, dates[i].end, dates[i].remove); + } + } /** - * Draw all datapoints as dots. - * This function can be used when the style is 'dot' or 'dot-line' + * Used in TimeStep to avoid the hidden times. + * @param timeStep + * @param previousTime */ - Graph3d.prototype._redrawDataDot = function() { - var canvas = this.frame.canvas; - var ctx = canvas.getContext('2d'); - var i; - - if (this.dataPoints === undefined || this.dataPoints.length <= 0) - return; // TODO: throw exception? - - // calculate the translations of all points - for (i = 0; i < this.dataPoints.length; i++) { - var trans = this._convertPointToTranslation(this.dataPoints[i].point); - var screen = this._convertTranslationToScreen(trans); - this.dataPoints[i].trans = trans; - this.dataPoints[i].screen = screen; - - // calculate the distance from the point at the bottom to the camera - var transBottom = this._convertPointToTranslation(this.dataPoints[i].bottom); - this.dataPoints[i].dist = this.showPerspective ? transBottom.length() : -transBottom.z; + exports.stepOverHiddenDates = function(timeStep, previousTime) { + var stepInHidden = false; + var currentValue = timeStep.current.valueOf(); + for (var i = 0; i < timeStep.hiddenDates.length; i++) { + var startDate = timeStep.hiddenDates[i].start; + var endDate = timeStep.hiddenDates[i].end; + if (currentValue >= startDate && currentValue < endDate) { + stepInHidden = true; + break; + } } - // order the translated points by depth - var sortDepth = function (a, b) { - return b.dist - a.dist; - }; - this.dataPoints.sort(sortDepth); + if (stepInHidden == true && currentValue < timeStep._end.valueOf() && currentValue != previousTime) { + var prevValue = moment(previousTime); + var newValue = moment(endDate); + //check if the next step should be major + if (prevValue.year() != newValue.year()) {timeStep.switchedYear = true;} + else if (prevValue.month() != newValue.month()) {timeStep.switchedMonth = true;} + else if (prevValue.dayOfYear() != newValue.dayOfYear()) {timeStep.switchedDay = true;} - // draw the datapoints as colored circles - var dotSize = this.frame.clientWidth * 0.02; // px - for (i = 0; i < this.dataPoints.length; i++) { - var point = this.dataPoints[i]; + timeStep.current = newValue.toDate(); + } + }; - if (this.style === Graph3d.STYLE.DOTLINE) { - // draw a vertical line from the bottom to the graph value - //var from = this._convert3Dto2D(new Point3d(point.point.x, point.point.y, this.zMin)); - var from = this._convert3Dto2D(point.bottom); - ctx.lineWidth = 1; - ctx.strokeStyle = this.colorGrid; - ctx.beginPath(); - ctx.moveTo(from.x, from.y); - ctx.lineTo(point.screen.x, point.screen.y); - ctx.stroke(); - } - // calculate radius for the circle - var size; - if (this.style === Graph3d.STYLE.DOTSIZE) { - size = dotSize/2 + 2*dotSize * (point.point.value - this.valueMin) / (this.valueMax - this.valueMin); - } - else { - size = dotSize; - } + ///** + // * Used in TimeStep to avoid the hidden times. + // * @param timeStep + // * @param previousTime + // */ + //exports.checkFirstStep = function(timeStep) { + // var stepInHidden = false; + // var currentValue = timeStep.current.valueOf(); + // for (var i = 0; i < timeStep.hiddenDates.length; i++) { + // var startDate = timeStep.hiddenDates[i].start; + // var endDate = timeStep.hiddenDates[i].end; + // if (currentValue >= startDate && currentValue < endDate) { + // stepInHidden = true; + // break; + // } + // } + // + // if (stepInHidden == true && currentValue <= timeStep._end.valueOf()) { + // var newValue = moment(endDate); + // timeStep.current = newValue.toDate(); + // } + //}; - var radius; - if (this.showPerspective) { - radius = size / -point.trans.z; - } - else { - radius = size * -(this.eye.z / this.camera.getArmLength()); - } - if (radius < 0) { - radius = 0; + /** + * replaces the Core toScreen methods + * @param Core + * @param time + * @param width + * @returns {number} + */ + exports.toScreen = function(Core, time, width) { + if (Core.body.hiddenDates.length == 0) { + var conversion = Core.range.conversion(width); + return (time.valueOf() - conversion.offset) * conversion.scale; + } + else { + var hidden = exports.isHidden(time, Core.body.hiddenDates) + if (hidden.hidden == true) { + time = hidden.startDate; } - var hue, color, borderColor; - if (this.style === Graph3d.STYLE.DOTCOLOR ) { - // calculate the color based on the value - hue = (1 - (point.point.value - this.valueMin) * this.scale.value) * 240; - color = this._hsv2rgb(hue, 1, 1); - borderColor = this._hsv2rgb(hue, 1, 0.8); - } - else if (this.style === Graph3d.STYLE.DOTSIZE) { - color = this.colorDot; - borderColor = this.colorDotBorder; - } - else { - // calculate Hue from the current value. At zMin the hue is 240, at zMax the hue is 0 - hue = (1 - (point.point.z - this.zMin) * this.scale.z / this.verticalRatio) * 240; - color = this._hsv2rgb(hue, 1, 1); - borderColor = this._hsv2rgb(hue, 1, 0.8); - } + var duration = exports.getHiddenDurationBetween(Core.body.hiddenDates, Core.range.start, Core.range.end); + time = exports.correctTimeForHidden(Core.body.hiddenDates, Core.range, time); - // draw the circle - ctx.lineWidth = 1.0; - ctx.strokeStyle = borderColor; - ctx.fillStyle = color; - ctx.beginPath(); - ctx.arc(point.screen.x, point.screen.y, radius, 0, Math.PI*2, true); - ctx.fill(); - ctx.stroke(); + var conversion = Core.range.conversion(width, duration); + return (time.valueOf() - conversion.offset) * conversion.scale; } }; + /** - * Draw all datapoints as bars. - * This function can be used when the style is 'bar', 'bar-color', or 'bar-size' + * Replaces the core toTime methods + * @param body + * @param range + * @param x + * @param width + * @returns {Date} */ - Graph3d.prototype._redrawDataBar = function() { - var canvas = this.frame.canvas; - var ctx = canvas.getContext('2d'); - var i, j, surface, corners; + exports.toTime = function(Core, x, width) { + if (Core.body.hiddenDates.length == 0) { + var conversion = Core.range.conversion(width); + return new Date(x / conversion.scale + conversion.offset); + } + else { + var hiddenDuration = exports.getHiddenDurationBetween(Core.body.hiddenDates, Core.range.start, Core.range.end); + var totalDuration = Core.range.end - Core.range.start - hiddenDuration; + var partialDuration = totalDuration * x / width; + var accumulatedHiddenDuration = exports.getAccumulatedHiddenDuration(Core.body.hiddenDates, Core.range, partialDuration); - if (this.dataPoints === undefined || this.dataPoints.length <= 0) - return; // TODO: throw exception? + var newTime = new Date(accumulatedHiddenDuration + partialDuration + Core.range.start); + return newTime; + } + }; - // calculate the translations of all points - for (i = 0; i < this.dataPoints.length; i++) { - var trans = this._convertPointToTranslation(this.dataPoints[i].point); - var screen = this._convertTranslationToScreen(trans); - this.dataPoints[i].trans = trans; - this.dataPoints[i].screen = screen; - // calculate the distance from the point at the bottom to the camera - var transBottom = this._convertPointToTranslation(this.dataPoints[i].bottom); - this.dataPoints[i].dist = this.showPerspective ? transBottom.length() : -transBottom.z; + /** + * Support function + * + * @param hiddenDates + * @param range + * @returns {number} + */ + exports.getHiddenDurationBetween = function(hiddenDates, start, end) { + var duration = 0; + for (var i = 0; i < hiddenDates.length; i++) { + var startDate = hiddenDates[i].start; + var endDate = hiddenDates[i].end; + // if time after the cutout, and the + if (startDate >= start && endDate < end) { + duration += endDate - startDate; + } } + return duration; + }; - // order the translated points by depth - var sortDepth = function (a, b) { - return b.dist - a.dist; - }; - this.dataPoints.sort(sortDepth); - - // draw the datapoints as bars - var xWidth = this.xBarWidth / 2; - var yWidth = this.yBarWidth / 2; - for (i = 0; i < this.dataPoints.length; i++) { - var point = this.dataPoints[i]; - // determine color - var hue, color, borderColor; - if (this.style === Graph3d.STYLE.BARCOLOR ) { - // calculate the color based on the value - hue = (1 - (point.point.value - this.valueMin) * this.scale.value) * 240; - color = this._hsv2rgb(hue, 1, 1); - borderColor = this._hsv2rgb(hue, 1, 0.8); - } - else if (this.style === Graph3d.STYLE.BARSIZE) { - color = this.colorDot; - borderColor = this.colorDotBorder; - } - else { - // calculate Hue from the current value. At zMin the hue is 240, at zMax the hue is 0 - hue = (1 - (point.point.z - this.zMin) * this.scale.z / this.verticalRatio) * 240; - color = this._hsv2rgb(hue, 1, 1); - borderColor = this._hsv2rgb(hue, 1, 0.8); + /** + * Support function + * @param hiddenDates + * @param range + * @param time + * @returns {{duration: number, time: *, offset: number}} + */ + exports.correctTimeForHidden = function(hiddenDates, range, time) { + time = moment(time).toDate().valueOf(); + time -= exports.getHiddenDurationBefore(hiddenDates,range,time); + return time; + }; + + exports.getHiddenDurationBefore = function(hiddenDates, range, time) { + var timeOffset = 0; + time = moment(time).toDate().valueOf(); + + for (var i = 0; i < hiddenDates.length; i++) { + var startDate = hiddenDates[i].start; + var endDate = hiddenDates[i].end; + // if time after the cutout, and the + if (startDate >= range.start && endDate < range.end) { + if (time >= endDate) { + timeOffset += (endDate - startDate); + } } + } + return timeOffset; + } - // calculate size for the bar - if (this.style === Graph3d.STYLE.BARSIZE) { - xWidth = (this.xBarWidth / 2) * ((point.point.value - this.valueMin) / (this.valueMax - this.valueMin) * 0.8 + 0.2); - yWidth = (this.yBarWidth / 2) * ((point.point.value - this.valueMin) / (this.valueMax - this.valueMin) * 0.8 + 0.2); + /** + * sum the duration from start to finish, including the hidden duration, + * until the required amount has been reached, return the accumulated hidden duration + * @param hiddenDates + * @param range + * @param time + * @returns {{duration: number, time: *, offset: number}} + */ + exports.getAccumulatedHiddenDuration = function(hiddenDates, range, requiredDuration) { + var hiddenDuration = 0; + var duration = 0; + var previousPoint = range.start; + //exports.printDates(hiddenDates) + for (var i = 0; i < hiddenDates.length; i++) { + var startDate = hiddenDates[i].start; + var endDate = hiddenDates[i].end; + // if time after the cutout, and the + if (startDate >= range.start && endDate < range.end) { + duration += startDate - previousPoint; + previousPoint = endDate; + if (duration >= requiredDuration) { + break; + } + else { + hiddenDuration += endDate - startDate; + } } + } - // calculate all corner points - var me = this; - var point3d = point.point; - var top = [ - {point: new Point3d(point3d.x - xWidth, point3d.y - yWidth, point3d.z)}, - {point: new Point3d(point3d.x + xWidth, point3d.y - yWidth, point3d.z)}, - {point: new Point3d(point3d.x + xWidth, point3d.y + yWidth, point3d.z)}, - {point: new Point3d(point3d.x - xWidth, point3d.y + yWidth, point3d.z)} - ]; - var bottom = [ - {point: new Point3d(point3d.x - xWidth, point3d.y - yWidth, this.zMin)}, - {point: new Point3d(point3d.x + xWidth, point3d.y - yWidth, this.zMin)}, - {point: new Point3d(point3d.x + xWidth, point3d.y + yWidth, this.zMin)}, - {point: new Point3d(point3d.x - xWidth, point3d.y + yWidth, this.zMin)} - ]; + return hiddenDuration; + }; - // calculate screen location of the points - top.forEach(function (obj) { - obj.screen = me._convert3Dto2D(obj.point); - }); - bottom.forEach(function (obj) { - obj.screen = me._convert3Dto2D(obj.point); - }); - // create five sides, calculate both corner points and center points - var surfaces = [ - {corners: top, center: Point3d.avg(bottom[0].point, bottom[2].point)}, - {corners: [top[0], top[1], bottom[1], bottom[0]], center: Point3d.avg(bottom[1].point, bottom[0].point)}, - {corners: [top[1], top[2], bottom[2], bottom[1]], center: Point3d.avg(bottom[2].point, bottom[1].point)}, - {corners: [top[2], top[3], bottom[3], bottom[2]], center: Point3d.avg(bottom[3].point, bottom[2].point)}, - {corners: [top[3], top[0], bottom[0], bottom[3]], center: Point3d.avg(bottom[0].point, bottom[3].point)} - ]; - point.surfaces = surfaces; - // calculate the distance of each of the surface centers to the camera - for (j = 0; j < surfaces.length; j++) { - surface = surfaces[j]; - var transCenter = this._convertPointToTranslation(surface.center); - surface.dist = this.showPerspective ? transCenter.length() : -transCenter.z; - // TODO: this dept calculation doesn't work 100% of the cases due to perspective, - // but the current solution is fast/simple and works in 99.9% of all cases - // the issue is visible in example 14, with graph.setCameraPosition({horizontal: 2.97, vertical: 0.5, distance: 0.9}) + /** + * used to step over to either side of a hidden block. Correction is disabled on tablets, might be set to true + * @param hiddenDates + * @param time + * @param direction + * @param correctionEnabled + * @returns {*} + */ + exports.snapAwayFromHidden = function(hiddenDates, time, direction, correctionEnabled) { + var isHidden = exports.isHidden(time, hiddenDates); + if (isHidden.hidden == true) { + if (direction < 0) { + if (correctionEnabled == true) { + return isHidden.startDate - (isHidden.endDate - time) - 1; + } + else { + return isHidden.startDate - 1; + } + } + else { + if (correctionEnabled == true) { + return isHidden.endDate + (time - isHidden.startDate) + 1; + } + else { + return isHidden.endDate + 1; + } } + } + else { + return time; + } - // order the surfaces by their (translated) depth - surfaces.sort(function (a, b) { - var diff = b.dist - a.dist; - if (diff) return diff; + } - // if equal depth, sort the top surface last - if (a.corners === top) return 1; - if (b.corners === top) return -1; - // both are equal - return 0; - }); + /** + * Check if a time is hidden + * + * @param time + * @param hiddenDates + * @returns {{hidden: boolean, startDate: Window.start, endDate: *}} + */ + exports.isHidden = function(time, hiddenDates) { + for (var i = 0; i < hiddenDates.length; i++) { + var startDate = hiddenDates[i].start; + var endDate = hiddenDates[i].end; - // draw the ordered surfaces - ctx.lineWidth = 1; - ctx.strokeStyle = borderColor; - ctx.fillStyle = color; - // NOTE: we start at j=2 instead of j=0 as we don't need to draw the two surfaces at the backside - for (j = 2; j < surfaces.length; j++) { - surface = surfaces[j]; - corners = surface.corners; - ctx.beginPath(); - ctx.moveTo(corners[3].screen.x, corners[3].screen.y); - ctx.lineTo(corners[0].screen.x, corners[0].screen.y); - ctx.lineTo(corners[1].screen.x, corners[1].screen.y); - ctx.lineTo(corners[2].screen.x, corners[2].screen.y); - ctx.lineTo(corners[3].screen.x, corners[3].screen.y); - ctx.fill(); - ctx.stroke(); + if (time >= startDate && time < endDate) { // if the start is entering a hidden zone + return {hidden: true, startDate: startDate, endDate: endDate}; + break; } } - }; + return {hidden: false, startDate: startDate, endDate: endDate}; + } +/***/ }, +/* 16 */ +/***/ function(module, exports, __webpack_require__) { /** - * Draw a line through all datapoints. - * This function can be used when the style is 'line' + * @constructor DataStep + * The class DataStep is an iterator for data for the lineGraph. You provide a start data point and an + * end data point. The class itself determines the best scale (step size) based on the + * provided start Date, end Date, and minimumStep. + * + * If minimumStep is provided, the step size is chosen as close as possible + * to the minimumStep but larger than minimumStep. If minimumStep is not + * provided, the scale is set to 1 DAY. + * The minimumStep should correspond with the onscreen size of about 6 characters + * + * Alternatively, you can set a scale by hand. + * After creation, you can initialize the class by executing first(). Then you + * can iterate from the start date to the end date via next(). You can check if + * the end date is reached with the function hasNext(). After each step, you can + * retrieve the current date via getCurrent(). + * The DataStep has scales ranging from milliseconds, seconds, minutes, hours, + * days, to years. + * + * Version: 1.2 + * + * @param {Date} [start] The start date, for example new Date(2010, 9, 21) + * or new Date(2010, 9, 21, 23, 45, 00) + * @param {Date} [end] The end date + * @param {Number} [minimumStep] Optional. Minimum step size in milliseconds */ - Graph3d.prototype._redrawDataLine = function() { - var canvas = this.frame.canvas, - ctx = canvas.getContext('2d'), - point, i; + function DataStep(start, end, minimumStep, containerHeight, customRange, alignZeros) { + // variables + this.current = 0; - if (this.dataPoints === undefined || this.dataPoints.length <= 0) - return; // TODO: throw exception? + this.autoScale = true; + this.stepIndex = 0; + this.step = 1; + this.scale = 1; - // calculate the translations of all points - for (i = 0; i < this.dataPoints.length; i++) { - var trans = this._convertPointToTranslation(this.dataPoints[i].point); - var screen = this._convertTranslationToScreen(trans); + this.marginStart; + this.marginEnd; + this.deadSpace = 0; - this.dataPoints[i].trans = trans; - this.dataPoints[i].screen = screen; - } + this.majorSteps = [1, 2, 5, 10]; + this.minorSteps = [0.25, 0.5, 1, 2]; - // start the line - if (this.dataPoints.length > 0) { - point = this.dataPoints[0]; + this.alignZeros = alignZeros; - ctx.lineWidth = 1; // TODO: make customizable - ctx.strokeStyle = 'blue'; // TODO: make customizable - ctx.beginPath(); - ctx.moveTo(point.screen.x, point.screen.y); - } + this.setRange(start, end, minimumStep, containerHeight, customRange); + } - // draw the datapoints as colored circles - for (i = 1; i < this.dataPoints.length; i++) { - point = this.dataPoints[i]; - ctx.lineTo(point.screen.x, point.screen.y); - } - // finish the line - if (this.dataPoints.length > 0) { - ctx.stroke(); - } - }; /** - * Start a moving operation inside the provided parent element - * @param {Event} event The event that occurred (required for - * retrieving the mouse position) + * Set a new range + * If minimumStep is provided, the step size is chosen as close as possible + * to the minimumStep but larger than minimumStep. If minimumStep is not + * provided, the scale is set to 1 DAY. + * The minimumStep should correspond with the onscreen size of about 6 characters + * @param {Number} [start] The start date and time. + * @param {Number} [end] The end date and time. + * @param {Number} [minimumStep] Optional. Minimum step size in milliseconds */ - Graph3d.prototype._onMouseDown = function(event) { - event = event || window.event; + DataStep.prototype.setRange = function(start, end, minimumStep, containerHeight, customRange) { + this._start = customRange.min === undefined ? start : customRange.min; + this._end = customRange.max === undefined ? end : customRange.max; - // check if mouse is still down (may be up when focus is lost for example - // in an iframe) - if (this.leftButtonDown) { - this._onMouseUp(event); + if (this._start == this._end) { + this._start -= 0.75; + this._end += 1; } - // only react on left mouse button down - this.leftButtonDown = event.which ? (event.which === 1) : (event.button === 1); - if (!this.leftButtonDown && !this.touchDown) return; - - // get mouse position (different code for IE and all other browsers) - this.startMouseX = getMouseX(event); - this.startMouseY = getMouseY(event); - - this.startStart = new Date(this.start); - this.startEnd = new Date(this.end); - this.startArmRotation = this.camera.getArmRotation(); - - this.frame.style.cursor = 'move'; + if (this.autoScale == true) { + this.setMinimumStep(minimumStep, containerHeight); + } - // add event listeners to handle moving the contents - // we store the function onmousemove and onmouseup in the graph, so we can - // remove the eventlisteners lateron in the function mouseUp() - var me = this; - this.onmousemove = function (event) {me._onMouseMove(event);}; - this.onmouseup = function (event) {me._onMouseUp(event);}; - util.addEventListener(document, 'mousemove', me.onmousemove); - util.addEventListener(document, 'mouseup', me.onmouseup); - util.preventDefault(event); + this.setFirst(customRange); }; - /** - * Perform moving operating. - * This function activated from within the funcion Graph.mouseDown(). - * @param {Event} event Well, eehh, the event + * Automatically determine the scale that bests fits the provided minimum step + * @param {Number} [minimumStep] The minimum step size in milliseconds */ - Graph3d.prototype._onMouseMove = function (event) { - event = event || window.event; - - // calculate change in mouse position - var diffX = parseFloat(getMouseX(event)) - this.startMouseX; - var diffY = parseFloat(getMouseY(event)) - this.startMouseY; - - var horizontalNew = this.startArmRotation.horizontal + diffX / 200; - var verticalNew = this.startArmRotation.vertical + diffY / 200; + DataStep.prototype.setMinimumStep = function(minimumStep, containerHeight) { + // round to floor + var size = this._end - this._start; + var safeSize = size * 1.2; + var minimumStepValue = minimumStep * (safeSize / containerHeight); + var orderOfMagnitude = Math.round(Math.log(safeSize)/Math.LN10); - var snapAngle = 4; // degrees - var snapValue = Math.sin(snapAngle / 360 * 2 * Math.PI); + var minorStepIdx = -1; + var magnitudefactor = Math.pow(10,orderOfMagnitude); - // snap horizontally to nice angles at 0pi, 0.5pi, 1pi, 1.5pi, etc... - // the -0.001 is to take care that the vertical axis is always drawn at the left front corner - if (Math.abs(Math.sin(horizontalNew)) < snapValue) { - horizontalNew = Math.round((horizontalNew / Math.PI)) * Math.PI - 0.001; - } - if (Math.abs(Math.cos(horizontalNew)) < snapValue) { - horizontalNew = (Math.round((horizontalNew/ Math.PI - 0.5)) + 0.5) * Math.PI - 0.001; + var start = 0; + if (orderOfMagnitude < 0) { + start = orderOfMagnitude; } - // snap vertically to nice angles - if (Math.abs(Math.sin(verticalNew)) < snapValue) { - verticalNew = Math.round((verticalNew / Math.PI)) * Math.PI; - } - if (Math.abs(Math.cos(verticalNew)) < snapValue) { - verticalNew = (Math.round((verticalNew/ Math.PI - 0.5)) + 0.5) * Math.PI; + var solutionFound = false; + for (var i = start; Math.abs(i) <= Math.abs(orderOfMagnitude); i++) { + magnitudefactor = Math.pow(10,i); + for (var j = 0; j < this.minorSteps.length; j++) { + var stepSize = magnitudefactor * this.minorSteps[j]; + if (stepSize >= minimumStepValue) { + solutionFound = true; + minorStepIdx = j; + break; + } + } + if (solutionFound == true) { + break; + } } - - this.camera.setArmRotation(horizontalNew, verticalNew); - this.redraw(); - - // fire a cameraPositionChange event - var parameters = this.getCameraPosition(); - this.emit('cameraPositionChange', parameters); - - util.preventDefault(event); + this.stepIndex = minorStepIdx; + this.scale = magnitudefactor; + this.step = magnitudefactor * this.minorSteps[minorStepIdx]; }; + /** - * Stop moving operating. - * This function activated from within the funcion Graph.mouseDown(). - * @param {event} event The event + * Round the current date to the first minor date value + * This must be executed once when the current date is set to start Date */ - Graph3d.prototype._onMouseUp = function (event) { - this.frame.style.cursor = 'auto'; - this.leftButtonDown = false; + DataStep.prototype.setFirst = function(customRange) { + if (customRange === undefined) { + customRange = {}; + } - // remove event listeners here - util.removeEventListener(document, 'mousemove', this.onmousemove); - util.removeEventListener(document, 'mouseup', this.onmouseup); - util.preventDefault(event); - }; + var niceStart = customRange.min === undefined ? this._start - (this.scale * 2 * this.minorSteps[this.stepIndex]) : customRange.min; + var niceEnd = customRange.max === undefined ? this._end + (this.scale * this.minorSteps[this.stepIndex]) : customRange.max; - /** - * After having moved the mouse, a tooltip should pop up when the mouse is resting on a data point - * @param {Event} event A mouse move event - */ - Graph3d.prototype._onTooltip = function (event) { - var delay = 300; // ms - var boundingRect = this.frame.getBoundingClientRect(); - var mouseX = getMouseX(event) - boundingRect.left; - var mouseY = getMouseY(event) - boundingRect.top; + this.marginEnd = customRange.max === undefined ? this.roundToMinor(niceEnd) : customRange.max; + this.marginStart = customRange.min === undefined ? this.roundToMinor(niceStart) : customRange.min; - if (!this.showTooltip) { - return; + // if we need to align the zero's we need to make sure that there is a zero to use. + if (this.alignZeros == true && (this.marginEnd - this.marginStart) % this.step != 0) { + this.marginEnd += this.marginEnd % this.step; } - if (this.tooltipTimeout) { - clearTimeout(this.tooltipTimeout); - } + this.deadSpace = this.roundToMinor(niceEnd) - niceEnd + this.roundToMinor(niceStart) - niceStart; + this.marginRange = this.marginEnd - this.marginStart; - // (delayed) display of a tooltip only if no mouse button is down - if (this.leftButtonDown) { - this._hideTooltip(); - return; - } - if (this.tooltip && this.tooltip.dataPoint) { - // tooltip is currently visible - var dataPoint = this._dataPointFromXY(mouseX, mouseY); - if (dataPoint !== this.tooltip.dataPoint) { - // datapoint changed - if (dataPoint) { - this._showTooltip(dataPoint); - } - else { - this._hideTooltip(); - } - } + this.current = this.marginEnd; + }; + + DataStep.prototype.roundToMinor = function(value) { + var rounded = value - (value % (this.scale * this.minorSteps[this.stepIndex])); + if (value % (this.scale * this.minorSteps[this.stepIndex]) > 0.5 * (this.scale * this.minorSteps[this.stepIndex])) { + return rounded + (this.scale * this.minorSteps[this.stepIndex]); } else { - // tooltip is currently not visible - var me = this; - this.tooltipTimeout = setTimeout(function () { - me.tooltipTimeout = null; - - // show a tooltip if we have a data point - var dataPoint = me._dataPointFromXY(mouseX, mouseY); - if (dataPoint) { - me._showTooltip(dataPoint); - } - }, delay); + return rounded; } - }; + } + /** - * Event handler for touchstart event on mobile devices + * Check if the there is a next step + * @return {boolean} true if the current date has not passed the end date */ - Graph3d.prototype._onTouchStart = function(event) { - this.touchDown = true; - - var me = this; - this.ontouchmove = function (event) {me._onTouchMove(event);}; - this.ontouchend = function (event) {me._onTouchEnd(event);}; - util.addEventListener(document, 'touchmove', me.ontouchmove); - util.addEventListener(document, 'touchend', me.ontouchend); - - this._onMouseDown(event); + DataStep.prototype.hasNext = function () { + return (this.current >= this.marginStart); }; /** - * Event handler for touchmove event on mobile devices + * Do the next step */ - Graph3d.prototype._onTouchMove = function(event) { - this._onMouseMove(event); + DataStep.prototype.next = function() { + var prev = this.current; + this.current -= this.step; + + // safety mechanism: if current time is still unchanged, move to the end + if (this.current == prev) { + this.current = this._end; + } }; /** - * Event handler for touchend event on mobile devices + * Do the next step */ - Graph3d.prototype._onTouchEnd = function(event) { - this.touchDown = false; - - util.removeEventListener(document, 'touchmove', this.ontouchmove); - util.removeEventListener(document, 'touchend', this.ontouchend); - - this._onMouseUp(event); + DataStep.prototype.previous = function() { + this.current += this.step; + this.marginEnd += this.step; + this.marginRange = this.marginEnd - this.marginStart; }; + /** - * Event handler for mouse wheel event, used to zoom the graph - * Code from http://adomas.org/javascript-mouse-wheel/ - * @param {event} event The event + * Get the current datetime + * @return {String} current The current date */ - Graph3d.prototype._onWheel = function(event) { - if (!event) /* For IE. */ - event = window.event; - - // retrieve delta - var delta = 0; - if (event.wheelDelta) { /* IE/Opera. */ - delta = event.wheelDelta/120; - } else if (event.detail) { /* Mozilla case. */ - // In Mozilla, sign of delta is different than in IE. - // Also, delta is multiple of 3. - delta = -event.detail/3; - } - - // If delta is nonzero, handle it. - // Basically, delta is now positive if wheel was scrolled up, - // and negative, if wheel was scrolled down. - if (delta) { - var oldLength = this.camera.getArmLength(); - var newLength = oldLength * (1 - delta / 10); - - this.camera.setArmLength(newLength); - this.redraw(); + DataStep.prototype.getCurrent = function(decimals) { + // prevent round-off errors when close to zero + var current = (Math.abs(this.current) < this.step / 2) ? 0 : this.current; + var toPrecision = '' + Number(current).toPrecision(5); - this._hideTooltip(); + // If decimals is specified, then limit or extend the string as required + if(decimals !== undefined && !isNaN(Number(decimals))) { + // If string includes exponent, then we need to add it to the end + var exp = ""; + var index = toPrecision.indexOf("e"); + if(index != -1) { + // Get the exponent + exp = toPrecision.slice(index); + // Remove the exponent in case we need to zero-extend + toPrecision = toPrecision.slice(0, index); + } + index = Math.max(toPrecision.indexOf(","), toPrecision.indexOf(".")); + if(index === -1) { + // No decimal found - if we want decimals, then we need to add it + if(decimals !== 0) { + toPrecision += '.'; + } + // Calculate how long the string should be + index = toPrecision.length + decimals; + } + else if(decimals !== 0) { + // Calculate how long the string should be - accounting for the decimal place + index += decimals + 1; + } + if(index > toPrecision.length) { + // We need to add zeros! + for(var cnt = index - toPrecision.length; cnt > 0; cnt--) { + toPrecision += '0'; + } + } + else { + // we need to remove characters + toPrecision = toPrecision.slice(0, index); + } + // Add the exponent if there is one + toPrecision += exp; + } + else { + if (toPrecision.indexOf(",") != -1 || toPrecision.indexOf(".") != -1) { + // If no decimal is specified, and there are decimal places, remove trailing zeros + for (var i = toPrecision.length - 1; i > 0; i--) { + if (toPrecision[i] == "0") { + toPrecision = toPrecision.slice(0, i); + } + else if (toPrecision[i] == "." || toPrecision[i] == ",") { + toPrecision = toPrecision.slice(0, i); + break; + } + else { + break; + } + } + } } - // fire a cameraPositionChange event - var parameters = this.getCameraPosition(); - this.emit('cameraPositionChange', parameters); - - // Prevent default actions caused by mouse wheel. - // That might be ugly, but we handle scrolls somehow - // anyway, so don't bother here.. - util.preventDefault(event); + return toPrecision; }; - /** - * Test whether a point lies inside given 2D triangle - * @param {Point2d} point - * @param {Point2d[]} triangle - * @return {boolean} Returns true if given point lies inside or on the edge of the triangle - * @private - */ - Graph3d.prototype._insideTriangle = function (point, triangle) { - var a = triangle[0], - b = triangle[1], - c = triangle[2]; - function sign (x) { - return x > 0 ? 1 : x < 0 ? -1 : 0; - } - var as = sign((b.x - a.x) * (point.y - a.y) - (b.y - a.y) * (point.x - a.x)); - var bs = sign((c.x - b.x) * (point.y - b.y) - (c.y - b.y) * (point.x - b.x)); - var cs = sign((a.x - c.x) * (point.y - c.y) - (a.y - c.y) * (point.x - c.x)); + /** + * Snap a date to a rounded value. + * The snap intervals are dependent on the current scale and step. + * @param {Date} date the date to be snapped. + * @return {Date} snappedDate + */ + DataStep.prototype.snap = function(date) { - // each of the three signs must be either equal to each other or zero - return (as == 0 || bs == 0 || as == bs) && - (bs == 0 || cs == 0 || bs == cs) && - (as == 0 || cs == 0 || as == cs); }; /** - * Find a data point close to given screen position (x, y) - * @param {Number} x - * @param {Number} y - * @return {Object | null} The closest data point or null if not close to any data point - * @private + * Check if the current value is a major value (for example when the step + * is DAY, a major value is each first day of the MONTH) + * @return {boolean} true if current date is major, else false. */ - Graph3d.prototype._dataPointFromXY = function (x, y) { - var i, - distMax = 100, // px - dataPoint = null, - closestDataPoint = null, - closestDist = null, - center = new Point2d(x, y); + DataStep.prototype.isMajor = function() { + return (this.current % (this.scale * this.majorSteps[this.stepIndex]) == 0); + }; - if (this.style === Graph3d.STYLE.BAR || - this.style === Graph3d.STYLE.BARCOLOR || - this.style === Graph3d.STYLE.BARSIZE) { - // the data points are ordered from far away to closest - for (i = this.dataPoints.length - 1; i >= 0; i--) { - dataPoint = this.dataPoints[i]; - var surfaces = dataPoint.surfaces; - if (surfaces) { - for (var s = surfaces.length - 1; s >= 0; s--) { - // split each surface in two triangles, and see if the center point is inside one of these - var surface = surfaces[s]; - var corners = surface.corners; - var triangle1 = [corners[0].screen, corners[1].screen, corners[2].screen]; - var triangle2 = [corners[2].screen, corners[3].screen, corners[0].screen]; - if (this._insideTriangle(center, triangle1) || - this._insideTriangle(center, triangle2)) { - // return immediately at the first hit - return dataPoint; - } - } - } - } - } - else { - // find the closest data point, using distance to the center of the point on 2d screen - for (i = 0; i < this.dataPoints.length; i++) { - dataPoint = this.dataPoints[i]; - var point = dataPoint.screen; - if (point) { - var distX = Math.abs(x - point.x); - var distY = Math.abs(y - point.y); - var dist = Math.sqrt(distX * distX + distY * distY); + module.exports = DataStep; - if ((closestDist === null || dist < closestDist) && dist < distMax) { - closestDist = dist; - closestDataPoint = dataPoint; - } - } - } - } +/***/ }, +/* 17 */ +/***/ function(module, exports, __webpack_require__) { - return closestDataPoint; - }; + var util = __webpack_require__(1); + var hammerUtil = __webpack_require__(47); + var moment = __webpack_require__(44); + var Component = __webpack_require__(20); + var DateUtil = __webpack_require__(15); /** - * Display a tooltip for given data point - * @param {Object} dataPoint - * @private + * @constructor Range + * A Range controls a numeric range with a start and end value. + * The Range adjusts the range based on mouse events or programmatic changes, + * and triggers events when the range is changing or has been changed. + * @param {{dom: Object, domProps: Object, emitter: Emitter}} body + * @param {Object} [options] See description at Range.setOptions */ - Graph3d.prototype._showTooltip = function (dataPoint) { - var content, line, dot; - - if (!this.tooltip) { - content = document.createElement('div'); - content.style.position = 'absolute'; - content.style.padding = '10px'; - content.style.border = '1px solid #4d4d4d'; - content.style.color = '#1a1a1a'; - content.style.background = 'rgba(255,255,255,0.7)'; - content.style.borderRadius = '2px'; - content.style.boxShadow = '5px 5px 10px rgba(128,128,128,0.5)'; + function Range(body, options) { + var now = moment().hours(0).minutes(0).seconds(0).milliseconds(0); + this.start = now.clone().add(-3, 'days').valueOf(); // Number + this.end = now.clone().add(4, 'days').valueOf(); // Number - line = document.createElement('div'); - line.style.position = 'absolute'; - line.style.height = '40px'; - line.style.width = '0'; - line.style.borderLeft = '1px solid #4d4d4d'; + this.body = body; + this.deltaDifference = 0; + this.scaleOffset = 0; + this.startToFront = false; + this.endToFront = true; - dot = document.createElement('div'); - dot.style.position = 'absolute'; - dot.style.height = '0'; - dot.style.width = '0'; - dot.style.border = '5px solid #4d4d4d'; - dot.style.borderRadius = '5px'; + // default options + this.defaultOptions = { + start: null, + end: null, + direction: 'horizontal', // 'horizontal' or 'vertical' + moveable: true, + zoomable: true, + min: null, + max: null, + zoomMin: 10, // milliseconds + zoomMax: 1000 * 60 * 60 * 24 * 365 * 10000 // milliseconds + }; + this.options = util.extend({}, this.defaultOptions); - this.tooltip = { - dataPoint: null, - dom: { - content: content, - line: line, - dot: dot - } - }; - } - else { - content = this.tooltip.dom.content; - line = this.tooltip.dom.line; - dot = this.tooltip.dom.dot; - } + this.props = { + touch: {} + }; + this.animateTimer = null; - this._hideTooltip(); + // drag listeners for dragging + this.body.emitter.on('dragstart', this._onDragStart.bind(this)); + this.body.emitter.on('drag', this._onDrag.bind(this)); + this.body.emitter.on('dragend', this._onDragEnd.bind(this)); - this.tooltip.dataPoint = dataPoint; - if (typeof this.showTooltip === 'function') { - content.innerHTML = this.showTooltip(dataPoint.point); - } - else { - content.innerHTML = '' + - '' + - '' + - '' + - '
x:' + dataPoint.point.x + '
y:' + dataPoint.point.y + '
z:' + dataPoint.point.z + '
'; - } + // ignore dragging when holding + this.body.emitter.on('hold', this._onHold.bind(this)); - content.style.left = '0'; - content.style.top = '0'; - this.frame.appendChild(content); - this.frame.appendChild(line); - this.frame.appendChild(dot); + // mouse wheel for zooming + this.body.emitter.on('mousewheel', this._onMouseWheel.bind(this)); + this.body.emitter.on('DOMMouseScroll', this._onMouseWheel.bind(this)); // For FF - // calculate sizes - var contentWidth = content.offsetWidth; - var contentHeight = content.offsetHeight; - var lineHeight = line.offsetHeight; - var dotWidth = dot.offsetWidth; - var dotHeight = dot.offsetHeight; + // pinch to zoom + this.body.emitter.on('touch', this._onTouch.bind(this)); + this.body.emitter.on('pinch', this._onPinch.bind(this)); - var left = dataPoint.screen.x - contentWidth / 2; - left = Math.min(Math.max(left, 10), this.frame.clientWidth - 10 - contentWidth); + this.setOptions(options); + } - line.style.left = dataPoint.screen.x + 'px'; - line.style.top = (dataPoint.screen.y - lineHeight) + 'px'; - content.style.left = left + 'px'; - content.style.top = (dataPoint.screen.y - lineHeight - contentHeight) + 'px'; - dot.style.left = (dataPoint.screen.x - dotWidth / 2) + 'px'; - dot.style.top = (dataPoint.screen.y - dotHeight / 2) + 'px'; - }; + Range.prototype = new Component(); /** - * Hide the tooltip when displayed - * @private + * Set options for the range controller + * @param {Object} options Available options: + * {Number | Date | String} start Start date for the range + * {Number | Date | String} end End date for the range + * {Number} min Minimum value for start + * {Number} max Maximum value for end + * {Number} zoomMin Set a minimum value for + * (end - start). + * {Number} zoomMax Set a maximum value for + * (end - start). + * {Boolean} moveable Enable moving of the range + * by dragging. True by default + * {Boolean} zoomable Enable zooming of the range + * by pinching/scrolling. True by default */ - Graph3d.prototype._hideTooltip = function () { - if (this.tooltip) { - this.tooltip.dataPoint = null; + Range.prototype.setOptions = function (options) { + if (options) { + // copy the options that we know + var fields = ['direction', 'min', 'max', 'zoomMin', 'zoomMax', 'moveable', 'zoomable', 'activate', 'hiddenDates']; + util.selectiveExtend(fields, this.options, options); - for (var prop in this.tooltip.dom) { - if (this.tooltip.dom.hasOwnProperty(prop)) { - var elem = this.tooltip.dom[prop]; - if (elem && elem.parentNode) { - elem.parentNode.removeChild(elem); - } - } + if ('start' in options || 'end' in options) { + // apply a new range. both start and end are optional + this.setRange(options.start, options.end); } } }; - /**--------------------------------------------------------------------------**/ - - /** - * Get the horizontal mouse position from a mouse event - * @param {Event} event - * @return {Number} mouse x + * Test whether direction has a valid value + * @param {String} direction 'horizontal' or 'vertical' */ - function getMouseX (event) { - if ('clientX' in event) return event.clientX; - return event.targetTouches[0] && event.targetTouches[0].clientX || 0; + function validateDirection (direction) { + if (direction != 'horizontal' && direction != 'vertical') { + throw new TypeError('Unknown direction "' + direction + '". ' + + 'Choose "horizontal" or "vertical".'); + } } /** - * Get the vertical mouse position from a mouse event - * @param {Event} event - * @return {Number} mouse y + * Set a new start and end range + * @param {Date | Number | String} [start] + * @param {Date | Number | String} [end] + * @param {boolean | number} [animate=false] If true, the range is animated + * smoothly to the new window. + * If animate is a number, the + * number is taken as duration + * Default duration is 500 ms. + * @param {Boolean} [byUser=false] + * */ - function getMouseY (event) { - if ('clientY' in event) return event.clientY; - return event.targetTouches[0] && event.targetTouches[0].clientY || 0; - } - - module.exports = Graph3d; - + Range.prototype.setRange = function(start, end, animate, byUser) { + if (byUser !== true) { + byUser = false; + } + var _start = start != undefined ? util.convert(start, 'Date').valueOf() : null; + var _end = end != undefined ? util.convert(end, 'Date').valueOf() : null; + this._cancelAnimation(); -/***/ }, -/* 11 */ -/***/ function(module, exports, __webpack_require__) { + if (animate) { + var me = this; + var initStart = this.start; + var initEnd = this.end; + var duration = typeof animate === 'number' ? animate : 500; + var initTime = new Date().valueOf(); + var anyChanged = false; - - /** - * Expose `Emitter`. - */ + var next = function () { + if (!me.props.touch.dragging) { + var now = new Date().valueOf(); + var time = now - initTime; + var done = time > duration; + var s = (done || _start === null) ? _start : util.easeInOutQuad(time, initStart, _start, duration); + var e = (done || _end === null) ? _end : util.easeInOutQuad(time, initEnd, _end, duration); - module.exports = Emitter; + changed = me._applyRange(s, e); + DateUtil.updateHiddenDates(me.body, me.options.hiddenDates); + anyChanged = anyChanged || changed; + if (changed) { + me.body.emitter.emit('rangechange', {start: new Date(me.start), end: new Date(me.end), byUser:byUser}); + } - /** - * Initialize a new `Emitter`. - * - * @api public - */ + if (done) { + if (anyChanged) { + me.body.emitter.emit('rangechanged', {start: new Date(me.start), end: new Date(me.end), byUser:byUser}); + } + } + else { + // animate with as high as possible frame rate, leave 20 ms in between + // each to prevent the browser from blocking + me.animateTimer = setTimeout(next, 20); + } + } + } - function Emitter(obj) { - if (obj) return mixin(obj); + return next(); + } + else { + var changed = this._applyRange(_start, _end); + DateUtil.updateHiddenDates(this.body, this.options.hiddenDates); + if (changed) { + var params = {start: new Date(this.start), end: new Date(this.end), byUser:byUser}; + this.body.emitter.emit('rangechange', params); + this.body.emitter.emit('rangechanged', params); + } + } }; /** - * Mixin the emitter properties. - * - * @param {Object} obj - * @return {Object} - * @api private + * Stop an animation + * @private */ - - function mixin(obj) { - for (var key in Emitter.prototype) { - obj[key] = Emitter.prototype[key]; + Range.prototype._cancelAnimation = function () { + if (this.animateTimer) { + clearTimeout(this.animateTimer); + this.animateTimer = null; } - 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 + * Set a new start and end range. This method is the same as setRange, but + * does not trigger a range change and range changed event, and it returns + * true when the range is changed + * @param {Number} [start] + * @param {Number} [end] + * @return {Boolean} changed + * @private */ + Range.prototype._applyRange = function(start, end) { + var newStart = (start != null) ? util.convert(start, 'Date').valueOf() : this.start, + newEnd = (end != null) ? util.convert(end, 'Date').valueOf() : this.end, + max = (this.options.max != null) ? util.convert(this.options.max, 'Date').valueOf() : null, + min = (this.options.min != null) ? util.convert(this.options.min, 'Date').valueOf() : null, + diff; - Emitter.prototype.once = function(event, fn){ - var self = this; - this._callbacks = this._callbacks || {}; + // check for valid number + if (isNaN(newStart) || newStart === null) { + throw new Error('Invalid start "' + start + '"'); + } + if (isNaN(newEnd) || newEnd === null) { + throw new Error('Invalid end "' + end + '"'); + } - function on() { - self.off(event, on); - fn.apply(this, arguments); + // prevent start < end + if (newEnd < newStart) { + newEnd = newStart; } - on.fn = fn; - this.on(event, on); - return this; - }; + // prevent start < min + if (min !== null) { + if (newStart < min) { + diff = (min - newStart); + newStart += diff; + newEnd += diff; - /** - * 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; + // prevent end > max + if (max != null) { + if (newEnd > max) { + newEnd = max; + } + } + } } - // specific event - var callbacks = this._callbacks[event]; - if (!callbacks) return this; + // prevent end > max + if (max !== null) { + if (newEnd > max) { + diff = (newEnd - max); + newStart -= diff; + newEnd -= diff; - // remove all handlers - if (1 == arguments.length) { - delete this._callbacks[event]; - return this; + // prevent start < min + if (min != null) { + if (newStart < min) { + newStart = min; + } + } + } } - // 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; + // prevent (end-start) < zoomMin + if (this.options.zoomMin !== null) { + var zoomMin = parseFloat(this.options.zoomMin); + if (zoomMin < 0) { + zoomMin = 0; + } + if ((newEnd - newStart) < zoomMin) { + if ((this.end - this.start) === zoomMin) { + // ignore this action, we are already zoomed to the minimum + newStart = this.start; + newEnd = this.end; + } + else { + // zoom to the minimum + diff = (zoomMin - (newEnd - newStart)); + newStart -= diff / 2; + newEnd += diff / 2; + } } } - 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); + // prevent (end-start) > zoomMax + if (this.options.zoomMax !== null) { + var zoomMax = parseFloat(this.options.zoomMax); + if (zoomMax < 0) { + zoomMax = 0; + } + if ((newEnd - newStart) > zoomMax) { + if ((this.end - this.start) === zoomMax) { + // ignore this action, we are already zoomed to the maximum + newStart = this.start; + newEnd = this.end; + } + else { + // zoom to the maximum + diff = ((newEnd - newStart) - zoomMax); + newStart += diff / 2; + newEnd -= diff / 2; + } } } - 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] || []; - }; + var changed = (this.start != newStart || this.end != newEnd); - /** - * Check if this emitter has `event` handlers. - * - * @param {String} event - * @return {Boolean} - * @api public - */ + // if the new range does NOT overlap with the old range, emit checkRangedItems to avoid not showing ranged items (ranged meaning has end time, not neccesarily of type Range) + if (!((newStart >= this.start && newStart <= this.end) || (newEnd >= this.start && newEnd <= this.end)) && + !((this.start >= newStart && this.start <= newEnd) || (this.end >= newStart && this.end <= newEnd) )) { + this.body.emitter.emit('checkRangedItems'); + } - Emitter.prototype.hasListeners = function(event){ - return !! this.listeners(event).length; + this.start = newStart; + this.end = newEnd; + return changed; }; - -/***/ }, -/* 12 */ -/***/ function(module, exports, __webpack_require__) { - /** - * @prototype Point3d - * @param {Number} [x] - * @param {Number} [y] - * @param {Number} [z] + * Retrieve the current range. + * @return {Object} An object with start and end properties */ - function Point3d(x, y, z) { - this.x = x !== undefined ? x : 0; - this.y = y !== undefined ? y : 0; - this.z = z !== undefined ? z : 0; + Range.prototype.getRange = function() { + return { + start: this.start, + end: this.end + }; }; /** - * Subtract the two provided points, returns a-b - * @param {Point3d} a - * @param {Point3d} b - * @return {Point3d} a-b + * Calculate the conversion offset and scale for current range, based on + * the provided width + * @param {Number} width + * @returns {{offset: number, scale: number}} conversion */ - Point3d.subtract = function(a, b) { - var sub = new Point3d(); - sub.x = a.x - b.x; - sub.y = a.y - b.y; - sub.z = a.z - b.z; - return sub; + Range.prototype.conversion = function (width, totalHidden) { + return Range.conversion(this.start, this.end, width, totalHidden); }; /** - * Add the two provided points, returns a+b - * @param {Point3d} a - * @param {Point3d} b - * @return {Point3d} a+b + * Static method to calculate the conversion offset and scale for a range, + * based on the provided start, end, and width + * @param {Number} start + * @param {Number} end + * @param {Number} width + * @returns {{offset: number, scale: number}} conversion */ - Point3d.add = function(a, b) { - var sum = new Point3d(); - sum.x = a.x + b.x; - sum.y = a.y + b.y; - sum.z = a.z + b.z; - return sum; + Range.conversion = function (start, end, width, totalHidden) { + if (totalHidden === undefined) { + totalHidden = 0; + } + if (width != 0 && (end - start != 0)) { + return { + offset: start, + scale: width / (end - start - totalHidden) + } + } + else { + return { + offset: 0, + scale: 1 + }; + } }; /** - * Calculate the average of two 3d points - * @param {Point3d} a - * @param {Point3d} b - * @return {Point3d} The average, (a+b)/2 + * Start dragging horizontally or vertically + * @param {Event} event + * @private */ - Point3d.avg = function(a, b) { - return new Point3d( - (a.x + b.x) / 2, - (a.y + b.y) / 2, - (a.z + b.z) / 2 - ); - }; + Range.prototype._onDragStart = function(event) { + this.deltaDifference = 0; + this.previousDelta = 0; + // only allow dragging when configured as movable + if (!this.options.moveable) return; - /** - * Calculate the cross product of the two provided points, returns axb - * Documentation: http://en.wikipedia.org/wiki/Cross_product - * @param {Point3d} a - * @param {Point3d} b - * @return {Point3d} cross product axb - */ - Point3d.crossProduct = function(a, b) { - var crossproduct = new Point3d(); + // refuse to drag when we where pinching to prevent the timeline make a jump + // when releasing the fingers in opposite order from the touch screen + if (!this.props.touch.allowDragging) return; - crossproduct.x = a.y * b.z - a.z * b.y; - crossproduct.y = a.z * b.x - a.x * b.z; - crossproduct.z = a.x * b.y - a.y * b.x; + this.props.touch.start = this.start; + this.props.touch.end = this.end; + this.props.touch.dragging = true; - return crossproduct; + if (this.body.dom.root) { + this.body.dom.root.style.cursor = 'move'; + } }; - /** - * Rtrieve the length of the vector (or the distance from this point to the origin - * @return {Number} length + * Perform dragging operation + * @param {Event} event + * @private */ - Point3d.prototype.length = function() { - return Math.sqrt( - this.x * this.x + - this.y * this.y + - this.z * this.z - ); - }; + Range.prototype._onDrag = function (event) { + // only allow dragging when configured as movable + if (!this.options.moveable) return; + // refuse to drag when we where pinching to prevent the timeline make a jump + // when releasing the fingers in opposite order from the touch screen + if (!this.props.touch.allowDragging) return; - module.exports = Point3d; + var direction = this.options.direction; + validateDirection(direction); + var delta = (direction == 'horizontal') ? event.gesture.deltaX : event.gesture.deltaY; + delta -= this.deltaDifference; + var interval = (this.props.touch.end - this.props.touch.start); -/***/ }, -/* 13 */ -/***/ function(module, exports, __webpack_require__) { + // normalize dragging speed if cutout is in between. + var duration = DateUtil.getHiddenDurationBetween(this.body.hiddenDates, this.start, this.end); + interval -= duration; - /** - * @prototype Point2d - * @param {Number} [x] - * @param {Number} [y] - */ - function Point2d (x, y) { - this.x = x !== undefined ? x : 0; - this.y = y !== undefined ? y : 0; - } + var width = (direction == 'horizontal') ? this.body.domProps.center.width : this.body.domProps.center.height; + var diffRange = -delta / width * interval; + var newStart = this.props.touch.start + diffRange; + var newEnd = this.props.touch.end + diffRange; - module.exports = Point2d; + // snapping times away from hidden zones + var safeStart = DateUtil.snapAwayFromHidden(this.body.hiddenDates, newStart, this.previousDelta-delta, true); + var safeEnd = DateUtil.snapAwayFromHidden(this.body.hiddenDates, newEnd, this.previousDelta-delta, true); + if (safeStart != newStart || safeEnd != newEnd) { + this.deltaDifference += delta; + this.props.touch.start = safeStart; + this.props.touch.end = safeEnd; + this._onDrag(event); + return; + } -/***/ }, -/* 14 */ -/***/ function(module, exports, __webpack_require__) { + this.previousDelta = delta; + this._applyRange(newStart, newEnd); - var Point3d = __webpack_require__(12); + // fire a rangechange event + this.body.emitter.emit('rangechange', { + start: new Date(this.start), + end: new Date(this.end), + byUser: true + }); + }; /** - * @class Camera - * The camera is mounted on a (virtual) camera arm. The camera arm can rotate - * The camera is always looking in the direction of the origin of the arm. - * This way, the camera always rotates around one fixed point, the location - * of the camera arm. - * - * Documentation: - * http://en.wikipedia.org/wiki/3D_projection + * Stop dragging operation + * @param {event} event + * @private */ - function Camera() { - this.armLocation = new Point3d(); - this.armRotation = {}; - this.armRotation.horizontal = 0; - this.armRotation.vertical = 0; - this.armLength = 1.7; - - this.cameraLocation = new Point3d(); - this.cameraRotation = new Point3d(0.5*Math.PI, 0, 0); + Range.prototype._onDragEnd = function (event) { + // only allow dragging when configured as movable + if (!this.options.moveable) return; - this.calculateCameraOrientation(); - } + // refuse to drag when we where pinching to prevent the timeline make a jump + // when releasing the fingers in opposite order from the touch screen + if (!this.props.touch.allowDragging) return; - /** - * Set the location (origin) of the arm - * @param {Number} x Normalized value of x - * @param {Number} y Normalized value of y - * @param {Number} z Normalized value of z - */ - Camera.prototype.setArmLocation = function(x, y, z) { - this.armLocation.x = x; - this.armLocation.y = y; - this.armLocation.z = z; + this.props.touch.dragging = false; + if (this.body.dom.root) { + this.body.dom.root.style.cursor = 'auto'; + } - this.calculateCameraOrientation(); + // fire a rangechanged event + this.body.emitter.emit('rangechanged', { + start: new Date(this.start), + end: new Date(this.end), + byUser: true + }); }; /** - * Set the rotation of the camera arm - * @param {Number} horizontal The horizontal rotation, between 0 and 2*PI. - * Optional, can be left undefined. - * @param {Number} vertical The vertical rotation, between 0 and 0.5*PI - * if vertical=0.5*PI, the graph is shown from the - * top. Optional, can be left undefined. + * Event handler for mouse wheel event, used to zoom + * Code from http://adomas.org/javascript-mouse-wheel/ + * @param {Event} event + * @private */ - Camera.prototype.setArmRotation = function(horizontal, vertical) { - if (horizontal !== undefined) { - this.armRotation.horizontal = horizontal; - } - - if (vertical !== undefined) { - this.armRotation.vertical = vertical; - if (this.armRotation.vertical < 0) this.armRotation.vertical = 0; - if (this.armRotation.vertical > 0.5*Math.PI) this.armRotation.vertical = 0.5*Math.PI; - } + Range.prototype._onMouseWheel = function(event) { + // only allow zooming when configured as zoomable and moveable + if (!(this.options.zoomable && this.options.moveable)) return; - if (horizontal !== undefined || vertical !== undefined) { - this.calculateCameraOrientation(); + // retrieve delta + var delta = 0; + if (event.wheelDelta) { /* IE/Opera. */ + delta = event.wheelDelta / 120; + } else if (event.detail) { /* Mozilla case. */ + // In Mozilla, sign of delta is different than in IE. + // Also, delta is multiple of 3. + delta = -event.detail / 3; } - }; - /** - * Retrieve the current arm rotation - * @return {object} An object with parameters horizontal and vertical - */ - Camera.prototype.getArmRotation = function() { - var rot = {}; - rot.horizontal = this.armRotation.horizontal; - rot.vertical = this.armRotation.vertical; - - return rot; - }; + // If delta is nonzero, handle it. + // Basically, delta is now positive if wheel was scrolled up, + // and negative, if wheel was scrolled down. + if (delta) { + // perform the zoom action. Delta is normally 1 or -1 - /** - * Set the (normalized) length of the camera arm. - * @param {Number} length A length between 0.71 and 5.0 - */ - Camera.prototype.setArmLength = function(length) { - if (length === undefined) - return; + // adjust a negative delta such that zooming in with delta 0.1 + // equals zooming out with a delta -0.1 + var scale; + if (delta < 0) { + scale = 1 - (delta / 5); + } + else { + scale = 1 / (1 + (delta / 5)) ; + } - this.armLength = length; + // calculate center, the date to zoom around + var gesture = hammerUtil.fakeGesture(this, event), + pointer = getPointer(gesture.center, this.body.dom.center), + pointerDate = this._pointerToDate(pointer); - // Radius must be larger than the corner of the graph, - // which has a distance of sqrt(0.5^2+0.5^2) = 0.71 from the center of the - // graph - if (this.armLength < 0.71) this.armLength = 0.71; - if (this.armLength > 5.0) this.armLength = 5.0; + this.zoom(scale, pointerDate, delta); + } - this.calculateCameraOrientation(); + // Prevent default actions caused by mouse wheel + // (else the page and timeline both zoom and scroll) + event.preventDefault(); }; /** - * Retrieve the arm length - * @return {Number} length + * Start of a touch gesture + * @private */ - Camera.prototype.getArmLength = function() { - return this.armLength; + Range.prototype._onTouch = function (event) { + this.props.touch.start = this.start; + this.props.touch.end = this.end; + this.props.touch.allowDragging = true; + this.props.touch.center = null; + this.scaleOffset = 0; + this.deltaDifference = 0; }; /** - * Retrieve the camera location - * @return {Point3d} cameraLocation + * On start of a hold gesture + * @private */ - Camera.prototype.getCameraLocation = function() { - return this.cameraLocation; + Range.prototype._onHold = function () { + this.props.touch.allowDragging = false; }; /** - * Retrieve the camera rotation - * @return {Point3d} cameraRotation + * Handle pinch event + * @param {Event} event + * @private */ - Camera.prototype.getCameraRotation = function() { - return this.cameraRotation; - }; + Range.prototype._onPinch = function (event) { + // only allow zooming when configured as zoomable and moveable + if (!(this.options.zoomable && this.options.moveable)) return; - /** - * Calculate the location and rotation of the camera based on the - * position and orientation of the camera arm - */ - Camera.prototype.calculateCameraOrientation = function() { - // calculate location of the camera - this.cameraLocation.x = this.armLocation.x - this.armLength * Math.sin(this.armRotation.horizontal) * Math.cos(this.armRotation.vertical); - this.cameraLocation.y = this.armLocation.y - this.armLength * Math.cos(this.armRotation.horizontal) * Math.cos(this.armRotation.vertical); - this.cameraLocation.z = this.armLocation.z + this.armLength * Math.sin(this.armRotation.vertical); + this.props.touch.allowDragging = false; - // calculate rotation of the camera - this.cameraRotation.x = Math.PI/2 - this.armRotation.vertical; - this.cameraRotation.y = 0; - this.cameraRotation.z = -this.armRotation.horizontal; - }; + if (event.gesture.touches.length > 1) { + if (!this.props.touch.center) { + this.props.touch.center = getPointer(event.gesture.center, this.body.dom.center); + } - module.exports = Camera; + var scale = 1 / (event.gesture.scale + this.scaleOffset); + var centerDate = this._pointerToDate(this.props.touch.center); -/***/ }, -/* 15 */ -/***/ function(module, exports, __webpack_require__) { + var hiddenDuration = DateUtil.getHiddenDurationBetween(this.body.hiddenDates, this.start, this.end); + var hiddenDurationBefore = DateUtil.getHiddenDurationBefore(this.body.hiddenDates, this, centerDate); + var hiddenDurationAfter = hiddenDuration - hiddenDurationBefore; - var DataView = __webpack_require__(9); + // calculate new start and end + var newStart = (centerDate - hiddenDurationBefore) + (this.props.touch.start - (centerDate - hiddenDurationBefore)) * scale; + var newEnd = (centerDate + hiddenDurationAfter) + (this.props.touch.end - (centerDate + hiddenDurationAfter)) * scale; - /** - * @class Filter - * - * @param {DataSet} data The google data table - * @param {Number} column The index of the column to be filtered - * @param {Graph} graph The graph - */ - function Filter (data, column, graph) { - this.data = data; - this.column = column; - this.graph = graph; // the parent graph - - this.index = undefined; - this.value = undefined; + // snapping times away from hidden zones + this.startToFront = 1 - scale > 0 ? false : true; // used to do the right autocorrection with periodic hidden times + this.endToFront = scale - 1 > 0 ? false : true; // used to do the right autocorrection with periodic hidden times - // read all distinct values and select the first one - this.values = graph.getDistinctValues(data.get(), this.column); + var safeStart = DateUtil.snapAwayFromHidden(this.body.hiddenDates, newStart, 1 - scale, true); + var safeEnd = DateUtil.snapAwayFromHidden(this.body.hiddenDates, newEnd, scale - 1, true); + if (safeStart != newStart || safeEnd != newEnd) { + this.props.touch.start = safeStart; + this.props.touch.end = safeEnd; + this.scaleOffset = 1 - event.gesture.scale; + newStart = safeStart; + newEnd = safeEnd; + } - // sort both numeric and string values correctly - this.values.sort(function (a, b) { - return a > b ? 1 : a < b ? -1 : 0; - }); + this.setRange(newStart, newEnd, false, true); - if (this.values.length > 0) { - this.selectValue(0); + this.startToFront = false; // revert to default + this.endToFront = true; // revert to default } + }; - // create an array with the filtered datapoints. this will be loaded afterwards - this.dataPoints = []; + /** + * Helper function to calculate the center date for zooming + * @param {{x: Number, y: Number}} pointer + * @return {number} date + * @private + */ + Range.prototype._pointerToDate = function (pointer) { + var conversion; + var direction = this.options.direction; - this.loaded = false; - this.onLoadCallback = undefined; + validateDirection(direction); - if (graph.animationPreload) { - this.loaded = false; - this.loadInBackground(); + if (direction == 'horizontal') { + return this.body.util.toTime(pointer.x).valueOf(); } else { - this.loaded = true; + var height = this.body.domProps.center.height; + conversion = this.conversion(height); + return pointer.y / conversion.scale + conversion.offset; } }; - /** - * Return the label - * @return {string} label + * Get the pointer location relative to the location of the dom element + * @param {{pageX: Number, pageY: Number}} touch + * @param {Element} element HTML DOM element + * @return {{x: Number, y: Number}} pointer + * @private */ - Filter.prototype.isLoaded = function() { - return this.loaded; - }; - + function getPointer (touch, element) { + return { + x: touch.pageX - util.getAbsoluteLeft(element), + y: touch.pageY - util.getAbsoluteTop(element) + }; + } /** - * Return the loaded progress - * @return {Number} percentage between 0 and 100 + * Zoom the range the given scale in or out. Start and end date will + * be adjusted, and the timeline will be redrawn. You can optionally give a + * date around which to zoom. + * For example, try scale = 0.9 or 1.1 + * @param {Number} scale Scaling factor. Values above 1 will zoom out, + * values below 1 will zoom in. + * @param {Number} [center] Value representing a date around which will + * be zoomed. */ - Filter.prototype.getLoadedProgress = function() { - var len = this.values.length; - - var i = 0; - while (this.dataPoints[i]) { - i++; + Range.prototype.zoom = function(scale, center, delta) { + // if centerDate is not provided, take it half between start Date and end Date + if (center == null) { + center = (this.start + this.end) / 2; } - return Math.round(i / len * 100); - }; + var hiddenDuration = DateUtil.getHiddenDurationBetween(this.body.hiddenDates, this.start, this.end); + var hiddenDurationBefore = DateUtil.getHiddenDurationBefore(this.body.hiddenDates, this, center); + var hiddenDurationAfter = hiddenDuration - hiddenDurationBefore; + // calculate new start and end + var newStart = (center-hiddenDurationBefore) + (this.start - (center-hiddenDurationBefore)) * scale; + var newEnd = (center+hiddenDurationAfter) + (this.end - (center+hiddenDurationAfter)) * scale; - /** - * Return the label - * @return {string} label - */ - Filter.prototype.getLabel = function() { - return this.graph.filterLabel; - }; + // snapping times away from hidden zones + this.startToFront = delta > 0 ? false : true; // used to do the right autocorrection with periodic hidden times + this.endToFront = -delta > 0 ? false : true; // used to do the right autocorrection with periodic hidden times + var safeStart = DateUtil.snapAwayFromHidden(this.body.hiddenDates, newStart, delta, true); + var safeEnd = DateUtil.snapAwayFromHidden(this.body.hiddenDates, newEnd, -delta, true); + if (safeStart != newStart || safeEnd != newEnd) { + newStart = safeStart; + newEnd = safeEnd; + } + this.setRange(newStart, newEnd, false, true); - /** - * Return the columnIndex of the filter - * @return {Number} columnIndex - */ - Filter.prototype.getColumn = function() { - return this.column; + this.startToFront = false; // revert to default + this.endToFront = true; // revert to default }; - /** - * Return the currently selected value. Returns undefined if there is no selection - * @return {*} value - */ - Filter.prototype.getSelectedValue = function() { - if (this.index === undefined) - return undefined; - return this.values[this.index]; - }; /** - * Retrieve all values of the filter - * @return {Array} values + * Move the range with a given delta to the left or right. Start and end + * value will be adjusted. For example, try delta = 0.1 or -0.1 + * @param {Number} delta Moving amount. Positive value will move right, + * negative value will move left */ - Filter.prototype.getValues = function() { - return this.values; - }; + Range.prototype.move = function(delta) { + // zoom start Date and end Date relative to the centerDate + var diff = (this.end - this.start); - /** - * Retrieve one value of the filter - * @param {Number} index - * @return {*} value - */ - Filter.prototype.getValue = function(index) { - if (index >= this.values.length) - throw 'Error: index out of range'; + // apply new values + var newStart = this.start + diff * delta; + var newEnd = this.end + diff * delta; - return this.values[index]; - }; + // TODO: reckon with min and max range + this.start = newStart; + this.end = newEnd; + }; /** - * Retrieve the (filtered) dataPoints for the currently selected filter index - * @param {Number} [index] (optional) - * @return {Array} dataPoints + * Move the range to a new center point + * @param {Number} moveTo New center point of the range */ - Filter.prototype._getDataPoints = function(index) { - if (index === undefined) - index = this.index; + Range.prototype.moveTo = function(moveTo) { + var center = (this.start + this.end) / 2; - if (index === undefined) - return []; + var diff = center - moveTo; - var dataPoints; - if (this.dataPoints[index]) { - dataPoints = this.dataPoints[index]; - } - else { - var f = {}; - f.column = this.column; - f.value = this.values[index]; + // calculate new start and end + var newStart = this.start - diff; + var newEnd = this.end - diff; - var dataView = new DataView(this.data,{filter: function (item) {return (item[f.column] == f.value);}}).get(); - dataPoints = this.graph._getDataPoints(dataView); + this.setRange(newStart, newEnd); + }; - this.dataPoints[index] = dataPoints; - } + module.exports = Range; - return dataPoints; - }; +/***/ }, +/* 18 */ +/***/ function(module, exports, __webpack_require__) { + // Utility functions for ordering and stacking of items + var EPSILON = 0.001; // used when checking collisions, to prevent round-off errors /** - * Set a callback function when the filter is fully loaded. + * Order items by their start data + * @param {Item[]} items */ - Filter.prototype.setOnLoadCallback = function(callback) { - this.onLoadCallback = callback; + exports.orderByStart = function(items) { + items.sort(function (a, b) { + return a.data.start - b.data.start; + }); }; - /** - * Add a value to the list with available values for this filter - * No double entries will be created. - * @param {Number} index + * Order items by their end date. If they have no end date, their start date + * is used. + * @param {Item[]} items */ - Filter.prototype.selectValue = function(index) { - if (index >= this.values.length) - throw 'Error: index out of range'; + exports.orderByEnd = function(items) { + items.sort(function (a, b) { + var aTime = ('end' in a.data) ? a.data.end : a.data.start, + bTime = ('end' in b.data) ? b.data.end : b.data.start; - this.index = index; - this.value = this.values[index]; + return aTime - bTime; + }); }; /** - * Load all filtered rows in the background one by one - * Start this method without providing an index! + * Adjust vertical positions of the items such that they don't overlap each + * other. + * @param {Item[]} items + * All visible items + * @param {{item: {horizontal: number, vertical: number}, axis: number}} margin + * Margins between items and between items and the axis. + * @param {boolean} [force=false] + * If true, all items will be repositioned. If false (default), only + * items having a top===null will be re-stacked */ - Filter.prototype.loadInBackground = function(index) { - if (index === undefined) - index = 0; + exports.stack = function(items, margin, force) { + var i, iMax; - var frame = this.graph.frame; + if (force) { + // reset top position of all items + for (i = 0, iMax = items.length; i < iMax; i++) { + items[i].top = null; + } + } - if (index < this.values.length) { - var dataPointsTemp = this._getDataPoints(index); - //this.graph.redrawInfo(); // TODO: not neat + // calculate new, non-overlapping positions + for (i = 0, iMax = items.length; i < iMax; i++) { + var item = items[i]; + if (item.stack && item.top === null) { + // initialize top position + item.top = margin.axis; - // create a progress box - if (frame.progress === undefined) { - frame.progress = document.createElement('DIV'); - frame.progress.style.position = 'absolute'; - frame.progress.style.color = 'gray'; - frame.appendChild(frame.progress); - } - var progress = this.getLoadedProgress(); - frame.progress.innerHTML = 'Loading animation... ' + progress + '%'; - // TODO: this is no nice solution... - frame.progress.style.bottom = 60 + 'px'; // TODO: use height of slider - frame.progress.style.left = 10 + 'px'; + do { + // TODO: optimize checking for overlap. when there is a gap without items, + // you only need to check for items from the next item on, not from zero + var collidingItem = null; + for (var j = 0, jj = items.length; j < jj; j++) { + var other = items[j]; + if (other.top !== null && other !== item && other.stack && exports.collision(item, other, margin.item)) { + collidingItem = other; + break; + } + } - var me = this; - setTimeout(function() {me.loadInBackground(index+1);}, 10); - this.loaded = false; + if (collidingItem != null) { + // There is a collision. Reposition the items above the colliding element + item.top = collidingItem.top + collidingItem.height + margin.item.vertical; + } + } while (collidingItem); + } } - else { - this.loaded = true; + }; - // remove the progress box - if (frame.progress !== undefined) { - frame.removeChild(frame.progress); - frame.progress = undefined; - } - if (this.onLoadCallback) - this.onLoadCallback(); + /** + * Adjust vertical positions of the items without stacking them + * @param {Item[]} items + * All visible items + * @param {{item: {horizontal: number, vertical: number}, axis: number}} margin + * Margins between items and between items and the axis. + */ + exports.nostack = function(items, margin, subgroups) { + var i, iMax, newTop; + + // reset top position of all items + for (i = 0, iMax = items.length; i < iMax; i++) { + if (items[i].data.subgroup !== undefined) { + newTop = margin.axis; + for (var subgroup in subgroups) { + if (subgroups.hasOwnProperty(subgroup)) { + if (subgroups[subgroup].visible == true && subgroups[subgroup].index < subgroups[items[i].data.subgroup].index) { + newTop += subgroups[subgroup].height + margin.item.vertical; + } + } + } + items[i].top = newTop; + } + else { + items[i].top = margin.axis; + } } }; - module.exports = Filter; + /** + * Test if the two provided items collide + * The items must have parameters left, width, top, and height. + * @param {Item} a The first item + * @param {Item} b The second item + * @param {{horizontal: number, vertical: number}} margin + * An object containing a horizontal and vertical + * minimum required margin. + * @return {boolean} true if a and b collide, else false + */ + exports.collision = function(a, b, margin) { + return ((a.left - margin.horizontal + EPSILON) < (b.left + b.width) && + (a.left + a.width + margin.horizontal - EPSILON) > b.left && + (a.top - margin.vertical + EPSILON) < (b.top + b.height) && + (a.top + a.height + margin.vertical - EPSILON) > b.top); + }; /***/ }, -/* 16 */ +/* 19 */ /***/ function(module, exports, __webpack_require__) { + var moment = __webpack_require__(44); + var DateUtil = __webpack_require__(15); var util = __webpack_require__(1); /** - * @constructor Slider + * @constructor TimeStep + * The class TimeStep is an iterator for dates. You provide a start date and an + * end date. The class itself determines the best scale (step size) based on the + * provided start Date, end Date, and minimumStep. * - * An html slider control with start/stop/prev/next buttons - * @param {Element} container The element where the slider will be created - * @param {Object} options Available options: - * {boolean} visible If true (default) the - * slider is visible. + * If minimumStep is provided, the step size is chosen as close as possible + * to the minimumStep but larger than minimumStep. If minimumStep is not + * provided, the scale is set to 1 DAY. + * The minimumStep should correspond with the onscreen size of about 6 characters + * + * Alternatively, you can set a scale by hand. + * After creation, you can initialize the class by executing first(). Then you + * can iterate from the start date to the end date via next(). You can check if + * the end date is reached with the function hasNext(). After each step, you can + * retrieve the current date via getCurrent(). + * The TimeStep has scales ranging from milliseconds, seconds, minutes, hours, + * days, to years. + * + * Version: 1.2 + * + * @param {Date} [start] The start date, for example new Date(2010, 9, 21) + * or new Date(2010, 9, 21, 23, 45, 00) + * @param {Date} [end] The end date + * @param {Number} [minimumStep] Optional. Minimum step size in milliseconds */ - function Slider(container, options) { - if (container === undefined) { - throw 'Error: No container element defined'; - } - this.container = container; - this.visible = (options && options.visible != undefined) ? options.visible : true; - - if (this.visible) { - this.frame = document.createElement('DIV'); - //this.frame.style.backgroundColor = '#E5E5E5'; - this.frame.style.width = '100%'; - this.frame.style.position = 'relative'; - this.container.appendChild(this.frame); - - this.frame.prev = document.createElement('INPUT'); - this.frame.prev.type = 'BUTTON'; - this.frame.prev.value = 'Prev'; - this.frame.appendChild(this.frame.prev); - - this.frame.play = document.createElement('INPUT'); - this.frame.play.type = 'BUTTON'; - this.frame.play.value = 'Play'; - this.frame.appendChild(this.frame.play); - - this.frame.next = document.createElement('INPUT'); - this.frame.next.type = 'BUTTON'; - this.frame.next.value = 'Next'; - this.frame.appendChild(this.frame.next); + function TimeStep(start, end, minimumStep, hiddenDates) { + // variables + this.current = new Date(); + this._start = new Date(); + this._end = new Date(); - this.frame.bar = document.createElement('INPUT'); - this.frame.bar.type = 'BUTTON'; - this.frame.bar.style.position = 'absolute'; - this.frame.bar.style.border = '1px solid red'; - this.frame.bar.style.width = '100px'; - this.frame.bar.style.height = '6px'; - this.frame.bar.style.borderRadius = '2px'; - this.frame.bar.style.MozBorderRadius = '2px'; - this.frame.bar.style.border = '1px solid #7F7F7F'; - this.frame.bar.style.backgroundColor = '#E5E5E5'; - this.frame.appendChild(this.frame.bar); + this.autoScale = true; + this.scale = 'day'; + this.step = 1; - this.frame.slide = document.createElement('INPUT'); - this.frame.slide.type = 'BUTTON'; - this.frame.slide.style.margin = '0px'; - this.frame.slide.value = ' '; - this.frame.slide.style.position = 'relative'; - this.frame.slide.style.left = '-100px'; - this.frame.appendChild(this.frame.slide); + // initialize the range + this.setRange(start, end, minimumStep); - // create events - var me = this; - this.frame.slide.onmousedown = function (event) {me._onMouseDown(event);}; - this.frame.prev.onclick = function (event) {me.prev(event);}; - this.frame.play.onclick = function (event) {me.togglePlay(event);}; - this.frame.next.onclick = function (event) {me.next(event);}; + // hidden Dates options + this.switchedDay = false; + this.switchedMonth = false; + this.switchedYear = false; + this.hiddenDates = hiddenDates; + if (hiddenDates === undefined) { + this.hiddenDates = []; } - this.onChangeCallback = undefined; - - this.values = []; - this.index = undefined; - - this.playTimeout = undefined; - this.playInterval = 1000; // milliseconds - this.playLoop = true; + this.format = TimeStep.FORMAT; // default formatting } - /** - * Select the previous index - */ - Slider.prototype.prev = function() { - var index = this.getIndex(); - if (index > 0) { - index--; - this.setIndex(index); + // Time formatting + TimeStep.FORMAT = { + minorLabels: { + millisecond:'SSS', + second: 's', + minute: 'HH:mm', + hour: 'HH:mm', + weekday: 'ddd D', + day: 'D', + month: 'MMM', + year: 'YYYY' + }, + majorLabels: { + millisecond:'HH:mm:ss', + second: 'D MMMM HH:mm', + minute: 'ddd D MMMM', + hour: 'ddd D MMMM', + weekday: 'MMMM YYYY', + day: 'MMMM YYYY', + month: 'YYYY', + year: '' } }; /** - * Select the next index + * Set custom formatting for the minor an major labels of the TimeStep. + * Both `minorLabels` and `majorLabels` are an Object with properties: + * 'millisecond, 'second, 'minute', 'hour', 'weekday, 'day, 'month, 'year'. + * @param {{minorLabels: Object, majorLabels: Object}} format */ - Slider.prototype.next = function() { - var index = this.getIndex(); - if (index < this.values.length - 1) { - index++; - this.setIndex(index); - } + TimeStep.prototype.setFormat = function (format) { + var defaultFormat = util.deepExtend({}, TimeStep.FORMAT); + this.format = util.deepExtend(defaultFormat, format); }; /** - * Select the next index + * Set a new range + * If minimumStep is provided, the step size is chosen as close as possible + * to the minimumStep but larger than minimumStep. If minimumStep is not + * provided, the scale is set to 1 DAY. + * The minimumStep should correspond with the onscreen size of about 6 characters + * @param {Date} [start] The start date and time. + * @param {Date} [end] The end date and time. + * @param {int} [minimumStep] Optional. Minimum step size in milliseconds */ - Slider.prototype.playNext = function() { - var start = new Date(); - - var index = this.getIndex(); - if (index < this.values.length - 1) { - index++; - this.setIndex(index); - } - else if (this.playLoop) { - // jump to the start - index = 0; - this.setIndex(index); + TimeStep.prototype.setRange = function(start, end, minimumStep) { + if (!(start instanceof Date) || !(end instanceof Date)) { + throw "No legal start or end date in method setRange"; } - var end = new Date(); - var diff = (end - start); - - // calculate how much time it to to set the index and to execute the callback - // function. - var interval = Math.max(this.playInterval - diff, 0); - // document.title = diff // TODO: cleanup + this._start = (start != undefined) ? new Date(start.valueOf()) : new Date(); + this._end = (end != undefined) ? new Date(end.valueOf()) : new Date(); - var me = this; - this.playTimeout = setTimeout(function() {me.playNext();}, interval); + if (this.autoScale) { + this.setMinimumStep(minimumStep); + } }; /** - * Toggle start or stop playing + * Set the range iterator to the start date. */ - Slider.prototype.togglePlay = function() { - if (this.playTimeout === undefined) { - this.play(); - } else { - this.stop(); - } + TimeStep.prototype.first = function() { + this.current = new Date(this._start.valueOf()); + this.roundToMinor(); }; /** - * Start playing + * Round the current date to the first minor date value + * This must be executed once when the current date is set to start Date */ - Slider.prototype.play = function() { - // Test whether already playing - if (this.playTimeout) return; - - this.playNext(); + TimeStep.prototype.roundToMinor = function() { + // round to floor + // IMPORTANT: we have no breaks in this switch! (this is no bug) + // noinspection FallThroughInSwitchStatementJS + switch (this.scale) { + case 'year': + this.current.setFullYear(this.step * Math.floor(this.current.getFullYear() / this.step)); + this.current.setMonth(0); + case 'month': this.current.setDate(1); + case 'day': // intentional fall through + case 'weekday': this.current.setHours(0); + case 'hour': this.current.setMinutes(0); + case 'minute': this.current.setSeconds(0); + case 'second': this.current.setMilliseconds(0); + //case 'millisecond': // nothing to do for milliseconds + } - if (this.frame) { - this.frame.play.value = 'Stop'; + if (this.step != 1) { + // round down to the first minor value that is a multiple of the current step size + switch (this.scale) { + case 'millisecond': this.current.setMilliseconds(this.current.getMilliseconds() - this.current.getMilliseconds() % this.step); break; + case 'second': this.current.setSeconds(this.current.getSeconds() - this.current.getSeconds() % this.step); break; + case 'minute': this.current.setMinutes(this.current.getMinutes() - this.current.getMinutes() % this.step); break; + case 'hour': this.current.setHours(this.current.getHours() - this.current.getHours() % this.step); break; + case 'weekday': // intentional fall through + case 'day': this.current.setDate((this.current.getDate()-1) - (this.current.getDate()-1) % this.step + 1); break; + case 'month': this.current.setMonth(this.current.getMonth() - this.current.getMonth() % this.step); break; + case 'year': this.current.setFullYear(this.current.getFullYear() - this.current.getFullYear() % this.step); break; + default: break; + } } }; /** - * Stop playing + * Check if the there is a next step + * @return {boolean} true if the current date has not passed the end date */ - Slider.prototype.stop = function() { - clearInterval(this.playTimeout); - this.playTimeout = undefined; - - if (this.frame) { - this.frame.play.value = 'Play'; - } + TimeStep.prototype.hasNext = function () { + return (this.current.valueOf() <= this._end.valueOf()); }; /** - * Set a callback function which will be triggered when the value of the - * slider bar has changed. + * Do the next step */ - Slider.prototype.setOnChangeCallback = function(callback) { - this.onChangeCallback = callback; + TimeStep.prototype.next = function() { + var prev = this.current.valueOf(); + + // Two cases, needed to prevent issues with switching daylight savings + // (end of March and end of October) + if (this.current.getMonth() < 6) { + switch (this.scale) { + case 'millisecond': + + this.current = new Date(this.current.valueOf() + this.step); break; + case 'second': this.current = new Date(this.current.valueOf() + this.step * 1000); break; + case 'minute': this.current = new Date(this.current.valueOf() + this.step * 1000 * 60); break; + case 'hour': + this.current = new Date(this.current.valueOf() + this.step * 1000 * 60 * 60); + // in case of skipping an hour for daylight savings, adjust the hour again (else you get: 0h 5h 9h ... instead of 0h 4h 8h ...) + var h = this.current.getHours(); + this.current.setHours(h - (h % this.step)); + break; + case 'weekday': // intentional fall through + case 'day': this.current.setDate(this.current.getDate() + this.step); break; + case 'month': this.current.setMonth(this.current.getMonth() + this.step); break; + case 'year': this.current.setFullYear(this.current.getFullYear() + this.step); break; + default: break; + } + } + else { + switch (this.scale) { + case 'millisecond': this.current = new Date(this.current.valueOf() + this.step); break; + case 'second': this.current.setSeconds(this.current.getSeconds() + this.step); break; + case 'minute': this.current.setMinutes(this.current.getMinutes() + this.step); break; + case 'hour': this.current.setHours(this.current.getHours() + this.step); break; + case 'weekday': // intentional fall through + case 'day': this.current.setDate(this.current.getDate() + this.step); break; + case 'month': this.current.setMonth(this.current.getMonth() + this.step); break; + case 'year': this.current.setFullYear(this.current.getFullYear() + this.step); break; + default: break; + } + } + + if (this.step != 1) { + // round down to the correct major value + switch (this.scale) { + case 'millisecond': if(this.current.getMilliseconds() < this.step) this.current.setMilliseconds(0); break; + case 'second': if(this.current.getSeconds() < this.step) this.current.setSeconds(0); break; + case 'minute': if(this.current.getMinutes() < this.step) this.current.setMinutes(0); break; + case 'hour': if(this.current.getHours() < this.step) this.current.setHours(0); break; + case 'weekday': // intentional fall through + case 'day': if(this.current.getDate() < this.step+1) this.current.setDate(1); break; + case 'month': if(this.current.getMonth() < this.step) this.current.setMonth(0); break; + case 'year': break; // nothing to do for year + default: break; + } + } + + // safety mechanism: if current time is still unchanged, move to the end + if (this.current.valueOf() == prev) { + this.current = new Date(this._end.valueOf()); + } + + DateUtil.stepOverHiddenDates(this, prev); }; + /** - * Set the interval for playing the list - * @param {Number} interval The interval in milliseconds + * Get the current datetime + * @return {Date} current The current date */ - Slider.prototype.setPlayInterval = function(interval) { - this.playInterval = interval; + TimeStep.prototype.getCurrent = function() { + return this.current; }; /** - * Retrieve the current play interval - * @return {Number} interval The interval in milliseconds + * Set a custom scale. Autoscaling will be disabled. + * For example setScale('minute', 5) will result + * in minor steps of 5 minutes, and major steps of an hour. + * + * @param {{scale: string, step: number}} params + * An object containing two properties: + * - A string 'scale'. Choose from 'millisecond', 'second', + * 'minute', 'hour', 'weekday, 'day, 'month, 'year'. + * - A number 'step'. A step size, by default 1. + * Choose for example 1, 2, 5, or 10. */ - Slider.prototype.getPlayInterval = function(interval) { - return this.playInterval; + TimeStep.prototype.setScale = function(params) { + if (params && typeof params.scale == 'string') { + this.scale = params.scale; + this.step = params.step > 0 ? params.step : 1; + this.autoScale = false; + } }; /** - * Set looping on or off - * @pararm {boolean} doLoop If true, the slider will jump to the start when - * the end is passed, and will jump to the end - * when the start is passed. + * Enable or disable autoscaling + * @param {boolean} enable If true, autoascaling is set true */ - Slider.prototype.setPlayLoop = function(doLoop) { - this.playLoop = doLoop; + TimeStep.prototype.setAutoScale = function (enable) { + this.autoScale = enable; }; /** - * Execute the onchange callback function + * Automatically determine the scale that bests fits the provided minimum step + * @param {Number} [minimumStep] The minimum step size in milliseconds */ - Slider.prototype.onChange = function() { - if (this.onChangeCallback !== undefined) { - this.onChangeCallback(); + TimeStep.prototype.setMinimumStep = function(minimumStep) { + if (minimumStep == undefined) { + return; } - }; - /** - * redraw the slider on the correct place - */ - Slider.prototype.redraw = function() { - if (this.frame) { - // resize the bar - this.frame.bar.style.top = (this.frame.clientHeight/2 - - this.frame.bar.offsetHeight/2) + 'px'; - this.frame.bar.style.width = (this.frame.clientWidth - - this.frame.prev.clientWidth - - this.frame.play.clientWidth - - this.frame.next.clientWidth - 30) + 'px'; + //var b = asc + ds; - // position the slider button - var left = this.indexToLeft(this.index); - this.frame.slide.style.left = (left) + 'px'; - } - }; + var stepYear = (1000 * 60 * 60 * 24 * 30 * 12); + var stepMonth = (1000 * 60 * 60 * 24 * 30); + var stepDay = (1000 * 60 * 60 * 24); + var stepHour = (1000 * 60 * 60); + var stepMinute = (1000 * 60); + var stepSecond = (1000); + var stepMillisecond= (1); + // find the smallest step that is larger than the provided minimumStep + if (stepYear*1000 > minimumStep) {this.scale = 'year'; this.step = 1000;} + if (stepYear*500 > minimumStep) {this.scale = 'year'; this.step = 500;} + if (stepYear*100 > minimumStep) {this.scale = 'year'; this.step = 100;} + if (stepYear*50 > minimumStep) {this.scale = 'year'; this.step = 50;} + if (stepYear*10 > minimumStep) {this.scale = 'year'; this.step = 10;} + if (stepYear*5 > minimumStep) {this.scale = 'year'; this.step = 5;} + if (stepYear > minimumStep) {this.scale = 'year'; this.step = 1;} + if (stepMonth*3 > minimumStep) {this.scale = 'month'; this.step = 3;} + if (stepMonth > minimumStep) {this.scale = 'month'; this.step = 1;} + if (stepDay*5 > minimumStep) {this.scale = 'day'; this.step = 5;} + if (stepDay*2 > minimumStep) {this.scale = 'day'; this.step = 2;} + if (stepDay > minimumStep) {this.scale = 'day'; this.step = 1;} + if (stepDay/2 > minimumStep) {this.scale = 'weekday'; this.step = 1;} + if (stepHour*4 > minimumStep) {this.scale = 'hour'; this.step = 4;} + if (stepHour > minimumStep) {this.scale = 'hour'; this.step = 1;} + if (stepMinute*15 > minimumStep) {this.scale = 'minute'; this.step = 15;} + if (stepMinute*10 > minimumStep) {this.scale = 'minute'; this.step = 10;} + if (stepMinute*5 > minimumStep) {this.scale = 'minute'; this.step = 5;} + if (stepMinute > minimumStep) {this.scale = 'minute'; this.step = 1;} + if (stepSecond*15 > minimumStep) {this.scale = 'second'; this.step = 15;} + if (stepSecond*10 > minimumStep) {this.scale = 'second'; this.step = 10;} + if (stepSecond*5 > minimumStep) {this.scale = 'second'; this.step = 5;} + if (stepSecond > minimumStep) {this.scale = 'second'; this.step = 1;} + if (stepMillisecond*200 > minimumStep) {this.scale = 'millisecond'; this.step = 200;} + if (stepMillisecond*100 > minimumStep) {this.scale = 'millisecond'; this.step = 100;} + if (stepMillisecond*50 > minimumStep) {this.scale = 'millisecond'; this.step = 50;} + if (stepMillisecond*10 > minimumStep) {this.scale = 'millisecond'; this.step = 10;} + if (stepMillisecond*5 > minimumStep) {this.scale = 'millisecond'; this.step = 5;} + if (stepMillisecond > minimumStep) {this.scale = 'millisecond'; this.step = 1;} + }; /** - * Set the list with values for the slider - * @param {Array} values A javascript array with values (any type) + * Snap a date to a rounded value. + * The snap intervals are dependent on the current scale and step. + * @param {Date} date the date to be snapped. + * @return {Date} snappedDate */ - Slider.prototype.setValues = function(values) { - this.values = values; + TimeStep.prototype.snap = function(date) { + var clone = new Date(date.valueOf()); - if (this.values.length > 0) - this.setIndex(0); - else - this.index = undefined; + if (this.scale == 'year') { + var year = clone.getFullYear() + Math.round(clone.getMonth() / 12); + clone.setFullYear(Math.round(year / this.step) * this.step); + clone.setMonth(0); + clone.setDate(0); + clone.setHours(0); + clone.setMinutes(0); + clone.setSeconds(0); + clone.setMilliseconds(0); + } + else if (this.scale == 'month') { + if (clone.getDate() > 15) { + clone.setDate(1); + clone.setMonth(clone.getMonth() + 1); + // important: first set Date to 1, after that change the month. + } + else { + clone.setDate(1); + } + + clone.setHours(0); + clone.setMinutes(0); + clone.setSeconds(0); + clone.setMilliseconds(0); + } + else if (this.scale == 'day') { + //noinspection FallthroughInSwitchStatementJS + switch (this.step) { + case 5: + case 2: + clone.setHours(Math.round(clone.getHours() / 24) * 24); break; + default: + clone.setHours(Math.round(clone.getHours() / 12) * 12); break; + } + clone.setMinutes(0); + clone.setSeconds(0); + clone.setMilliseconds(0); + } + else if (this.scale == 'weekday') { + //noinspection FallthroughInSwitchStatementJS + switch (this.step) { + case 5: + case 2: + clone.setHours(Math.round(clone.getHours() / 12) * 12); break; + default: + clone.setHours(Math.round(clone.getHours() / 6) * 6); break; + } + clone.setMinutes(0); + clone.setSeconds(0); + clone.setMilliseconds(0); + } + else if (this.scale == 'hour') { + switch (this.step) { + case 4: + clone.setMinutes(Math.round(clone.getMinutes() / 60) * 60); break; + default: + clone.setMinutes(Math.round(clone.getMinutes() / 30) * 30); break; + } + clone.setSeconds(0); + clone.setMilliseconds(0); + } else if (this.scale == 'minute') { + //noinspection FallthroughInSwitchStatementJS + switch (this.step) { + case 15: + case 10: + clone.setMinutes(Math.round(clone.getMinutes() / 5) * 5); + clone.setSeconds(0); + break; + case 5: + clone.setSeconds(Math.round(clone.getSeconds() / 60) * 60); break; + default: + clone.setSeconds(Math.round(clone.getSeconds() / 30) * 30); break; + } + clone.setMilliseconds(0); + } + else if (this.scale == 'second') { + //noinspection FallthroughInSwitchStatementJS + switch (this.step) { + case 15: + case 10: + clone.setSeconds(Math.round(clone.getSeconds() / 5) * 5); + clone.setMilliseconds(0); + break; + case 5: + clone.setMilliseconds(Math.round(clone.getMilliseconds() / 1000) * 1000); break; + default: + clone.setMilliseconds(Math.round(clone.getMilliseconds() / 500) * 500); break; + } + } + else if (this.scale == 'millisecond') { + var step = this.step > 5 ? this.step / 2 : 1; + clone.setMilliseconds(Math.round(clone.getMilliseconds() / step) * step); + } + + return clone; }; /** - * Select a value by its index - * @param {Number} index + * Check if the current value is a major value (for example when the step + * is DAY, a major value is each first day of the MONTH) + * @return {boolean} true if current date is major, else false. */ - Slider.prototype.setIndex = function(index) { - if (index < this.values.length) { - this.index = index; - - this.redraw(); - this.onChange(); + TimeStep.prototype.isMajor = function() { + if (this.switchedYear == true) { + this.switchedYear = false; + switch (this.scale) { + case 'year': + case 'month': + case 'weekday': + case 'day': + case 'hour': + case 'minute': + case 'second': + case 'millisecond': + return true; + default: + return false; + } } - else { - throw 'Error: index out of range'; + else if (this.switchedMonth == true) { + this.switchedMonth = false; + switch (this.scale) { + case 'weekday': + case 'day': + case 'hour': + case 'minute': + case 'second': + case 'millisecond': + return true; + default: + return false; + } + } + else if (this.switchedDay == true) { + this.switchedDay = false; + switch (this.scale) { + case 'millisecond': + case 'second': + case 'minute': + case 'hour': + return true; + default: + return false; + } + } + + switch (this.scale) { + case 'millisecond': + return (this.current.getMilliseconds() == 0); + case 'second': + return (this.current.getSeconds() == 0); + case 'minute': + return (this.current.getHours() == 0) && (this.current.getMinutes() == 0); + case 'hour': + return (this.current.getHours() == 0); + case 'weekday': // intentional fall through + case 'day': + return (this.current.getDate() == 1); + case 'month': + return (this.current.getMonth() == 0); + case 'year': + return false; + default: + return false; } }; + /** - * retrieve the index of the currently selected vaue - * @return {Number} index + * Returns formatted text for the minor axislabel, depending on the current + * date and the scale. For example when scale is MINUTE, the current time is + * formatted as "hh:mm". + * @param {Date} [date] custom date. if not provided, current date is taken */ - Slider.prototype.getIndex = function() { - return this.index; - }; + TimeStep.prototype.getLabelMinor = function(date) { + if (date == undefined) { + date = this.current; + } + var format = this.format.minorLabels[this.scale]; + return (format && format.length > 0) ? moment(date).format(format) : ''; + }; /** - * retrieve the currently selected value - * @return {*} value + * Returns formatted text for the major axis label, depending on the current + * date and the scale. For example when scale is MINUTE, the major scale is + * hours, and the hour will be formatted as "hh". + * @param {Date} [date] custom date. if not provided, current date is taken */ - Slider.prototype.get = function() { - return this.values[this.index]; + TimeStep.prototype.getLabelMajor = function(date) { + if (date == undefined) { + date = this.current; + } + + var format = this.format.majorLabels[this.scale]; + return (format && format.length > 0) ? moment(date).format(format) : ''; }; + TimeStep.prototype.getClassName = function() { + var m = moment(this.current); + var date = m.locale ? m.locale('en') : m.lang('en'); // old versions of moment have .lang() function + var step = this.step; - Slider.prototype._onMouseDown = function(event) { - // only react on left mouse button down - var leftButtonDown = event.which ? (event.which === 1) : (event.button === 1); - if (!leftButtonDown) return; + function even(value) { + return (value / step % 2 == 0) ? ' even' : ' odd'; + } - this.startClientX = event.clientX; - this.startSlideX = parseFloat(this.frame.slide.style.left); + function today(date) { + if (date.isSame(new Date(), 'day')) { + return ' today'; + } + if (date.isSame(moment().add(1, 'day'), 'day')) { + return ' tomorrow'; + } + if (date.isSame(moment().add(-1, 'day'), 'day')) { + return ' yesterday'; + } + return ''; + } - this.frame.style.cursor = 'move'; + function currentWeek(date) { + return date.isSame(new Date(), 'week') ? ' current-week' : ''; + } - // add event listeners to handle moving the contents - // we store the function onmousemove and onmouseup in the graph, so we can - // remove the eventlisteners lateron in the function mouseUp() - var me = this; - this.onmousemove = function (event) {me._onMouseMove(event);}; - this.onmouseup = function (event) {me._onMouseUp(event);}; - util.addEventListener(document, 'mousemove', this.onmousemove); - util.addEventListener(document, 'mouseup', this.onmouseup); - util.preventDefault(event); - }; + function currentMonth(date) { + return date.isSame(new Date(), 'month') ? ' current-month' : ''; + } + function currentYear(date) { + return date.isSame(new Date(), 'year') ? ' current-year' : ''; + } - Slider.prototype.leftToIndex = function (left) { - var width = parseFloat(this.frame.bar.style.width) - - this.frame.slide.clientWidth - 10; - var x = left - 3; + switch (this.scale) { + case 'millisecond': + return even(date.milliseconds()).trim(); - var index = Math.round(x / width * (this.values.length-1)); - if (index < 0) index = 0; - if (index > this.values.length-1) index = this.values.length-1; + case 'second': + return even(date.seconds()).trim(); - return index; - }; + case 'minute': + return even(date.minutes()).trim(); - Slider.prototype.indexToLeft = function (index) { - var width = parseFloat(this.frame.bar.style.width) - - this.frame.slide.clientWidth - 10; + case 'hour': + var hours = date.hours(); + if (this.step == 4) { + hours = hours + '-' + (hours + 4); + } + return hours + 'h' + today(date) + even(date.hours()); - var x = index / (this.values.length-1) * width; - var left = x + 3; + case 'weekday': + return date.format('dddd').toLowerCase() + + today(date) + currentWeek(date) + even(date.date()); - return left; + case 'day': + var day = date.date(); + var month = date.format('MMMM').toLowerCase(); + return 'day' + day + ' ' + month + currentMonth(date) + even(day - 1); + + case 'month': + return date.format('MMMM').toLowerCase() + + currentMonth(date) + even(date.month()); + + case 'year': + var year = date.year(); + return 'year' + year + currentYear(date)+ even(year); + + default: + return ''; + } }; + module.exports = TimeStep; - Slider.prototype._onMouseMove = function (event) { - var diff = event.clientX - this.startClientX; - var x = this.startSlideX + diff; +/***/ }, +/* 20 */ +/***/ function(module, exports, __webpack_require__) { - var index = this.leftToIndex(x); + /** + * Prototype for visual components + * @param {{dom: Object, domProps: Object, emitter: Emitter, range: Range}} [body] + * @param {Object} [options] + */ + function Component (body, options) { + this.options = null; + this.props = null; + } - this.setIndex(index); + /** + * Set options for the component. The new options will be merged into the + * current options. + * @param {Object} options + */ + Component.prototype.setOptions = function(options) { + if (options) { + util.extend(this.options, options); + } + }; - util.preventDefault(); + /** + * Repaint the component + * @return {boolean} Returns true if the component is resized + */ + Component.prototype.redraw = function() { + // should be implemented by the component + return false; }; + /** + * Destroy the component. Cleanup DOM and event listeners + */ + Component.prototype.destroy = function() { + // should be implemented by the component + }; - Slider.prototype._onMouseUp = function (event) { - this.frame.style.cursor = 'auto'; + /** + * Test whether the component is resized since the last time _isResized() was + * called. + * @return {Boolean} Returns true if the component is resized + * @protected + */ + Component.prototype._isResized = function() { + var resized = (this.props._previousWidth !== this.props.width || + this.props._previousHeight !== this.props.height); - // remove event listeners - util.removeEventListener(document, 'mousemove', this.onmousemove); - util.removeEventListener(document, 'mouseup', this.onmouseup); + this.props._previousWidth = this.props.width; + this.props._previousHeight = this.props.height; - util.preventDefault(); + return resized; }; - module.exports = Slider; + module.exports = Component; /***/ }, -/* 17 */ +/* 21 */ /***/ function(module, exports, __webpack_require__) { + var util = __webpack_require__(1); + var Component = __webpack_require__(20); + var moment = __webpack_require__(44); + var locales = __webpack_require__(48); + /** - * @prototype StepNumber - * The class StepNumber is an iterator for Numbers. You provide a start and end - * value, and a best step size. StepNumber itself rounds to fixed values and - * a finds the step that best fits the provided step. - * - * If prettyStep is true, the step size is chosen as close as possible to the - * provided step, but being a round value like 1, 2, 5, 10, 20, 50, .... - * - * Example usage: - * var step = new StepNumber(0, 10, 2.5, true); - * step.start(); - * while (!step.end()) { - * alert(step.getCurrent()); - * step.next(); - * } - * - * Version: 1.0 - * - * @param {Number} start The start value - * @param {Number} end The end value - * @param {Number} step Optional. Step size. Must be a positive value. - * @param {boolean} prettyStep Optional. If true, the step size is rounded - * To a pretty step size (like 1, 2, 5, 10, 20, 50, ...) + * A current time bar + * @param {{range: Range, dom: Object, domProps: Object}} body + * @param {Object} [options] Available parameters: + * {Boolean} [showCurrentTime] + * @constructor CurrentTime + * @extends Component */ - function StepNumber(start, end, step, prettyStep) { - // set default values - this._start = 0; - this._end = 0; - this._step = 1; - this.prettyStep = true; - this.precision = 5; + function CurrentTime (body, options) { + this.body = body; - this._current = 0; - this.setRange(start, end, step, prettyStep); - }; + // default options + this.defaultOptions = { + showCurrentTime: true, + + locales: locales, + locale: 'en' + }; + this.options = util.extend({}, this.defaultOptions); + this.offset = 0; + + this._create(); + + this.setOptions(options); + } + + CurrentTime.prototype = new Component(); /** - * Set a new range: start, end and step. - * - * @param {Number} start The start value - * @param {Number} end The end value - * @param {Number} step Optional. Step size. Must be a positive value. - * @param {boolean} prettyStep Optional. If true, the step size is rounded - * To a pretty step size (like 1, 2, 5, 10, 20, 50, ...) + * Create the HTML DOM for the current time bar + * @private */ - StepNumber.prototype.setRange = function(start, end, step, prettyStep) { - this._start = start ? start : 0; - this._end = end ? end : 0; + CurrentTime.prototype._create = function() { + var bar = document.createElement('div'); + bar.className = 'currenttime'; + bar.style.position = 'absolute'; + bar.style.top = '0px'; + bar.style.height = '100%'; - this.setStep(step, prettyStep); + this.bar = bar; }; /** - * Set a new step size - * @param {Number} step New step size. Must be a positive value - * @param {boolean} prettyStep Optional. If true, the provided step is rounded - * to a pretty step size (like 1, 2, 5, 10, 20, 50, ...) + * Destroy the CurrentTime bar */ - StepNumber.prototype.setStep = function(step, prettyStep) { - if (step === undefined || step <= 0) - return; + CurrentTime.prototype.destroy = function () { + this.options.showCurrentTime = false; + this.redraw(); // will remove the bar from the DOM and stop refreshing - if (prettyStep !== undefined) - this.prettyStep = prettyStep; + this.body = null; + }; - if (this.prettyStep === true) - this._step = StepNumber.calculatePrettyStep(step); - else - this._step = step; + /** + * Set options for the component. Options will be merged in current options. + * @param {Object} options Available parameters: + * {boolean} [showCurrentTime] + */ + CurrentTime.prototype.setOptions = function(options) { + if (options) { + // copy all options that we know + util.selectiveExtend(['showCurrentTime', 'locale', 'locales'], this.options, options); + } }; /** - * Calculate a nice step size, closest to the desired step size. - * Returns a value in one of the ranges 1*10^n, 2*10^n, or 5*10^n, where n is an - * integer Number. For example 1, 2, 5, 10, 20, 50, etc... - * @param {Number} step Desired step size - * @return {Number} Nice step size + * Repaint the component + * @return {boolean} Returns true if the component is resized */ - StepNumber.calculatePrettyStep = function (step) { - var log10 = function (x) {return Math.log(x) / Math.LN10;}; + CurrentTime.prototype.redraw = function() { + if (this.options.showCurrentTime) { + var parent = this.body.dom.backgroundVertical; + if (this.bar.parentNode != parent) { + // attach to the dom + if (this.bar.parentNode) { + this.bar.parentNode.removeChild(this.bar); + } + parent.appendChild(this.bar); - // try three steps (multiple of 1, 2, or 5 - var step1 = Math.pow(10, Math.round(log10(step))), - step2 = 2 * Math.pow(10, Math.round(log10(step / 2))), - step5 = 5 * Math.pow(10, Math.round(log10(step / 5))); + this.start(); + } - // choose the best step (closest to minimum step) - var prettyStep = step1; - if (Math.abs(step2 - step) <= Math.abs(prettyStep - step)) prettyStep = step2; - if (Math.abs(step5 - step) <= Math.abs(prettyStep - step)) prettyStep = step5; + var now = new Date(new Date().valueOf() + this.offset); + var x = this.body.util.toScreen(now); - // for safety - if (prettyStep <= 0) { - prettyStep = 1; + var locale = this.options.locales[this.options.locale]; + var title = locale.current + ' ' + locale.time + ': ' + moment(now).format('dddd, MMMM Do YYYY, H:mm:ss'); + title = title.charAt(0).toUpperCase() + title.substring(1); + + this.bar.style.left = x + 'px'; + this.bar.title = title; + } + else { + // remove the line from the DOM + if (this.bar.parentNode) { + this.bar.parentNode.removeChild(this.bar); + } + this.stop(); } - return prettyStep; + return false; }; /** - * returns the current value of the step - * @return {Number} current value + * Start auto refreshing the current time bar */ - StepNumber.prototype.getCurrent = function () { - return parseFloat(this._current.toPrecision(this.precision)); - }; + CurrentTime.prototype.start = function() { + var me = this; - /** - * returns the current step size - * @return {Number} current step size - */ - StepNumber.prototype.getStep = function () { - return this._step; + function update () { + me.stop(); + + // determine interval to refresh + var scale = me.body.range.conversion(me.body.domProps.center.width).scale; + var interval = 1 / scale / 10; + if (interval < 30) interval = 30; + if (interval > 1000) interval = 1000; + + me.redraw(); + + // start a timer to adjust for the new time + me.currentTimeTimer = setTimeout(update, interval); + } + + update(); }; /** - * Set the current value to the largest value smaller than start, which - * is a multiple of the step size + * Stop auto refreshing the current time bar */ - StepNumber.prototype.start = function() { - this._current = this._start - this._start % this._step; + CurrentTime.prototype.stop = function() { + if (this.currentTimeTimer !== undefined) { + clearTimeout(this.currentTimeTimer); + delete this.currentTimeTimer; + } }; /** - * Do a step, add the step size to the current value + * Set a current time. This can be used for example to ensure that a client's + * time is synchronized with a shared server time. + * @param {Date | String | Number} time A Date, unix timestamp, or + * ISO date string. */ - StepNumber.prototype.next = function () { - this._current += this._step; + CurrentTime.prototype.setCurrentTime = function(time) { + var t = util.convert(time, 'Date').valueOf(); + var now = new Date().valueOf(); + this.offset = t - now; + this.redraw(); }; /** - * Returns true whether the end is reached - * @return {boolean} True if the current value has passed the end value. + * Get the current time. + * @return {Date} Returns the current time. */ - StepNumber.prototype.end = function () { - return (this._current > this._end); + CurrentTime.prototype.getCurrentTime = function() { + return new Date(new Date().valueOf() + this.offset); }; - module.exports = StepNumber; + module.exports = CurrentTime; /***/ }, -/* 18 */ +/* 22 */ /***/ function(module, exports, __webpack_require__) { - var Emitter = __webpack_require__(11); - var Hammer = __webpack_require__(19); + var Hammer = __webpack_require__(45); var util = __webpack_require__(1); - var DataSet = __webpack_require__(7); - var DataView = __webpack_require__(9); - var Range = __webpack_require__(21); - var Core = __webpack_require__(25); - var TimeAxis = __webpack_require__(37); - var CurrentTime = __webpack_require__(39); - var CustomTime = __webpack_require__(41); - var ItemSet = __webpack_require__(26); + var Component = __webpack_require__(20); + var moment = __webpack_require__(44); + var locales = __webpack_require__(48); /** - * Create a timeline visualization - * @param {HTMLElement} container - * @param {vis.DataSet | Array | google.visualization.DataTable} [items] - * @param {vis.DataSet | Array | google.visualization.DataTable} [groups] - * @param {Object} [options] See Timeline.setOptions for the available options. - * @constructor - * @extends Core + * A custom time bar + * @param {{range: Range, dom: Object}} body + * @param {Object} [options] Available parameters: + * {Boolean} [showCustomTime] + * @constructor CustomTime + * @extends Component */ - function Timeline (container, items, groups, options) { - if (!(this instanceof Timeline)) { - throw new SyntaxError('Constructor must be called with the new operator'); - } - // if the third element is options, the forth is groups (optionally); - if (!(Array.isArray(groups) || groups instanceof DataSet) && groups instanceof Object) { - var forthArgument = options; - options = groups; - groups = forthArgument; - } + function CustomTime (body, options) { + this.body = body; - var me = this; + // default options this.defaultOptions = { - start: null, - end: null, + showCustomTime: false, + locales: locales, + locale: 'en' + }; + this.options = util.extend({}, this.defaultOptions); - autoResize: true, + this.customTime = new Date(); + this.eventParams = {}; // stores state parameters while dragging the bar - orientation: 'bottom', - width: null, - height: null, - maxHeight: null, - minHeight: null - }; - this.options = util.deepExtend({}, this.defaultOptions); + // create the DOM + this._create(); - // Create the DOM, props, and emitter - this._create(container); + this.setOptions(options); + } - // all components listed here will be repainted automatically - this.components = []; + CustomTime.prototype = new Component(); - this.body = { - dom: this.dom, - domProps: this.props, - emitter: { - on: this.on.bind(this), - off: this.off.bind(this), - emit: this.emit.bind(this) - }, - hiddenDates: [], - util: { - snap: null, // will be specified after TimeAxis is created - toScreen: me._toScreen.bind(me), - toGlobalScreen: me._toGlobalScreen.bind(me), // this refers to the root.width - toTime: me._toTime.bind(me), - toGlobalTime : me._toGlobalTime.bind(me) - } - }; + /** + * Set options for the component. Options will be merged in current options. + * @param {Object} options Available parameters: + * {boolean} [showCustomTime] + */ + CustomTime.prototype.setOptions = function(options) { + if (options) { + // copy all options that we know + util.selectiveExtend(['showCustomTime', 'locale', 'locales'], this.options, options); + } + }; - // range - this.range = new Range(this.body); - this.components.push(this.range); - this.body.range = this.range; + /** + * Create the DOM for the custom time + * @private + */ + CustomTime.prototype._create = function() { + var bar = document.createElement('div'); + bar.className = 'customtime'; + bar.style.position = 'absolute'; + bar.style.top = '0px'; + bar.style.height = '100%'; + this.bar = bar; - // time axis - this.timeAxis = new TimeAxis(this.body); - this.components.push(this.timeAxis); - this.body.util.snap = this.timeAxis.snap.bind(this.timeAxis); + var drag = document.createElement('div'); + drag.style.position = 'relative'; + drag.style.top = '0px'; + drag.style.left = '-10px'; + drag.style.height = '100%'; + drag.style.width = '20px'; + bar.appendChild(drag); - // current time bar - this.currentTime = new CurrentTime(this.body); - this.components.push(this.currentTime); + // attach event listeners + this.hammer = Hammer(bar, { + prevent_default: true + }); + this.hammer.on('dragstart', this._onDragStart.bind(this)); + this.hammer.on('drag', this._onDrag.bind(this)); + this.hammer.on('dragend', this._onDragEnd.bind(this)); + }; - // custom time bar - // Note: time bar will be attached in this.setOptions when selected - this.customTime = new CustomTime(this.body); - this.components.push(this.customTime); + /** + * Destroy the CustomTime bar + */ + CustomTime.prototype.destroy = function () { + this.options.showCustomTime = false; + this.redraw(); // will remove the bar from the DOM - // item set - this.itemSet = new ItemSet(this.body); - this.components.push(this.itemSet); + this.hammer.enable(false); + this.hammer = null; - this.itemsData = null; // DataSet - this.groupsData = null; // DataSet + this.body = null; + }; - // apply options - if (options) { - this.setOptions(options); - } + /** + * Repaint the component + * @return {boolean} Returns true if the component is resized + */ + CustomTime.prototype.redraw = function () { + if (this.options.showCustomTime) { + var parent = this.body.dom.backgroundVertical; + if (this.bar.parentNode != parent) { + // attach to the dom + if (this.bar.parentNode) { + this.bar.parentNode.removeChild(this.bar); + } + parent.appendChild(this.bar); + } - // IMPORTANT: THIS HAPPENS BEFORE SET ITEMS! - if (groups) { - this.setGroups(groups); - } + var x = this.body.util.toScreen(this.customTime); - // create itemset - if (items) { - this.setItems(items); + var locale = this.options.locales[this.options.locale]; + var title = locale.time + ': ' + moment(this.customTime).format('dddd, MMMM Do YYYY, H:mm:ss'); + title = title.charAt(0).toUpperCase() + title.substring(1); + + this.bar.style.left = x + 'px'; + this.bar.title = title; } else { - this.redraw(); + // remove the line from the DOM + if (this.bar.parentNode) { + this.bar.parentNode.removeChild(this.bar); + } } - } - // Extend the functionality from Core - Timeline.prototype = new Core(); + return false; + }; /** - * Set items - * @param {vis.DataSet | Array | google.visualization.DataTable | null} items + * Set custom time. + * @param {Date | number | string} time */ - Timeline.prototype.setItems = function(items) { - var initialLoad = (this.itemsData == null); - - // convert to type DataSet when needed - var newDataSet; - if (!items) { - newDataSet = null; - } - else if (items instanceof DataSet || items instanceof DataView) { - newDataSet = items; - } - else { - // turn an array into a dataset - newDataSet = new DataSet(items, { - type: { - start: 'Date', - end: 'Date' - } - }); - } - - // set items - this.itemsData = newDataSet; - this.itemSet && this.itemSet.setItems(newDataSet); - - if (initialLoad) { - if (this.options.start != undefined || this.options.end != undefined) { - if (this.options.start == undefined || this.options.end == undefined) { - var dataRange = this._getDataRange(); - } - - var start = this.options.start != undefined ? this.options.start : dataRange.start; - var end = this.options.end != undefined ? this.options.end : dataRange.end; - - this.setWindow(start, end, {animate: false}); - } - else { - this.fit({animate: false}); - } - } + CustomTime.prototype.setCustomTime = function(time) { + this.customTime = util.convert(time, 'Date'); + this.redraw(); }; /** - * Set groups - * @param {vis.DataSet | Array | google.visualization.DataTable} groups + * Retrieve the current custom time. + * @return {Date} customTime */ - Timeline.prototype.setGroups = function(groups) { - // convert to type DataSet when needed - var newDataSet; - if (!groups) { - newDataSet = null; - } - else if (groups instanceof DataSet || groups instanceof DataView) { - newDataSet = groups; - } - else { - // turn an array into a dataset - newDataSet = new DataSet(groups); - } - - this.groupsData = newDataSet; - this.itemSet.setGroups(newDataSet); + CustomTime.prototype.getCustomTime = function() { + return new Date(this.customTime.valueOf()); }; /** - * Set selected items by their id. Replaces the current selection - * Unknown id's are silently ignored. - * @param {string[] | string} [ids] An array with zero or more id's of the items to be - * selected. If ids is an empty array, all items will be - * unselected. - * @param {Object} [options] Available options: - * `focus: boolean` - * If true, focus will be set to the selected item(s) - * `animate: boolean | number` - * If true (default), the range is animated - * smoothly to the new window. - * If a number, the number is taken as duration - * for the animation. Default duration is 500 ms. - * Only applicable when option focus is true. + * Start moving horizontally + * @param {Event} event + * @private */ - Timeline.prototype.setSelection = function(ids, options) { - this.itemSet && this.itemSet.setSelection(ids); + CustomTime.prototype._onDragStart = function(event) { + this.eventParams.dragging = true; + this.eventParams.customTime = this.customTime; - if (options && options.focus) { - this.focus(ids, options); - } + event.stopPropagation(); + event.preventDefault(); }; /** - * Get the selected items by their id - * @return {Array} ids The ids of the selected items + * Perform moving operating. + * @param {Event} event + * @private */ - Timeline.prototype.getSelection = function() { - return this.itemSet && this.itemSet.getSelection() || []; + CustomTime.prototype._onDrag = function (event) { + if (!this.eventParams.dragging) return; + + var deltaX = event.gesture.deltaX, + x = this.body.util.toScreen(this.eventParams.customTime) + deltaX, + time = this.body.util.toTime(x); + + this.setCustomTime(time); + + // fire a timechange event + this.body.emitter.emit('timechange', { + time: new Date(this.customTime.valueOf()) + }); + + event.stopPropagation(); + event.preventDefault(); }; /** - * Adjust the visible window such that the selected item (or multiple items) - * are centered on screen. - * @param {String | String[]} id An item id or array with item ids - * @param {Object} [options] Available options: - * `animate: boolean | number` - * If true (default), the range is animated - * smoothly to the new window. - * If a number, the number is taken as duration - * for the animation. Default duration is 500 ms. - * Only applicable when option focus is true + * Stop moving operating. + * @param {event} event + * @private */ - Timeline.prototype.focus = function(id, options) { - if (!this.itemsData || id == undefined) return; - - var ids = Array.isArray(id) ? id : [id]; + CustomTime.prototype._onDragEnd = function (event) { + if (!this.eventParams.dragging) return; - // get the specified item(s) - var itemsData = this.itemsData.getDataSet().get(ids, { - type: { - start: 'Date', - end: 'Date' - } + // fire a timechanged event + this.body.emitter.emit('timechanged', { + time: new Date(this.customTime.valueOf()) }); - // calculate minimum start and maximum end of specified items - var start = null; - var end = null; - itemsData.forEach(function (itemData) { - var s = itemData.start.valueOf(); - var e = 'end' in itemData ? itemData.end.valueOf() : itemData.start.valueOf(); + event.stopPropagation(); + event.preventDefault(); + }; - if (start === null || s < start) { - start = s; - } + module.exports = CustomTime; - if (end === null || e > end) { - end = e; - } - }); - if (start !== null && end !== null) { - // calculate the new middle and interval for the window - var middle = (start + end) / 2; - var interval = Math.max((this.range.end - this.range.start), (end - start) * 1.1); +/***/ }, +/* 23 */ +/***/ function(module, exports, __webpack_require__) { - var animate = (options && options.animate !== undefined) ? options.animate : true; - this.range.setRange(middle - interval / 2, middle + interval / 2, animate); - } - }; + var util = __webpack_require__(1); + var DOMutil = __webpack_require__(2); + var Component = __webpack_require__(20); + var DataStep = __webpack_require__(16); /** - * Get the data range of the item set. - * @returns {{min: Date, max: Date}} range A range with a start and end Date. - * When no minimum is found, min==null - * When no maximum is found, max==null + * A horizontal time axis + * @param {Object} [options] See DataAxis.setOptions for the available + * options. + * @constructor DataAxis + * @extends Component + * @param body */ - Timeline.prototype.getItemRange = function() { - // calculate min from start filed - var dataset = this.itemsData.getDataSet(), - min = null, - max = null; - - if (dataset) { - // calculate the minimum value of the field 'start' - var minItem = dataset.min('start'); - min = minItem ? util.convert(minItem.start, 'Date').valueOf() : null; - // Note: we convert first to Date and then to number because else - // a conversion from ISODate to Number will fail + function DataAxis (body, options, svg, linegraphOptions) { + this.id = util.randomUUID(); + this.body = body; - // calculate maximum value of fields 'start' and 'end' - var maxStartItem = dataset.max('start'); - if (maxStartItem) { - max = util.convert(maxStartItem.start, 'Date').valueOf(); - } - var maxEndItem = dataset.max('end'); - if (maxEndItem) { - if (max == null) { - max = util.convert(maxEndItem.end, 'Date').valueOf(); - } - else { - max = Math.max(max, util.convert(maxEndItem.end, 'Date').valueOf()); - } + this.defaultOptions = { + orientation: 'left', // supported: 'left', 'right' + showMinorLabels: true, + showMajorLabels: true, + icons: true, + majorLinesOffset: 7, + minorLinesOffset: 4, + labelOffsetX: 10, + labelOffsetY: 2, + iconWidth: 20, + width: '40px', + visible: true, + alignZeros: true, + customRange: { + left: {min:undefined, max:undefined}, + right: {min:undefined, max:undefined} + }, + title: { + left: {text:undefined}, + right: {text:undefined} + }, + format: { + left: {decimals: undefined}, + right: {decimals: undefined} } - } + }; - return { - min: (min != null) ? new Date(min) : null, - max: (max != null) ? new Date(max) : null + this.linegraphOptions = linegraphOptions; + this.linegraphSVG = svg; + this.props = {}; + this.DOMelements = { // dynamic elements + lines: {}, + labels: {}, + title: {} }; - }; + this.dom = {}; - module.exports = Timeline; + this.range = {start:0, end:0}; + this.options = util.extend({}, this.defaultOptions); + this.conversionFactor = 1; -/***/ }, -/* 19 */ -/***/ function(module, exports, __webpack_require__) { + this.setOptions(options); + this.width = Number(('' + this.options.width).replace("px","")); + this.minWidth = this.width; + this.height = this.linegraphSVG.offsetHeight; + this.hidden = false; - // Only load hammer.js when in a browser environment - // (loading hammer.js in a node.js environment gives errors) - if (typeof window !== 'undefined') { - module.exports = window['Hammer'] || __webpack_require__(20); - } - else { - module.exports = function () { - throw Error('hammer.js is only available in a browser, not in node.js.'); - } - } + this.stepPixels = 25; + this.stepPixelsForced = 25; + this.zeroCrossing = -1; + this.lineOffset = 0; + this.master = true; + this.svgElements = {}; + this.iconsRemoved = false; -/***/ }, -/* 20 */ -/***/ function(module, exports, __webpack_require__) { - var __WEBPACK_AMD_DEFINE_RESULT__;/*! Hammer.JS - v1.1.3 - 2014-05-20 - * http://eightmedia.github.io/hammer.js - * - * Copyright (c) 2014 Jorik Tangelder ; - * Licensed under the MIT license */ + this.groups = {}; + this.amountOfGroups = 0; - (function(window, undefined) { - 'use strict'; + // create the HTML DOM + this._create(); - /** - * @main - * @module hammer - * - * @class Hammer - * @static - */ + var me = this; + this.body.emitter.on("verticalDrag", function() { + me.dom.lineContainer.style.top = me.body.domProps.scrollTop + 'px'; + }); + } - /** - * Hammer, use this to create instances - * ```` - * var hammertime = new Hammer(myElement); - * ```` - * - * @method Hammer - * @param {HTMLElement} element - * @param {Object} [options={}] - * @return {Hammer.Instance} - */ - var Hammer = function Hammer(element, options) { - return new Hammer.Instance(element, options || {}); - }; + DataAxis.prototype = new Component(); - /** - * version, as defined in package.json - * the value will be set at each build - * @property VERSION - * @final - * @type {String} - */ - Hammer.VERSION = '1.1.3'; - /** - * default settings. - * more settings are defined per gesture at `/gestures`. Each gesture can be disabled/enabled - * by setting it's name (like `swipe`) to false. - * You can set the defaults for all instances by changing this object before creating an instance. - * @example - * ```` - * Hammer.defaults.drag = false; - * Hammer.defaults.behavior.touchAction = 'pan-y'; - * delete Hammer.defaults.behavior.userSelect; - * ```` - * @property defaults - * @type {Object} - */ - Hammer.defaults = { - /** - * this setting object adds styles and attributes to the element to prevent the browser from doing - * its native behavior. The css properties are auto prefixed for the browsers when needed. - * @property defaults.behavior - * @type {Object} - */ - behavior: { - /** - * Disables text selection to improve the dragging gesture. When the value is `none` it also sets - * `onselectstart=false` for IE on the element. Mainly for desktop browsers. - * @property defaults.behavior.userSelect - * @type {String} - * @default 'none' - */ - userSelect: 'none', + DataAxis.prototype.addGroup = function(label, graphOptions) { + if (!this.groups.hasOwnProperty(label)) { + this.groups[label] = graphOptions; + } + this.amountOfGroups += 1; + }; - /** - * Specifies whether and how a given region can be manipulated by the user (for instance, by panning or zooming). - * Used by Chrome 35> and IE10>. By default this makes the element blocking any touch event. - * @property defaults.behavior.touchAction - * @type {String} - * @default: 'pan-y' - */ - touchAction: 'pan-y', + DataAxis.prototype.updateGroup = function(label, graphOptions) { + this.groups[label] = graphOptions; + }; - /** - * Disables the default callout shown when you touch and hold a touch target. - * On iOS, when you touch and hold a touch target such as a link, Safari displays - * a callout containing information about the link. This property allows you to disable that callout. - * @property defaults.behavior.touchCallout - * @type {String} - * @default 'none' - */ - touchCallout: 'none', + DataAxis.prototype.removeGroup = function(label) { + if (this.groups.hasOwnProperty(label)) { + delete this.groups[label]; + this.amountOfGroups -= 1; + } + }; - /** - * Specifies whether zooming is enabled. Used by IE10> - * @property defaults.behavior.contentZooming - * @type {String} - * @default 'none' - */ - contentZooming: 'none', - /** - * Specifies that an entire element should be draggable instead of its contents. - * Mainly for desktop browsers. - * @property defaults.behavior.userDrag - * @type {String} - * @default 'none' - */ - userDrag: 'none', + DataAxis.prototype.setOptions = function (options) { + if (options) { + var redraw = false; + if (this.options.orientation != options.orientation && options.orientation !== undefined) { + redraw = true; + } + var fields = [ + 'orientation', + 'showMinorLabels', + 'showMajorLabels', + 'icons', + 'majorLinesOffset', + 'minorLinesOffset', + 'labelOffsetX', + 'labelOffsetY', + 'iconWidth', + 'width', + 'visible', + 'customRange', + 'title', + 'format', + 'alignZeros' + ]; + util.selectiveExtend(fields, this.options, options); - /** - * Overrides the highlight color shown when the user taps a link or a JavaScript - * clickable element in Safari on iPhone. This property obeys the alpha value, if specified. - * - * If you don't specify an alpha value, Safari on iPhone applies a default alpha value - * to the color. To disable tap highlighting, set the alpha value to 0 (invisible). - * If you set the alpha value to 1.0 (opaque), the element is not visible when tapped. - * @property defaults.behavior.tapHighlightColor - * @type {String} - * @default 'rgba(0,0,0,0)' - */ - tapHighlightColor: 'rgba(0,0,0,0)' + this.minWidth = Number(('' + this.options.width).replace("px","")); + + if (redraw == true && this.dom.frame) { + this.hide(); + this.show(); } + } }; - /** - * hammer document where the base events are added at - * @property DOCUMENT - * @type {HTMLElement} - * @default window.document - */ - Hammer.DOCUMENT = document; /** - * detect support for pointer events - * @property HAS_POINTEREVENTS - * @type {Boolean} + * Create the HTML DOM for the DataAxis */ - Hammer.HAS_POINTEREVENTS = navigator.pointerEnabled || navigator.msPointerEnabled; + DataAxis.prototype._create = function() { + this.dom.frame = document.createElement('div'); + this.dom.frame.style.width = this.options.width; + this.dom.frame.style.height = this.height; - /** - * detect support for touch events - * @property HAS_TOUCHEVENTS - * @type {Boolean} - */ - Hammer.HAS_TOUCHEVENTS = ('ontouchstart' in window); + this.dom.lineContainer = document.createElement('div'); + this.dom.lineContainer.style.width = '100%'; + this.dom.lineContainer.style.height = this.height; + this.dom.lineContainer.style.position = 'relative'; - /** - * detect mobile browsers - * @property IS_MOBILE - * @type {Boolean} - */ - Hammer.IS_MOBILE = /mobile|tablet|ip(ad|hone|od)|android|silk/i.test(navigator.userAgent); + // create svg element for graph drawing. + this.svg = document.createElementNS('http://www.w3.org/2000/svg',"svg"); + this.svg.style.position = "absolute"; + this.svg.style.top = '0px'; + this.svg.style.height = '100%'; + this.svg.style.width = '100%'; + this.svg.style.display = "block"; + this.dom.frame.appendChild(this.svg); + }; - /** - * detect if we want to support mouseevents at all - * @property NO_MOUSEEVENTS - * @type {Boolean} - */ - Hammer.NO_MOUSEEVENTS = (Hammer.HAS_TOUCHEVENTS && Hammer.IS_MOBILE) || Hammer.HAS_POINTEREVENTS; + DataAxis.prototype._redrawGroupIcons = function () { + DOMutil.prepareElements(this.svgElements); - /** - * interval in which Hammer recalculates current velocity/direction/angle in ms - * @property CALCULATE_INTERVAL - * @type {Number} - * @default 25 - */ - Hammer.CALCULATE_INTERVAL = 25; + var x; + var iconWidth = this.options.iconWidth; + var iconHeight = 15; + var iconOffset = 4; + var y = iconOffset + 0.5 * iconHeight; - /** - * eventtypes per touchevent (start, move, end) are filled by `Event.determineEventTypes` on `setup` - * the object contains the DOM event names per type (`EVENT_START`, `EVENT_MOVE`, `EVENT_END`) - * @property EVENT_TYPES - * @private - * @writeOnce - * @type {Object} - */ - var EVENT_TYPES = {}; + if (this.options.orientation == 'left') { + x = iconOffset; + } + else { + x = this.width - iconWidth - iconOffset; + } - /** - * direction strings, for safe comparisons - * @property DIRECTION_DOWN|LEFT|UP|RIGHT - * @final - * @type {String} - * @default 'down' 'left' 'up' 'right' - */ - var DIRECTION_DOWN = Hammer.DIRECTION_DOWN = 'down'; - var DIRECTION_LEFT = Hammer.DIRECTION_LEFT = 'left'; - var DIRECTION_UP = Hammer.DIRECTION_UP = 'up'; - var DIRECTION_RIGHT = Hammer.DIRECTION_RIGHT = 'right'; + for (var groupId in this.groups) { + if (this.groups.hasOwnProperty(groupId)) { + if (this.groups[groupId].visible == true && (this.linegraphOptions.visibility[groupId] === undefined || this.linegraphOptions.visibility[groupId] == true)) { + this.groups[groupId].drawIcon(x, y, this.svgElements, this.svg, iconWidth, iconHeight); + y += iconHeight + iconOffset; + } + } + } - /** - * pointertype strings, for safe comparisons - * @property POINTER_MOUSE|TOUCH|PEN - * @final - * @type {String} - * @default 'mouse' 'touch' 'pen' - */ - var POINTER_MOUSE = Hammer.POINTER_MOUSE = 'mouse'; - var POINTER_TOUCH = Hammer.POINTER_TOUCH = 'touch'; - var POINTER_PEN = Hammer.POINTER_PEN = 'pen'; + DOMutil.cleanupElements(this.svgElements); + this.iconsRemoved = false; + }; - /** - * eventtypes - * @property EVENT_START|MOVE|END|RELEASE|TOUCH - * @final - * @type {String} - * @default 'start' 'change' 'move' 'end' 'release' 'touch' - */ - var EVENT_START = Hammer.EVENT_START = 'start'; - var EVENT_MOVE = Hammer.EVENT_MOVE = 'move'; - var EVENT_END = Hammer.EVENT_END = 'end'; - var EVENT_RELEASE = Hammer.EVENT_RELEASE = 'release'; - var EVENT_TOUCH = Hammer.EVENT_TOUCH = 'touch'; + DataAxis.prototype._cleanupIcons = function() { + if (this.iconsRemoved == false) { + DOMutil.prepareElements(this.svgElements); + DOMutil.cleanupElements(this.svgElements); + this.iconsRemoved = true; + } + } /** - * if the window events are set... - * @property READY - * @writeOnce - * @type {Boolean} - * @default false + * Create the HTML DOM for the DataAxis */ - Hammer.READY = false; + DataAxis.prototype.show = function() { + this.hidden = false; + if (!this.dom.frame.parentNode) { + if (this.options.orientation == 'left') { + this.body.dom.left.appendChild(this.dom.frame); + } + else { + this.body.dom.right.appendChild(this.dom.frame); + } + } + + if (!this.dom.lineContainer.parentNode) { + this.body.dom.backgroundHorizontal.appendChild(this.dom.lineContainer); + } + }; /** - * plugins namespace - * @property plugins - * @type {Object} + * Create the HTML DOM for the DataAxis */ - Hammer.plugins = Hammer.plugins || {}; + DataAxis.prototype.hide = function() { + this.hidden = true; + if (this.dom.frame.parentNode) { + this.dom.frame.parentNode.removeChild(this.dom.frame); + } + + if (this.dom.lineContainer.parentNode) { + this.dom.lineContainer.parentNode.removeChild(this.dom.lineContainer); + } + }; /** - * gestures namespace - * see `/gestures` for the definitions - * @property gestures - * @type {Object} + * Set a range (start and end) + * @param end + * @param start + * @param end */ - Hammer.gestures = Hammer.gestures || {}; + DataAxis.prototype.setRange = function (start, end) { + if (this.master == false && this.options.alignZeros == true && this.zeroCrossing != -1) { + if (start > 0) { + start = 0; + } + } + this.range.start = start; + this.range.end = end; + }; /** - * setup events to detect gestures on the document - * this function is called when creating an new instance - * @private + * Repaint the component + * @return {boolean} Returns true if the component is resized */ - function setup() { - if(Hammer.READY) { - return; + DataAxis.prototype.redraw = function () { + var resized = false; + var activeGroups = 0; + + // Make sure the line container adheres to the vertical scrolling. + this.dom.lineContainer.style.top = this.body.domProps.scrollTop + 'px'; + + for (var groupId in this.groups) { + if (this.groups.hasOwnProperty(groupId)) { + if (this.groups[groupId].visible == true && (this.linegraphOptions.visibility[groupId] === undefined || this.linegraphOptions.visibility[groupId] == true)) { + activeGroups++; + } } + } + if (this.amountOfGroups == 0 || activeGroups == 0) { + this.hide(); + } + else { + this.show(); + this.height = Number(this.linegraphSVG.style.height.replace("px","")); - // find what eventtypes we add listeners to - Event.determineEventTypes(); + // svg offsetheight did not work in firefox and explorer... + this.dom.lineContainer.style.height = this.height + 'px'; + this.width = this.options.visible == true ? Number(('' + this.options.width).replace("px","")) : 0; - // Register all gestures inside Hammer.gestures - Utils.each(Hammer.gestures, function(gesture) { - Detection.register(gesture); - }); + var props = this.props; + var frame = this.dom.frame; - // Add touch events on the document - Event.onTouch(Hammer.DOCUMENT, EVENT_MOVE, Detection.detect); - Event.onTouch(Hammer.DOCUMENT, EVENT_END, Detection.detect); + // update classname + frame.className = 'dataaxis'; - // Hammer is ready...! - Hammer.READY = true; - } + // calculate character width and height + this._calculateCharSize(); - /** - * @module hammer - * - * @class Utils - * @static - */ - var Utils = Hammer.utils = { - /** - * extend method, could also be used for cloning when `dest` is an empty object. - * changes the dest object - * @method extend - * @param {Object} dest - * @param {Object} src - * @param {Boolean} [merge=false] do a merge - * @return {Object} dest - */ - extend: function extend(dest, src, merge) { - for(var key in src) { - if(!src.hasOwnProperty(key) || (dest[key] !== undefined && merge)) { - continue; - } - dest[key] = src[key]; - } - return dest; - }, + var orientation = this.options.orientation; + var showMinorLabels = this.options.showMinorLabels; + var showMajorLabels = this.options.showMajorLabels; - /** - * simple addEventListener wrapper - * @method on - * @param {HTMLElement} element - * @param {String} type - * @param {Function} handler - */ - on: function on(element, type, handler) { - element.addEventListener(type, handler, false); - }, + // determine the width and height of the elements for the axis + props.minorLabelHeight = showMinorLabels ? props.minorCharHeight : 0; + props.majorLabelHeight = showMajorLabels ? props.majorCharHeight : 0; - /** - * simple removeEventListener wrapper - * @method off - * @param {HTMLElement} element - * @param {String} type - * @param {Function} handler - */ - off: function off(element, type, handler) { - element.removeEventListener(type, handler, false); - }, + props.minorLineWidth = this.body.dom.backgroundHorizontal.offsetWidth - this.lineOffset - this.width + 2 * this.options.minorLinesOffset; + props.minorLineHeight = 1; + props.majorLineWidth = this.body.dom.backgroundHorizontal.offsetWidth - this.lineOffset - this.width + 2 * this.options.majorLinesOffset; + props.majorLineHeight = 1; - /** - * forEach over arrays and objects - * @method each - * @param {Object|Array} obj - * @param {Function} iterator - * @param {any} iterator.item - * @param {Number} iterator.index - * @param {Object|Array} iterator.obj the source object - * @param {Object} context value to use as `this` in the iterator - */ - each: function each(obj, iterator, context) { - var i, len; + // take frame offline while updating (is almost twice as fast) + if (orientation == 'left') { + frame.style.top = '0'; + frame.style.left = '0'; + frame.style.bottom = ''; + frame.style.width = this.width + 'px'; + frame.style.height = this.height + "px"; + this.props.width = this.body.domProps.left.width; + this.props.height = this.body.domProps.left.height; + } + else { // right + frame.style.top = ''; + frame.style.bottom = '0'; + frame.style.left = '0'; + frame.style.width = this.width + 'px'; + frame.style.height = this.height + "px"; + this.props.width = this.body.domProps.right.width; + this.props.height = this.body.domProps.right.height; + } - // native forEach on arrays - if('forEach' in obj) { - obj.forEach(iterator, context); - // arrays - } else if(obj.length !== undefined) { - for(i = 0, len = obj.length; i < len; i++) { - if(iterator.call(context, obj[i], i, obj) === false) { - return; - } - } - // objects - } else { - for(i in obj) { - if(obj.hasOwnProperty(i) && - iterator.call(context, obj[i], i, obj) === false) { - return; - } - } - } - }, + resized = this._redrawLabels(); + resized = this._isResized() || resized; - /** - * find if a string contains the string using indexOf - * @method inStr - * @param {String} src - * @param {String} find - * @return {Boolean} found - */ - inStr: function inStr(src, find) { - return src.indexOf(find) > -1; - }, + if (this.options.icons == true) { + this._redrawGroupIcons(); + } + else { + this._cleanupIcons(); + } - /** - * find if a array contains the object using indexOf or a simple polyfill - * @method inArray - * @param {String} src - * @param {String} find - * @return {Boolean|Number} false when not found, or the index - */ - inArray: function inArray(src, find) { - if(src.indexOf) { - var index = src.indexOf(find); - return (index === -1) ? false : index; - } else { - for(var i = 0, len = src.length; i < len; i++) { - if(src[i] === find) { - return i; - } - } - return false; - } - }, + this._redrawTitle(orientation); + } + return resized; + }; - /** - * convert an array-like object (`arguments`, `touchlist`) to an array - * @method toArray - * @param {Object} obj - * @return {Array} - */ - toArray: function toArray(obj) { - return Array.prototype.slice.call(obj, 0); - }, + /** + * Repaint major and minor text labels and vertical grid lines + * @private + */ + DataAxis.prototype._redrawLabels = function () { + var resized = false; + DOMutil.prepareElements(this.DOMelements.lines); + DOMutil.prepareElements(this.DOMelements.labels); - /** - * find if a node is in the given parent - * @method hasParent - * @param {HTMLElement} node - * @param {HTMLElement} parent - * @return {Boolean} found - */ - hasParent: function hasParent(node, parent) { - while(node) { - if(node == parent) { - return true; - } - node = node.parentNode; - } - return false; - }, + var orientation = this.options['orientation']; - /** - * get the center of all the touches - * @method getCenter - * @param {Array} touches - * @return {Object} center contains `pageX`, `pageY`, `clientX` and `clientY` properties - */ - getCenter: function getCenter(touches) { - var pageX = [], - pageY = [], - clientX = [], - clientY = [], - min = Math.min, - max = Math.max; + // calculate range and step (step such that we have space for 7 characters per label) + var minimumStep = this.master ? this.props.majorCharHeight || 10 : this.stepPixelsForced; - // no need to loop when only one touch - if(touches.length === 1) { - return { - pageX: touches[0].pageX, - pageY: touches[0].pageY, - clientX: touches[0].clientX, - clientY: touches[0].clientY - }; - } + var step = new DataStep( + this.range.start, + this.range.end, + minimumStep, + this.dom.frame.offsetHeight, + this.options.customRange[this.options.orientation], + this.master == false && this.options.alignZeros // doess the step have to align zeros? only if not master and the options is on + ); - Utils.each(touches, function(touch) { - pageX.push(touch.pageX); - pageY.push(touch.pageY); - clientX.push(touch.clientX); - clientY.push(touch.clientY); - }); + this.step = step; + // get the distance in pixels for a step + // dead space is space that is "left over" after a step + var stepPixels = (this.dom.frame.offsetHeight - (step.deadSpace * (this.dom.frame.offsetHeight / step.marginRange))) / (((step.marginRange - step.deadSpace) / step.step)); - return { - pageX: (min.apply(Math, pageX) + max.apply(Math, pageX)) / 2, - pageY: (min.apply(Math, pageY) + max.apply(Math, pageY)) / 2, - clientX: (min.apply(Math, clientX) + max.apply(Math, clientX)) / 2, - clientY: (min.apply(Math, clientY) + max.apply(Math, clientY)) / 2 - }; - }, + this.stepPixels = stepPixels; - /** - * calculate the velocity between two points. unit is in px per ms. - * @method getVelocity - * @param {Number} deltaTime - * @param {Number} deltaX - * @param {Number} deltaY - * @return {Object} velocity `x` and `y` - */ - getVelocity: function getVelocity(deltaTime, deltaX, deltaY) { - return { - x: Math.abs(deltaX / deltaTime) || 0, - y: Math.abs(deltaY / deltaTime) || 0 - }; - }, + var amountOfSteps = this.height / stepPixels; + var stepDifference = 0; - /** - * calculate the angle between two coordinates - * @method getAngle - * @param {Touch} touch1 - * @param {Touch} touch2 - * @return {Number} angle - */ - getAngle: function getAngle(touch1, touch2) { - var x = touch2.clientX - touch1.clientX, - y = touch2.clientY - touch1.clientY; + // the slave axis needs to use the same horizontal lines as the master axis. + if (this.master == false) { + stepPixels = this.stepPixelsForced; + stepDifference = Math.round((this.dom.frame.offsetHeight / stepPixels) - amountOfSteps); + for (var i = 0; i < 0.5 * stepDifference; i++) { + step.previous(); + } + amountOfSteps = this.height / stepPixels; - return Math.atan2(y, x) * 180 / Math.PI; - }, + if (this.zeroCrossing != -1 && this.options.alignZeros == true) { + var zeroStepDifference = (step.marginEnd / step.step) - this.zeroCrossing; + if (zeroStepDifference > 0) { + for (var i = 0; i < zeroStepDifference; i++) {step.next();} + } + else if (zeroStepDifference < 0) { + for (var i = 0; i < -zeroStepDifference; i++) {step.previous();} + } + } + } + else { + amountOfSteps += 0.25; + } - /** - * do a small comparision to get the direction between two touches. - * @method getDirection - * @param {Touch} touch1 - * @param {Touch} touch2 - * @return {String} direction matches `DIRECTION_LEFT|RIGHT|UP|DOWN` - */ - getDirection: function getDirection(touch1, touch2) { - var x = Math.abs(touch1.clientX - touch2.clientX), - y = Math.abs(touch1.clientY - touch2.clientY); - if(x >= y) { - return touch1.clientX - touch2.clientX > 0 ? DIRECTION_LEFT : DIRECTION_RIGHT; - } - return touch1.clientY - touch2.clientY > 0 ? DIRECTION_UP : DIRECTION_DOWN; - }, + this.valueAtZero = step.marginEnd; + var marginStartPos = 0; - /** - * calculate the distance between two touches - * @method getDistance - * @param {Touch}touch1 - * @param {Touch} touch2 - * @return {Number} distance - */ - getDistance: function getDistance(touch1, touch2) { - var x = touch2.clientX - touch1.clientX, - y = touch2.clientY - touch1.clientY; + // do not draw the first label + var max = 1; - return Math.sqrt((x * x) + (y * y)); - }, + // Get the number of decimal places + var decimals; + if(this.options.format[orientation] !== undefined) { + decimals = this.options.format[orientation].decimals; + } - /** - * calculate the scale factor between two touchLists - * no scale is 1, and goes down to 0 when pinched together, and bigger when pinched out - * @method getScale - * @param {Array} start array of touches - * @param {Array} end array of touches - * @return {Number} scale - */ - getScale: function getScale(start, end) { - // need two fingers... - if(start.length >= 2 && end.length >= 2) { - return this.getDistance(end[0], end[1]) / this.getDistance(start[0], start[1]); - } - return 1; - }, + this.maxLabelSize = 0; + var y = 0; + while (max < Math.round(amountOfSteps)) { + step.next(); + y = Math.round(max * stepPixels); + marginStartPos = max * stepPixels; + var isMajor = step.isMajor(); - /** - * calculate the rotation degrees between two touchLists - * @method getRotation - * @param {Array} start array of touches - * @param {Array} end array of touches - * @return {Number} rotation - */ - getRotation: function getRotation(start, end) { - // need two fingers - if(start.length >= 2 && end.length >= 2) { - return this.getAngle(end[1], end[0]) - this.getAngle(start[1], start[0]); - } - return 0; - }, - - /** - * find out if the direction is vertical * - * @method isVertical - * @param {String} direction matches `DIRECTION_UP|DOWN` - * @return {Boolean} is_vertical - */ - isVertical: function isVertical(direction) { - return direction == DIRECTION_UP || direction == DIRECTION_DOWN; - }, - - /** - * set css properties with their prefixes - * @param {HTMLElement} element - * @param {String} prop - * @param {String} value - * @param {Boolean} [toggle=true] - * @return {Boolean} - */ - setPrefixedCss: function setPrefixedCss(element, prop, value, toggle) { - var prefixes = ['', 'Webkit', 'Moz', 'O', 'ms']; - prop = Utils.toCamelCase(prop); + if (this.options['showMinorLabels'] && isMajor == false || this.master == false && this.options['showMinorLabels'] == true) { + this._redrawLabel(y - 2, step.getCurrent(decimals), orientation, 'yAxis minor', this.props.minorCharHeight); + } - for(var i = 0; i < prefixes.length; i++) { - var p = prop; - // prefixes - if(prefixes[i]) { - p = prefixes[i] + p.slice(0, 1).toUpperCase() + p.slice(1); - } + if (isMajor && this.options['showMajorLabels'] && this.master == true || + this.options['showMinorLabels'] == false && this.master == false && isMajor == true) { + if (y >= 0) { + this._redrawLabel(y - 2, step.getCurrent(decimals), orientation, 'yAxis major', this.props.majorCharHeight); + } + this._redrawLine(y, orientation, 'grid horizontal major', this.options.majorLinesOffset, this.props.majorLineWidth); + } + else { + this._redrawLine(y, orientation, 'grid horizontal minor', this.options.minorLinesOffset, this.props.minorLineWidth); + } - // test the style - if(p in element.style) { - element.style[p] = (toggle == null || toggle) && value || ''; - break; - } - } - }, + if (this.master == true && step.current == 0) { + this.zeroCrossing = max; + } - /** - * toggle browser default behavior by setting css properties. - * `userSelect='none'` also sets `element.onselectstart` to false - * `userDrag='none'` also sets `element.ondragstart` to false - * - * @method toggleBehavior - * @param {HtmlElement} element - * @param {Object} props - * @param {Boolean} [toggle=true] - */ - toggleBehavior: function toggleBehavior(element, props, toggle) { - if(!props || !element || !element.style) { - return; - } + max++; + } - // set the css properties - Utils.each(props, function(value, prop) { - Utils.setPrefixedCss(element, prop, value, toggle); - }); + if (this.master == false) { + this.conversionFactor = y / (this.valueAtZero - step.current); + } + else { + this.conversionFactor = this.dom.frame.offsetHeight / step.marginRange; + } - var falseFn = toggle && function() { - return false; - }; + // Note that title is rotated, so we're using the height, not width! + var titleWidth = 0; + if (this.options.title[orientation] !== undefined && this.options.title[orientation].text !== undefined) { + titleWidth = this.props.titleCharHeight; + } + var offset = this.options.icons == true ? Math.max(this.options.iconWidth, titleWidth) + this.options.labelOffsetX + 15 : titleWidth + this.options.labelOffsetX + 15; - // also the disable onselectstart - if(props.userSelect == 'none') { - element.onselectstart = falseFn; - } - // and disable ondragstart - if(props.userDrag == 'none') { - element.ondragstart = falseFn; - } - }, + // this will resize the yAxis to accommodate the labels. + if (this.maxLabelSize > (this.width - offset) && this.options.visible == true) { + this.width = this.maxLabelSize + offset; + this.options.width = this.width + "px"; + DOMutil.cleanupElements(this.DOMelements.lines); + DOMutil.cleanupElements(this.DOMelements.labels); + this.redraw(); + resized = true; + } + // this will resize the yAxis if it is too big for the labels. + else if (this.maxLabelSize < (this.width - offset) && this.options.visible == true && this.width > this.minWidth) { + this.width = Math.max(this.minWidth,this.maxLabelSize + offset); + this.options.width = this.width + "px"; + DOMutil.cleanupElements(this.DOMelements.lines); + DOMutil.cleanupElements(this.DOMelements.labels); + this.redraw(); + resized = true; + } + else { + DOMutil.cleanupElements(this.DOMelements.lines); + DOMutil.cleanupElements(this.DOMelements.labels); + resized = false; + } - /** - * convert a string with underscores to camelCase - * so prevent_default becomes preventDefault - * @param {String} str - * @return {String} camelCaseStr - */ - toCamelCase: function toCamelCase(str) { - return str.replace(/[_-]([a-z])/g, function(s) { - return s[1].toUpperCase(); - }); - } + return resized; }; + DataAxis.prototype.convertValue = function (value) { + var invertedValue = this.valueAtZero - value; + var convertedValue = invertedValue * this.conversionFactor; + return convertedValue; + }; /** - * @module hammer - */ - /** - * @class Event - * @static + * Create a label for the axis at position x + * @private + * @param y + * @param text + * @param orientation + * @param className + * @param characterHeight */ - var Event = Hammer.event = { - /** - * when touch events have been fired, this is true - * this is used to stop mouse events - * @property prevent_mouseevents - * @private - * @type {Boolean} - */ - preventMouseEvents: false, - - /** - * if EVENT_START has been fired - * @property started - * @private - * @type {Boolean} - */ - started: false, - - /** - * when the mouse is hold down, this is true - * @property should_detect - * @private - * @type {Boolean} - */ - shouldDetect: false, - - /** - * simple event binder with a hook and support for multiple types - * @method on - * @param {HTMLElement} element - * @param {String} type - * @param {Function} handler - * @param {Function} [hook] - * @param {Object} hook.type - */ - on: function on(element, type, handler, hook) { - var types = type.split(' '); - Utils.each(types, function(type) { - Utils.on(element, type, handler); - hook && hook(type); - }); - }, - - /** - * simple event unbinder with a hook and support for multiple types - * @method off - * @param {HTMLElement} element - * @param {String} type - * @param {Function} handler - * @param {Function} [hook] - * @param {Object} hook.type - */ - off: function off(element, type, handler, hook) { - var types = type.split(' '); - Utils.each(types, function(type) { - Utils.off(element, type, handler); - hook && hook(type); - }); - }, + DataAxis.prototype._redrawLabel = function (y, text, orientation, className, characterHeight) { + // reuse redundant label + var label = DOMutil.getDOMElement('div',this.DOMelements.labels, this.dom.frame); //this.dom.redundant.labels.shift(); + label.className = className; + label.innerHTML = text; + if (orientation == 'left') { + label.style.left = '-' + this.options.labelOffsetX + 'px'; + label.style.textAlign = "right"; + } + else { + label.style.right = '-' + this.options.labelOffsetX + 'px'; + label.style.textAlign = "left"; + } - /** - * the core touch event handler. - * this finds out if we should to detect gestures - * @method onTouch - * @param {HTMLElement} element - * @param {String} eventType matches `EVENT_START|MOVE|END` - * @param {Function} handler - * @return onTouchHandler {Function} the core event handler - */ - onTouch: function onTouch(element, eventType, handler) { - var self = this; + label.style.top = y - 0.5 * characterHeight + this.options.labelOffsetY + 'px'; - var onTouchHandler = function onTouchHandler(ev) { - var srcType = ev.type.toLowerCase(), - isPointer = Hammer.HAS_POINTEREVENTS, - isMouse = Utils.inStr(srcType, 'mouse'), - triggerType; + text += ''; - // if we are in a mouseevent, but there has been a touchevent triggered in this session - // we want to do nothing. simply break out of the event. - if(isMouse && self.preventMouseEvents) { - return; + var largestWidth = Math.max(this.props.majorCharWidth,this.props.minorCharWidth); + if (this.maxLabelSize < text.length * largestWidth) { + this.maxLabelSize = text.length * largestWidth; + } + }; - // mousebutton must be down - } else if(isMouse && eventType == EVENT_START && ev.button === 0) { - self.preventMouseEvents = false; - self.shouldDetect = true; - } else if(isPointer && eventType == EVENT_START) { - self.shouldDetect = (ev.buttons === 1 || PointerEvent.matchType(POINTER_TOUCH, ev)); - // just a valid start event, but no mouse - } else if(!isMouse && eventType == EVENT_START) { - self.preventMouseEvents = true; - self.shouldDetect = true; - } + /** + * Create a minor line for the axis at position y + * @param y + * @param orientation + * @param className + * @param offset + * @param width + */ + DataAxis.prototype._redrawLine = function (y, orientation, className, offset, width) { + if (this.master == true) { + var line = DOMutil.getDOMElement('div',this.DOMelements.lines, this.dom.lineContainer);//this.dom.redundant.lines.shift(); + line.className = className; + line.innerHTML = ''; - // update the pointer event before entering the detection - if(isPointer && eventType != EVENT_END) { - PointerEvent.updatePointer(eventType, ev); - } + if (orientation == 'left') { + line.style.left = (this.width - offset) + 'px'; + } + else { + line.style.right = (this.width - offset) + 'px'; + } - // we are in a touch/down state, so allowed detection of gestures - if(self.shouldDetect) { - triggerType = self.doDetect.call(self, ev, eventType, element, handler); - } + line.style.width = width + 'px'; + line.style.top = y + 'px'; + } + }; - // ...and we are done with the detection - // so reset everything to start each detection totally fresh - if(triggerType == EVENT_END) { - self.preventMouseEvents = false; - self.shouldDetect = false; - PointerEvent.reset(); - // update the pointerevent object after the detection - } + /** + * Create a title for the axis + * @private + * @param orientation + */ + DataAxis.prototype._redrawTitle = function (orientation) { + DOMutil.prepareElements(this.DOMelements.title); - if(isPointer && eventType == EVENT_END) { - PointerEvent.updatePointer(eventType, ev); - } - }; + // Check if the title is defined for this axes + if (this.options.title[orientation] !== undefined && this.options.title[orientation].text !== undefined) { + var title = DOMutil.getDOMElement('div', this.DOMelements.title, this.dom.frame); + title.className = 'yAxis title ' + orientation; + title.innerHTML = this.options.title[orientation].text; - this.on(element, EVENT_TYPES[eventType], onTouchHandler); - return onTouchHandler; - }, + // Add style - if provided + if (this.options.title[orientation].style !== undefined) { + util.addCssText(title, this.options.title[orientation].style); + } - /** - * the core detection method - * this finds out what hammer-touch-events to trigger - * @method doDetect - * @param {Object} ev - * @param {String} eventType matches `EVENT_START|MOVE|END` - * @param {HTMLElement} element - * @param {Function} handler - * @return {String} triggerType matches `EVENT_START|MOVE|END` - */ - doDetect: function doDetect(ev, eventType, element, handler) { - var touchList = this.getTouchList(ev, eventType); - var touchListLength = touchList.length; - var triggerType = eventType; - var triggerChange = touchList.trigger; // used by fakeMultitouch plugin - var changedLength = touchListLength; + if (orientation == 'left') { + title.style.left = this.props.titleCharHeight + 'px'; + } + else { + title.style.right = this.props.titleCharHeight + 'px'; + } - // at each touchstart-like event we want also want to trigger a TOUCH event... - if(eventType == EVENT_START) { - triggerChange = EVENT_TOUCH; - // ...the same for a touchend-like event - } else if(eventType == EVENT_END) { - triggerChange = EVENT_RELEASE; + title.style.width = this.height + 'px'; + } - // keep track of how many touches have been removed - changedLength = touchList.length - ((ev.changedTouches) ? ev.changedTouches.length : 1); - } + // we need to clean up in case we did not use all elements. + DOMutil.cleanupElements(this.DOMelements.title); + }; - // after there are still touches on the screen, - // we just want to trigger a MOVE event. so change the START or END to a MOVE - // but only after detection has been started, the first time we actualy want a START - if(changedLength > 0 && this.started) { - triggerType = EVENT_MOVE; - } - // detection has been started, we keep track of this, see above - this.started = true; - // generate some event data, some basic information - var evData = this.collectEventData(element, triggerType, touchList, ev); - // trigger the triggerType event before the change (TOUCH, RELEASE) events - // but the END event should be at last - if(eventType != EVENT_END) { - handler.call(Detection, evData); - } + /** + * Determine the size of text on the axis (both major and minor axis). + * The size is calculated only once and then cached in this.props. + * @private + */ + DataAxis.prototype._calculateCharSize = function () { + // determine the char width and height on the minor axis + if (!('minorCharHeight' in this.props)) { + var textMinor = document.createTextNode('0'); + var measureCharMinor = document.createElement('div'); + measureCharMinor.className = 'yAxis minor measure'; + measureCharMinor.appendChild(textMinor); + this.dom.frame.appendChild(measureCharMinor); - // trigger a change (TOUCH, RELEASE) event, this means the length of the touches changed - if(triggerChange) { - evData.changedLength = changedLength; - evData.eventType = triggerChange; + this.props.minorCharHeight = measureCharMinor.clientHeight; + this.props.minorCharWidth = measureCharMinor.clientWidth; - handler.call(Detection, evData); + this.dom.frame.removeChild(measureCharMinor); + } - evData.eventType = triggerType; - delete evData.changedLength; - } + if (!('majorCharHeight' in this.props)) { + var textMajor = document.createTextNode('0'); + var measureCharMajor = document.createElement('div'); + measureCharMajor.className = 'yAxis major measure'; + measureCharMajor.appendChild(textMajor); + this.dom.frame.appendChild(measureCharMajor); - // trigger the END event - if(triggerType == EVENT_END) { - handler.call(Detection, evData); + this.props.majorCharHeight = measureCharMajor.clientHeight; + this.props.majorCharWidth = measureCharMajor.clientWidth; - // ...and we are done with the detection - // so reset everything to start each detection totally fresh - this.started = false; - } + this.dom.frame.removeChild(measureCharMajor); + } - return triggerType; - }, + if (!('titleCharHeight' in this.props)) { + var textTitle = document.createTextNode('0'); + var measureCharTitle = document.createElement('div'); + measureCharTitle.className = 'yAxis title measure'; + measureCharTitle.appendChild(textTitle); + this.dom.frame.appendChild(measureCharTitle); - /** - * we have different events for each device/browser - * determine what we need and set them in the EVENT_TYPES constant - * the `onTouch` method is bind to these properties. - * @method determineEventTypes - * @return {Object} events - */ - determineEventTypes: function determineEventTypes() { - var types; - if(Hammer.HAS_POINTEREVENTS) { - if(window.PointerEvent) { - types = [ - 'pointerdown', - 'pointermove', - 'pointerup pointercancel lostpointercapture' - ]; - } else { - types = [ - 'MSPointerDown', - 'MSPointerMove', - 'MSPointerUp MSPointerCancel MSLostPointerCapture' - ]; - } - } else if(Hammer.NO_MOUSEEVENTS) { - types = [ - 'touchstart', - 'touchmove', - 'touchend touchcancel' - ]; - } else { - types = [ - 'touchstart mousedown', - 'touchmove mousemove', - 'touchend touchcancel mouseup' - ]; - } + this.props.titleCharHeight = measureCharTitle.clientHeight; + this.props.titleCharWidth = measureCharTitle.clientWidth; - EVENT_TYPES[EVENT_START] = types[0]; - EVENT_TYPES[EVENT_MOVE] = types[1]; - EVENT_TYPES[EVENT_END] = types[2]; - return EVENT_TYPES; - }, + this.dom.frame.removeChild(measureCharTitle); + } + }; - /** - * create touchList depending on the event - * @method getTouchList - * @param {Object} ev - * @param {String} eventType - * @return {Array} touches - */ - getTouchList: function getTouchList(ev, eventType) { - // get the fake pointerEvent touchlist - if(Hammer.HAS_POINTEREVENTS) { - return PointerEvent.getTouchList(); - } + /** + * Snap a date to a rounded value. + * The snap intervals are dependent on the current scale and step. + * @param {Date} date the date to be snapped. + * @return {Date} snappedDate + */ + DataAxis.prototype.snap = function(date) { + return this.step.snap(date); + }; - // get the touchlist - if(ev.touches) { - if(eventType == EVENT_MOVE) { - return ev.touches; - } + module.exports = DataAxis; - var identifiers = []; - var concat = [].concat(Utils.toArray(ev.touches), Utils.toArray(ev.changedTouches)); - var touchList = []; - Utils.each(concat, function(touch) { - if(Utils.inArray(identifiers, touch.identifier) === false) { - touchList.push(touch); - } - identifiers.push(touch.identifier); - }); +/***/ }, +/* 24 */ +/***/ function(module, exports, __webpack_require__) { - return touchList; - } + var util = __webpack_require__(1); + var DOMutil = __webpack_require__(2); + var Line = __webpack_require__(51); + var Bar = __webpack_require__(52); + var Points = __webpack_require__(53); - // make fake touchList from mouse position - ev.identifier = 1; - return [ev]; - }, - - /** - * collect basic event data - * @method collectEventData - * @param {HTMLElement} element - * @param {String} eventType matches `EVENT_START|MOVE|END` - * @param {Array} touches - * @param {Object} ev - * @return {Object} ev - */ - collectEventData: function collectEventData(element, eventType, touches, ev) { - // find out pointerType - var pointerType = POINTER_TOUCH; - if(Utils.inStr(ev.type, 'mouse') || PointerEvent.matchType(POINTER_MOUSE, ev)) { - pointerType = POINTER_MOUSE; - } else if(PointerEvent.matchType(POINTER_PEN, ev)) { - pointerType = POINTER_PEN; - } - - return { - center: Utils.getCenter(touches), - timeStamp: Date.now(), - target: ev.target, - touches: touches, - eventType: eventType, - pointerType: pointerType, - srcEvent: ev, - - /** - * prevent the browser default actions - * mostly used to disable scrolling of the browser - */ - preventDefault: function() { - var srcEvent = this.srcEvent; - srcEvent.preventManipulation && srcEvent.preventManipulation(); - srcEvent.preventDefault && srcEvent.preventDefault(); - }, + /** + * /** + * @param {object} group | the object of the group from the dataset + * @param {string} groupId | ID of the group + * @param {object} options | the default options + * @param {array} groupsUsingDefaultStyles | this array has one entree. + * It is passed as an array so it is passed by reference. + * It enumerates through the default styles + * @constructor + */ + function GraphGroup (group, groupId, options, groupsUsingDefaultStyles) { + this.id = groupId; + var fields = ['sampling','style','sort','yAxisOrientation','barChart','drawPoints','shaded','catmullRom'] + this.options = util.selectiveBridgeObject(fields,options); + this.usingDefaultStyle = group.className === undefined; + this.groupsUsingDefaultStyles = groupsUsingDefaultStyles; + this.zeroPosition = 0; + this.update(group); + if (this.usingDefaultStyle == true) { + this.groupsUsingDefaultStyles[0] += 1; + } + this.itemsData = []; + this.visible = group.visible === undefined ? true : group.visible; + } - /** - * stop bubbling the event up to its parents - */ - stopPropagation: function() { - this.srcEvent.stopPropagation(); - }, - /** - * immediately stop gesture detection - * might be useful after a swipe was detected - * @return {*} - */ - stopDetect: function() { - return Detection.stopDetect(); - } - }; + /** + * this loads a reference to all items in this group into this group. + * @param {array} items + */ + GraphGroup.prototype.setItems = function(items) { + if (items != null) { + this.itemsData = items; + if (this.options.sort == true) { + this.itemsData.sort(function (a,b) {return a.x - b.x;}) } + } + else { + this.itemsData = []; + } }; /** - * @module hammer - * - * @class PointerEvent - * @static + * this is used for plotting barcharts, this way, we only have to calculate it once. + * @param pos */ - var PointerEvent = Hammer.PointerEvent = { - /** - * holds all pointers, by `identifier` - * @property pointers - * @type {Object} - */ - pointers: {}, + GraphGroup.prototype.setZeroPosition = function(pos) { + this.zeroPosition = pos; + }; - /** - * get the pointers as an array - * @method getTouchList - * @return {Array} touchlist - */ - getTouchList: function getTouchList() { - var touchlist = []; - // we can use forEach since pointerEvents only is in IE10 - Utils.each(this.pointers, function(pointer) { - touchlist.push(pointer); - }); - return touchlist; - }, - /** - * update the position of a pointer - * @method updatePointer - * @param {String} eventType matches `EVENT_START|MOVE|END` - * @param {Object} pointerEvent - */ - updatePointer: function updatePointer(eventType, pointerEvent) { - if(eventType == EVENT_END || (eventType != EVENT_END && pointerEvent.buttons !== 1)) { - delete this.pointers[pointerEvent.pointerId]; - } else { - pointerEvent.identifier = pointerEvent.pointerId; - this.pointers[pointerEvent.pointerId] = pointerEvent; - } - }, + /** + * set the options of the graph group over the default options. + * @param options + */ + GraphGroup.prototype.setOptions = function(options) { + if (options !== undefined) { + var fields = ['sampling','style','sort','yAxisOrientation','barChart']; + util.selectiveDeepExtend(fields, this.options, options); - /** - * check if ev matches pointertype - * @method matchType - * @param {String} pointerType matches `POINTER_MOUSE|TOUCH|PEN` - * @param {PointerEvent} ev - */ - matchType: function matchType(pointerType, ev) { - if(!ev.pointerType) { - return false; + util.mergeOptions(this.options, options,'catmullRom'); + util.mergeOptions(this.options, options,'drawPoints'); + util.mergeOptions(this.options, options,'shaded'); + + if (options.catmullRom) { + if (typeof options.catmullRom == 'object') { + if (options.catmullRom.parametrization) { + if (options.catmullRom.parametrization == 'uniform') { + this.options.catmullRom.alpha = 0; + } + else if (options.catmullRom.parametrization == 'chordal') { + this.options.catmullRom.alpha = 1.0; + } + else { + this.options.catmullRom.parametrization = 'centripetal'; + this.options.catmullRom.alpha = 0.5; + } } + } + } + } - var pt = ev.pointerType, - types = {}; + if (this.options.style == 'line') { + this.type = new Line(this.id, this.options); + } + else if (this.options.style == 'bar') { + this.type = new Bar(this.id, this.options); + } + else if (this.options.style == 'points') { + this.type = new Points(this.id, this.options); + } + }; - types[POINTER_MOUSE] = (pt === (ev.MSPOINTER_TYPE_MOUSE || POINTER_MOUSE)); - types[POINTER_TOUCH] = (pt === (ev.MSPOINTER_TYPE_TOUCH || POINTER_TOUCH)); - types[POINTER_PEN] = (pt === (ev.MSPOINTER_TYPE_PEN || POINTER_PEN)); - return types[pointerType]; - }, - /** - * reset the stored pointers - * @method reset - */ - reset: function resetList() { - this.pointers = {}; - } + /** + * this updates the current group class with the latest group dataset entree, used in _updateGroup in linegraph + * @param group + */ + GraphGroup.prototype.update = function(group) { + this.group = group; + this.content = group.content || 'graph'; + this.className = group.className || this.className || "graphGroup" + this.groupsUsingDefaultStyles[0] % 10; + this.visible = group.visible === undefined ? true : group.visible; + this.style = group.style; + this.setOptions(group.options); }; /** - * @module hammer + * draw the icon for the legend. * - * @class Detection - * @static + * @param x + * @param y + * @param JSONcontainer + * @param SVGcontainer + * @param iconWidth + * @param iconHeight */ - var Detection = Hammer.detection = { - // contains all registred Hammer.gestures in the correct order - gestures: [], + GraphGroup.prototype.drawIcon = function(x, y, JSONcontainer, SVGcontainer, iconWidth, iconHeight) { + var fillHeight = iconHeight * 0.5; + var path, fillPath; - // data of the current Hammer.gesture detection session - current: null, + var outline = DOMutil.getSVGElement("rect", JSONcontainer, SVGcontainer); + outline.setAttributeNS(null, "x", x); + outline.setAttributeNS(null, "y", y - fillHeight); + outline.setAttributeNS(null, "width", iconWidth); + outline.setAttributeNS(null, "height", 2*fillHeight); + outline.setAttributeNS(null, "class", "outline"); - // the previous Hammer.gesture session data - // is a full clone of the previous gesture.current object - previous: null, + if (this.options.style == 'line') { + path = DOMutil.getSVGElement("path", JSONcontainer, SVGcontainer); + path.setAttributeNS(null, "class", this.className); + if(this.style !== undefined) { + path.setAttributeNS(null, "style", this.style); + } - // when this becomes true, no gestures are fired - stopped: false, + path.setAttributeNS(null, "d", "M" + x + ","+y+" L" + (x + iconWidth) + ","+y+""); + if (this.options.shaded.enabled == true) { + fillPath = DOMutil.getSVGElement("path", JSONcontainer, SVGcontainer); + if (this.options.shaded.orientation == 'top') { + fillPath.setAttributeNS(null, "d", "M"+x+", " + (y - fillHeight) + + "L"+x+","+y+" L"+ (x + iconWidth) + ","+y+" L"+ (x + iconWidth) + "," + (y - fillHeight)); + } + else { + fillPath.setAttributeNS(null, "d", "M"+x+","+y+" " + + "L"+x+"," + (y + fillHeight) + " " + + "L"+ (x + iconWidth) + "," + (y + fillHeight) + + "L"+ (x + iconWidth) + ","+y); + } + fillPath.setAttributeNS(null, "class", this.className + " iconFill"); + } - /** - * start Hammer.gesture detection - * @method startDetect - * @param {Hammer.Instance} inst - * @param {Object} eventData - */ - startDetect: function startDetect(inst, eventData) { - // already busy with a Hammer.gesture detection on an element - if(this.current) { - return; - } + if (this.options.drawPoints.enabled == true) { + DOMutil.drawPoint(x + 0.5 * iconWidth,y, this, JSONcontainer, SVGcontainer); + } + } + else { + var barWidth = Math.round(0.3 * iconWidth); + var bar1Height = Math.round(0.4 * iconHeight); + var bar2Height = Math.round(0.75 * iconHeight); - this.stopped = false; + var offset = Math.round((iconWidth - (2 * barWidth))/3); - // holds current session - this.current = { - inst: inst, // reference to HammerInstance we're working for - startEvent: Utils.extend({}, eventData), // start eventData for distances, timing etc - lastEvent: false, // last eventData - lastCalcEvent: false, // last eventData for calculations. - futureCalcEvent: false, // last eventData for calculations. - lastCalcData: {}, // last lastCalcData - name: '' // current gesture we're in/detected, can be 'tap', 'hold' etc - }; + DOMutil.drawBar(x + 0.5*barWidth + offset , y + fillHeight - bar1Height - 1, barWidth, bar1Height, this.className + ' bar', JSONcontainer, SVGcontainer); + DOMutil.drawBar(x + 1.5*barWidth + offset + 2, y + fillHeight - bar2Height - 1, barWidth, bar2Height, this.className + ' bar', JSONcontainer, SVGcontainer); + } + }; - this.detect(eventData); - }, - /** - * Hammer.gesture detection - * @method detect - * @param {Object} eventData - * @return {any} - */ - detect: function detect(eventData) { - if(!this.current || this.stopped) { - return; - } + /** + * return the legend entree for this group. + * + * @param iconWidth + * @param iconHeight + * @returns {{icon: HTMLElement, label: (group.content|*|string), orientation: (.options.yAxisOrientation|*)}} + */ + GraphGroup.prototype.getLegend = function(iconWidth, iconHeight) { + var svg = document.createElementNS('http://www.w3.org/2000/svg',"svg"); + this.drawIcon(0,0.5*iconHeight,[],svg,iconWidth,iconHeight); + return {icon: svg, label: this.content, orientation:this.options.yAxisOrientation}; + } - // extend event data with calculations about scale, distance etc - eventData = this.extendEventData(eventData); + GraphGroup.prototype.getYRange = function(groupData) { + return this.type.getYRange(groupData); + } - // hammer instance and instance options - var inst = this.current.inst, - instOptions = inst.options; + GraphGroup.prototype.draw = function(dataset, group, framework) { + this.type.draw(dataset, group, framework); + } - // call Hammer.gesture handlers - Utils.each(this.gestures, function triggerGesture(gesture) { - // only when the instance options have enabled this gesture - if(!this.stopped && inst.enabled && instOptions[gesture.name]) { - gesture.handler.call(gesture, eventData, inst); - } - }, this); - // store as previous event event - if(this.current) { - this.current.lastEvent = eventData; - } + module.exports = GraphGroup; - if(eventData.eventType == EVENT_END) { - this.stopDetect(); - } - return eventData; - }, +/***/ }, +/* 25 */ +/***/ function(module, exports, __webpack_require__) { - /** - * clear the Hammer.gesture vars - * this is called on endDetect, but can also be used when a final Hammer.gesture has been detected - * to stop other Hammer.gestures from being fired - * @method stopDetect - */ - stopDetect: function stopDetect() { - // clone current data to the store as the previous gesture - // used for the double tap gesture, since this is an other gesture detect session - this.previous = Utils.extend({}, this.current); + var util = __webpack_require__(1); + var stack = __webpack_require__(18); + var RangeItem = __webpack_require__(35); - // reset the current - this.current = null; - this.stopped = true; - }, + /** + * @constructor Group + * @param {Number | String} groupId + * @param {Object} data + * @param {ItemSet} itemSet + */ + function Group (groupId, data, itemSet) { + this.groupId = groupId; + this.subgroups = {}; + this.subgroupIndex = 0; + this.subgroupOrderer = data && data.subgroupOrder; + this.itemSet = itemSet; - /** - * calculate velocity, angle and direction - * @method getVelocityData - * @param {Object} ev - * @param {Object} center - * @param {Number} deltaTime - * @param {Number} deltaX - * @param {Number} deltaY - */ - getCalculatedData: function getCalculatedData(ev, center, deltaTime, deltaX, deltaY) { - var cur = this.current, - recalc = false, - calcEv = cur.lastCalcEvent, - calcData = cur.lastCalcData; + this.dom = {}; + this.props = { + label: { + width: 0, + height: 0 + } + }; + this.className = null; - if(calcEv && ev.timeStamp - calcEv.timeStamp > Hammer.CALCULATE_INTERVAL) { - center = calcEv.center; - deltaTime = ev.timeStamp - calcEv.timeStamp; - deltaX = ev.center.clientX - calcEv.center.clientX; - deltaY = ev.center.clientY - calcEv.center.clientY; - recalc = true; - } + this.items = {}; // items filtered by groupId of this group + this.visibleItems = []; // items currently visible in window + this.orderedItems = { + byStart: [], + byEnd: [] + }; + this.checkRangedItems = false; // needed to refresh the ranged items if the window is programatically changed with NO overlap. + var me = this; + this.itemSet.body.emitter.on("checkRangedItems", function () { + me.checkRangedItems = true; + }) - if(ev.eventType == EVENT_TOUCH || ev.eventType == EVENT_RELEASE) { - cur.futureCalcEvent = ev; - } + this._create(); - if(!cur.lastCalcEvent || recalc) { - calcData.velocity = Utils.getVelocity(deltaTime, deltaX, deltaY); - calcData.angle = Utils.getAngle(center, ev.center); - calcData.direction = Utils.getDirection(center, ev.center); + this.setData(data); + } - cur.lastCalcEvent = cur.futureCalcEvent || ev; - cur.futureCalcEvent = ev; - } + /** + * Create DOM elements for the group + * @private + */ + Group.prototype._create = function() { + var label = document.createElement('div'); + label.className = 'vlabel'; + this.dom.label = label; - ev.velocityX = calcData.velocity.x; - ev.velocityY = calcData.velocity.y; - ev.interimAngle = calcData.angle; - ev.interimDirection = calcData.direction; - }, + var inner = document.createElement('div'); + inner.className = 'inner'; + label.appendChild(inner); + this.dom.inner = inner; - /** - * extend eventData for Hammer.gestures - * @method extendEventData - * @param {Object} ev - * @return {Object} ev - */ - extendEventData: function extendEventData(ev) { - var cur = this.current, - startEv = cur.startEvent, - lastEv = cur.lastEvent || startEv; + var foreground = document.createElement('div'); + foreground.className = 'group'; + foreground['timeline-group'] = this; + this.dom.foreground = foreground; - // update the start touchlist to calculate the scale/rotation - if(ev.eventType == EVENT_TOUCH || ev.eventType == EVENT_RELEASE) { - startEv.touches = []; - Utils.each(ev.touches, function(touch) { - startEv.touches.push({ - clientX: touch.clientX, - clientY: touch.clientY - }); - }); - } + this.dom.background = document.createElement('div'); + this.dom.background.className = 'group'; - var deltaTime = ev.timeStamp - startEv.timeStamp, - deltaX = ev.center.clientX - startEv.center.clientX, - deltaY = ev.center.clientY - startEv.center.clientY; + this.dom.axis = document.createElement('div'); + this.dom.axis.className = 'group'; - this.getCalculatedData(ev, lastEv.center, deltaTime, deltaX, deltaY); + // create a hidden marker to detect when the Timelines container is attached + // to the DOM, or the style of a parent of the Timeline is changed from + // display:none is changed to visible. + this.dom.marker = document.createElement('div'); + this.dom.marker.style.visibility = 'hidden'; // TODO: ask jos why this is not none? + this.dom.marker.innerHTML = '?'; + this.dom.background.appendChild(this.dom.marker); + }; - Utils.extend(ev, { - startEvent: startEv, + /** + * Set the group data for this group + * @param {Object} data Group data, can contain properties content and className + */ + Group.prototype.setData = function(data) { + // update contents + var content = data && data.content; + if (content instanceof Element) { + this.dom.inner.appendChild(content); + } + else if (content !== undefined && content !== null) { + this.dom.inner.innerHTML = content; + } + else { + this.dom.inner.innerHTML = this.groupId || ''; // groupId can be null + } - deltaTime: deltaTime, - deltaX: deltaX, - deltaY: deltaY, + // update title + this.dom.label.title = data && data.title || ''; - distance: Utils.getDistance(startEv.center, ev.center), - angle: Utils.getAngle(startEv.center, ev.center), - direction: Utils.getDirection(startEv.center, ev.center), - scale: Utils.getScale(startEv.touches, ev.touches), - rotation: Utils.getRotation(startEv.touches, ev.touches) - }); + if (!this.dom.inner.firstChild) { + util.addClassName(this.dom.inner, 'hidden'); + } + else { + util.removeClassName(this.dom.inner, 'hidden'); + } - return ev; - }, + // update className + var className = data && data.className || null; + if (className != this.className) { + if (this.className) { + util.removeClassName(this.dom.label, this.className); + util.removeClassName(this.dom.foreground, this.className); + util.removeClassName(this.dom.background, this.className); + util.removeClassName(this.dom.axis, this.className); + } + util.addClassName(this.dom.label, className); + util.addClassName(this.dom.foreground, className); + util.addClassName(this.dom.background, className); + util.addClassName(this.dom.axis, className); + this.className = className; + } - /** - * register new gesture - * @method register - * @param {Object} gesture object, see `gestures/` for documentation - * @return {Array} gestures - */ - register: function register(gesture) { - // add an enable gesture options if there is no given - var options = gesture.defaults || {}; - if(options[gesture.name] === undefined) { - options[gesture.name] = true; - } + // update style + if (this.style) { + util.removeCssText(this.dom.label, this.style); + this.style = null; + } + if (data && data.style) { + util.addCssText(this.dom.label, data.style); + this.style = data.style; + } + }; - // extend Hammer default options with the Hammer.gesture options - Utils.extend(Hammer.defaults, options, true); + /** + * Get the width of the group label + * @return {number} width + */ + Group.prototype.getLabelWidth = function() { + return this.props.label.width; + }; - // set its index - gesture.index = gesture.index || 1000; - // add Hammer.gesture to the list - this.gestures.push(gesture); + /** + * Repaint this group + * @param {{start: number, end: number}} range + * @param {{item: {horizontal: number, vertical: number}, axis: number}} margin + * @param {boolean} [restack=false] Force restacking of all items + * @return {boolean} Returns true if the group is resized + */ + Group.prototype.redraw = function(range, margin, restack) { + var resized = false; - // sort the list by index - this.gestures.sort(function(a, b) { - if(a.index < b.index) { - return -1; - } - if(a.index > b.index) { - return 1; - } - return 0; - }); + this.visibleItems = this._updateVisibleItems(this.orderedItems, this.visibleItems, range); - return this.gestures; - } - }; + // force recalculation of the height of the items when the marker height changed + // (due to the Timeline being attached to the DOM or changed from display:none to visible) + var markerHeight = this.dom.marker.clientHeight; + if (markerHeight != this.lastMarkerHeight) { + this.lastMarkerHeight = markerHeight; + util.forEach(this.items, function (item) { + item.dirty = true; + if (item.displayed) item.redraw(); + }); - /** - * @module hammer - */ + restack = true; + } - /** - * create new hammer instance - * all methods should return the instance itself, so it is chainable. - * - * @class Instance - * @constructor - * @param {HTMLElement} element - * @param {Object} [options={}] options are merged with `Hammer.defaults` - * @return {Hammer.Instance} - */ - Hammer.Instance = function(element, options) { - var self = this; + // reposition visible items vertically + if (this.itemSet.options.stack) { // TODO: ugly way to access options... + stack.stack(this.visibleItems, margin, restack); + } + else { // no stacking + stack.nostack(this.visibleItems, margin, this.subgroups); + } - // setup HammerJS window events and register all gestures - // this also sets up the default options - setup(); + // recalculate the height of the group + var height = this._calculateHeight(margin); - /** - * @property element - * @type {HTMLElement} - */ - this.element = element; + // calculate actual size and position + var foreground = this.dom.foreground; + this.top = foreground.offsetTop; + this.left = foreground.offsetLeft; + this.width = foreground.offsetWidth; + resized = util.updateProperty(this, 'height', height) || resized; - /** - * @property enabled - * @type {Boolean} - * @protected - */ - this.enabled = true; + // recalculate size of label + resized = util.updateProperty(this.props.label, 'width', this.dom.inner.clientWidth) || resized; + resized = util.updateProperty(this.props.label, 'height', this.dom.inner.clientHeight) || resized; - /** - * options, merged with the defaults - * options with an _ are converted to camelCase - * @property options - * @type {Object} - */ - Utils.each(options, function(value, name) { - delete options[name]; - options[Utils.toCamelCase(name)] = value; - }); + // apply new height + this.dom.background.style.height = height + 'px'; + this.dom.foreground.style.height = height + 'px'; + this.dom.label.style.height = height + 'px'; - this.options = Utils.extend(Utils.extend({}, Hammer.defaults), options || {}); + // update vertical position of items after they are re-stacked and the height of the group is calculated + for (var i = 0, ii = this.visibleItems.length; i < ii; i++) { + var item = this.visibleItems[i]; + item.repositionY(margin); + } - // add some css to the element to prevent the browser from doing its native behavoir - if(this.options.behavior) { - Utils.toggleBehavior(this.element, this.options.behavior, true); - } + return resized; + }; - /** - * event start handler on the element to start the detection - * @property eventStartHandler - * @type {Object} - */ - this.eventStartHandler = Event.onTouch(element, EVENT_START, function(ev) { - if(self.enabled && ev.eventType == EVENT_START) { - Detection.startDetect(self, ev); - } else if(ev.eventType == EVENT_TOUCH) { - Detection.detect(ev); - } + /** + * recalculate the height of the group + * @param {{item: {horizontal: number, vertical: number}, axis: number}} margin + * @returns {number} Returns the height + * @private + */ + Group.prototype._calculateHeight = function (margin) { + // recalculate the height of the group + var height; + var visibleItems = this.visibleItems; + //var visibleSubgroups = []; + //this.visibleSubgroups = 0; + this.resetSubgroups(); + var me = this; + if (visibleItems.length) { + var min = visibleItems[0].top; + var max = visibleItems[0].top + visibleItems[0].height; + util.forEach(visibleItems, function (item) { + min = Math.min(min, item.top); + max = Math.max(max, (item.top + item.height)); + if (item.data.subgroup !== undefined) { + me.subgroups[item.data.subgroup].height = Math.max(me.subgroups[item.data.subgroup].height,item.height); + me.subgroups[item.data.subgroup].visible = true; + //if (visibleSubgroups.indexOf(item.data.subgroup) == -1){ + // visibleSubgroups.push(item.data.subgroup); + // me.visibleSubgroups += 1; + //} + } }); + if (min > margin.axis) { + // there is an empty gap between the lowest item and the axis + var offset = min - margin.axis; + max -= offset; + util.forEach(visibleItems, function (item) { + item.top -= offset; + }); + } + height = max + margin.item.vertical / 2; + } + else { + height = margin.axis + margin.item.vertical; + } + height = Math.max(height, this.props.label.height); - /** - * keep a list of user event handlers which needs to be removed when calling 'dispose' - * @property eventHandlers - * @type {Array} - */ - this.eventHandlers = []; + return height; }; - Hammer.Instance.prototype = { - /** - * bind events to the instance - * @method on - * @chainable - * @param {String} gestures multiple gestures by splitting with a space - * @param {Function} handler - * @param {Object} handler.ev event object - */ - on: function onEvent(gestures, handler) { - var self = this; - Event.on(self.element, gestures, handler, function(type) { - self.eventHandlers.push({ gesture: type, handler: handler }); - }); - return self; - }, - - /** - * unbind events to the instance - * @method off - * @chainable - * @param {String} gestures - * @param {Function} handler - */ - off: function offEvent(gestures, handler) { - var self = this; + /** + * Show this group: attach to the DOM + */ + Group.prototype.show = function() { + if (!this.dom.label.parentNode) { + this.itemSet.dom.labelSet.appendChild(this.dom.label); + } - Event.off(self.element, gestures, handler, function(type) { - var index = Utils.inArray({ gesture: type, handler: handler }); - if(index !== false) { - self.eventHandlers.splice(index, 1); - } - }); - return self; - }, + if (!this.dom.foreground.parentNode) { + this.itemSet.dom.foreground.appendChild(this.dom.foreground); + } - /** - * trigger gesture event - * @method trigger - * @chainable - * @param {String} gesture - * @param {Object} [eventData] - */ - trigger: function triggerEvent(gesture, eventData) { - // optional - if(!eventData) { - eventData = {}; - } + if (!this.dom.background.parentNode) { + this.itemSet.dom.background.appendChild(this.dom.background); + } - // create DOM event - var event = Hammer.DOCUMENT.createEvent('Event'); - event.initEvent(gesture, true, true); - event.gesture = eventData; + if (!this.dom.axis.parentNode) { + this.itemSet.dom.axis.appendChild(this.dom.axis); + } + }; - // trigger on the target if it is in the instance element, - // this is for event delegation tricks - var element = this.element; - if(Utils.hasParent(eventData.target, element)) { - element = eventData.target; - } + /** + * Hide this group: remove from the DOM + */ + Group.prototype.hide = function() { + var label = this.dom.label; + if (label.parentNode) { + label.parentNode.removeChild(label); + } - element.dispatchEvent(event); - return this; - }, + var foreground = this.dom.foreground; + if (foreground.parentNode) { + foreground.parentNode.removeChild(foreground); + } - /** - * enable of disable hammer.js detection - * @method enable - * @chainable - * @param {Boolean} state - */ - enable: function enable(state) { - this.enabled = state; - return this; - }, + var background = this.dom.background; + if (background.parentNode) { + background.parentNode.removeChild(background); + } - /** - * dispose this hammer instance - * @method dispose - * @return {Null} - */ - dispose: function dispose() { - var i, eh; + var axis = this.dom.axis; + if (axis.parentNode) { + axis.parentNode.removeChild(axis); + } + }; - // undo all changes made by stop_browser_behavior - Utils.toggleBehavior(this.element, this.options.behavior, false); + /** + * Add an item to the group + * @param {Item} item + */ + Group.prototype.add = function(item) { + this.items[item.id] = item; + item.setParent(this); - // unbind all custom event handlers - for(i = -1; (eh = this.eventHandlers[++i]);) { - Utils.off(this.element, eh.gesture, eh.handler); - } + // add to + if (item.data.subgroup !== undefined) { + if (this.subgroups[item.data.subgroup] === undefined) { + this.subgroups[item.data.subgroup] = {height:0, visible: false, index:this.subgroupIndex, items: []}; + this.subgroupIndex++; + } + this.subgroups[item.data.subgroup].items.push(item); + } + this.orderSubgroups(); - this.eventHandlers = []; + if (this.visibleItems.indexOf(item) == -1) { + var range = this.itemSet.body.range; // TODO: not nice accessing the range like this + this._checkIfVisible(item, this.visibleItems, range); + } + }; - // unbind the start event listener - Event.off(this.element, EVENT_TYPES[EVENT_START], this.eventStartHandler); + Group.prototype.orderSubgroups = function() { + if (this.subgroupOrderer !== undefined) { + var sortArray = []; + if (typeof this.subgroupOrderer == 'string') { + for (var subgroup in this.subgroups) { + sortArray.push({subgroup: subgroup, sortField: this.subgroups[subgroup].items[0].data[this.subgroupOrderer]}) + } + sortArray.sort(function (a, b) { + return a.sortField - b.sortField; + }) + } + else if (typeof this.subgroupOrderer == 'function') { + for (var subgroup in this.subgroups) { + sortArray.push(this.subgroups[subgroup].items[0].data); + } + sortArray.sort(this.subgroupOrderer); + } - return null; + if (sortArray.length > 0) { + for (var i = 0; i < sortArray.length; i++) { + this.subgroups[sortArray[i].subgroup].index = i; + } } + } }; + Group.prototype.resetSubgroups = function() { + for (var subgroup in this.subgroups) { + if (this.subgroups.hasOwnProperty(subgroup)) { + this.subgroups[subgroup].visible = false; + } + } + }; /** - * @module gestures - */ - /** - * Move with x fingers (default 1) around on the page. - * Preventing the default browser behavior is a good way to improve feel and working. - * ```` - * hammertime.on("drag", function(ev) { - * console.log(ev); - * ev.gesture.preventDefault(); - * }); - * ```` - * - * @class Drag - * @static - */ - /** - * @event drag - * @param {Object} ev - */ - /** - * @event dragstart - * @param {Object} ev - */ - /** - * @event dragend - * @param {Object} ev - */ - /** - * @event drapleft - * @param {Object} ev - */ - /** - * @event dragright - * @param {Object} ev - */ - /** - * @event dragup - * @param {Object} ev - */ - /** - * @event dragdown - * @param {Object} ev + * Remove an item from the group + * @param {Item} item */ + Group.prototype.remove = function(item) { + delete this.items[item.id]; + item.setParent(null); + + // remove from visible items + var index = this.visibleItems.indexOf(item); + if (index != -1) this.visibleItems.splice(index, 1); + + // TODO: also remove from ordered items? + }; + /** - * @param {String} name + * Remove an item from the corresponding DataSet + * @param {Item} item */ - (function(name) { - var triggered = false; + Group.prototype.removeFromDataSet = function(item) { + this.itemSet.removeItem(item.id); + }; - function dragGesture(ev, inst) { - var cur = Detection.current; - // max touches - if(inst.options.dragMaxTouches > 0 && - ev.touches.length > inst.options.dragMaxTouches) { - return; - } + /** + * Reorder the items + */ + Group.prototype.order = function() { + var array = util.toArray(this.items); + var startArray = []; + var endArray = []; - switch(ev.eventType) { - case EVENT_START: - triggered = false; - break; + for (var i = 0; i < array.length; i++) { + if (array[i].data.end !== undefined) { + endArray.push(array[i]); + } + startArray.push(array[i]); + } + this.orderedItems = { + byStart: startArray, + byEnd: endArray + }; - case EVENT_MOVE: - // when the distance we moved is too small we skip this gesture - // or we can be already in dragging - if(ev.distance < inst.options.dragMinDistance && - cur.name != name) { - return; - } + stack.orderByStart(this.orderedItems.byStart); + stack.orderByEnd(this.orderedItems.byEnd); + }; - var startCenter = cur.startEvent.center; - // we are dragging! - if(cur.name != name) { - cur.name = name; - if(inst.options.dragDistanceCorrection && ev.distance > 0) { - // When a drag is triggered, set the event center to dragMinDistance pixels from the original event center. - // Without this correction, the dragged distance would jumpstart at dragMinDistance pixels instead of at 0. - // It might be useful to save the original start point somewhere - var factor = Math.abs(inst.options.dragMinDistance / ev.distance); - startCenter.pageX += ev.deltaX * factor; - startCenter.pageY += ev.deltaY * factor; - startCenter.clientX += ev.deltaX * factor; - startCenter.clientY += ev.deltaY * factor; - - // recalculate event data using new start point - ev = Detection.extendEventData(ev); - } - } - - // lock drag to axis? - if(cur.lastEvent.dragLockToAxis || - ( inst.options.dragLockToAxis && - inst.options.dragLockMinDistance <= ev.distance - )) { - ev.dragLockToAxis = true; - } - - // keep direction on the axis that the drag gesture started on - var lastDirection = cur.lastEvent.direction; - if(ev.dragLockToAxis && lastDirection !== ev.direction) { - if(Utils.isVertical(lastDirection)) { - ev.direction = (ev.deltaY < 0) ? DIRECTION_UP : DIRECTION_DOWN; - } else { - ev.direction = (ev.deltaX < 0) ? DIRECTION_LEFT : DIRECTION_RIGHT; - } - } - - // first time, trigger dragstart event - if(!triggered) { - inst.trigger(name + 'start', ev); - triggered = true; - } + /** + * Update the visible items + * @param {{byStart: Item[], byEnd: Item[]}} orderedItems All items ordered by start date and by end date + * @param {Item[]} visibleItems The previously visible items. + * @param {{start: number, end: number}} range Visible range + * @return {Item[]} visibleItems The new visible items. + * @private + */ + Group.prototype._updateVisibleItems = function(orderedItems, oldVisibleItems, range) { + var visibleItems = []; + var visibleItemsLookup = {}; // we keep this to quickly look up if an item already exists in the list without using indexOf on visibleItems + var interval = (range.end - range.start) / 4; + var lowerBound = range.start - interval; + var upperBound = range.end + interval; + var item, i; - // trigger events - inst.trigger(name, ev); - inst.trigger(name + ev.direction, ev); + // this function is used to do the binary search. + var searchFunction = function (value) { + if (value < lowerBound) {return -1;} + else if (value <= upperBound) {return 0;} + else {return 1;} + } - var isVertical = Utils.isVertical(ev.direction); + // first check if the items that were in view previously are still in view. + // IMPORTANT: this handles the case for the items with startdate before the window and enddate after the window! + // also cleans up invisible items. + if (oldVisibleItems.length > 0) { + for (i = 0; i < oldVisibleItems.length; i++) { + this._checkIfVisibleWithReference(oldVisibleItems[i], visibleItems, visibleItemsLookup, range); + } + } - // block the browser events - if((inst.options.dragBlockVertical && isVertical) || - (inst.options.dragBlockHorizontal && !isVertical)) { - ev.preventDefault(); - } - break; + // we do a binary search for the items that have only start values. + var initialPosByStart = util.binarySearchCustom(orderedItems.byStart, searchFunction, 'data','start'); - case EVENT_RELEASE: - if(triggered && ev.changedLength <= inst.options.dragMaxTouches) { - inst.trigger(name + 'end', ev); - triggered = false; - } - break; + // trace the visible items from the inital start pos both ways until an invisible item is found, we only look at the start values. + this._traceVisible(initialPosByStart, orderedItems.byStart, visibleItems, visibleItemsLookup, function (item) { + return (item.data.start < lowerBound || item.data.start > upperBound); + }); - case EVENT_END: - triggered = false; - break; - } + // if the window has changed programmatically without overlapping the old window, the ranged items with start < lowerBound and end > upperbound are not shown. + // We therefore have to brute force check all items in the byEnd list + if (this.checkRangedItems == true) { + this.checkRangedItems = false; + for (i = 0; i < orderedItems.byEnd.length; i++) { + this._checkIfVisibleWithReference(orderedItems.byEnd[i], visibleItems, visibleItemsLookup, range); } + } + else { + // we do a binary search for the items that have defined end times. + var initialPosByEnd = util.binarySearchCustom(orderedItems.byEnd, searchFunction, 'data','end'); - Hammer.gestures.Drag = { - name: name, - index: 50, - handler: dragGesture, - defaults: { - /** - * minimal movement that have to be made before the drag event gets triggered - * @property dragMinDistance - * @type {Number} - * @default 10 - */ - dragMinDistance: 10, + // trace the visible items from the inital start pos both ways until an invisible item is found, we only look at the end values. + this._traceVisible(initialPosByEnd, orderedItems.byEnd, visibleItems, visibleItemsLookup, function (item) { + return (item.data.end < lowerBound || item.data.end > upperBound); + }); + } - /** - * Set dragDistanceCorrection to true to make the starting point of the drag - * be calculated from where the drag was triggered, not from where the touch started. - * Useful to avoid a jerk-starting drag, which can make fine-adjustments - * through dragging difficult, and be visually unappealing. - * @property dragDistanceCorrection - * @type {Boolean} - * @default true - */ - dragDistanceCorrection: true, - /** - * set 0 for unlimited, but this can conflict with transform - * @property dragMaxTouches - * @type {Number} - * @default 1 - */ - dragMaxTouches: 1, + // finally, we reposition all the visible items. + for (i = 0; i < visibleItems.length; i++) { + item = visibleItems[i]; + if (!item.displayed) item.show(); + // reposition item horizontally + item.repositionX(); + } - /** - * prevent default browser behavior when dragging occurs - * be careful with it, it makes the element a blocking element - * when you are using the drag gesture, it is a good practice to set this true - * @property dragBlockHorizontal - * @type {Boolean} - * @default false - */ - dragBlockHorizontal: false, + // debug + //console.log("new line") + //if (this.groupId == null) { + // for (i = 0; i < orderedItems.byStart.length; i++) { + // item = orderedItems.byStart[i].data; + // console.log('start',i,initialPosByStart, item.start.valueOf(), item.content, item.start >= lowerBound && item.start <= upperBound,i == initialPosByStart ? "<------------------- HEREEEE" : "") + // } + // for (i = 0; i < orderedItems.byEnd.length; i++) { + // item = orderedItems.byEnd[i].data; + // console.log('rangeEnd',i,initialPosByEnd, item.end.valueOf(), item.content, item.end >= range.start && item.end <= range.end,i == initialPosByEnd ? "<------------------- HEREEEE" : "") + // } + //} - /** - * same as `dragBlockHorizontal`, but for vertical movement - * @property dragBlockVertical - * @type {Boolean} - * @default false - */ - dragBlockVertical: false, + return visibleItems; + }; - /** - * dragLockToAxis keeps the drag gesture on the axis that it started on, - * It disallows vertical directions if the initial direction was horizontal, and vice versa. - * @property dragLockToAxis - * @type {Boolean} - * @default false - */ - dragLockToAxis: false, + Group.prototype._traceVisible = function (initialPos, items, visibleItems, visibleItemsLookup, breakCondition) { + var item; + var i; - /** - * drag lock only kicks in when distance > dragLockMinDistance - * This way, locking occurs only when the distance has become large enough to reliably determine the direction - * @property dragLockMinDistance - * @type {Number} - * @default 25 - */ - dragLockMinDistance: 25 + if (initialPos != -1) { + for (i = initialPos; i >= 0; i--) { + item = items[i]; + if (breakCondition(item)) { + break; + } + else { + if (visibleItemsLookup[item.id] === undefined) { + visibleItemsLookup[item.id] = true; + visibleItems.push(item); } - }; - })('drag'); + } + } + + for (i = initialPos + 1; i < items.length; i++) { + item = items[i]; + if (breakCondition(item)) { + break; + } + else { + if (visibleItemsLookup[item.id] === undefined) { + visibleItemsLookup[item.id] = true; + visibleItems.push(item); + } + } + } + } + } + /** - * @module gestures - */ - /** - * trigger a simple gesture event, so you can do anything in your handler. - * only usable if you know what your doing... + * this function is very similar to the _checkIfInvisible() but it does not + * return booleans, hides the item if it should not be seen and always adds to + * the visibleItems. + * this one is for brute forcing and hiding. * - * @class Gesture - * @static - */ - /** - * @event gesture - * @param {Object} ev + * @param {Item} item + * @param {Array} visibleItems + * @param {{start:number, end:number}} range + * @private */ - Hammer.gestures.Gesture = { - name: 'gesture', - index: 1337, - handler: function releaseGesture(ev, inst) { - inst.trigger(this.name, ev); + Group.prototype._checkIfVisible = function(item, visibleItems, range) { + if (item.isVisible(range)) { + if (!item.displayed) item.show(); + // reposition item horizontally + item.repositionX(); + visibleItems.push(item); + } + else { + if (item.displayed) item.hide(); } }; + /** - * @module gestures - */ - /** - * Touch stays at the same place for x time + * this function is very similar to the _checkIfInvisible() but it does not + * return booleans, hides the item if it should not be seen and always adds to + * the visibleItems. + * this one is for brute forcing and hiding. * - * @class Hold - * @static - */ - /** - * @event hold - * @param {Object} ev + * @param {Item} item + * @param {Array} visibleItems + * @param {{start:number, end:number}} range + * @private */ + Group.prototype._checkIfVisibleWithReference = function(item, visibleItems, visibleItemsLookup, range) { + if (item.isVisible(range)) { + if (visibleItemsLookup[item.id] === undefined) { + visibleItemsLookup[item.id] = true; + visibleItems.push(item); + } + } + else { + if (item.displayed) item.hide(); + } + }; - /** - * @param {String} name - */ - (function(name) { - var timer; - function holdGesture(ev, inst) { - var options = inst.options, - current = Detection.current; - switch(ev.eventType) { - case EVENT_START: - clearTimeout(timer); + module.exports = Group; - // set the gesture so we can check in the timeout if it still is - current.name = name; - // set timer and if after the timeout it still is hold, - // we trigger the hold event - timer = setTimeout(function() { - if(current && current.name == name) { - inst.trigger(name, ev); - } - }, options.holdTimeout); - break; +/***/ }, +/* 26 */ +/***/ function(module, exports, __webpack_require__) { - case EVENT_MOVE: - if(ev.distance > options.holdThreshold) { - clearTimeout(timer); - } - break; + var util = __webpack_require__(1); + var Group = __webpack_require__(25); - case EVENT_RELEASE: - clearTimeout(timer); - break; - } - } + /** + * @constructor BackgroundGroup + * @param {Number | String} groupId + * @param {Object} data + * @param {ItemSet} itemSet + */ + function BackgroundGroup (groupId, data, itemSet) { + Group.call(this, groupId, data, itemSet); - Hammer.gestures.Hold = { - name: name, - index: 10, - defaults: { - /** - * @property holdTimeout - * @type {Number} - * @default 500 - */ - holdTimeout: 500, + this.width = 0; + this.height = 0; + this.top = 0; + this.left = 0; + } - /** - * movement allowed while holding - * @property holdThreshold - * @type {Number} - * @default 2 - */ - holdThreshold: 2 - }, - handler: holdGesture - }; - })('hold'); + BackgroundGroup.prototype = Object.create(Group.prototype); /** - * @module gestures - */ - /** - * when a touch is being released from the page - * - * @class Release - * @static - */ - /** - * @event release - * @param {Object} ev + * Repaint this group + * @param {{start: number, end: number}} range + * @param {{item: {horizontal: number, vertical: number}, axis: number}} margin + * @param {boolean} [restack=false] Force restacking of all items + * @return {boolean} Returns true if the group is resized */ - Hammer.gestures.Release = { - name: 'release', - index: Infinity, - handler: function releaseGesture(ev, inst) { - if(ev.eventType == EVENT_RELEASE) { - inst.trigger(this.name, ev); - } - } + BackgroundGroup.prototype.redraw = function(range, margin, restack) { + var resized = false; + + this.visibleItems = this._updateVisibleItems(this.orderedItems, this.visibleItems, range); + + // calculate actual size + this.width = this.dom.background.offsetWidth; + + // apply new height (just always zero for BackgroundGroup + this.dom.background.style.height = '0'; + + // update vertical position of items after they are re-stacked and the height of the group is calculated + for (var i = 0, ii = this.visibleItems.length; i < ii; i++) { + var item = this.visibleItems[i]; + item.repositionY(margin); + } + + return resized; }; /** - * @module gestures - */ - /** - * triggers swipe events when the end velocity is above the threshold - * for best usage, set `preventDefault` (on the drag gesture) to `true` - * ```` - * hammertime.on("dragleft swipeleft", function(ev) { - * console.log(ev); - * ev.gesture.preventDefault(); - * }); - * ```` - * - * @class Swipe - * @static - */ - /** - * @event swipe - * @param {Object} ev - */ - /** - * @event swipeleft - * @param {Object} ev - */ - /** - * @event swiperight - * @param {Object} ev - */ - /** - * @event swipeup - * @param {Object} ev - */ - /** - * @event swipedown - * @param {Object} ev + * Show this group: attach to the DOM */ - Hammer.gestures.Swipe = { - name: 'swipe', - index: 40, - defaults: { - /** - * @property swipeMinTouches - * @type {Number} - * @default 1 - */ - swipeMinTouches: 1, + BackgroundGroup.prototype.show = function() { + if (!this.dom.background.parentNode) { + this.itemSet.dom.background.appendChild(this.dom.background); + } + }; - /** - * @property swipeMaxTouches - * @type {Number} - * @default 1 - */ - swipeMaxTouches: 1, + module.exports = BackgroundGroup; - /** - * horizontal swipe velocity - * @property swipeVelocityX - * @type {Number} - * @default 0.6 - */ - swipeVelocityX: 0.6, - /** - * vertical swipe velocity - * @property swipeVelocityY - * @type {Number} - * @default 0.6 - */ - swipeVelocityY: 0.6 - }, +/***/ }, +/* 27 */ +/***/ function(module, exports, __webpack_require__) { - handler: function swipeGesture(ev, inst) { - if(ev.eventType == EVENT_RELEASE) { - var touches = ev.touches.length, - options = inst.options; + var Hammer = __webpack_require__(45); + var util = __webpack_require__(1); + var DataSet = __webpack_require__(3); + var DataView = __webpack_require__(4); + var Component = __webpack_require__(20); + var Group = __webpack_require__(25); + var BackgroundGroup = __webpack_require__(26); + var BoxItem = __webpack_require__(33); + var PointItem = __webpack_require__(34); + var RangeItem = __webpack_require__(35); + var BackgroundItem = __webpack_require__(32); - // max touches - if(touches < options.swipeMinTouches || - touches > options.swipeMaxTouches) { - return; - } - // when the distance we moved is too small we skip this gesture - // or we can be already in dragging - if(ev.velocityX > options.swipeVelocityX || - ev.velocityY > options.swipeVelocityY) { - // trigger swipe events - inst.trigger(this.name, ev); - inst.trigger(this.name + ev.direction, ev); - } - } - } - }; + var UNGROUPED = '__ungrouped__'; // reserved group id for ungrouped items + var BACKGROUND = '__background__'; // reserved group id for background items without group /** - * @module gestures - */ - /** - * Single tap and a double tap on a place - * - * @class Tap - * @static - */ - /** - * @event tap - * @param {Object} ev - */ - /** - * @event doubletap - * @param {Object} ev + * An ItemSet holds a set of items and ranges which can be displayed in a + * range. The width is determined by the parent of the ItemSet, and the height + * is determined by the size of the items. + * @param {{dom: Object, domProps: Object, emitter: Emitter, range: Range}} body + * @param {Object} [options] See ItemSet.setOptions for the available options. + * @constructor ItemSet + * @extends Component */ + function ItemSet(body, options) { + this.body = body; - /** - * @param {String} name - */ - (function(name) { - var hasMoved = false; - - function tapGesture(ev, inst) { - var options = inst.options, - current = Detection.current, - prev = Detection.previous, - sincePrev, - didDoubleTap; + this.defaultOptions = { + type: null, // 'box', 'point', 'range', 'background' + orientation: 'bottom', // 'top' or 'bottom' + align: 'auto', // alignment of box items + stack: true, + groupOrder: null, - switch(ev.eventType) { - case EVENT_START: - hasMoved = false; - break; + selectable: true, + editable: { + updateTime: false, + updateGroup: false, + add: false, + remove: false + }, - case EVENT_MOVE: - hasMoved = hasMoved || (ev.distance > options.tapMaxDistance); - break; + onAdd: function (item, callback) { + callback(item); + }, + onUpdate: function (item, callback) { + callback(item); + }, + onMove: function (item, callback) { + callback(item); + }, + onRemove: function (item, callback) { + callback(item); + }, + onMoving: function (item, callback) { + callback(item); + }, - case EVENT_END: - if(!Utils.inStr(ev.srcEvent.type, 'cancel') && ev.deltaTime < options.tapMaxTime && !hasMoved) { - // previous gesture, for the double tap since these are two different gesture detections - sincePrev = prev && prev.lastEvent && ev.timeStamp - prev.lastEvent.timeStamp; - didDoubleTap = false; + margin: { + item: { + horizontal: 10, + vertical: 10 + }, + axis: 20 + }, + padding: 5 + }; - // check if double tap - if(prev && prev.name == name && - (sincePrev && sincePrev < options.doubleTapInterval) && - ev.distance < options.doubleTapDistance) { - inst.trigger('doubletap', ev); - didDoubleTap = true; - } + // options is shared by this ItemSet and all its items + this.options = util.extend({}, this.defaultOptions); - // do a single tap - if(!didDoubleTap || options.tapAlways) { - current.name = name; - inst.trigger(current.name, ev); - } - } - break; - } - } + // options for getting items from the DataSet with the correct type + this.itemOptions = { + type: {start: 'Date', end: 'Date'} + }; - Hammer.gestures.Tap = { - name: name, - index: 100, - handler: tapGesture, - defaults: { - /** - * max time of a tap, this is for the slow tappers - * @property tapMaxTime - * @type {Number} - * @default 250 - */ - tapMaxTime: 250, + this.conversion = { + toScreen: body.util.toScreen, + toTime: body.util.toTime + }; + this.dom = {}; + this.props = {}; + this.hammer = null; - /** - * max distance of movement of a tap, this is for the slow tappers - * @property tapMaxDistance - * @type {Number} - * @default 10 - */ - tapMaxDistance: 10, + var me = this; + this.itemsData = null; // DataSet + this.groupsData = null; // DataSet - /** - * always trigger the `tap` event, even while double-tapping - * @property tapAlways - * @type {Boolean} - * @default true - */ - tapAlways: true, + // listeners for the DataSet of the items + this.itemListeners = { + 'add': function (event, params, senderId) { + me._onAdd(params.items); + }, + 'update': function (event, params, senderId) { + me._onUpdate(params.items); + }, + 'remove': function (event, params, senderId) { + me._onRemove(params.items); + } + }; - /** - * max distance between two taps - * @property doubleTapDistance - * @type {Number} - * @default 20 - */ - doubleTapDistance: 20, + // listeners for the DataSet of the groups + this.groupListeners = { + 'add': function (event, params, senderId) { + me._onAddGroups(params.items); + }, + 'update': function (event, params, senderId) { + me._onUpdateGroups(params.items); + }, + 'remove': function (event, params, senderId) { + me._onRemoveGroups(params.items); + } + }; - /** - * max time between two taps - * @property doubleTapInterval - * @type {Number} - * @default 300 - */ - doubleTapInterval: 300 - } - }; - })('tap'); + this.items = {}; // object with an Item for every data item + this.groups = {}; // Group object for every group + this.groupIds = []; - /** - * @module gestures - */ - /** - * when a touch is being touched at the page - * - * @class Touch - * @static - */ - /** - * @event touch - * @param {Object} ev - */ - Hammer.gestures.Touch = { - name: 'touch', - index: -Infinity, - defaults: { - /** - * call preventDefault at touchstart, and makes the element blocking by disabling the scrolling of the page, - * but it improves gestures like transforming and dragging. - * be careful with using this, it can be very annoying for users to be stuck on the page - * @property preventDefault - * @type {Boolean} - * @default false - */ - preventDefault: false, + this.selection = []; // list with the ids of all selected nodes + this.stackDirty = true; // if true, all items will be restacked on next redraw - /** - * disable mouse events, so only touch (or pen!) input triggers events - * @property preventMouse - * @type {Boolean} - * @default false - */ - preventMouse: false - }, - handler: function touchGesture(ev, inst) { - if(inst.options.preventMouse && ev.pointerType == POINTER_MOUSE) { - ev.stopDetect(); - return; - } + this.touchParams = {}; // stores properties while dragging + // create the HTML DOM - if(inst.options.preventDefault) { - ev.preventDefault(); - } + this._create(); - if(ev.eventType == EVENT_TOUCH) { - inst.trigger('touch', ev); - } - } + this.setOptions(options); + } + + ItemSet.prototype = new Component(); + + // available item types will be registered here + ItemSet.types = { + background: BackgroundItem, + box: BoxItem, + range: RangeItem, + point: PointItem }; /** - * @module gestures - */ - /** - * User want to scale or rotate with 2 fingers - * Preventing the default browser behavior is a good way to improve feel and working. This can be done with the - * `preventDefault` option. - * - * @class Transform - * @static - */ - /** - * @event transform - * @param {Object} ev - */ - /** - * @event transformstart - * @param {Object} ev - */ - /** - * @event transformend - * @param {Object} ev - */ - /** - * @event pinchin - * @param {Object} ev - */ - /** - * @event pinchout - * @param {Object} ev - */ - /** - * @event rotate - * @param {Object} ev + * Create the HTML DOM for the ItemSet */ + ItemSet.prototype._create = function(){ + var frame = document.createElement('div'); + frame.className = 'itemset'; + frame['timeline-itemset'] = this; + this.dom.frame = frame; - /** - * @param {String} name - */ - (function(name) { - var triggered = false; + // create background panel + var background = document.createElement('div'); + background.className = 'background'; + frame.appendChild(background); + this.dom.background = background; - function transformGesture(ev, inst) { - switch(ev.eventType) { - case EVENT_START: - triggered = false; - break; + // create foreground panel + var foreground = document.createElement('div'); + foreground.className = 'foreground'; + frame.appendChild(foreground); + this.dom.foreground = foreground; - case EVENT_MOVE: - // at least multitouch - if(ev.touches.length < 2) { - return; - } + // create axis panel + var axis = document.createElement('div'); + axis.className = 'axis'; + this.dom.axis = axis; - var scaleThreshold = Math.abs(1 - ev.scale); - var rotationThreshold = Math.abs(ev.rotation); + // create labelset + var labelSet = document.createElement('div'); + labelSet.className = 'labelset'; + this.dom.labelSet = labelSet; - // when the distance we moved is too small we skip this gesture - // or we can be already in dragging - if(scaleThreshold < inst.options.transformMinScale && - rotationThreshold < inst.options.transformMinRotation) { - return; - } + // create ungrouped Group + this._updateUngrouped(); - // we are transforming! - Detection.current.name = name; + // create background Group + var backgroundGroup = new BackgroundGroup(BACKGROUND, null, this); + backgroundGroup.show(); + this.groups[BACKGROUND] = backgroundGroup; - // first time, trigger dragstart event - if(!triggered) { - inst.trigger(name + 'start', ev); - triggered = true; - } + // attach event listeners + // Note: we bind to the centerContainer for the case where the height + // of the center container is larger than of the ItemSet, so we + // can click in the empty area to create a new item or deselect an item. + this.hammer = Hammer(this.body.dom.centerContainer, { + preventDefault: true + }); - inst.trigger(name, ev); // basic transform event + // drag items when selected + this.hammer.on('touch', this._onTouch.bind(this)); + this.hammer.on('dragstart', this._onDragStart.bind(this)); + this.hammer.on('drag', this._onDrag.bind(this)); + this.hammer.on('dragend', this._onDragEnd.bind(this)); - // trigger rotate event - if(rotationThreshold > inst.options.transformMinRotation) { - inst.trigger('rotate', ev); - } + // single select (or unselect) when tapping an item + this.hammer.on('tap', this._onSelectItem.bind(this)); - // trigger pinch event - if(scaleThreshold > inst.options.transformMinScale) { - inst.trigger('pinch', ev); - inst.trigger('pinch' + (ev.scale < 1 ? 'in' : 'out'), ev); - } - break; + // multi select when holding mouse/touch, or on ctrl+click + this.hammer.on('hold', this._onMultiSelectItem.bind(this)); - case EVENT_RELEASE: - if(triggered && ev.changedLength < 2) { - inst.trigger(name + 'end', ev); - triggered = false; - } - break; + // add item on doubletap + this.hammer.on('doubletap', this._onAddItem.bind(this)); + + // attach to the DOM + this.show(); + }; + + /** + * Set options for the ItemSet. Existing options will be extended/overwritten. + * @param {Object} [options] The following options are available: + * {String} type + * Default type for the items. Choose from 'box' + * (default), 'point', 'range', or 'background'. + * The default style can be overwritten by + * individual items. + * {String} align + * Alignment for the items, only applicable for + * BoxItem. Choose 'center' (default), 'left', or + * 'right'. + * {String} orientation + * Orientation of the item set. Choose 'top' or + * 'bottom' (default). + * {Function} groupOrder + * A sorting function for ordering groups + * {Boolean} stack + * If true (deafult), items will be stacked on + * top of each other. + * {Number} margin.axis + * Margin between the axis and the items in pixels. + * Default is 20. + * {Number} margin.item.horizontal + * Horizontal margin between items in pixels. + * Default is 10. + * {Number} margin.item.vertical + * Vertical Margin between items in pixels. + * Default is 10. + * {Number} margin.item + * Margin between items in pixels in both horizontal + * and vertical direction. Default is 10. + * {Number} margin + * Set margin for both axis and items in pixels. + * {Number} padding + * Padding of the contents of an item in pixels. + * Must correspond with the items css. Default is 5. + * {Boolean} selectable + * If true (default), items can be selected. + * {Boolean} editable + * Set all editable options to true or false + * {Boolean} editable.updateTime + * Allow dragging an item to an other moment in time + * {Boolean} editable.updateGroup + * Allow dragging an item to an other group + * {Boolean} editable.add + * Allow creating new items on double tap + * {Boolean} editable.remove + * Allow removing items by clicking the delete button + * top right of a selected item. + * {Function(item: Item, callback: Function)} onAdd + * Callback function triggered when an item is about to be added: + * when the user double taps an empty space in the Timeline. + * {Function(item: Item, callback: Function)} onUpdate + * Callback function fired when an item is about to be updated. + * This function typically has to show a dialog where the user + * change the item. If not implemented, nothing happens. + * {Function(item: Item, callback: Function)} onMove + * Fired when an item has been moved. If not implemented, + * the move action will be accepted. + * {Function(item: Item, callback: Function)} onRemove + * Fired when an item is about to be deleted. + * If not implemented, the item will be always removed. + */ + ItemSet.prototype.setOptions = function(options) { + if (options) { + // copy all options that we know + var fields = ['type', 'align', 'orientation', 'padding', 'stack', 'selectable', 'groupOrder', 'dataAttributes', 'template','hide']; + util.selectiveExtend(fields, this.options, options); + + if ('margin' in options) { + if (typeof options.margin === 'number') { + this.options.margin.axis = options.margin; + this.options.margin.item.horizontal = options.margin; + this.options.margin.item.vertical = options.margin; + } + else if (typeof options.margin === 'object') { + util.selectiveExtend(['axis'], this.options.margin, options.margin); + if ('item' in options.margin) { + if (typeof options.margin.item === 'number') { + this.options.margin.item.horizontal = options.margin.item; + this.options.margin.item.vertical = options.margin.item; + } + else if (typeof options.margin.item === 'object') { + util.selectiveExtend(['horizontal', 'vertical'], this.options.margin.item, options.margin.item); + } } + } } - Hammer.gestures.Transform = { - name: name, - index: 45, - defaults: { - /** - * minimal scale factor, no scale is 1, zoomin is to 0 and zoomout until higher then 1 - * @property transformMinScale - * @type {Number} - * @default 0.01 - */ - transformMinScale: 0.01, + if ('editable' in options) { + if (typeof options.editable === 'boolean') { + this.options.editable.updateTime = options.editable; + this.options.editable.updateGroup = options.editable; + this.options.editable.add = options.editable; + this.options.editable.remove = options.editable; + } + else if (typeof options.editable === 'object') { + util.selectiveExtend(['updateTime', 'updateGroup', 'add', 'remove'], this.options.editable, options.editable); + } + } - /** - * rotation in degrees - * @property transformMinRotation - * @type {Number} - * @default 1 - */ - transformMinRotation: 1 - }, + // callback functions + var addCallback = (function (name) { + var fn = options[name]; + if (fn) { + if (!(fn instanceof Function)) { + throw new Error('option ' + name + ' must be a function ' + name + '(item, callback)'); + } + this.options[name] = fn; + } + }).bind(this); + ['onAdd', 'onUpdate', 'onRemove', 'onMove', 'onMoving'].forEach(addCallback); - handler: transformGesture - }; - })('transform'); + // force the itemSet to refresh: options like orientation and margins may be changed + this.markDirty(); + } + }; /** - * @module hammer + * Mark the ItemSet dirty so it will refresh everything with next redraw */ - - // AMD export - if(true) { - !(__WEBPACK_AMD_DEFINE_RESULT__ = function() { - return Hammer; - }.call(exports, __webpack_require__, exports, module), __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__)); - // commonjs export - } else if(typeof module !== 'undefined' && module.exports) { - module.exports = Hammer; - // browser export - } else { - window.Hammer = Hammer; - } - - })(window); - -/***/ }, -/* 21 */ -/***/ function(module, exports, __webpack_require__) { - - var util = __webpack_require__(1); - var hammerUtil = __webpack_require__(22); - var moment = __webpack_require__(2); - var Component = __webpack_require__(23); - var DateUtil = __webpack_require__(24); + ItemSet.prototype.markDirty = function() { + this.groupIds = []; + this.stackDirty = true; + }; /** - * @constructor Range - * A Range controls a numeric range with a start and end value. - * The Range adjusts the range based on mouse events or programmatic changes, - * and triggers events when the range is changing or has been changed. - * @param {{dom: Object, domProps: Object, emitter: Emitter}} body - * @param {Object} [options] See description at Range.setOptions + * Destroy the ItemSet */ - function Range(body, options) { - var now = moment().hours(0).minutes(0).seconds(0).milliseconds(0); - this.start = now.clone().add(-3, 'days').valueOf(); // Number - this.end = now.clone().add(4, 'days').valueOf(); // Number - - this.body = body; - this.deltaDifference = 0; - this.scaleOffset = 0; - this.startToFront = false; - this.endToFront = true; + ItemSet.prototype.destroy = function() { + this.hide(); + this.setItems(null); + this.setGroups(null); - // default options - this.defaultOptions = { - start: null, - end: null, - direction: 'horizontal', // 'horizontal' or 'vertical' - moveable: true, - zoomable: true, - min: null, - max: null, - zoomMin: 10, // milliseconds - zoomMax: 1000 * 60 * 60 * 24 * 365 * 10000 // milliseconds - }; - this.options = util.extend({}, this.defaultOptions); + this.hammer = null; - this.props = { - touch: {} - }; - this.animateTimer = null; + this.body = null; + this.conversion = null; + }; - // drag listeners for dragging - this.body.emitter.on('dragstart', this._onDragStart.bind(this)); - this.body.emitter.on('drag', this._onDrag.bind(this)); - this.body.emitter.on('dragend', this._onDragEnd.bind(this)); + /** + * Hide the component from the DOM + */ + ItemSet.prototype.hide = function() { + // remove the frame containing the items + if (this.dom.frame.parentNode) { + this.dom.frame.parentNode.removeChild(this.dom.frame); + } - // ignore dragging when holding - this.body.emitter.on('hold', this._onHold.bind(this)); + // remove the axis with dots + if (this.dom.axis.parentNode) { + this.dom.axis.parentNode.removeChild(this.dom.axis); + } - // mouse wheel for zooming - this.body.emitter.on('mousewheel', this._onMouseWheel.bind(this)); - this.body.emitter.on('DOMMouseScroll', this._onMouseWheel.bind(this)); // For FF + // remove the labelset containing all group labels + if (this.dom.labelSet.parentNode) { + this.dom.labelSet.parentNode.removeChild(this.dom.labelSet); + } + }; - // pinch to zoom - this.body.emitter.on('touch', this._onTouch.bind(this)); - this.body.emitter.on('pinch', this._onPinch.bind(this)); + /** + * Show the component in the DOM (when not already visible). + * @return {Boolean} changed + */ + ItemSet.prototype.show = function() { + // show frame containing the items + if (!this.dom.frame.parentNode) { + this.body.dom.center.appendChild(this.dom.frame); + } - this.setOptions(options); - } + // show axis with dots + if (!this.dom.axis.parentNode) { + this.body.dom.backgroundVertical.appendChild(this.dom.axis); + } - Range.prototype = new Component(); + // show labelset containing labels + if (!this.dom.labelSet.parentNode) { + this.body.dom.left.appendChild(this.dom.labelSet); + } + }; /** - * Set options for the range controller - * @param {Object} options Available options: - * {Number | Date | String} start Start date for the range - * {Number | Date | String} end End date for the range - * {Number} min Minimum value for start - * {Number} max Maximum value for end - * {Number} zoomMin Set a minimum value for - * (end - start). - * {Number} zoomMax Set a maximum value for - * (end - start). - * {Boolean} moveable Enable moving of the range - * by dragging. True by default - * {Boolean} zoomable Enable zooming of the range - * by pinching/scrolling. True by default + * Set selected items by their id. Replaces the current selection + * Unknown id's are silently ignored. + * @param {string[] | string} [ids] An array with zero or more id's of the items to be + * selected, or a single item id. If ids is undefined + * or an empty array, all items will be unselected. */ - Range.prototype.setOptions = function (options) { - if (options) { - // copy the options that we know - var fields = ['direction', 'min', 'max', 'zoomMin', 'zoomMax', 'moveable', 'zoomable', 'activate', 'hiddenDates']; - util.selectiveExtend(fields, this.options, options); + ItemSet.prototype.setSelection = function(ids) { + var i, ii, id, item; - if ('start' in options || 'end' in options) { - // apply a new range. both start and end are optional - this.setRange(options.start, options.end); + if (ids == undefined) ids = []; + if (!Array.isArray(ids)) ids = [ids]; + + // unselect currently selected items + for (i = 0, ii = this.selection.length; i < ii; i++) { + id = this.selection[i]; + item = this.items[id]; + if (item) item.unselect(); + } + + // select items + this.selection = []; + for (i = 0, ii = ids.length; i < ii; i++) { + id = ids[i]; + item = this.items[id]; + if (item) { + this.selection.push(id); + item.select(); } } }; /** - * Test whether direction has a valid value - * @param {String} direction 'horizontal' or 'vertical' + * Get the selected items by their id + * @return {Array} ids The ids of the selected items */ - function validateDirection (direction) { - if (direction != 'horizontal' && direction != 'vertical') { - throw new TypeError('Unknown direction "' + direction + '". ' + - 'Choose "horizontal" or "vertical".'); - } - } + ItemSet.prototype.getSelection = function() { + return this.selection.concat([]); + }; /** - * Set a new start and end range - * @param {Date | Number | String} [start] - * @param {Date | Number | String} [end] - * @param {boolean | number} [animate=false] If true, the range is animated - * smoothly to the new window. - * If animate is a number, the - * number is taken as duration - * Default duration is 500 ms. - * @param {Boolean} [byUser=false] - * + * Get the id's of the currently visible items. + * @returns {Array} The ids of the visible items */ - Range.prototype.setRange = function(start, end, animate, byUser) { - if (byUser !== true) { - byUser = false; - } - var _start = start != undefined ? util.convert(start, 'Date').valueOf() : null; - var _end = end != undefined ? util.convert(end, 'Date').valueOf() : null; - this._cancelAnimation(); - - if (animate) { - var me = this; - var initStart = this.start; - var initEnd = this.end; - var duration = typeof animate === 'number' ? animate : 500; - var initTime = new Date().valueOf(); - var anyChanged = false; - - var next = function () { - if (!me.props.touch.dragging) { - var now = new Date().valueOf(); - var time = now - initTime; - var done = time > duration; - var s = (done || _start === null) ? _start : util.easeInOutQuad(time, initStart, _start, duration); - var e = (done || _end === null) ? _end : util.easeInOutQuad(time, initEnd, _end, duration); + ItemSet.prototype.getVisibleItems = function() { + var range = this.body.range.getRange(); + var left = this.body.util.toScreen(range.start); + var right = this.body.util.toScreen(range.end); - changed = me._applyRange(s, e); - DateUtil.updateHiddenDates(me.body, me.options.hiddenDates); - anyChanged = anyChanged || changed; - if (changed) { - me.body.emitter.emit('rangechange', {start: new Date(me.start), end: new Date(me.end), byUser:byUser}); - } + var ids = []; + for (var groupId in this.groups) { + if (this.groups.hasOwnProperty(groupId)) { + var group = this.groups[groupId]; + var rawVisibleItems = group.visibleItems; - if (done) { - if (anyChanged) { - me.body.emitter.emit('rangechanged', {start: new Date(me.start), end: new Date(me.end), byUser:byUser}); - } - } - else { - // animate with as high as possible frame rate, leave 20 ms in between - // each to prevent the browser from blocking - me.animateTimer = setTimeout(next, 20); + // filter the "raw" set with visibleItems into a set which is really + // visible by pixels + for (var i = 0; i < rawVisibleItems.length; i++) { + var item = rawVisibleItems[i]; + // TODO: also check whether visible vertically + if ((item.left < right) && (item.left + item.width > left)) { + ids.push(item.id); } } } - - return next(); - } - else { - var changed = this._applyRange(_start, _end); - DateUtil.updateHiddenDates(this.body, this.options.hiddenDates); - if (changed) { - var params = {start: new Date(this.start), end: new Date(this.end), byUser:byUser}; - this.body.emitter.emit('rangechange', params); - this.body.emitter.emit('rangechanged', params); - } } + + return ids; }; /** - * Stop an animation + * Deselect a selected item + * @param {String | Number} id * @private */ - Range.prototype._cancelAnimation = function () { - if (this.animateTimer) { - clearTimeout(this.animateTimer); - this.animateTimer = null; + ItemSet.prototype._deselect = function(id) { + var selection = this.selection; + for (var i = 0, ii = selection.length; i < ii; i++) { + if (selection[i] == id) { // non-strict comparison! + selection.splice(i, 1); + break; + } } }; /** - * Set a new start and end range. This method is the same as setRange, but - * does not trigger a range change and range changed event, and it returns - * true when the range is changed - * @param {Number} [start] - * @param {Number} [end] - * @return {Boolean} changed - * @private + * Repaint the component + * @return {boolean} Returns true if the component is resized */ - Range.prototype._applyRange = function(start, end) { - var newStart = (start != null) ? util.convert(start, 'Date').valueOf() : this.start, - newEnd = (end != null) ? util.convert(end, 'Date').valueOf() : this.end, - max = (this.options.max != null) ? util.convert(this.options.max, 'Date').valueOf() : null, - min = (this.options.min != null) ? util.convert(this.options.min, 'Date').valueOf() : null, - diff; + ItemSet.prototype.redraw = function() { + var margin = this.options.margin, + range = this.body.range, + asSize = util.option.asSize, + options = this.options, + orientation = options.orientation, + resized = false, + frame = this.dom.frame, + editable = options.editable.updateTime || options.editable.updateGroup; - // check for valid number - if (isNaN(newStart) || newStart === null) { - throw new Error('Invalid start "' + start + '"'); - } - if (isNaN(newEnd) || newEnd === null) { - throw new Error('Invalid end "' + end + '"'); - } + // recalculate absolute position (before redrawing groups) + this.props.top = this.body.domProps.top.height + this.body.domProps.border.top; + this.props.left = this.body.domProps.left.width + this.body.domProps.border.left; - // prevent start < end - if (newEnd < newStart) { - newEnd = newStart; - } + // update class name + frame.className = 'itemset' + (editable ? ' editable' : ''); - // prevent start < min - if (min !== null) { - if (newStart < min) { - diff = (min - newStart); - newStart += diff; - newEnd += diff; + // reorder the groups (if needed) + resized = this._orderGroups() || resized; - // prevent end > max - if (max != null) { - if (newEnd > max) { - newEnd = max; - } - } - } - } + // check whether zoomed (in that case we need to re-stack everything) + // TODO: would be nicer to get this as a trigger from Range + var visibleInterval = range.end - range.start; + var zoomed = (visibleInterval != this.lastVisibleInterval) || (this.props.width != this.props.lastWidth); + if (zoomed) this.stackDirty = true; + this.lastVisibleInterval = visibleInterval; + this.props.lastWidth = this.props.width; - // prevent end > max - if (max !== null) { - if (newEnd > max) { - diff = (newEnd - max); - newStart -= diff; - newEnd -= diff; + var restack = this.stackDirty; + var firstGroup = this._firstGroup(); + var firstMargin = { + item: margin.item, + axis: margin.axis + }; + var nonFirstMargin = { + item: margin.item, + axis: margin.item.vertical / 2 + }; + var height = 0; + var minHeight = margin.axis + margin.item.vertical; - // prevent start < min - if (min != null) { - if (newStart < min) { - newStart = min; - } - } - } - } + // redraw the background group + this.groups[BACKGROUND].redraw(range, nonFirstMargin, restack); - // prevent (end-start) < zoomMin - if (this.options.zoomMin !== null) { - var zoomMin = parseFloat(this.options.zoomMin); - if (zoomMin < 0) { - zoomMin = 0; - } - if ((newEnd - newStart) < zoomMin) { - if ((this.end - this.start) === zoomMin) { - // ignore this action, we are already zoomed to the minimum - newStart = this.start; - newEnd = this.end; - } - else { - // zoom to the minimum - diff = (zoomMin - (newEnd - newStart)); - newStart -= diff / 2; - newEnd += diff / 2; - } - } - } + // redraw all regular groups + util.forEach(this.groups, function (group) { + var groupMargin = (group == firstGroup) ? firstMargin : nonFirstMargin; + var groupResized = group.redraw(range, groupMargin, restack); + resized = groupResized || resized; + height += group.height; + }); + height = Math.max(height, minHeight); + this.stackDirty = false; - // prevent (end-start) > zoomMax - if (this.options.zoomMax !== null) { - var zoomMax = parseFloat(this.options.zoomMax); - if (zoomMax < 0) { - zoomMax = 0; - } - if ((newEnd - newStart) > zoomMax) { - if ((this.end - this.start) === zoomMax) { - // ignore this action, we are already zoomed to the maximum - newStart = this.start; - newEnd = this.end; - } - else { - // zoom to the maximum - diff = ((newEnd - newStart) - zoomMax); - newStart += diff / 2; - newEnd -= diff / 2; - } - } - } + // update frame height + frame.style.height = asSize(height); - var changed = (this.start != newStart || this.end != newEnd); + // calculate actual size + this.props.width = frame.offsetWidth; + this.props.height = height; - // if the new range does NOT overlap with the old range, emit checkRangedItems to avoid not showing ranged items (ranged meaning has end time, not neccesarily of type Range) - if (!((newStart >= this.start && newStart <= this.end) || (newEnd >= this.start && newEnd <= this.end)) && - !((this.start >= newStart && this.start <= newEnd) || (this.end >= newStart && this.end <= newEnd) )) { - this.body.emitter.emit('checkRangedItems'); - } + // reposition axis + this.dom.axis.style.top = asSize((orientation == 'top') ? + (this.body.domProps.top.height + this.body.domProps.border.top) : + (this.body.domProps.top.height + this.body.domProps.centerContainer.height)); + this.dom.axis.style.left = '0'; - this.start = newStart; - this.end = newEnd; - return changed; - }; + // check if this component is resized + resized = this._isResized() || resized; - /** - * Retrieve the current range. - * @return {Object} An object with start and end properties - */ - Range.prototype.getRange = function() { - return { - start: this.start, - end: this.end - }; + return resized; }; /** - * Calculate the conversion offset and scale for current range, based on - * the provided width - * @param {Number} width - * @returns {{offset: number, scale: number}} conversion + * Get the first group, aligned with the axis + * @return {Group | null} firstGroup + * @private */ - Range.prototype.conversion = function (width, totalHidden) { - return Range.conversion(this.start, this.end, width, totalHidden); + ItemSet.prototype._firstGroup = function() { + var firstGroupIndex = (this.options.orientation == 'top') ? 0 : (this.groupIds.length - 1); + var firstGroupId = this.groupIds[firstGroupIndex]; + var firstGroup = this.groups[firstGroupId] || this.groups[UNGROUPED]; + + return firstGroup || null; }; /** - * Static method to calculate the conversion offset and scale for a range, - * based on the provided start, end, and width - * @param {Number} start - * @param {Number} end - * @param {Number} width - * @returns {{offset: number, scale: number}} conversion + * Create or delete the group holding all ungrouped items. This group is used when + * there are no groups specified. + * @protected */ - Range.conversion = function (start, end, width, totalHidden) { - if (totalHidden === undefined) { - totalHidden = 0; - } - if (width != 0 && (end - start != 0)) { - return { - offset: start, - scale: width / (end - start - totalHidden) + ItemSet.prototype._updateUngrouped = function() { + var ungrouped = this.groups[UNGROUPED]; + var background = this.groups[BACKGROUND]; + var item, itemId; + + if (this.groupsData) { + // remove the group holding all ungrouped items + if (ungrouped) { + ungrouped.hide(); + delete this.groups[UNGROUPED]; + + for (itemId in this.items) { + if (this.items.hasOwnProperty(itemId)) { + item = this.items[itemId]; + item.parent && item.parent.remove(item); + var groupId = this._getGroupId(item.data); + var group = this.groups[groupId]; + group && group.add(item) || item.hide(); + } + } } } else { - return { - offset: 0, - scale: 1 - }; + // create a group holding all (unfiltered) items + if (!ungrouped) { + var id = null; + var data = null; + ungrouped = new Group(id, data, this); + this.groups[UNGROUPED] = ungrouped; + + for (itemId in this.items) { + if (this.items.hasOwnProperty(itemId)) { + item = this.items[itemId]; + ungrouped.add(item); + } + } + + ungrouped.show(); + } } }; /** - * Start dragging horizontally or vertically - * @param {Event} event - * @private + * Get the element for the labelset + * @return {HTMLElement} labelSet */ - Range.prototype._onDragStart = function(event) { - this.deltaDifference = 0; - this.previousDelta = 0; - // only allow dragging when configured as movable - if (!this.options.moveable) return; + ItemSet.prototype.getLabelSet = function() { + return this.dom.labelSet; + }; - // refuse to drag when we where pinching to prevent the timeline make a jump - // when releasing the fingers in opposite order from the touch screen - if (!this.props.touch.allowDragging) return; + /** + * Set items + * @param {vis.DataSet | null} items + */ + ItemSet.prototype.setItems = function(items) { + var me = this, + ids, + oldItemsData = this.itemsData; - this.props.touch.start = this.start; - this.props.touch.end = this.end; - this.props.touch.dragging = true; + // replace the dataset + if (!items) { + this.itemsData = null; + } + else if (items instanceof DataSet || items instanceof DataView) { + this.itemsData = items; + } + else { + throw new TypeError('Data must be an instance of DataSet or DataView'); + } - if (this.body.dom.root) { - this.body.dom.root.style.cursor = 'move'; + if (oldItemsData) { + // unsubscribe from old dataset + util.forEach(this.itemListeners, function (callback, event) { + oldItemsData.off(event, callback); + }); + + // remove all drawn items + ids = oldItemsData.getIds(); + this._onRemove(ids); + } + + if (this.itemsData) { + // subscribe to new dataset + var id = this.id; + util.forEach(this.itemListeners, function (callback, event) { + me.itemsData.on(event, callback, id); + }); + + // add all new items + ids = this.itemsData.getIds(); + this._onAdd(ids); + + // update the group holding all ungrouped items + this._updateUngrouped(); } }; /** - * Perform dragging operation - * @param {Event} event - * @private + * Get the current items + * @returns {vis.DataSet | null} */ - Range.prototype._onDrag = function (event) { - // only allow dragging when configured as movable - if (!this.options.moveable) return; - // refuse to drag when we where pinching to prevent the timeline make a jump - // when releasing the fingers in opposite order from the touch screen - if (!this.props.touch.allowDragging) return; + ItemSet.prototype.getItems = function() { + return this.itemsData; + }; - var direction = this.options.direction; - validateDirection(direction); + /** + * Set groups + * @param {vis.DataSet} groups + */ + ItemSet.prototype.setGroups = function(groups) { + var me = this, + ids; - var delta = (direction == 'horizontal') ? event.gesture.deltaX : event.gesture.deltaY; - delta -= this.deltaDifference; - var interval = (this.props.touch.end - this.props.touch.start); + // unsubscribe from current dataset + if (this.groupsData) { + util.forEach(this.groupListeners, function (callback, event) { + me.groupsData.unsubscribe(event, callback); + }); - // normalize dragging speed if cutout is in between. - var duration = DateUtil.getHiddenDurationBetween(this.body.hiddenDates, this.start, this.end); - interval -= duration; + // remove all drawn groups + ids = this.groupsData.getIds(); + this.groupsData = null; + this._onRemoveGroups(ids); // note: this will cause a redraw + } - var width = (direction == 'horizontal') ? this.body.domProps.center.width : this.body.domProps.center.height; - var diffRange = -delta / width * interval; - var newStart = this.props.touch.start + diffRange; - var newEnd = this.props.touch.end + diffRange; + // replace the dataset + if (!groups) { + this.groupsData = null; + } + else if (groups instanceof DataSet || groups instanceof DataView) { + this.groupsData = groups; + } + else { + throw new TypeError('Data must be an instance of DataSet or DataView'); + } + if (this.groupsData) { + // subscribe to new dataset + var id = this.id; + util.forEach(this.groupListeners, function (callback, event) { + me.groupsData.on(event, callback, id); + }); - // snapping times away from hidden zones - var safeStart = DateUtil.snapAwayFromHidden(this.body.hiddenDates, newStart, this.previousDelta-delta, true); - var safeEnd = DateUtil.snapAwayFromHidden(this.body.hiddenDates, newEnd, this.previousDelta-delta, true); - if (safeStart != newStart || safeEnd != newEnd) { - this.deltaDifference += delta; - this.props.touch.start = safeStart; - this.props.touch.end = safeEnd; - this._onDrag(event); - return; + // draw all ms + ids = this.groupsData.getIds(); + this._onAddGroups(ids); } - this.previousDelta = delta; - this._applyRange(newStart, newEnd); + // update the group holding all ungrouped items + this._updateUngrouped(); - // fire a rangechange event - this.body.emitter.emit('rangechange', { - start: new Date(this.start), - end: new Date(this.end), - byUser: true - }); + // update the order of all items in each group + this._order(); + + this.body.emitter.emit('change', {queue: true}); }; /** - * Stop dragging operation - * @param {event} event - * @private + * Get the current groups + * @returns {vis.DataSet | null} groups */ - Range.prototype._onDragEnd = function (event) { - // only allow dragging when configured as movable - if (!this.options.moveable) return; + ItemSet.prototype.getGroups = function() { + return this.groupsData; + }; - // refuse to drag when we where pinching to prevent the timeline make a jump - // when releasing the fingers in opposite order from the touch screen - if (!this.props.touch.allowDragging) return; + /** + * Remove an item by its id + * @param {String | Number} id + */ + ItemSet.prototype.removeItem = function(id) { + var item = this.itemsData.get(id), + dataset = this.itemsData.getDataSet(); - this.props.touch.dragging = false; - if (this.body.dom.root) { - this.body.dom.root.style.cursor = 'auto'; + if (item) { + // confirm deletion + this.options.onRemove(item, function (item) { + if (item) { + // remove by id here, it is possible that an item has no id defined + // itself, so better not delete by the item itself + dataset.remove(id); + } + }); } - - // fire a rangechanged event - this.body.emitter.emit('rangechanged', { - start: new Date(this.start), - end: new Date(this.end), - byUser: true - }); }; /** - * Event handler for mouse wheel event, used to zoom - * Code from http://adomas.org/javascript-mouse-wheel/ - * @param {Event} event + * Get the time of an item based on it's data and options.type + * @param {Object} itemData + * @returns {string} Returns the type * @private */ - Range.prototype._onMouseWheel = function(event) { - // only allow zooming when configured as zoomable and moveable - if (!(this.options.zoomable && this.options.moveable)) return; + ItemSet.prototype._getType = function (itemData) { + return itemData.type || this.options.type || (itemData.end ? 'range' : 'box'); + }; - // retrieve delta - var delta = 0; - if (event.wheelDelta) { /* IE/Opera. */ - delta = event.wheelDelta / 120; - } else if (event.detail) { /* Mozilla case. */ - // In Mozilla, sign of delta is different than in IE. - // Also, delta is multiple of 3. - delta = -event.detail / 3; + + /** + * Get the group id for an item + * @param {Object} itemData + * @returns {string} Returns the groupId + * @private + */ + ItemSet.prototype._getGroupId = function (itemData) { + var type = this._getType(itemData); + if (type == 'background' && itemData.group == undefined) { + return BACKGROUND; + } + else { + return this.groupsData ? itemData.group : UNGROUPED; } + }; - // If delta is nonzero, handle it. - // Basically, delta is now positive if wheel was scrolled up, - // and negative, if wheel was scrolled down. - if (delta) { - // perform the zoom action. Delta is normally 1 or -1 + /** + * Handle updated items + * @param {Number[]} ids + * @protected + */ + ItemSet.prototype._onUpdate = function(ids) { + var me = this; - // adjust a negative delta such that zooming in with delta 0.1 - // equals zooming out with a delta -0.1 - var scale; - if (delta < 0) { - scale = 1 - (delta / 5); + ids.forEach(function (id) { + var itemData = me.itemsData.get(id, me.itemOptions); + var item = me.items[id]; + var type = me._getType(itemData); + + var constructor = ItemSet.types[type]; + + if (item) { + // update item + if (!constructor || !(item instanceof constructor)) { + // item type has changed, delete the item and recreate it + me._removeItem(item); + item = null; + } + else { + me._updateItem(item, itemData); + } } - else { - scale = 1 / (1 + (delta / 5)) ; + + if (!item) { + // create item + if (constructor) { + item = new constructor(itemData, me.conversion, me.options); + item.id = id; // TODO: not so nice setting id afterwards + me._addItem(item); + } + else if (type == 'rangeoverflow') { + // TODO: deprecated since version 2.1.0 (or 3.0.0?). cleanup some day + throw new TypeError('Item type "rangeoverflow" is deprecated. Use css styling instead: ' + + '.vis.timeline .item.range .content {overflow: visible;}'); + } + else { + throw new TypeError('Unknown item type "' + type + '"'); + } } + }); - // calculate center, the date to zoom around - var gesture = hammerUtil.fakeGesture(this, event), - pointer = getPointer(gesture.center, this.body.dom.center), - pointerDate = this._pointerToDate(pointer); + this._order(); + this.stackDirty = true; // force re-stacking of all items next redraw + this.body.emitter.emit('change', {queue: true}); + }; - this.zoom(scale, pointerDate, delta); - } + /** + * Handle added items + * @param {Number[]} ids + * @protected + */ + ItemSet.prototype._onAdd = ItemSet.prototype._onUpdate; - // Prevent default actions caused by mouse wheel - // (else the page and timeline both zoom and scroll) - event.preventDefault(); + /** + * Handle removed items + * @param {Number[]} ids + * @protected + */ + ItemSet.prototype._onRemove = function(ids) { + var count = 0; + var me = this; + ids.forEach(function (id) { + var item = me.items[id]; + if (item) { + count++; + me._removeItem(item); + } + }); + + if (count) { + // update order + this._order(); + this.stackDirty = true; // force re-stacking of all items next redraw + this.body.emitter.emit('change', {queue: true}); + } }; /** - * Start of a touch gesture + * Update the order of item in all groups * @private */ - Range.prototype._onTouch = function (event) { - this.props.touch.start = this.start; - this.props.touch.end = this.end; - this.props.touch.allowDragging = true; - this.props.touch.center = null; - this.scaleOffset = 0; - this.deltaDifference = 0; + ItemSet.prototype._order = function() { + // reorder the items in all groups + // TODO: optimization: only reorder groups affected by the changed items + util.forEach(this.groups, function (group) { + group.order(); + }); }; /** - * On start of a hold gesture + * Handle updated groups + * @param {Number[]} ids * @private */ - Range.prototype._onHold = function () { - this.props.touch.allowDragging = false; + ItemSet.prototype._onUpdateGroups = function(ids) { + this._onAddGroups(ids); }; /** - * Handle pinch event - * @param {Event} event + * Handle changed groups (added or updated) + * @param {Number[]} ids * @private */ - Range.prototype._onPinch = function (event) { - // only allow zooming when configured as zoomable and moveable - if (!(this.options.zoomable && this.options.moveable)) return; + ItemSet.prototype._onAddGroups = function(ids) { + var me = this; - this.props.touch.allowDragging = false; + ids.forEach(function (id) { + var groupData = me.groupsData.get(id); + var group = me.groups[id]; - if (event.gesture.touches.length > 1) { - if (!this.props.touch.center) { - this.props.touch.center = getPointer(event.gesture.center, this.body.dom.center); - } + if (!group) { + // check for reserved ids + if (id == UNGROUPED || id == BACKGROUND) { + throw new Error('Illegal group id. ' + id + ' is a reserved id.'); + } - var scale = 1 / (event.gesture.scale + this.scaleOffset); - var centerDate = this._pointerToDate(this.props.touch.center); + var groupOptions = Object.create(me.options); + util.extend(groupOptions, { + height: null + }); - var hiddenDuration = DateUtil.getHiddenDurationBetween(this.body.hiddenDates, this.start, this.end); - var hiddenDurationBefore = DateUtil.getHiddenDurationBefore(this.body.hiddenDates, this, centerDate); - var hiddenDurationAfter = hiddenDuration - hiddenDurationBefore; + group = new Group(id, groupData, me); + me.groups[id] = group; - // calculate new start and end - var newStart = (centerDate - hiddenDurationBefore) + (this.props.touch.start - (centerDate - hiddenDurationBefore)) * scale; - var newEnd = (centerDate + hiddenDurationAfter) + (this.props.touch.end - (centerDate + hiddenDurationAfter)) * scale; + // add items with this groupId to the new group + for (var itemId in me.items) { + if (me.items.hasOwnProperty(itemId)) { + var item = me.items[itemId]; + if (item.data.group == id) { + group.add(item); + } + } + } - // snapping times away from hidden zones - this.startToFront = 1 - scale > 0 ? false : true; // used to do the right autocorrection with periodic hidden times - this.endToFront = scale - 1 > 0 ? false : true; // used to do the right autocorrection with periodic hidden times + group.order(); + group.show(); + } + else { + // update group + group.setData(groupData); + } + }); - var safeStart = DateUtil.snapAwayFromHidden(this.body.hiddenDates, newStart, 1 - scale, true); - var safeEnd = DateUtil.snapAwayFromHidden(this.body.hiddenDates, newEnd, scale - 1, true); - if (safeStart != newStart || safeEnd != newEnd) { - this.props.touch.start = safeStart; - this.props.touch.end = safeEnd; - this.scaleOffset = 1 - event.gesture.scale; - newStart = safeStart; - newEnd = safeEnd; + this.body.emitter.emit('change', {queue: true}); + }; + + /** + * Handle removed groups + * @param {Number[]} ids + * @private + */ + ItemSet.prototype._onRemoveGroups = function(ids) { + var groups = this.groups; + ids.forEach(function (id) { + var group = groups[id]; + + if (group) { + group.hide(); + delete groups[id]; } + }); - this.setRange(newStart, newEnd, false, true); + this.markDirty(); - this.startToFront = false; // revert to default - this.endToFront = true; // revert to default - } + this.body.emitter.emit('change', {queue: true}); }; /** - * Helper function to calculate the center date for zooming - * @param {{x: Number, y: Number}} pointer - * @return {number} date + * Reorder the groups if needed + * @return {boolean} changed * @private */ - Range.prototype._pointerToDate = function (pointer) { - var conversion; - var direction = this.options.direction; + ItemSet.prototype._orderGroups = function () { + if (this.groupsData) { + // reorder the groups + var groupIds = this.groupsData.getIds({ + order: this.options.groupOrder + }); - validateDirection(direction); + var changed = !util.equalArray(groupIds, this.groupIds); + if (changed) { + // hide all groups, removes them from the DOM + var groups = this.groups; + groupIds.forEach(function (groupId) { + groups[groupId].hide(); + }); - if (direction == 'horizontal') { - return this.body.util.toTime(pointer.x).valueOf(); + // show the groups again, attach them to the DOM in correct order + groupIds.forEach(function (groupId) { + groups[groupId].show(); + }); + + this.groupIds = groupIds; + } + + return changed; } else { - var height = this.body.domProps.center.height; - conversion = this.conversion(height); - return pointer.y / conversion.scale + conversion.offset; + return false; } }; /** - * Get the pointer location relative to the location of the dom element - * @param {{pageX: Number, pageY: Number}} touch - * @param {Element} element HTML DOM element - * @return {{x: Number, y: Number}} pointer + * Add a new item + * @param {Item} item * @private */ - function getPointer (touch, element) { - return { - x: touch.pageX - util.getAbsoluteLeft(element), - y: touch.pageY - util.getAbsoluteTop(element) - }; - } + ItemSet.prototype._addItem = function(item) { + this.items[item.id] = item; + + // add to group + var groupId = this._getGroupId(item.data); + var group = this.groups[groupId]; + if (group) group.add(item); + }; /** - * Zoom the range the given scale in or out. Start and end date will - * be adjusted, and the timeline will be redrawn. You can optionally give a - * date around which to zoom. - * For example, try scale = 0.9 or 1.1 - * @param {Number} scale Scaling factor. Values above 1 will zoom out, - * values below 1 will zoom in. - * @param {Number} [center] Value representing a date around which will - * be zoomed. + * Update an existing item + * @param {Item} item + * @param {Object} itemData + * @private */ - Range.prototype.zoom = function(scale, center, delta) { - // if centerDate is not provided, take it half between start Date and end Date - if (center == null) { - center = (this.start + this.end) / 2; - } + ItemSet.prototype._updateItem = function(item, itemData) { + var oldGroupId = item.data.group; - var hiddenDuration = DateUtil.getHiddenDurationBetween(this.body.hiddenDates, this.start, this.end); - var hiddenDurationBefore = DateUtil.getHiddenDurationBefore(this.body.hiddenDates, this, center); - var hiddenDurationAfter = hiddenDuration - hiddenDurationBefore; + // update the items data (will redraw the item when displayed) + item.setData(itemData); - // calculate new start and end - var newStart = (center-hiddenDurationBefore) + (this.start - (center-hiddenDurationBefore)) * scale; - var newEnd = (center+hiddenDurationAfter) + (this.end - (center+hiddenDurationAfter)) * scale; + // update group + if (oldGroupId != item.data.group) { + var oldGroup = this.groups[oldGroupId]; + if (oldGroup) oldGroup.remove(item); - // snapping times away from hidden zones - this.startToFront = delta > 0 ? false : true; // used to do the right autocorrection with periodic hidden times - this.endToFront = -delta > 0 ? false : true; // used to do the right autocorrection with periodic hidden times - var safeStart = DateUtil.snapAwayFromHidden(this.body.hiddenDates, newStart, delta, true); - var safeEnd = DateUtil.snapAwayFromHidden(this.body.hiddenDates, newEnd, -delta, true); - if (safeStart != newStart || safeEnd != newEnd) { - newStart = safeStart; - newEnd = safeEnd; + var groupId = this._getGroupId(item.data); + var group = this.groups[groupId]; + if (group) group.add(item); } + }; - this.setRange(newStart, newEnd, false, true); + /** + * Delete an item from the ItemSet: remove it from the DOM, from the map + * with items, and from the map with visible items, and from the selection + * @param {Item} item + * @private + */ + ItemSet.prototype._removeItem = function(item) { + // remove from DOM + item.hide(); - this.startToFront = false; // revert to default - this.endToFront = true; // revert to default - }; + // remove from items + delete this.items[item.id]; + // remove from selection + var index = this.selection.indexOf(item.id); + if (index != -1) this.selection.splice(index, 1); + // remove from group + item.parent && item.parent.remove(item); + }; /** - * Move the range with a given delta to the left or right. Start and end - * value will be adjusted. For example, try delta = 0.1 or -0.1 - * @param {Number} delta Moving amount. Positive value will move right, - * negative value will move left + * Create an array containing all items being a range (having an end date) + * @param array + * @returns {Array} + * @private */ - Range.prototype.move = function(delta) { - // zoom start Date and end Date relative to the centerDate - var diff = (this.end - this.start); - - // apply new values - var newStart = this.start + diff * delta; - var newEnd = this.end + diff * delta; + ItemSet.prototype._constructByEndArray = function(array) { + var endArray = []; - // TODO: reckon with min and max range + for (var i = 0; i < array.length; i++) { + if (array[i] instanceof RangeItem) { + endArray.push(array[i]); + } + } + return endArray; + }; - this.start = newStart; - this.end = newEnd; + /** + * Register the clicked item on touch, before dragStart is initiated. + * + * dragStart is initiated from a mousemove event, which can have left the item + * already resulting in an item == null + * + * @param {Event} event + * @private + */ + ItemSet.prototype._onTouch = function (event) { + // store the touched item, used in _onDragStart + this.touchParams.item = ItemSet.itemFromTarget(event); }; /** - * Move the range to a new center point - * @param {Number} moveTo New center point of the range + * Start dragging the selected events + * @param {Event} event + * @private */ - Range.prototype.moveTo = function(moveTo) { - var center = (this.start + this.end) / 2; + ItemSet.prototype._onDragStart = function (event) { + if (!this.options.editable.updateTime && !this.options.editable.updateGroup) { + return; + } - var diff = center - moveTo; + var item = this.touchParams.item || null; + var me = this; + var props; - // calculate new start and end - var newStart = this.start - diff; - var newEnd = this.end - diff; + if (item && item.selected) { + var dragLeftItem = event.target.dragLeftItem; + var dragRightItem = event.target.dragRightItem; - this.setRange(newStart, newEnd); - }; + if (dragLeftItem) { + props = { + item: dragLeftItem, + initialX: event.gesture.center.clientX + }; - module.exports = Range; + if (me.options.editable.updateTime) { + props.start = item.data.start.valueOf(); + } + if (me.options.editable.updateGroup) { + if ('group' in item.data) props.group = item.data.group; + } + + this.touchParams.itemProps = [props]; + } + else if (dragRightItem) { + props = { + item: dragRightItem, + initialX: event.gesture.center.clientX + }; + if (me.options.editable.updateTime) { + props.end = item.data.end.valueOf(); + } + if (me.options.editable.updateGroup) { + if ('group' in item.data) props.group = item.data.group; + } -/***/ }, -/* 22 */ -/***/ function(module, exports, __webpack_require__) { + this.touchParams.itemProps = [props]; + } + else { + this.touchParams.itemProps = this.getSelection().map(function (id) { + var item = me.items[id]; + var props = { + item: item, + initialX: event.gesture.center.clientX + }; + + if (me.options.editable.updateTime) { + if ('start' in item.data) props.start = item.data.start.valueOf(); + if ('end' in item.data) props.end = item.data.end.valueOf(); + } + if (me.options.editable.updateGroup) { + if ('group' in item.data) props.group = item.data.group; + } + + return props; + }); + } - var Hammer = __webpack_require__(19); + event.stopPropagation(); + } + }; /** - * Fake a hammer.js gesture. Event can be a ScrollEvent or MouseMoveEvent - * @param {Element} element + * Drag selected items * @param {Event} event + * @private */ - exports.fakeGesture = function(element, event) { - var eventType = null; + ItemSet.prototype._onDrag = function (event) { + event.preventDefault() - // for hammer.js 1.0.5 - // var gesture = Hammer.event.collectEventData(this, eventType, event); + if (this.touchParams.itemProps) { + var me = this; + var snap = this.body.util.snap || null; + var xOffset = this.body.dom.root.offsetLeft + this.body.domProps.left.width; - // for hammer.js 1.0.6+ - var touches = Hammer.event.getTouchList(event, eventType); - var gesture = Hammer.event.collectEventData(this, eventType, touches, event); + // move + this.touchParams.itemProps.forEach(function (props) { + var newProps = {}; + var current = me.body.util.toTime(event.gesture.center.clientX - xOffset); + var initial = me.body.util.toTime(props.initialX - xOffset); + var offset = current - initial; - // on IE in standards mode, no touches are recognized by hammer.js, - // resulting in NaN values for center.pageX and center.pageY - if (isNaN(gesture.center.pageX)) { - gesture.center.pageX = event.pageX; - } - if (isNaN(gesture.center.pageY)) { - gesture.center.pageY = event.pageY; - } + if ('start' in props) { + var start = new Date(props.start + offset); + newProps.start = snap ? snap(start) : start; + } - return gesture; - }; + if ('end' in props) { + var end = new Date(props.end + offset); + newProps.end = snap ? snap(end) : end; + } + if ('group' in props) { + // drag from one group to another + var group = ItemSet.groupFromTarget(event); + newProps.group = group && group.groupId; + } -/***/ }, -/* 23 */ -/***/ function(module, exports, __webpack_require__) { + // confirm moving the item + var itemData = util.extend({}, props.item.data, newProps); + me.options.onMoving(itemData, function (itemData) { + if (itemData) { + me._updateItemProps(props.item, itemData); + } + }); + }); - /** - * Prototype for visual components - * @param {{dom: Object, domProps: Object, emitter: Emitter, range: Range}} [body] - * @param {Object} [options] - */ - function Component (body, options) { - this.options = null; - this.props = null; - } + this.stackDirty = true; // force re-stacking of all items next redraw + this.body.emitter.emit('change'); - /** - * Set options for the component. The new options will be merged into the - * current options. - * @param {Object} options - */ - Component.prototype.setOptions = function(options) { - if (options) { - util.extend(this.options, options); + event.stopPropagation(); } }; /** - * Repaint the component - * @return {boolean} Returns true if the component is resized + * Update an items properties + * @param {Item} item + * @param {Object} props Can contain properties start, end, and group. + * @private */ - Component.prototype.redraw = function() { - // should be implemented by the component - return false; + ItemSet.prototype._updateItemProps = function(item, props) { + // TODO: copy all properties from props to item? (also new ones) + if ('start' in props) item.data.start = props.start; + if ('end' in props) item.data.end = props.end; + if ('group' in props && item.data.group != props.group) { + this._moveToGroup(item, props.group) + } }; /** - * Destroy the component. Cleanup DOM and event listeners + * Move an item to another group + * @param {Item} item + * @param {String | Number} groupId + * @private */ - Component.prototype.destroy = function() { - // should be implemented by the component + ItemSet.prototype._moveToGroup = function(item, groupId) { + var group = this.groups[groupId]; + if (group && group.groupId != item.data.group) { + var oldGroup = item.parent; + oldGroup.remove(item); + oldGroup.order(); + group.add(item); + group.order(); + + item.data.group = group.groupId; + } }; /** - * Test whether the component is resized since the last time _isResized() was - * called. - * @return {Boolean} Returns true if the component is resized - * @protected + * End of dragging selected items + * @param {Event} event + * @private */ - Component.prototype._isResized = function() { - var resized = (this.props._previousWidth !== this.props.width || - this.props._previousHeight !== this.props.height); + ItemSet.prototype._onDragEnd = function (event) { + event.preventDefault() - this.props._previousWidth = this.props.width; - this.props._previousHeight = this.props.height; + if (this.touchParams.itemProps) { + // prepare a change set for the changed items + var changes = [], + me = this, + dataset = this.itemsData.getDataSet(); - return resized; - }; + var itemProps = this.touchParams.itemProps ; + this.touchParams.itemProps = null; + itemProps.forEach(function (props) { + var id = props.item.id, + itemData = me.itemsData.get(id, me.itemOptions); - module.exports = Component; + var changed = false; + if ('start' in props.item.data) { + changed = (props.start != props.item.data.start.valueOf()); + itemData.start = util.convert(props.item.data.start, + dataset._options.type && dataset._options.type.start || 'Date'); + } + if ('end' in props.item.data) { + changed = changed || (props.end != props.item.data.end.valueOf()); + itemData.end = util.convert(props.item.data.end, + dataset._options.type && dataset._options.type.end || 'Date'); + } + if ('group' in props.item.data) { + changed = changed || (props.group != props.item.data.group); + itemData.group = props.item.data.group; + } + // only apply changes when start or end is actually changed + if (changed) { + me.options.onMove(itemData, function (itemData) { + if (itemData) { + // apply changes + itemData[dataset._fieldId] = id; // ensure the item contains its id (can be undefined) + changes.push(itemData); + } + else { + // restore original values + me._updateItemProps(props.item, props); -/***/ }, -/* 24 */ -/***/ function(module, exports, __webpack_require__) { + me.stackDirty = true; // force re-stacking of all items next redraw + me.body.emitter.emit('change'); + } + }); + } + }); - /** - * Created by Alex on 10/3/2014. - */ - var moment = __webpack_require__(2); + // apply the changes to the data (if there are changes) + if (changes.length) { + dataset.update(changes); + } + event.stopPropagation(); + } + }; /** - * used in Core to convert the options into a volatile variable - * - * @param Core + * Handle selecting/deselecting an item when tapping it + * @param {Event} event + * @private */ - exports.convertHiddenOptions = function(body, hiddenDates) { - body.hiddenDates = []; - if (hiddenDates) { - if (Array.isArray(hiddenDates) == true) { - for (var i = 0; i < hiddenDates.length; i++) { - if (hiddenDates[i].repeat === undefined) { - var dateItem = {}; - dateItem.start = moment(hiddenDates[i].start).toDate().valueOf(); - dateItem.end = moment(hiddenDates[i].end).toDate().valueOf(); - body.hiddenDates.push(dateItem); - } - } - body.hiddenDates.sort(function (a, b) { - return a.start - b.start; - }); // sort by start time - } + ItemSet.prototype._onSelectItem = function (event) { + if (!this.options.selectable) return; + + var ctrlKey = event.gesture.srcEvent && event.gesture.srcEvent.ctrlKey; + var shiftKey = event.gesture.srcEvent && event.gesture.srcEvent.shiftKey; + if (ctrlKey || shiftKey) { + this._onMultiSelectItem(event); + return; } - }; + var oldSelection = this.getSelection(); + + var item = ItemSet.itemFromTarget(event); + var selection = item ? [item.id] : []; + this.setSelection(selection); + + var newSelection = this.getSelection(); + + // emit a select event, + // except when old selection is empty and new selection is still empty + if (newSelection.length > 0 || oldSelection.length > 0) { + this.body.emitter.emit('select', { + items: newSelection + }); + } + }; /** - * create new entrees for the repeating hidden dates - * @param body - * @param hiddenDates + * Handle creation and updates of an item on double tap + * @param event + * @private */ - exports.updateHiddenDates = function (body, hiddenDates) { - if (hiddenDates && body.domProps.centerContainer.width !== undefined) { - exports.convertHiddenOptions(body, hiddenDates); + ItemSet.prototype._onAddItem = function (event) { + if (!this.options.selectable) return; + if (!this.options.editable.add) return; - var start = moment(body.range.start); - var end = moment(body.range.end); + var me = this, + snap = this.body.util.snap || null, + item = ItemSet.itemFromTarget(event); - var totalRange = (body.range.end - body.range.start); - var pixelTime = totalRange / body.domProps.centerContainer.width; + if (item) { + // update item - for (var i = 0; i < hiddenDates.length; i++) { - if (hiddenDates[i].repeat !== undefined) { - var startDate = moment(hiddenDates[i].start); - var endDate = moment(hiddenDates[i].end); + // execute async handler to update the item (or cancel it) + var itemData = me.itemsData.get(item.id); // get a clone of the data from the dataset + this.options.onUpdate(itemData, function (itemData) { + if (itemData) { + me.itemsData.getDataSet().update(itemData); + } + }); + } + else { + // add item + var xAbs = util.getAbsoluteLeft(this.dom.frame); + var x = event.gesture.center.pageX - xAbs; + var start = this.body.util.toTime(x); + var newItem = { + start: snap ? snap(start) : start, + content: 'new item' + }; - if (startDate._d == "Invalid Date") { - throw new Error("Supplied start date is not valid: " + hiddenDates[i].start); - } - if (endDate._d == "Invalid Date") { - throw new Error("Supplied end date is not valid: " + hiddenDates[i].end); - } + // when default type is a range, add a default end date to the new item + if (this.options.type === 'range') { + var end = this.body.util.toTime(x + this.props.width / 5); + newItem.end = snap ? snap(end) : end; + } - var duration = endDate - startDate; - if (duration >= 4 * pixelTime) { + newItem[this.itemsData._fieldId] = util.randomUUID(); - var offset = 0; - var runUntil = end.clone(); - switch (hiddenDates[i].repeat) { - case "daily": // case of time - if (startDate.day() != endDate.day()) { - offset = 1; - } - startDate.dayOfYear(start.dayOfYear()); - startDate.year(start.year()); - startDate.subtract(7,'days'); + var group = ItemSet.groupFromTarget(event); + if (group) { + newItem.group = group.groupId; + } - endDate.dayOfYear(start.dayOfYear()); - endDate.year(start.year()); - endDate.subtract(7 - offset,'days'); + // execute async handler to customize (or cancel) adding an item + this.options.onAdd(newItem, function (item) { + if (item) { + me.itemsData.getDataSet().add(item); + // TODO: need to trigger a redraw? + } + }); + } + }; - runUntil.add(1, 'weeks'); - break; - case "weekly": - var dayOffset = endDate.diff(startDate,'days') - var day = startDate.day(); + /** + * Handle selecting/deselecting multiple items when holding an item + * @param {Event} event + * @private + */ + ItemSet.prototype._onMultiSelectItem = function (event) { + if (!this.options.selectable) return; - // set the start date to the range.start - startDate.date(start.date()); - startDate.month(start.month()); - startDate.year(start.year()); - endDate = startDate.clone(); + var selection, + item = ItemSet.itemFromTarget(event); - // force - startDate.day(day); - endDate.day(day); - endDate.add(dayOffset,'days'); + if (item) { + // multi select items + selection = this.getSelection(); // current selection - startDate.subtract(1,'weeks'); - endDate.subtract(1,'weeks'); + var shiftKey = event.gesture.touches[0] && event.gesture.touches[0].shiftKey || false; + if (shiftKey) { + // select all items between the old selection and the tapped item - runUntil.add(1, 'weeks'); - break - case "monthly": - if (startDate.month() != endDate.month()) { - offset = 1; - } - startDate.month(start.month()); - startDate.year(start.year()); - startDate.subtract(1,'months'); + // determine the selection range + selection.push(item.id); + var range = ItemSet._getItemRange(this.itemsData.get(selection, this.itemOptions)); - endDate.month(start.month()); - endDate.year(start.year()); - endDate.subtract(1,'months'); - endDate.add(offset,'months'); + // select all items within the selection range + selection = []; + for (var id in this.items) { + if (this.items.hasOwnProperty(id)) { + var _item = this.items[id]; + var start = _item.data.start; + var end = (_item.data.end !== undefined) ? _item.data.end : start; - runUntil.add(1, 'months'); - break; - case "yearly": - if (startDate.year() != endDate.year()) { - offset = 1; - } - startDate.year(start.year()); - startDate.subtract(1,'years'); - endDate.year(start.year()); - endDate.subtract(1,'years'); - endDate.add(offset,'years'); - - runUntil.add(1, 'years'); - break; - default: - console.log("Wrong repeat format, allowed are: daily, weekly, monthly, yearly. Given:", hiddenDates[i].repeat); - return; - } - while (startDate < runUntil) { - body.hiddenDates.push({start: startDate.valueOf(), end: endDate.valueOf()}); - switch (hiddenDates[i].repeat) { - case "daily": - startDate.add(1, 'days'); - endDate.add(1, 'days'); - break; - case "weekly": - startDate.add(1, 'weeks'); - endDate.add(1, 'weeks'); - break - case "monthly": - startDate.add(1, 'months'); - endDate.add(1, 'months'); - break; - case "yearly": - startDate.add(1, 'y'); - endDate.add(1, 'y'); - break; - default: - console.log("Wrong repeat format, allowed are: daily, weekly, monthly, yearly. Given:", hiddenDates[i].repeat); - return; - } + if (start >= range.min && end <= range.max) { + selection.push(_item.id); // do not use id but item.id, id itself is stringified } - body.hiddenDates.push({start: startDate.valueOf(), end: endDate.valueOf()}); } } } - // remove duplicates, merge where possible - exports.removeDuplicates(body); - // ensure the new positions are not on hidden dates - var startHidden = exports.isHidden(body.range.start, body.hiddenDates); - var endHidden = exports.isHidden(body.range.end,body.hiddenDates); - var rangeStart = body.range.start; - var rangeEnd = body.range.end; - if (startHidden.hidden == true) {rangeStart = body.range.startToFront == true ? startHidden.startDate - 1 : startHidden.endDate + 1;} - if (endHidden.hidden == true) {rangeEnd = body.range.endToFront == true ? endHidden.startDate - 1 : endHidden.endDate + 1;} - if (startHidden.hidden == true || endHidden.hidden == true) { - body.range._applyRange(rangeStart, rangeEnd); + else { + // add/remove this item from the current selection + var index = selection.indexOf(item.id); + if (index == -1) { + // item is not yet selected -> select it + selection.push(item.id); + } + else { + // item is already selected -> deselect it + selection.splice(index, 1); + } } - } - } + this.setSelection(selection); + this.body.emitter.emit('select', { + items: this.getSelection() + }); + } + }; /** - * remove duplicates from the hidden dates list. Duplicates are evil. They mess everything up. - * Scales with N^2 - * @param body + * Calculate the time range of a list of items + * @param {Array.} itemsData + * @return {{min: Date, max: Date}} Returns the range of the provided items + * @private */ - exports.removeDuplicates = function(body) { - var hiddenDates = body.hiddenDates; - var safeDates = []; - for (var i = 0; i < hiddenDates.length; i++) { - for (var j = 0; j < hiddenDates.length; j++) { - if (i != j && hiddenDates[j].remove != true && hiddenDates[i].remove != true) { - // j inside i - if (hiddenDates[j].start >= hiddenDates[i].start && hiddenDates[j].end <= hiddenDates[i].end) { - hiddenDates[j].remove = true; - } - // j start inside i - else if (hiddenDates[j].start >= hiddenDates[i].start && hiddenDates[j].start <= hiddenDates[i].end) { - hiddenDates[i].end = hiddenDates[j].end; - hiddenDates[j].remove = true; - } - // j end inside i - else if (hiddenDates[j].end >= hiddenDates[i].start && hiddenDates[j].end <= hiddenDates[i].end) { - hiddenDates[i].start = hiddenDates[j].start; - hiddenDates[j].remove = true; - } + ItemSet._getItemRange = function(itemsData) { + var max = null; + var min = null; + + itemsData.forEach(function (data) { + if (min == null || data.start < min) { + min = data.start; + } + + if (data.end != undefined) { + if (max == null || data.end > max) { + max = data.end; + } + } + else { + if (max == null || data.start > max) { + max = data.start; } } + }); + + return { + min: min, + max: max } + }; - for (var i = 0; i < hiddenDates.length; i++) { - if (hiddenDates[i].remove !== true) { - safeDates.push(hiddenDates[i]); + /** + * Find an item from an event target: + * searches for the attribute 'timeline-item' in the event target's element tree + * @param {Event} event + * @return {Item | null} item + */ + ItemSet.itemFromTarget = function(event) { + var target = event.target; + while (target) { + if (target.hasOwnProperty('timeline-item')) { + return target['timeline-item']; } + target = target.parentNode; } - body.hiddenDates = safeDates; - body.hiddenDates.sort(function (a, b) { - return a.start - b.start; - }); // sort by start time - } - - exports.printDates = function(dates) { - for (var i =0; i < dates.length; i++) { - console.log(i, new Date(dates[i].start),new Date(dates[i].end), dates[i].start, dates[i].end, dates[i].remove); - } - } + return null; + }; /** - * Used in TimeStep to avoid the hidden times. - * @param timeStep - * @param previousTime + * Find the Group from an event target: + * searches for the attribute 'timeline-group' in the event target's element tree + * @param {Event} event + * @return {Group | null} group */ - exports.stepOverHiddenDates = function(timeStep, previousTime) { - var stepInHidden = false; - var currentValue = timeStep.current.valueOf(); - for (var i = 0; i < timeStep.hiddenDates.length; i++) { - var startDate = timeStep.hiddenDates[i].start; - var endDate = timeStep.hiddenDates[i].end; - if (currentValue >= startDate && currentValue < endDate) { - stepInHidden = true; - break; + ItemSet.groupFromTarget = function(event) { + var target = event.target; + while (target) { + if (target.hasOwnProperty('timeline-group')) { + return target['timeline-group']; } + target = target.parentNode; } - if (stepInHidden == true && currentValue < timeStep._end.valueOf() && currentValue != previousTime) { - var prevValue = moment(previousTime); - var newValue = moment(endDate); - //check if the next step should be major - if (prevValue.year() != newValue.year()) {timeStep.switchedYear = true;} - else if (prevValue.month() != newValue.month()) {timeStep.switchedMonth = true;} - else if (prevValue.dayOfYear() != newValue.dayOfYear()) {timeStep.switchedDay = true;} + return null; + }; - timeStep.current = newValue.toDate(); + /** + * Find the ItemSet from an event target: + * searches for the attribute 'timeline-itemset' in the event target's element tree + * @param {Event} event + * @return {ItemSet | null} item + */ + ItemSet.itemSetFromTarget = function(event) { + var target = event.target; + while (target) { + if (target.hasOwnProperty('timeline-itemset')) { + return target['timeline-itemset']; + } + target = target.parentNode; } + + return null; }; + module.exports = ItemSet; - ///** - // * Used in TimeStep to avoid the hidden times. - // * @param timeStep - // * @param previousTime - // */ - //exports.checkFirstStep = function(timeStep) { - // var stepInHidden = false; - // var currentValue = timeStep.current.valueOf(); - // for (var i = 0; i < timeStep.hiddenDates.length; i++) { - // var startDate = timeStep.hiddenDates[i].start; - // var endDate = timeStep.hiddenDates[i].end; - // if (currentValue >= startDate && currentValue < endDate) { - // stepInHidden = true; - // break; - // } - // } - // - // if (stepInHidden == true && currentValue <= timeStep._end.valueOf()) { - // var newValue = moment(endDate); - // timeStep.current = newValue.toDate(); - // } - //}; + +/***/ }, +/* 28 */ +/***/ function(module, exports, __webpack_require__) { + + var util = __webpack_require__(1); + var DOMutil = __webpack_require__(2); + var Component = __webpack_require__(20); /** - * replaces the Core toScreen methods - * @param Core - * @param time - * @param width - * @returns {number} + * Legend for Graph2d */ - exports.toScreen = function(Core, time, width) { - if (Core.body.hiddenDates.length == 0) { - var conversion = Core.range.conversion(width); - return (time.valueOf() - conversion.offset) * conversion.scale; - } - else { - var hidden = exports.isHidden(time, Core.body.hiddenDates) - if (hidden.hidden == true) { - time = hidden.startDate; + function Legend(body, options, side, linegraphOptions) { + this.body = body; + this.defaultOptions = { + enabled: true, + icons: true, + iconSize: 20, + iconSpacing: 6, + left: { + visible: true, + position: 'top-left' // top/bottom - left,center,right + }, + right: { + visible: true, + position: 'top-left' // top/bottom - left,center,right } + } + this.side = side; + this.options = util.extend({},this.defaultOptions); + this.linegraphOptions = linegraphOptions; - var duration = exports.getHiddenDurationBetween(Core.body.hiddenDates, Core.range.start, Core.range.end); - time = exports.correctTimeForHidden(Core.body.hiddenDates, Core.range, time); + this.svgElements = {}; + this.dom = {}; + this.groups = {}; + this.amountOfGroups = 0; + this._create(); - var conversion = Core.range.conversion(width, duration); - return (time.valueOf() - conversion.offset) * conversion.scale; - } - }; + this.setOptions(options); + } + Legend.prototype = new Component(); - /** - * Replaces the core toTime methods - * @param body - * @param range - * @param x - * @param width - * @returns {Date} - */ - exports.toTime = function(Core, x, width) { - if (Core.body.hiddenDates.length == 0) { - var conversion = Core.range.conversion(width); - return new Date(x / conversion.scale + conversion.offset); + Legend.prototype.clear = function() { + this.groups = {}; + this.amountOfGroups = 0; + } + + Legend.prototype.addGroup = function(label, graphOptions) { + + if (!this.groups.hasOwnProperty(label)) { + this.groups[label] = graphOptions; } - else { - var hiddenDuration = exports.getHiddenDurationBetween(Core.body.hiddenDates, Core.range.start, Core.range.end); - var totalDuration = Core.range.end - Core.range.start - hiddenDuration; - var partialDuration = totalDuration * x / width; - var accumulatedHiddenDuration = exports.getAccumulatedHiddenDuration(Core.body.hiddenDates, Core.range, partialDuration); + this.amountOfGroups += 1; + }; - var newTime = new Date(accumulatedHiddenDuration + partialDuration + Core.range.start); - return newTime; + Legend.prototype.updateGroup = function(label, graphOptions) { + this.groups[label] = graphOptions; + }; + + Legend.prototype.removeGroup = function(label) { + if (this.groups.hasOwnProperty(label)) { + delete this.groups[label]; + this.amountOfGroups -= 1; } }; + Legend.prototype._create = function() { + this.dom.frame = document.createElement('div'); + this.dom.frame.className = 'legend'; + this.dom.frame.style.position = "absolute"; + this.dom.frame.style.top = "10px"; + this.dom.frame.style.display = "block"; + + this.dom.textArea = document.createElement('div'); + this.dom.textArea.className = 'legendText'; + this.dom.textArea.style.position = "relative"; + this.dom.textArea.style.top = "0px"; + + this.svg = document.createElementNS('http://www.w3.org/2000/svg',"svg"); + this.svg.style.position = 'absolute'; + this.svg.style.top = 0 +'px'; + this.svg.style.width = this.options.iconSize + 5 + 'px'; + this.svg.style.height = '100%'; + + this.dom.frame.appendChild(this.svg); + this.dom.frame.appendChild(this.dom.textArea); + }; /** - * Support function - * - * @param hiddenDates - * @param range - * @returns {number} + * Hide the component from the DOM */ - exports.getHiddenDurationBetween = function(hiddenDates, start, end) { - var duration = 0; - for (var i = 0; i < hiddenDates.length; i++) { - var startDate = hiddenDates[i].start; - var endDate = hiddenDates[i].end; - // if time after the cutout, and the - if (startDate >= start && endDate < end) { - duration += endDate - startDate; - } + Legend.prototype.hide = function() { + // remove the frame containing the items + if (this.dom.frame.parentNode) { + this.dom.frame.parentNode.removeChild(this.dom.frame); } - return duration; }; - /** - * Support function - * @param hiddenDates - * @param range - * @param time - * @returns {{duration: number, time: *, offset: number}} + * Show the component in the DOM (when not already visible). + * @return {Boolean} changed */ - exports.correctTimeForHidden = function(hiddenDates, range, time) { - time = moment(time).toDate().valueOf(); - time -= exports.getHiddenDurationBefore(hiddenDates,range,time); - return time; + Legend.prototype.show = function() { + // show frame containing the items + if (!this.dom.frame.parentNode) { + this.body.dom.center.appendChild(this.dom.frame); + } }; - exports.getHiddenDurationBefore = function(hiddenDates, range, time) { - var timeOffset = 0; - time = moment(time).toDate().valueOf(); + Legend.prototype.setOptions = function(options) { + var fields = ['enabled','orientation','icons','left','right']; + util.selectiveDeepExtend(fields, this.options, options); + }; - for (var i = 0; i < hiddenDates.length; i++) { - var startDate = hiddenDates[i].start; - var endDate = hiddenDates[i].end; - // if time after the cutout, and the - if (startDate >= range.start && endDate < range.end) { - if (time >= endDate) { - timeOffset += (endDate - startDate); + Legend.prototype.redraw = function() { + var activeGroups = 0; + for (var groupId in this.groups) { + if (this.groups.hasOwnProperty(groupId)) { + if (this.groups[groupId].visible == true && (this.linegraphOptions.visibility[groupId] === undefined || this.linegraphOptions.visibility[groupId] == true)) { + activeGroups++; } } } - return timeOffset; - } - /** - * sum the duration from start to finish, including the hidden duration, - * until the required amount has been reached, return the accumulated hidden duration - * @param hiddenDates - * @param range - * @param time - * @returns {{duration: number, time: *, offset: number}} - */ - exports.getAccumulatedHiddenDuration = function(hiddenDates, range, requiredDuration) { - var hiddenDuration = 0; - var duration = 0; - var previousPoint = range.start; - //exports.printDates(hiddenDates) - for (var i = 0; i < hiddenDates.length; i++) { - var startDate = hiddenDates[i].start; - var endDate = hiddenDates[i].end; - // if time after the cutout, and the - if (startDate >= range.start && endDate < range.end) { - duration += startDate - previousPoint; - previousPoint = endDate; - if (duration >= requiredDuration) { - break; - } - else { - hiddenDuration += endDate - startDate; + if (this.options[this.side].visible == false || this.amountOfGroups == 0 || this.options.enabled == false || activeGroups == 0) { + this.hide(); + } + else { + this.show(); + if (this.options[this.side].position == 'top-left' || this.options[this.side].position == 'bottom-left') { + this.dom.frame.style.left = '4px'; + this.dom.frame.style.textAlign = "left"; + this.dom.textArea.style.textAlign = "left"; + this.dom.textArea.style.left = (this.options.iconSize + 15) + 'px'; + this.dom.textArea.style.right = ''; + this.svg.style.left = 0 +'px'; + this.svg.style.right = ''; + } + else { + this.dom.frame.style.right = '4px'; + this.dom.frame.style.textAlign = "right"; + this.dom.textArea.style.textAlign = "right"; + this.dom.textArea.style.right = (this.options.iconSize + 15) + 'px'; + this.dom.textArea.style.left = ''; + this.svg.style.right = 0 +'px'; + this.svg.style.left = ''; + } + + if (this.options[this.side].position == 'top-left' || this.options[this.side].position == 'top-right') { + this.dom.frame.style.top = 4 - Number(this.body.dom.center.style.top.replace("px","")) + 'px'; + this.dom.frame.style.bottom = ''; + } + else { + var scrollableHeight = this.body.domProps.center.height - this.body.domProps.centerContainer.height; + this.dom.frame.style.bottom = 4 + scrollableHeight + Number(this.body.dom.center.style.top.replace("px","")) + 'px'; + this.dom.frame.style.top = ''; + } + + if (this.options.icons == false) { + this.dom.frame.style.width = this.dom.textArea.offsetWidth + 10 + 'px'; + this.dom.textArea.style.right = ''; + this.dom.textArea.style.left = ''; + this.svg.style.width = '0px'; + } + else { + this.dom.frame.style.width = this.options.iconSize + 15 + this.dom.textArea.offsetWidth + 10 + 'px' + this.drawLegendIcons(); + } + + var content = ''; + for (var groupId in this.groups) { + if (this.groups.hasOwnProperty(groupId)) { + if (this.groups[groupId].visible == true && (this.linegraphOptions.visibility[groupId] === undefined || this.linegraphOptions.visibility[groupId] == true)) { + content += this.groups[groupId].content + '
'; + } } } + this.dom.textArea.innerHTML = content; + this.dom.textArea.style.lineHeight = ((0.75 * this.options.iconSize) + this.options.iconSpacing) + 'px'; } - - return hiddenDuration; }; + Legend.prototype.drawLegendIcons = function() { + if (this.dom.frame.parentNode) { + DOMutil.prepareElements(this.svgElements); + var padding = window.getComputedStyle(this.dom.frame).paddingTop; + var iconOffset = Number(padding.replace('px','')); + var x = iconOffset; + var iconWidth = this.options.iconSize; + var iconHeight = 0.75 * this.options.iconSize; + var y = iconOffset + 0.5 * iconHeight + 3; + this.svg.style.width = iconWidth + 5 + iconOffset + 'px'; - /** - * used to step over to either side of a hidden block. Correction is disabled on tablets, might be set to true - * @param hiddenDates - * @param time - * @param direction - * @param correctionEnabled - * @returns {*} - */ - exports.snapAwayFromHidden = function(hiddenDates, time, direction, correctionEnabled) { - var isHidden = exports.isHidden(time, hiddenDates); - if (isHidden.hidden == true) { - if (direction < 0) { - if (correctionEnabled == true) { - return isHidden.startDate - (isHidden.endDate - time) - 1; - } - else { - return isHidden.startDate - 1; - } - } - else { - if (correctionEnabled == true) { - return isHidden.endDate + (time - isHidden.startDate) + 1; - } - else { - return isHidden.endDate + 1; + for (var groupId in this.groups) { + if (this.groups.hasOwnProperty(groupId)) { + if (this.groups[groupId].visible == true && (this.linegraphOptions.visibility[groupId] === undefined || this.linegraphOptions.visibility[groupId] == true)) { + this.groups[groupId].drawIcon(x, y, this.svgElements, this.svg, iconWidth, iconHeight); + y += iconHeight + this.options.iconSpacing; + } } } + + DOMutil.cleanupElements(this.svgElements); } - else { - return time; - } + }; - } + module.exports = Legend; - /** - * Check if a time is hidden - * - * @param time - * @param hiddenDates - * @returns {{hidden: boolean, startDate: Window.start, endDate: *}} - */ - exports.isHidden = function(time, hiddenDates) { - for (var i = 0; i < hiddenDates.length; i++) { - var startDate = hiddenDates[i].start; - var endDate = hiddenDates[i].end; - - if (time >= startDate && time < endDate) { // if the start is entering a hidden zone - return {hidden: true, startDate: startDate, endDate: endDate}; - break; - } - } - return {hidden: false, startDate: startDate, endDate: endDate}; - } - /***/ }, -/* 25 */ +/* 29 */ /***/ function(module, exports, __webpack_require__) { - var Emitter = __webpack_require__(11); - var Hammer = __webpack_require__(19); var util = __webpack_require__(1); - var DataSet = __webpack_require__(7); - var DataView = __webpack_require__(9); - var Range = __webpack_require__(21); - var ItemSet = __webpack_require__(26); - var Activator = __webpack_require__(35); - var DateUtil = __webpack_require__(24); + var DOMutil = __webpack_require__(2); + var DataSet = __webpack_require__(3); + var DataView = __webpack_require__(4); + var Component = __webpack_require__(20); + var DataAxis = __webpack_require__(23); + var GraphGroup = __webpack_require__(24); + var Legend = __webpack_require__(28); + var BarGraphFunctions = __webpack_require__(52); + + var UNGROUPED = '__ungrouped__'; // reserved group id for ungrouped items /** - * Create a timeline visualization - * @param {HTMLElement} container - * @param {vis.DataSet | Array | google.visualization.DataTable} [items] - * @param {Object} [options] See Core.setOptions for the available options. + * This is the constructor of the LineGraph. It requires a Timeline body and options. + * + * @param body + * @param options * @constructor */ - function Core () {} + function LineGraph(body, options) { + this.id = util.randomUUID(); + this.body = body; - // turn Core into an event emitter - Emitter(Core.prototype); + this.defaultOptions = { + yAxisOrientation: 'left', + defaultGroup: 'default', + sort: true, + sampling: true, + graphHeight: '400px', + shaded: { + enabled: false, + orientation: 'bottom' // top, bottom + }, + style: 'line', // line, bar + barChart: { + width: 50, + handleOverlap: 'overlap', + align: 'center' // left, center, right + }, + catmullRom: { + enabled: true, + parametrization: 'centripetal', // uniform (alpha = 0.0), chordal (alpha = 1.0), centripetal (alpha = 0.5) + alpha: 0.5 + }, + drawPoints: { + enabled: true, + size: 6, + style: 'square' // square, circle + }, + dataAxis: { + showMinorLabels: true, + showMajorLabels: true, + icons: false, + width: '40px', + visible: true, + alignZeros: true, + customRange: { + left: {min:undefined, max:undefined}, + right: {min:undefined, max:undefined} + } + //, these options are not set by default, but this shows the format they will be in + //format: { + // left: {decimals: 2}, + // right: {decimals: 2} + //}, + //title: { + // left: { + // text: 'left', + // style: 'color:black;' + // }, + // right: { + // text: 'right', + // style: 'color:black;' + // } + //} + }, + legend: { + enabled: false, + icons: true, + left: { + visible: true, + position: 'top-left' // top/bottom - left,right + }, + right: { + visible: true, + position: 'top-right' // top/bottom - left,right + } + }, + groups: { + visibility: {} + } + }; - /** - * Create the main DOM for the Core: a root panel containing left, right, - * top, bottom, content, and background panel. - * @param {Element} container The container element where the Core will - * be attached. - * @private - */ - Core.prototype._create = function (container) { + // options is shared by this ItemSet and all its items + this.options = util.extend({}, this.defaultOptions); this.dom = {}; + this.props = {}; + this.hammer = null; + this.groups = {}; + this.abortedGraphUpdate = false; + this.updateSVGheight = false; + this.updateSVGheightOnResize = false; - this.dom.root = document.createElement('div'); - this.dom.background = document.createElement('div'); - this.dom.backgroundVertical = document.createElement('div'); - this.dom.backgroundHorizontal = document.createElement('div'); - this.dom.centerContainer = document.createElement('div'); - this.dom.leftContainer = document.createElement('div'); - this.dom.rightContainer = document.createElement('div'); - this.dom.center = document.createElement('div'); - this.dom.left = document.createElement('div'); - this.dom.right = document.createElement('div'); - this.dom.top = document.createElement('div'); - this.dom.bottom = document.createElement('div'); - this.dom.shadowTop = document.createElement('div'); - this.dom.shadowBottom = document.createElement('div'); - this.dom.shadowTopLeft = document.createElement('div'); - this.dom.shadowBottomLeft = document.createElement('div'); - this.dom.shadowTopRight = document.createElement('div'); - this.dom.shadowBottomRight = document.createElement('div'); + var me = this; + this.itemsData = null; // DataSet + this.groupsData = null; // DataSet - this.dom.root.className = 'vis timeline root'; - this.dom.background.className = 'vispanel background'; - this.dom.backgroundVertical.className = 'vispanel background vertical'; - this.dom.backgroundHorizontal.className = 'vispanel background horizontal'; - this.dom.centerContainer.className = 'vispanel center'; - this.dom.leftContainer.className = 'vispanel left'; - this.dom.rightContainer.className = 'vispanel right'; - this.dom.top.className = 'vispanel top'; - this.dom.bottom.className = 'vispanel bottom'; - this.dom.left.className = 'content'; - this.dom.center.className = 'content'; - this.dom.right.className = 'content'; - this.dom.shadowTop.className = 'shadow top'; - this.dom.shadowBottom.className = 'shadow bottom'; - this.dom.shadowTopLeft.className = 'shadow top'; - this.dom.shadowBottomLeft.className = 'shadow bottom'; - this.dom.shadowTopRight.className = 'shadow top'; - this.dom.shadowBottomRight.className = 'shadow bottom'; + // listeners for the DataSet of the items + this.itemListeners = { + 'add': function (event, params, senderId) { + me._onAdd(params.items); + }, + 'update': function (event, params, senderId) { + me._onUpdate(params.items); + }, + 'remove': function (event, params, senderId) { + me._onRemove(params.items); + } + }; - this.dom.root.appendChild(this.dom.background); - this.dom.root.appendChild(this.dom.backgroundVertical); - this.dom.root.appendChild(this.dom.backgroundHorizontal); - this.dom.root.appendChild(this.dom.centerContainer); - this.dom.root.appendChild(this.dom.leftContainer); - this.dom.root.appendChild(this.dom.rightContainer); - this.dom.root.appendChild(this.dom.top); - this.dom.root.appendChild(this.dom.bottom); + // listeners for the DataSet of the groups + this.groupListeners = { + 'add': function (event, params, senderId) { + me._onAddGroups(params.items); + }, + 'update': function (event, params, senderId) { + me._onUpdateGroups(params.items); + }, + 'remove': function (event, params, senderId) { + me._onRemoveGroups(params.items); + } + }; - this.dom.centerContainer.appendChild(this.dom.center); - this.dom.leftContainer.appendChild(this.dom.left); - this.dom.rightContainer.appendChild(this.dom.right); + this.items = {}; // object with an Item for every data item + this.selection = []; // list with the ids of all selected nodes + this.lastStart = this.body.range.start; + this.touchParams = {}; // stores properties while dragging - this.dom.centerContainer.appendChild(this.dom.shadowTop); - this.dom.centerContainer.appendChild(this.dom.shadowBottom); - this.dom.leftContainer.appendChild(this.dom.shadowTopLeft); - this.dom.leftContainer.appendChild(this.dom.shadowBottomLeft); - this.dom.rightContainer.appendChild(this.dom.shadowTopRight); - this.dom.rightContainer.appendChild(this.dom.shadowBottomRight); + this.svgElements = {}; + this.setOptions(options); + this.groupsUsingDefaultStyles = [0]; + this.COUNTER = 0; + this.body.emitter.on('rangechanged', function() { + me.lastStart = me.body.range.start; + me.svg.style.left = util.option.asSize(-me.props.width); + me.redraw.call(me,true); + }); - this.on('rangechange', this.redraw.bind(this)); - this.on('touch', this._onTouch.bind(this)); - this.on('pinch', this._onPinch.bind(this)); - this.on('dragstart', this._onDragStart.bind(this)); - this.on('drag', this._onDrag.bind(this)); + // create the HTML DOM + this._create(); + this.framework = {svg: this.svg, svgElements: this.svgElements, options: this.options, groups: this.groups}; + this.body.emitter.emit('change'); - var me = this; - this.on('change', function (properties) { - if (properties && properties.queue == true) { - // redraw once on next tick - if (!me._redrawTimer) { - me._redrawTimer = setTimeout(function () { - me._redrawTimer = null; - me.redraw(); - }, 0) - } - } - else { - // redraw immediately - me.redraw(); - } - }); + } - // create event listeners for all interesting events, these events will be - // emitted via emitter - this.hammer = Hammer(this.dom.root, { - preventDefault: true - }); - this.listeners = {}; + LineGraph.prototype = new Component(); - var events = [ - 'touch', 'pinch', - 'tap', 'doubletap', 'hold', - 'dragstart', 'drag', 'dragend', - 'mousewheel', 'DOMMouseScroll' // DOMMouseScroll is needed for Firefox - ]; - events.forEach(function (event) { - var listener = function () { - var args = [event].concat(Array.prototype.slice.call(arguments, 0)); - if (me.isActive()) { - me.emit.apply(me, args); - } - }; - me.hammer.on(event, listener); - me.listeners[event] = listener; - }); + /** + * Create the HTML DOM for the ItemSet + */ + LineGraph.prototype._create = function(){ + var frame = document.createElement('div'); + frame.className = 'LineGraph'; + this.dom.frame = frame; - // size properties of each of the panels - this.props = { - root: {}, - background: {}, - centerContainer: {}, - leftContainer: {}, - rightContainer: {}, - center: {}, - left: {}, - right: {}, - top: {}, - bottom: {}, - border: {}, - scrollTop: 0, - scrollTopMin: 0 - }; - this.touch = {}; // store state information needed for touch events + // create svg element for graph drawing. + this.svg = document.createElementNS('http://www.w3.org/2000/svg','svg'); + this.svg.style.position = 'relative'; + this.svg.style.height = ('' + this.options.graphHeight).replace('px','') + 'px'; + this.svg.style.display = 'block'; + frame.appendChild(this.svg); - this.redrawCount = 0; + // data axis + this.options.dataAxis.orientation = 'left'; + this.yAxisLeft = new DataAxis(this.body, this.options.dataAxis, this.svg, this.options.groups); - // attach the root panel to the provided container - if (!container) throw new Error('No container provided'); - container.appendChild(this.dom.root); + this.options.dataAxis.orientation = 'right'; + this.yAxisRight = new DataAxis(this.body, this.options.dataAxis, this.svg, this.options.groups); + delete this.options.dataAxis.orientation; + + // legends + this.legendLeft = new Legend(this.body, this.options.legend, 'left', this.options.groups); + this.legendRight = new Legend(this.body, this.options.legend, 'right', this.options.groups); + + this.show(); }; /** - * Set options. Options will be passed to all components loaded in the Timeline. - * @param {Object} [options] - * {String} orientation - * Vertical orientation for the Timeline, - * can be 'bottom' (default) or 'top'. - * {String | Number} width - * Width for the timeline, a number in pixels or - * a css string like '1000px' or '75%'. '100%' by default. - * {String | Number} height - * Fixed height for the Timeline, a number in pixels or - * a css string like '400px' or '75%'. If undefined, - * The Timeline will automatically size such that - * its contents fit. - * {String | Number} minHeight - * Minimum height for the Timeline, a number in pixels or - * a css string like '400px' or '75%'. - * {String | Number} maxHeight - * Maximum height for the Timeline, a number in pixels or - * a css string like '400px' or '75%'. - * {Number | Date | String} start - * Start date for the visible window - * {Number | Date | String} end - * End date for the visible window + * set the options of the LineGraph. the mergeOptions is used for subObjects that have an enabled element. + * @param {object} options */ - Core.prototype.setOptions = function (options) { + LineGraph.prototype.setOptions = function(options) { if (options) { - // copy the known options - var fields = ['width', 'height', 'minHeight', 'maxHeight', 'autoResize', 'start', 'end', 'orientation', 'clickToUse', 'dataAttributes', 'hiddenDates']; - util.selectiveExtend(fields, this.options, options); - - if ('hiddenDates' in this.options) { - DateUtil.convertHiddenOptions(this.body, this.options.hiddenDates); + var fields = ['sampling','defaultGroup','height','graphHeight','yAxisOrientation','style','barChart','dataAxis','sort','groups']; + if (options.graphHeight === undefined && options.height !== undefined && this.body.domProps.centerContainer.height !== undefined) { + this.updateSVGheight = true; + this.updateSVGheightOnResize = true; } - - if ('clickToUse' in options) { - if (options.clickToUse) { - if (!this.activator) { - this.activator = new Activator(this.dom.root); - } + else if (this.body.domProps.centerContainer.height !== undefined && options.graphHeight !== undefined) { + if (parseInt((options.graphHeight + '').replace("px",'')) < this.body.domProps.centerContainer.height) { + this.updateSVGheight = true; } - else { - if (this.activator) { - this.activator.destroy(); - delete this.activator; + } + util.selectiveDeepExtend(fields, this.options, options); + util.mergeOptions(this.options, options,'catmullRom'); + util.mergeOptions(this.options, options,'drawPoints'); + util.mergeOptions(this.options, options,'shaded'); + util.mergeOptions(this.options, options,'legend'); + + if (options.catmullRom) { + if (typeof options.catmullRom == 'object') { + if (options.catmullRom.parametrization) { + if (options.catmullRom.parametrization == 'uniform') { + this.options.catmullRom.alpha = 0; + } + else if (options.catmullRom.parametrization == 'chordal') { + this.options.catmullRom.alpha = 1.0; + } + else { + this.options.catmullRom.parametrization = 'centripetal'; + this.options.catmullRom.alpha = 0.5; + } } } } - // enable/disable autoResize - this._initAutoResize(); - } + if (this.yAxisLeft) { + if (options.dataAxis !== undefined) { + this.yAxisLeft.setOptions(this.options.dataAxis); + this.yAxisRight.setOptions(this.options.dataAxis); + } + } - // propagate options to all components - this.components.forEach(function (component) { - component.setOptions(options); - }); + if (this.legendLeft) { + if (options.legend !== undefined) { + this.legendLeft.setOptions(this.options.legend); + this.legendRight.setOptions(this.options.legend); + } + } - // TODO: remove deprecation error one day (deprecated since version 0.8.0) - if (options && options.order) { - throw new Error('Option order is deprecated. There is no replacement for this feature.'); + if (this.groups.hasOwnProperty(UNGROUPED)) { + this.groups[UNGROUPED].setOptions(options); + } } - // redraw everything - this.redraw(); + // this is used to redraw the graph if the visibility of the groups is changed. + if (this.dom.frame) { + this.redraw(true); + } }; /** - * Returns true when the Timeline is active. - * @returns {boolean} + * Hide the component from the DOM */ - Core.prototype.isActive = function () { - return !this.activator || this.activator.active; + LineGraph.prototype.hide = function() { + // remove the frame containing the items + if (this.dom.frame.parentNode) { + this.dom.frame.parentNode.removeChild(this.dom.frame); + } }; + /** - * Destroy the Core, clean up all DOM elements and event listeners. + * Show the component in the DOM (when not already visible). + * @return {Boolean} changed */ - Core.prototype.destroy = function () { - // unbind datasets - this.clear(); + LineGraph.prototype.show = function() { + // show frame containing the items + if (!this.dom.frame.parentNode) { + this.body.dom.center.appendChild(this.dom.frame); + } + }; - // remove all event listeners - this.off(); - // stop checking for changed size - this._stopAutoResize(); + /** + * Set items + * @param {vis.DataSet | null} items + */ + LineGraph.prototype.setItems = function(items) { + var me = this, + ids, + oldItemsData = this.itemsData; - // remove from DOM - if (this.dom.root.parentNode) { - this.dom.root.parentNode.removeChild(this.dom.root); + // replace the dataset + if (!items) { + this.itemsData = null; } - this.dom = null; - - // remove Activator - if (this.activator) { - this.activator.destroy(); - delete this.activator; + else if (items instanceof DataSet || items instanceof DataView) { + this.itemsData = items; } - - // cleanup hammer touch events - for (var event in this.listeners) { - if (this.listeners.hasOwnProperty(event)) { - delete this.listeners[event]; - } + else { + throw new TypeError('Data must be an instance of DataSet or DataView'); } - this.listeners = null; - this.hammer = null; - // give all components the opportunity to cleanup - this.components.forEach(function (component) { - component.destroy(); - }); + if (oldItemsData) { + // unsubscribe from old dataset + util.forEach(this.itemListeners, function (callback, event) { + oldItemsData.off(event, callback); + }); - this.body = null; - }; + // remove all drawn items + ids = oldItemsData.getIds(); + this._onRemove(ids); + } + if (this.itemsData) { + // subscribe to new dataset + var id = this.id; + util.forEach(this.itemListeners, function (callback, event) { + me.itemsData.on(event, callback, id); + }); - /** - * Set a custom time bar - * @param {Date} time - */ - Core.prototype.setCustomTime = function (time) { - if (!this.customTime) { - throw new Error('Cannot get custom time: Custom time bar is not enabled'); + // add all new items + ids = this.itemsData.getIds(); + this._onAdd(ids); } - - this.customTime.setCustomTime(time); + this._updateUngrouped(); + //this._updateGraph(); + this.redraw(true); }; + /** - * Retrieve the current custom time. - * @return {Date} customTime + * Set groups + * @param {vis.DataSet} groups */ - Core.prototype.getCustomTime = function() { - if (!this.customTime) { - throw new Error('Cannot get custom time: Custom time bar is not enabled'); - } - - return this.customTime.getCustomTime(); - }; - - - /** - * Get the id's of the currently visible items. - * @returns {Array} The ids of the visible items - */ - Core.prototype.getVisibleItems = function() { - return this.itemSet && this.itemSet.getVisibleItems() || []; - }; - + LineGraph.prototype.setGroups = function(groups) { + var me = this; + var ids; + // unsubscribe from current dataset + if (this.groupsData) { + util.forEach(this.groupListeners, function (callback, event) { + me.groupsData.unsubscribe(event, callback); + }); - /** - * Clear the Core. By Default, items, groups and options are cleared. - * Example usage: - * - * timeline.clear(); // clear items, groups, and options - * timeline.clear({options: true}); // clear options only - * - * @param {Object} [what] Optionally specify what to clear. By default: - * {items: true, groups: true, options: true} - */ - Core.prototype.clear = function(what) { - // clear items - if (!what || what.items) { - this.setItems(null); + // remove all drawn groups + ids = this.groupsData.getIds(); + this.groupsData = null; + this._onRemoveGroups(ids); // note: this will cause a redraw } - // clear groups - if (!what || what.groups) { - this.setGroups(null); + // replace the dataset + if (!groups) { + this.groupsData = null; + } + else if (groups instanceof DataSet || groups instanceof DataView) { + this.groupsData = groups; + } + else { + throw new TypeError('Data must be an instance of DataSet or DataView'); } - // clear options of timeline and of each of the components - if (!what || what.options) { - this.components.forEach(function (component) { - component.setOptions(component.defaultOptions); + if (this.groupsData) { + // subscribe to new dataset + var id = this.id; + util.forEach(this.groupListeners, function (callback, event) { + me.groupsData.on(event, callback, id); }); - this.setOptions(this.defaultOptions); // this will also do a redraw + // draw all ms + ids = this.groupsData.getIds(); + this._onAddGroups(ids); } + this._onUpdate(); }; + /** - * Set Core window such that it fits all items - * @param {Object} [options] Available options: - * `animate: boolean | number` - * If true (default), the range is animated - * smoothly to the new window. - * If a number, the number is taken as duration - * for the animation. Default duration is 500 ms. + * Update the data + * @param [ids] + * @private */ - Core.prototype.fit = function(options) { - var range = this._getDataRange(); - - // skip range set if there is no start and end date - if (range.start === null && range.end === null) { - return; + LineGraph.prototype._onUpdate = function(ids) { + this._updateUngrouped(); + this._updateAllGroupData(); + //this._updateGraph(); + this.redraw(true); + }; + LineGraph.prototype._onAdd = function (ids) {this._onUpdate(ids);}; + LineGraph.prototype._onRemove = function (ids) {this._onUpdate(ids);}; + LineGraph.prototype._onUpdateGroups = function (groupIds) { + for (var i = 0; i < groupIds.length; i++) { + var group = this.groupsData.get(groupIds[i]); + this._updateGroup(group, groupIds[i]); } - var animate = (options && options.animate !== undefined) ? options.animate : true; - this.range.setRange(range.start, range.end, animate); + //this._updateGraph(); + this.redraw(true); }; + LineGraph.prototype._onAddGroups = function (groupIds) {this._onUpdateGroups(groupIds);}; + /** - * Calculate the data range of the items and applies a 5% window around it. - * @returns {{start: Date | null, end: Date | null}} - * @protected + * this cleans the group out off the legends and the dataaxis, updates the ungrouped and updates the graph + * @param {Array} groupIds + * @private */ - Core.prototype._getDataRange = function() { - // apply the data range as range - var dataRange = this.getItemRange(); - - // add 5% space on both sides - var start = dataRange.min; - var end = dataRange.max; - if (start != null && end != null) { - var interval = (end.valueOf() - start.valueOf()); - if (interval <= 0) { - // prevent an empty interval - interval = 24 * 60 * 60 * 1000; // 1 day + LineGraph.prototype._onRemoveGroups = function (groupIds) { + for (var i = 0; i < groupIds.length; i++) { + if (this.groups.hasOwnProperty(groupIds[i])) { + if (this.groups[groupIds[i]].options.yAxisOrientation == 'right') { + this.yAxisRight.removeGroup(groupIds[i]); + this.legendRight.removeGroup(groupIds[i]); + this.legendRight.redraw(); + } + else { + this.yAxisLeft.removeGroup(groupIds[i]); + this.legendLeft.removeGroup(groupIds[i]); + this.legendLeft.redraw(); + } + delete this.groups[groupIds[i]]; } - start = new Date(start.valueOf() - interval * 0.05); - end = new Date(end.valueOf() + interval * 0.05); - } - - return { - start: start, - end: end } + this._updateUngrouped(); + //this._updateGraph(); + this.redraw(true); }; + /** - * Set the visible window. Both parameters are optional, you can change only - * start or only end. Syntax: - * - * TimeLine.setWindow(start, end) - * TimeLine.setWindow(range) - * - * Where start and end can be a Date, number, or string, and range is an - * object with properties start and end. + * update a group object with the group dataset entree * - * @param {Date | Number | String | Object} [start] Start date of visible window - * @param {Date | Number | String} [end] End date of visible window - * @param {Object} [options] Available options: - * `animate: boolean | number` - * If true (default), the range is animated - * smoothly to the new window. - * If a number, the number is taken as duration - * for the animation. Default duration is 500 ms. + * @param group + * @param groupId + * @private */ - Core.prototype.setWindow = function(start, end, options) { - var animate = (options && options.animate !== undefined) ? options.animate : true; - if (arguments.length == 1) { - var range = arguments[0]; - this.range.setRange(range.start, range.end, animate); + LineGraph.prototype._updateGroup = function (group, groupId) { + if (!this.groups.hasOwnProperty(groupId)) { + this.groups[groupId] = new GraphGroup(group, groupId, this.options, this.groupsUsingDefaultStyles); + if (this.groups[groupId].options.yAxisOrientation == 'right') { + this.yAxisRight.addGroup(groupId, this.groups[groupId]); + this.legendRight.addGroup(groupId, this.groups[groupId]); + } + else { + this.yAxisLeft.addGroup(groupId, this.groups[groupId]); + this.legendLeft.addGroup(groupId, this.groups[groupId]); + } } else { - this.range.setRange(start, end, animate); + this.groups[groupId].update(group); + if (this.groups[groupId].options.yAxisOrientation == 'right') { + this.yAxisRight.updateGroup(groupId, this.groups[groupId]); + this.legendRight.updateGroup(groupId, this.groups[groupId]); + } + else { + this.yAxisLeft.updateGroup(groupId, this.groups[groupId]); + this.legendLeft.updateGroup(groupId, this.groups[groupId]); + } } + this.legendLeft.redraw(); + this.legendRight.redraw(); }; - /** - * Move the window such that given time is centered on screen. - * @param {Date | Number | String} time - * @param {Object} [options] Available options: - * `animate: boolean | number` - * If true (default), the range is animated - * smoothly to the new window. - * If a number, the number is taken as duration - * for the animation. Default duration is 500 ms. - */ - Core.prototype.moveTo = function(time, options) { - var interval = this.range.end - this.range.start; - var t = util.convert(time, 'Date').valueOf(); - - var start = t - interval / 2; - var end = t + interval / 2; - var animate = (options && options.animate !== undefined) ? options.animate : true; - - this.range.setRange(start, end, animate); - }; /** - * Get the visible window - * @return {{start: Date, end: Date}} Visible range + * this updates all groups, it is used when there is an update the the itemset. + * + * @private */ - Core.prototype.getWindow = function() { - var range = this.range.getRange(); - return { - start: new Date(range.start), - end: new Date(range.end) - }; + LineGraph.prototype._updateAllGroupData = function () { + if (this.itemsData != null) { + var groupsContent = {}; + var groupId; + for (groupId in this.groups) { + if (this.groups.hasOwnProperty(groupId)) { + groupsContent[groupId] = []; + } + } + for (var itemId in this.itemsData._data) { + if (this.itemsData._data.hasOwnProperty(itemId)) { + var item = this.itemsData._data[itemId]; + if (groupsContent[item.group] === undefined) { + throw new Error('Cannot find referenced group. Possible reason: items added before groups? Groups need to be added before items, as items refer to groups.') + } + item.x = util.convert(item.x,'Date'); + groupsContent[item.group].push(item); + } + } + for (groupId in this.groups) { + if (this.groups.hasOwnProperty(groupId)) { + this.groups[groupId].setItems(groupsContent[groupId]); + } + } + } }; + /** - * Force a redraw of the Core. Can be useful to manually redraw when - * option autoResize=false + * Create or delete the group holding all ungrouped items. This group is used when + * there are no groups specified. This anonymous group is called 'graph'. + * @protected */ - Core.prototype.redraw = function() { - var resized = false; - var options = this.options; - var props = this.props; - var dom = this.dom; - - if (!dom) return; // when destroyed - - DateUtil.updateHiddenDates(this.body, this.options.hiddenDates); + LineGraph.prototype._updateUngrouped = function() { + if (this.itemsData && this.itemsData != null) { + var ungroupedCounter = 0; + for (var itemId in this.itemsData._data) { + if (this.itemsData._data.hasOwnProperty(itemId)) { + var item = this.itemsData._data[itemId]; + if (item != undefined) { + if (item.hasOwnProperty('group')) { + if (item.group === undefined) { + item.group = UNGROUPED; + } + } + else { + item.group = UNGROUPED; + } + ungroupedCounter = item.group == UNGROUPED ? ungroupedCounter + 1 : ungroupedCounter; + } + } + } - // update class names - if (options.orientation == 'top') { - util.addClassName(dom.root, 'top'); - util.removeClassName(dom.root, 'bottom'); + if (ungroupedCounter == 0) { + delete this.groups[UNGROUPED]; + this.legendLeft.removeGroup(UNGROUPED); + this.legendRight.removeGroup(UNGROUPED); + this.yAxisLeft.removeGroup(UNGROUPED); + this.yAxisRight.removeGroup(UNGROUPED); + } + else { + var group = {id: UNGROUPED, content: this.options.defaultGroup}; + this._updateGroup(group, UNGROUPED); + } } else { - util.removeClassName(dom.root, 'top'); - util.addClassName(dom.root, 'bottom'); - } - - // update root width and height options - dom.root.style.maxHeight = util.option.asSize(options.maxHeight, ''); - dom.root.style.minHeight = util.option.asSize(options.minHeight, ''); - dom.root.style.width = util.option.asSize(options.width, ''); - - // calculate border widths - props.border.left = (dom.centerContainer.offsetWidth - dom.centerContainer.clientWidth) / 2; - props.border.right = props.border.left; - props.border.top = (dom.centerContainer.offsetHeight - dom.centerContainer.clientHeight) / 2; - props.border.bottom = props.border.top; - var borderRootHeight= dom.root.offsetHeight - dom.root.clientHeight; - var borderRootWidth = dom.root.offsetWidth - dom.root.clientWidth; - - // workaround for a bug in IE: the clientWidth of an element with - // a height:0px and overflow:hidden is not calculated and always has value 0 - if (dom.centerContainer.clientHeight === 0) { - props.border.left = props.border.top; - props.border.right = props.border.left; - } - if (dom.root.clientHeight === 0) { - borderRootWidth = borderRootHeight; + delete this.groups[UNGROUPED]; + this.legendLeft.removeGroup(UNGROUPED); + this.legendRight.removeGroup(UNGROUPED); + this.yAxisLeft.removeGroup(UNGROUPED); + this.yAxisRight.removeGroup(UNGROUPED); } - // calculate the heights. If any of the side panels is empty, we set the height to - // minus the border width, such that the border will be invisible - props.center.height = dom.center.offsetHeight; - props.left.height = dom.left.offsetHeight; - props.right.height = dom.right.offsetHeight; - props.top.height = dom.top.clientHeight || -props.border.top; - props.bottom.height = dom.bottom.clientHeight || -props.border.bottom; + this.legendLeft.redraw(); + this.legendRight.redraw(); + }; - // TODO: compensate borders when any of the panels is empty. - // apply auto height - // TODO: only calculate autoHeight when needed (else we cause an extra reflow/repaint of the DOM) - var contentHeight = Math.max(props.left.height, props.center.height, props.right.height); - var autoHeight = props.top.height + contentHeight + props.bottom.height + - borderRootHeight + props.border.top + props.border.bottom; - dom.root.style.height = util.option.asSize(options.height, autoHeight + 'px'); + /** + * Redraw the component, mandatory function + * @return {boolean} Returns true if the component is resized + */ + LineGraph.prototype.redraw = function(forceGraphUpdate) { + var resized = false; - // calculate heights of the content panels - props.root.height = dom.root.offsetHeight; - props.background.height = props.root.height - borderRootHeight; - var containerHeight = props.root.height - props.top.height - props.bottom.height - - borderRootHeight; - props.centerContainer.height = containerHeight; - props.leftContainer.height = containerHeight; - props.rightContainer.height = props.leftContainer.height; + // calculate actual size and position + this.props.width = this.dom.frame.offsetWidth; + this.props.height = this.body.domProps.centerContainer.height; - // calculate the widths of the panels - props.root.width = dom.root.offsetWidth; - props.background.width = props.root.width - borderRootWidth; - props.left.width = dom.leftContainer.clientWidth || -props.border.left; - props.leftContainer.width = props.left.width; - props.right.width = dom.rightContainer.clientWidth || -props.border.right; - props.rightContainer.width = props.right.width; - var centerWidth = props.root.width - props.left.width - props.right.width - borderRootWidth; - props.center.width = centerWidth; - props.centerContainer.width = centerWidth; - props.top.width = centerWidth; - props.bottom.width = centerWidth; + // update the graph if there is no lastWidth or with, used for the initial draw + if (this.lastWidth === undefined && this.props.width) { + forceGraphUpdate = true; + } - // resize the panels - dom.background.style.height = props.background.height + 'px'; - dom.backgroundVertical.style.height = props.background.height + 'px'; - dom.backgroundHorizontal.style.height = props.centerContainer.height + 'px'; - dom.centerContainer.style.height = props.centerContainer.height + 'px'; - dom.leftContainer.style.height = props.leftContainer.height + 'px'; - dom.rightContainer.style.height = props.rightContainer.height + 'px'; + // check if this component is resized + resized = this._isResized() || resized; - dom.background.style.width = props.background.width + 'px'; - dom.backgroundVertical.style.width = props.centerContainer.width + 'px'; - dom.backgroundHorizontal.style.width = props.background.width + 'px'; - dom.centerContainer.style.width = props.center.width + 'px'; - dom.top.style.width = props.top.width + 'px'; - dom.bottom.style.width = props.bottom.width + 'px'; + // check whether zoomed (in that case we need to re-stack everything) + var visibleInterval = this.body.range.end - this.body.range.start; + var zoomed = (visibleInterval != this.lastVisibleInterval); + this.lastVisibleInterval = visibleInterval; - // reposition the panels - dom.background.style.left = '0'; - dom.background.style.top = '0'; - dom.backgroundVertical.style.left = (props.left.width + props.border.left) + 'px'; - dom.backgroundVertical.style.top = '0'; - dom.backgroundHorizontal.style.left = '0'; - dom.backgroundHorizontal.style.top = props.top.height + 'px'; - dom.centerContainer.style.left = props.left.width + 'px'; - dom.centerContainer.style.top = props.top.height + 'px'; - dom.leftContainer.style.left = '0'; - dom.leftContainer.style.top = props.top.height + 'px'; - dom.rightContainer.style.left = (props.left.width + props.center.width) + 'px'; - dom.rightContainer.style.top = props.top.height + 'px'; - dom.top.style.left = props.left.width + 'px'; - dom.top.style.top = '0'; - dom.bottom.style.left = props.left.width + 'px'; - dom.bottom.style.top = (props.top.height + props.centerContainer.height) + 'px'; - // update the scrollTop, feasible range for the offset can be changed - // when the height of the Core or of the contents of the center changed - this._updateScrollTop(); + // the svg element is three times as big as the width, this allows for fully dragging left and right + // without reloading the graph. the controls for this are bound to events in the constructor + if (resized == true) { + this.svg.style.width = util.option.asSize(3*this.props.width); + this.svg.style.left = util.option.asSize(-this.props.width); - // reposition the scrollable contents - var offset = this.props.scrollTop; - if (options.orientation == 'bottom') { - offset += Math.max(this.props.centerContainer.height - this.props.center.height - - this.props.border.top - this.props.border.bottom, 0); + // if the height of the graph is set as proportional, change the height of the svg + if ((this.options.height + '').indexOf("%") != -1 || this.updateSVGheightOnResize == true) { + this.updateSVGheight = true; + } } - dom.center.style.left = '0'; - dom.center.style.top = offset + 'px'; - dom.left.style.left = '0'; - dom.left.style.top = offset + 'px'; - dom.right.style.left = '0'; - dom.right.style.top = offset + 'px'; - - // show shadows when vertical scrolling is available - var visibilityTop = this.props.scrollTop == 0 ? 'hidden' : ''; - var visibilityBottom = this.props.scrollTop == this.props.scrollTopMin ? 'hidden' : ''; - dom.shadowTop.style.visibility = visibilityTop; - dom.shadowBottom.style.visibility = visibilityBottom; - dom.shadowTopLeft.style.visibility = visibilityTop; - dom.shadowBottomLeft.style.visibility = visibilityBottom; - dom.shadowTopRight.style.visibility = visibilityTop; - dom.shadowBottomRight.style.visibility = visibilityBottom; - // redraw all components - this.components.forEach(function (component) { - resized = component.redraw() || resized; - }); - if (resized) { - // keep repainting until all sizes are settled - var MAX_REDRAWS = 3; // maximum number of consecutive redraws - if (this.redrawCount < MAX_REDRAWS) { - this.redrawCount++; - this.redraw(); + // update the height of the graph on each redraw of the graph. + if (this.updateSVGheight == true) { + if (this.options.graphHeight != this.body.domProps.centerContainer.height + 'px') { + this.options.graphHeight = this.body.domProps.centerContainer.height + 'px'; + this.svg.style.height = this.body.domProps.centerContainer.height + 'px'; } - else { - console.log('WARNING: infinite loop in redraw?'); + this.updateSVGheight = false; + } + else { + this.svg.style.height = ('' + this.options.graphHeight).replace('px','') + 'px'; + } + + // zoomed is here to ensure that animations are shown correctly. + if (resized == true || zoomed == true || this.abortedGraphUpdate == true || forceGraphUpdate == true) { + resized = this._updateGraph() || resized; + } + else { + // move the whole svg while dragging + if (this.lastStart != 0) { + var offset = this.body.range.start - this.lastStart; + var range = this.body.range.end - this.body.range.start; + if (this.props.width != 0) { + var rangePerPixelInv = this.props.width/range; + var xOffset = offset * rangePerPixelInv; + this.svg.style.left = (-this.props.width - xOffset) + 'px'; + } } - this.redrawCount = 0; } - this.emit("finishedRedraw"); + this.legendLeft.redraw(); + this.legendRight.redraw(); + return resized; }; - // TODO: deprecated since version 1.1.0, remove some day - Core.prototype.repaint = function () { - throw new Error('Function repaint is deprecated. Use redraw instead.'); - }; /** - * Set a current time. This can be used for example to ensure that a client's - * time is synchronized with a shared server time. - * Only applicable when option `showCurrentTime` is true. - * @param {Date | String | Number} time A Date, unix timestamp, or - * ISO date string. + * Update and redraw the graph. + * */ - Core.prototype.setCurrentTime = function(time) { - if (!this.currentTime) { - throw new Error('Option showCurrentTime must be true'); - } - - this.currentTime.setCurrentTime(time); - }; - - /** - * Get the current time. - * Only applicable when option `showCurrentTime` is true. - * @return {Date} Returns the current time. - */ - Core.prototype.getCurrentTime = function() { - if (!this.currentTime) { - throw new Error('Option showCurrentTime must be true'); + LineGraph.prototype._updateGraph = function () { + // reset the svg elements + DOMutil.prepareElements(this.svgElements); + if (this.props.width != 0 && this.itemsData != null) { + var group, i; + var preprocessedGroupData = {}; + var processedGroupData = {}; + var groupRanges = {}; + var changeCalled = false; + + // getting group Ids + var groupIds = []; + for (var groupId in this.groups) { + if (this.groups.hasOwnProperty(groupId)) { + group = this.groups[groupId]; + if (group.visible == true && (this.options.groups.visibility[groupId] === undefined || this.options.groups.visibility[groupId] == true)) { + groupIds.push(groupId); + } + } + } + if (groupIds.length > 0) { + // this is the range of the SVG canvas + var minDate = this.body.util.toGlobalTime(-this.body.domProps.root.width); + var maxDate = this.body.util.toGlobalTime(2 * this.body.domProps.root.width); + var groupsData = {}; + // fill groups data, this only loads the data we require based on the timewindow + this._getRelevantData(groupIds, groupsData, minDate, maxDate); + + // apply sampling, if disabled, it will pass through this function. + this._applySampling(groupIds, groupsData); + + // we transform the X coordinates to detect collisions + for (i = 0; i < groupIds.length; i++) { + preprocessedGroupData[groupIds[i]] = this._convertXcoordinates(groupsData[groupIds[i]]); + } + + // now all needed data has been collected we start the processing. + this._getYRanges(groupIds, preprocessedGroupData, groupRanges); + + // update the Y axis first, we use this data to draw at the correct Y points + // changeCalled is required to clean the SVG on a change emit. + changeCalled = this._updateYAxis(groupIds, groupRanges); + var MAX_CYCLES = 5; + if (changeCalled == true && this.COUNTER < MAX_CYCLES) { + DOMutil.cleanupElements(this.svgElements); + this.abortedGraphUpdate = true; + this.COUNTER++; + this.body.emitter.emit('change'); + return true; + } + else { + if (this.COUNTER > MAX_CYCLES) { + console.log("WARNING: there may be an infinite loop in the _updateGraph emitter cycle.") + } + this.COUNTER = 0; + this.abortedGraphUpdate = false; + + // With the yAxis scaled correctly, use this to get the Y values of the points. + for (i = 0; i < groupIds.length; i++) { + group = this.groups[groupIds[i]]; + processedGroupData[groupIds[i]] = this._convertYcoordinates(groupsData[groupIds[i]], group); + } + + // draw the groups + for (i = 0; i < groupIds.length; i++) { + group = this.groups[groupIds[i]]; + if (group.options.style != 'bar') { // bar needs to be drawn enmasse + group.draw(processedGroupData[groupIds[i]], group, this.framework); + } + } + BarGraphFunctions.draw(groupIds, processedGroupData, this.framework); + } + } } - return this.currentTime.getCurrentTime(); + // cleanup unused svg elements + DOMutil.cleanupElements(this.svgElements); + return false; }; - /** - * Convert a position on screen (pixels) to a datetime - * @param {int} x Position on the screen in pixels - * @return {Date} time The datetime the corresponds with given position x - * @private - */ - // TODO: move this function to Range - Core.prototype._toTime = function(x) { - return DateUtil.toTime(this, x, this.props.center.width); - }; /** - * Convert a position on the global screen (pixels) to a datetime - * @param {int} x Position on the screen in pixels - * @return {Date} time The datetime the corresponds with given position x + * first select and preprocess the data from the datasets. + * the groups have their preselection of data, we now loop over this data to see + * what data we need to draw. Sorted data is much faster. + * more optimization is possible by doing the sampling before and using the binary search + * to find the end date to determine the increment. + * + * @param {array} groupIds + * @param {object} groupsData + * @param {date} minDate + * @param {date} maxDate * @private */ - // TODO: move this function to Range - Core.prototype._toGlobalTime = function(x) { - return DateUtil.toTime(this, x, this.props.root.width); - //var conversion = this.range.conversion(this.props.root.width); - //return new Date(x / conversion.scale + conversion.offset); + LineGraph.prototype._getRelevantData = function (groupIds, groupsData, minDate, maxDate) { + var group, i, j, item; + if (groupIds.length > 0) { + for (i = 0; i < groupIds.length; i++) { + group = this.groups[groupIds[i]]; + groupsData[groupIds[i]] = []; + var dataContainer = groupsData[groupIds[i]]; + // optimization for sorted data + if (group.options.sort == true) { + var guess = Math.max(0, util.binarySearchValue(group.itemsData, minDate, 'x', 'before')); + for (j = guess; j < group.itemsData.length; j++) { + item = group.itemsData[j]; + if (item !== undefined) { + if (item.x > maxDate) { + dataContainer.push(item); + break; + } + else { + dataContainer.push(item); + } + } + } + } + else { + for (j = 0; j < group.itemsData.length; j++) { + item = group.itemsData[j]; + if (item !== undefined) { + if (item.x > minDate && item.x < maxDate) { + dataContainer.push(item); + } + } + } + } + } + } }; + /** - * Convert a datetime (Date object) into a position on the screen - * @param {Date} time A date - * @return {int} x The position on the screen in pixels which corresponds - * with the given date. + * + * @param groupIds + * @param groupsData * @private */ - // TODO: move this function to Range - Core.prototype._toScreen = function(time) { - return DateUtil.toScreen(this, time, this.props.center.width); - }; + LineGraph.prototype._applySampling = function (groupIds, groupsData) { + var group; + if (groupIds.length > 0) { + for (var i = 0; i < groupIds.length; i++) { + group = this.groups[groupIds[i]]; + if (group.options.sampling == true) { + var dataContainer = groupsData[groupIds[i]]; + if (dataContainer.length > 0) { + var increment = 1; + var amountOfPoints = dataContainer.length; + // the global screen is used because changing the width of the yAxis may affect the increment, resulting in an endless loop + // of width changing of the yAxis. + var xDistance = this.body.util.toGlobalScreen(dataContainer[dataContainer.length - 1].x) - this.body.util.toGlobalScreen(dataContainer[0].x); + var pointsPerPixel = amountOfPoints / xDistance; + increment = Math.min(Math.ceil(0.2 * amountOfPoints), Math.max(1, Math.round(pointsPerPixel))); + var sampledData = []; + for (var j = 0; j < amountOfPoints; j += increment) { + sampledData.push(dataContainer[j]); - /** - * Convert a datetime (Date object) into a position on the root - * This is used to get the pixel density estimate for the screen, not the center panel - * @param {Date} time A date - * @return {int} x The position on root in pixels which corresponds - * with the given date. - * @private - */ - // TODO: move this function to Range - Core.prototype._toGlobalScreen = function(time) { - return DateUtil.toScreen(this, time, this.props.root.width); - //var conversion = this.range.conversion(this.props.root.width); - //return (time.valueOf() - conversion.offset) * conversion.scale; + } + groupsData[groupIds[i]] = sampledData; + } + } + } + } }; /** - * Initialize watching when option autoResize is true + * + * + * @param {array} groupIds + * @param {object} groupsData + * @param {object} groupRanges | this is being filled here * @private */ - Core.prototype._initAutoResize = function () { - if (this.options.autoResize == true) { - this._startAutoResize(); - } - else { - this._stopAutoResize(); + LineGraph.prototype._getYRanges = function (groupIds, groupsData, groupRanges) { + var groupData, group, i; + var barCombinedDataLeft = []; + var barCombinedDataRight = []; + var options; + if (groupIds.length > 0) { + for (i = 0; i < groupIds.length; i++) { + groupData = groupsData[groupIds[i]]; + options = this.groups[groupIds[i]].options; + if (groupData.length > 0) { + group = this.groups[groupIds[i]]; + // if bar graphs are stacked, their range need to be handled differently and accumulated over all groups. + if (options.barChart.handleOverlap == 'stack' && options.style == 'bar') { + if (options.yAxisOrientation == 'left') {barCombinedDataLeft = barCombinedDataLeft.concat(group.getYRange(groupData)) ;} + else {barCombinedDataRight = barCombinedDataRight.concat(group.getYRange(groupData));} + } + else { + groupRanges[groupIds[i]] = group.getYRange(groupData,groupIds[i]); + } + } + } + + // if bar graphs are stacked, their range need to be handled differently and accumulated over all groups. + BarGraphFunctions.getStackedBarYRange(barCombinedDataLeft , groupRanges, groupIds, '__barchartLeft' , 'left' ); + BarGraphFunctions.getStackedBarYRange(barCombinedDataRight, groupRanges, groupIds, '__barchartRight', 'right'); } }; + /** - * Watch for changes in the size of the container. On resize, the Panel will - * automatically redraw itself. + * this sets the Y ranges for the Y axis. It also determines which of the axis should be shown or hidden. + * @param {Array} groupIds + * @param {Object} groupRanges * @private */ - Core.prototype._startAutoResize = function () { - var me = this; - - this._stopAutoResize(); - - this._onResize = function() { - if (me.options.autoResize != true) { - // stop watching when the option autoResize is changed to false - me._stopAutoResize(); - return; + LineGraph.prototype._updateYAxis = function (groupIds, groupRanges) { + var resized = false; + var yAxisLeftUsed = false; + var yAxisRightUsed = false; + var minLeft = 1e9, minRight = 1e9, maxLeft = -1e9, maxRight = -1e9, minVal, maxVal; + // if groups are present + if (groupIds.length > 0) { + // this is here to make sure that if there are no items in the axis but there are groups, that there is no infinite draw/redraw loop. + for (var i = 0; i < groupIds.length; i++) { + var group = this.groups[groupIds[i]]; + if (group && group.options.yAxisOrientation != 'right') { + yAxisLeftUsed = true; + minLeft = 0; + maxLeft = 0; + } + else if (group && group.options.yAxisOrientation) { + yAxisRightUsed = true; + minRight = 0; + maxRight = 0; + } } - if (me.dom.root) { - // check whether the frame is resized - // Note: we compare offsetWidth here, not clientWidth. For some reason, - // IE does not restore the clientWidth from 0 to the actual width after - // changing the timeline's container display style from none to visible - if ((me.dom.root.offsetWidth != me.props.lastWidth) || - (me.dom.root.offsetHeight != me.props.lastHeight)) { - me.props.lastWidth = me.dom.root.offsetWidth; - me.props.lastHeight = me.dom.root.offsetHeight; + // if there are items: + for (var i = 0; i < groupIds.length; i++) { + if (groupRanges.hasOwnProperty(groupIds[i])) { + if (groupRanges[groupIds[i]].ignore !== true) { + minVal = groupRanges[groupIds[i]].min; + maxVal = groupRanges[groupIds[i]].max; - me.emit('change'); + if (groupRanges[groupIds[i]].yAxisOrientation != 'right') { + yAxisLeftUsed = true; + minLeft = minLeft > minVal ? minVal : minLeft; + maxLeft = maxLeft < maxVal ? maxVal : maxLeft; + } + else { + yAxisRightUsed = true; + minRight = minRight > minVal ? minVal : minRight; + maxRight = maxRight < maxVal ? maxVal : maxRight; + } + } } } - }; - // add event listener to window resize - util.addEventListener(window, 'resize', this._onResize); + if (yAxisLeftUsed == true) { + this.yAxisLeft.setRange(minLeft, maxLeft); + } + if (yAxisRightUsed == true) { + this.yAxisRight.setRange(minRight, maxRight); + } + } + resized = this._toggleAxisVisiblity(yAxisLeftUsed , this.yAxisLeft) || resized; + resized = this._toggleAxisVisiblity(yAxisRightUsed, this.yAxisRight) || resized; - this.watchTimer = setInterval(this._onResize, 1000); - }; + if (yAxisRightUsed == true && yAxisLeftUsed == true) { + this.yAxisLeft.drawIcons = true; + this.yAxisRight.drawIcons = true; + } + else { + this.yAxisLeft.drawIcons = false; + this.yAxisRight.drawIcons = false; + } + this.yAxisRight.master = !yAxisLeftUsed; + if (this.yAxisRight.master == false) { + if (yAxisRightUsed == true) {this.yAxisLeft.lineOffset = this.yAxisRight.width;} + else {this.yAxisLeft.lineOffset = 0;} - /** - * Stop watching for a resize of the frame. - * @private - */ - Core.prototype._stopAutoResize = function () { - if (this.watchTimer) { - clearInterval(this.watchTimer); - this.watchTimer = undefined; + resized = this.yAxisLeft.redraw() || resized; + this.yAxisRight.stepPixelsForced = this.yAxisLeft.stepPixels; + this.yAxisRight.zeroCrossing = this.yAxisLeft.zeroCrossing; + resized = this.yAxisRight.redraw() || resized; + } + else { + resized = this.yAxisRight.redraw() || resized; } - // remove event listener on window.resize - util.removeEventListener(window, 'resize', this._onResize); - this._onResize = null; - }; + // clean the accumulated lists + if (groupIds.indexOf('__barchartLeft') != -1) { + groupIds.splice(groupIds.indexOf('__barchartLeft'),1); + } + if (groupIds.indexOf('__barchartRight') != -1) { + groupIds.splice(groupIds.indexOf('__barchartRight'),1); + } - /** - * Start moving the timeline vertically - * @param {Event} event - * @private - */ - Core.prototype._onTouch = function (event) { - this.touch.allowDragging = true; + return resized; }; - /** - * Start moving the timeline vertically - * @param {Event} event - * @private - */ - Core.prototype._onPinch = function (event) { - this.touch.allowDragging = false; - }; /** - * Start moving the timeline vertically - * @param {Event} event + * This shows or hides the Y axis if needed. If there is a change, the changed event is emitted by the updateYAxis function + * + * @param {boolean} axisUsed + * @returns {boolean} * @private + * @param axis */ - Core.prototype._onDragStart = function (event) { - this.touch.initialScrollTop = this.props.scrollTop; + LineGraph.prototype._toggleAxisVisiblity = function (axisUsed, axis) { + var changed = false; + if (axisUsed == false) { + if (axis.dom.frame.parentNode && axis.hidden == false) { + axis.hide() + changed = true; + } + } + else { + if (!axis.dom.frame.parentNode && axis.hidden == true) { + axis.show(); + changed = true; + } + } + return changed; }; + /** - * Move the timeline vertically - * @param {Event} event + * This uses the DataAxis object to generate the correct X coordinate on the SVG window. It uses the + * util function toScreen to get the x coordinate from the timestamp. It also pre-filters the data and get the minMax ranges for + * the yAxis. + * + * @param datapoints + * @returns {Array} * @private */ - Core.prototype._onDrag = function (event) { - // refuse to drag when we where pinching to prevent the timeline make a jump - // when releasing the fingers in opposite order from the touch screen - if (!this.touch.allowDragging) return; - - var delta = event.gesture.deltaY; - - var oldScrollTop = this._getScrollTop(); - var newScrollTop = this._setScrollTop(this.touch.initialScrollTop + delta); - + LineGraph.prototype._convertXcoordinates = function (datapoints) { + var extractedData = []; + var xValue, yValue; + var toScreen = this.body.util.toScreen; - if (newScrollTop != oldScrollTop) { - this.redraw(); // TODO: this causes two redraws when dragging, the other is triggered by rangechange already - this.emit("verticalDrag"); + for (var i = 0; i < datapoints.length; i++) { + xValue = toScreen(datapoints[i].x) + this.props.width; + yValue = datapoints[i].y; + extractedData.push({x: xValue, y: yValue}); } - }; - /** - * Apply a scrollTop - * @param {Number} scrollTop - * @returns {Number} scrollTop Returns the applied scrollTop - * @private - */ - Core.prototype._setScrollTop = function (scrollTop) { - this.props.scrollTop = scrollTop; - this._updateScrollTop(); - return this.props.scrollTop; + return extractedData; }; + /** - * Update the current scrollTop when the height of the containers has been changed - * @returns {Number} scrollTop Returns the applied scrollTop + * This uses the DataAxis object to generate the correct X coordinate on the SVG window. It uses the + * util function toScreen to get the x coordinate from the timestamp. It also pre-filters the data and get the minMax ranges for + * the yAxis. + * + * @param datapoints + * @param group + * @returns {Array} * @private */ - Core.prototype._updateScrollTop = function () { - // recalculate the scrollTopMin - var scrollTopMin = Math.min(this.props.centerContainer.height - this.props.center.height, 0); // is negative or zero - if (scrollTopMin != this.props.scrollTopMin) { - // in case of bottom orientation, change the scrollTop such that the contents - // do not move relative to the time axis at the bottom - if (this.options.orientation == 'bottom') { - this.props.scrollTop += (scrollTopMin - this.props.scrollTopMin); - } - this.props.scrollTopMin = scrollTopMin; + LineGraph.prototype._convertYcoordinates = function (datapoints, group) { + var extractedData = []; + var xValue, yValue; + var toScreen = this.body.util.toScreen; + var axis = this.yAxisLeft; + var svgHeight = Number(this.svg.style.height.replace('px','')); + if (group.options.yAxisOrientation == 'right') { + axis = this.yAxisRight; } - // limit the scrollTop to the feasible scroll range - if (this.props.scrollTop > 0) this.props.scrollTop = 0; - if (this.props.scrollTop < scrollTopMin) this.props.scrollTop = scrollTopMin; + for (var i = 0; i < datapoints.length; i++) { + xValue = toScreen(datapoints[i].x) + this.props.width; + yValue = Math.round(axis.convertValue(datapoints[i].y)); + extractedData.push({x: xValue, y: yValue}); + } - return this.props.scrollTop; - }; + group.setZeroPosition(Math.min(svgHeight, axis.convertValue(0))); - /** - * Get the current scrollTop - * @returns {number} scrollTop - * @private - */ - Core.prototype._getScrollTop = function () { - return this.props.scrollTop; + return extractedData; }; - module.exports = Core; + + module.exports = LineGraph; /***/ }, -/* 26 */ +/* 30 */ /***/ function(module, exports, __webpack_require__) { - var Hammer = __webpack_require__(19); var util = __webpack_require__(1); - var DataSet = __webpack_require__(7); - var DataView = __webpack_require__(9); - var Component = __webpack_require__(23); - var Group = __webpack_require__(27); - var BackgroundGroup = __webpack_require__(31); - var BoxItem = __webpack_require__(32); - var PointItem = __webpack_require__(33); - var RangeItem = __webpack_require__(29); - var BackgroundItem = __webpack_require__(34); - - - var UNGROUPED = '__ungrouped__'; // reserved group id for ungrouped items - var BACKGROUND = '__background__'; // reserved group id for background items without group + var Component = __webpack_require__(20); + var TimeStep = __webpack_require__(19); + var DateUtil = __webpack_require__(15); + var moment = __webpack_require__(44); /** - * An ItemSet holds a set of items and ranges which can be displayed in a - * range. The width is determined by the parent of the ItemSet, and the height - * is determined by the size of the items. + * A horizontal time axis * @param {{dom: Object, domProps: Object, emitter: Emitter, range: Range}} body - * @param {Object} [options] See ItemSet.setOptions for the available options. - * @constructor ItemSet + * @param {Object} [options] See TimeAxis.setOptions for the available + * options. + * @constructor TimeAxis * @extends Component */ - function ItemSet(body, options) { - this.body = body; - - this.defaultOptions = { - type: null, // 'box', 'point', 'range', 'background' - orientation: 'bottom', // 'top' or 'bottom' - align: 'auto', // alignment of box items - stack: true, - groupOrder: null, - - selectable: true, - editable: { - updateTime: false, - updateGroup: false, - add: false, - remove: false - }, - - onAdd: function (item, callback) { - callback(item); - }, - onUpdate: function (item, callback) { - callback(item); - }, - onMove: function (item, callback) { - callback(item); - }, - onRemove: function (item, callback) { - callback(item); - }, - onMoving: function (item, callback) { - callback(item); - }, - - margin: { - item: { - horizontal: 10, - vertical: 10 - }, - axis: 20 - }, - padding: 5 - }; - - // options is shared by this ItemSet and all its items - this.options = util.extend({}, this.defaultOptions); - - // options for getting items from the DataSet with the correct type - this.itemOptions = { - type: {start: 'Date', end: 'Date'} - }; - - this.conversion = { - toScreen: body.util.toScreen, - toTime: body.util.toTime - }; - this.dom = {}; - this.props = {}; - this.hammer = null; - - var me = this; - this.itemsData = null; // DataSet - this.groupsData = null; // DataSet - - // listeners for the DataSet of the items - this.itemListeners = { - 'add': function (event, params, senderId) { - me._onAdd(params.items); - }, - 'update': function (event, params, senderId) { - me._onUpdate(params.items); - }, - 'remove': function (event, params, senderId) { - me._onRemove(params.items); + function TimeAxis (body, options) { + this.dom = { + foreground: null, + lines: [], + majorTexts: [], + minorTexts: [], + redundant: { + lines: [], + majorTexts: [], + minorTexts: [] } }; - - // listeners for the DataSet of the groups - this.groupListeners = { - 'add': function (event, params, senderId) { - me._onAddGroups(params.items); - }, - 'update': function (event, params, senderId) { - me._onUpdateGroups(params.items); + this.props = { + range: { + start: 0, + end: 0, + minimumStep: 0 }, - 'remove': function (event, params, senderId) { - me._onRemoveGroups(params.items); - } + lineTop: 0 }; - this.items = {}; // object with an Item for every data item - this.groups = {}; // Group object for every group - this.groupIds = []; + this.defaultOptions = { + orientation: 'bottom', // supported: 'top', 'bottom' + // TODO: implement timeaxis orientations 'left' and 'right' + showMinorLabels: true, + showMajorLabels: true, + format: null, + timeAxis: null + }; + this.options = util.extend({}, this.defaultOptions); - this.selection = []; // list with the ids of all selected nodes - this.stackDirty = true; // if true, all items will be restacked on next redraw + this.body = body; - this.touchParams = {}; // stores properties while dragging // create the HTML DOM - this._create(); this.setOptions(options); } - ItemSet.prototype = new Component(); - - // available item types will be registered here - ItemSet.types = { - background: BackgroundItem, - box: BoxItem, - range: RangeItem, - point: PointItem - }; - - /** - * Create the HTML DOM for the ItemSet - */ - ItemSet.prototype._create = function(){ - var frame = document.createElement('div'); - frame.className = 'itemset'; - frame['timeline-itemset'] = this; - this.dom.frame = frame; - - // create background panel - var background = document.createElement('div'); - background.className = 'background'; - frame.appendChild(background); - this.dom.background = background; - - // create foreground panel - var foreground = document.createElement('div'); - foreground.className = 'foreground'; - frame.appendChild(foreground); - this.dom.foreground = foreground; - - // create axis panel - var axis = document.createElement('div'); - axis.className = 'axis'; - this.dom.axis = axis; - - // create labelset - var labelSet = document.createElement('div'); - labelSet.className = 'labelset'; - this.dom.labelSet = labelSet; - - // create ungrouped Group - this._updateUngrouped(); - - // create background Group - var backgroundGroup = new BackgroundGroup(BACKGROUND, null, this); - backgroundGroup.show(); - this.groups[BACKGROUND] = backgroundGroup; - - // attach event listeners - // Note: we bind to the centerContainer for the case where the height - // of the center container is larger than of the ItemSet, so we - // can click in the empty area to create a new item or deselect an item. - this.hammer = Hammer(this.body.dom.centerContainer, { - preventDefault: true - }); - - // drag items when selected - this.hammer.on('touch', this._onTouch.bind(this)); - this.hammer.on('dragstart', this._onDragStart.bind(this)); - this.hammer.on('drag', this._onDrag.bind(this)); - this.hammer.on('dragend', this._onDragEnd.bind(this)); - - // single select (or unselect) when tapping an item - this.hammer.on('tap', this._onSelectItem.bind(this)); - - // multi select when holding mouse/touch, or on ctrl+click - this.hammer.on('hold', this._onMultiSelectItem.bind(this)); - - // add item on doubletap - this.hammer.on('doubletap', this._onAddItem.bind(this)); - - // attach to the DOM - this.show(); - }; + TimeAxis.prototype = new Component(); /** - * Set options for the ItemSet. Existing options will be extended/overwritten. - * @param {Object} [options] The following options are available: - * {String} type - * Default type for the items. Choose from 'box' - * (default), 'point', 'range', or 'background'. - * The default style can be overwritten by - * individual items. - * {String} align - * Alignment for the items, only applicable for - * BoxItem. Choose 'center' (default), 'left', or - * 'right'. - * {String} orientation - * Orientation of the item set. Choose 'top' or - * 'bottom' (default). - * {Function} groupOrder - * A sorting function for ordering groups - * {Boolean} stack - * If true (deafult), items will be stacked on - * top of each other. - * {Number} margin.axis - * Margin between the axis and the items in pixels. - * Default is 20. - * {Number} margin.item.horizontal - * Horizontal margin between items in pixels. - * Default is 10. - * {Number} margin.item.vertical - * Vertical Margin between items in pixels. - * Default is 10. - * {Number} margin.item - * Margin between items in pixels in both horizontal - * and vertical direction. Default is 10. - * {Number} margin - * Set margin for both axis and items in pixels. - * {Number} padding - * Padding of the contents of an item in pixels. - * Must correspond with the items css. Default is 5. - * {Boolean} selectable - * If true (default), items can be selected. - * {Boolean} editable - * Set all editable options to true or false - * {Boolean} editable.updateTime - * Allow dragging an item to an other moment in time - * {Boolean} editable.updateGroup - * Allow dragging an item to an other group - * {Boolean} editable.add - * Allow creating new items on double tap - * {Boolean} editable.remove - * Allow removing items by clicking the delete button - * top right of a selected item. - * {Function(item: Item, callback: Function)} onAdd - * Callback function triggered when an item is about to be added: - * when the user double taps an empty space in the Timeline. - * {Function(item: Item, callback: Function)} onUpdate - * Callback function fired when an item is about to be updated. - * This function typically has to show a dialog where the user - * change the item. If not implemented, nothing happens. - * {Function(item: Item, callback: Function)} onMove - * Fired when an item has been moved. If not implemented, - * the move action will be accepted. - * {Function(item: Item, callback: Function)} onRemove - * Fired when an item is about to be deleted. - * If not implemented, the item will be always removed. + * Set options for the TimeAxis. + * Parameters will be merged in current options. + * @param {Object} options Available options: + * {string} [orientation] + * {boolean} [showMinorLabels] + * {boolean} [showMajorLabels] */ - ItemSet.prototype.setOptions = function(options) { + TimeAxis.prototype.setOptions = function(options) { if (options) { // copy all options that we know - var fields = ['type', 'align', 'orientation', 'padding', 'stack', 'selectable', 'groupOrder', 'dataAttributes', 'template','hide']; - util.selectiveExtend(fields, this.options, options); - - if ('margin' in options) { - if (typeof options.margin === 'number') { - this.options.margin.axis = options.margin; - this.options.margin.item.horizontal = options.margin; - this.options.margin.item.vertical = options.margin; - } - else if (typeof options.margin === 'object') { - util.selectiveExtend(['axis'], this.options.margin, options.margin); - if ('item' in options.margin) { - if (typeof options.margin.item === 'number') { - this.options.margin.item.horizontal = options.margin.item; - this.options.margin.item.vertical = options.margin.item; - } - else if (typeof options.margin.item === 'object') { - util.selectiveExtend(['horizontal', 'vertical'], this.options.margin.item, options.margin.item); - } - } - } - } + util.selectiveExtend([ + 'orientation', + 'showMinorLabels', + 'showMajorLabels', + 'hiddenDates', + 'format', + 'timeAxis' + ], this.options, options); - if ('editable' in options) { - if (typeof options.editable === 'boolean') { - this.options.editable.updateTime = options.editable; - this.options.editable.updateGroup = options.editable; - this.options.editable.add = options.editable; - this.options.editable.remove = options.editable; + // apply locale to moment.js + // TODO: not so nice, this is applied globally to moment.js + if ('locale' in options) { + if (typeof moment.locale === 'function') { + // moment.js 2.8.1+ + moment.locale(options.locale); } - else if (typeof options.editable === 'object') { - util.selectiveExtend(['updateTime', 'updateGroup', 'add', 'remove'], this.options.editable, options.editable); + else { + moment.lang(options.locale); } } - - // callback functions - var addCallback = (function (name) { - var fn = options[name]; - if (fn) { - if (!(fn instanceof Function)) { - throw new Error('option ' + name + ' must be a function ' + name + '(item, callback)'); - } - this.options[name] = fn; - } - }).bind(this); - ['onAdd', 'onUpdate', 'onRemove', 'onMove', 'onMoving'].forEach(addCallback); - - // force the itemSet to refresh: options like orientation and margins may be changed - this.markDirty(); } }; /** - * Mark the ItemSet dirty so it will refresh everything with next redraw - */ - ItemSet.prototype.markDirty = function() { - this.groupIds = []; - this.stackDirty = true; - }; - - /** - * Destroy the ItemSet + * Create the HTML DOM for the TimeAxis */ - ItemSet.prototype.destroy = function() { - this.hide(); - this.setItems(null); - this.setGroups(null); - - this.hammer = null; + TimeAxis.prototype._create = function() { + this.dom.foreground = document.createElement('div'); + this.dom.background = document.createElement('div'); - this.body = null; - this.conversion = null; + this.dom.foreground.className = 'timeaxis foreground'; + this.dom.background.className = 'timeaxis background'; }; /** - * Hide the component from the DOM + * Destroy the TimeAxis */ - ItemSet.prototype.hide = function() { - // remove the frame containing the items - if (this.dom.frame.parentNode) { - this.dom.frame.parentNode.removeChild(this.dom.frame); + TimeAxis.prototype.destroy = function() { + // remove from DOM + if (this.dom.foreground.parentNode) { + this.dom.foreground.parentNode.removeChild(this.dom.foreground); } - - // remove the axis with dots - if (this.dom.axis.parentNode) { - this.dom.axis.parentNode.removeChild(this.dom.axis); + if (this.dom.background.parentNode) { + this.dom.background.parentNode.removeChild(this.dom.background); } - // remove the labelset containing all group labels - if (this.dom.labelSet.parentNode) { - this.dom.labelSet.parentNode.removeChild(this.dom.labelSet); - } + this.body = null; }; /** - * Show the component in the DOM (when not already visible). - * @return {Boolean} changed + * Repaint the component + * @return {boolean} Returns true if the component is resized */ - ItemSet.prototype.show = function() { - // show frame containing the items - if (!this.dom.frame.parentNode) { - this.body.dom.center.appendChild(this.dom.frame); - } - - // show axis with dots - if (!this.dom.axis.parentNode) { - this.body.dom.backgroundVertical.appendChild(this.dom.axis); - } + TimeAxis.prototype.redraw = function () { + var options = this.options; + var props = this.props; + var foreground = this.dom.foreground; + var background = this.dom.background; - // show labelset containing labels - if (!this.dom.labelSet.parentNode) { - this.body.dom.left.appendChild(this.dom.labelSet); - } - }; + // determine the correct parent DOM element (depending on option orientation) + var parent = (options.orientation == 'top') ? this.body.dom.top : this.body.dom.bottom; + var parentChanged = (foreground.parentNode !== parent); - /** - * Set selected items by their id. Replaces the current selection - * Unknown id's are silently ignored. - * @param {string[] | string} [ids] An array with zero or more id's of the items to be - * selected, or a single item id. If ids is undefined - * or an empty array, all items will be unselected. - */ - ItemSet.prototype.setSelection = function(ids) { - var i, ii, id, item; + // calculate character width and height + this._calculateCharSize(); - if (ids == undefined) ids = []; - if (!Array.isArray(ids)) ids = [ids]; + // TODO: recalculate sizes only needed when parent is resized or options is changed + var orientation = this.options.orientation, + showMinorLabels = this.options.showMinorLabels, + showMajorLabels = this.options.showMajorLabels; - // unselect currently selected items - for (i = 0, ii = this.selection.length; i < ii; i++) { - id = this.selection[i]; - item = this.items[id]; - if (item) item.unselect(); - } + // determine the width and height of the elemens for the axis + props.minorLabelHeight = showMinorLabels ? props.minorCharHeight : 0; + props.majorLabelHeight = showMajorLabels ? props.majorCharHeight : 0; + props.height = props.minorLabelHeight + props.majorLabelHeight; + props.width = foreground.offsetWidth; - // select items - this.selection = []; - for (i = 0, ii = ids.length; i < ii; i++) { - id = ids[i]; - item = this.items[id]; - if (item) { - this.selection.push(id); - item.select(); - } - } - }; + props.minorLineHeight = this.body.domProps.root.height - props.majorLabelHeight - + (options.orientation == 'top' ? this.body.domProps.bottom.height : this.body.domProps.top.height); + props.minorLineWidth = 1; // TODO: really calculate width + props.majorLineHeight = props.minorLineHeight + props.majorLabelHeight; + props.majorLineWidth = 1; // TODO: really calculate width - /** - * Get the selected items by their id - * @return {Array} ids The ids of the selected items - */ - ItemSet.prototype.getSelection = function() { - return this.selection.concat([]); - }; + // take foreground and background offline while updating (is almost twice as fast) + var foregroundNextSibling = foreground.nextSibling; + var backgroundNextSibling = background.nextSibling; + foreground.parentNode && foreground.parentNode.removeChild(foreground); + background.parentNode && background.parentNode.removeChild(background); - /** - * Get the id's of the currently visible items. - * @returns {Array} The ids of the visible items - */ - ItemSet.prototype.getVisibleItems = function() { - var range = this.body.range.getRange(); - var left = this.body.util.toScreen(range.start); - var right = this.body.util.toScreen(range.end); + foreground.style.height = this.props.height + 'px'; - var ids = []; - for (var groupId in this.groups) { - if (this.groups.hasOwnProperty(groupId)) { - var group = this.groups[groupId]; - var rawVisibleItems = group.visibleItems; + this._repaintLabels(); - // filter the "raw" set with visibleItems into a set which is really - // visible by pixels - for (var i = 0; i < rawVisibleItems.length; i++) { - var item = rawVisibleItems[i]; - // TODO: also check whether visible vertically - if ((item.left < right) && (item.left + item.width > left)) { - ids.push(item.id); - } - } - } + // put DOM online again (at the same place) + if (foregroundNextSibling) { + parent.insertBefore(foreground, foregroundNextSibling); + } + else { + parent.appendChild(foreground) + } + if (backgroundNextSibling) { + this.body.dom.backgroundVertical.insertBefore(background, backgroundNextSibling); + } + else { + this.body.dom.backgroundVertical.appendChild(background) } - return ids; + return this._isResized() || parentChanged; }; /** - * Deselect a selected item - * @param {String | Number} id + * Repaint major and minor text labels and vertical grid lines * @private */ - ItemSet.prototype._deselect = function(id) { - var selection = this.selection; - for (var i = 0, ii = selection.length; i < ii; i++) { - if (selection[i] == id) { // non-strict comparison! - selection.splice(i, 1); - break; - } - } - }; + TimeAxis.prototype._repaintLabels = function () { + var orientation = this.options.orientation; - /** - * Repaint the component - * @return {boolean} Returns true if the component is resized - */ - ItemSet.prototype.redraw = function() { - var margin = this.options.margin, - range = this.body.range, - asSize = util.option.asSize, - options = this.options, - orientation = options.orientation, - resized = false, - frame = this.dom.frame, - editable = options.editable.updateTime || options.editable.updateGroup; + // calculate range and step (step such that we have space for 7 characters per label) + var start = util.convert(this.body.range.start, 'Number'); + var end = util.convert(this.body.range.end, 'Number'); + var timeLabelsize = this.body.util.toTime((this.props.minorCharWidth || 10) * 7).valueOf(); + var minimumStep = timeLabelsize - DateUtil.getHiddenDurationBefore(this.body.hiddenDates, this.body.range, timeLabelsize); + minimumStep -= this.body.util.toTime(0).valueOf(); - // recalculate absolute position (before redrawing groups) - this.props.top = this.body.domProps.top.height + this.body.domProps.border.top; - this.props.left = this.body.domProps.left.width + this.body.domProps.border.left; + var step = new TimeStep(new Date(start), new Date(end), minimumStep, this.body.hiddenDates); + if (this.options.format) { + step.setFormat(this.options.format); + } + if (this.options.timeAxis) { + step.setScale(this.options.timeAxis); + } + this.step = step; - // update class name - frame.className = 'itemset' + (editable ? ' editable' : ''); + // Move all DOM elements to a "redundant" list, where they + // can be picked for re-use, and clear the lists with lines and texts. + // At the end of the function _repaintLabels, left over elements will be cleaned up + var dom = this.dom; + dom.redundant.lines = dom.lines; + dom.redundant.majorTexts = dom.majorTexts; + dom.redundant.minorTexts = dom.minorTexts; + dom.lines = []; + dom.majorTexts = []; + dom.minorTexts = []; - // reorder the groups (if needed) - resized = this._orderGroups() || resized; + var cur; + var x = 0; + var isMajor; + var xPrev = 0; + var width = 0; + var prevLine; + var xFirstMajorLabel = undefined; + var max = 0; + var className; - // check whether zoomed (in that case we need to re-stack everything) - // TODO: would be nicer to get this as a trigger from Range - var visibleInterval = range.end - range.start; - var zoomed = (visibleInterval != this.lastVisibleInterval) || (this.props.width != this.props.lastWidth); - if (zoomed) this.stackDirty = true; - this.lastVisibleInterval = visibleInterval; - this.props.lastWidth = this.props.width; + step.first(); + while (step.hasNext() && max < 1000) { + max++; - var restack = this.stackDirty; - var firstGroup = this._firstGroup(); - var firstMargin = { - item: margin.item, - axis: margin.axis - }; - var nonFirstMargin = { - item: margin.item, - axis: margin.item.vertical / 2 - }; - var height = 0; - var minHeight = margin.axis + margin.item.vertical; + cur = step.getCurrent(); + isMajor = step.isMajor(); + className = step.getClassName(); - // redraw the background group - this.groups[BACKGROUND].redraw(range, nonFirstMargin, restack); + xPrev = x; + x = this.body.util.toScreen(cur); + width = x - xPrev; + if (prevLine) { + prevLine.style.width = width + 'px'; + } - // redraw all regular groups - util.forEach(this.groups, function (group) { - var groupMargin = (group == firstGroup) ? firstMargin : nonFirstMargin; - var groupResized = group.redraw(range, groupMargin, restack); - resized = groupResized || resized; - height += group.height; - }); - height = Math.max(height, minHeight); - this.stackDirty = false; + if (this.options.showMinorLabels) { + this._repaintMinorText(x, step.getLabelMinor(), orientation, className); + } - // update frame height - frame.style.height = asSize(height); + if (isMajor && this.options.showMajorLabels) { + if (x > 0) { + if (xFirstMajorLabel == undefined) { + xFirstMajorLabel = x; + } + this._repaintMajorText(x, step.getLabelMajor(), orientation, className); + } + prevLine = this._repaintMajorLine(x, orientation, className); + } + else { + prevLine = this._repaintMinorLine(x, orientation, className); + } - // calculate actual size - this.props.width = frame.offsetWidth; - this.props.height = height; + step.next(); + } - // reposition axis - this.dom.axis.style.top = asSize((orientation == 'top') ? - (this.body.domProps.top.height + this.body.domProps.border.top) : - (this.body.domProps.top.height + this.body.domProps.centerContainer.height)); - this.dom.axis.style.left = '0'; + // create a major label on the left when needed + if (this.options.showMajorLabels) { + var leftTime = this.body.util.toTime(0), + leftText = step.getLabelMajor(leftTime), + widthText = leftText.length * (this.props.majorCharWidth || 10) + 10; // upper bound estimation - // check if this component is resized - resized = this._isResized() || resized; + if (xFirstMajorLabel == undefined || widthText < xFirstMajorLabel) { + this._repaintMajorText(0, leftText, orientation, className); + } + } - return resized; + // Cleanup leftover DOM elements from the redundant list + util.forEach(this.dom.redundant, function (arr) { + while (arr.length) { + var elem = arr.pop(); + if (elem && elem.parentNode) { + elem.parentNode.removeChild(elem); + } + } + }); }; /** - * Get the first group, aligned with the axis - * @return {Group | null} firstGroup + * Create a minor label for the axis at position x + * @param {Number} x + * @param {String} text + * @param {String} orientation "top" or "bottom" (default) + * @param {String} className * @private */ - ItemSet.prototype._firstGroup = function() { - var firstGroupIndex = (this.options.orientation == 'top') ? 0 : (this.groupIds.length - 1); - var firstGroupId = this.groupIds[firstGroupIndex]; - var firstGroup = this.groups[firstGroupId] || this.groups[UNGROUPED]; + TimeAxis.prototype._repaintMinorText = function (x, text, orientation, className) { + // reuse redundant label + var label = this.dom.redundant.minorTexts.shift(); - return firstGroup || null; + if (!label) { + // create new label + var content = document.createTextNode(''); + label = document.createElement('div'); + label.appendChild(content); + this.dom.foreground.appendChild(label); + } + this.dom.minorTexts.push(label); + + label.childNodes[0].nodeValue = text; + + label.style.top = (orientation == 'top') ? (this.props.majorLabelHeight + 'px') : '0'; + label.style.left = x + 'px'; + label.className = 'text minor ' + className; + //label.title = title; // TODO: this is a heavy operation }; /** - * Create or delete the group holding all ungrouped items. This group is used when - * there are no groups specified. - * @protected + * Create a Major label for the axis at position x + * @param {Number} x + * @param {String} text + * @param {String} orientation "top" or "bottom" (default) + * @param {String} className + * @private */ - ItemSet.prototype._updateUngrouped = function() { - var ungrouped = this.groups[UNGROUPED]; - var background = this.groups[BACKGROUND]; - var item, itemId; - - if (this.groupsData) { - // remove the group holding all ungrouped items - if (ungrouped) { - ungrouped.hide(); - delete this.groups[UNGROUPED]; + TimeAxis.prototype._repaintMajorText = function (x, text, orientation, className) { + // reuse redundant label + var label = this.dom.redundant.majorTexts.shift(); - for (itemId in this.items) { - if (this.items.hasOwnProperty(itemId)) { - item = this.items[itemId]; - item.parent && item.parent.remove(item); - var groupId = this._getGroupId(item.data); - var group = this.groups[groupId]; - group && group.add(item) || item.hide(); - } - } - } + if (!label) { + // create label + var content = document.createTextNode(text); + label = document.createElement('div'); + label.appendChild(content); + this.dom.foreground.appendChild(label); } - else { - // create a group holding all (unfiltered) items - if (!ungrouped) { - var id = null; - var data = null; - ungrouped = new Group(id, data, this); - this.groups[UNGROUPED] = ungrouped; + this.dom.majorTexts.push(label); - for (itemId in this.items) { - if (this.items.hasOwnProperty(itemId)) { - item = this.items[itemId]; - ungrouped.add(item); - } - } + label.childNodes[0].nodeValue = text; + label.className = 'text major ' + className; + //label.title = title; // TODO: this is a heavy operation - ungrouped.show(); - } - } + label.style.top = (orientation == 'top') ? '0' : (this.props.minorLabelHeight + 'px'); + label.style.left = x + 'px'; }; /** - * Get the element for the labelset - * @return {HTMLElement} labelSet + * Create a minor line for the axis at position x + * @param {Number} x + * @param {String} orientation "top" or "bottom" (default) + * @param {String} className + * @return {Element} Returns the created line + * @private */ - ItemSet.prototype.getLabelSet = function() { - return this.dom.labelSet; + TimeAxis.prototype._repaintMinorLine = function (x, orientation, className) { + // reuse redundant line + var line = this.dom.redundant.lines.shift(); + if (!line) { + // create vertical line + line = document.createElement('div'); + this.dom.background.appendChild(line); + } + this.dom.lines.push(line); + + var props = this.props; + if (orientation == 'top') { + line.style.top = props.majorLabelHeight + 'px'; + } + else { + line.style.top = this.body.domProps.top.height + 'px'; + } + line.style.height = props.minorLineHeight + 'px'; + line.style.left = (x - props.minorLineWidth / 2) + 'px'; + + line.className = 'grid vertical minor ' + className; + + return line; }; /** - * Set items - * @param {vis.DataSet | null} items + * Create a Major line for the axis at position x + * @param {Number} x + * @param {String} orientation "top" or "bottom" (default) + * @param {String} className + * @return {Element} Returns the created line + * @private */ - ItemSet.prototype.setItems = function(items) { - var me = this, - ids, - oldItemsData = this.itemsData; - - // replace the dataset - if (!items) { - this.itemsData = null; + TimeAxis.prototype._repaintMajorLine = function (x, orientation, className) { + // reuse redundant line + var line = this.dom.redundant.lines.shift(); + if (!line) { + // create vertical line + line = document.createElement('div'); + this.dom.background.appendChild(line); } - else if (items instanceof DataSet || items instanceof DataView) { - this.itemsData = items; + this.dom.lines.push(line); + + var props = this.props; + if (orientation == 'top') { + line.style.top = '0'; } else { - throw new TypeError('Data must be an instance of DataSet or DataView'); + line.style.top = this.body.domProps.top.height + 'px'; } + line.style.left = (x - props.majorLineWidth / 2) + 'px'; + line.style.height = props.majorLineHeight + 'px'; - if (oldItemsData) { - // unsubscribe from old dataset - util.forEach(this.itemListeners, function (callback, event) { - oldItemsData.off(event, callback); - }); + line.className = 'grid vertical major ' + className; - // remove all drawn items - ids = oldItemsData.getIds(); - this._onRemove(ids); - } + return line; + }; - if (this.itemsData) { - // subscribe to new dataset - var id = this.id; - util.forEach(this.itemListeners, function (callback, event) { - me.itemsData.on(event, callback, id); - }); + /** + * Determine the size of text on the axis (both major and minor axis). + * The size is calculated only once and then cached in this.props. + * @private + */ + TimeAxis.prototype._calculateCharSize = function () { + // Note: We calculate char size with every redraw. Size may change, for + // example when any of the timelines parents had display:none for example. - // add all new items - ids = this.itemsData.getIds(); - this._onAdd(ids); + // determine the char width and height on the minor axis + if (!this.dom.measureCharMinor) { + this.dom.measureCharMinor = document.createElement('DIV'); + this.dom.measureCharMinor.className = 'text minor measure'; + this.dom.measureCharMinor.style.position = 'absolute'; - // update the group holding all ungrouped items - this._updateUngrouped(); + this.dom.measureCharMinor.appendChild(document.createTextNode('0')); + this.dom.foreground.appendChild(this.dom.measureCharMinor); + } + this.props.minorCharHeight = this.dom.measureCharMinor.clientHeight; + this.props.minorCharWidth = this.dom.measureCharMinor.clientWidth; + + // determine the char width and height on the major axis + if (!this.dom.measureCharMajor) { + this.dom.measureCharMajor = document.createElement('DIV'); + this.dom.measureCharMajor.className = 'text major measure'; + this.dom.measureCharMajor.style.position = 'absolute'; + + this.dom.measureCharMajor.appendChild(document.createTextNode('0')); + this.dom.foreground.appendChild(this.dom.measureCharMajor); } + this.props.majorCharHeight = this.dom.measureCharMajor.clientHeight; + this.props.majorCharWidth = this.dom.measureCharMajor.clientWidth; }; /** - * Get the current items - * @returns {vis.DataSet | null} + * Snap a date to a rounded value. + * The snap intervals are dependent on the current scale and step. + * @param {Date} date the date to be snapped. + * @return {Date} snappedDate */ - ItemSet.prototype.getItems = function() { - return this.itemsData; + TimeAxis.prototype.snap = function(date) { + return this.step.snap(date); }; - /** - * Set groups - * @param {vis.DataSet} groups - */ - ItemSet.prototype.setGroups = function(groups) { - var me = this, - ids; + module.exports = TimeAxis; - // unsubscribe from current dataset - if (this.groupsData) { - util.forEach(this.groupListeners, function (callback, event) { - me.groupsData.unsubscribe(event, callback); - }); - // remove all drawn groups - ids = this.groupsData.getIds(); - this.groupsData = null; - this._onRemoveGroups(ids); // note: this will cause a redraw - } +/***/ }, +/* 31 */ +/***/ function(module, exports, __webpack_require__) { - // replace the dataset - if (!groups) { - this.groupsData = null; - } - else if (groups instanceof DataSet || groups instanceof DataView) { - this.groupsData = groups; - } - else { - throw new TypeError('Data must be an instance of DataSet or DataView'); - } + var Hammer = __webpack_require__(45); + var util = __webpack_require__(1); - if (this.groupsData) { - // subscribe to new dataset - var id = this.id; - util.forEach(this.groupListeners, function (callback, event) { - me.groupsData.on(event, callback, id); - }); + /** + * @constructor Item + * @param {Object} data Object containing (optional) parameters type, + * start, end, content, group, className. + * @param {{toScreen: function, toTime: function}} conversion + * Conversion functions from time to screen and vice versa + * @param {Object} options Configuration options + * // TODO: describe available options + */ + function Item (data, conversion, options) { + this.id = null; + this.parent = null; + this.data = data; + this.dom = null; + this.conversion = conversion || {}; + this.options = options || {}; - // draw all ms - ids = this.groupsData.getIds(); - this._onAddGroups(ids); - } + this.selected = false; + this.displayed = false; + this.dirty = true; - // update the group holding all ungrouped items - this._updateUngrouped(); + this.top = null; + this.left = null; + this.width = null; + this.height = null; + } - // update the order of all items in each group - this._order(); + Item.prototype.stack = true; - this.body.emitter.emit('change', {queue: true}); + /** + * Select current item + */ + Item.prototype.select = function() { + this.selected = true; + this.dirty = true; + if (this.displayed) this.redraw(); }; /** - * Get the current groups - * @returns {vis.DataSet | null} groups + * Unselect current item */ - ItemSet.prototype.getGroups = function() { - return this.groupsData; + Item.prototype.unselect = function() { + this.selected = false; + this.dirty = true; + if (this.displayed) this.redraw(); }; /** - * Remove an item by its id - * @param {String | Number} id + * Set data for the item. Existing data will be updated. The id should not + * be changed. When the item is displayed, it will be redrawn immediately. + * @param {Object} data */ - ItemSet.prototype.removeItem = function(id) { - var item = this.itemsData.get(id), - dataset = this.itemsData.getDataSet(); + Item.prototype.setData = function(data) { + this.data = data; + this.dirty = true; + if (this.displayed) this.redraw(); + }; - if (item) { - // confirm deletion - this.options.onRemove(item, function (item) { - if (item) { - // remove by id here, it is possible that an item has no id defined - // itself, so better not delete by the item itself - dataset.remove(id); - } - }); + /** + * Set a parent for the item + * @param {ItemSet | Group} parent + */ + Item.prototype.setParent = function(parent) { + if (this.displayed) { + this.hide(); + this.parent = parent; + if (this.parent) { + this.show(); + } + } + else { + this.parent = parent; } }; /** - * Get the time of an item based on it's data and options.type - * @param {Object} itemData - * @returns {string} Returns the type - * @private + * Check whether this item is visible inside given range + * @returns {{start: Number, end: Number}} range with a timestamp for start and end + * @returns {boolean} True if visible */ - ItemSet.prototype._getType = function (itemData) { - return itemData.type || this.options.type || (itemData.end ? 'range' : 'box'); + Item.prototype.isVisible = function(range) { + // Should be implemented by Item implementations + return false; }; - /** - * Get the group id for an item - * @param {Object} itemData - * @returns {string} Returns the groupId - * @private + * Show the Item in the DOM (when not already visible) + * @return {Boolean} changed */ - ItemSet.prototype._getGroupId = function (itemData) { - var type = this._getType(itemData); - if (type == 'background' && itemData.group == undefined) { - return BACKGROUND; - } - else { - return this.groupsData ? itemData.group : UNGROUPED; - } + Item.prototype.show = function() { + return false; }; /** - * Handle updated items - * @param {Number[]} ids - * @protected + * Hide the Item from the DOM (when visible) + * @return {Boolean} changed */ - ItemSet.prototype._onUpdate = function(ids) { - var me = this; + Item.prototype.hide = function() { + return false; + }; - ids.forEach(function (id) { - var itemData = me.itemsData.get(id, me.itemOptions); - var item = me.items[id]; - var type = me._getType(itemData); + /** + * Repaint the item + */ + Item.prototype.redraw = function() { + // should be implemented by the item + }; - var constructor = ItemSet.types[type]; - - if (item) { - // update item - if (!constructor || !(item instanceof constructor)) { - // item type has changed, delete the item and recreate it - me._removeItem(item); - item = null; - } - else { - me._updateItem(item, itemData); - } - } - - if (!item) { - // create item - if (constructor) { - item = new constructor(itemData, me.conversion, me.options); - item.id = id; // TODO: not so nice setting id afterwards - me._addItem(item); - } - else if (type == 'rangeoverflow') { - // TODO: deprecated since version 2.1.0 (or 3.0.0?). cleanup some day - throw new TypeError('Item type "rangeoverflow" is deprecated. Use css styling instead: ' + - '.vis.timeline .item.range .content {overflow: visible;}'); - } - else { - throw new TypeError('Unknown item type "' + type + '"'); - } - } - }); - - this._order(); - this.stackDirty = true; // force re-stacking of all items next redraw - this.body.emitter.emit('change', {queue: true}); - }; + /** + * Reposition the Item horizontally + */ + Item.prototype.repositionX = function() { + // should be implemented by the item + }; /** - * Handle added items - * @param {Number[]} ids - * @protected + * Reposition the Item vertically */ - ItemSet.prototype._onAdd = ItemSet.prototype._onUpdate; + Item.prototype.repositionY = function() { + // should be implemented by the item + }; /** - * Handle removed items - * @param {Number[]} ids + * Repaint a delete button on the top right of the item when the item is selected + * @param {HTMLElement} anchor * @protected */ - ItemSet.prototype._onRemove = function(ids) { - var count = 0; - var me = this; - ids.forEach(function (id) { - var item = me.items[id]; - if (item) { - count++; - me._removeItem(item); - } - }); + Item.prototype._repaintDeleteButton = function (anchor) { + if (this.selected && this.options.editable.remove && !this.dom.deleteButton) { + // create and show button + var me = this; - if (count) { - // update order - this._order(); - this.stackDirty = true; // force re-stacking of all items next redraw - this.body.emitter.emit('change', {queue: true}); - } - }; + var deleteButton = document.createElement('div'); + deleteButton.className = 'delete'; + deleteButton.title = 'Delete this item'; - /** - * Update the order of item in all groups - * @private - */ - ItemSet.prototype._order = function() { - // reorder the items in all groups - // TODO: optimization: only reorder groups affected by the changed items - util.forEach(this.groups, function (group) { - group.order(); - }); - }; + Hammer(deleteButton, { + preventDefault: true + }).on('tap', function (event) { + me.parent.removeFromDataSet(me); + event.stopPropagation(); + }); - /** - * Handle updated groups - * @param {Number[]} ids - * @private - */ - ItemSet.prototype._onUpdateGroups = function(ids) { - this._onAddGroups(ids); + anchor.appendChild(deleteButton); + this.dom.deleteButton = deleteButton; + } + else if (!this.selected && this.dom.deleteButton) { + // remove button + if (this.dom.deleteButton.parentNode) { + this.dom.deleteButton.parentNode.removeChild(this.dom.deleteButton); + } + this.dom.deleteButton = null; + } }; /** - * Handle changed groups (added or updated) - * @param {Number[]} ids + * Set HTML contents for the item + * @param {Element} element HTML element to fill with the contents * @private */ - ItemSet.prototype._onAddGroups = function(ids) { - var me = this; - - ids.forEach(function (id) { - var groupData = me.groupsData.get(id); - var group = me.groups[id]; - - if (!group) { - // check for reserved ids - if (id == UNGROUPED || id == BACKGROUND) { - throw new Error('Illegal group id. ' + id + ' is a reserved id.'); - } - - var groupOptions = Object.create(me.options); - util.extend(groupOptions, { - height: null - }); - - group = new Group(id, groupData, me); - me.groups[id] = group; - - // add items with this groupId to the new group - for (var itemId in me.items) { - if (me.items.hasOwnProperty(itemId)) { - var item = me.items[itemId]; - if (item.data.group == id) { - group.add(item); - } - } - } + Item.prototype._updateContents = function (element) { + var content; + if (this.options.template) { + var itemData = this.parent.itemSet.itemsData.get(this.id); // get a clone of the data from the dataset + content = this.options.template(itemData); + } + else { + content = this.data.content; + } - group.order(); - group.show(); + if(content !== this.content) { + // only replace the content when changed + if (content instanceof Element) { + element.innerHTML = ''; + element.appendChild(content); + } + else if (content != undefined) { + element.innerHTML = content; } else { - // update group - group.setData(groupData); + if (!(this.data.type == 'background' && this.data.content === undefined)) { + throw new Error('Property "content" missing in item ' + this.id); + } } - }); - this.body.emitter.emit('change', {queue: true}); + this.content = content; + } }; /** - * Handle removed groups - * @param {Number[]} ids + * Set HTML contents for the item + * @param {Element} element HTML element to fill with the contents * @private */ - ItemSet.prototype._onRemoveGroups = function(ids) { - var groups = this.groups; - ids.forEach(function (id) { - var group = groups[id]; - - if (group) { - group.hide(); - delete groups[id]; - } - }); - - this.markDirty(); - - this.body.emitter.emit('change', {queue: true}); + Item.prototype._updateTitle = function (element) { + if (this.data.title != null) { + element.title = this.data.title || ''; + } + else { + element.removeAttribute('title'); + } }; /** - * Reorder the groups if needed - * @return {boolean} changed + * Process dataAttributes timeline option and set as data- attributes on dom.content + * @param {Element} element HTML element to which the attributes will be attached * @private */ - ItemSet.prototype._orderGroups = function () { - if (this.groupsData) { - // reorder the groups - var groupIds = this.groupsData.getIds({ - order: this.options.groupOrder - }); + Item.prototype._updateDataAttributes = function(element) { + if (this.options.dataAttributes && this.options.dataAttributes.length > 0) { + var attributes = []; - var changed = !util.equalArray(groupIds, this.groupIds); - if (changed) { - // hide all groups, removes them from the DOM - var groups = this.groups; - groupIds.forEach(function (groupId) { - groups[groupId].hide(); - }); + if (Array.isArray(this.options.dataAttributes)) { + attributes = this.options.dataAttributes; + } + else if (this.options.dataAttributes == 'all') { + attributes = Object.keys(this.data); + } + else { + return; + } - // show the groups again, attach them to the DOM in correct order - groupIds.forEach(function (groupId) { - groups[groupId].show(); - }); + for (var i = 0; i < attributes.length; i++) { + var name = attributes[i]; + var value = this.data[name]; - this.groupIds = groupIds; + if (value != null) { + element.setAttribute('data-' + name, value); + } + else { + element.removeAttribute('data-' + name); + } } - - return changed; - } - else { - return false; } }; /** - * Add a new item - * @param {Item} item + * Update custom styles of the element + * @param element * @private */ - ItemSet.prototype._addItem = function(item) { - this.items[item.id] = item; + Item.prototype._updateStyle = function(element) { + // remove old styles + if (this.style) { + util.removeCssText(element, this.style); + this.style = null; + } - // add to group - var groupId = this._getGroupId(item.data); - var group = this.groups[groupId]; - if (group) group.add(item); + // append new styles + if (this.data.style) { + util.addCssText(element, this.data.style); + this.style = this.data.style; + } }; - /** - * Update an existing item - * @param {Item} item - * @param {Object} itemData - * @private - */ - ItemSet.prototype._updateItem = function(item, itemData) { - var oldGroupId = item.data.group; + module.exports = Item; - // update the items data (will redraw the item when displayed) - item.setData(itemData); - // update group - if (oldGroupId != item.data.group) { - var oldGroup = this.groups[oldGroupId]; - if (oldGroup) oldGroup.remove(item); +/***/ }, +/* 32 */ +/***/ function(module, exports, __webpack_require__) { - var groupId = this._getGroupId(item.data); - var group = this.groups[groupId]; - if (group) group.add(item); - } - }; + var Hammer = __webpack_require__(45); + var Item = __webpack_require__(31); + var BackgroundGroup = __webpack_require__(26); + var RangeItem = __webpack_require__(35); /** - * Delete an item from the ItemSet: remove it from the DOM, from the map - * with items, and from the map with visible items, and from the selection - * @param {Item} item - * @private + * @constructor BackgroundItem + * @extends Item + * @param {Object} data Object containing parameters start, end + * content, className. + * @param {{toScreen: function, toTime: function}} conversion + * Conversion functions from time to screen and vice versa + * @param {Object} [options] Configuration options + * // TODO: describe options */ - ItemSet.prototype._removeItem = function(item) { - // remove from DOM - item.hide(); + // TODO: implement support for the BackgroundItem just having a start, then being displayed as a sort of an annotation + function BackgroundItem (data, conversion, options) { + this.props = { + content: { + width: 0 + } + }; + this.overflow = false; // if contents can overflow (css styling), this flag is set to true - // remove from items - delete this.items[item.id]; + // validate data + if (data) { + if (data.start == undefined) { + throw new Error('Property "start" missing in item ' + data.id); + } + if (data.end == undefined) { + throw new Error('Property "end" missing in item ' + data.id); + } + } - // remove from selection - var index = this.selection.indexOf(item.id); - if (index != -1) this.selection.splice(index, 1); + Item.call(this, data, conversion, options); - // remove from group - item.parent && item.parent.remove(item); - }; + this.emptyContent = false; + } - /** - * Create an array containing all items being a range (having an end date) - * @param array - * @returns {Array} - * @private - */ - ItemSet.prototype._constructByEndArray = function(array) { - var endArray = []; + BackgroundItem.prototype = new Item (null, null, null); - for (var i = 0; i < array.length; i++) { - if (array[i] instanceof RangeItem) { - endArray.push(array[i]); - } - } - return endArray; - }; + BackgroundItem.prototype.baseClassName = 'item background'; + BackgroundItem.prototype.stack = false; /** - * Register the clicked item on touch, before dragStart is initiated. - * - * dragStart is initiated from a mousemove event, which can have left the item - * already resulting in an item == null - * - * @param {Event} event - * @private + * Check whether this item is visible inside given range + * @returns {{start: Number, end: Number}} range with a timestamp for start and end + * @returns {boolean} True if visible */ - ItemSet.prototype._onTouch = function (event) { - // store the touched item, used in _onDragStart - this.touchParams.item = ItemSet.itemFromTarget(event); + BackgroundItem.prototype.isVisible = function(range) { + // determine visibility + return (this.data.start < range.end) && (this.data.end > range.start); }; /** - * Start dragging the selected events - * @param {Event} event - * @private + * Repaint the item */ - ItemSet.prototype._onDragStart = function (event) { - if (!this.options.editable.updateTime && !this.options.editable.updateGroup) { - return; - } + BackgroundItem.prototype.redraw = function() { + var dom = this.dom; + if (!dom) { + // create DOM + this.dom = {}; + dom = this.dom; - var item = this.touchParams.item || null; - var me = this; - var props; + // background box + dom.box = document.createElement('div'); + // className is updated in redraw() - if (item && item.selected) { - var dragLeftItem = event.target.dragLeftItem; - var dragRightItem = event.target.dragRightItem; + // contents box + dom.content = document.createElement('div'); + dom.content.className = 'content'; + dom.box.appendChild(dom.content); - if (dragLeftItem) { - props = { - item: dragLeftItem, - initialX: event.gesture.center.clientX - }; + // Note: we do NOT attach this item as attribute to the DOM, + // such that background items cannot be selected + //dom.box['timeline-item'] = this; - if (me.options.editable.updateTime) { - props.start = item.data.start.valueOf(); - } - if (me.options.editable.updateGroup) { - if ('group' in item.data) props.group = item.data.group; - } + this.dirty = true; + } - this.touchParams.itemProps = [props]; + // append DOM to parent DOM + if (!this.parent) { + throw new Error('Cannot redraw item: no parent attached'); + } + if (!dom.box.parentNode) { + var background = this.parent.dom.background; + if (!background) { + throw new Error('Cannot redraw item: parent has no background container element'); } - else if (dragRightItem) { - props = { - item: dragRightItem, - initialX: event.gesture.center.clientX - }; + background.appendChild(dom.box); + } + this.displayed = true; - if (me.options.editable.updateTime) { - props.end = item.data.end.valueOf(); - } - if (me.options.editable.updateGroup) { - if ('group' in item.data) props.group = item.data.group; - } + // Update DOM when item is marked dirty. An item is marked dirty when: + // - the item is not yet rendered + // - the item's data is changed + // - the item is selected/deselected + if (this.dirty) { + this._updateContents(this.dom.content); + this._updateTitle(this.dom.content); + this._updateDataAttributes(this.dom.content); + this._updateStyle(this.dom.box); - this.touchParams.itemProps = [props]; - } - else { - this.touchParams.itemProps = this.getSelection().map(function (id) { - var item = me.items[id]; - var props = { - item: item, - initialX: event.gesture.center.clientX - }; + // update class + var className = (this.data.className ? (' ' + this.data.className) : '') + + (this.selected ? ' selected' : ''); + dom.box.className = this.baseClassName + className; - if (me.options.editable.updateTime) { - if ('start' in item.data) props.start = item.data.start.valueOf(); - if ('end' in item.data) props.end = item.data.end.valueOf(); - } - if (me.options.editable.updateGroup) { - if ('group' in item.data) props.group = item.data.group; - } + // determine from css whether this box has overflow + this.overflow = window.getComputedStyle(dom.content).overflow !== 'hidden'; - return props; - }); - } + // recalculate size + this.props.content.width = this.dom.content.offsetWidth; + this.height = 0; // set height zero, so this item will be ignored when stacking items - event.stopPropagation(); + this.dirty = false; } }; /** - * Drag selected items - * @param {Event} event - * @private + * Show the item in the DOM (when not already visible). The items DOM will + * be created when needed. */ - ItemSet.prototype._onDrag = function (event) { - event.preventDefault() - - if (this.touchParams.itemProps) { - var me = this; - var snap = this.body.util.snap || null; - var xOffset = this.body.dom.root.offsetLeft + this.body.domProps.left.width; + BackgroundItem.prototype.show = RangeItem.prototype.show; - // move - this.touchParams.itemProps.forEach(function (props) { - var newProps = {}; - var current = me.body.util.toTime(event.gesture.center.clientX - xOffset); - var initial = me.body.util.toTime(props.initialX - xOffset); - var offset = current - initial; + /** + * Hide the item from the DOM (when visible) + * @return {Boolean} changed + */ + BackgroundItem.prototype.hide = RangeItem.prototype.hide; - if ('start' in props) { - var start = new Date(props.start + offset); - newProps.start = snap ? snap(start) : start; - } + /** + * Reposition the item horizontally + * @Override + */ + BackgroundItem.prototype.repositionX = RangeItem.prototype.repositionX; - if ('end' in props) { - var end = new Date(props.end + offset); - newProps.end = snap ? snap(end) : end; - } + /** + * Reposition the item vertically + * @Override + */ + BackgroundItem.prototype.repositionY = function(margin) { + var onTop = this.options.orientation === 'top'; + this.dom.content.style.top = onTop ? '' : '0'; + this.dom.content.style.bottom = onTop ? '0' : ''; + var height; - if ('group' in props) { - // drag from one group to another - var group = ItemSet.groupFromTarget(event); - newProps.group = group && group.groupId; + // special positioning for subgroups + if (this.data.subgroup !== undefined) { + var itemSubgroup = this.data.subgroup; + var subgroups = this.parent.subgroups; + var subgroupIndex = subgroups[itemSubgroup].index; + // if the orientation is top, we need to take the difference in height into account. + if (onTop == true) { + // the first subgroup will have to account for the distance from the top to the first item. + height = this.parent.subgroups[itemSubgroup].height + margin.item.vertical; + height += subgroupIndex == 0 ? margin.axis - 0.5*margin.item.vertical : 0; + var newTop = this.parent.top; + for (var subgroup in subgroups) { + if (subgroups.hasOwnProperty(subgroup)) { + if (subgroups[subgroup].visible == true && subgroups[subgroup].index < subgroupIndex) { + newTop += subgroups[subgroup].height + margin.item.vertical; + } + } } - // confirm moving the item - var itemData = util.extend({}, props.item.data, newProps); - me.options.onMoving(itemData, function (itemData) { - if (itemData) { - me._updateItemProps(props.item, itemData); + // the others will have to be offset downwards with this same distance. + newTop += subgroupIndex != 0 ? margin.axis - 0.5 * margin.item.vertical : 0; + this.dom.box.style.top = newTop + 'px'; + this.dom.box.style.bottom = ''; + } + // and when the orientation is bottom: + else { + var newTop = this.parent.top; + for (var subgroup in subgroups) { + if (subgroups.hasOwnProperty(subgroup)) { + if (subgroups[subgroup].visible == true && subgroups[subgroup].index > subgroupIndex) { + newTop += subgroups[subgroup].height + margin.item.vertical; + } } - }); - }); - - this.stackDirty = true; // force re-stacking of all items next redraw - this.body.emitter.emit('change'); - - event.stopPropagation(); + } + height = this.parent.subgroups[itemSubgroup].height + margin.item.vertical; + this.dom.box.style.top = newTop + 'px'; + this.dom.box.style.bottom = ''; + } + } + // and in the case of no subgroups: + else { + // we want backgrounds with groups to only show in groups. + if (this.parent instanceof BackgroundGroup) { + // if the item is not in a group: + height = Math.max(this.parent.height, + this.parent.itemSet.body.domProps.center.height, + this.parent.itemSet.body.domProps.centerContainer.height); + this.dom.box.style.top = onTop ? '0' : ''; + this.dom.box.style.bottom = onTop ? '' : '0'; + } + else { + height = this.parent.height; + // same alignment for items when orientation is top or bottom + this.dom.box.style.top = this.parent.top + 'px'; + this.dom.box.style.bottom = ''; + } } + this.dom.box.style.height = height + 'px'; }; + module.exports = BackgroundItem; + + +/***/ }, +/* 33 */ +/***/ function(module, exports, __webpack_require__) { + + var Item = __webpack_require__(31); + var util = __webpack_require__(1); + /** - * Update an items properties - * @param {Item} item - * @param {Object} props Can contain properties start, end, and group. - * @private + * @constructor BoxItem + * @extends Item + * @param {Object} data Object containing parameters start + * content, className. + * @param {{toScreen: function, toTime: function}} conversion + * Conversion functions from time to screen and vice versa + * @param {Object} [options] Configuration options + * // TODO: describe available options */ - ItemSet.prototype._updateItemProps = function(item, props) { - // TODO: copy all properties from props to item? (also new ones) - if ('start' in props) item.data.start = props.start; - if ('end' in props) item.data.end = props.end; - if ('group' in props && item.data.group != props.group) { - this._moveToGroup(item, props.group) + function BoxItem (data, conversion, options) { + this.props = { + dot: { + width: 0, + height: 0 + }, + line: { + width: 0, + height: 0 + } + }; + + // validate data + if (data) { + if (data.start == undefined) { + throw new Error('Property "start" missing in item ' + data); + } } - }; + + Item.call(this, data, conversion, options); + } + + BoxItem.prototype = new Item (null, null, null); /** - * Move an item to another group - * @param {Item} item - * @param {String | Number} groupId - * @private + * Check whether this item is visible inside given range + * @returns {{start: Number, end: Number}} range with a timestamp for start and end + * @returns {boolean} True if visible */ - ItemSet.prototype._moveToGroup = function(item, groupId) { - var group = this.groups[groupId]; - if (group && group.groupId != item.data.group) { - var oldGroup = item.parent; - oldGroup.remove(item); - oldGroup.order(); - group.add(item); - group.order(); - - item.data.group = group.groupId; - } + BoxItem.prototype.isVisible = function(range) { + // determine visibility + // TODO: account for the real width of the item. Right now we just add 1/4 to the window + var interval = (range.end - range.start) / 4; + return (this.data.start > range.start - interval) && (this.data.start < range.end + interval); }; /** - * End of dragging selected items - * @param {Event} event - * @private + * Repaint the item */ - ItemSet.prototype._onDragEnd = function (event) { - event.preventDefault() + BoxItem.prototype.redraw = function() { + var dom = this.dom; + if (!dom) { + // create DOM + this.dom = {}; + dom = this.dom; - if (this.touchParams.itemProps) { - // prepare a change set for the changed items - var changes = [], - me = this, - dataset = this.itemsData.getDataSet(); + // create main box + dom.box = document.createElement('DIV'); - var itemProps = this.touchParams.itemProps ; - this.touchParams.itemProps = null; - itemProps.forEach(function (props) { - var id = props.item.id, - itemData = me.itemsData.get(id, me.itemOptions); + // contents box (inside the background box). used for making margins + dom.content = document.createElement('DIV'); + dom.content.className = 'content'; + dom.box.appendChild(dom.content); - var changed = false; - if ('start' in props.item.data) { - changed = (props.start != props.item.data.start.valueOf()); - itemData.start = util.convert(props.item.data.start, - dataset._options.type && dataset._options.type.start || 'Date'); - } - if ('end' in props.item.data) { - changed = changed || (props.end != props.item.data.end.valueOf()); - itemData.end = util.convert(props.item.data.end, - dataset._options.type && dataset._options.type.end || 'Date'); - } - if ('group' in props.item.data) { - changed = changed || (props.group != props.item.data.group); - itemData.group = props.item.data.group; - } + // line to axis + dom.line = document.createElement('DIV'); + dom.line.className = 'line'; - // only apply changes when start or end is actually changed - if (changed) { - me.options.onMove(itemData, function (itemData) { - if (itemData) { - // apply changes - itemData[dataset._fieldId] = id; // ensure the item contains its id (can be undefined) - changes.push(itemData); - } - else { - // restore original values - me._updateItemProps(props.item, props); + // dot on axis + dom.dot = document.createElement('DIV'); + dom.dot.className = 'dot'; - me.stackDirty = true; // force re-stacking of all items next redraw - me.body.emitter.emit('change'); - } - }); - } - }); + // attach this item as attribute + dom.box['timeline-item'] = this; - // apply the changes to the data (if there are changes) - if (changes.length) { - dataset.update(changes); - } + this.dirty = true; + } - event.stopPropagation(); + // append DOM to parent DOM + if (!this.parent) { + throw new Error('Cannot redraw item: no parent attached'); + } + if (!dom.box.parentNode) { + var foreground = this.parent.dom.foreground; + if (!foreground) throw new Error('Cannot redraw item: parent has no foreground container element'); + foreground.appendChild(dom.box); + } + if (!dom.line.parentNode) { + var background = this.parent.dom.background; + if (!background) throw new Error('Cannot redraw item: parent has no background container element'); + background.appendChild(dom.line); + } + if (!dom.dot.parentNode) { + var axis = this.parent.dom.axis; + if (!background) throw new Error('Cannot redraw item: parent has no axis container element'); + axis.appendChild(dom.dot); + } + this.displayed = true; + + // Update DOM when item is marked dirty. An item is marked dirty when: + // - the item is not yet rendered + // - the item's data is changed + // - the item is selected/deselected + if (this.dirty) { + this._updateContents(this.dom.content); + this._updateTitle(this.dom.box); + this._updateDataAttributes(this.dom.box); + this._updateStyle(this.dom.box); + + // update class + var className = (this.data.className? ' ' + this.data.className : '') + + (this.selected ? ' selected' : ''); + dom.box.className = 'item box' + className; + dom.line.className = 'item line' + className; + dom.dot.className = 'item dot' + className; + + // recalculate size + this.props.dot.height = dom.dot.offsetHeight; + this.props.dot.width = dom.dot.offsetWidth; + this.props.line.width = dom.line.offsetWidth; + this.width = dom.box.offsetWidth; + this.height = dom.box.offsetHeight; + + this.dirty = false; } + + this._repaintDeleteButton(dom.box); }; /** - * Handle selecting/deselecting an item when tapping it - * @param {Event} event - * @private + * Show the item in the DOM (when not already displayed). The items DOM will + * be created when needed. */ - ItemSet.prototype._onSelectItem = function (event) { - if (!this.options.selectable) return; - - var ctrlKey = event.gesture.srcEvent && event.gesture.srcEvent.ctrlKey; - var shiftKey = event.gesture.srcEvent && event.gesture.srcEvent.shiftKey; - if (ctrlKey || shiftKey) { - this._onMultiSelectItem(event); - return; + BoxItem.prototype.show = function() { + if (!this.displayed) { + this.redraw(); } + }; - var oldSelection = this.getSelection(); + /** + * Hide the item from the DOM (when visible) + */ + BoxItem.prototype.hide = function() { + if (this.displayed) { + var dom = this.dom; - var item = ItemSet.itemFromTarget(event); - var selection = item ? [item.id] : []; - this.setSelection(selection); + if (dom.box.parentNode) dom.box.parentNode.removeChild(dom.box); + if (dom.line.parentNode) dom.line.parentNode.removeChild(dom.line); + if (dom.dot.parentNode) dom.dot.parentNode.removeChild(dom.dot); - var newSelection = this.getSelection(); + this.top = null; + this.left = null; - // emit a select event, - // except when old selection is empty and new selection is still empty - if (newSelection.length > 0 || oldSelection.length > 0) { - this.body.emitter.emit('select', { - items: newSelection - }); + this.displayed = false; } }; /** - * Handle creation and updates of an item on double tap - * @param event - * @private + * Reposition the item horizontally + * @Override */ - ItemSet.prototype._onAddItem = function (event) { - if (!this.options.selectable) return; - if (!this.options.editable.add) return; - - var me = this, - snap = this.body.util.snap || null, - item = ItemSet.itemFromTarget(event); - - if (item) { - // update item + BoxItem.prototype.repositionX = function() { + var start = this.conversion.toScreen(this.data.start); + var align = this.options.align; + var left; + var box = this.dom.box; + var line = this.dom.line; + var dot = this.dom.dot; - // execute async handler to update the item (or cancel it) - var itemData = me.itemsData.get(item.id); // get a clone of the data from the dataset - this.options.onUpdate(itemData, function (itemData) { - if (itemData) { - me.itemsData.getDataSet().update(itemData); - } - }); + // calculate left position of the box + if (align == 'right') { + this.left = start - this.width; + } + else if (align == 'left') { + this.left = start; } else { - // add item - var xAbs = util.getAbsoluteLeft(this.dom.frame); - var x = event.gesture.center.pageX - xAbs; - var start = this.body.util.toTime(x); - var newItem = { - start: snap ? snap(start) : start, - content: 'new item' - }; - - // when default type is a range, add a default end date to the new item - if (this.options.type === 'range') { - var end = this.body.util.toTime(x + this.props.width / 5); - newItem.end = snap ? snap(end) : end; - } + // default or 'center' + this.left = start - this.width / 2; + } - newItem[this.itemsData._fieldId] = util.randomUUID(); + // reposition box + box.style.left = this.left + 'px'; - var group = ItemSet.groupFromTarget(event); - if (group) { - newItem.group = group.groupId; - } + // reposition line + line.style.left = (start - this.props.line.width / 2) + 'px'; - // execute async handler to customize (or cancel) adding an item - this.options.onAdd(newItem, function (item) { - if (item) { - me.itemsData.getDataSet().add(item); - // TODO: need to trigger a redraw? - } - }); - } + // reposition dot + dot.style.left = (start - this.props.dot.width / 2) + 'px'; }; /** - * Handle selecting/deselecting multiple items when holding an item - * @param {Event} event - * @private + * Reposition the item vertically + * @Override */ - ItemSet.prototype._onMultiSelectItem = function (event) { - if (!this.options.selectable) return; + BoxItem.prototype.repositionY = function() { + var orientation = this.options.orientation; + var box = this.dom.box; + var line = this.dom.line; + var dot = this.dom.dot; - var selection, - item = ItemSet.itemFromTarget(event); + if (orientation == 'top') { + box.style.top = (this.top || 0) + 'px'; - if (item) { - // multi select items - selection = this.getSelection(); // current selection + line.style.top = '0'; + line.style.height = (this.parent.top + this.top + 1) + 'px'; + line.style.bottom = ''; + } + else { // orientation 'bottom' + var itemSetHeight = this.parent.itemSet.props.height; // TODO: this is nasty + var lineHeight = itemSetHeight - this.parent.top - this.parent.height + this.top; - var shiftKey = event.gesture.touches[0] && event.gesture.touches[0].shiftKey || false; - if (shiftKey) { - // select all items between the old selection and the tapped item + box.style.top = (this.parent.height - this.top - this.height || 0) + 'px'; + line.style.top = (itemSetHeight - lineHeight) + 'px'; + line.style.bottom = '0'; + } - // determine the selection range - selection.push(item.id); - var range = ItemSet._getItemRange(this.itemsData.get(selection, this.itemOptions)); + dot.style.top = (-this.props.dot.height / 2) + 'px'; + }; - // select all items within the selection range - selection = []; - for (var id in this.items) { - if (this.items.hasOwnProperty(id)) { - var _item = this.items[id]; - var start = _item.data.start; - var end = (_item.data.end !== undefined) ? _item.data.end : start; + module.exports = BoxItem; - if (start >= range.min && end <= range.max) { - selection.push(_item.id); // do not use id but item.id, id itself is stringified - } - } - } - } - else { - // add/remove this item from the current selection - var index = selection.indexOf(item.id); - if (index == -1) { - // item is not yet selected -> select it - selection.push(item.id); - } - else { - // item is already selected -> deselect it - selection.splice(index, 1); - } - } - this.setSelection(selection); +/***/ }, +/* 34 */ +/***/ function(module, exports, __webpack_require__) { - this.body.emitter.emit('select', { - items: this.getSelection() - }); - } - }; + var Item = __webpack_require__(31); /** - * Calculate the time range of a list of items - * @param {Array.} itemsData - * @return {{min: Date, max: Date}} Returns the range of the provided items - * @private + * @constructor PointItem + * @extends Item + * @param {Object} data Object containing parameters start + * content, className. + * @param {{toScreen: function, toTime: function}} conversion + * Conversion functions from time to screen and vice versa + * @param {Object} [options] Configuration options + * // TODO: describe available options */ - ItemSet._getItemRange = function(itemsData) { - var max = null; - var min = null; - - itemsData.forEach(function (data) { - if (min == null || data.start < min) { - min = data.start; + function PointItem (data, conversion, options) { + this.props = { + dot: { + top: 0, + width: 0, + height: 0 + }, + content: { + height: 0, + marginLeft: 0 } + }; - if (data.end != undefined) { - if (max == null || data.end > max) { - max = data.end; - } - } - else { - if (max == null || data.start > max) { - max = data.start; - } + // validate data + if (data) { + if (data.start == undefined) { + throw new Error('Property "start" missing in item ' + data); } - }); - - return { - min: min, - max: max } - }; + + Item.call(this, data, conversion, options); + } + + PointItem.prototype = new Item (null, null, null); /** - * Find an item from an event target: - * searches for the attribute 'timeline-item' in the event target's element tree - * @param {Event} event - * @return {Item | null} item + * Check whether this item is visible inside given range + * @returns {{start: Number, end: Number}} range with a timestamp for start and end + * @returns {boolean} True if visible */ - ItemSet.itemFromTarget = function(event) { - var target = event.target; - while (target) { - if (target.hasOwnProperty('timeline-item')) { - return target['timeline-item']; - } - target = target.parentNode; - } - - return null; + PointItem.prototype.isVisible = function(range) { + // determine visibility + // TODO: account for the real width of the item. Right now we just add 1/4 to the window + var interval = (range.end - range.start) / 4; + return (this.data.start > range.start - interval) && (this.data.start < range.end + interval); }; /** - * Find the Group from an event target: - * searches for the attribute 'timeline-group' in the event target's element tree - * @param {Event} event - * @return {Group | null} group + * Repaint the item */ - ItemSet.groupFromTarget = function(event) { - var target = event.target; - while (target) { - if (target.hasOwnProperty('timeline-group')) { - return target['timeline-group']; - } - target = target.parentNode; - } + PointItem.prototype.redraw = function() { + var dom = this.dom; + if (!dom) { + // create DOM + this.dom = {}; + dom = this.dom; - return null; - }; + // background box + dom.point = document.createElement('div'); + // className is updated in redraw() - /** - * Find the ItemSet from an event target: - * searches for the attribute 'timeline-itemset' in the event target's element tree - * @param {Event} event - * @return {ItemSet | null} item - */ - ItemSet.itemSetFromTarget = function(event) { - var target = event.target; - while (target) { - if (target.hasOwnProperty('timeline-itemset')) { - return target['timeline-itemset']; + // contents box, right from the dot + dom.content = document.createElement('div'); + dom.content.className = 'content'; + dom.point.appendChild(dom.content); + + // dot at start + dom.dot = document.createElement('div'); + dom.point.appendChild(dom.dot); + + // attach this item as attribute + dom.point['timeline-item'] = this; + + this.dirty = true; + } + + // append DOM to parent DOM + if (!this.parent) { + throw new Error('Cannot redraw item: no parent attached'); + } + if (!dom.point.parentNode) { + var foreground = this.parent.dom.foreground; + if (!foreground) { + throw new Error('Cannot redraw item: parent has no foreground container element'); } - target = target.parentNode; + foreground.appendChild(dom.point); } + this.displayed = true; - return null; - }; + // Update DOM when item is marked dirty. An item is marked dirty when: + // - the item is not yet rendered + // - the item's data is changed + // - the item is selected/deselected + if (this.dirty) { + this._updateContents(this.dom.content); + this._updateTitle(this.dom.point); + this._updateDataAttributes(this.dom.point); + this._updateStyle(this.dom.point); - module.exports = ItemSet; + // update class + var className = (this.data.className? ' ' + this.data.className : '') + + (this.selected ? ' selected' : ''); + dom.point.className = 'item point' + className; + dom.dot.className = 'item dot' + className; + + // recalculate size + this.width = dom.point.offsetWidth; + this.height = dom.point.offsetHeight; + this.props.dot.width = dom.dot.offsetWidth; + this.props.dot.height = dom.dot.offsetHeight; + this.props.content.height = dom.content.offsetHeight; + // resize contents + dom.content.style.marginLeft = 2 * this.props.dot.width + 'px'; + //dom.content.style.marginRight = ... + 'px'; // TODO: margin right -/***/ }, -/* 27 */ -/***/ function(module, exports, __webpack_require__) { + dom.dot.style.top = ((this.height - this.props.dot.height) / 2) + 'px'; + dom.dot.style.left = (this.props.dot.width / 2) + 'px'; - var util = __webpack_require__(1); - var stack = __webpack_require__(28); - var RangeItem = __webpack_require__(29); + this.dirty = false; + } + + this._repaintDeleteButton(dom.point); + }; /** - * @constructor Group - * @param {Number | String} groupId - * @param {Object} data - * @param {ItemSet} itemSet + * Show the item in the DOM (when not already visible). The items DOM will + * be created when needed. */ - function Group (groupId, data, itemSet) { - this.groupId = groupId; - this.subgroups = {}; - this.subgroupIndex = 0; - this.subgroupOrderer = data && data.subgroupOrder; - this.itemSet = itemSet; + PointItem.prototype.show = function() { + if (!this.displayed) { + this.redraw(); + } + }; - this.dom = {}; - this.props = { - label: { - width: 0, - height: 0 + /** + * Hide the item from the DOM (when visible) + */ + PointItem.prototype.hide = function() { + if (this.displayed) { + if (this.dom.point.parentNode) { + this.dom.point.parentNode.removeChild(this.dom.point); } - }; - this.className = null; - this.items = {}; // items filtered by groupId of this group - this.visibleItems = []; // items currently visible in window - this.orderedItems = { - byStart: [], - byEnd: [] - }; - this.checkRangedItems = false; // needed to refresh the ranged items if the window is programatically changed with NO overlap. - var me = this; - this.itemSet.body.emitter.on("checkRangedItems", function () { - me.checkRangedItems = true; - }) - - this._create(); + this.top = null; + this.left = null; - this.setData(data); - } + this.displayed = false; + } + }; /** - * Create DOM elements for the group - * @private + * Reposition the item horizontally + * @Override */ - Group.prototype._create = function() { - var label = document.createElement('div'); - label.className = 'vlabel'; - this.dom.label = label; - - var inner = document.createElement('div'); - inner.className = 'inner'; - label.appendChild(inner); - this.dom.inner = inner; - - var foreground = document.createElement('div'); - foreground.className = 'group'; - foreground['timeline-group'] = this; - this.dom.foreground = foreground; - - this.dom.background = document.createElement('div'); - this.dom.background.className = 'group'; + PointItem.prototype.repositionX = function() { + var start = this.conversion.toScreen(this.data.start); - this.dom.axis = document.createElement('div'); - this.dom.axis.className = 'group'; + this.left = start - this.props.dot.width; - // create a hidden marker to detect when the Timelines container is attached - // to the DOM, or the style of a parent of the Timeline is changed from - // display:none is changed to visible. - this.dom.marker = document.createElement('div'); - this.dom.marker.style.visibility = 'hidden'; // TODO: ask jos why this is not none? - this.dom.marker.innerHTML = '?'; - this.dom.background.appendChild(this.dom.marker); + // reposition point + this.dom.point.style.left = this.left + 'px'; }; /** - * Set the group data for this group - * @param {Object} data Group data, can contain properties content and className + * Reposition the item vertically + * @Override */ - Group.prototype.setData = function(data) { - // update contents - var content = data && data.content; - if (content instanceof Element) { - this.dom.inner.appendChild(content); - } - else if (content !== undefined && content !== null) { - this.dom.inner.innerHTML = content; + PointItem.prototype.repositionY = function() { + var orientation = this.options.orientation, + point = this.dom.point; + + if (orientation == 'top') { + point.style.top = this.top + 'px'; } else { - this.dom.inner.innerHTML = this.groupId || ''; // groupId can be null + point.style.top = (this.parent.height - this.top - this.height) + 'px'; } + }; - // update title - this.dom.label.title = data && data.title || ''; + module.exports = PointItem; - if (!this.dom.inner.firstChild) { - util.addClassName(this.dom.inner, 'hidden'); - } - else { - util.removeClassName(this.dom.inner, 'hidden'); - } - // update className - var className = data && data.className || null; - if (className != this.className) { - if (this.className) { - util.removeClassName(this.dom.label, this.className); - util.removeClassName(this.dom.foreground, this.className); - util.removeClassName(this.dom.background, this.className); - util.removeClassName(this.dom.axis, this.className); +/***/ }, +/* 35 */ +/***/ function(module, exports, __webpack_require__) { + + var Hammer = __webpack_require__(45); + var Item = __webpack_require__(31); + + /** + * @constructor RangeItem + * @extends Item + * @param {Object} data Object containing parameters start, end + * content, className. + * @param {{toScreen: function, toTime: function}} conversion + * Conversion functions from time to screen and vice versa + * @param {Object} [options] Configuration options + * // TODO: describe options + */ + function RangeItem (data, conversion, options) { + this.props = { + content: { + width: 0 } - util.addClassName(this.dom.label, className); - util.addClassName(this.dom.foreground, className); - util.addClassName(this.dom.background, className); - util.addClassName(this.dom.axis, className); - this.className = className; - } + }; + this.overflow = false; // if contents can overflow (css styling), this flag is set to true - // update style - if (this.style) { - util.removeCssText(this.dom.label, this.style); - this.style = null; - } - if (data && data.style) { - util.addCssText(this.dom.label, data.style); - this.style = data.style; + // validate data + if (data) { + if (data.start == undefined) { + throw new Error('Property "start" missing in item ' + data.id); + } + if (data.end == undefined) { + throw new Error('Property "end" missing in item ' + data.id); + } } - }; + + Item.call(this, data, conversion, options); + } + + RangeItem.prototype = new Item (null, null, null); + + RangeItem.prototype.baseClassName = 'item range'; /** - * Get the width of the group label - * @return {number} width + * Check whether this item is visible inside given range + * @returns {{start: Number, end: Number}} range with a timestamp for start and end + * @returns {boolean} True if visible */ - Group.prototype.getLabelWidth = function() { - return this.props.label.width; + RangeItem.prototype.isVisible = function(range) { + // determine visibility + return (this.data.start < range.end) && (this.data.end > range.start); }; - /** - * Repaint this group - * @param {{start: number, end: number}} range - * @param {{item: {horizontal: number, vertical: number}, axis: number}} margin - * @param {boolean} [restack=false] Force restacking of all items - * @return {boolean} Returns true if the group is resized + * Repaint the item */ - Group.prototype.redraw = function(range, margin, restack) { - var resized = false; + RangeItem.prototype.redraw = function() { + var dom = this.dom; + if (!dom) { + // create DOM + this.dom = {}; + dom = this.dom; - this.visibleItems = this._updateVisibleItems(this.orderedItems, this.visibleItems, range); + // background box + dom.box = document.createElement('div'); + // className is updated in redraw() - // force recalculation of the height of the items when the marker height changed - // (due to the Timeline being attached to the DOM or changed from display:none to visible) - var markerHeight = this.dom.marker.clientHeight; - if (markerHeight != this.lastMarkerHeight) { - this.lastMarkerHeight = markerHeight; + // contents box + dom.content = document.createElement('div'); + dom.content.className = 'content'; + dom.box.appendChild(dom.content); - util.forEach(this.items, function (item) { - item.dirty = true; - if (item.displayed) item.redraw(); - }); + // attach this item as attribute + dom.box['timeline-item'] = this; - restack = true; + this.dirty = true; } - // reposition visible items vertically - if (this.itemSet.options.stack) { // TODO: ugly way to access options... - stack.stack(this.visibleItems, margin, restack); + // append DOM to parent DOM + if (!this.parent) { + throw new Error('Cannot redraw item: no parent attached'); } - else { // no stacking - stack.nostack(this.visibleItems, margin, this.subgroups); + if (!dom.box.parentNode) { + var foreground = this.parent.dom.foreground; + if (!foreground) { + throw new Error('Cannot redraw item: parent has no foreground container element'); + } + foreground.appendChild(dom.box); } + this.displayed = true; - // recalculate the height of the group - var height = this._calculateHeight(margin); + // Update DOM when item is marked dirty. An item is marked dirty when: + // - the item is not yet rendered + // - the item's data is changed + // - the item is selected/deselected + if (this.dirty) { + this._updateContents(this.dom.content); + this._updateTitle(this.dom.box); + this._updateDataAttributes(this.dom.box); + this._updateStyle(this.dom.box); - // calculate actual size and position - var foreground = this.dom.foreground; - this.top = foreground.offsetTop; - this.left = foreground.offsetLeft; - this.width = foreground.offsetWidth; - resized = util.updateProperty(this, 'height', height) || resized; + // update class + var className = (this.data.className ? (' ' + this.data.className) : '') + + (this.selected ? ' selected' : ''); + dom.box.className = this.baseClassName + className; - // recalculate size of label - resized = util.updateProperty(this.props.label, 'width', this.dom.inner.clientWidth) || resized; - resized = util.updateProperty(this.props.label, 'height', this.dom.inner.clientHeight) || resized; + // determine from css whether this box has overflow + this.overflow = window.getComputedStyle(dom.content).overflow !== 'hidden'; - // apply new height - this.dom.background.style.height = height + 'px'; - this.dom.foreground.style.height = height + 'px'; - this.dom.label.style.height = height + 'px'; + // recalculate size + // turn off max-width to be able to calculate the real width + // this causes an extra browser repaint/reflow, but so be it + this.dom.content.style.maxWidth = 'none'; + this.props.content.width = this.dom.content.offsetWidth; + this.height = this.dom.box.offsetHeight; + this.dom.content.style.maxWidth = ''; - // update vertical position of items after they are re-stacked and the height of the group is calculated - for (var i = 0, ii = this.visibleItems.length; i < ii; i++) { - var item = this.visibleItems[i]; - item.repositionY(margin); + this.dirty = false; } - return resized; + this._repaintDeleteButton(dom.box); + this._repaintDragLeft(); + this._repaintDragRight(); }; /** - * recalculate the height of the group - * @param {{item: {horizontal: number, vertical: number}, axis: number}} margin - * @returns {number} Returns the height - * @private + * Show the item in the DOM (when not already visible). The items DOM will + * be created when needed. */ - Group.prototype._calculateHeight = function (margin) { - // recalculate the height of the group - var height; - var visibleItems = this.visibleItems; - //var visibleSubgroups = []; - //this.visibleSubgroups = 0; - this.resetSubgroups(); - var me = this; - if (visibleItems.length) { - var min = visibleItems[0].top; - var max = visibleItems[0].top + visibleItems[0].height; - util.forEach(visibleItems, function (item) { - min = Math.min(min, item.top); - max = Math.max(max, (item.top + item.height)); - if (item.data.subgroup !== undefined) { - me.subgroups[item.data.subgroup].height = Math.max(me.subgroups[item.data.subgroup].height,item.height); - me.subgroups[item.data.subgroup].visible = true; - //if (visibleSubgroups.indexOf(item.data.subgroup) == -1){ - // visibleSubgroups.push(item.data.subgroup); - // me.visibleSubgroups += 1; - //} - } - }); - if (min > margin.axis) { - // there is an empty gap between the lowest item and the axis - var offset = min - margin.axis; - max -= offset; - util.forEach(visibleItems, function (item) { - item.top -= offset; - }); - } - height = max + margin.item.vertical / 2; - } - else { - height = margin.axis + margin.item.vertical; + RangeItem.prototype.show = function() { + if (!this.displayed) { + this.redraw(); } - height = Math.max(height, this.props.label.height); - - return height; }; /** - * Show this group: attach to the DOM + * Hide the item from the DOM (when visible) + * @return {Boolean} changed */ - Group.prototype.show = function() { - if (!this.dom.label.parentNode) { - this.itemSet.dom.labelSet.appendChild(this.dom.label); - } + RangeItem.prototype.hide = function() { + if (this.displayed) { + var box = this.dom.box; - if (!this.dom.foreground.parentNode) { - this.itemSet.dom.foreground.appendChild(this.dom.foreground); - } + if (box.parentNode) { + box.parentNode.removeChild(box); + } - if (!this.dom.background.parentNode) { - this.itemSet.dom.background.appendChild(this.dom.background); - } + this.top = null; + this.left = null; - if (!this.dom.axis.parentNode) { - this.itemSet.dom.axis.appendChild(this.dom.axis); + this.displayed = false; } }; /** - * Hide this group: remove from the DOM + * Reposition the item horizontally + * @Override */ - Group.prototype.hide = function() { - var label = this.dom.label; - if (label.parentNode) { - label.parentNode.removeChild(label); - } + RangeItem.prototype.repositionX = function() { + var parentWidth = this.parent.width; + var start = this.conversion.toScreen(this.data.start); + var end = this.conversion.toScreen(this.data.end); + var contentLeft; + var contentWidth; - var foreground = this.dom.foreground; - if (foreground.parentNode) { - foreground.parentNode.removeChild(foreground); + // limit the width of the this, as browsers cannot draw very wide divs + if (start < -parentWidth) { + start = -parentWidth; } - - var background = this.dom.background; - if (background.parentNode) { - background.parentNode.removeChild(background); + if (end > 2 * parentWidth) { + end = 2 * parentWidth; } + var boxWidth = Math.max(end - start, 1); - var axis = this.dom.axis; - if (axis.parentNode) { - axis.parentNode.removeChild(axis); + if (this.overflow) { + this.left = start; + this.width = boxWidth + this.props.content.width; + contentWidth = this.props.content.width; + + // Note: The calculation of width is an optimistic calculation, giving + // a width which will not change when moving the Timeline + // So no re-stacking needed, which is nicer for the eye; + } + else { + this.left = start; + this.width = boxWidth; + contentWidth = Math.min(end - start - 2 * this.options.padding, this.props.content.width); } - }; - /** - * Add an item to the group - * @param {Item} item - */ - Group.prototype.add = function(item) { - this.items[item.id] = item; - item.setParent(this); - - // add to - if (item.data.subgroup !== undefined) { - if (this.subgroups[item.data.subgroup] === undefined) { - this.subgroups[item.data.subgroup] = {height:0, visible: false, index:this.subgroupIndex, items: []}; - this.subgroupIndex++; - } - this.subgroups[item.data.subgroup].items.push(item); - } - this.orderSubgroups(); - - if (this.visibleItems.indexOf(item) == -1) { - var range = this.itemSet.body.range; // TODO: not nice accessing the range like this - this._checkIfVisible(item, this.visibleItems, range); - } - }; - - Group.prototype.orderSubgroups = function() { - if (this.subgroupOrderer !== undefined) { - var sortArray = []; - if (typeof this.subgroupOrderer == 'string') { - for (var subgroup in this.subgroups) { - sortArray.push({subgroup: subgroup, sortField: this.subgroups[subgroup].items[0].data[this.subgroupOrderer]}) - } - sortArray.sort(function (a, b) { - return a.sortField - b.sortField; - }) - } - else if (typeof this.subgroupOrderer == 'function') { - for (var subgroup in this.subgroups) { - sortArray.push(this.subgroups[subgroup].items[0].data); - } - sortArray.sort(this.subgroupOrderer); - } - - if (sortArray.length > 0) { - for (var i = 0; i < sortArray.length; i++) { - this.subgroups[sortArray[i].subgroup].index = i; - } - } - } - }; - - Group.prototype.resetSubgroups = function() { - for (var subgroup in this.subgroups) { - if (this.subgroups.hasOwnProperty(subgroup)) { - this.subgroups[subgroup].visible = false; - } - } - }; - - /** - * Remove an item from the group - * @param {Item} item - */ - Group.prototype.remove = function(item) { - delete this.items[item.id]; - item.setParent(null); - - // remove from visible items - var index = this.visibleItems.indexOf(item); - if (index != -1) this.visibleItems.splice(index, 1); - - // TODO: also remove from ordered items? - }; - - - /** - * Remove an item from the corresponding DataSet - * @param {Item} item - */ - Group.prototype.removeFromDataSet = function(item) { - this.itemSet.removeItem(item.id); - }; - - - /** - * Reorder the items - */ - Group.prototype.order = function() { - var array = util.toArray(this.items); - var startArray = []; - var endArray = []; - - for (var i = 0; i < array.length; i++) { - if (array[i].data.end !== undefined) { - endArray.push(array[i]); - } - startArray.push(array[i]); - } - this.orderedItems = { - byStart: startArray, - byEnd: endArray - }; - - stack.orderByStart(this.orderedItems.byStart); - stack.orderByEnd(this.orderedItems.byEnd); - }; - - - /** - * Update the visible items - * @param {{byStart: Item[], byEnd: Item[]}} orderedItems All items ordered by start date and by end date - * @param {Item[]} visibleItems The previously visible items. - * @param {{start: number, end: number}} range Visible range - * @return {Item[]} visibleItems The new visible items. - * @private - */ - Group.prototype._updateVisibleItems = function(orderedItems, oldVisibleItems, range) { - var visibleItems = []; - var visibleItemsLookup = {}; // we keep this to quickly look up if an item already exists in the list without using indexOf on visibleItems - var interval = (range.end - range.start) / 4; - var lowerBound = range.start - interval; - var upperBound = range.end + interval; - var item, i; - - // this function is used to do the binary search. - var searchFunction = function (value) { - if (value < lowerBound) {return -1;} - else if (value <= upperBound) {return 0;} - else {return 1;} - } - - // first check if the items that were in view previously are still in view. - // IMPORTANT: this handles the case for the items with startdate before the window and enddate after the window! - // also cleans up invisible items. - if (oldVisibleItems.length > 0) { - for (i = 0; i < oldVisibleItems.length; i++) { - this._checkIfVisibleWithReference(oldVisibleItems[i], visibleItems, visibleItemsLookup, range); - } - } - - // we do a binary search for the items that have only start values. - var initialPosByStart = util.binarySearchCustom(orderedItems.byStart, searchFunction, 'data','start'); - - // trace the visible items from the inital start pos both ways until an invisible item is found, we only look at the start values. - this._traceVisible(initialPosByStart, orderedItems.byStart, visibleItems, visibleItemsLookup, function (item) { - return (item.data.start < lowerBound || item.data.start > upperBound); - }); - - // if the window has changed programmatically without overlapping the old window, the ranged items with start < lowerBound and end > upperbound are not shown. - // We therefore have to brute force check all items in the byEnd list - if (this.checkRangedItems == true) { - this.checkRangedItems = false; - for (i = 0; i < orderedItems.byEnd.length; i++) { - this._checkIfVisibleWithReference(orderedItems.byEnd[i], visibleItems, visibleItemsLookup, range); - } - } - else { - // we do a binary search for the items that have defined end times. - var initialPosByEnd = util.binarySearchCustom(orderedItems.byEnd, searchFunction, 'data','end'); - - // trace the visible items from the inital start pos both ways until an invisible item is found, we only look at the end values. - this._traceVisible(initialPosByEnd, orderedItems.byEnd, visibleItems, visibleItemsLookup, function (item) { - return (item.data.end < lowerBound || item.data.end > upperBound); - }); - } - - - // finally, we reposition all the visible items. - for (i = 0; i < visibleItems.length; i++) { - item = visibleItems[i]; - if (!item.displayed) item.show(); - // reposition item horizontally - item.repositionX(); - } + this.dom.box.style.left = this.left + 'px'; + this.dom.box.style.width = boxWidth + 'px'; - // debug - //console.log("new line") - //if (this.groupId == null) { - // for (i = 0; i < orderedItems.byStart.length; i++) { - // item = orderedItems.byStart[i].data; - // console.log('start',i,initialPosByStart, item.start.valueOf(), item.content, item.start >= lowerBound && item.start <= upperBound,i == initialPosByStart ? "<------------------- HEREEEE" : "") - // } - // for (i = 0; i < orderedItems.byEnd.length; i++) { - // item = orderedItems.byEnd[i].data; - // console.log('rangeEnd',i,initialPosByEnd, item.end.valueOf(), item.content, item.end >= range.start && item.end <= range.end,i == initialPosByEnd ? "<------------------- HEREEEE" : "") - // } - //} + switch (this.options.align) { + case 'left': + this.dom.content.style.left = '0'; + break; - return visibleItems; - }; + case 'right': + this.dom.content.style.left = Math.max((boxWidth - contentWidth - 2 * this.options.padding), 0) + 'px'; + break; - Group.prototype._traceVisible = function (initialPos, items, visibleItems, visibleItemsLookup, breakCondition) { - var item; - var i; + case 'center': + this.dom.content.style.left = Math.max((boxWidth - contentWidth - 2 * this.options.padding) / 2, 0) + 'px'; + break; - if (initialPos != -1) { - for (i = initialPos; i >= 0; i--) { - item = items[i]; - if (breakCondition(item)) { - break; - } - else { - if (visibleItemsLookup[item.id] === undefined) { - visibleItemsLookup[item.id] = true; - visibleItems.push(item); + default: // 'auto' + // when range exceeds left of the window, position the contents at the left of the visible area + if (this.overflow) { + if (end > 0) { + contentLeft = Math.max(-start, 0); + } + else { + contentLeft = -contentWidth; // ensure it's not visible anymore } - } - } - - for (i = initialPos + 1; i < items.length; i++) { - item = items[i]; - if (breakCondition(item)) { - break; } else { - if (visibleItemsLookup[item.id] === undefined) { - visibleItemsLookup[item.id] = true; - visibleItems.push(item); + if (start < 0) { + contentLeft = Math.min(-start, + (end - start - contentWidth - 2 * this.options.padding)); + // TODO: remove the need for options.padding. it's terrible. + } + else { + contentLeft = 0; } } - } + this.dom.content.style.left = contentLeft + 'px'; } - } - - - /** - * this function is very similar to the _checkIfInvisible() but it does not - * return booleans, hides the item if it should not be seen and always adds to - * the visibleItems. - * this one is for brute forcing and hiding. - * - * @param {Item} item - * @param {Array} visibleItems - * @param {{start:number, end:number}} range - * @private - */ - Group.prototype._checkIfVisible = function(item, visibleItems, range) { - if (item.isVisible(range)) { - if (!item.displayed) item.show(); - // reposition item horizontally - item.repositionX(); - visibleItems.push(item); - } - else { - if (item.displayed) item.hide(); - } }; - /** - * this function is very similar to the _checkIfInvisible() but it does not - * return booleans, hides the item if it should not be seen and always adds to - * the visibleItems. - * this one is for brute forcing and hiding. - * - * @param {Item} item - * @param {Array} visibleItems - * @param {{start:number, end:number}} range - * @private + * Reposition the item vertically + * @Override */ - Group.prototype._checkIfVisibleWithReference = function(item, visibleItems, visibleItemsLookup, range) { - if (item.isVisible(range)) { - if (visibleItemsLookup[item.id] === undefined) { - visibleItemsLookup[item.id] = true; - visibleItems.push(item); - } + RangeItem.prototype.repositionY = function() { + var orientation = this.options.orientation, + box = this.dom.box; + + if (orientation == 'top') { + box.style.top = this.top + 'px'; } else { - if (item.displayed) item.hide(); + box.style.top = (this.parent.height - this.top - this.height) + 'px'; } }; - - - module.exports = Group; - - -/***/ }, -/* 28 */ -/***/ function(module, exports, __webpack_require__) { - - // Utility functions for ordering and stacking of items - var EPSILON = 0.001; // used when checking collisions, to prevent round-off errors - - /** - * Order items by their start data - * @param {Item[]} items - */ - exports.orderByStart = function(items) { - items.sort(function (a, b) { - return a.data.start - b.data.start; - }); - }; - /** - * Order items by their end date. If they have no end date, their start date - * is used. - * @param {Item[]} items + * Repaint a drag area on the left side of the range when the range is selected + * @protected */ - exports.orderByEnd = function(items) { - items.sort(function (a, b) { - var aTime = ('end' in a.data) ? a.data.end : a.data.start, - bTime = ('end' in b.data) ? b.data.end : b.data.start; - - return aTime - bTime; - }); - }; + RangeItem.prototype._repaintDragLeft = function () { + if (this.selected && this.options.editable.updateTime && !this.dom.dragLeft) { + // create and show drag area + var dragLeft = document.createElement('div'); + dragLeft.className = 'drag-left'; + dragLeft.dragLeftItem = this; - /** - * Adjust vertical positions of the items such that they don't overlap each - * other. - * @param {Item[]} items - * All visible items - * @param {{item: {horizontal: number, vertical: number}, axis: number}} margin - * Margins between items and between items and the axis. - * @param {boolean} [force=false] - * If true, all items will be repositioned. If false (default), only - * items having a top===null will be re-stacked - */ - exports.stack = function(items, margin, force) { - var i, iMax; + // TODO: this should be redundant? + Hammer(dragLeft, { + preventDefault: true + }).on('drag', function () { + //console.log('drag left') + }); - if (force) { - // reset top position of all items - for (i = 0, iMax = items.length; i < iMax; i++) { - items[i].top = null; - } + this.dom.box.appendChild(dragLeft); + this.dom.dragLeft = dragLeft; } - - // calculate new, non-overlapping positions - for (i = 0, iMax = items.length; i < iMax; i++) { - var item = items[i]; - if (item.stack && item.top === null) { - // initialize top position - item.top = margin.axis; - - do { - // TODO: optimize checking for overlap. when there is a gap without items, - // you only need to check for items from the next item on, not from zero - var collidingItem = null; - for (var j = 0, jj = items.length; j < jj; j++) { - var other = items[j]; - if (other.top !== null && other !== item && other.stack && exports.collision(item, other, margin.item)) { - collidingItem = other; - break; - } - } - - if (collidingItem != null) { - // There is a collision. Reposition the items above the colliding element - item.top = collidingItem.top + collidingItem.height + margin.item.vertical; - } - } while (collidingItem); + else if (!this.selected && this.dom.dragLeft) { + // delete drag area + if (this.dom.dragLeft.parentNode) { + this.dom.dragLeft.parentNode.removeChild(this.dom.dragLeft); } + this.dom.dragLeft = null; } }; - /** - * Adjust vertical positions of the items without stacking them - * @param {Item[]} items - * All visible items - * @param {{item: {horizontal: number, vertical: number}, axis: number}} margin - * Margins between items and between items and the axis. + * Repaint a drag area on the right side of the range when the range is selected + * @protected */ - exports.nostack = function(items, margin, subgroups) { - var i, iMax, newTop; + RangeItem.prototype._repaintDragRight = function () { + if (this.selected && this.options.editable.updateTime && !this.dom.dragRight) { + // create and show drag area + var dragRight = document.createElement('div'); + dragRight.className = 'drag-right'; + dragRight.dragRightItem = this; - // reset top position of all items - for (i = 0, iMax = items.length; i < iMax; i++) { - if (items[i].data.subgroup !== undefined) { - newTop = margin.axis; - for (var subgroup in subgroups) { - if (subgroups.hasOwnProperty(subgroup)) { - if (subgroups[subgroup].visible == true && subgroups[subgroup].index < subgroups[items[i].data.subgroup].index) { - newTop += subgroups[subgroup].height + margin.item.vertical; - } - } - } - items[i].top = newTop; - } - else { - items[i].top = margin.axis; + // TODO: this should be redundant? + Hammer(dragRight, { + preventDefault: true + }).on('drag', function () { + //console.log('drag right') + }); + + this.dom.box.appendChild(dragRight); + this.dom.dragRight = dragRight; + } + else if (!this.selected && this.dom.dragRight) { + // delete drag area + if (this.dom.dragRight.parentNode) { + this.dom.dragRight.parentNode.removeChild(this.dom.dragRight); } + this.dom.dragRight = null; } }; - /** - * Test if the two provided items collide - * The items must have parameters left, width, top, and height. - * @param {Item} a The first item - * @param {Item} b The second item - * @param {{horizontal: number, vertical: number}} margin - * An object containing a horizontal and vertical - * minimum required margin. - * @return {boolean} true if a and b collide, else false - */ - exports.collision = function(a, b, margin) { - return ((a.left - margin.horizontal + EPSILON) < (b.left + b.width) && - (a.left + a.width + margin.horizontal - EPSILON) > b.left && - (a.top - margin.vertical + EPSILON) < (b.top + b.height) && - (a.top + a.height + margin.vertical - EPSILON) > b.top); - }; + module.exports = RangeItem; /***/ }, -/* 29 */ +/* 36 */ /***/ function(module, exports, __webpack_require__) { - var Hammer = __webpack_require__(19); - var Item = __webpack_require__(30); + var Emitter = __webpack_require__(56); + var Hammer = __webpack_require__(45); + var keycharm = __webpack_require__(57); + var util = __webpack_require__(1); + var hammerUtil = __webpack_require__(47); + var DataSet = __webpack_require__(3); + var DataView = __webpack_require__(4); + var dotparser = __webpack_require__(42); + var gephiParser = __webpack_require__(43); + var Groups = __webpack_require__(38); + var Images = __webpack_require__(39); + var Node = __webpack_require__(40); + var Edge = __webpack_require__(37); + var Popup = __webpack_require__(41); + var MixinLoader = __webpack_require__(54); + var Activator = __webpack_require__(55); + var locales = __webpack_require__(49); + + // Load custom shapes into CanvasRenderingContext2D + __webpack_require__(50); /** - * @constructor RangeItem - * @extends Item - * @param {Object} data Object containing parameters start, end - * content, className. - * @param {{toScreen: function, toTime: function}} conversion - * Conversion functions from time to screen and vice versa - * @param {Object} [options] Configuration options - * // TODO: describe options + * @constructor Network + * Create a network visualization, displaying nodes and edges. + * + * @param {Element} container The DOM element in which the Network will + * be created. Normally a div element. + * @param {Object} data An object containing parameters + * {Array} nodes + * {Array} edges + * @param {Object} options Options */ - function RangeItem (data, conversion, options) { - this.props = { - content: { - width: 0 - } - }; - this.overflow = false; // if contents can overflow (css styling), this flag is set to true - - // validate data - if (data) { - if (data.start == undefined) { - throw new Error('Property "start" missing in item ' + data.id); - } - if (data.end == undefined) { - throw new Error('Property "end" missing in item ' + data.id); - } + function Network (container, data, options) { + if (!(this instanceof Network)) { + throw new SyntaxError('Constructor must be called with the new operator'); } - Item.call(this, data, conversion, options); - } + this._determineBrowserMethod(); + this._initializeMixinLoaders(); - RangeItem.prototype = new Item (null, null, null); + // create variables and set default values + this.containerElement = container; - RangeItem.prototype.baseClassName = 'item range'; + // render and calculation settings + this.renderRefreshRate = 60; // hz (fps) + this.renderTimestep = 1000 / this.renderRefreshRate; // ms -- saves calculation later on + this.renderTime = 0; // measured time it takes to render a frame + this.physicsTime = 0; // measured time it takes to render a frame + this.runDoubleSpeed = false; + this.physicsDiscreteStepsize = 0.50; // discrete stepsize of the simulation - /** - * Check whether this item is visible inside given range - * @returns {{start: Number, end: Number}} range with a timestamp for start and end - * @returns {boolean} True if visible - */ - RangeItem.prototype.isVisible = function(range) { - // determine visibility - return (this.data.start < range.end) && (this.data.end > range.start); - }; + this.initializing = true; - /** - * Repaint the item - */ - RangeItem.prototype.redraw = function() { - var dom = this.dom; - if (!dom) { - // create DOM - this.dom = {}; - dom = this.dom; + this.triggerFunctions = {add:null,edit:null,editEdge:null,connect:null,del:null}; - // background box - dom.box = document.createElement('div'); - // className is updated in redraw() + // set constant values + this.defaultOptions = { + nodes: { + mass: 1, + radiusMin: 10, + radiusMax: 30, + radius: 10, + shape: 'ellipse', + image: undefined, + widthMin: 16, // px + widthMax: 64, // px + fontColor: 'black', + fontSize: 14, // px + fontFace: 'verdana', + fontFill: undefined, + fontStrokeWidth: 0, // px + fontStrokeColor: 'white', + level: -1, + color: { + border: '#2B7CE9', + background: '#97C2FC', + highlight: { + border: '#2B7CE9', + background: '#D2E5FF' + }, + hover: { + border: '#2B7CE9', + background: '#D2E5FF' + } + }, + group: undefined, + borderWidth: 1, + borderWidthSelected: undefined + }, + edges: { + widthMin: 1, // + widthMax: 15,// + width: 1, + widthSelectionMultiplier: 2, + hoverWidth: 1.5, + style: 'line', + color: { + color:'#848484', + highlight:'#848484', + hover: '#848484' + }, + fontColor: '#343434', + fontSize: 14, // px + fontFace: 'arial', + fontFill: 'white', + fontStrokeWidth: 0, // px + fontStrokeColor: 'white', + labelAlignment:'horizontal', + arrowScaleFactor: 1, + dash: { + length: 10, + gap: 5, + altLength: undefined + }, + inheritColor: "from" // to, from, false, true (== from) + }, + configurePhysics:false, + physics: { + barnesHut: { + enabled: true, + thetaInverted: 1 / 0.5, // inverted to save time during calculation + gravitationalConstant: -2000, + centralGravity: 0.3, + springLength: 95, + springConstant: 0.04, + damping: 0.09 + }, + repulsion: { + centralGravity: 0.0, + springLength: 200, + springConstant: 0.05, + nodeDistance: 100, + damping: 0.09 + }, + hierarchicalRepulsion: { + enabled: false, + centralGravity: 0.0, + springLength: 100, + springConstant: 0.01, + nodeDistance: 150, + damping: 0.09 + }, + damping: null, + centralGravity: null, + springLength: null, + springConstant: null + }, + clustering: { // Per Node in Cluster = PNiC + enabled: false, // (Boolean) | global on/off switch for clustering. + initialMaxNodes: 100, // (# nodes) | if the initial amount of nodes is larger than this, we cluster until the total number is less than this threshold. + clusterThreshold:500, // (# nodes) | during calculate forces, we check if the total number of nodes is larger than this. If it is, cluster until reduced to reduceToNodes + reduceToNodes:300, // (# nodes) | during calculate forces, we check if the total number of nodes is larger than clusterThreshold. If it is, cluster until reduced to this + chainThreshold: 0.4, // (% of all drawn nodes)| maximum percentage of allowed chainnodes (long strings of connected nodes) within all nodes. (lower means less chains). + clusterEdgeThreshold: 20, // (px) | edge length threshold. if smaller, this node is clustered. + sectorThreshold: 100, // (# nodes in cluster) | cluster size threshold. If larger, expanding in own sector. + screenSizeThreshold: 0.2, // (% of canvas) | relative size threshold. If the width or height of a clusternode takes up this much of the screen, decluster node. + fontSizeMultiplier: 4.0, // (px PNiC) | how much the cluster font size grows per node in cluster (in px). + maxFontSize: 1000, + forceAmplification: 0.1, // (multiplier PNiC) | factor of increase fo the repulsion force of a cluster (per node in cluster). + distanceAmplification: 0.1, // (multiplier PNiC) | factor how much the repulsion distance of a cluster increases (per node in cluster). + edgeGrowth: 20, // (px PNiC) | amount of clusterSize connected to the edge is multiplied with this and added to edgeLength. + nodeScaling: {width: 1, // (px PNiC) | growth of the width per node in cluster. + height: 1, // (px PNiC) | growth of the height per node in cluster. + radius: 1}, // (px PNiC) | growth of the radius per node in cluster. + maxNodeSizeIncrements: 600, // (# increments) | max growth of the width per node in cluster. + activeAreaBoxSize: 80, // (px) | box area around the curser where clusters are popped open. + clusterLevelDifference: 2 + }, + navigation: { + enabled: false + }, + keyboard: { + enabled: false, + speed: {x: 10, y: 10, zoom: 0.02}, + bindToWindow: true + }, + dataManipulation: { + enabled: false, + initiallyVisible: false + }, + hierarchicalLayout: { + enabled:false, + levelSeparation: 150, + nodeSpacing: 100, + direction: "UD", // UD, DU, LR, RL + layout: "hubsize" // hubsize, directed + }, + freezeForStabilization: false, + smoothCurves: { + enabled: true, + dynamic: true, + type: "continuous", + roundness: 0.5 + }, + maxVelocity: 30, + minVelocity: 0.1, // px/s + stabilize: true, // stabilize before displaying the network + stabilizationIterations: 1000, // maximum number of iteration to stabilize + zoomExtentOnStabilize: true, + locale: 'en', + locales: locales, + tooltip: { + delay: 300, + fontColor: 'black', + fontSize: 14, // px + fontFace: 'verdana', + color: { + border: '#666', + background: '#FFFFC6' + } + }, + dragNetwork: true, + dragNodes: true, + zoomable: true, + hover: false, + hideEdgesOnDrag: false, + hideNodesOnDrag: false, + width : '100%', + height : '100%', + selectable: true + }; + this.constants = util.extend({}, this.defaultOptions); + this.pixelRatio = 1; + + + this.hoverObj = {nodes:{},edges:{}}; + this.controlNodesActive = false; + this.navigationHammers = {existing:[], _new: []}; - // contents box - dom.content = document.createElement('div'); - dom.content.className = 'content'; - dom.box.appendChild(dom.content); + // animation properties + this.animationSpeed = 1/this.renderRefreshRate; + this.animationEasingFunction = "easeInOutQuint"; + this.animating = false; + this.easingTime = 0; + this.sourceScale = 0; + this.targetScale = 0; + this.sourceTranslation = 0; + this.targetTranslation = 0; + this.lockedOnNodeId = null; + this.lockedOnNodeOffset = null; + this.touchTime = 0; - // attach this item as attribute - dom.box['timeline-item'] = this; + // Node variables + var network = this; + this.groups = new Groups(); // object with groups + this.images = new Images(); // object with images + this.images.setOnloadCallback(function (status) { + network._redraw(); + }); - this.dirty = true; - } + // keyboard navigation variables + this.xIncrement = 0; + this.yIncrement = 0; + this.zoomIncrement = 0; - // append DOM to parent DOM - if (!this.parent) { - throw new Error('Cannot redraw item: no parent attached'); - } - if (!dom.box.parentNode) { - var foreground = this.parent.dom.foreground; - if (!foreground) { - throw new Error('Cannot redraw item: parent has no foreground container element'); - } - foreground.appendChild(dom.box); - } - this.displayed = true; + // loading all the mixins: + // load the force calculation functions, grouped under the physics system. + this._loadPhysicsSystem(); + // create a frame and canvas + this._create(); + // load the sector system. (mandatory, fully integrated with Network) + this._loadSectorSystem(); + // load the cluster system. (mandatory, even when not using the cluster system, there are function calls to it) + this._loadClusterSystem(); + // load the selection system. (mandatory, required by Network) + this._loadSelectionSystem(); + // load the selection system. (mandatory, required by Network) + this._loadHierarchySystem(); - // Update DOM when item is marked dirty. An item is marked dirty when: - // - the item is not yet rendered - // - the item's data is changed - // - the item is selected/deselected - if (this.dirty) { - this._updateContents(this.dom.content); - this._updateTitle(this.dom.box); - this._updateDataAttributes(this.dom.box); - this._updateStyle(this.dom.box); - // update class - var className = (this.data.className ? (' ' + this.data.className) : '') + - (this.selected ? ' selected' : ''); - dom.box.className = this.baseClassName + className; + // apply options + this._setTranslation(this.frame.clientWidth / 2, this.frame.clientHeight / 2); + this._setScale(1); + this.setOptions(options); - // determine from css whether this box has overflow - this.overflow = window.getComputedStyle(dom.content).overflow !== 'hidden'; + // other vars + this.freezeSimulation = false;// freeze the simulation + this.cachedFunctions = {}; + this.startedStabilization = false; + this.stabilized = false; + this.stabilizationIterations = null; + this.draggingNodes = false; - // recalculate size - // turn off max-width to be able to calculate the real width - // this causes an extra browser repaint/reflow, but so be it - this.dom.content.style.maxWidth = 'none'; - this.props.content.width = this.dom.content.offsetWidth; - this.height = this.dom.box.offsetHeight; - this.dom.content.style.maxWidth = ''; + // containers for nodes and edges + this.calculationNodes = {}; + this.calculationNodeIndices = []; + this.nodeIndices = []; // array with all the indices of the nodes. Used to speed up forces calculation + this.nodes = {}; // object with Node objects + this.edges = {}; // object with Edge objects - this.dirty = false; + // position and scale variables and objects + this.canvasTopLeft = {"x": 0,"y": 0}; // coordinates of the top left of the canvas. they will be set during _redraw. + this.canvasBottomRight = {"x": 0,"y": 0}; // coordinates of the bottom right of the canvas. they will be set during _redraw + this.pointerPosition = {"x": 0,"y": 0}; // coordinates of the bottom right of the canvas. they will be set during _redraw + this.areaCenter = {}; // object with x and y elements used for determining the center of the zoom action + this.scale = 1; // defining the global scale variable in the constructor + this.previousScale = this.scale; // this is used to check if the zoom operation is zooming in or out + + // datasets or dataviews + this.nodesData = null; // A DataSet or DataView + this.edgesData = null; // A DataSet or DataView + + // create event listeners used to subscribe on the DataSets of the nodes and edges + this.nodesListeners = { + 'add': function (event, params) { + network._addNodes(params.items); + network.start(); + }, + 'update': function (event, params) { + network._updateNodes(params.items, params.data); + network.start(); + }, + 'remove': function (event, params) { + network._removeNodes(params.items); + network.start(); + } + }; + this.edgesListeners = { + 'add': function (event, params) { + network._addEdges(params.items); + network.start(); + }, + 'update': function (event, params) { + network._updateEdges(params.items); + network.start(); + }, + 'remove': function (event, params) { + network._removeEdges(params.items); + network.start(); + } + }; + + // properties for the animation + this.moving = true; + this.timer = undefined; // Scheduling function. Is definded in this.start(); + + // load data (the disable start variable will be the same as the enabled clustering) + this.setData(data,this.constants.clustering.enabled || this.constants.hierarchicalLayout.enabled); + + // hierarchical layout + this.initializing = false; + if (this.constants.hierarchicalLayout.enabled == true) { + this._setupHierarchicalLayout(); + } + else { + // zoom so all data will fit on the screen, if clustering is enabled, we do not want start to be called here. + if (this.constants.stabilize == false) { + this.zoomExtent(undefined, true,this.constants.clustering.enabled); + } } - this._repaintDeleteButton(dom.box); - this._repaintDragLeft(); - this._repaintDragRight(); - }; + // if clustering is disabled, the simulation will have started in the setData function + if (this.constants.clustering.enabled) { + this.startWithClustering(); + } + } + + // Extend Network with an Emitter mixin + Emitter(Network.prototype); /** - * Show the item in the DOM (when not already visible). The items DOM will - * be created when needed. + * Determine if the browser requires a setTimeout or a requestAnimationFrame. This was required because + * some implementations (safari and IE9) did not support requestAnimationFrame + * @private */ - RangeItem.prototype.show = function() { - if (!this.displayed) { - this.redraw(); + Network.prototype._determineBrowserMethod = function() { + var browserType = navigator.userAgent.toLowerCase(); + this.requiresTimeout = false; + if (browserType.indexOf('msie 9.0') != -1) { // IE 9 + this.requiresTimeout = true; } - }; + else if (browserType.indexOf('safari') != -1) { // safari + if (browserType.indexOf('chrome') <= -1) { + this.requiresTimeout = true; + } + } + } + /** - * Hide the item from the DOM (when visible) - * @return {Boolean} changed + * Get the script path where the vis.js library is located + * + * @returns {string | null} path Path or null when not found. Path does not + * end with a slash. + * @private */ - RangeItem.prototype.hide = function() { - if (this.displayed) { - var box = this.dom.box; + Network.prototype._getScriptPath = function() { + var scripts = document.getElementsByTagName( 'script' ); - if (box.parentNode) { - box.parentNode.removeChild(box); + // find script named vis.js or vis.min.js + for (var i = 0; i < scripts.length; i++) { + var src = scripts[i].src; + var match = src && /\/?vis(.min)?\.js$/.exec(src); + if (match) { + // return path without the script name + return src.substring(0, src.length - match[0].length); } - - this.top = null; - this.left = null; - - this.displayed = false; } + + return null; }; + /** - * Reposition the item horizontally - * @Override + * Find the center position of the network + * @private */ - RangeItem.prototype.repositionX = function() { - var parentWidth = this.parent.width; - var start = this.conversion.toScreen(this.data.start); - var end = this.conversion.toScreen(this.data.end); - var contentLeft; - var contentWidth; - - // limit the width of the this, as browsers cannot draw very wide divs - if (start < -parentWidth) { - start = -parentWidth; + Network.prototype._getRange = function() { + var minY = 1e9, maxY = -1e9, minX = 1e9, maxX = -1e9, node; + for (var nodeId in this.nodes) { + if (this.nodes.hasOwnProperty(nodeId)) { + node = this.nodes[nodeId]; + if (minX > (node.boundingBox.left)) {minX = node.boundingBox.left;} + if (maxX < (node.boundingBox.right)) {maxX = node.boundingBox.right;} + if (minY > (node.boundingBox.bottom)) {minY = node.boundingBox.top;} // top is negative, bottom is positive + if (maxY < (node.boundingBox.top)) {maxY = node.boundingBox.bottom;} // top is negative, bottom is positive + } } - if (end > 2 * parentWidth) { - end = 2 * parentWidth; + if (minX == 1e9 && maxX == -1e9 && minY == 1e9 && maxY == -1e9) { + minY = 0, maxY = 0, minX = 0, maxX = 0; } - var boxWidth = Math.max(end - start, 1); + return {minX: minX, maxX: maxX, minY: minY, maxY: maxY}; + }; - if (this.overflow) { - this.left = start; - this.width = boxWidth + this.props.content.width; - contentWidth = this.props.content.width; - // Note: The calculation of width is an optimistic calculation, giving - // a width which will not change when moving the Timeline - // So no re-stacking needed, which is nicer for the eye; - } - else { - this.left = start; - this.width = boxWidth; - contentWidth = Math.min(end - start - 2 * this.options.padding, this.props.content.width); - } + /** + * @param {object} range = {minX: minX, maxX: maxX, minY: minY, maxY: maxY}; + * @returns {{x: number, y: number}} + * @private + */ + Network.prototype._findCenter = function(range) { + return {x: (0.5 * (range.maxX + range.minX)), + y: (0.5 * (range.maxY + range.minY))}; + }; - this.dom.box.style.left = this.left + 'px'; - this.dom.box.style.width = boxWidth + 'px'; - switch (this.options.align) { - case 'left': - this.dom.content.style.left = '0'; - break; + /** + * This function zooms out to fit all data on screen based on amount of nodes + * + * @param {Boolean} [initialZoom] | zoom based on fitted formula or range, true = fitted, default = false; + * @param {Boolean} [disableStart] | If true, start is not called. + */ + Network.prototype.zoomExtent = function(animationOptions, initialZoom, disableStart) { + this._redraw(true); - case 'right': - this.dom.content.style.left = Math.max((boxWidth - contentWidth - 2 * this.options.padding), 0) + 'px'; - break; + if (initialZoom === undefined) {initialZoom = false;} + if (disableStart === undefined) {disableStart = false;} + if (animationOptions === undefined) {animationOptions = false;} - case 'center': - this.dom.content.style.left = Math.max((boxWidth - contentWidth - 2 * this.options.padding) / 2, 0) + 'px'; - break; + var range = this._getRange(); + var zoomLevel; - default: // 'auto' - // when range exceeds left of the window, position the contents at the left of the visible area - if (this.overflow) { - if (end > 0) { - contentLeft = Math.max(-start, 0); - } - else { - contentLeft = -contentWidth; // ensure it's not visible anymore - } + if (initialZoom == true) { + var numberOfNodes = this.nodeIndices.length; + if (this.constants.smoothCurves == true) { + if (this.constants.clustering.enabled == true && + numberOfNodes >= this.constants.clustering.initialMaxNodes) { + zoomLevel = 49.07548 / (numberOfNodes + 142.05338) + 9.1444e-04; // this is obtained from fitting a dataset from 5 points with scale levels that looked good. } else { - if (start < 0) { - contentLeft = Math.min(-start, - (end - start - contentWidth - 2 * this.options.padding)); - // TODO: remove the need for options.padding. it's terrible. - } - else { - contentLeft = 0; - } + zoomLevel = 12.662 / (numberOfNodes + 7.4147) + 0.0964822; // this is obtained from fitting a dataset from 5 points with scale levels that looked good. } - this.dom.content.style.left = contentLeft + 'px'; - } - }; - - /** - * Reposition the item vertically - * @Override - */ - RangeItem.prototype.repositionY = function() { - var orientation = this.options.orientation, - box = this.dom.box; + } + else { + if (this.constants.clustering.enabled == true && + numberOfNodes >= this.constants.clustering.initialMaxNodes) { + zoomLevel = 77.5271985 / (numberOfNodes + 187.266146) + 4.76710517e-05; // this is obtained from fitting a dataset from 5 points with scale levels that looked good. + } + else { + zoomLevel = 30.5062972 / (numberOfNodes + 19.93597763) + 0.08413486; // this is obtained from fitting a dataset from 5 points with scale levels that looked good. + } + } - if (orientation == 'top') { - box.style.top = this.top + 'px'; + // correct for larger canvasses. + var factor = Math.min(this.frame.canvas.clientWidth / 600, this.frame.canvas.clientHeight / 600); + zoomLevel *= factor; } else { - box.style.top = (this.parent.height - this.top - this.height) + 'px'; + var xDistance = Math.abs(range.maxX - range.minX) * 1.1; + var yDistance = Math.abs(range.maxY - range.minY) * 1.1; + + var xZoomLevel = this.frame.canvas.clientWidth / xDistance; + var yZoomLevel = this.frame.canvas.clientHeight / yDistance; + zoomLevel = (xZoomLevel <= yZoomLevel) ? xZoomLevel : yZoomLevel; } - }; - /** - * Repaint a drag area on the left side of the range when the range is selected - * @protected - */ - RangeItem.prototype._repaintDragLeft = function () { - if (this.selected && this.options.editable.updateTime && !this.dom.dragLeft) { - // create and show drag area - var dragLeft = document.createElement('div'); - dragLeft.className = 'drag-left'; - dragLeft.dragLeftItem = this; + if (zoomLevel > 1.0) { + zoomLevel = 1.0; + } - // TODO: this should be redundant? - Hammer(dragLeft, { - preventDefault: true - }).on('drag', function () { - //console.log('drag left') - }); - this.dom.box.appendChild(dragLeft); - this.dom.dragLeft = dragLeft; + var center = this._findCenter(range); + if (disableStart == false) { + var options = {position: center, scale: zoomLevel, animation: animationOptions}; + this.moveTo(options); + this.moving = true; + this.start(); } - else if (!this.selected && this.dom.dragLeft) { - // delete drag area - if (this.dom.dragLeft.parentNode) { - this.dom.dragLeft.parentNode.removeChild(this.dom.dragLeft); - } - this.dom.dragLeft = null; + else { + center.x *= zoomLevel; + center.y *= zoomLevel; + center.x -= 0.5 * this.frame.canvas.clientWidth; + center.y -= 0.5 * this.frame.canvas.clientHeight; + this._setScale(zoomLevel); + this._setTranslation(-center.x,-center.y); } }; + /** - * Repaint a drag area on the right side of the range when the range is selected - * @protected + * Update the this.nodeIndices with the most recent node index list + * @private */ - RangeItem.prototype._repaintDragRight = function () { - if (this.selected && this.options.editable.updateTime && !this.dom.dragRight) { - // create and show drag area - var dragRight = document.createElement('div'); - dragRight.className = 'drag-right'; - dragRight.dragRightItem = this; - - // TODO: this should be redundant? - Hammer(dragRight, { - preventDefault: true - }).on('drag', function () { - //console.log('drag right') - }); - - this.dom.box.appendChild(dragRight); - this.dom.dragRight = dragRight; - } - else if (!this.selected && this.dom.dragRight) { - // delete drag area - if (this.dom.dragRight.parentNode) { - this.dom.dragRight.parentNode.removeChild(this.dom.dragRight); + Network.prototype._updateNodeIndexList = function() { + this._clearNodeIndexList(); + for (var idx in this.nodes) { + if (this.nodes.hasOwnProperty(idx)) { + this.nodeIndices.push(idx); } - this.dom.dragRight = null; } }; - module.exports = RangeItem; - - -/***/ }, -/* 30 */ -/***/ function(module, exports, __webpack_require__) { - - var Hammer = __webpack_require__(19); - var util = __webpack_require__(1); - - /** - * @constructor Item - * @param {Object} data Object containing (optional) parameters type, - * start, end, content, group, className. - * @param {{toScreen: function, toTime: function}} conversion - * Conversion functions from time to screen and vice versa - * @param {Object} options Configuration options - * // TODO: describe available options - */ - function Item (data, conversion, options) { - this.id = null; - this.parent = null; - this.data = data; - this.dom = null; - this.conversion = conversion || {}; - this.options = options || {}; - - this.selected = false; - this.displayed = false; - this.dirty = true; - - this.top = null; - this.left = null; - this.width = null; - this.height = null; - } - - Item.prototype.stack = true; /** - * Select current item + * Set nodes and edges, and optionally options as well. + * + * @param {Object} data Object containing parameters: + * {Array | DataSet | DataView} [nodes] Array with nodes + * {Array | DataSet | DataView} [edges] Array with edges + * {String} [dot] String containing data in DOT format + * {String} [gephi] String containing data in gephi JSON format + * {Options} [options] Object with options + * @param {Boolean} [disableStart] | optional: disable the calling of the start function. */ - Item.prototype.select = function() { - this.selected = true; - this.dirty = true; - if (this.displayed) this.redraw(); - }; + Network.prototype.setData = function(data, disableStart) { + if (disableStart === undefined) { + disableStart = false; + } + // we set initializing to true to ensure that the hierarchical layout is not performed until both nodes and edges are added. + this.initializing = true; - /** - * Unselect current item - */ - Item.prototype.unselect = function() { - this.selected = false; - this.dirty = true; - if (this.displayed) this.redraw(); - }; + if (data && data.dot && (data.nodes || data.edges)) { + throw new SyntaxError('Data must contain either parameter "dot" or ' + + ' parameter pair "nodes" and "edges", but not both.'); + } - /** - * Set data for the item. Existing data will be updated. The id should not - * be changed. When the item is displayed, it will be redrawn immediately. - * @param {Object} data - */ - Item.prototype.setData = function(data) { - this.data = data; - this.dirty = true; - if (this.displayed) this.redraw(); - }; + // clean up in case there is anyone in an active mode of the manipulation. This is the same option as bound to the escape button. + if (this.constants.dataManipulation.enabled == true) { + this._createManipulatorBar(); + } - /** - * Set a parent for the item - * @param {ItemSet | Group} parent - */ - Item.prototype.setParent = function(parent) { - if (this.displayed) { - this.hide(); - this.parent = parent; - if (this.parent) { - this.show(); + // set options + this.setOptions(data && data.options); + // set all data + if (data && data.dot) { + // parse DOT file + if(data && data.dot) { + var dotData = dotparser.DOTToGraph(data.dot); + this.setData(dotData); + return; + } + } + else if (data && data.gephi) { + // parse DOT file + if(data && data.gephi) { + var gephiData = gephiParser.parseGephi(data.gephi); + this.setData(gephiData); + return; } } else { - this.parent = parent; + this._setNodes(data && data.nodes); + this._setEdges(data && data.edges); + } + this._putDataInSector(); + if (disableStart == false) { + if (this.constants.hierarchicalLayout.enabled == true) { + this._resetLevels(); + this._setupHierarchicalLayout(); + } + else { + // find a stable position or start animating to a stable position + if (this.constants.stabilize) { + this._stabilize(); + } + } + this.start(); } + this.initializing = false; }; /** - * Check whether this item is visible inside given range - * @returns {{start: Number, end: Number}} range with a timestamp for start and end - * @returns {boolean} True if visible + * Set options + * @param {Object} options */ - Item.prototype.isVisible = function(range) { - // Should be implemented by Item implementations - return false; - }; + Network.prototype.setOptions = function (options) { + if (options) { + var prop; + var fields = ['nodes','edges','smoothCurves','hierarchicalLayout','clustering','navigation', + 'keyboard','dataManipulation','onAdd','onEdit','onEditEdge','onConnect','onDelete','clickToUse' + ]; + // extend all but the values in fields + util.selectiveNotDeepExtend(fields,this.constants, options); + util.selectiveNotDeepExtend(['color'],this.constants.nodes, options.nodes); + util.selectiveNotDeepExtend(['color','length'],this.constants.edges, options.edges); - /** - * Show the Item in the DOM (when not already visible) - * @return {Boolean} changed - */ - Item.prototype.show = function() { - return false; - }; + if (options.physics) { + util.mergeOptions(this.constants.physics, options.physics,'barnesHut'); + util.mergeOptions(this.constants.physics, options.physics,'repulsion'); - /** - * Hide the Item from the DOM (when visible) - * @return {Boolean} changed - */ - Item.prototype.hide = function() { - return false; - }; + if (options.physics.hierarchicalRepulsion) { + this.constants.hierarchicalLayout.enabled = true; + this.constants.physics.hierarchicalRepulsion.enabled = true; + this.constants.physics.barnesHut.enabled = false; + for (prop in options.physics.hierarchicalRepulsion) { + if (options.physics.hierarchicalRepulsion.hasOwnProperty(prop)) { + this.constants.physics.hierarchicalRepulsion[prop] = options.physics.hierarchicalRepulsion[prop]; + } + } + } + } - /** - * Repaint the item - */ - Item.prototype.redraw = function() { - // should be implemented by the item - }; + if (options.onAdd) {this.triggerFunctions.add = options.onAdd;} + if (options.onEdit) {this.triggerFunctions.edit = options.onEdit;} + if (options.onEditEdge) {this.triggerFunctions.editEdge = options.onEditEdge;} + if (options.onConnect) {this.triggerFunctions.connect = options.onConnect;} + if (options.onDelete) {this.triggerFunctions.del = options.onDelete;} - /** - * Reposition the Item horizontally - */ - Item.prototype.repositionX = function() { - // should be implemented by the item - }; + util.mergeOptions(this.constants, options,'smoothCurves'); + util.mergeOptions(this.constants, options,'hierarchicalLayout'); + util.mergeOptions(this.constants, options,'clustering'); + util.mergeOptions(this.constants, options,'navigation'); + util.mergeOptions(this.constants, options,'keyboard'); + util.mergeOptions(this.constants, options,'dataManipulation'); - /** - * Reposition the Item vertically - */ - Item.prototype.repositionY = function() { - // should be implemented by the item - }; - /** - * Repaint a delete button on the top right of the item when the item is selected - * @param {HTMLElement} anchor - * @protected - */ - Item.prototype._repaintDeleteButton = function (anchor) { - if (this.selected && this.options.editable.remove && !this.dom.deleteButton) { - // create and show button - var me = this; + if (options.dataManipulation) { + this.editMode = this.constants.dataManipulation.initiallyVisible; + } - var deleteButton = document.createElement('div'); - deleteButton.className = 'delete'; - deleteButton.title = 'Delete this item'; - Hammer(deleteButton, { - preventDefault: true - }).on('tap', function (event) { - me.parent.removeFromDataSet(me); - event.stopPropagation(); - }); + // TODO: work out these options and document them + if (options.edges) { + if (options.edges.color !== undefined) { + if (util.isString(options.edges.color)) { + this.constants.edges.color = {}; + this.constants.edges.color.color = options.edges.color; + this.constants.edges.color.highlight = options.edges.color; + this.constants.edges.color.hover = options.edges.color; + } + else { + if (options.edges.color.color !== undefined) {this.constants.edges.color.color = options.edges.color.color;} + if (options.edges.color.highlight !== undefined) {this.constants.edges.color.highlight = options.edges.color.highlight;} + if (options.edges.color.hover !== undefined) {this.constants.edges.color.hover = options.edges.color.hover;} + } + this.constants.edges.inheritColor = false; + } - anchor.appendChild(deleteButton); - this.dom.deleteButton = deleteButton; - } - else if (!this.selected && this.dom.deleteButton) { - // remove button - if (this.dom.deleteButton.parentNode) { - this.dom.deleteButton.parentNode.removeChild(this.dom.deleteButton); + if (!options.edges.fontColor) { + if (options.edges.color !== undefined) { + if (util.isString(options.edges.color)) {this.constants.edges.fontColor = options.edges.color;} + else if (options.edges.color.color !== undefined) {this.constants.edges.fontColor = options.edges.color.color;} + } + } } - this.dom.deleteButton = null; - } - }; - - /** - * Set HTML contents for the item - * @param {Element} element HTML element to fill with the contents - * @private - */ - Item.prototype._updateContents = function (element) { - var content; - if (this.options.template) { - var itemData = this.parent.itemSet.itemsData.get(this.id); // get a clone of the data from the dataset - content = this.options.template(itemData); - } - else { - content = this.data.content; - } - if(content !== this.content) { - // only replace the content when changed - if (content instanceof Element) { - element.innerHTML = ''; - element.appendChild(content); + if (options.nodes) { + if (options.nodes.color) { + var newColorObj = util.parseColor(options.nodes.color); + this.constants.nodes.color.background = newColorObj.background; + this.constants.nodes.color.border = newColorObj.border; + this.constants.nodes.color.highlight.background = newColorObj.highlight.background; + this.constants.nodes.color.highlight.border = newColorObj.highlight.border; + this.constants.nodes.color.hover.background = newColorObj.hover.background; + this.constants.nodes.color.hover.border = newColorObj.hover.border; + } } - else if (content != undefined) { - element.innerHTML = content; + if (options.groups) { + for (var groupname in options.groups) { + if (options.groups.hasOwnProperty(groupname)) { + var group = options.groups[groupname]; + this.groups.add(groupname, group); + } + } } - else { - if (!(this.data.type == 'background' && this.data.content === undefined)) { - throw new Error('Property "content" missing in item ' + this.id); + + if (options.tooltip) { + for (prop in options.tooltip) { + if (options.tooltip.hasOwnProperty(prop)) { + this.constants.tooltip[prop] = options.tooltip[prop]; + } + } + if (options.tooltip.color) { + this.constants.tooltip.color = util.parseColor(options.tooltip.color); } } - this.content = content; + if ('clickToUse' in options) { + if (options.clickToUse) { + if (!this.activator) { + this.activator = new Activator(this.frame); + this.activator.on('change', this._createKeyBinds.bind(this)); + } + } + else { + if (this.activator) { + this.activator.destroy(); + delete this.activator; + } + } + } + + if (options.labels) { + throw new Error('Option "labels" is deprecated. Use options "locale" and "locales" instead.'); + } + + + // (Re)loading the mixins that can be enabled or disabled in the options. + // load the force calculation functions, grouped under the physics system. + this._loadPhysicsSystem(); + // load the navigation system. + this._loadNavigationControls(); + // load the data manipulation system + this._loadManipulationSystem(); + // configure the smooth curves + this._configureSmoothCurves(); + + // bind hammer + this._bindHammer(); + + // bind keys. If disabled, this will not do anything; + this._createKeyBinds(); + + + this.setSize(this.constants.width, this.constants.height); + this.moving = true; + this.start(); } }; + + /** - * Set HTML contents for the item - * @param {Element} element HTML element to fill with the contents + * Create the main frame for the Network. + * This function is executed once when a Network object is created. The frame + * contains a canvas, and this canvas contains all objects like the axis and + * nodes. * @private */ - Item.prototype._updateTitle = function (element) { - if (this.data.title != null) { - element.title = this.data.title || ''; + Network.prototype._create = function () { + // remove all elements from the container element. + while (this.containerElement.hasChildNodes()) { + this.containerElement.removeChild(this.containerElement.firstChild); + } + + this.frame = document.createElement('div'); + this.frame.className = 'vis network-frame'; + this.frame.style.position = 'relative'; + this.frame.style.overflow = 'hidden'; + this.frame.tabIndex = 900; + + + ////////////////////////////////////////////////////////////////// + + this.frame.canvas = document.createElement("canvas"); + this.frame.canvas.style.position = 'relative'; + this.frame.appendChild(this.frame.canvas); + + if (!this.frame.canvas.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.frame.canvas.appendChild(noCanvas); } else { - element.removeAttribute('title'); + var ctx = this.frame.canvas.getContext("2d"); + this.pixelRatio = (window.devicePixelRatio || 1) / (ctx.webkitBackingStorePixelRatio || + ctx.mozBackingStorePixelRatio || + ctx.msBackingStorePixelRatio || + ctx.oBackingStorePixelRatio || + ctx.backingStorePixelRatio || 1); + + this.frame.canvas.getContext("2d").setTransform(this.pixelRatio, 0, 0, this.pixelRatio, 0, 0); } + + this._bindHammer(); }; + /** - * Process dataAttributes timeline option and set as data- attributes on dom.content - * @param {Element} element HTML element to which the attributes will be attached + * This function binds hammer, it can be repeated over and over due to the uniqueness check. * @private */ - Item.prototype._updateDataAttributes = function(element) { - if (this.options.dataAttributes && this.options.dataAttributes.length > 0) { - var attributes = []; - - if (Array.isArray(this.options.dataAttributes)) { - attributes = this.options.dataAttributes; - } - else if (this.options.dataAttributes == 'all') { - attributes = Object.keys(this.data); - } - else { - return; - } - - for (var i = 0; i < attributes.length; i++) { - var name = attributes[i]; - var value = this.data[name]; + Network.prototype._bindHammer = function() { + var me = this; + if (this.hammer !== undefined) { + this.hammer.dispose(); + } + this.drag = {}; + this.pinch = {}; + this.hammer = Hammer(this.frame.canvas, { + prevent_default: true + }); + this.hammer.on('tap', me._onTap.bind(me) ); + this.hammer.on('doubletap', me._onDoubleTap.bind(me) ); + this.hammer.on('hold', me._onHold.bind(me) ); + this.hammer.on('touch', me._onTouch.bind(me) ); + this.hammer.on('dragstart', me._onDragStart.bind(me) ); + this.hammer.on('drag', me._onDrag.bind(me) ); + this.hammer.on('dragend', me._onDragEnd.bind(me) ); - if (value != null) { - element.setAttribute('data-' + name, value); - } - else { - element.removeAttribute('data-' + name); - } - } + if (this.constants.zoomable == true) { + this.hammer.on('mousewheel', me._onMouseWheel.bind(me)); + this.hammer.on('DOMMouseScroll', me._onMouseWheel.bind(me)); // for FF + this.hammer.on('pinch', me._onPinch.bind(me) ); } - }; + + this.hammer.on('mousemove', me._onMouseMoveTitle.bind(me) ); + + this.hammerFrame = Hammer(this.frame, { + prevent_default: true + }); + this.hammerFrame.on('release', me._onRelease.bind(me) ); + + // add the frame to the container element + this.containerElement.appendChild(this.frame); + } /** - * Update custom styles of the element - * @param element + * Binding the keys for keyboard navigation. These functions are defined in the NavigationMixin * @private */ - Item.prototype._updateStyle = function(element) { - // remove old styles - if (this.style) { - util.removeCssText(element, this.style); - this.style = null; + Network.prototype._createKeyBinds = function() { + var me = this; + if (this.keycharm !== undefined) { + this.keycharm.destroy(); } - // append new styles - if (this.data.style) { - util.addCssText(element, this.data.style); - this.style = this.data.style; + if (this.constants.keyboard.bindToWindow == true) { + this.keycharm = keycharm({container: window, preventDefault: false}); + } + else { + this.keycharm = keycharm({container: this.frame, preventDefault: false}); } - }; - - module.exports = Item; + this.keycharm.reset(); -/***/ }, -/* 31 */ -/***/ function(module, exports, __webpack_require__) { + if (this.constants.keyboard.enabled && this.isActive()) { + this.keycharm.bind("up", this._moveUp.bind(me) , "keydown"); + this.keycharm.bind("up", this._yStopMoving.bind(me), "keyup"); + this.keycharm.bind("down", this._moveDown.bind(me) , "keydown"); + this.keycharm.bind("down", this._yStopMoving.bind(me), "keyup"); + this.keycharm.bind("left", this._moveLeft.bind(me) , "keydown"); + this.keycharm.bind("left", this._xStopMoving.bind(me), "keyup"); + this.keycharm.bind("right",this._moveRight.bind(me), "keydown"); + this.keycharm.bind("right",this._xStopMoving.bind(me), "keyup"); + this.keycharm.bind("=", this._zoomIn.bind(me), "keydown"); + this.keycharm.bind("=", this._stopZoom.bind(me), "keyup"); + this.keycharm.bind("num+", this._zoomIn.bind(me), "keydown"); + this.keycharm.bind("num+", this._stopZoom.bind(me), "keyup"); + this.keycharm.bind("num-", this._zoomOut.bind(me), "keydown"); + this.keycharm.bind("num-", this._stopZoom.bind(me), "keyup"); + this.keycharm.bind("-", this._zoomOut.bind(me), "keydown"); + this.keycharm.bind("-", this._stopZoom.bind(me), "keyup"); + this.keycharm.bind("[", this._zoomIn.bind(me), "keydown"); + this.keycharm.bind("[", this._stopZoom.bind(me), "keyup"); + this.keycharm.bind("]", this._zoomOut.bind(me), "keydown"); + this.keycharm.bind("]", this._stopZoom.bind(me), "keyup"); + this.keycharm.bind("pageup",this._zoomIn.bind(me), "keydown"); + this.keycharm.bind("pageup",this._stopZoom.bind(me), "keyup"); + this.keycharm.bind("pagedown",this._zoomOut.bind(me),"keydown"); + this.keycharm.bind("pagedown",this._stopZoom.bind(me), "keyup"); + } - var util = __webpack_require__(1); - var Group = __webpack_require__(27); + if (this.constants.dataManipulation.enabled == true) { + this.keycharm.bind("esc",this._createManipulatorBar.bind(me)); + this.keycharm.bind("delete",this._deleteSelected.bind(me)); + } + }; /** - * @constructor BackgroundGroup - * @param {Number | String} groupId - * @param {Object} data - * @param {ItemSet} itemSet + * Cleans up all bindings of the network, removing it fully from the memory IF the variable is set to null after calling this function. + * var network = new vis.Network(..); + * network.destroy(); + * network = null; */ - function BackgroundGroup (groupId, data, itemSet) { - Group.call(this, groupId, data, itemSet); + Network.prototype.destroy = function() { + this.start = function () {}; + this.redraw = function () {}; + this.timer = false; - this.width = 0; - this.height = 0; - this.top = 0; - this.left = 0; + // cleanup physicsConfiguration if it exists + this._cleanupPhysicsConfiguration(); + + // remove keybindings + this.keycharm.reset(); + + // clear hammer bindings + this.hammer.dispose(); + + // clear events + this.off(); + + this._recursiveDOMDelete(this.containerElement); } - BackgroundGroup.prototype = Object.create(Group.prototype); + Network.prototype._recursiveDOMDelete = function(DOMobject) { + while (DOMobject.hasChildNodes() == true) { + this._recursiveDOMDelete(DOMobject.firstChild); + DOMobject.removeChild(DOMobject.firstChild); + } + } /** - * Repaint this group - * @param {{start: number, end: number}} range - * @param {{item: {horizontal: number, vertical: number}, axis: number}} margin - * @param {boolean} [restack=false] Force restacking of all items - * @return {boolean} Returns true if the group is resized + * Get the pointer location from a touch location + * @param {{pageX: Number, pageY: Number}} touch + * @return {{x: Number, y: Number}} pointer + * @private */ - BackgroundGroup.prototype.redraw = function(range, margin, restack) { - var resized = false; - - this.visibleItems = this._updateVisibleItems(this.orderedItems, this.visibleItems, range); + Network.prototype._getPointer = function (touch) { + return { + x: touch.pageX - util.getAbsoluteLeft(this.frame.canvas), + y: touch.pageY - util.getAbsoluteTop(this.frame.canvas) + }; + }; - // calculate actual size - this.width = this.dom.background.offsetWidth; + /** + * On start of a touch gesture, store the pointer + * @param event + * @private + */ + Network.prototype._onTouch = function (event) { + if (new Date().valueOf() - this.touchTime > 100) { + this.drag.pointer = this._getPointer(event.gesture.center); + this.drag.pinched = false; + this.pinch.scale = this._getScale(); - // apply new height (just always zero for BackgroundGroup - this.dom.background.style.height = '0'; + // to avoid double fireing of this event because we have two hammer instances. (on canvas and on frame) + this.touchTime = new Date().valueOf(); - // update vertical position of items after they are re-stacked and the height of the group is calculated - for (var i = 0, ii = this.visibleItems.length; i < ii; i++) { - var item = this.visibleItems[i]; - item.repositionY(margin); + this._handleTouch(this.drag.pointer); } - - return resized; }; /** - * Show this group: attach to the DOM + * handle drag start event + * @private */ - BackgroundGroup.prototype.show = function() { - if (!this.dom.background.parentNode) { - this.itemSet.dom.background.appendChild(this.dom.background); - } + Network.prototype._onDragStart = function (event) { + this._handleDragStart(event); }; - module.exports = BackgroundGroup; + /** + * This function is called by _onDragStart. + * It is separated out because we can then overload it for the datamanipulation system. + * + * @private + */ + Network.prototype._handleDragStart = function(event) { + // in case the touch event was triggered on an external div, do the initial touch now. + if (this.drag.pointer === undefined) { + this._onTouch(event); + } -/***/ }, -/* 32 */ -/***/ function(module, exports, __webpack_require__) { + var node = this._getNodeAt(this.drag.pointer); + // note: drag.pointer is set in _onTouch to get the initial touch location - var Item = __webpack_require__(30); - var util = __webpack_require__(1); + this.drag.dragging = true; + this.drag.selection = []; + this.drag.translation = this._getTranslation(); + this.drag.nodeId = null; + this.draggingNodes = false; - /** - * @constructor BoxItem - * @extends Item - * @param {Object} data Object containing parameters start - * content, className. - * @param {{toScreen: function, toTime: function}} conversion - * Conversion functions from time to screen and vice versa - * @param {Object} [options] Configuration options - * // TODO: describe available options - */ - function BoxItem (data, conversion, options) { - this.props = { - dot: { - width: 0, - height: 0 - }, - line: { - width: 0, - height: 0 + if (node != null && this.constants.dragNodes == true) { + this.draggingNodes = true; + this.drag.nodeId = node.id; + // select the clicked node if not yet selected + if (!node.isSelected()) { + this._selectObject(node,false); } - }; - // validate data - if (data) { - if (data.start == undefined) { - throw new Error('Property "start" missing in item ' + data); + this.emit("dragStart",{nodeIds:this.getSelection().nodes}); + + // create an array with the selected nodes and their original location and status + for (var objectId in this.selectionObj.nodes) { + if (this.selectionObj.nodes.hasOwnProperty(objectId)) { + var object = this.selectionObj.nodes[objectId]; + var s = { + id: object.id, + node: object, + + // store original x, y, xFixed and yFixed, make the node temporarily Fixed + x: object.x, + y: object.y, + xFixed: object.xFixed, + yFixed: object.yFixed + }; + + object.xFixed = true; + object.yFixed = true; + + this.drag.selection.push(s); + } } } + }; - Item.call(this, data, conversion, options); - } - - BoxItem.prototype = new Item (null, null, null); /** - * Check whether this item is visible inside given range - * @returns {{start: Number, end: Number}} range with a timestamp for start and end - * @returns {boolean} True if visible + * handle drag event + * @private */ - BoxItem.prototype.isVisible = function(range) { - // determine visibility - // TODO: account for the real width of the item. Right now we just add 1/4 to the window - var interval = (range.end - range.start) / 4; - return (this.data.start > range.start - interval) && (this.data.start < range.end + interval); + Network.prototype._onDrag = function (event) { + this._handleOnDrag(event) }; + /** - * Repaint the item + * This function is called by _onDrag. + * It is separated out because we can then overload it for the datamanipulation system. + * + * @private */ - BoxItem.prototype.redraw = function() { - var dom = this.dom; - if (!dom) { - // create DOM - this.dom = {}; - dom = this.dom; + Network.prototype._handleOnDrag = function(event) { + if (this.drag.pinched) { + return; + } - // create main box - dom.box = document.createElement('DIV'); + // remove the focus on node if it is focussed on by the focusOnNode + this.releaseNode(); - // contents box (inside the background box). used for making margins - dom.content = document.createElement('DIV'); - dom.content.className = 'content'; - dom.box.appendChild(dom.content); + var pointer = this._getPointer(event.gesture.center); + var me = this; + var drag = this.drag; + var selection = drag.selection; + if (selection && selection.length && this.constants.dragNodes == true) { + // calculate delta's and new location + var deltaX = pointer.x - drag.pointer.x; + var deltaY = pointer.y - drag.pointer.y; - // line to axis - dom.line = document.createElement('DIV'); - dom.line.className = 'line'; + // update position of all selected nodes + selection.forEach(function (s) { + var node = s.node; - // dot on axis - dom.dot = document.createElement('DIV'); - dom.dot.className = 'dot'; + if (!s.xFixed) { + node.x = me._XconvertDOMtoCanvas(me._XconvertCanvasToDOM(s.x) + deltaX); + } - // attach this item as attribute - dom.box['timeline-item'] = this; + if (!s.yFixed) { + node.y = me._YconvertDOMtoCanvas(me._YconvertCanvasToDOM(s.y) + deltaY); + } + }); - this.dirty = true; + + // start _animationStep if not yet running + if (!this.moving) { + this.moving = true; + this.start(); + } } + else { + // move the network + if (this.constants.dragNetwork == true) { + // if the drag was not started properly because the click started outside the network div, start it now. + if (this.drag.pointer === undefined) { + this._handleDragStart(event); + return; + } + var diffX = pointer.x - this.drag.pointer.x; + var diffY = pointer.y - this.drag.pointer.y; - // append DOM to parent DOM - if (!this.parent) { - throw new Error('Cannot redraw item: no parent attached'); + this._setTranslation( + this.drag.translation.x + diffX, + this.drag.translation.y + diffY + ); + this._redraw(); + } } - if (!dom.box.parentNode) { - var foreground = this.parent.dom.foreground; - if (!foreground) throw new Error('Cannot redraw item: parent has no foreground container element'); - foreground.appendChild(dom.box); + }; + + /** + * handle drag start event + * @private + */ + Network.prototype._onDragEnd = function (event) { + this._handleDragEnd(event); + }; + + + Network.prototype._handleDragEnd = function(event) { + this.drag.dragging = false; + var selection = this.drag.selection; + if (selection && selection.length) { + selection.forEach(function (s) { + // restore original xFixed and yFixed + s.node.xFixed = s.xFixed; + s.node.yFixed = s.yFixed; + }); + this.moving = true; + this.start(); } - if (!dom.line.parentNode) { - var background = this.parent.dom.background; - if (!background) throw new Error('Cannot redraw item: parent has no background container element'); - background.appendChild(dom.line); + else { + this._redraw(); } - if (!dom.dot.parentNode) { - var axis = this.parent.dom.axis; - if (!background) throw new Error('Cannot redraw item: parent has no axis container element'); - axis.appendChild(dom.dot); + if (this.draggingNodes == false) { + this.emit("dragEnd",{nodeIds:[]}); + } + else { + this.emit("dragEnd",{nodeIds:this.getSelection().nodes}); } - this.displayed = true; - - // Update DOM when item is marked dirty. An item is marked dirty when: - // - the item is not yet rendered - // - the item's data is changed - // - the item is selected/deselected - if (this.dirty) { - this._updateContents(this.dom.content); - this._updateTitle(this.dom.box); - this._updateDataAttributes(this.dom.box); - this._updateStyle(this.dom.box); - // update class - var className = (this.data.className? ' ' + this.data.className : '') + - (this.selected ? ' selected' : ''); - dom.box.className = 'item box' + className; - dom.line.className = 'item line' + className; - dom.dot.className = 'item dot' + className; + } + /** + * handle tap/click event: select/unselect a node + * @private + */ + Network.prototype._onTap = function (event) { + var pointer = this._getPointer(event.gesture.center); + this.pointerPosition = pointer; + this._handleTap(pointer); - // recalculate size - this.props.dot.height = dom.dot.offsetHeight; - this.props.dot.width = dom.dot.offsetWidth; - this.props.line.width = dom.line.offsetWidth; - this.width = dom.box.offsetWidth; - this.height = dom.box.offsetHeight; + }; - this.dirty = false; - } - this._repaintDeleteButton(dom.box); + /** + * handle doubletap event + * @private + */ + Network.prototype._onDoubleTap = function (event) { + var pointer = this._getPointer(event.gesture.center); + this._handleDoubleTap(pointer); }; + /** - * Show the item in the DOM (when not already displayed). The items DOM will - * be created when needed. + * handle long tap event: multi select nodes + * @private */ - BoxItem.prototype.show = function() { - if (!this.displayed) { - this.redraw(); - } + Network.prototype._onHold = function (event) { + var pointer = this._getPointer(event.gesture.center); + this.pointerPosition = pointer; + this._handleOnHold(pointer); }; /** - * Hide the item from the DOM (when visible) + * handle the release of the screen + * + * @private */ - BoxItem.prototype.hide = function() { - if (this.displayed) { - var dom = this.dom; - - if (dom.box.parentNode) dom.box.parentNode.removeChild(dom.box); - if (dom.line.parentNode) dom.line.parentNode.removeChild(dom.line); - if (dom.dot.parentNode) dom.dot.parentNode.removeChild(dom.dot); - - this.top = null; - this.left = null; - - this.displayed = false; - } + Network.prototype._onRelease = function (event) { + var pointer = this._getPointer(event.gesture.center); + this._handleOnRelease(pointer); }; /** - * Reposition the item horizontally - * @Override + * Handle pinch event + * @param event + * @private */ - BoxItem.prototype.repositionX = function() { - var start = this.conversion.toScreen(this.data.start); - var align = this.options.align; - var left; - var box = this.dom.box; - var line = this.dom.line; - var dot = this.dom.dot; + Network.prototype._onPinch = function (event) { + var pointer = this._getPointer(event.gesture.center); - // calculate left position of the box - if (align == 'right') { - this.left = start - this.width; - } - else if (align == 'left') { - this.left = start; - } - else { - // default or 'center' - this.left = start - this.width / 2; + this.drag.pinched = true; + if (!('scale' in this.pinch)) { + this.pinch.scale = 1; } - // reposition box - box.style.left = this.left + 'px'; - - // reposition line - line.style.left = (start - this.props.line.width / 2) + 'px'; - - // reposition dot - dot.style.left = (start - this.props.dot.width / 2) + 'px'; + // TODO: enabled moving while pinching? + var scale = this.pinch.scale * event.gesture.scale; + this._zoom(scale, pointer) }; /** - * Reposition the item vertically - * @Override + * Zoom the network in or out + * @param {Number} scale a number around 1, and between 0.01 and 10 + * @param {{x: Number, y: Number}} pointer Position on screen + * @return {Number} appliedScale scale is limited within the boundaries + * @private */ - BoxItem.prototype.repositionY = function() { - var orientation = this.options.orientation; - var box = this.dom.box; - var line = this.dom.line; - var dot = this.dom.dot; + Network.prototype._zoom = function(scale, pointer) { + if (this.constants.zoomable == true) { + var scaleOld = this._getScale(); + if (scale < 0.00001) { + scale = 0.00001; + } + if (scale > 10) { + scale = 10; + } - if (orientation == 'top') { - box.style.top = (this.top || 0) + 'px'; + var preScaleDragPointer = null; + if (this.drag !== undefined) { + if (this.drag.dragging == true) { + preScaleDragPointer = this.DOMtoCanvas(this.drag.pointer); + } + } + // + this.frame.canvas.clientHeight / 2 + var translation = this._getTranslation(); - line.style.top = '0'; - line.style.height = (this.parent.top + this.top + 1) + 'px'; - line.style.bottom = ''; - } - else { // orientation 'bottom' - var itemSetHeight = this.parent.itemSet.props.height; // TODO: this is nasty - var lineHeight = itemSetHeight - this.parent.top - this.parent.height + this.top; + var scaleFrac = scale / scaleOld; + var tx = (1 - scaleFrac) * pointer.x + translation.x * scaleFrac; + var ty = (1 - scaleFrac) * pointer.y + translation.y * scaleFrac; - box.style.top = (this.parent.height - this.top - this.height || 0) + 'px'; - line.style.top = (itemSetHeight - lineHeight) + 'px'; - line.style.bottom = '0'; - } + this.areaCenter = {"x" : this._XconvertDOMtoCanvas(pointer.x), + "y" : this._YconvertDOMtoCanvas(pointer.y)}; - dot.style.top = (-this.props.dot.height / 2) + 'px'; - }; + this._setScale(scale); + this._setTranslation(tx, ty); + this.updateClustersDefault(); - module.exports = BoxItem; + if (preScaleDragPointer != null) { + var postScaleDragPointer = this.canvasToDOM(preScaleDragPointer); + this.drag.pointer.x = postScaleDragPointer.x; + this.drag.pointer.y = postScaleDragPointer.y; + } + this._redraw(); -/***/ }, -/* 33 */ -/***/ function(module, exports, __webpack_require__) { + if (scaleOld < scale) { + this.emit("zoom", {direction:"+"}); + } + else { + this.emit("zoom", {direction:"-"}); + } + + return scale; + } + }; - var Item = __webpack_require__(30); /** - * @constructor PointItem - * @extends Item - * @param {Object} data Object containing parameters start - * content, className. - * @param {{toScreen: function, toTime: function}} conversion - * Conversion functions from time to screen and vice versa - * @param {Object} [options] Configuration options - * // TODO: describe available options + * Event handler for mouse wheel event, used to zoom the timeline + * See http://adomas.org/javascript-mouse-wheel/ + * https://github.com/EightMedia/hammer.js/issues/256 + * @param {MouseEvent} event + * @private */ - function PointItem (data, conversion, options) { - this.props = { - dot: { - top: 0, - width: 0, - height: 0 - }, - content: { - height: 0, - marginLeft: 0 - } - }; + Network.prototype._onMouseWheel = function(event) { + // retrieve delta + var delta = 0; + if (event.wheelDelta) { /* IE/Opera. */ + delta = event.wheelDelta/120; + } else if (event.detail) { /* Mozilla case. */ + // In Mozilla, sign of delta is different than in IE. + // Also, delta is multiple of 3. + delta = -event.detail/3; + } - // validate data - if (data) { - if (data.start == undefined) { - throw new Error('Property "start" missing in item ' + data); + // If delta is nonzero, handle it. + // Basically, delta is now positive if wheel was scrolled up, + // and negative, if wheel was scrolled down. + if (delta) { + + // calculate the new scale + var scale = this._getScale(); + var zoom = delta / 10; + if (delta < 0) { + zoom = zoom / (1 - zoom); } - } + scale *= (1 + zoom); - Item.call(this, data, conversion, options); - } + // calculate the pointer location + var gesture = hammerUtil.fakeGesture(this, event); + var pointer = this._getPointer(gesture.center); - PointItem.prototype = new Item (null, null, null); + // apply the new scale + this._zoom(scale, pointer); + } - /** - * Check whether this item is visible inside given range - * @returns {{start: Number, end: Number}} range with a timestamp for start and end - * @returns {boolean} True if visible - */ - PointItem.prototype.isVisible = function(range) { - // determine visibility - // TODO: account for the real width of the item. Right now we just add 1/4 to the window - var interval = (range.end - range.start) / 4; - return (this.data.start > range.start - interval) && (this.data.start < range.end + interval); + // Prevent default actions caused by mouse wheel. + event.preventDefault(); }; + /** - * Repaint the item + * Mouse move handler for checking whether the title moves over a node with a title. + * @param {Event} event + * @private */ - PointItem.prototype.redraw = function() { - var dom = this.dom; - if (!dom) { - // create DOM - this.dom = {}; - dom = this.dom; + Network.prototype._onMouseMoveTitle = function (event) { + var gesture = hammerUtil.fakeGesture(this, event); + var pointer = this._getPointer(gesture.center); - // background box - dom.point = document.createElement('div'); - // className is updated in redraw() + // check if the previously selected node is still selected + if (this.popupObj) { + this._checkHidePopup(pointer); + } - // contents box, right from the dot - dom.content = document.createElement('div'); - dom.content.className = 'content'; - dom.point.appendChild(dom.content); + // if we bind the keyboard to the div, we have to highlight it to use it. This highlights it on mouse over + if (this.constants.keyboard.bindToWindow == false && this.constants.keyboard.enabled == true) { + this.frame.focus(); + } - // dot at start - dom.dot = document.createElement('div'); - dom.point.appendChild(dom.dot); + // start a timeout that will check if the mouse is positioned above + // an element + var me = this; + var checkShow = function() { + me._checkShowPopup(pointer); + }; + if (this.popupTimer) { + clearInterval(this.popupTimer); // stop any running calculationTimer + } + if (!this.drag.dragging) { + this.popupTimer = setTimeout(checkShow, this.constants.tooltip.delay); + } - // attach this item as attribute - dom.point['timeline-item'] = this; - this.dirty = true; - } + /** + * Adding hover highlights + */ + if (this.constants.hover == true) { + // removing all hover highlights + for (var edgeId in this.hoverObj.edges) { + if (this.hoverObj.edges.hasOwnProperty(edgeId)) { + this.hoverObj.edges[edgeId].hover = false; + delete this.hoverObj.edges[edgeId]; + } + } - // append DOM to parent DOM - if (!this.parent) { - throw new Error('Cannot redraw item: no parent attached'); - } - if (!dom.point.parentNode) { - var foreground = this.parent.dom.foreground; - if (!foreground) { - throw new Error('Cannot redraw item: parent has no foreground container element'); + // adding hover highlights + var obj = this._getNodeAt(pointer); + if (obj == null) { + obj = this._getEdgeAt(pointer); } - foreground.appendChild(dom.point); + if (obj != null) { + this._hoverObject(obj); + } + + // removing all node hover highlights except for the selected one. + for (var nodeId in this.hoverObj.nodes) { + if (this.hoverObj.nodes.hasOwnProperty(nodeId)) { + if (obj instanceof Node && obj.id != nodeId || obj instanceof Edge || obj == null) { + this._blurObject(this.hoverObj.nodes[nodeId]); + delete this.hoverObj.nodes[nodeId]; + } + } + } + this.redraw(); } - this.displayed = true; + }; - // Update DOM when item is marked dirty. An item is marked dirty when: - // - the item is not yet rendered - // - the item's data is changed - // - the item is selected/deselected - if (this.dirty) { - this._updateContents(this.dom.content); - this._updateTitle(this.dom.point); - this._updateDataAttributes(this.dom.point); - this._updateStyle(this.dom.point); + /** + * Check if there is an element on the given position in the network + * (a node or edge). If so, and if this element has a title, + * show a popup window with its title. + * + * @param {{x:Number, y:Number}} pointer + * @private + */ + Network.prototype._checkShowPopup = function (pointer) { + var obj = { + left: this._XconvertDOMtoCanvas(pointer.x), + top: this._YconvertDOMtoCanvas(pointer.y), + right: this._XconvertDOMtoCanvas(pointer.x), + bottom: this._YconvertDOMtoCanvas(pointer.y) + }; - // update class - var className = (this.data.className? ' ' + this.data.className : '') + - (this.selected ? ' selected' : ''); - dom.point.className = 'item point' + className; - dom.dot.className = 'item dot' + className; + var id; + var lastPopupNode = this.popupObj; + var nodeUnderCursor = false; - // recalculate size - this.width = dom.point.offsetWidth; - this.height = dom.point.offsetHeight; - this.props.dot.width = dom.dot.offsetWidth; - this.props.dot.height = dom.dot.offsetHeight; - this.props.content.height = dom.content.offsetHeight; + if (this.popupObj == undefined) { + // search the nodes for overlap, select the top one in case of multiple nodes + var nodes = this.nodes; + var overlappingNodes = []; + for (id in nodes) { + if (nodes.hasOwnProperty(id)) { + var node = nodes[id]; + if (node.isOverlappingWith(obj)) { + if (node.getTitle() !== undefined) { + overlappingNodes.push(id); + } + } + } + } - // resize contents - dom.content.style.marginLeft = 2 * this.props.dot.width + 'px'; - //dom.content.style.marginRight = ... + 'px'; // TODO: margin right + if (overlappingNodes.length > 0) { + // if there are overlapping nodes, select the last one, this is the + // one which is drawn on top of the others + this.popupObj = this.nodes[overlappingNodes[overlappingNodes.length - 1]]; + // if you hover over a node, the title of the edge is not supposed to be shown. + nodeUnderCursor = true; + } + } - dom.dot.style.top = ((this.height - this.props.dot.height) / 2) + 'px'; - dom.dot.style.left = (this.props.dot.width / 2) + 'px'; + if (this.popupObj === undefined && nodeUnderCursor == false) { + // search the edges for overlap + var edges = this.edges; + var overlappingEdges = []; + for (id in edges) { + if (edges.hasOwnProperty(id)) { + var edge = edges[id]; + if (edge.connected && (edge.getTitle() !== undefined) && + edge.isOverlappingWith(obj)) { + overlappingEdges.push(id); + } + } + } - this.dirty = false; + if (overlappingEdges.length > 0) { + this.popupObj = this.edges[overlappingEdges[overlappingEdges.length - 1]]; + } } - this._repaintDeleteButton(dom.point); - }; + if (this.popupObj) { + // show popup message window + if (this.popupObj != lastPopupNode) { + var me = this; + if (!me.popup) { + me.popup = new Popup(me.frame, me.constants.tooltip); + } - /** - * Show the item in the DOM (when not already visible). The items DOM will - * be created when needed. - */ - PointItem.prototype.show = function() { - if (!this.displayed) { - this.redraw(); + // adjust a small offset such that the mouse cursor is located in the + // bottom left location of the popup, and you can easily move over the + // popup area + me.popup.setPosition(pointer.x - 3, pointer.y - 3); + me.popup.setText(me.popupObj.getTitle()); + me.popup.show(); + } + } + else { + if (this.popup) { + this.popup.hide(); + } } }; + /** - * Hide the item from the DOM (when visible) + * Check if the popup must be hided, which is the case when the mouse is no + * longer hovering on the object + * @param {{x:Number, y:Number}} pointer + * @private */ - PointItem.prototype.hide = function() { - if (this.displayed) { - if (this.dom.point.parentNode) { - this.dom.point.parentNode.removeChild(this.dom.point); + Network.prototype._checkHidePopup = function (pointer) { + if (!this.popupObj || !this._getNodeAt(pointer) ) { + this.popupObj = undefined; + if (this.popup) { + this.popup.hide(); } - - this.top = null; - this.left = null; - - this.displayed = false; } }; + /** - * Reposition the item horizontally - * @Override + * Set a new size for the network + * @param {string} width Width in pixels or percentage (for example '800px' + * or '50%') + * @param {string} height Height in pixels or percentage (for example '400px' + * or '30%') */ - PointItem.prototype.repositionX = function() { - var start = this.conversion.toScreen(this.data.start); + Network.prototype.setSize = function(width, height) { + var emitEvent = false; + var oldWidth = this.frame.canvas.width; + var oldHeight = this.frame.canvas.height; + if (width != this.constants.width || height != this.constants.height || this.frame.style.width != width || this.frame.style.height != height) { + this.frame.style.width = width; + this.frame.style.height = height; - this.left = start - this.props.dot.width; + this.frame.canvas.style.width = '100%'; + this.frame.canvas.style.height = '100%'; - // reposition point - this.dom.point.style.left = this.left + 'px'; - }; + this.frame.canvas.width = this.frame.canvas.clientWidth * this.pixelRatio; + this.frame.canvas.height = this.frame.canvas.clientHeight * this.pixelRatio; - /** - * Reposition the item vertically - * @Override - */ - PointItem.prototype.repositionY = function() { - var orientation = this.options.orientation, - point = this.dom.point; + this.constants.width = width; + this.constants.height = height; - if (orientation == 'top') { - point.style.top = this.top + 'px'; + emitEvent = true; } else { - point.style.top = (this.parent.height - this.top - this.height) + 'px'; - } - }; - - module.exports = PointItem; - + // this would adapt the width of the canvas to the width from 100% if and only if + // there is a change. -/***/ }, -/* 34 */ -/***/ function(module, exports, __webpack_require__) { + if (this.frame.canvas.width != this.frame.canvas.clientWidth * this.pixelRatio) { + this.frame.canvas.width = this.frame.canvas.clientWidth * this.pixelRatio; + emitEvent = true; + } + if (this.frame.canvas.height != this.frame.canvas.clientHeight * this.pixelRatio) { + this.frame.canvas.height = this.frame.canvas.clientHeight * this.pixelRatio; + emitEvent = true; + } + } - var Hammer = __webpack_require__(19); - var Item = __webpack_require__(30); - var BackgroundGroup = __webpack_require__(31); - var RangeItem = __webpack_require__(29); + if (emitEvent == true) { + this.emit('resize', {width:this.frame.canvas.width * this.pixelRatio,height:this.frame.canvas.height * this.pixelRatio, oldWidth: oldWidth * this.pixelRatio, oldHeight: oldHeight * this.pixelRatio}); + } + }; /** - * @constructor BackgroundItem - * @extends Item - * @param {Object} data Object containing parameters start, end - * content, className. - * @param {{toScreen: function, toTime: function}} conversion - * Conversion functions from time to screen and vice versa - * @param {Object} [options] Configuration options - * // TODO: describe options + * Set a data set with nodes for the network + * @param {Array | DataSet | DataView} nodes The data containing the nodes. + * @private */ - // TODO: implement support for the BackgroundItem just having a start, then being displayed as a sort of an annotation - function BackgroundItem (data, conversion, options) { - this.props = { - content: { - width: 0 - } - }; - this.overflow = false; // if contents can overflow (css styling), this flag is set to true + Network.prototype._setNodes = function(nodes) { + var oldNodesData = this.nodesData; - // validate data - if (data) { - if (data.start == undefined) { - throw new Error('Property "start" missing in item ' + data.id); - } - if (data.end == undefined) { - throw new Error('Property "end" missing in item ' + data.id); - } + if (nodes instanceof DataSet || nodes instanceof DataView) { + this.nodesData = nodes; + } + else if (Array.isArray(nodes)) { + this.nodesData = new DataSet(); + this.nodesData.add(nodes); + } + else if (!nodes) { + this.nodesData = new DataSet(); + } + else { + throw new TypeError('Array or DataSet expected'); } - Item.call(this, data, conversion, options); - - this.emptyContent = false; - } + if (oldNodesData) { + // unsubscribe from old dataset + util.forEach(this.nodesListeners, function (callback, event) { + oldNodesData.off(event, callback); + }); + } - BackgroundItem.prototype = new Item (null, null, null); + // remove drawn nodes + this.nodes = {}; - BackgroundItem.prototype.baseClassName = 'item background'; - BackgroundItem.prototype.stack = false; + if (this.nodesData) { + // subscribe to new dataset + var me = this; + util.forEach(this.nodesListeners, function (callback, event) { + me.nodesData.on(event, callback); + }); - /** - * Check whether this item is visible inside given range - * @returns {{start: Number, end: Number}} range with a timestamp for start and end - * @returns {boolean} True if visible - */ - BackgroundItem.prototype.isVisible = function(range) { - // determine visibility - return (this.data.start < range.end) && (this.data.end > range.start); + // draw all new nodes + var ids = this.nodesData.getIds(); + this._addNodes(ids); + } + this._updateSelection(); }; /** - * Repaint the item + * Add nodes + * @param {Number[] | String[]} ids + * @private */ - BackgroundItem.prototype.redraw = function() { - var dom = this.dom; - if (!dom) { - // create DOM - this.dom = {}; - dom = this.dom; - - // background box - dom.box = document.createElement('div'); - // className is updated in redraw() - - // contents box - dom.content = document.createElement('div'); - dom.content.className = 'content'; - dom.box.appendChild(dom.content); - - // Note: we do NOT attach this item as attribute to the DOM, - // such that background items cannot be selected - //dom.box['timeline-item'] = this; - - this.dirty = true; - } - - // append DOM to parent DOM - if (!this.parent) { - throw new Error('Cannot redraw item: no parent attached'); - } - if (!dom.box.parentNode) { - var background = this.parent.dom.background; - if (!background) { - throw new Error('Cannot redraw item: parent has no background container element'); + Network.prototype._addNodes = function(ids) { + var id; + for (var i = 0, len = ids.length; i < len; i++) { + id = ids[i]; + var data = this.nodesData.get(id); + var node = new Node(data, this.images, this.groups, this.constants); + this.nodes[id] = node; // note: this may replace an existing node + if ((node.xFixed == false || node.yFixed == false) && (node.x === null || node.y === null)) { + var radius = 10 * 0.1*ids.length + 10; + var angle = 2 * Math.PI * Math.random(); + if (node.xFixed == false) {node.x = radius * Math.cos(angle);} + if (node.yFixed == false) {node.y = radius * Math.sin(angle);} } - background.appendChild(dom.box); + this.moving = true; } - this.displayed = true; - - // Update DOM when item is marked dirty. An item is marked dirty when: - // - the item is not yet rendered - // - the item's data is changed - // - the item is selected/deselected - if (this.dirty) { - this._updateContents(this.dom.content); - this._updateTitle(this.dom.content); - this._updateDataAttributes(this.dom.content); - this._updateStyle(this.dom.box); - - // update class - var className = (this.data.className ? (' ' + this.data.className) : '') + - (this.selected ? ' selected' : ''); - dom.box.className = this.baseClassName + className; - - // determine from css whether this box has overflow - this.overflow = window.getComputedStyle(dom.content).overflow !== 'hidden'; - - // recalculate size - this.props.content.width = this.dom.content.offsetWidth; - this.height = 0; // set height zero, so this item will be ignored when stacking items - this.dirty = false; + this._updateNodeIndexList(); + if (this.constants.hierarchicalLayout.enabled == true && this.initializing == false) { + this._resetLevels(); + this._setupHierarchicalLayout(); } + this._updateCalculationNodes(); + this._reconnectEdges(); + this._updateValueRange(this.nodes); + this.updateLabels(); }; /** - * Show the item in the DOM (when not already visible). The items DOM will - * be created when needed. - */ - BackgroundItem.prototype.show = RangeItem.prototype.show; - - /** - * Hide the item from the DOM (when visible) - * @return {Boolean} changed - */ - BackgroundItem.prototype.hide = RangeItem.prototype.hide; - - /** - * Reposition the item horizontally - * @Override - */ - BackgroundItem.prototype.repositionX = RangeItem.prototype.repositionX; - - /** - * Reposition the item vertically - * @Override + * Update existing nodes, or create them when not yet existing + * @param {Number[] | String[]} ids + * @private */ - BackgroundItem.prototype.repositionY = function(margin) { - var onTop = this.options.orientation === 'top'; - this.dom.content.style.top = onTop ? '' : '0'; - this.dom.content.style.bottom = onTop ? '0' : ''; - var height; - - // special positioning for subgroups - if (this.data.subgroup !== undefined) { - var itemSubgroup = this.data.subgroup; - var subgroups = this.parent.subgroups; - var subgroupIndex = subgroups[itemSubgroup].index; - // if the orientation is top, we need to take the difference in height into account. - if (onTop == true) { - // the first subgroup will have to account for the distance from the top to the first item. - height = this.parent.subgroups[itemSubgroup].height + margin.item.vertical; - height += subgroupIndex == 0 ? margin.axis - 0.5*margin.item.vertical : 0; - var newTop = this.parent.top; - for (var subgroup in subgroups) { - if (subgroups.hasOwnProperty(subgroup)) { - if (subgroups[subgroup].visible == true && subgroups[subgroup].index < subgroupIndex) { - newTop += subgroups[subgroup].height + margin.item.vertical; - } - } - } - - // the others will have to be offset downwards with this same distance. - newTop += subgroupIndex != 0 ? margin.axis - 0.5 * margin.item.vertical : 0; - this.dom.box.style.top = newTop + 'px'; - this.dom.box.style.bottom = ''; + Network.prototype._updateNodes = function(ids,changedData) { + var nodes = this.nodes; + for (var i = 0, len = ids.length; i < len; i++) { + var id = ids[i]; + var node = nodes[id]; + var data = changedData[i]; + if (node) { + // update node + node.setProperties(data, this.constants); } - // and when the orientation is bottom: else { - var newTop = this.parent.top; - for (var subgroup in subgroups) { - if (subgroups.hasOwnProperty(subgroup)) { - if (subgroups[subgroup].visible == true && subgroups[subgroup].index > subgroupIndex) { - newTop += subgroups[subgroup].height + margin.item.vertical; - } - } - } - height = this.parent.subgroups[itemSubgroup].height + margin.item.vertical; - this.dom.box.style.top = newTop + 'px'; - this.dom.box.style.bottom = ''; + // create node + node = new Node(properties, this.images, this.groups, this.constants); + nodes[id] = node; } } - // and in the case of no subgroups: - else { - // we want backgrounds with groups to only show in groups. - if (this.parent instanceof BackgroundGroup) { - // if the item is not in a group: - height = Math.max(this.parent.height, - this.parent.itemSet.body.domProps.center.height, - this.parent.itemSet.body.domProps.centerContainer.height); - this.dom.box.style.top = onTop ? '0' : ''; - this.dom.box.style.bottom = onTop ? '' : '0'; - } - else { - height = this.parent.height; - // same alignment for items when orientation is top or bottom - this.dom.box.style.top = this.parent.top + 'px'; - this.dom.box.style.bottom = ''; - } + this.moving = true; + if (this.constants.hierarchicalLayout.enabled == true && this.initializing == false) { + this._resetLevels(); + this._setupHierarchicalLayout(); } - this.dom.box.style.height = height + 'px'; + this._updateNodeIndexList(); + this._updateValueRange(nodes); }; - module.exports = BackgroundItem; - - -/***/ }, -/* 35 */ -/***/ function(module, exports, __webpack_require__) { - - var keycharm = __webpack_require__(36); - var Emitter = __webpack_require__(11); - var Hammer = __webpack_require__(19); - var util = __webpack_require__(1); - /** - * Turn an element into an clickToUse element. - * When not active, the element has a transparent overlay. When the overlay is - * clicked, the mode is changed to active. - * When active, the element is displayed with a blue border around it, and - * the interactive contents of the element can be used. When clicked outside - * the element, the elements mode is changed to inactive. - * @param {Element} container - * @constructor + * Remove existing nodes. If nodes do not exist, the method will just ignore it. + * @param {Number[] | String[]} ids + * @private */ - function Activator(container) { - this.active = false; + Network.prototype._removeNodes = function(ids) { + var nodes = this.nodes; + for (var i = 0, len = ids.length; i < len; i++) { + var id = ids[i]; + delete nodes[id]; + } + this._updateNodeIndexList(); + if (this.constants.hierarchicalLayout.enabled == true && this.initializing == false) { + this._resetLevels(); + this._setupHierarchicalLayout(); + } + this._updateCalculationNodes(); + this._reconnectEdges(); + this._updateSelection(); + this._updateValueRange(nodes); + }; - this.dom = { - container: container - }; + /** + * Load edges by reading the data table + * @param {Array | DataSet | DataView} edges The data containing the edges. + * @private + * @private + */ + Network.prototype._setEdges = function(edges) { + var oldEdgesData = this.edgesData; - this.dom.overlay = document.createElement('div'); - this.dom.overlay.className = 'overlay'; + if (edges instanceof DataSet || edges instanceof DataView) { + this.edgesData = edges; + } + else if (Array.isArray(edges)) { + this.edgesData = new DataSet(); + this.edgesData.add(edges); + } + else if (!edges) { + this.edgesData = new DataSet(); + } + else { + throw new TypeError('Array or DataSet expected'); + } - this.dom.container.appendChild(this.dom.overlay); + if (oldEdgesData) { + // unsubscribe from old dataset + util.forEach(this.edgesListeners, function (callback, event) { + oldEdgesData.off(event, callback); + }); + } - this.hammer = Hammer(this.dom.overlay, {prevent_default: false}); - this.hammer.on('tap', this._onTapOverlay.bind(this)); + // remove drawn edges + this.edges = {}; - // block all touch events (except tap) - var me = this; - var events = [ - 'touch', 'pinch', - 'doubletap', 'hold', - 'dragstart', 'drag', 'dragend', - 'mousewheel', 'DOMMouseScroll' // DOMMouseScroll is needed for Firefox - ]; - events.forEach(function (event) { - me.hammer.on(event, function (event) { - event.stopPropagation(); + if (this.edgesData) { + // subscribe to new dataset + var me = this; + util.forEach(this.edgesListeners, function (callback, event) { + me.edgesData.on(event, callback); }); - }); - - // attach a tap event to the window, in order to deactivate when clicking outside the timeline - this.windowHammer = Hammer(window, {prevent_default: false}); - this.windowHammer.on('tap', function (event) { - // deactivate when clicked outside the container - if (!_hasParent(event.target, container)) { - me.deactivate(); - } - }); - if (this.keycharm !== undefined) { - this.keycharm.destroy(); + // draw all new nodes + var ids = this.edgesData.getIds(); + this._addEdges(ids); } - this.keycharm = keycharm(); - - // keycharm listener only bounded when active) - this.escListener = this.deactivate.bind(this); - } - - // turn into an event emitter - Emitter(Activator.prototype); - // The currently active activator - Activator.current = null; + this._reconnectEdges(); + }; /** - * Destroy the activator. Cleans up all created DOM and event listeners + * Add edges + * @param {Number[] | String[]} ids + * @private */ - Activator.prototype.destroy = function () { - this.deactivate(); + Network.prototype._addEdges = function (ids) { + var edges = this.edges, + edgesData = this.edgesData; - // remove dom - this.dom.overlay.parentNode.removeChild(this.dom.overlay); + for (var i = 0, len = ids.length; i < len; i++) { + var id = ids[i]; - // cleanup hammer instances - this.hammer = null; - this.windowHammer = null; - // FIXME: cleaning up hammer instances doesn't work (Timeline not removed from memory) + var oldEdge = edges[id]; + if (oldEdge) { + oldEdge.disconnect(); + } + + var data = edgesData.get(id, {"showInternalIds" : true}); + edges[id] = new Edge(data, this, this.constants); + } + this.moving = true; + this._updateValueRange(edges); + this._createBezierNodes(); + this._updateCalculationNodes(); + if (this.constants.hierarchicalLayout.enabled == true && this.initializing == false) { + this._resetLevels(); + this._setupHierarchicalLayout(); + } }; /** - * Activate the element - * Overlay is hidden, element is decorated with a blue shadow border + * Update existing edges, or create them when not yet existing + * @param {Number[] | String[]} ids + * @private */ - Activator.prototype.activate = function () { - // we allow only one active activator at a time - if (Activator.current) { - Activator.current.deactivate(); - } - Activator.current = this; - - this.active = true; - this.dom.overlay.style.display = 'none'; - util.addClassName(this.dom.container, 'vis-active'); + Network.prototype._updateEdges = function (ids) { + var edges = this.edges, + edgesData = this.edgesData; + for (var i = 0, len = ids.length; i < len; i++) { + var id = ids[i]; - this.emit('change'); - this.emit('activate'); + var data = edgesData.get(id); + var edge = edges[id]; + if (edge) { + // update edge + edge.disconnect(); + edge.setProperties(data, this.constants); + edge.connect(); + } + else { + // create edge + edge = new Edge(data, this, this.constants); + this.edges[id] = edge; + } + } - // ugly hack: bind ESC after emitting the events, as the Network rebinds all - // keyboard events on a 'change' event - this.keycharm.bind('esc', this.escListener); + this._createBezierNodes(); + if (this.constants.hierarchicalLayout.enabled == true && this.initializing == false) { + this._resetLevels(); + this._setupHierarchicalLayout(); + } + this.moving = true; + this._updateValueRange(edges); }; /** - * Deactivate the element - * Overlay is displayed on top of the element + * Remove existing edges. Non existing ids will be ignored + * @param {Number[] | String[]} ids + * @private */ - Activator.prototype.deactivate = function () { - this.active = false; - this.dom.overlay.style.display = ''; - util.removeClassName(this.dom.container, 'vis-active'); - this.keycharm.unbind('esc', this.escListener); + Network.prototype._removeEdges = function (ids) { + var edges = this.edges; + for (var i = 0, len = ids.length; i < len; i++) { + var id = ids[i]; + var edge = edges[id]; + if (edge) { + if (edge.via != null) { + delete this.sectors['support']['nodes'][edge.via.id]; + } + edge.disconnect(); + delete edges[id]; + } + } - this.emit('change'); - this.emit('deactivate'); + this.moving = true; + this._updateValueRange(edges); + if (this.constants.hierarchicalLayout.enabled == true && this.initializing == false) { + this._resetLevels(); + this._setupHierarchicalLayout(); + } + this._updateCalculationNodes(); }; /** - * Handle a tap event: activate the container - * @param event + * Reconnect all edges * @private */ - Activator.prototype._onTapOverlay = function (event) { - // activate the container - this.activate(); - event.stopPropagation(); + Network.prototype._reconnectEdges = function() { + var id, + nodes = this.nodes, + edges = this.edges; + for (id in nodes) { + if (nodes.hasOwnProperty(id)) { + nodes[id].edges = []; + nodes[id].dynamicEdges = []; + } + } + + for (id in edges) { + if (edges.hasOwnProperty(id)) { + var edge = edges[id]; + edge.from = null; + edge.to = null; + edge.connect(); + } + } }; /** - * Test whether the element has the requested parent element somewhere in - * its chain of parent nodes. - * @param {HTMLElement} element - * @param {HTMLElement} parent - * @returns {boolean} Returns true when the parent is found somewhere in the - * chain of parent nodes. + * Update the values of all object in the given array according to the current + * value range of the objects in the array. + * @param {Object} obj An object containing a set of Edges or Nodes + * The objects must have a method getValue() and + * setValueRange(min, max). * @private */ - function _hasParent(element, parent) { - while (element) { - if (element === parent) { - return true + Network.prototype._updateValueRange = function(obj) { + var id; + + // determine the range of the objects + var valueMin = undefined; + var valueMax = undefined; + for (id in obj) { + if (obj.hasOwnProperty(id)) { + var value = obj[id].getValue(); + if (value !== undefined) { + valueMin = (valueMin === undefined) ? value : Math.min(value, valueMin); + valueMax = (valueMax === undefined) ? value : Math.max(value, valueMax); + } } - element = element.parentNode; } - return false; - } - - module.exports = Activator; + // adjust the range of all objects + if (valueMin !== undefined && valueMax !== undefined) { + for (id in obj) { + if (obj.hasOwnProperty(id)) { + obj[id].setValueRange(valueMin, valueMax); + } + } + } + }; -/***/ }, -/* 36 */ -/***/ function(module, exports, __webpack_require__) { + /** + * Redraw the network with the current data + * chart will be resized too. + */ + Network.prototype.redraw = function() { + this.setSize(this.constants.width, this.constants.height); + this._redraw(); + }; - var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;"use strict"; /** - * Created by Alex on 11/6/2014. + * Redraw the network with the current data + * @param hidden | used to get the first estimate of the node sizes. only the nodes are drawn after which they are quickly drawn over. + * @private */ + Network.prototype._redraw = function(hidden) { + var ctx = this.frame.canvas.getContext('2d'); - // 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 () { + ctx.setTransform(this.pixelRatio, 0, 0, this.pixelRatio, 0, 0); - function keycharm(options) { - var preventDefault = options && options.preventDefault || false; + // clear the canvas + var w = this.frame.canvas.width * this.pixelRatio; + var h = this.frame.canvas.height * this.pixelRatio; + ctx.clearRect(0, 0, w, h); - var container = options && options.container || window; - var _exportFunctions = {}; - var _bound = {keydown:{}, keyup:{}}; - var _keys = {}; - var i; - - // a - z - for (i = 97; i <= 122; i++) {_keys[String.fromCharCode(i)] = {code:65 + (i - 97), shift: false};} - // A - Z - for (i = 65; i <= 90; i++) {_keys[String.fromCharCode(i)] = {code:i, shift: true};} - // 0 - 9 - for (i = 0; i <= 9; i++) {_keys['' + i] = {code:48 + i, shift: false};} - // F1 - F12 - for (i = 1; i <= 12; i++) {_keys['F' + i] = {code:111 + i, shift: false};} - // num0 - num9 - for (i = 0; i <= 9; i++) {_keys['num' + i] = {code:96 + i, shift: false};} - - // numpad misc - _keys['num*'] = {code:106, shift: false}; - _keys['num+'] = {code:107, shift: false}; - _keys['num-'] = {code:109, shift: false}; - _keys['num/'] = {code:111, shift: false}; - _keys['num.'] = {code:110, shift: false}; - // arrows - _keys['left'] = {code:37, shift: false}; - _keys['up'] = {code:38, shift: false}; - _keys['right'] = {code:39, shift: false}; - _keys['down'] = {code:40, shift: false}; - // extra keys - _keys['space'] = {code:32, shift: false}; - _keys['enter'] = {code:13, shift: false}; - _keys['shift'] = {code:16, shift: undefined}; - _keys['esc'] = {code:27, shift: false}; - _keys['backspace'] = {code:8, shift: false}; - _keys['tab'] = {code:9, shift: false}; - _keys['ctrl'] = {code:17, shift: false}; - _keys['alt'] = {code:18, shift: false}; - _keys['delete'] = {code:46, shift: false}; - _keys['pageup'] = {code:33, shift: false}; - _keys['pagedown'] = {code:34, shift: false}; - // symbols - _keys['='] = {code:187, shift: false}; - _keys['-'] = {code:189, shift: false}; - _keys[']'] = {code:221, shift: false}; - _keys['['] = {code:219, shift: false}; - - - - var down = function(event) {handleEvent(event,'keydown');}; - var up = function(event) {handleEvent(event,'keyup');}; - - // handle the actualy bound key with the event - var handleEvent = function(event,type) { - if (_bound[type][event.keyCode] !== undefined) { - var bound = _bound[type][event.keyCode]; - for (var i = 0; i < bound.length; i++) { - if (bound[i].shift === undefined) { - bound[i].fn(event); - } - else if (bound[i].shift == true && event.shiftKey == true) { - bound[i].fn(event); - } - else if (bound[i].shift == false && event.shiftKey == false) { - bound[i].fn(event); - } - } + // set scaling and translation + ctx.save(); + ctx.translate(this.translation.x, this.translation.y); + ctx.scale(this.scale, this.scale); - if (preventDefault == true) { - event.preventDefault(); - } - } - }; + this.canvasTopLeft = { + "x": this._XconvertDOMtoCanvas(0), + "y": this._YconvertDOMtoCanvas(0) + }; + this.canvasBottomRight = { + "x": this._XconvertDOMtoCanvas(this.frame.canvas.clientWidth * this.pixelRatio), + "y": this._YconvertDOMtoCanvas(this.frame.canvas.clientHeight * this.pixelRatio) + }; - // 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}); - }; + if (!(hidden == true)) { + this._doInAllSectors("_drawAllSectorNodes", ctx); + if (this.drag.dragging == false || this.drag.dragging === undefined || this.constants.hideEdgesOnDrag == false) { + this._doInAllSectors("_drawEdges", ctx); + } + } + if (this.drag.dragging == false || this.drag.dragging === undefined || this.constants.hideNodesOnDrag == false) { + this._doInAllSectors("_drawNodes",ctx,false); + } - // 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); - } - } - }; + if (!(hidden == true)) { + if (this.controlNodesActive == true) { + this._doInAllSectors("_drawControlNodes", ctx); + } + } - // 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"; - }; + // this._doInSupportSector("_drawNodes",ctx,true); + // this._drawTree(ctx,"#F00F0F"); - // 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] = []; - } - }; + // restore original scaling and translation + ctx.restore(); - // reset all bound variables. - _exportFunctions.reset = function() { - _bound = {keydown:{}, keyup:{}}; - }; + if (hidden == true) { + ctx.clearRect(0, 0, w, h); + } + }; - // unbind all listeners and reset all variables. - _exportFunctions.destroy = function() { - _bound = {keydown:{}, keyup:{}}; - container.removeEventListener('keydown', down, true); - container.removeEventListener('keyup', up, true); + /** + * Set the translation of the network + * @param {Number} offsetX Horizontal offset + * @param {Number} offsetY Vertical offset + * @private + */ + Network.prototype._setTranslation = function(offsetX, offsetY) { + if (this.translation === undefined) { + this.translation = { + x: 0, + y: 0 }; - - // create listeners. - container.addEventListener('keydown',down,true); - container.addEventListener('keyup',up,true); - - // return the public functions. - return _exportFunctions; } - return keycharm; - })); + if (offsetX !== undefined) { + this.translation.x = offsetX; + } + if (offsetY !== undefined) { + this.translation.y = offsetY; + } + this.emit('viewChanged'); + }; + /** + * Get the translation of the network + * @return {Object} translation An object with parameters x and y, both a number + * @private + */ + Network.prototype._getTranslation = function() { + return { + x: this.translation.x, + y: this.translation.y + }; + }; + /** + * Scale the network + * @param {Number} scale Scaling factor 1.0 is unscaled + * @private + */ + Network.prototype._setScale = function(scale) { + this.scale = scale; + }; -/***/ }, -/* 37 */ -/***/ function(module, exports, __webpack_require__) { + /** + * Get the current scale of the network + * @return {Number} scale Scaling factor 1.0 is unscaled + * @private + */ + Network.prototype._getScale = function() { + return this.scale; + }; - var util = __webpack_require__(1); - var Component = __webpack_require__(23); - var TimeStep = __webpack_require__(38); - var DateUtil = __webpack_require__(24); - var moment = __webpack_require__(2); + /** + * Convert the X coordinate in DOM-space (coordinate point in browser relative to the container div) to + * the X coordinate in canvas-space (the simulation sandbox, which the camera looks upon) + * @param {number} x + * @returns {number} + * @private + */ + Network.prototype._XconvertDOMtoCanvas = function(x) { + return (x - this.translation.x) / this.scale; + }; /** - * A horizontal time axis - * @param {{dom: Object, domProps: Object, emitter: Emitter, range: Range}} body - * @param {Object} [options] See TimeAxis.setOptions for the available - * options. - * @constructor TimeAxis - * @extends Component + * Convert the X coordinate in canvas-space (the simulation sandbox, which the camera looks upon) to + * the X coordinate in DOM-space (coordinate point in browser relative to the container div) + * @param {number} x + * @returns {number} + * @private */ - function TimeAxis (body, options) { - this.dom = { - foreground: null, - lines: [], - majorTexts: [], - minorTexts: [], - redundant: { - lines: [], - majorTexts: [], - minorTexts: [] - } - }; - this.props = { - range: { - start: 0, - end: 0, - minimumStep: 0 - }, - lineTop: 0 - }; + Network.prototype._XconvertCanvasToDOM = function(x) { + return x * this.scale + this.translation.x; + }; - this.defaultOptions = { - orientation: 'bottom', // supported: 'top', 'bottom' - // TODO: implement timeaxis orientations 'left' and 'right' - showMinorLabels: true, - showMajorLabels: true, - format: null - }; - this.options = util.extend({}, this.defaultOptions); + /** + * Convert the Y coordinate in DOM-space (coordinate point in browser relative to the container div) to + * the Y coordinate in canvas-space (the simulation sandbox, which the camera looks upon) + * @param {number} y + * @returns {number} + * @private + */ + Network.prototype._YconvertDOMtoCanvas = function(y) { + return (y - this.translation.y) / this.scale; + }; - this.body = body; + /** + * Convert the Y coordinate in canvas-space (the simulation sandbox, which the camera looks upon) to + * the Y coordinate in DOM-space (coordinate point in browser relative to the container div) + * @param {number} y + * @returns {number} + * @private + */ + Network.prototype._YconvertCanvasToDOM = function(y) { + return y * this.scale + this.translation.y ; + }; - // create the HTML DOM - this._create(); - this.setOptions(options); - } + /** + * + * @param {object} pos = {x: number, y: number} + * @returns {{x: number, y: number}} + * @constructor + */ + Network.prototype.canvasToDOM = function (pos) { + return {x: this._XconvertCanvasToDOM(pos.x), y: this._YconvertCanvasToDOM(pos.y)}; + }; - TimeAxis.prototype = new Component(); + /** + * + * @param {object} pos = {x: number, y: number} + * @returns {{x: number, y: number}} + * @constructor + */ + Network.prototype.DOMtoCanvas = function (pos) { + return {x: this._XconvertDOMtoCanvas(pos.x), y: this._YconvertDOMtoCanvas(pos.y)}; + }; /** - * Set options for the TimeAxis. - * Parameters will be merged in current options. - * @param {Object} options Available options: - * {string} [orientation] - * {boolean} [showMinorLabels] - * {boolean} [showMajorLabels] + * Redraw all nodes + * The 2d context of a HTML canvas can be retrieved by canvas.getContext('2d'); + * @param {CanvasRenderingContext2D} ctx + * @param {Boolean} [alwaysShow] + * @private */ - TimeAxis.prototype.setOptions = function(options) { - if (options) { - // copy all options that we know - util.selectiveExtend([ - 'orientation', - 'showMinorLabels', - 'showMajorLabels', - 'hiddenDates', - 'format' - ], this.options, options); + Network.prototype._drawNodes = function(ctx,alwaysShow) { + if (alwaysShow === undefined) { + alwaysShow = false; + } - // apply locale to moment.js - // TODO: not so nice, this is applied globally to moment.js - if ('locale' in options) { - if (typeof moment.locale === 'function') { - // moment.js 2.8.1+ - moment.locale(options.locale); + // first draw the unselected nodes + var nodes = this.nodes; + var selected = []; + + for (var id in nodes) { + if (nodes.hasOwnProperty(id)) { + nodes[id].setScaleAndPos(this.scale,this.canvasTopLeft,this.canvasBottomRight); + if (nodes[id].isSelected()) { + selected.push(id); } else { - moment.lang(options.locale); + if (nodes[id].inArea() || alwaysShow) { + nodes[id].draw(ctx); + } } } } + + // draw the selected nodes on top + for (var s = 0, sMax = selected.length; s < sMax; s++) { + if (nodes[selected[s]].inArea() || alwaysShow) { + nodes[selected[s]].draw(ctx); + } + } }; /** - * Create the HTML DOM for the TimeAxis + * Redraw all edges + * The 2d context of a HTML canvas can be retrieved by canvas.getContext('2d'); + * @param {CanvasRenderingContext2D} ctx + * @private */ - TimeAxis.prototype._create = function() { - this.dom.foreground = document.createElement('div'); - this.dom.background = document.createElement('div'); - - this.dom.foreground.className = 'timeaxis foreground'; - this.dom.background.className = 'timeaxis background'; + Network.prototype._drawEdges = function(ctx) { + var edges = this.edges; + for (var id in edges) { + if (edges.hasOwnProperty(id)) { + var edge = edges[id]; + edge.setScale(this.scale); + if (edge.connected) { + edges[id].draw(ctx); + } + } + } }; /** - * Destroy the TimeAxis + * Redraw all edges + * The 2d context of a HTML canvas can be retrieved by canvas.getContext('2d'); + * @param {CanvasRenderingContext2D} ctx + * @private */ - TimeAxis.prototype.destroy = function() { - // remove from DOM - if (this.dom.foreground.parentNode) { - this.dom.foreground.parentNode.removeChild(this.dom.foreground); - } - if (this.dom.background.parentNode) { - this.dom.background.parentNode.removeChild(this.dom.background); + Network.prototype._drawControlNodes = function(ctx) { + var edges = this.edges; + for (var id in edges) { + if (edges.hasOwnProperty(id)) { + edges[id]._drawControlNodes(ctx); + } } - - this.body = null; }; /** - * Repaint the component - * @return {boolean} Returns true if the component is resized + * Find a stable position for all nodes + * @private */ - TimeAxis.prototype.redraw = function () { - var options = this.options; - var props = this.props; - var foreground = this.dom.foreground; - var background = this.dom.background; - - // determine the correct parent DOM element (depending on option orientation) - var parent = (options.orientation == 'top') ? this.body.dom.top : this.body.dom.bottom; - var parentChanged = (foreground.parentNode !== parent); - - // calculate character width and height - this._calculateCharSize(); - - // TODO: recalculate sizes only needed when parent is resized or options is changed - var orientation = this.options.orientation, - showMinorLabels = this.options.showMinorLabels, - showMajorLabels = this.options.showMajorLabels; - - // determine the width and height of the elemens for the axis - props.minorLabelHeight = showMinorLabels ? props.minorCharHeight : 0; - props.majorLabelHeight = showMajorLabels ? props.majorCharHeight : 0; - props.height = props.minorLabelHeight + props.majorLabelHeight; - props.width = foreground.offsetWidth; - - props.minorLineHeight = this.body.domProps.root.height - props.majorLabelHeight - - (options.orientation == 'top' ? this.body.domProps.bottom.height : this.body.domProps.top.height); - props.minorLineWidth = 1; // TODO: really calculate width - props.majorLineHeight = props.minorLineHeight + props.majorLabelHeight; - props.majorLineWidth = 1; // TODO: really calculate width - - // take foreground and background offline while updating (is almost twice as fast) - var foregroundNextSibling = foreground.nextSibling; - var backgroundNextSibling = background.nextSibling; - foreground.parentNode && foreground.parentNode.removeChild(foreground); - background.parentNode && background.parentNode.removeChild(background); - - foreground.style.height = this.props.height + 'px'; - - this._repaintLabels(); - - // put DOM online again (at the same place) - if (foregroundNextSibling) { - parent.insertBefore(foreground, foregroundNextSibling); - } - else { - parent.appendChild(foreground) + Network.prototype._stabilize = function() { + if (this.constants.freezeForStabilization == true) { + this._freezeDefinedNodes(); } - if (backgroundNextSibling) { - this.body.dom.backgroundVertical.insertBefore(background, backgroundNextSibling); + + // find stable position + var count = 0; + while (this.moving && count < this.constants.stabilizationIterations) { + this._physicsTick(); + count++; } - else { - this.body.dom.backgroundVertical.appendChild(background) + + if (this.constants.zoomExtentOnStabilize == true) { + this.zoomExtent(undefined, false, true); } - return this._isResized() || parentChanged; + if (this.constants.freezeForStabilization == true) { + this._restoreFrozenNodes(); + } }; /** - * Repaint major and minor text labels and vertical grid lines + * When initializing and stabilizing, we can freeze nodes with a predefined position. This greatly speeds up stabilization + * because only the supportnodes for the smoothCurves have to settle. + * * @private */ - TimeAxis.prototype._repaintLabels = function () { - var orientation = this.options.orientation; - - // calculate range and step (step such that we have space for 7 characters per label) - var start = util.convert(this.body.range.start, 'Number'); - var end = util.convert(this.body.range.end, 'Number'); - var timeLabelsize = this.body.util.toTime((this.props.minorCharWidth || 10) * 7).valueOf(); - var minimumStep = timeLabelsize - DateUtil.getHiddenDurationBefore(this.body.hiddenDates, this.body.range, timeLabelsize); - minimumStep -= this.body.util.toTime(0).valueOf(); - - var step = new TimeStep(new Date(start), new Date(end), minimumStep, this.body.hiddenDates); - if (this.options.format) { - step.setFormat(this.options.format); + Network.prototype._freezeDefinedNodes = function() { + var nodes = this.nodes; + for (var id in nodes) { + if (nodes.hasOwnProperty(id)) { + if (nodes[id].x != null && nodes[id].y != null) { + nodes[id].fixedData.x = nodes[id].xFixed; + nodes[id].fixedData.y = nodes[id].yFixed; + nodes[id].xFixed = true; + nodes[id].yFixed = true; + } + } } - this.step = step; + }; - // Move all DOM elements to a "redundant" list, where they - // can be picked for re-use, and clear the lists with lines and texts. - // At the end of the function _repaintLabels, left over elements will be cleaned up - var dom = this.dom; - dom.redundant.lines = dom.lines; - dom.redundant.majorTexts = dom.majorTexts; - dom.redundant.minorTexts = dom.minorTexts; - dom.lines = []; - dom.majorTexts = []; - dom.minorTexts = []; + /** + * Unfreezes the nodes that have been frozen by _freezeDefinedNodes. + * + * @private + */ + Network.prototype._restoreFrozenNodes = function() { + var nodes = this.nodes; + for (var id in nodes) { + if (nodes.hasOwnProperty(id)) { + if (nodes[id].fixedData.x != null) { + nodes[id].xFixed = nodes[id].fixedData.x; + nodes[id].yFixed = nodes[id].fixedData.y; + } + } + } + }; - var cur; - var x = 0; - var isMajor; - var xPrev = 0; - var width = 0; - var prevLine; - var xFirstMajorLabel = undefined; - var max = 0; - var className; - step.first(); - while (step.hasNext() && max < 1000) { - max++; + /** + * Check if any of the nodes is still moving + * @param {number} vmin the minimum velocity considered as 'moving' + * @return {boolean} true if moving, false if non of the nodes is moving + * @private + */ + Network.prototype._isMoving = function(vmin) { + var nodes = this.nodes; + for (var id in nodes) { + if (nodes.hasOwnProperty(id) && nodes[id].isMoving(vmin)) { + return true; + } + } + return false; + }; - cur = step.getCurrent(); - isMajor = step.isMajor(); - className = step.getClassName(); - xPrev = x; - x = this.body.util.toScreen(cur); - width = x - xPrev; - if (prevLine) { - prevLine.style.width = width + 'px'; - } + /** + * /** + * Perform one discrete step for all nodes + * + * @private + */ + Network.prototype._discreteStepNodes = function() { + var interval = this.physicsDiscreteStepsize; + var nodes = this.nodes; + var nodeId; + var nodesPresent = false; - if (this.options.showMinorLabels) { - this._repaintMinorText(x, step.getLabelMinor(), orientation, className); + if (this.constants.maxVelocity > 0) { + for (nodeId in nodes) { + if (nodes.hasOwnProperty(nodeId)) { + nodes[nodeId].discreteStepLimited(interval, this.constants.maxVelocity); + nodesPresent = true; + } } - - if (isMajor && this.options.showMajorLabels) { - if (x > 0) { - if (xFirstMajorLabel == undefined) { - xFirstMajorLabel = x; - } - this._repaintMajorText(x, step.getLabelMajor(), orientation, className); + } + else { + for (nodeId in nodes) { + if (nodes.hasOwnProperty(nodeId)) { + nodes[nodeId].discreteStep(interval); + nodesPresent = true; } - prevLine = this._repaintMajorLine(x, orientation, className); + } + } + + if (nodesPresent == true) { + var vminCorrected = this.constants.minVelocity / Math.max(this.scale,0.05); + if (vminCorrected > 0.5*this.constants.maxVelocity) { + return true; } else { - prevLine = this._repaintMinorLine(x, orientation, className); + return this._isMoving(vminCorrected); } - - step.next(); } + return false; + }; - // create a major label on the left when needed - if (this.options.showMajorLabels) { - var leftTime = this.body.util.toTime(0), - leftText = step.getLabelMajor(leftTime), - widthText = leftText.length * (this.props.majorCharWidth || 10) + 10; // upper bound estimation - if (xFirstMajorLabel == undefined || widthText < xFirstMajorLabel) { - this._repaintMajorText(0, leftText, orientation, className); + Network.prototype._revertPhysicsState = function() { + var nodes = this.nodes; + for (var nodeId in nodes) { + if (nodes.hasOwnProperty(nodeId)) { + nodes[nodeId].revertPosition(); } } + } - // Cleanup leftover DOM elements from the redundant list - util.forEach(this.dom.redundant, function (arr) { - while (arr.length) { - var elem = arr.pop(); - if (elem && elem.parentNode) { - elem.parentNode.removeChild(elem); - } - } - }); - }; + Network.prototype._revertPhysicsTick = function() { + this._doInAllActiveSectors("_revertPhysicsState"); + if (this.constants.smoothCurves.enabled == true && this.constants.smoothCurves.dynamic == true) { + this._doInSupportSector("_revertPhysicsState"); + } + } /** - * Create a minor label for the axis at position x - * @param {Number} x - * @param {String} text - * @param {String} orientation "top" or "bottom" (default) - * @param {String} className + * A single simulation step (or "tick") in the physics simulation + * * @private */ - TimeAxis.prototype._repaintMinorText = function (x, text, orientation, className) { - // reuse redundant label - var label = this.dom.redundant.minorTexts.shift(); + Network.prototype._physicsTick = function() { + if (!this.freezeSimulation) { + if (this.moving == true) { + var mainMovingStatus = false; + var supportMovingStatus = false; - if (!label) { - // create new label - var content = document.createTextNode(''); - label = document.createElement('div'); - label.appendChild(content); - this.dom.foreground.appendChild(label); - } - this.dom.minorTexts.push(label); + this._doInAllActiveSectors("_initializeForceCalculation"); + var mainMoving = this._doInAllActiveSectors("_discreteStepNodes"); + if (this.constants.smoothCurves.enabled == true && this.constants.smoothCurves.dynamic == true) { + supportMovingStatus = this._doInSupportSector("_discreteStepNodes"); + } - label.childNodes[0].nodeValue = text; + // gather movement data from all sectors, if one moves, we are NOT stabilzied + for (var i = 0; i < mainMoving.length; i++) {mainMovingStatus = mainMoving[0] || mainMovingStatus;} - label.style.top = (orientation == 'top') ? (this.props.majorLabelHeight + 'px') : '0'; - label.style.left = x + 'px'; - label.className = 'text minor ' + className; - //label.title = title; // TODO: this is a heavy operation + // determine if the network has stabilzied + this.moving = mainMovingStatus || supportMovingStatus; + + if (this.moving == false) { + this._revertPhysicsTick(); + } + else { + // this is here to ensure that there is no start event when the network is already stable. + if (this.startedStabilization == false) { + this.emit("startStabilization"); + this.startedStabilization = true; + } + } + + this.stabilizationIterations++; + } + } }; + /** - * Create a Major label for the axis at position x - * @param {Number} x - * @param {String} text - * @param {String} orientation "top" or "bottom" (default) - * @param {String} className + * This function runs one step of the animation. It calls an x amount of physics ticks and one render tick. + * It reschedules itself at the beginning of the function + * * @private */ - TimeAxis.prototype._repaintMajorText = function (x, text, orientation, className) { - // reuse redundant label - var label = this.dom.redundant.majorTexts.shift(); + Network.prototype._animationStep = function() { + // reset the timer so a new scheduled animation step can be set + this.timer = undefined; - if (!label) { - // create label - var content = document.createTextNode(text); - label = document.createElement('div'); - label.appendChild(content); - this.dom.foreground.appendChild(label); + // handle the keyboad movement + this._handleNavigation(); + + // check if the physics have settled + if (this.moving == true) { + var startTime = Date.now(); + this._physicsTick(); + var physicsTime = Date.now() - startTime; + + // run double speed if it is a little graph + if ((this.renderTimestep - this.renderTime > 2 * physicsTime || this.runDoubleSpeed == true) && this.moving == true) { + this._physicsTick(); + + // this makes sure there is no jitter. The decision is taken once to run it at double speed. + if (this.renderTime != 0) { + this.runDoubleSpeed = true + } + } } - this.dom.majorTexts.push(label); - label.childNodes[0].nodeValue = text; - label.className = 'text major ' + className; - //label.title = title; // TODO: this is a heavy operation + var renderStartTime = Date.now(); + this._redraw(); + this.renderTime = Date.now() - renderStartTime; - label.style.top = (orientation == 'top') ? '0' : (this.props.minorLabelHeight + 'px'); - label.style.left = x + 'px'; + // this schedules a new animation step + this.start(); + }; + + if (typeof window !== 'undefined') { + window.requestAnimationFrame = window.requestAnimationFrame || window.mozRequestAnimationFrame || + window.webkitRequestAnimationFrame || window.msRequestAnimationFrame; + } + + /** + * Schedule a animation step with the refreshrate interval. + */ + Network.prototype.start = function() { + if (this.moving == true || this.xIncrement != 0 || this.yIncrement != 0 || this.zoomIncrement != 0 || this.animating == true) { + if (!this.timer) { + if (this.requiresTimeout == true) { + this.timer = window.setTimeout(this._animationStep.bind(this), this.renderTimestep); // wait this.renderTimeStep milliseconds and perform the animation step function + } + else { + this.timer = window.requestAnimationFrame(this._animationStep.bind(this)); // wait this.renderTimeStep milliseconds and perform the animation step function + } + } + } + else { + this._redraw(); + // this check is to ensure that the network does not emit these events if it was already stabilized and setOptions is called (setting moving to true and calling start()) + if (this.stabilizationIterations > 1) { + // trigger the "stabilized" event. + // The event is triggered on the next tick, to prevent the case that + // it is fired while initializing the Network, in which case you would not + // be able to catch it + var me = this; + var params = { + iterations: me.stabilizationIterations + }; + this.stabilizationIterations = 0; + this.startedStabilization = false; + setTimeout(function () { + me.emit("stabilized", params); + }, 0); + } + else { + this.stabilizationIterations = 0; + } + } }; + /** - * Create a minor line for the axis at position x - * @param {Number} x - * @param {String} orientation "top" or "bottom" (default) - * @param {String} className - * @return {Element} Returns the created line + * Move the network according to the keyboard presses. + * * @private */ - TimeAxis.prototype._repaintMinorLine = function (x, orientation, className) { - // reuse redundant line - var line = this.dom.redundant.lines.shift(); - if (!line) { - // create vertical line - line = document.createElement('div'); - this.dom.background.appendChild(line); + Network.prototype._handleNavigation = function() { + if (this.xIncrement != 0 || this.yIncrement != 0) { + var translation = this._getTranslation(); + this._setTranslation(translation.x+this.xIncrement, translation.y+this.yIncrement); } - this.dom.lines.push(line); + if (this.zoomIncrement != 0) { + var center = { + x: this.frame.canvas.clientWidth / 2, + y: this.frame.canvas.clientHeight / 2 + }; + this._zoom(this.scale*(1 + this.zoomIncrement), center); + } + }; - var props = this.props; - if (orientation == 'top') { - line.style.top = props.majorLabelHeight + 'px'; + + /** + * Freeze the _animationStep + */ + Network.prototype.toggleFreeze = function() { + if (this.freezeSimulation == false) { + this.freezeSimulation = true; } else { - line.style.top = this.body.domProps.top.height + 'px'; + this.freezeSimulation = false; + this.start(); } - line.style.height = props.minorLineHeight + 'px'; - line.style.left = (x - props.minorLineWidth / 2) + 'px'; - - line.className = 'grid vertical minor ' + className; - - return line; }; + /** - * Create a Major line for the axis at position x - * @param {Number} x - * @param {String} orientation "top" or "bottom" (default) - * @param {String} className - * @return {Element} Returns the created line + * This function cleans the support nodes if they are not needed and adds them when they are. + * + * @param {boolean} [disableStart] * @private */ - TimeAxis.prototype._repaintMajorLine = function (x, orientation, className) { - // reuse redundant line - var line = this.dom.redundant.lines.shift(); - if (!line) { - // create vertical line - line = document.createElement('div'); - this.dom.background.appendChild(line); + Network.prototype._configureSmoothCurves = function(disableStart) { + if (disableStart === undefined) { + disableStart = true; } - this.dom.lines.push(line); - - var props = this.props; - if (orientation == 'top') { - line.style.top = '0'; + if (this.constants.smoothCurves.enabled == true && this.constants.smoothCurves.dynamic == true) { + this._createBezierNodes(); + // cleanup unused support nodes + for (var nodeId in this.sectors['support']['nodes']) { + if (this.sectors['support']['nodes'].hasOwnProperty(nodeId)) { + if (this.edges[this.sectors['support']['nodes'][nodeId].parentEdgeId] === undefined) { + delete this.sectors['support']['nodes'][nodeId]; + } + } + } } else { - line.style.top = this.body.domProps.top.height + 'px'; + // delete the support nodes + this.sectors['support']['nodes'] = {}; + for (var edgeId in this.edges) { + if (this.edges.hasOwnProperty(edgeId)) { + this.edges[edgeId].via = null; + } + } } - line.style.left = (x - props.majorLineWidth / 2) + 'px'; - line.style.height = props.majorLineHeight + 'px'; - line.className = 'grid vertical major ' + className; - return line; + this._updateCalculationNodes(); + if (!disableStart) { + this.moving = true; + this.start(); + } }; + /** - * Determine the size of text on the axis (both major and minor axis). - * The size is calculated only once and then cached in this.props. + * 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. + * * @private */ - TimeAxis.prototype._calculateCharSize = function () { - // Note: We calculate char size with every redraw. Size may change, for - // example when any of the timelines parents had display:none for example. - - // determine the char width and height on the minor axis - if (!this.dom.measureCharMinor) { - this.dom.measureCharMinor = document.createElement('DIV'); - this.dom.measureCharMinor.className = 'text minor measure'; - this.dom.measureCharMinor.style.position = 'absolute'; + Network.prototype._createBezierNodes = function() { + if (this.constants.smoothCurves.enabled == true && this.constants.smoothCurves.dynamic == true) { + for (var edgeId in this.edges) { + if (this.edges.hasOwnProperty(edgeId)) { + var edge = this.edges[edgeId]; + if (edge.via == null) { + var nodeId = "edgeId:".concat(edge.id); + this.sectors['support']['nodes'][nodeId] = new Node( + {id:nodeId, + mass:1, + shape:'circle', + image:"", + internalMultiplier:1 + },{},{},this.constants); + edge.via = this.sectors['support']['nodes'][nodeId]; + edge.via.parentEdgeId = edge.id; + edge.positionBezierNode(); + } + } + } + } + }; - this.dom.measureCharMinor.appendChild(document.createTextNode('0')); - this.dom.foreground.appendChild(this.dom.measureCharMinor); + /** + * load the functions that load the mixins into the prototype. + * + * @private + */ + Network.prototype._initializeMixinLoaders = function () { + for (var mixin in MixinLoader) { + if (MixinLoader.hasOwnProperty(mixin)) { + Network.prototype[mixin] = MixinLoader[mixin]; + } } - this.props.minorCharHeight = this.dom.measureCharMinor.clientHeight; - this.props.minorCharWidth = this.dom.measureCharMinor.clientWidth; + }; - // determine the char width and height on the major axis - if (!this.dom.measureCharMajor) { - this.dom.measureCharMajor = document.createElement('DIV'); - this.dom.measureCharMajor.className = 'text major measure'; - this.dom.measureCharMajor.style.position = 'absolute'; + /** + * Load the XY positions of the nodes into the dataset. + */ + Network.prototype.storePosition = function() { + console.log("storePosition is depricated: use .storePositions() from now on.") + this.storePositions(); + }; - this.dom.measureCharMajor.appendChild(document.createTextNode('0')); - this.dom.foreground.appendChild(this.dom.measureCharMajor); + /** + * Load the XY positions of the nodes into the dataset. + */ + Network.prototype.storePositions = function() { + var dataArray = []; + for (var nodeId in this.nodes) { + if (this.nodes.hasOwnProperty(nodeId)) { + var node = this.nodes[nodeId]; + var allowedToMoveX = !this.nodes.xFixed; + var allowedToMoveY = !this.nodes.yFixed; + if (this.nodesData._data[nodeId].x != Math.round(node.x) || this.nodesData._data[nodeId].y != Math.round(node.y)) { + dataArray.push({id:nodeId,x:Math.round(node.x),y:Math.round(node.y),allowedToMoveX:allowedToMoveX,allowedToMoveY:allowedToMoveY}); + } + } } - this.props.majorCharHeight = this.dom.measureCharMajor.clientHeight; - this.props.majorCharWidth = this.dom.measureCharMajor.clientWidth; + this.nodesData.update(dataArray); }; /** - * Snap a date to a rounded value. - * The snap intervals are dependent on the current scale and step. - * @param {Date} date the date to be snapped. - * @return {Date} snappedDate + * Return the positions of the nodes. */ - TimeAxis.prototype.snap = function(date) { - return this.step.snap(date); + Network.prototype.getPositions = function(ids) { + var dataArray = {}; + if (ids !== undefined) { + if (Array.isArray(ids) == true) { + for (var i = 0; i < ids.length; i++) { + if (this.nodes[ids[i]] !== undefined) { + var node = this.nodes[ids[i]]; + dataArray[ids[i]] = {x: Math.round(node.x), y: Math.round(node.y)}; + } + } + } + else { + if (this.nodes[ids] !== undefined) { + var node = this.nodes[ids]; + dataArray[ids] = {x: Math.round(node.x), y: Math.round(node.y)}; + } + } + } + else { + for (var nodeId in this.nodes) { + if (this.nodes.hasOwnProperty(nodeId)) { + var node = this.nodes[nodeId]; + dataArray[nodeId] = {x: Math.round(node.x), y: Math.round(node.y)}; + } + } + } + return dataArray; }; - module.exports = TimeAxis; -/***/ }, -/* 38 */ -/***/ function(module, exports, __webpack_require__) { - - var moment = __webpack_require__(2); - var DateUtil = __webpack_require__(24); - var util = __webpack_require__(1); - /** - * @constructor TimeStep - * The class TimeStep is an iterator for dates. You provide a start date and an - * end date. The class itself determines the best scale (step size) based on the - * provided start Date, end Date, and minimumStep. - * - * If minimumStep is provided, the step size is chosen as close as possible - * to the minimumStep but larger than minimumStep. If minimumStep is not - * provided, the scale is set to 1 DAY. - * The minimumStep should correspond with the onscreen size of about 6 characters - * - * Alternatively, you can set a scale by hand. - * After creation, you can initialize the class by executing first(). Then you - * can iterate from the start date to the end date via next(). You can check if - * the end date is reached with the function hasNext(). After each step, you can - * retrieve the current date via getCurrent(). - * The TimeStep has scales ranging from milliseconds, seconds, minutes, hours, - * days, to years. - * - * Version: 1.2 + * Center a node in view. * - * @param {Date} [start] The start date, for example new Date(2010, 9, 21) - * or new Date(2010, 9, 21, 23, 45, 00) - * @param {Date} [end] The end date - * @param {Number} [minimumStep] Optional. Minimum step size in milliseconds + * @param {Number} nodeId + * @param {Number} [options] */ - function TimeStep(start, end, minimumStep, hiddenDates) { - // variables - this.current = new Date(); - this._start = new Date(); - this._end = new Date(); - - this.autoScale = true; - this.scale = 'day'; - this.step = 1; - - // initialize the range - this.setRange(start, end, minimumStep); + Network.prototype.focusOnNode = function (nodeId, options) { + if (this.nodes.hasOwnProperty(nodeId)) { + if (options === undefined) { + options = {}; + } + var nodePosition = {x: this.nodes[nodeId].x, y: this.nodes[nodeId].y}; + options.position = nodePosition; + options.lockedOnNode = nodeId; - // hidden Dates options - this.switchedDay = false; - this.switchedMonth = false; - this.switchedYear = false; - this.hiddenDates = hiddenDates; - if (hiddenDates === undefined) { - this.hiddenDates = []; + this.moveTo(options) } - - this.format = TimeStep.FORMAT; // default formatting - } - - // Time formatting - TimeStep.FORMAT = { - minorLabels: { - millisecond:'SSS', - second: 's', - minute: 'HH:mm', - hour: 'HH:mm', - weekday: 'ddd D', - day: 'D', - month: 'MMM', - year: 'YYYY' - }, - majorLabels: { - millisecond:'HH:mm:ss', - second: 'D MMMM HH:mm', - minute: 'ddd D MMMM', - hour: 'ddd D MMMM', - weekday: 'MMMM YYYY', - day: 'MMMM YYYY', - month: 'YYYY', - year: '' + else { + console.log("This nodeId cannot be found."); } }; /** - * Set custom formatting for the minor an major labels of the TimeStep. - * Both `minorLabels` and `majorLabels` are an Object with properties: - * 'millisecond, 'second, 'minute', 'hour', 'weekday, 'day, 'month, 'year'. - * @param {{minorLabels: Object, majorLabels: Object}} format + * + * @param {Object} options | options.offset = {x:Number, y:Number} // offset from the center in DOM pixels + * | options.scale = Number // scale to move to + * | options.position = {x:Number, y:Number} // position to move to + * | options.animation = {duration:Number, easingFunction:String} || Boolean // position to move to */ - TimeStep.prototype.setFormat = function (format) { - var defaultFormat = util.deepExtend({}, TimeStep.FORMAT); - this.format = util.deepExtend(defaultFormat, format); + Network.prototype.moveTo = function (options) { + if (options === undefined) { + options = {}; + return; + } + if (options.offset === undefined) {options.offset = {x: 0, y: 0}; } + if (options.offset.x === undefined) {options.offset.x = 0; } + if (options.offset.y === undefined) {options.offset.y = 0; } + if (options.scale === undefined) {options.scale = this._getScale(); } + if (options.position === undefined) {options.position = this._getTranslation();} + if (options.animation === undefined) {options.animation = {duration:0}; } + if (options.animation === false ) {options.animation = {duration:0}; } + if (options.animation === true ) {options.animation = {}; } + if (options.animation.duration === undefined) {options.animation.duration = 1000; } // default duration + if (options.animation.easingFunction === undefined) {options.animation.easingFunction = "easeInOutQuad"; } // default easing function + + this.animateView(options); }; /** - * Set a new range - * If minimumStep is provided, the step size is chosen as close as possible - * to the minimumStep but larger than minimumStep. If minimumStep is not - * provided, the scale is set to 1 DAY. - * The minimumStep should correspond with the onscreen size of about 6 characters - * @param {Date} [start] The start date and time. - * @param {Date} [end] The end date and time. - * @param {int} [minimumStep] Optional. Minimum step size in milliseconds + * + * @param {Object} options | options.offset = {x:Number, y:Number} // offset from the center in DOM pixels + * | options.time = Number // animation time in milliseconds + * | options.scale = Number // scale to animate to + * | options.position = {x:Number, y:Number} // position to animate to + * | options.easingFunction = String // linear, easeInQuad, easeOutQuad, easeInOutQuad, + * // easeInCubic, easeOutCubic, easeInOutCubic, + * // easeInQuart, easeOutQuart, easeInOutQuart, + * // easeInQuint, easeOutQuint, easeInOutQuint */ - TimeStep.prototype.setRange = function(start, end, minimumStep) { - if (!(start instanceof Date) || !(end instanceof Date)) { - throw "No legal start or end date in method setRange"; + Network.prototype.animateView = function (options) { + if (options === undefined) { + options = {}; + return; } - this._start = (start != undefined) ? new Date(start.valueOf()) : new Date(); - this._end = (end != undefined) ? new Date(end.valueOf()) : new Date(); + // release if something focussed on the node + this.releaseNode(); + if (options.locked == true) { + this.lockedOnNodeId = options.lockedOnNode; + this.lockedOnNodeOffset = options.offset; + } - if (this.autoScale) { - this.setMinimumStep(minimumStep); + // forcefully complete the old animation if it was still running + if (this.easingTime != 0) { + this._transitionRedraw(1); // by setting easingtime to 1, we finish the animation. } - }; - /** - * Set the range iterator to the start date. - */ - TimeStep.prototype.first = function() { - this.current = new Date(this._start.valueOf()); - this.roundToMinor(); - }; + this.sourceScale = this._getScale(); + this.sourceTranslation = this._getTranslation(); + this.targetScale = options.scale; - /** - * Round the current date to the first minor date value - * This must be executed once when the current date is set to start Date - */ - TimeStep.prototype.roundToMinor = function() { - // round to floor - // IMPORTANT: we have no breaks in this switch! (this is no bug) - // noinspection FallThroughInSwitchStatementJS - switch (this.scale) { - case 'year': - this.current.setFullYear(this.step * Math.floor(this.current.getFullYear() / this.step)); - this.current.setMonth(0); - case 'month': this.current.setDate(1); - case 'day': // intentional fall through - case 'weekday': this.current.setHours(0); - case 'hour': this.current.setMinutes(0); - case 'minute': this.current.setSeconds(0); - case 'second': this.current.setMilliseconds(0); - //case 'millisecond': // nothing to do for milliseconds - } + // set the scale so the viewCenter is based on the correct zoom level. This is overridden in the transitionRedraw + // but at least then we'll have the target transition + this._setScale(this.targetScale); + var viewCenter = this.DOMtoCanvas({x: 0.5 * this.frame.canvas.clientWidth, y: 0.5 * this.frame.canvas.clientHeight}); + var distanceFromCenter = { // offset from view, distance view has to change by these x and y to center the node + x: viewCenter.x - options.position.x, + y: viewCenter.y - options.position.y + }; + this.targetTranslation = { + x: this.sourceTranslation.x + distanceFromCenter.x * this.targetScale + options.offset.x, + y: this.sourceTranslation.y + distanceFromCenter.y * this.targetScale + options.offset.y + }; - if (this.step != 1) { - // round down to the first minor value that is a multiple of the current step size - switch (this.scale) { - case 'millisecond': this.current.setMilliseconds(this.current.getMilliseconds() - this.current.getMilliseconds() % this.step); break; - case 'second': this.current.setSeconds(this.current.getSeconds() - this.current.getSeconds() % this.step); break; - case 'minute': this.current.setMinutes(this.current.getMinutes() - this.current.getMinutes() % this.step); break; - case 'hour': this.current.setHours(this.current.getHours() - this.current.getHours() % this.step); break; - case 'weekday': // intentional fall through - case 'day': this.current.setDate((this.current.getDate()-1) - (this.current.getDate()-1) % this.step + 1); break; - case 'month': this.current.setMonth(this.current.getMonth() - this.current.getMonth() % this.step); break; - case 'year': this.current.setFullYear(this.current.getFullYear() - this.current.getFullYear() % this.step); break; - default: break; + // if the time is set to 0, don't do an animation + if (options.animation.duration == 0) { + if (this.lockedOnNodeId != null) { + this._classicRedraw = this._redraw; + this._redraw = this._lockedRedraw; + } + else { + this._setScale(this.targetScale); + this._setTranslation(this.targetTranslation.x, this.targetTranslation.y); + this._redraw(); } } + else { + this.animating = true; + this.animationSpeed = 1 / (this.renderRefreshRate * options.animation.duration * 0.001) || 1 / this.renderRefreshRate; + this.animationEasingFunction = options.animation.easingFunction; + this._classicRedraw = this._redraw; + this._redraw = this._transitionRedraw; + this._redraw(); + this.start(); + } }; /** - * Check if the there is a next step - * @return {boolean} true if the current date has not passed the end date + * used to animate smoothly by hijacking the redraw function. + * @private */ - TimeStep.prototype.hasNext = function () { - return (this.current.valueOf() <= this._end.valueOf()); - }; + Network.prototype._lockedRedraw = function () { + var nodePosition = {x: this.nodes[this.lockedOnNodeId].x, y: this.nodes[this.lockedOnNodeId].y}; + var viewCenter = this.DOMtoCanvas({x: 0.5 * this.frame.canvas.clientWidth, y: 0.5 * this.frame.canvas.clientHeight}); + var distanceFromCenter = { // offset from view, distance view has to change by these x and y to center the node + x: viewCenter.x - nodePosition.x, + y: viewCenter.y - nodePosition.y + }; + var sourceTranslation = this._getTranslation(); + var targetTranslation = { + x: sourceTranslation.x + distanceFromCenter.x * this.scale + this.lockedOnNodeOffset.x, + y: sourceTranslation.y + distanceFromCenter.y * this.scale + this.lockedOnNodeOffset.y + }; + + this._setTranslation(targetTranslation.x,targetTranslation.y); + this._classicRedraw(); + } + + Network.prototype.releaseNode = function () { + if (this.lockedOnNodeId != null) { + this._redraw = this._classicRedraw; + this.lockedOnNodeId = null; + this.lockedOnNodeOffset = null; + } + } /** - * Do the next step + * + * @param easingTime + * @private */ - TimeStep.prototype.next = function() { - var prev = this.current.valueOf(); + Network.prototype._transitionRedraw = function (easingTime) { + this.easingTime = easingTime || this.easingTime + this.animationSpeed; + this.easingTime += this.animationSpeed; - // Two cases, needed to prevent issues with switching daylight savings - // (end of March and end of October) - if (this.current.getMonth() < 6) { - switch (this.scale) { - case 'millisecond': + var progress = util.easingFunctions[this.animationEasingFunction](this.easingTime); - this.current = new Date(this.current.valueOf() + this.step); break; - case 'second': this.current = new Date(this.current.valueOf() + this.step * 1000); break; - case 'minute': this.current = new Date(this.current.valueOf() + this.step * 1000 * 60); break; - case 'hour': - this.current = new Date(this.current.valueOf() + this.step * 1000 * 60 * 60); - // in case of skipping an hour for daylight savings, adjust the hour again (else you get: 0h 5h 9h ... instead of 0h 4h 8h ...) - var h = this.current.getHours(); - this.current.setHours(h - (h % this.step)); - break; - case 'weekday': // intentional fall through - case 'day': this.current.setDate(this.current.getDate() + this.step); break; - case 'month': this.current.setMonth(this.current.getMonth() + this.step); break; - case 'year': this.current.setFullYear(this.current.getFullYear() + this.step); break; - default: break; - } - } - else { - switch (this.scale) { - case 'millisecond': this.current = new Date(this.current.valueOf() + this.step); break; - case 'second': this.current.setSeconds(this.current.getSeconds() + this.step); break; - case 'minute': this.current.setMinutes(this.current.getMinutes() + this.step); break; - case 'hour': this.current.setHours(this.current.getHours() + this.step); break; - case 'weekday': // intentional fall through - case 'day': this.current.setDate(this.current.getDate() + this.step); break; - case 'month': this.current.setMonth(this.current.getMonth() + this.step); break; - case 'year': this.current.setFullYear(this.current.getFullYear() + this.step); break; - default: break; - } - } + this._setScale(this.sourceScale + (this.targetScale - this.sourceScale) * progress); + this._setTranslation( + this.sourceTranslation.x + (this.targetTranslation.x - this.sourceTranslation.x) * progress, + this.sourceTranslation.y + (this.targetTranslation.y - this.sourceTranslation.y) * progress + ); - if (this.step != 1) { - // round down to the correct major value - switch (this.scale) { - case 'millisecond': if(this.current.getMilliseconds() < this.step) this.current.setMilliseconds(0); break; - case 'second': if(this.current.getSeconds() < this.step) this.current.setSeconds(0); break; - case 'minute': if(this.current.getMinutes() < this.step) this.current.setMinutes(0); break; - case 'hour': if(this.current.getHours() < this.step) this.current.setHours(0); break; - case 'weekday': // intentional fall through - case 'day': if(this.current.getDate() < this.step+1) this.current.setDate(1); break; - case 'month': if(this.current.getMonth() < this.step) this.current.setMonth(0); break; - case 'year': break; // nothing to do for year - default: break; - } - } + this._classicRedraw(); - // safety mechanism: if current time is still unchanged, move to the end - if (this.current.valueOf() == prev) { - this.current = new Date(this._end.valueOf()); + // cleanup + if (this.easingTime >= 1.0) { + this.animating = false; + this.easingTime = 0; + if (this.lockedOnNodeId != null) { + this._redraw = this._lockedRedraw; + } + else { + this._redraw = this._classicRedraw; + } + this.emit("animationFinished"); } - - DateUtil.stepOverHiddenDates(this, prev); }; + Network.prototype._classicRedraw = function () { + // placeholder function to be overloaded by animations; + }; /** - * Get the current datetime - * @return {Date} current The current date + * Returns true when the Network is active. + * @returns {boolean} */ - TimeStep.prototype.getCurrent = function() { - return this.current; + Network.prototype.isActive = function () { + return !this.activator || this.activator.active; }; + /** - * Set a custom scale. Autoscaling will be disabled. - * For example setScale(SCALE.MINUTES, 5) will result - * in minor steps of 5 minutes, and major steps of an hour. - * - * @param {string} newScale - * A scale. Choose from 'millisecond, 'second, - * 'minute', 'hour', 'weekday, 'day, 'month, 'year'. - * @param {Number} newStep A step size, by default 1. Choose for - * example 1, 2, 5, or 10. + * Sets the scale + * @returns {Number} */ - TimeStep.prototype.setScale = function(newScale, newStep) { - this.scale = newScale; + Network.prototype.setScale = function () { + return this._setScale(); + }; - if (newStep > 0) { - this.step = newStep; - } - this.autoScale = false; + /** + * Returns the scale + * @returns {Number} + */ + Network.prototype.getScale = function () { + return this._getScale(); }; + /** - * Enable or disable autoscaling - * @param {boolean} enable If true, autoascaling is set true + * Returns the scale + * @returns {Number} */ - TimeStep.prototype.setAutoScale = function (enable) { - this.autoScale = enable; + Network.prototype.getCenterCoordinates = function () { + return this.DOMtoCanvas({x: 0.5 * this.frame.canvas.clientWidth, y: 0.5 * this.frame.canvas.clientHeight}); }; + Network.prototype.getBoundingBox = function(nodeId) { + if (this.nodes[nodeId] !== undefined) { + return this.nodes[nodeId].boundingBox; + } + } + + module.exports = Network; + + +/***/ }, +/* 37 */ +/***/ function(module, exports, __webpack_require__) { + + var util = __webpack_require__(1); + var Node = __webpack_require__(40); + /** - * Automatically determine the scale that bests fits the provided minimum step - * @param {Number} [minimumStep] The minimum step size in milliseconds + * @class Edge + * + * A edge connects two nodes + * @param {Object} properties Object with properties. Must contain + * At least properties from and to. + * Available properties: 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 */ - TimeStep.prototype.setMinimumStep = function(minimumStep) { - if (minimumStep == undefined) { - return; + function Edge (properties, network, networkConstants) { + if (!network) { + throw "No network provided"; } + var fields = ['edges','physics']; + var constants = util.selectiveBridgeObject(fields,networkConstants); + this.options = constants.edges; + this.physics = constants.physics; + this.options['smoothCurves'] = networkConstants['smoothCurves']; - //var b = asc + ds; - var stepYear = (1000 * 60 * 60 * 24 * 30 * 12); - var stepMonth = (1000 * 60 * 60 * 24 * 30); - var stepDay = (1000 * 60 * 60 * 24); - var stepHour = (1000 * 60 * 60); - var stepMinute = (1000 * 60); - var stepSecond = (1000); - var stepMillisecond= (1); + this.network = network; - // find the smallest step that is larger than the provided minimumStep - if (stepYear*1000 > minimumStep) {this.scale = 'year'; this.step = 1000;} - if (stepYear*500 > minimumStep) {this.scale = 'year'; this.step = 500;} - if (stepYear*100 > minimumStep) {this.scale = 'year'; this.step = 100;} - if (stepYear*50 > minimumStep) {this.scale = 'year'; this.step = 50;} - if (stepYear*10 > minimumStep) {this.scale = 'year'; this.step = 10;} - if (stepYear*5 > minimumStep) {this.scale = 'year'; this.step = 5;} - if (stepYear > minimumStep) {this.scale = 'year'; this.step = 1;} - if (stepMonth*3 > minimumStep) {this.scale = 'month'; this.step = 3;} - if (stepMonth > minimumStep) {this.scale = 'month'; this.step = 1;} - if (stepDay*5 > minimumStep) {this.scale = 'day'; this.step = 5;} - if (stepDay*2 > minimumStep) {this.scale = 'day'; this.step = 2;} - if (stepDay > minimumStep) {this.scale = 'day'; this.step = 1;} - if (stepDay/2 > minimumStep) {this.scale = 'weekday'; this.step = 1;} - if (stepHour*4 > minimumStep) {this.scale = 'hour'; this.step = 4;} - if (stepHour > minimumStep) {this.scale = 'hour'; this.step = 1;} - if (stepMinute*15 > minimumStep) {this.scale = 'minute'; this.step = 15;} - if (stepMinute*10 > minimumStep) {this.scale = 'minute'; this.step = 10;} - if (stepMinute*5 > minimumStep) {this.scale = 'minute'; this.step = 5;} - if (stepMinute > minimumStep) {this.scale = 'minute'; this.step = 1;} - if (stepSecond*15 > minimumStep) {this.scale = 'second'; this.step = 15;} - if (stepSecond*10 > minimumStep) {this.scale = 'second'; this.step = 10;} - if (stepSecond*5 > minimumStep) {this.scale = 'second'; this.step = 5;} - if (stepSecond > minimumStep) {this.scale = 'second'; this.step = 1;} - if (stepMillisecond*200 > minimumStep) {this.scale = 'millisecond'; this.step = 200;} - if (stepMillisecond*100 > minimumStep) {this.scale = 'millisecond'; this.step = 100;} - if (stepMillisecond*50 > minimumStep) {this.scale = 'millisecond'; this.step = 50;} - if (stepMillisecond*10 > minimumStep) {this.scale = 'millisecond'; this.step = 10;} - if (stepMillisecond*5 > minimumStep) {this.scale = 'millisecond'; this.step = 5;} - if (stepMillisecond > minimumStep) {this.scale = 'millisecond'; this.step = 1;} - }; + // initialize variables + this.id = undefined; + this.fromId = undefined; + this.toId = undefined; + this.title = undefined; + this.widthSelected = this.options.width * this.options.widthSelectionMultiplier; + this.value = undefined; + this.selected = false; + this.hover = false; + this.labelDimensions = {top:0,left:0,width:0,height:0,yLine:0}; // could be cached + this.dirtyLabel = true; + + this.from = null; // a node + this.to = null; // a node + this.via = null; // a temp node + + this.fromBackup = null; // used to clean up after reconnect + this.toBackup = null;; // used to clean up after reconnect + + // we use this to be able to reconnect the edge to a cluster if its node is put into a cluster + // by storing the original information we can revert to the original connection when the cluser is opened. + this.originalFromId = []; + this.originalToId = []; + + this.connected = false; + + this.widthFixed = false; + this.lengthFixed = false; + + this.setProperties(properties); + + this.controlNodesEnabled = false; + this.controlNodes = {from:null, to:null, positions:{}}; + this.connectedNode = null; + } /** - * Snap a date to a rounded value. - * The snap intervals are dependent on the current scale and step. - * @param {Date} date the date to be snapped. - * @return {Date} snappedDate + * Set or overwrite properties for the edge + * @param {Object} properties an object with properties + * @param {Object} constants and object with default, global properties */ - TimeStep.prototype.snap = function(date) { - var clone = new Date(date.valueOf()); - - if (this.scale == 'year') { - var year = clone.getFullYear() + Math.round(clone.getMonth() / 12); - clone.setFullYear(Math.round(year / this.step) * this.step); - clone.setMonth(0); - clone.setDate(0); - clone.setHours(0); - clone.setMinutes(0); - clone.setSeconds(0); - clone.setMilliseconds(0); + Edge.prototype.setProperties = function(properties) { + if (!properties) { + return; } - else if (this.scale == 'month') { - if (clone.getDate() > 15) { - clone.setDate(1); - clone.setMonth(clone.getMonth() + 1); - // important: first set Date to 1, after that change the month. - } - else { - clone.setDate(1); - } - clone.setHours(0); - clone.setMinutes(0); - clone.setSeconds(0); - clone.setMilliseconds(0); - } - else if (this.scale == 'day') { - //noinspection FallthroughInSwitchStatementJS - switch (this.step) { - case 5: - case 2: - clone.setHours(Math.round(clone.getHours() / 24) * 24); break; - default: - clone.setHours(Math.round(clone.getHours() / 12) * 12); break; - } - clone.setMinutes(0); - clone.setSeconds(0); - clone.setMilliseconds(0); - } - else if (this.scale == 'weekday') { - //noinspection FallthroughInSwitchStatementJS - switch (this.step) { - case 5: - case 2: - clone.setHours(Math.round(clone.getHours() / 12) * 12); break; - default: - clone.setHours(Math.round(clone.getHours() / 6) * 6); break; - } - clone.setMinutes(0); - clone.setSeconds(0); - clone.setMilliseconds(0); - } - else if (this.scale == 'hour') { - switch (this.step) { - case 4: - clone.setMinutes(Math.round(clone.getMinutes() / 60) * 60); break; - default: - clone.setMinutes(Math.round(clone.getMinutes() / 30) * 30); break; - } - clone.setSeconds(0); - clone.setMilliseconds(0); - } else if (this.scale == 'minute') { - //noinspection FallthroughInSwitchStatementJS - switch (this.step) { - case 15: - case 10: - clone.setMinutes(Math.round(clone.getMinutes() / 5) * 5); - clone.setSeconds(0); - break; - case 5: - clone.setSeconds(Math.round(clone.getSeconds() / 60) * 60); break; - default: - clone.setSeconds(Math.round(clone.getSeconds() / 30) * 30); break; - } - clone.setMilliseconds(0); - } - else if (this.scale == 'second') { - //noinspection FallthroughInSwitchStatementJS - switch (this.step) { - case 15: - case 10: - clone.setSeconds(Math.round(clone.getSeconds() / 5) * 5); - clone.setMilliseconds(0); - break; - case 5: - clone.setMilliseconds(Math.round(clone.getMilliseconds() / 1000) * 1000); break; - default: - clone.setMilliseconds(Math.round(clone.getMilliseconds() / 500) * 500); break; - } - } - else if (this.scale == 'millisecond') { - var step = this.step > 5 ? this.step / 2 : 1; - clone.setMilliseconds(Math.round(clone.getMilliseconds() / step) * step); - } - - return clone; - }; + var fields = ['style','fontSize','fontFace','fontColor','fontFill','fontStrokeWidth','fontStrokeColor','width', + 'widthSelectionMultiplier','hoverWidth','arrowScaleFactor','dash','inheritColor','labelAlignment' + ]; + util.selectiveDeepExtend(fields, this.options, properties); - /** - * Check if the current value is a major value (for example when the step - * is DAY, a major value is each first day of the MONTH) - * @return {boolean} true if current date is major, else false. - */ - TimeStep.prototype.isMajor = function() { - if (this.switchedYear == true) { - this.switchedYear = false; - switch (this.scale) { - case 'year': - case 'month': - case 'weekday': - case 'day': - case 'hour': - case 'minute': - case 'second': - case 'millisecond': - return true; - default: - return false; - } - } - else if (this.switchedMonth == true) { - this.switchedMonth = false; - switch (this.scale) { - case 'weekday': - case 'day': - case 'hour': - case 'minute': - case 'second': - case 'millisecond': - return true; - default: - return false; + if (properties.from !== undefined) {this.fromId = properties.from;} + if (properties.to !== undefined) {this.toId = properties.to;} + + if (properties.id !== undefined) {this.id = properties.id;} + if (properties.label !== undefined) {this.label = properties.label; this.dirtyLabel = true;} + + if (properties.title !== undefined) {this.title = properties.title;} + if (properties.value !== undefined) {this.value = properties.value;} + if (properties.length !== undefined) {this.physics.springLength = properties.length;} + + if (properties.color !== undefined) { + this.options.inheritColor = false; + if (util.isString(properties.color)) { + this.options.color.color = properties.color; + this.options.color.highlight = properties.color; } - } - else if (this.switchedDay == true) { - this.switchedDay = false; - switch (this.scale) { - case 'millisecond': - case 'second': - case 'minute': - case 'hour': - return true; - default: - return false; + else { + if (properties.color.color !== undefined) {this.options.color.color = properties.color.color;} + if (properties.color.highlight !== undefined) {this.options.color.highlight = properties.color.highlight;} + if (properties.color.hover !== undefined) {this.options.color.hover = properties.color.hover;} } } - switch (this.scale) { - case 'millisecond': - return (this.current.getMilliseconds() == 0); - case 'second': - return (this.current.getSeconds() == 0); - case 'minute': - return (this.current.getHours() == 0) && (this.current.getMinutes() == 0); - case 'hour': - return (this.current.getHours() == 0); - case 'weekday': // intentional fall through - case 'day': - return (this.current.getDate() == 1); - case 'month': - return (this.current.getMonth() == 0); - case 'year': - return false; - default: - return false; - } - }; + // A node is connected when it has a from and to node. + this.connect(); + this.widthFixed = this.widthFixed || (properties.width !== undefined); + this.lengthFixed = this.lengthFixed || (properties.length !== undefined); - /** - * Returns formatted text for the minor axislabel, depending on the current - * date and the scale. For example when scale is MINUTE, the current time is - * formatted as "hh:mm". - * @param {Date} [date] custom date. if not provided, current date is taken - */ - TimeStep.prototype.getLabelMinor = function(date) { - if (date == undefined) { - date = this.current; - } + this.widthSelected = this.options.width* this.options.widthSelectionMultiplier; - var format = this.format.minorLabels[this.scale]; - return (format && format.length > 0) ? moment(date).format(format) : ''; + // set draw method based on style + switch (this.options.style) { + case 'line': this.draw = this._drawLine; break; + case 'arrow': this.draw = this._drawArrow; break; + case 'arrow-center': this.draw = this._drawArrowCenter; break; + case 'dash-line': this.draw = this._drawDashLine; break; + default: this.draw = this._drawLine; break; + } + }; /** - * Returns formatted text for the major axis label, depending on the current - * date and the scale. For example when scale is MINUTE, the major scale is - * hours, and the hour will be formatted as "hh". - * @param {Date} [date] custom date. if not provided, current date is taken + * Connect an edge to its nodes */ - TimeStep.prototype.getLabelMajor = function(date) { - if (date == undefined) { - date = this.current; - } - - var format = this.format.majorLabels[this.scale]; - return (format && format.length > 0) ? moment(date).format(format) : ''; - }; + Edge.prototype.connect = function () { + this.disconnect(); - TimeStep.prototype.getClassName = function() { - var m = moment(this.current); - var date = m.locale ? m.locale('en') : m.lang('en'); // old versions of moment have .lang() function - var step = this.step; + this.from = this.network.nodes[this.fromId] || null; + this.to = this.network.nodes[this.toId] || null; + this.connected = (this.from && this.to); - function even(value) { - return (value / step % 2 == 0) ? ' even' : ' odd'; + if (this.connected) { + this.from.attachEdge(this); + this.to.attachEdge(this); } - - function today(date) { - if (date.isSame(new Date(), 'day')) { - return ' today'; - } - if (date.isSame(moment().add(1, 'day'), 'day')) { - return ' tomorrow'; + else { + if (this.from) { + this.from.detachEdge(this); } - if (date.isSame(moment().add(-1, 'day'), 'day')) { - return ' yesterday'; + if (this.to) { + this.to.detachEdge(this); } - return ''; - } - - function currentWeek(date) { - return date.isSame(new Date(), 'week') ? ' current-week' : ''; } + }; - function currentMonth(date) { - return date.isSame(new Date(), 'month') ? ' current-month' : ''; + /** + * Disconnect an edge from its nodes + */ + Edge.prototype.disconnect = function () { + if (this.from) { + this.from.detachEdge(this); + this.from = null; } - - function currentYear(date) { - return date.isSame(new Date(), 'year') ? ' current-year' : ''; + if (this.to) { + this.to.detachEdge(this); + this.to = null; } - switch (this.scale) { - case 'millisecond': - return even(date.milliseconds()).trim(); - - case 'second': - return even(date.seconds()).trim(); - - case 'minute': - return even(date.minutes()).trim(); - - case 'hour': - var hours = date.hours(); - if (this.step == 4) { - hours = hours + '-' + (hours + 4); - } - return hours + 'h' + today(date) + even(date.hours()); - - case 'weekday': - return date.format('dddd').toLowerCase() + - today(date) + currentWeek(date) + even(date.date()); - - case 'day': - var day = date.date(); - var month = date.format('MMMM').toLowerCase(); - return 'day' + day + ' ' + month + currentMonth(date) + even(day - 1); - - case 'month': - return date.format('MMMM').toLowerCase() + - currentMonth(date) + even(date.month()); - - case 'year': - var year = date.year(); - return 'year' + year + currentYear(date)+ even(year); - - default: - return ''; - } + this.connected = false; }; - module.exports = TimeStep; - - -/***/ }, -/* 39 */ -/***/ function(module, exports, __webpack_require__) { - - var util = __webpack_require__(1); - var Component = __webpack_require__(23); - var moment = __webpack_require__(2); - var locales = __webpack_require__(40); - /** - * A current time bar - * @param {{range: Range, dom: Object, domProps: Object}} body - * @param {Object} [options] Available parameters: - * {Boolean} [showCurrentTime] - * @constructor CurrentTime - * @extends Component + * get the title of this edge. + * @return {string} title The title of the edge, or undefined when no title + * has been set. */ - function CurrentTime (body, options) { - this.body = body; - - // default options - this.defaultOptions = { - showCurrentTime: true, - - locales: locales, - locale: 'en' - }; - this.options = util.extend({}, this.defaultOptions); - this.offset = 0; - - this._create(); - - this.setOptions(options); - } + Edge.prototype.getTitle = function() { + return typeof this.title === "function" ? this.title() : this.title; + }; - CurrentTime.prototype = new Component(); /** - * Create the HTML DOM for the current time bar - * @private + * Retrieve the value of the edge. Can be undefined + * @return {Number} value */ - CurrentTime.prototype._create = function() { - var bar = document.createElement('div'); - bar.className = 'currenttime'; - bar.style.position = 'absolute'; - bar.style.top = '0px'; - bar.style.height = '100%'; - - this.bar = bar; + Edge.prototype.getValue = function() { + return this.value; }; /** - * Destroy the CurrentTime bar + * Adjust the value range of the edge. The edge will adjust it's width + * based on its value. + * @param {Number} min + * @param {Number} max */ - CurrentTime.prototype.destroy = function () { - this.options.showCurrentTime = false; - this.redraw(); // will remove the bar from the DOM and stop refreshing - - this.body = null; + Edge.prototype.setValueRange = function(min, max) { + if (!this.widthFixed && this.value !== undefined) { + var scale = (this.options.widthMax - this.options.widthMin) / (max - min); + this.options.width= (this.value - min) * scale + this.options.widthMin; + this.widthSelected = this.options.width* this.options.widthSelectionMultiplier; + } }; /** - * Set options for the component. Options will be merged in current options. - * @param {Object} options Available parameters: - * {boolean} [showCurrentTime] + * 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 */ - CurrentTime.prototype.setOptions = function(options) { - if (options) { - // copy all options that we know - util.selectiveExtend(['showCurrentTime', 'locale', 'locales'], this.options, options); - } + Edge.prototype.draw = function(ctx) { + throw "Method draw not initialized in edge"; }; /** - * Repaint the component - * @return {boolean} Returns true if the component is resized + * 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 */ - CurrentTime.prototype.redraw = function() { - if (this.options.showCurrentTime) { - var parent = this.body.dom.backgroundVertical; - if (this.bar.parentNode != parent) { - // attach to the dom - if (this.bar.parentNode) { - this.bar.parentNode.removeChild(this.bar); - } - parent.appendChild(this.bar); - - this.start(); - } - - var now = new Date(new Date().valueOf() + this.offset); - var x = this.body.util.toScreen(now); + Edge.prototype.isOverlappingWith = function(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 locale = this.options.locales[this.options.locale]; - var title = locale.current + ' ' + locale.time + ': ' + moment(now).format('dddd, MMMM Do YYYY, H:mm:ss'); - title = title.charAt(0).toUpperCase() + title.substring(1); + var dist = this._getDistanceToEdge(xFrom, yFrom, xTo, yTo, xObj, yObj); - this.bar.style.left = x + 'px'; - this.bar.title = title; + return (dist < distMax); } else { - // remove the line from the DOM - if (this.bar.parentNode) { - this.bar.parentNode.removeChild(this.bar); - } - this.stop(); + return false } + }; - return false; + Edge.prototype._getColor = function() { + var colorObj = this.options.color; + if (this.options.inheritColor == "to") { + colorObj = { + highlight: this.to.options.color.highlight.border, + hover: this.to.options.color.hover.border, + color: this.to.options.color.border + }; + } + else if (this.options.inheritColor == "from" || this.options.inheritColor == true) { + colorObj = { + highlight: this.from.options.color.highlight.border, + hover: this.from.options.color.hover.border, + color: this.from.options.color.border + }; + } + + if (this.selected == true) {return colorObj.highlight;} + else if (this.hover == true) {return colorObj.hover;} + else {return colorObj.color;} }; + /** - * Start auto refreshing the current time bar + * Redraw a edge as a line + * Draw this edge in the given canvas + * The 2d context of a HTML canvas can be retrieved by canvas.getContext("2d"); + * @param {CanvasRenderingContext2D} ctx + * @private */ - CurrentTime.prototype.start = function() { - var me = this; - - function update () { - me.stop(); - - // determine interval to refresh - var scale = me.body.range.conversion(me.body.domProps.center.width).scale; - var interval = 1 / scale / 10; - if (interval < 30) interval = 30; - if (interval > 1000) interval = 1000; + Edge.prototype._drawLine = function(ctx) { + // set style + ctx.strokeStyle = this._getColor(); + ctx.lineWidth = this._getLineWidth(); - me.redraw(); + if (this.from != this.to) { + // draw line + var via = this._line(ctx); - // start a timer to adjust for the new time - me.currentTimeTimer = setTimeout(update, interval); + // draw label + var point; + if (this.label) { + if (this.options.smoothCurves.enabled == true && via != null) { + var midpointX = 0.5*(0.5*(this.from.x + via.x) + 0.5*(this.to.x + via.x)); + var midpointY = 0.5*(0.5*(this.from.y + via.y) + 0.5*(this.to.y + via.y)); + point = {x:midpointX, y:midpointY}; + } + else { + point = this._pointOnLine(0.5); + } + this._label(ctx, this.label, point.x, point.y); + } + } + else { + var x, y; + var radius = this.physics.springLength / 4; + var node = this.from; + if (!node.width) { + node.resize(ctx); + } + if (node.width > node.height) { + x = node.x + node.width / 2; + y = node.y - radius; + } + else { + x = node.x + radius; + y = node.y - node.height / 2; + } + this._circle(ctx, x, y, radius); + point = this._pointOnCircle(x, y, radius, 0.5); + this._label(ctx, this.label, point.x, point.y); } - - update(); }; /** - * Stop auto refreshing the current time bar + * Get the line width of the edge. Depends on width and whether one of the + * connected nodes is selected. + * @return {Number} width + * @private */ - CurrentTime.prototype.stop = function() { - if (this.currentTimeTimer !== undefined) { - clearTimeout(this.currentTimeTimer); - delete this.currentTimeTimer; + Edge.prototype._getLineWidth = function() { + if (this.selected == true) { + return Math.max(Math.min(this.widthSelected, this.options.widthMax), 0.3*this.networkScaleInv); + } + else { + if (this.hover == true) { + return Math.max(Math.min(this.options.hoverWidth, this.options.widthMax), 0.3*this.networkScaleInv); + } + else { + return Math.max(this.options.width, 0.3*this.networkScaleInv); + } } }; - /** - * Set a current time. This can be used for example to ensure that a client's - * time is synchronized with a shared server time. - * @param {Date | String | Number} time A Date, unix timestamp, or - * ISO date string. - */ - CurrentTime.prototype.setCurrentTime = function(time) { - var t = util.convert(time, 'Date').valueOf(); - var now = new Date().valueOf(); - this.offset = t - now; - this.redraw(); - }; - - /** - * Get the current time. - * @return {Date} Returns the current time. - */ - CurrentTime.prototype.getCurrentTime = function() { - return new Date(new Date().valueOf() + this.offset); - }; - - module.exports = CurrentTime; - + Edge.prototype._getViaCoordinates = function () { + if (this.options.smoothCurves.dynamic == true && this.options.smoothCurves.enabled == true ) { + return this.via; + } + else if (this.options.smoothCurves.enabled == false) { + return {x:0,y:0}; + } + else { + var xVia = null; + var yVia = null; + var factor = this.options.smoothCurves.roundness; + var type = this.options.smoothCurves.type; -/***/ }, -/* 40 */ -/***/ function(module, exports, __webpack_require__) { + var dx = Math.abs(this.from.x - this.to.x); + var dy = Math.abs(this.from.y - this.to.y); + if (type == 'discrete' || type == 'diagonalCross') { + if (Math.abs(this.from.x - this.to.x) < Math.abs(this.from.y - this.to.y)) { + if (this.from.y > this.to.y) { + if (this.from.x < this.to.x) { + xVia = this.from.x + factor * dy; + yVia = this.from.y - factor * dy; + } + else if (this.from.x > this.to.x) { + xVia = this.from.x - factor * dy; + yVia = this.from.y - factor * dy; + } + } + else if (this.from.y < this.to.y) { + if (this.from.x < this.to.x) { + xVia = this.from.x + factor * dy; + yVia = this.from.y + factor * dy; + } + else if (this.from.x > this.to.x) { + xVia = this.from.x - factor * dy; + yVia = this.from.y + factor * dy; + } + } + if (type == "discrete") { + xVia = dx < factor * dy ? this.from.x : xVia; + } + } + else if (Math.abs(this.from.x - this.to.x) > Math.abs(this.from.y - this.to.y)) { + if (this.from.y > this.to.y) { + if (this.from.x < this.to.x) { + xVia = this.from.x + factor * dx; + yVia = this.from.y - factor * dx; + } + else if (this.from.x > this.to.x) { + xVia = this.from.x - factor * dx; + yVia = this.from.y - factor * dx; + } + } + else if (this.from.y < this.to.y) { + if (this.from.x < this.to.x) { + xVia = this.from.x + factor * dx; + yVia = this.from.y + factor * dx; + } + else if (this.from.x > this.to.x) { + xVia = this.from.x - factor * dx; + yVia = this.from.y + factor * dx; + } + } + if (type == "discrete") { + yVia = dy < factor * dx ? this.from.y : yVia; + } + } + } + else if (type == "straightCross") { + if (Math.abs(this.from.x - this.to.x) < Math.abs(this.from.y - this.to.y)) { // up - down + xVia = this.from.x; + if (this.from.y < this.to.y) { + yVia = this.to.y - (1 - factor) * dy; + } + else { + yVia = this.to.y + (1 - factor) * dy; + } + } + else if (Math.abs(this.from.x - this.to.x) > Math.abs(this.from.y - this.to.y)) { // left - right + if (this.from.x < this.to.x) { + xVia = this.to.x - (1 - factor) * dx; + } + else { + xVia = this.to.x + (1 - factor) * dx; + } + yVia = this.from.y; + } + } + else if (type == 'horizontal') { + if (this.from.x < this.to.x) { + xVia = this.to.x - (1 - factor) * dx; + } + else { + xVia = this.to.x + (1 - factor) * dx; + } + yVia = this.from.y; + } + else if (type == 'vertical') { + xVia = this.from.x; + if (this.from.y < this.to.y) { + yVia = this.to.y - (1 - factor) * dy; + } + else { + yVia = this.to.y + (1 - factor) * dy; + } + } + else { // continuous + if (Math.abs(this.from.x - this.to.x) < Math.abs(this.from.y - this.to.y)) { + if (this.from.y > this.to.y) { + if (this.from.x < this.to.x) { + // console.log(1) + xVia = this.from.x + factor * dy; + yVia = this.from.y - factor * dy; + xVia = this.to.x < xVia ? this.to.x : xVia; + } + else if (this.from.x > this.to.x) { + // console.log(2) + xVia = this.from.x - factor * dy; + yVia = this.from.y - factor * dy; + xVia = this.to.x > xVia ? this.to.x : xVia; + } + } + else if (this.from.y < this.to.y) { + if (this.from.x < this.to.x) { + // console.log(3) + xVia = this.from.x + factor * dy; + yVia = this.from.y + factor * dy; + xVia = this.to.x < xVia ? this.to.x : xVia; + } + else if (this.from.x > this.to.x) { + // console.log(4, this.from.x, this.to.x) + xVia = this.from.x - factor * dy; + yVia = this.from.y + factor * dy; + xVia = this.to.x > xVia ? this.to.x : xVia; + } + } + } + else if (Math.abs(this.from.x - this.to.x) > Math.abs(this.from.y - this.to.y)) { + if (this.from.y > this.to.y) { + if (this.from.x < this.to.x) { + // console.log(5) + xVia = this.from.x + factor * dx; + yVia = this.from.y - factor * dx; + yVia = this.to.y > yVia ? this.to.y : yVia; + } + else if (this.from.x > this.to.x) { + // console.log(6) + xVia = this.from.x - factor * dx; + yVia = this.from.y - factor * dx; + yVia = this.to.y > yVia ? this.to.y : yVia; + } + } + else if (this.from.y < this.to.y) { + if (this.from.x < this.to.x) { + // console.log(7) + xVia = this.from.x + factor * dx; + yVia = this.from.y + factor * dx; + yVia = this.to.y < yVia ? this.to.y : yVia; + } + else if (this.from.x > this.to.x) { + // console.log(8) + xVia = this.from.x - factor * dx; + yVia = this.from.y + factor * dx; + yVia = this.to.y < yVia ? this.to.y : yVia; + } + } + } + } - // English - exports['en'] = { - current: 'current', - time: 'time' - }; - exports['en_EN'] = exports['en']; - exports['en_US'] = exports['en']; - // Dutch - exports['nl'] = { - custom: 'aangepaste', - time: 'tijd' + return {x: xVia, y: yVia}; + } }; - exports['nl_NL'] = exports['nl']; - exports['nl_BE'] = exports['nl']; - - -/***/ }, -/* 41 */ -/***/ function(module, exports, __webpack_require__) { - - var Hammer = __webpack_require__(19); - var util = __webpack_require__(1); - var Component = __webpack_require__(23); - var moment = __webpack_require__(2); - var locales = __webpack_require__(40); - - /** - * A custom time bar - * @param {{range: Range, dom: Object}} body - * @param {Object} [options] Available parameters: - * {Boolean} [showCustomTime] - * @constructor CustomTime - * @extends Component - */ - - function CustomTime (body, options) { - this.body = body; - - // default options - this.defaultOptions = { - showCustomTime: false, - locales: locales, - locale: 'en' - }; - this.options = util.extend({}, this.defaultOptions); - - this.customTime = new Date(); - this.eventParams = {}; // stores state parameters while dragging the bar - - // create the DOM - this._create(); - - this.setOptions(options); - } - - CustomTime.prototype = new Component(); /** - * Set options for the component. Options will be merged in current options. - * @param {Object} options Available parameters: - * {boolean} [showCustomTime] + * Draw a line between two nodes + * @param {CanvasRenderingContext2D} ctx + * @private */ - CustomTime.prototype.setOptions = function(options) { - if (options) { - // copy all options that we know - util.selectiveExtend(['showCustomTime', 'locale', 'locales'], this.options, options); + Edge.prototype._line = function (ctx) { + // draw a straight line + ctx.beginPath(); + ctx.moveTo(this.from.x, this.from.y); + if (this.options.smoothCurves.enabled == true) { + if (this.options.smoothCurves.dynamic == false) { + var via = this._getViaCoordinates(); + if (via.x == null) { + ctx.lineTo(this.to.x, this.to.y); + ctx.stroke(); + return null; + } + else { + // this.via.x = via.x; + // this.via.y = via.y; + ctx.quadraticCurveTo(via.x,via.y,this.to.x, this.to.y); + ctx.stroke(); + return via; + } + } + else { + ctx.quadraticCurveTo(this.via.x,this.via.y,this.to.x, this.to.y); + ctx.stroke(); + return this.via; + } + } + else { + ctx.lineTo(this.to.x, this.to.y); + ctx.stroke(); + return null; } }; /** - * Create the DOM for the custom time + * Draw a line from a node to itself, a circle + * @param {CanvasRenderingContext2D} ctx + * @param {Number} x + * @param {Number} y + * @param {Number} radius * @private */ - CustomTime.prototype._create = function() { - var bar = document.createElement('div'); - bar.className = 'customtime'; - bar.style.position = 'absolute'; - bar.style.top = '0px'; - bar.style.height = '100%'; - this.bar = bar; - - var drag = document.createElement('div'); - drag.style.position = 'relative'; - drag.style.top = '0px'; - drag.style.left = '-10px'; - drag.style.height = '100%'; - drag.style.width = '20px'; - bar.appendChild(drag); - - // attach event listeners - this.hammer = Hammer(bar, { - prevent_default: true - }); - this.hammer.on('dragstart', this._onDragStart.bind(this)); - this.hammer.on('drag', this._onDrag.bind(this)); - this.hammer.on('dragend', this._onDragEnd.bind(this)); + Edge.prototype._circle = function (ctx, x, y, radius) { + // draw a circle + ctx.beginPath(); + ctx.arc(x, y, radius, 0, 2 * Math.PI, false); + ctx.stroke(); }; /** - * Destroy the CustomTime bar + * Draw label with white background and with the middle at (x, y) + * @param {CanvasRenderingContext2D} ctx + * @param {String} text + * @param {Number} x + * @param {Number} y + * @private */ - CustomTime.prototype.destroy = function () { - this.options.showCustomTime = false; - this.redraw(); // will remove the bar from the DOM - - this.hammer.enable(false); - this.hammer = null; + Edge.prototype._label = function (ctx, text, x, y) { + if (text) { + ctx.font = ((this.from.selected || this.to.selected) ? "bold " : "") + + this.options.fontSize + "px " + this.options.fontFace; + var yLine; - this.body = null; - }; + if (this.dirtyLabel == true) { + var lines = String(text).split('\n'); + var lineCount = lines.length; + var fontSize = Number(this.options.fontSize); + yLine = y + (1 - lineCount) / 2 * fontSize; - /** - * Repaint the component - * @return {boolean} Returns true if the component is resized - */ - CustomTime.prototype.redraw = function () { - if (this.options.showCustomTime) { - var parent = this.body.dom.backgroundVertical; - if (this.bar.parentNode != parent) { - // attach to the dom - if (this.bar.parentNode) { - this.bar.parentNode.removeChild(this.bar); + var 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; } - parent.appendChild(this.bar); - } - - var x = this.body.util.toScreen(this.customTime); - - var locale = this.options.locales[this.options.locale]; - var title = locale.time + ': ' + moment(this.customTime).format('dddd, MMMM Do YYYY, H:mm:ss'); - title = title.charAt(0).toUpperCase() + title.substring(1); + var height = this.options.fontSize * lineCount; + var left = x - width / 2; + var top = y - height / 2; - this.bar.style.left = x + 'px'; - this.bar.title = title; - } - else { - // remove the line from the DOM - if (this.bar.parentNode) { - this.bar.parentNode.removeChild(this.bar); + // cache + this.labelDimensions = {top:top,left:left,width:width,height:height,yLine:yLine}; } - } - - return false; - }; - /** - * Set custom time. - * @param {Date | number | string} time - */ - CustomTime.prototype.setCustomTime = function(time) { - this.customTime = util.convert(time, 'Date'); - this.redraw(); - }; + var yLine = this.labelDimensions.yLine; + + ctx.save(); + + if (this.options.labelAlignment != "horizontal"){ + ctx.translate(x, yLine); + this._rotateForLabelAlignment(ctx); + x = 0; + yLine = 0; + } - /** - * Retrieve the current custom time. - * @return {Date} customTime - */ - CustomTime.prototype.getCustomTime = function() { - return new Date(this.customTime.valueOf()); + + this._drawLabelRect(ctx); + this._drawLabelText(ctx,x,yLine, lines, lineCount, fontSize); + + ctx.restore(); + } }; /** - * Start moving horizontally - * @param {Event} event + * Rotates the canvas so the text is most readable + * @param {CanvasRenderingContext2D} ctx * @private */ - CustomTime.prototype._onDragStart = function(event) { - this.eventParams.dragging = true; - this.eventParams.customTime = this.customTime; + Edge.prototype._rotateForLabelAlignment = function(ctx) { + var dy = this.from.y - this.to.y; + var dx = this.from.x - this.to.x; + var angleInDegrees = Math.atan2(dy, dx); - event.stopPropagation(); - event.preventDefault(); + // rotate so label it is readable + if((angleInDegrees < -1 && dx < 0) || (angleInDegrees > 0 && dx < 0)){ + angleInDegrees = angleInDegrees + Math.PI; + } + + ctx.rotate(angleInDegrees); }; /** - * Perform moving operating. - * @param {Event} event + * Draws the label rectangle + * @param {CanvasRenderingContext2D} ctx + * @param {String} labelAlignment * @private */ - CustomTime.prototype._onDrag = function (event) { - if (!this.eventParams.dragging) return; - - var deltaX = event.gesture.deltaX, - x = this.body.util.toScreen(this.eventParams.customTime) + deltaX, - time = this.body.util.toTime(x); - - this.setCustomTime(time); - - // fire a timechange event - this.body.emitter.emit('timechange', { - time: new Date(this.customTime.valueOf()) - }); + Edge.prototype._drawLabelRect = function(ctx) { + if (this.options.fontFill !== undefined && this.options.fontFill !== null && this.options.fontFill !== "none") { + ctx.fillStyle = this.options.fontFill; + + var lineMargin = 2; - event.stopPropagation(); - event.preventDefault(); + if (this.options.labelAlignment == 'line-center') { + ctx.fillRect(-this.labelDimensions.width * 0.5, -this.labelDimensions.height * 0.5, this.labelDimensions.width, this.labelDimensions.height); + } + else if (this.options.labelAlignment == 'line-above') { + ctx.fillRect(-this.labelDimensions.width * 0.5, -(this.labelDimensions.height + lineMargin), this.labelDimensions.width, this.labelDimensions.height); + } + else if (this.options.labelAlignment == 'line-below') { + ctx.fillRect(-this.labelDimensions.width * 0.5, lineMargin, this.labelDimensions.width, this.labelDimensions.height); + } + else { + ctx.fillRect(this.labelDimensions.left, this.labelDimensions.top, this.labelDimensions.width, this.labelDimensions.height); + } + } }; /** - * Stop moving operating. - * @param {event} event + * Draws the label text + * @param {CanvasRenderingContext2D} ctx + * @param {Number} x + * @param {Number} yLine + * @param {Array} lines + * @param {Number} lineCount + * @param {Number} fontSize * @private */ - CustomTime.prototype._onDragEnd = function (event) { - if (!this.eventParams.dragging) return; - - // fire a timechanged event - this.body.emitter.emit('timechanged', { - time: new Date(this.customTime.valueOf()) - }); - - event.stopPropagation(); - event.preventDefault(); - }; - - module.exports = CustomTime; - + Edge.prototype._drawLabelText = function(ctx, x, yLine, lines, lineCount, fontSize) { + // draw text + ctx.fillStyle = this.options.fontColor || "black"; + ctx.textAlign = "center"; -/***/ }, -/* 42 */ -/***/ function(module, exports, __webpack_require__) { + // check for label alignment + if (this.options.labelAlignment != 'horizontal') { + var lineMargin = 2; + if (this.options.labelAlignment == 'line-above') { + ctx.textBaseline = "alphabetic"; + yLine -= 2 * lineMargin; // distance from edge, required because we use alphabetic. Alphabetic has less difference between browsers + } + else if (this.options.labelAlignment == 'line-below') { + 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 = "middle"; + } - var Emitter = __webpack_require__(11); - var Hammer = __webpack_require__(19); - var util = __webpack_require__(1); - var DataSet = __webpack_require__(7); - var DataView = __webpack_require__(9); - var Range = __webpack_require__(21); - var Core = __webpack_require__(25); - var TimeAxis = __webpack_require__(37); - var CurrentTime = __webpack_require__(39); - var CustomTime = __webpack_require__(41); - var LineGraph = __webpack_require__(43); + // check for strokeWidth + if (this.options.fontStrokeWidth > 0){ + ctx.lineWidth = this.options.fontStrokeWidth; + ctx.strokeStyle = this.options.fontStrokeColor; + ctx.lineJoin = 'round'; + } + for (var i = 0; i < lineCount; i++) { + if(this.options.fontStrokeWidth > 0){ + ctx.strokeText(lines[i], x, yLine); + } + ctx.fillText(lines[i], x, yLine); + yLine += fontSize; + } + }; /** - * Create a timeline visualization - * @param {HTMLElement} container - * @param {vis.DataSet | Array | google.visualization.DataTable} [items] - * @param {Object} [options] See Graph2d.setOptions for the available options. - * @constructor - * @extends Core + * Redraw a edge as a dashed line + * Draw this edge in the given canvas + * @author David Jordan + * @date 2012-08-08 + * The 2d context of a HTML canvas can be retrieved by canvas.getContext("2d"); + * @param {CanvasRenderingContext2D} ctx + * @private */ - function Graph2d (container, items, groups, options) { - // if the third element is options, the forth is groups (optionally); - if (!(Array.isArray(groups) || groups instanceof DataSet) && groups instanceof Object) { - var forthArgument = options; - options = groups; - groups = forthArgument; - } - - var me = this; - this.defaultOptions = { - start: null, - end: null, - - autoResize: true, - - orientation: 'bottom', - width: null, - height: null, - maxHeight: null, - minHeight: null - }; - this.options = util.deepExtend({}, this.defaultOptions); - - // Create the DOM, props, and emitter - this._create(container); - - // all components listed here will be repainted automatically - this.components = []; + Edge.prototype._drawDashLine = function(ctx) { + // set style + ctx.strokeStyle = this._getColor(); + ctx.lineWidth = this._getLineWidth(); - this.body = { - dom: this.dom, - domProps: this.props, - emitter: { - on: this.on.bind(this), - off: this.off.bind(this), - emit: this.emit.bind(this) - }, - hiddenDates: [], - util: { - snap: null, // will be specified after TimeAxis is created - toScreen: me._toScreen.bind(me), - toGlobalScreen: me._toGlobalScreen.bind(me), // this refers to the root.width - toTime: me._toTime.bind(me), - toGlobalTime : me._toGlobalTime.bind(me) + var via = null; + // only firefox and chrome support this method, else we use the legacy one. + if (ctx.setLineDash !== undefined) { + ctx.save(); + // configure the dash pattern + var pattern = [0]; + if (this.options.dash.length !== undefined && this.options.dash.gap !== undefined) { + pattern = [this.options.dash.length,this.options.dash.gap]; + } + else { + pattern = [5,5]; } - }; - - // range - this.range = new Range(this.body); - this.components.push(this.range); - this.body.range = this.range; - - // time axis - this.timeAxis = new TimeAxis(this.body); - this.components.push(this.timeAxis); - this.body.util.snap = this.timeAxis.snap.bind(this.timeAxis); - - // current time bar - this.currentTime = new CurrentTime(this.body); - this.components.push(this.currentTime); - - // custom time bar - // Note: time bar will be attached in this.setOptions when selected - this.customTime = new CustomTime(this.body); - this.components.push(this.customTime); - // item set - this.linegraph = new LineGraph(this.body); - this.components.push(this.linegraph); + // set dash settings for chrome or firefox + ctx.setLineDash(pattern); + ctx.lineDashOffset = 0; - this.itemsData = null; // DataSet - this.groupsData = null; // DataSet + // draw the line + via = this._line(ctx); - // apply options - if (options) { - this.setOptions(options); + // restore the dash settings. + ctx.setLineDash([0]); + ctx.lineDashOffset = 0; + ctx.restore(); } - - // IMPORTANT: THIS HAPPENS BEFORE SET ITEMS! - if (groups) { - this.setGroups(groups); + else { // unsupporting smooth lines + // draw dashed line + ctx.beginPath(); + ctx.lineCap = 'round'; + if (this.options.dash.altLength !== undefined) //If an alt dash value has been set add to the array this value + { + ctx.dashedLine(this.from.x,this.from.y,this.to.x,this.to.y, + [this.options.dash.length,this.options.dash.gap,this.options.dash.altLength,this.options.dash.gap]); + } + else if (this.options.dash.length !== undefined && this.options.dash.gap !== undefined) //If a dash and gap value has been set add to the array this value + { + ctx.dashedLine(this.from.x,this.from.y,this.to.x,this.to.y, + [this.options.dash.length,this.options.dash.gap]); + } + else //If all else fails draw a line + { + ctx.moveTo(this.from.x, this.from.y); + ctx.lineTo(this.to.x, this.to.y); + } + ctx.stroke(); } - // create itemset - if (items) { - this.setItems(items); - } - else { - this.redraw(); + // draw label + if (this.label) { + var point; + if (this.options.smoothCurves.enabled == true && via != null) { + var midpointX = 0.5*(0.5*(this.from.x + via.x) + 0.5*(this.to.x + via.x)); + var midpointY = 0.5*(0.5*(this.from.y + via.y) + 0.5*(this.to.y + via.y)); + point = {x:midpointX, y:midpointY}; + } + else { + point = this._pointOnLine(0.5); + } + this._label(ctx, this.label, point.x, point.y); } - } - - // Extend the functionality from Core - Graph2d.prototype = new Core(); + }; /** - * Set items - * @param {vis.DataSet | Array | google.visualization.DataTable | null} items + * Get a point on a line + * @param {Number} percentage. Value between 0 (line start) and 1 (line end) + * @return {Object} point + * @private */ - Graph2d.prototype.setItems = function(items) { - var initialLoad = (this.itemsData == null); - - // convert to type DataSet when needed - var newDataSet; - if (!items) { - newDataSet = null; - } - else if (items instanceof DataSet || items instanceof DataView) { - newDataSet = items; + Edge.prototype._pointOnLine = function (percentage) { + return { + x: (1 - percentage) * this.from.x + percentage * this.to.x, + y: (1 - percentage) * this.from.y + percentage * this.to.y } - else { - // turn an array into a dataset - newDataSet = new DataSet(items, { - type: { - start: 'Date', - end: 'Date' - } - }); + }; + + /** + * 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 + */ + Edge.prototype._pointOnCircle = function (x, y, radius, percentage) { + var angle = (percentage - 3/8) * 2 * Math.PI; + return { + x: x + radius * Math.cos(angle), + y: y - radius * Math.sin(angle) } + }; - // set items - this.itemsData = newDataSet; - this.linegraph && this.linegraph.setItems(newDataSet); + /** + * Redraw a edge as a line with an arrow halfway the line + * Draw this edge in the given canvas + * The 2d context of a HTML canvas can be retrieved by canvas.getContext("2d"); + * @param {CanvasRenderingContext2D} ctx + * @private + */ + Edge.prototype._drawArrowCenter = function(ctx) { + var point; + // set style + ctx.strokeStyle = this._getColor(); + ctx.fillStyle = ctx.strokeStyle; + ctx.lineWidth = this._getLineWidth(); - if (initialLoad) { - if (this.options.start != undefined || this.options.end != undefined) { - var start = this.options.start != undefined ? this.options.start : null; - var end = this.options.end != undefined ? this.options.end : null; + if (this.from != this.to) { + // draw line + var via = this._line(ctx); - this.setWindow(start, end, {animate: false}); + var angle = Math.atan2((this.to.y - this.from.y), (this.to.x - this.from.x)); + var length = (10 + 5 * this.options.width) * this.options.arrowScaleFactor; + // draw an arrow halfway the line + if (this.options.smoothCurves.enabled == true && via != null) { + var midpointX = 0.5*(0.5*(this.from.x + via.x) + 0.5*(this.to.x + via.x)); + var midpointY = 0.5*(0.5*(this.from.y + via.y) + 0.5*(this.to.y + via.y)); + point = {x:midpointX, y:midpointY}; } else { - this.fit({animate: false}); + point = this._pointOnLine(0.5); } - } - }; - /** - * Set groups - * @param {vis.DataSet | Array | google.visualization.DataTable} groups - */ - Graph2d.prototype.setGroups = function(groups) { - // convert to type DataSet when needed - var newDataSet; - if (!groups) { - newDataSet = null; - } - else if (groups instanceof DataSet || groups instanceof DataView) { - newDataSet = groups; + ctx.arrow(point.x, point.y, angle, length); + ctx.fill(); + ctx.stroke(); + + // draw label + if (this.label) { + this._label(ctx, this.label, point.x, point.y); + } } else { - // turn an array into a dataset - newDataSet = new DataSet(groups); - } + // draw circle + var x, y; + var radius = 0.25 * Math.max(100,this.physics.springLength); + var node = this.from; + if (!node.width) { + node.resize(ctx); + } + if (node.width > node.height) { + x = node.x + node.width * 0.5; + y = node.y - radius; + } + else { + x = node.x + radius; + y = node.y - node.height * 0.5; + } + this._circle(ctx, x, y, radius); - this.groupsData = newDataSet; - this.linegraph.setGroups(newDataSet); - }; + // draw all arrows + var angle = 0.2 * Math.PI; + var length = (10 + 5 * this.options.width) * this.options.arrowScaleFactor; + point = this._pointOnCircle(x, y, radius, 0.5); + ctx.arrow(point.x, point.y, angle, length); + ctx.fill(); + ctx.stroke(); - /** - * Returns an object containing an SVG element with the icon of the group (size determined by iconWidth and iconHeight), the label of the group (content) and the yAxisOrientation of the group (left or right). - * @param groupId - * @param width - * @param height - */ - Graph2d.prototype.getLegend = function(groupId, width, height) { - if (width === undefined) {width = 15;} - if (height === undefined) {height = 15;} - if (this.linegraph.groups[groupId] !== undefined) { - return this.linegraph.groups[groupId].getLegend(width,height); - } - else { - return "cannot find group:" + groupId; + // draw label + if (this.label) { + point = this._pointOnCircle(x, y, radius, 0.5); + this._label(ctx, this.label, point.x, point.y); + } } + }; + + Edge.prototype._pointOnBezier = function(t) { + var via = this._getViaCoordinates(); + + var x = Math.pow(1-t,2)*this.from.x + (2*t*(1 - t))*via.x + Math.pow(t,2)*this.to.x; + var y = Math.pow(1-t,2)*this.from.y + (2*t*(1 - t))*via.y + Math.pow(t,2)*this.to.y; + + return {x:x,y:y}; } /** - * This checks if the visible option of the supplied group (by ID) is true or false. - * @param groupId + * This function uses binary search to look for the point where the bezier curve crosses the border of the node. + * + * @param from + * @param ctx * @returns {*} + * @private */ - Graph2d.prototype.isGroupVisible = function(groupId) { - if (this.linegraph.groups[groupId] !== undefined) { - return (this.linegraph.groups[groupId].visible && (this.linegraph.options.groups.visibility[groupId] === undefined || this.linegraph.options.groups.visibility[groupId] == true)); - } - else { - return false; + Edge.prototype._findBorderPosition = function(from,ctx) { + var maxIterations = 10; + var iteration = 0; + var low = 0; + var high = 1; + var pos,angle,distanceToBorder, distanceToNodes, difference; + var threshold = 0.2; + var node = this.to; + if (from == true) { + node = this.from; } - } + while (low <= high && iteration < maxIterations) { + var middle = (low + high) * 0.5; - /** - * Get the data range of the item set. - * @returns {{min: Date, max: Date}} range A range with a start and end Date. - * When no minimum is found, min==null - * When no maximum is found, max==null - */ - Graph2d.prototype.getItemRange = function() { - var min = null; - var max = null; - - // calculate min from start filed - for (var groupId in this.linegraph.groups) { - if (this.linegraph.groups.hasOwnProperty(groupId)) { - if (this.linegraph.groups[groupId].visible == true) { - for (var i = 0; i < this.linegraph.groups[groupId].itemsData.length; i++) { - var item = this.linegraph.groups[groupId].itemsData[i]; - var value = util.convert(item.x, 'Date').valueOf(); - min = min == null ? value : min > value ? value : min; - max = max == null ? value : max < value ? value : max; - } + pos = this._pointOnBezier(middle); + angle = Math.atan2((node.y - pos.y), (node.x - pos.x)); + distanceToBorder = node.distanceToBorder(ctx,angle); + distanceToNodes = Math.sqrt(Math.pow(pos.x-node.x,2) + Math.pow(pos.y-node.y,2)); + difference = distanceToBorder - distanceToNodes; + if (Math.abs(difference) < threshold) { + break; // found + } + else if (difference < 0) { // distance to nodes is larger than distance to border --> t needs to be bigger if we're looking at the to node. + if (from == false) { + low = middle; + } + else { + high = middle; + } + } + else { + if (from == false) { + high = middle; + } + else { + low = middle; } } + + iteration++; } + pos.t = middle; - return { - min: (min != null) ? new Date(min) : null, - max: (max != null) ? new Date(max) : null - }; + return pos; }; + /** + * Redraw a edge as a line with an arrow + * Draw this edge in the given canvas + * The 2d context of a HTML canvas can be retrieved by canvas.getContext("2d"); + * @param {CanvasRenderingContext2D} ctx + * @private + */ + Edge.prototype._drawArrow = function(ctx) { + // set style + ctx.strokeStyle = this._getColor(); + ctx.fillStyle = ctx.strokeStyle; + ctx.lineWidth = this._getLineWidth(); + // set vars + var angle, length, arrowPos; - module.exports = Graph2d; - - -/***/ }, -/* 43 */ -/***/ function(module, exports, __webpack_require__) { + // if not connected to itself + if (this.from != this.to) { + // draw line + this._line(ctx); - var util = __webpack_require__(1); - var DOMutil = __webpack_require__(6); - var DataSet = __webpack_require__(7); - var DataView = __webpack_require__(9); - var Component = __webpack_require__(23); - var DataAxis = __webpack_require__(44); - var GraphGroup = __webpack_require__(46); - var Legend = __webpack_require__(50); - var BarGraphFunctions = __webpack_require__(49); + // draw arrow head + if (this.options.smoothCurves.enabled == true) { + var via = this._getViaCoordinates(); + arrowPos = this._findBorderPosition(false, ctx); + var guidePos = this._pointOnBezier(Math.max(0.0, arrowPos.t - 0.1)) + angle = Math.atan2((arrowPos.y - guidePos.y), (arrowPos.x - guidePos.x)); + } + else { + angle = Math.atan2((this.to.y - this.from.y), (this.to.x - this.from.x)); + var dx = (this.to.x - this.from.x); + var dy = (this.to.y - this.from.y); + var edgeSegmentLength = Math.sqrt(dx * dx + dy * dy); + var toBorderDist = this.to.distanceToBorder(ctx, angle); + var toBorderPoint = (edgeSegmentLength - toBorderDist) / edgeSegmentLength; - var UNGROUPED = '__ungrouped__'; // reserved group id for ungrouped items + arrowPos = {}; + arrowPos.x = (1 - toBorderPoint) * this.from.x + toBorderPoint * this.to.x; + arrowPos.y = (1 - toBorderPoint) * this.from.y + toBorderPoint * this.to.y; + } - /** - * This is the constructor of the LineGraph. It requires a Timeline body and options. - * - * @param body - * @param options - * @constructor - */ - function LineGraph(body, options) { - this.id = util.randomUUID(); - this.body = body; + // draw arrow at the end of the line + length = (10 + 5 * this.options.width) * this.options.arrowScaleFactor; + ctx.arrow(arrowPos.x,arrowPos.y, angle, length); + ctx.fill(); + ctx.stroke(); - this.defaultOptions = { - yAxisOrientation: 'left', - defaultGroup: 'default', - sort: true, - sampling: true, - graphHeight: '400px', - shaded: { - enabled: false, - orientation: 'bottom' // top, bottom - }, - style: 'line', // line, bar - barChart: { - width: 50, - handleOverlap: 'overlap', - align: 'center' // left, center, right - }, - catmullRom: { - enabled: true, - parametrization: 'centripetal', // uniform (alpha = 0.0), chordal (alpha = 1.0), centripetal (alpha = 0.5) - alpha: 0.5 - }, - drawPoints: { - enabled: true, - size: 6, - style: 'square' // square, circle - }, - dataAxis: { - showMinorLabels: true, - showMajorLabels: true, - icons: false, - width: '40px', - visible: true, - alignZeros: true, - customRange: { - left: {min:undefined, max:undefined}, - right: {min:undefined, max:undefined} + // draw label + if (this.label) { + var point; + if (this.options.smoothCurves.enabled == true && via != null) { + point = this._pointOnBezier(0.5); } - //, these options are not set by default, but this shows the format they will be in - //format: { - // left: {decimals: 2}, - // right: {decimals: 2} - //}, - //title: { - // left: { - // text: 'left', - // style: 'color:black;' - // }, - // right: { - // text: 'right', - // style: 'color:black;' - // } - //} - }, - legend: { - enabled: false, - icons: true, - left: { - visible: true, - position: 'top-left' // top/bottom - left,right - }, - right: { - visible: true, - position: 'top-right' // top/bottom - left,right + else { + point = this._pointOnLine(0.5); } - }, - groups: { - visibility: {} + this._label(ctx, this.label, point.x, point.y); } - }; - - // options is shared by this ItemSet and all its items - this.options = util.extend({}, this.defaultOptions); - this.dom = {}; - this.props = {}; - this.hammer = null; - this.groups = {}; - this.abortedGraphUpdate = false; - this.updateSVGheight = false; - this.updateSVGheightOnResize = false; + } + else { + // draw circle + var node = this.from; + var x, y, arrow; + var radius = 0.25 * Math.max(100,this.physics.springLength); + if (!node.width) { + node.resize(ctx); + } + if (node.width > node.height) { + x = node.x + node.width * 0.5; + y = node.y - radius; + arrow = { + x: x, + y: node.y, + angle: 0.9 * Math.PI + }; + } + else { + x = node.x + radius; + y = node.y - node.height * 0.5; + arrow = { + x: node.x, + y: y, + angle: 0.6 * Math.PI + }; + } + ctx.beginPath(); + // TODO: similarly, for a line without arrows, draw to the border of the nodes instead of the center + ctx.arc(x, y, radius, 0, 2 * Math.PI, false); + ctx.stroke(); - var me = this; - this.itemsData = null; // DataSet - this.groupsData = null; // DataSet + // draw all arrows + var length = (10 + 5 * this.options.width) * this.options.arrowScaleFactor; + ctx.arrow(arrow.x, arrow.y, arrow.angle, length); + ctx.fill(); + ctx.stroke(); - // listeners for the DataSet of the items - this.itemListeners = { - 'add': function (event, params, senderId) { - me._onAdd(params.items); - }, - 'update': function (event, params, senderId) { - me._onUpdate(params.items); - }, - 'remove': function (event, params, senderId) { - me._onRemove(params.items); + // draw label + if (this.label) { + point = this._pointOnCircle(x, y, radius, 0.5); + this._label(ctx, this.label, point.x, point.y); } - }; + } + }; - // listeners for the DataSet of the groups - this.groupListeners = { - 'add': function (event, params, senderId) { - me._onAddGroups(params.items); - }, - 'update': function (event, params, senderId) { - me._onUpdateGroups(params.items); - }, - 'remove': function (event, params, senderId) { - me._onRemoveGroups(params.items); + /** + * Calculate the distance between a point (x3,y3) and a line segment from + * (x1,y1) to (x2,y2). + * http://stackoverflow.com/questions/849211/shortest-distancae-between-a-point-and-a-line-segment + * @param {number} x1 + * @param {number} y1 + * @param {number} x2 + * @param {number} y2 + * @param {number} x3 + * @param {number} y3 + * @private + */ + Edge.prototype._getDistanceToEdge = function (x1,y1, x2,y2, x3,y3) { // x3,y3 is the point + var returnValue = 0; + if (this.from != this.to) { + if (this.options.smoothCurves.enabled == true) { + var xVia, yVia; + if (this.options.smoothCurves.enabled == true && this.options.smoothCurves.dynamic == true) { + xVia = this.via.x; + yVia = this.via.y; + } + else { + var via = this._getViaCoordinates(); + xVia = via.x; + yVia = via.y; + } + var minDistance = 1e9; + var distance; + var i,t,x,y, lastX, lastY; + for (i = 0; i < 10; i++) { + t = 0.1*i; + x = Math.pow(1-t,2)*x1 + (2*t*(1 - t))*xVia + Math.pow(t,2)*x2; + y = Math.pow(1-t,2)*y1 + (2*t*(1 - t))*yVia + Math.pow(t,2)*y2; + if (i > 0) { + distance = this._getDistanceToLine(lastX,lastY,x,y, x3,y3); + minDistance = distance < minDistance ? distance : minDistance; + } + lastX = x; lastY = y; + } + returnValue = minDistance; } - }; + else { + returnValue = this._getDistanceToLine(x1,y1,x2,y2,x3,y3); + } + } + else { + var x, y, dx, dy; + var radius = 0.25 * this.physics.springLength; + var node = this.from; + if (node.width > node.height) { + x = node.x + 0.5 * node.width; + y = node.y - radius; + } + else { + x = node.x + radius; + y = node.y - 0.5 * node.height; + } + dx = x - x3; + dy = y - y3; + returnValue = Math.abs(Math.sqrt(dx*dx + dy*dy) - radius); + } - this.items = {}; // object with an Item for every data item - this.selection = []; // list with the ids of all selected nodes - this.lastStart = this.body.range.start; - this.touchParams = {}; // stores properties while dragging + if (this.labelDimensions.left < x3 && + this.labelDimensions.left + this.labelDimensions.width > x3 && + this.labelDimensions.top < y3 && + this.labelDimensions.top + this.labelDimensions.height > y3) { + return 0; + } + else { + return returnValue; + } + }; - this.svgElements = {}; - this.setOptions(options); - this.groupsUsingDefaultStyles = [0]; - this.COUNTER = 0; - this.body.emitter.on('rangechanged', function() { - me.lastStart = me.body.range.start; - me.svg.style.left = util.option.asSize(-me.props.width); - me.redraw.call(me,true); - }); + Edge.prototype._getDistanceToLine = function(x1,y1,x2,y2,x3,y3) { + var px = x2-x1, + py = y2-y1, + something = px*px + py*py, + u = ((x3 - x1) * px + (y3 - y1) * py) / something; - // create the HTML DOM - this._create(); - this.framework = {svg: this.svg, svgElements: this.svgElements, options: this.options, groups: this.groups}; - this.body.emitter.emit('change'); + if (u > 1) { + u = 1; + } + else if (u < 0) { + u = 0; + } - } + var x = x1 + u * px, + y = y1 + u * py, + dx = x - x3, + dy = y - y3; - LineGraph.prototype = new Component(); + //# Note: If the actual distance does not matter, + //# if you only want to compare what this function + //# returns to other results of this function, you + //# can just return the squared distance instead + //# (i.e. remove the sqrt) to gain a little performance + + return Math.sqrt(dx*dx + dy*dy); + }; /** - * Create the HTML DOM for the ItemSet + * This allows the zoom level of the network to influence the rendering + * + * @param scale */ - LineGraph.prototype._create = function(){ - var frame = document.createElement('div'); - frame.className = 'LineGraph'; - this.dom.frame = frame; - - // create svg element for graph drawing. - this.svg = document.createElementNS('http://www.w3.org/2000/svg','svg'); - this.svg.style.position = 'relative'; - this.svg.style.height = ('' + this.options.graphHeight).replace('px','') + 'px'; - this.svg.style.display = 'block'; - frame.appendChild(this.svg); + Edge.prototype.setScale = function(scale) { + this.networkScaleInv = 1.0/scale; + }; - // data axis - this.options.dataAxis.orientation = 'left'; - this.yAxisLeft = new DataAxis(this.body, this.options.dataAxis, this.svg, this.options.groups); - this.options.dataAxis.orientation = 'right'; - this.yAxisRight = new DataAxis(this.body, this.options.dataAxis, this.svg, this.options.groups); - delete this.options.dataAxis.orientation; + Edge.prototype.select = function() { + this.selected = true; + }; - // legends - this.legendLeft = new Legend(this.body, this.options.legend, 'left', this.options.groups); - this.legendRight = new Legend(this.body, this.options.legend, 'right', this.options.groups); + Edge.prototype.unselect = function() { + this.selected = false; + }; - this.show(); + Edge.prototype.positionBezierNode = function() { + if (this.via !== null && this.from !== null && this.to !== null) { + 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 !== null) { + this.via.x = 0; + this.via.y = 0; + } }; /** - * set the options of the LineGraph. the mergeOptions is used for subObjects that have an enabled element. - * @param {object} options + * This function draws the control nodes for the manipulator. + * In order to enable this, only set the this.controlNodesEnabled to true. + * @param ctx */ - LineGraph.prototype.setOptions = function(options) { - if (options) { - var fields = ['sampling','defaultGroup','height','graphHeight','yAxisOrientation','style','barChart','dataAxis','sort','groups']; - if (options.graphHeight === undefined && options.height !== undefined && this.body.domProps.centerContainer.height !== undefined) { - this.updateSVGheight = true; - this.updateSVGheightOnResize = true; - } - else if (this.body.domProps.centerContainer.height !== undefined && options.graphHeight !== undefined) { - if (parseInt((options.graphHeight + '').replace("px",'')) < this.body.domProps.centerContainer.height) { - this.updateSVGheight = true; - } - } - util.selectiveDeepExtend(fields, this.options, options); - util.mergeOptions(this.options, options,'catmullRom'); - util.mergeOptions(this.options, options,'drawPoints'); - util.mergeOptions(this.options, options,'shaded'); - util.mergeOptions(this.options, options,'legend'); - - if (options.catmullRom) { - if (typeof options.catmullRom == 'object') { - if (options.catmullRom.parametrization) { - if (options.catmullRom.parametrization == 'uniform') { - this.options.catmullRom.alpha = 0; - } - else if (options.catmullRom.parametrization == 'chordal') { - this.options.catmullRom.alpha = 1.0; - } - else { - this.options.catmullRom.parametrization = 'centripetal'; - this.options.catmullRom.alpha = 0.5; - } - } - } + Edge.prototype._drawControlNodes = function(ctx) { + if (this.controlNodesEnabled == true) { + if (this.controlNodes.from === null && this.controlNodes.to === null) { + var nodeIdFrom = "edgeIdFrom:".concat(this.id); + var nodeIdTo = "edgeIdTo:".concat(this.id); + var constants = { + nodes:{group:'', radius:7, borderWidth:2, borderWidthSelected: 2}, + physics:{damping:0}, + clustering: {maxNodeSizeIncrements: 0 ,nodeScaling: {width:0, height: 0, radius:0}} + }; + this.controlNodes.from = new Node( + {id:nodeIdFrom, + shape:'dot', + color:{background:'#ff0000', border:'#3c3c3c', highlight: {background:'#07f968'}} + },{},{},constants); + this.controlNodes.to = new Node( + {id:nodeIdTo, + shape:'dot', + color:{background:'#ff0000', border:'#3c3c3c', highlight: {background:'#07f968'}} + },{},{},constants); } - if (this.yAxisLeft) { - if (options.dataAxis !== undefined) { - this.yAxisLeft.setOptions(this.options.dataAxis); - this.yAxisRight.setOptions(this.options.dataAxis); - } + this.controlNodes.positions = {}; + if (this.controlNodes.from.selected == false) { + this.controlNodes.positions.from = this.getControlNodeFromPosition(ctx); + this.controlNodes.from.x = this.controlNodes.positions.from.x; + this.controlNodes.from.y = this.controlNodes.positions.from.y; } - - if (this.legendLeft) { - if (options.legend !== undefined) { - this.legendLeft.setOptions(this.options.legend); - this.legendRight.setOptions(this.options.legend); - } + if (this.controlNodes.to.selected == false) { + this.controlNodes.positions.to = this.getControlNodeToPosition(ctx); + this.controlNodes.to.x = this.controlNodes.positions.to.x; + this.controlNodes.to.y = this.controlNodes.positions.to.y; } - if (this.groups.hasOwnProperty(UNGROUPED)) { - this.groups[UNGROUPED].setOptions(options); - } + this.controlNodes.from.draw(ctx); + this.controlNodes.to.draw(ctx); } - - // this is used to redraw the graph if the visibility of the groups is changed. - if (this.dom.frame) { - this.redraw(true); + else { + this.controlNodes = {from:null, to:null, positions:{}}; } }; /** - * Hide the component from the DOM + * Enable control nodes. + * @private */ - LineGraph.prototype.hide = function() { - // remove the frame containing the items - if (this.dom.frame.parentNode) { - this.dom.frame.parentNode.removeChild(this.dom.frame); - } + Edge.prototype._enableControlNodes = function() { + this.fromBackup = this.from; + this.toBackup = this.to; + this.controlNodesEnabled = true; }; - /** - * Show the component in the DOM (when not already visible). - * @return {Boolean} changed + * disable control nodes and remove from dynamicEdges from old node + * @private */ - LineGraph.prototype.show = function() { - // show frame containing the items - if (!this.dom.frame.parentNode) { - this.body.dom.center.appendChild(this.dom.frame); + Edge.prototype._disableControlNodes = function() { + this.fromId = this.from.id; + this.toId = this.to.id; + if (this.fromId != this.fromBackup.id) { // from was changed, remove edge from old 'from' node dynamic edges + this.fromBackup.detachEdge(this); } + else if (this.toId != this.toBackup.id) { // to was changed, remove edge from old 'to' node dynamic edges + this.toBackup.detachEdge(this); + } + + this.fromBackup = null; + this.toBackup = null; + this.controlNodesEnabled = false; }; /** - * Set items - * @param {vis.DataSet | null} items + * This checks if one of the control nodes is selected and if so, returns the control node object. Else it returns null. + * @param x + * @param y + * @returns {null} + * @private */ - LineGraph.prototype.setItems = function(items) { - var me = this, - ids, - oldItemsData = this.itemsData; + Edge.prototype._getSelectedControlNode = function(x,y) { + var positions = this.controlNodes.positions; + var fromDistance = Math.sqrt(Math.pow(x - positions.from.x,2) + Math.pow(y - positions.from.y,2)); + var toDistance = Math.sqrt(Math.pow(x - positions.to.x ,2) + Math.pow(y - positions.to.y ,2)); - // replace the dataset - if (!items) { - this.itemsData = null; + if (fromDistance < 15) { + this.connectedNode = this.from; + this.from = this.controlNodes.from; + return this.controlNodes.from; } - else if (items instanceof DataSet || items instanceof DataView) { - this.itemsData = items; + else if (toDistance < 15) { + this.connectedNode = this.to; + this.to = this.controlNodes.to; + return this.controlNodes.to; } else { - throw new TypeError('Data must be an instance of DataSet or DataView'); + return null; } + }; - if (oldItemsData) { - // unsubscribe from old dataset - util.forEach(this.itemListeners, function (callback, event) { - oldItemsData.off(event, callback); - }); - // remove all drawn items - ids = oldItemsData.getIds(); - this._onRemove(ids); + /** + * this resets the control nodes to their original position. + * @private + */ + Edge.prototype._restoreControlNodes = function() { + if (this.controlNodes.from.selected == true) { + this.from = this.connectedNode; + this.connectedNode = null; + this.controlNodes.from.unselect(); } - - if (this.itemsData) { - // subscribe to new dataset - var id = this.id; - util.forEach(this.itemListeners, function (callback, event) { - me.itemsData.on(event, callback, id); - }); - - // add all new items - ids = this.itemsData.getIds(); - this._onAdd(ids); + else if (this.controlNodes.to.selected == true) { + this.to = this.connectedNode; + this.connectedNode = null; + this.controlNodes.to.unselect(); } - this._updateUngrouped(); - //this._updateGraph(); - this.redraw(true); }; - /** - * Set groups - * @param {vis.DataSet} groups + * this calculates the position of the control nodes on the edges of the parent nodes. + * + * @param ctx + * @returns {x: *, y: *} */ - LineGraph.prototype.setGroups = function(groups) { - var me = this; - var ids; - - // unsubscribe from current dataset - if (this.groupsData) { - util.forEach(this.groupListeners, function (callback, event) { - me.groupsData.unsubscribe(event, callback); - }); - - // remove all drawn groups - ids = this.groupsData.getIds(); - this.groupsData = null; - this._onRemoveGroups(ids); // note: this will cause a redraw - } - - // replace the dataset - if (!groups) { - this.groupsData = null; - } - else if (groups instanceof DataSet || groups instanceof DataView) { - this.groupsData = groups; + Edge.prototype.getControlNodeFromPosition = function(ctx) { + // draw arrow head + var controlnodeFromPos; + if (this.options.smoothCurves.enabled == true) { + controlnodeFromPos = this._findBorderPosition(true, ctx); } else { - throw new TypeError('Data must be an instance of DataSet or DataView'); + var angle = Math.atan2((this.to.y - this.from.y), (this.to.x - this.from.x)); + var dx = (this.to.x - this.from.x); + var dy = (this.to.y - this.from.y); + var edgeSegmentLength = Math.sqrt(dx * dx + dy * dy); + + var fromBorderDist = this.from.distanceToBorder(ctx, angle + Math.PI); + var fromBorderPoint = (edgeSegmentLength - fromBorderDist) / edgeSegmentLength; + controlnodeFromPos = {}; + controlnodeFromPos.x = (fromBorderPoint) * this.from.x + (1 - fromBorderPoint) * this.to.x; + controlnodeFromPos.y = (fromBorderPoint) * this.from.y + (1 - fromBorderPoint) * this.to.y; } - if (this.groupsData) { - // subscribe to new dataset - var id = this.id; - util.forEach(this.groupListeners, function (callback, event) { - me.groupsData.on(event, callback, id); - }); - - // draw all ms - ids = this.groupsData.getIds(); - this._onAddGroups(ids); - } - this._onUpdate(); + return controlnodeFromPos; }; - /** - * Update the data - * @param [ids] - * @private + * this calculates the position of the control nodes on the edges of the parent nodes. + * + * @param ctx + * @returns {{from: {x: number, y: number}, to: {x: *, y: *}}} */ - LineGraph.prototype._onUpdate = function(ids) { - this._updateUngrouped(); - this._updateAllGroupData(); - //this._updateGraph(); - this.redraw(true); - }; - LineGraph.prototype._onAdd = function (ids) {this._onUpdate(ids);}; - LineGraph.prototype._onRemove = function (ids) {this._onUpdate(ids);}; - LineGraph.prototype._onUpdateGroups = function (groupIds) { - for (var i = 0; i < groupIds.length; i++) { - var group = this.groupsData.get(groupIds[i]); - this._updateGroup(group, groupIds[i]); + Edge.prototype.getControlNodeToPosition = function(ctx) { + // draw arrow head + var controlnodeFromPos,controlnodeToPos; + if (this.options.smoothCurves.enabled == true) { + controlnodeToPos = this._findBorderPosition(false, ctx); } + else { + var angle = Math.atan2((this.to.y - this.from.y), (this.to.x - this.from.x)); + var dx = (this.to.x - this.from.x); + var dy = (this.to.y - this.from.y); + var edgeSegmentLength = Math.sqrt(dx * dx + dy * dy); + var toBorderDist = this.to.distanceToBorder(ctx, angle); + var toBorderPoint = (edgeSegmentLength - toBorderDist) / edgeSegmentLength; - //this._updateGraph(); - this.redraw(true); + controlnodeToPos = {}; + controlnodeToPos.x = (1 - toBorderPoint) * this.from.x + toBorderPoint * this.to.x; + controlnodeToPos.y = (1 - toBorderPoint) * this.from.y + toBorderPoint * this.to.y; + } + + return controlnodeToPos; }; - LineGraph.prototype._onAddGroups = function (groupIds) {this._onUpdateGroups(groupIds);}; + module.exports = Edge; + +/***/ }, +/* 38 */ +/***/ function(module, exports, __webpack_require__) { + + var util = __webpack_require__(1); /** - * this cleans the group out off the legends and the dataaxis, updates the ungrouped and updates the graph - * @param {Array} groupIds - * @private + * @class Groups + * This class can store groups and properties specific for groups. */ - LineGraph.prototype._onRemoveGroups = function (groupIds) { - for (var i = 0; i < groupIds.length; i++) { - if (this.groups.hasOwnProperty(groupIds[i])) { - if (this.groups[groupIds[i]].options.yAxisOrientation == 'right') { - this.yAxisRight.removeGroup(groupIds[i]); - this.legendRight.removeGroup(groupIds[i]); - this.legendRight.redraw(); - } - else { - this.yAxisLeft.removeGroup(groupIds[i]); - this.legendLeft.removeGroup(groupIds[i]); - this.legendLeft.redraw(); - } - delete this.groups[groupIds[i]]; - } - } - this._updateUngrouped(); - //this._updateGraph(); - this.redraw(true); - }; + function Groups() { + this.clear(); + this.defaultIndex = 0; + } /** - * update a group object with the group dataset entree - * - * @param group - * @param groupId - * @private + * default constants for group colors */ - LineGraph.prototype._updateGroup = function (group, groupId) { - if (!this.groups.hasOwnProperty(groupId)) { - this.groups[groupId] = new GraphGroup(group, groupId, this.options, this.groupsUsingDefaultStyles); - if (this.groups[groupId].options.yAxisOrientation == 'right') { - this.yAxisRight.addGroup(groupId, this.groups[groupId]); - this.legendRight.addGroup(groupId, this.groups[groupId]); - } - else { - this.yAxisLeft.addGroup(groupId, this.groups[groupId]); - this.legendLeft.addGroup(groupId, this.groups[groupId]); - } - } - else { - this.groups[groupId].update(group); - if (this.groups[groupId].options.yAxisOrientation == 'right') { - this.yAxisRight.updateGroup(groupId, this.groups[groupId]); - this.legendRight.updateGroup(groupId, this.groups[groupId]); - } - else { - this.yAxisLeft.updateGroup(groupId, this.groups[groupId]); - this.legendLeft.updateGroup(groupId, this.groups[groupId]); - } - } - this.legendLeft.redraw(); - this.legendRight.redraw(); - }; + Groups.DEFAULT = [ + {border: "#2B7CE9", background: "#97C2FC", highlight: {border: "#2B7CE9", background: "#D2E5FF"}, hover: {border: "#2B7CE9", background: "#D2E5FF"}}, // blue + {border: "#FFA500", background: "#FFFF00", highlight: {border: "#FFA500", background: "#FFFFA3"}, hover: {border: "#FFA500", background: "#FFFFA3"}}, // yellow + {border: "#FA0A10", background: "#FB7E81", highlight: {border: "#FA0A10", background: "#FFAFB1"}, hover: {border: "#FA0A10", background: "#FFAFB1"}}, // red + {border: "#41A906", background: "#7BE141", highlight: {border: "#41A906", background: "#A1EC76"}, hover: {border: "#41A906", background: "#A1EC76"}}, // green + {border: "#E129F0", background: "#EB7DF4", highlight: {border: "#E129F0", background: "#F0B3F5"}, hover: {border: "#E129F0", background: "#F0B3F5"}}, // magenta + {border: "#7C29F0", background: "#AD85E4", highlight: {border: "#7C29F0", background: "#D3BDF0"}, hover: {border: "#7C29F0", background: "#D3BDF0"}}, // purple + {border: "#C37F00", background: "#FFA807", highlight: {border: "#C37F00", background: "#FFCA66"}, hover: {border: "#C37F00", background: "#FFCA66"}}, // orange + {border: "#4220FB", background: "#6E6EFD", highlight: {border: "#4220FB", background: "#9B9BFD"}, hover: {border: "#4220FB", background: "#9B9BFD"}}, // darkblue + {border: "#FD5A77", background: "#FFC0CB", highlight: {border: "#FD5A77", background: "#FFD1D9"}, hover: {border: "#FD5A77", background: "#FFD1D9"}}, // pink + {border: "#4AD63A", background: "#C2FABC", highlight: {border: "#4AD63A", background: "#E6FFE3"}, hover: {border: "#4AD63A", background: "#E6FFE3"}} // mint + ]; /** - * this updates all groups, it is used when there is an update the the itemset. - * - * @private + * Clear all groups */ - LineGraph.prototype._updateAllGroupData = function () { - if (this.itemsData != null) { - var groupsContent = {}; - var groupId; - for (groupId in this.groups) { - if (this.groups.hasOwnProperty(groupId)) { - groupsContent[groupId] = []; - } - } - for (var itemId in this.itemsData._data) { - if (this.itemsData._data.hasOwnProperty(itemId)) { - var item = this.itemsData._data[itemId]; - if (groupsContent[item.group] === undefined) { - throw new Error('Cannot find referenced group. Possible reason: items added before groups? Groups need to be added before items, as items refer to groups.') - } - item.x = util.convert(item.x,'Date'); - groupsContent[item.group].push(item); - } - } - for (groupId in this.groups) { - if (this.groups.hasOwnProperty(groupId)) { - this.groups[groupId].setItems(groupsContent[groupId]); + Groups.prototype.clear = function () { + this.groups = {}; + this.groups.length = function() + { + var i = 0; + for ( var p in this ) { + if (this.hasOwnProperty(p)) { + i++; } } + return i; } }; /** - * Create or delete the group holding all ungrouped items. This group is used when - * there are no groups specified. This anonymous group is called 'graph'. - * @protected + * get group properties of a groupname. If groupname is not found, a new group + * is added. + * @param {*} groupname Can be a number, string, Date, etc. + * @return {Object} group The created group, containing all group properties */ - LineGraph.prototype._updateUngrouped = function() { - if (this.itemsData && this.itemsData != null) { - var ungroupedCounter = 0; - for (var itemId in this.itemsData._data) { - if (this.itemsData._data.hasOwnProperty(itemId)) { - var item = this.itemsData._data[itemId]; - if (item != undefined) { - if (item.hasOwnProperty('group')) { - if (item.group === undefined) { - item.group = UNGROUPED; - } - } - else { - item.group = UNGROUPED; - } - ungroupedCounter = item.group == UNGROUPED ? ungroupedCounter + 1 : ungroupedCounter; - } - } - } - - if (ungroupedCounter == 0) { - delete this.groups[UNGROUPED]; - this.legendLeft.removeGroup(UNGROUPED); - this.legendRight.removeGroup(UNGROUPED); - this.yAxisLeft.removeGroup(UNGROUPED); - this.yAxisRight.removeGroup(UNGROUPED); - } - else { - var group = {id: UNGROUPED, content: this.options.defaultGroup}; - this._updateGroup(group, UNGROUPED); - } - } - else { - delete this.groups[UNGROUPED]; - this.legendLeft.removeGroup(UNGROUPED); - this.legendRight.removeGroup(UNGROUPED); - this.yAxisLeft.removeGroup(UNGROUPED); - this.yAxisRight.removeGroup(UNGROUPED); + Groups.prototype.get = function (groupname) { + var group = this.groups[groupname]; + if (group == undefined) { + // create new group + var index = this.defaultIndex % Groups.DEFAULT.length; + this.defaultIndex++; + group = {}; + group.color = Groups.DEFAULT[index]; + this.groups[groupname] = group; } - this.legendLeft.redraw(); - this.legendRight.redraw(); + return group; }; - /** - * Redraw the component, mandatory function - * @return {boolean} Returns true if the component is resized + * Add a custom group style + * @param {String} groupname + * @param {Object} style An object containing borderColor, + * backgroundColor, etc. + * @return {Object} group The created group object */ - LineGraph.prototype.redraw = function(forceGraphUpdate) { - var resized = false; - - // calculate actual size and position - this.props.width = this.dom.frame.offsetWidth; - this.props.height = this.body.domProps.centerContainer.height; - - // update the graph if there is no lastWidth or with, used for the initial draw - if (this.lastWidth === undefined && this.props.width) { - forceGraphUpdate = true; - } - - // check if this component is resized - resized = this._isResized() || resized; - - // check whether zoomed (in that case we need to re-stack everything) - var visibleInterval = this.body.range.end - this.body.range.start; - var zoomed = (visibleInterval != this.lastVisibleInterval); - this.lastVisibleInterval = visibleInterval; - + Groups.prototype.add = function (groupname, style) { + this.groups[groupname] = style; + return style; + }; - // the svg element is three times as big as the width, this allows for fully dragging left and right - // without reloading the graph. the controls for this are bound to events in the constructor - if (resized == true) { - this.svg.style.width = util.option.asSize(3*this.props.width); - this.svg.style.left = util.option.asSize(-this.props.width); + module.exports = Groups; - // if the height of the graph is set as proportional, change the height of the svg - if ((this.options.height + '').indexOf("%") != -1 || this.updateSVGheightOnResize == true) { - this.updateSVGheight = true; - } - } - // update the height of the graph on each redraw of the graph. - if (this.updateSVGheight == true) { - if (this.options.graphHeight != this.body.domProps.centerContainer.height + 'px') { - this.options.graphHeight = this.body.domProps.centerContainer.height + 'px'; - this.svg.style.height = this.body.domProps.centerContainer.height + 'px'; - } - this.updateSVGheight = false; - } - else { - this.svg.style.height = ('' + this.options.graphHeight).replace('px','') + 'px'; - } +/***/ }, +/* 39 */ +/***/ function(module, exports, __webpack_require__) { - // zoomed is here to ensure that animations are shown correctly. - if (resized == true || zoomed == true || this.abortedGraphUpdate == true || forceGraphUpdate == true) { - resized = this._updateGraph() || resized; - } - else { - // move the whole svg while dragging - if (this.lastStart != 0) { - var offset = this.body.range.start - this.lastStart; - var range = this.body.range.end - this.body.range.start; - if (this.props.width != 0) { - var rangePerPixelInv = this.props.width/range; - var xOffset = offset * rangePerPixelInv; - this.svg.style.left = (-this.props.width - xOffset) + 'px'; - } - } - } + /** + * @class Images + * This class loads images and keeps them stored. + */ + function Images() { + this.images = {}; + this.imageBroken = {}; + this.callback = undefined; + } - this.legendLeft.redraw(); - this.legendRight.redraw(); - return resized; + /** + * Set an onload callback function. This will be called each time an image + * is loaded + * @param {function} callback + */ + Images.prototype.setOnloadCallback = function(callback) { + this.callback = callback; }; - /** - * Update and redraw the graph. * + * @param {string} url Url of the image + * @param {string} url Url of an image to use if the url image is not found + * @return {Image} img The image object */ - LineGraph.prototype._updateGraph = function () { - // reset the svg elements - DOMutil.prepareElements(this.svgElements); - if (this.props.width != 0 && this.itemsData != null) { - var group, i; - var preprocessedGroupData = {}; - var processedGroupData = {}; - var groupRanges = {}; - var changeCalled = false; - - // getting group Ids - var groupIds = []; - for (var groupId in this.groups) { - if (this.groups.hasOwnProperty(groupId)) { - group = this.groups[groupId]; - if (group.visible == true && (this.options.groups.visibility[groupId] === undefined || this.options.groups.visibility[groupId] == true)) { - groupIds.push(groupId); - } + Images.prototype.load = function(url, brokenUrl) { + var img = this.images[url]; // make a pointer + if (img === undefined) { + // create the image + var me = this; + img = new Image(); + img.onload = function () { + // IE11 fix -- thanks dponch! + if (this.width == 0) { + document.body.appendChild(this); + this.width = this.offsetWidth; + this.height = this.offsetHeight; + document.body.removeChild(this); } - } - if (groupIds.length > 0) { - // this is the range of the SVG canvas - var minDate = this.body.util.toGlobalTime(-this.body.domProps.root.width); - var maxDate = this.body.util.toGlobalTime(2 * this.body.domProps.root.width); - var groupsData = {}; - // fill groups data, this only loads the data we require based on the timewindow - this._getRelevantData(groupIds, groupsData, minDate, maxDate); - // apply sampling, if disabled, it will pass through this function. - this._applySampling(groupIds, groupsData); - - // we transform the X coordinates to detect collisions - for (i = 0; i < groupIds.length; i++) { - preprocessedGroupData[groupIds[i]] = this._convertXcoordinates(groupsData[groupIds[i]]); + if (me.callback) { + me.images[url] = img; + me.callback(this); } + }; - // now all needed data has been collected we start the processing. - this._getYRanges(groupIds, preprocessedGroupData, groupRanges); - - // update the Y axis first, we use this data to draw at the correct Y points - // changeCalled is required to clean the SVG on a change emit. - changeCalled = this._updateYAxis(groupIds, groupRanges); - var MAX_CYCLES = 5; - if (changeCalled == true && this.COUNTER < MAX_CYCLES) { - DOMutil.cleanupElements(this.svgElements); - this.abortedGraphUpdate = true; - this.COUNTER++; - this.body.emitter.emit('change'); - return true; + img.onerror = function () { + if (brokenUrl === undefined) { + console.error("Could not load image:", url); + delete this.src; + if (me.callback) { + me.callback(this); + } } else { - if (this.COUNTER > MAX_CYCLES) { - console.log("WARNING: there may be an infinite loop in the _updateGraph emitter cycle.") - } - this.COUNTER = 0; - this.abortedGraphUpdate = false; - - // With the yAxis scaled correctly, use this to get the Y values of the points. - for (i = 0; i < groupIds.length; i++) { - group = this.groups[groupIds[i]]; - processedGroupData[groupIds[i]] = this._convertYcoordinates(groupsData[groupIds[i]], group); - } - - // draw the groups - for (i = 0; i < groupIds.length; i++) { - group = this.groups[groupIds[i]]; - if (group.options.style != 'bar') { // bar needs to be drawn enmasse - group.draw(processedGroupData[groupIds[i]], group, this.framework); + if (me.imageBroken[url] === true) { + if (this.src == brokenUrl) { + console.error("Could not load brokenImage:", brokenUrl); + delete this.src; + if (me.callback) { + me.callback(this); + } + } + else { + console.error("Could not load image:", url); + this.src = brokenUrl; } } - BarGraphFunctions.draw(groupIds, processedGroupData, this.framework); + else { + console.error("Could not load image:", url); + this.src = brokenUrl; + me.imageBroken[url] = true; + } } - } + }; + + img.src = url; } - // cleanup unused svg elements - DOMutil.cleanupElements(this.svgElements); - return false; + return img; }; + module.exports = Images; - /** - * first select and preprocess the data from the datasets. - * the groups have their preselection of data, we now loop over this data to see - * what data we need to draw. Sorted data is much faster. - * more optimization is possible by doing the sampling before and using the binary search - * to find the end date to determine the increment. - * - * @param {array} groupIds - * @param {object} groupsData - * @param {date} minDate - * @param {date} maxDate - * @private - */ - LineGraph.prototype._getRelevantData = function (groupIds, groupsData, minDate, maxDate) { - var group, i, j, item; - if (groupIds.length > 0) { - for (i = 0; i < groupIds.length; i++) { - group = this.groups[groupIds[i]]; - groupsData[groupIds[i]] = []; - var dataContainer = groupsData[groupIds[i]]; - // optimization for sorted data - if (group.options.sort == true) { - var guess = Math.max(0, util.binarySearchValue(group.itemsData, minDate, 'x', 'before')); - for (j = guess; j < group.itemsData.length; j++) { - item = group.itemsData[j]; - if (item !== undefined) { - if (item.x > maxDate) { - dataContainer.push(item); - break; - } - else { - dataContainer.push(item); - } - } - } - } - else { - for (j = 0; j < group.itemsData.length; j++) { - item = group.itemsData[j]; - if (item !== undefined) { - if (item.x > minDate && item.x < maxDate) { - dataContainer.push(item); - } - } - } - } - } - } - }; +/***/ }, +/* 40 */ +/***/ function(module, exports, __webpack_require__) { + + var util = __webpack_require__(1); /** + * @class Node + * A node. A node can be connected to other nodes via one or multiple edges. + * @param {object} properties An object containing properties for the node. All + * properties 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" + * {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 properties + * @param {Object} constants An object with default values for + * example for the color * - * @param groupIds - * @param groupsData - * @private */ - LineGraph.prototype._applySampling = function (groupIds, groupsData) { - var group; - if (groupIds.length > 0) { - for (var i = 0; i < groupIds.length; i++) { - group = this.groups[groupIds[i]]; - if (group.options.sampling == true) { - var dataContainer = groupsData[groupIds[i]]; - if (dataContainer.length > 0) { - var increment = 1; - var amountOfPoints = dataContainer.length; + function Node(properties, imagelist, grouplist, networkConstants) { + var constants = util.selectiveBridgeObject(['nodes'],networkConstants); + this.options = constants.nodes; - // the global screen is used because changing the width of the yAxis may affect the increment, resulting in an endless loop - // of width changing of the yAxis. - var xDistance = this.body.util.toGlobalScreen(dataContainer[dataContainer.length - 1].x) - this.body.util.toGlobalScreen(dataContainer[0].x); - var pointsPerPixel = amountOfPoints / xDistance; - increment = Math.min(Math.ceil(0.2 * amountOfPoints), Math.max(1, Math.round(pointsPerPixel))); + this.selected = false; + this.hover = false; - var sampledData = []; - for (var j = 0; j < amountOfPoints; j += increment) { - sampledData.push(dataContainer[j]); + this.edges = []; // all edges connected to this node + this.dynamicEdges = []; + this.reroutedEdges = {}; - } - groupsData[groupIds[i]] = sampledData; - } - } - } - } - }; + this.fontDrawThreshold = 3; + + // set defaults for the properties + this.id = undefined; + this.allowedToMoveX = false; + this.allowedToMoveY = false; + this.xFixed = false; + this.yFixed = false; + this.horizontalAlignLeft = true; // these are for the navigation controls + this.verticalAlignTop = true; // these are for the navigation controls + this.baseRadiusValue = networkConstants.nodes.radius; + this.radiusFixed = false; + this.level = -1; + this.preassignedLevel = false; + this.hierarchyEnumerated = false; + this.labelDimensions = {top:0, left:0, width:0, height:0, yLine:0}; // could be cached + this.boundingBox = {top:0, left:0, right:0, bottom:0}; + + this.imagelist = imagelist; + this.grouplist = grouplist; + + // physics properties + this.fx = 0.0; // external force x + this.fy = 0.0; // external force y + this.vx = 0.0; // velocity x + this.vy = 0.0; // velocity y + this.x = null; + this.y = null; + + // used for reverting to previous position on stabilization + this.previousState = {vx:0,vy:0,x:0,y:0}; + + this.damping = networkConstants.physics.damping; // written every time gravity is calculated + this.fixedData = {x:null,y:null}; + + this.setProperties(properties, constants); + + // creating the variables for clustering + this.resetCluster(); + this.dynamicEdgesLength = 0; + this.clusterSession = 0; + this.clusterSizeWidthFactor = networkConstants.clustering.nodeScaling.width; + this.clusterSizeHeightFactor = networkConstants.clustering.nodeScaling.height; + this.clusterSizeRadiusFactor = networkConstants.clustering.nodeScaling.radius; + this.maxNodeSizeIncrements = networkConstants.clustering.maxNodeSizeIncrements; + this.growthIndicator = 0; + + // variables to tell the node about the network. + this.networkScaleInv = 1; + this.networkScale = 1; + this.canvasTopLeft = {"x": -300, "y": -300}; + this.canvasBottomRight = {"x": 300, "y": 300}; + this.parentEdgeId = null; + } /** - * - * - * @param {array} groupIds - * @param {object} groupsData - * @param {object} groupRanges | this is being filled here - * @private + * Revert the position and velocity of the previous step. */ - LineGraph.prototype._getYRanges = function (groupIds, groupsData, groupRanges) { - var groupData, group, i; - var barCombinedDataLeft = []; - var barCombinedDataRight = []; - var options; - if (groupIds.length > 0) { - for (i = 0; i < groupIds.length; i++) { - groupData = groupsData[groupIds[i]]; - options = this.groups[groupIds[i]].options; - if (groupData.length > 0) { - group = this.groups[groupIds[i]]; - // if bar graphs are stacked, their range need to be handled differently and accumulated over all groups. - if (options.barChart.handleOverlap == 'stack' && options.style == 'bar') { - if (options.yAxisOrientation == 'left') {barCombinedDataLeft = barCombinedDataLeft.concat(group.getYRange(groupData)) ;} - else {barCombinedDataRight = barCombinedDataRight.concat(group.getYRange(groupData));} - } - else { - groupRanges[groupIds[i]] = group.getYRange(groupData,groupIds[i]); - } - } - } + Node.prototype.revertPosition = function() { + this.x = this.previousState.x; + this.y = this.previousState.y; + this.vx = this.previousState.vx; + this.vy = this.previousState.vy; + } - // if bar graphs are stacked, their range need to be handled differently and accumulated over all groups. - BarGraphFunctions.getStackedBarYRange(barCombinedDataLeft , groupRanges, groupIds, '__barchartLeft' , 'left' ); - BarGraphFunctions.getStackedBarYRange(barCombinedDataRight, groupRanges, groupIds, '__barchartRight', 'right'); + + /** + * (re)setting the clustering variables and objects + */ + Node.prototype.resetCluster = function() { + // clustering variables + this.formationScale = undefined; // this is used to determine when to open the cluster + this.clusterSize = 1; // this signifies the total amount of nodes in this cluster + this.containedNodes = {}; + this.containedEdges = {}; + this.clusterSessions = []; + }; + + /** + * Attach a edge to the node + * @param {Edge} edge + */ + Node.prototype.attachEdge = function(edge) { + if (this.edges.indexOf(edge) == -1) { + this.edges.push(edge); + } + if (this.dynamicEdges.indexOf(edge) == -1) { + this.dynamicEdges.push(edge); + } + this.dynamicEdgesLength = this.dynamicEdges.length; + }; + + /** + * Detach a edge from the node + * @param {Edge} edge + */ + Node.prototype.detachEdge = function(edge) { + var index = this.edges.indexOf(edge); + if (index != -1) { + this.edges.splice(index, 1); + } + index = this.dynamicEdges.indexOf(edge); + if (index != -1) { + this.dynamicEdges.splice(index, 1); } + this.dynamicEdgesLength = this.dynamicEdges.length; }; /** - * this sets the Y ranges for the Y axis. It also determines which of the axis should be shown or hidden. - * @param {Array} groupIds - * @param {Object} groupRanges - * @private + * Set or overwrite properties for the node + * @param {Object} properties an object with properties + * @param {Object} constants and object with default, global properties */ - LineGraph.prototype._updateYAxis = function (groupIds, groupRanges) { - var resized = false; - var yAxisLeftUsed = false; - var yAxisRightUsed = false; - var minLeft = 1e9, minRight = 1e9, maxLeft = -1e9, maxRight = -1e9, minVal, maxVal; - // if groups are present - if (groupIds.length > 0) { - // this is here to make sure that if there are no items in the axis but there are groups, that there is no infinite draw/redraw loop. - for (var i = 0; i < groupIds.length; i++) { - var group = this.groups[groupIds[i]]; - if (group && group.options.yAxisOrientation != 'right') { - yAxisLeftUsed = true; - minLeft = 0; - maxLeft = 0; - } - else if (group && group.options.yAxisOrientation) { - yAxisRightUsed = true; - minRight = 0; - maxRight = 0; - } - } + Node.prototype.setProperties = function(properties, constants) { + if (!properties) { + return; + } - // if there are items: - for (var i = 0; i < groupIds.length; i++) { - if (groupRanges.hasOwnProperty(groupIds[i])) { - if (groupRanges[groupIds[i]].ignore !== true) { - minVal = groupRanges[groupIds[i]].min; - maxVal = groupRanges[groupIds[i]].max; + var fields = ['borderWidth','borderWidthSelected','shape','image','brokenImage','radius','fontColor', + 'fontSize','fontFace','fontFill','fontStrokeWidth','fontStrokeColor','group','mass' + ]; + util.selectiveDeepExtend(fields, this.options, properties); - if (groupRanges[groupIds[i]].yAxisOrientation != 'right') { - yAxisLeftUsed = true; - minLeft = minLeft > minVal ? minVal : minLeft; - maxLeft = maxLeft < maxVal ? maxVal : maxLeft; - } - else { - yAxisRightUsed = true; - minRight = minRight > minVal ? minVal : minRight; - maxRight = maxRight < maxVal ? maxVal : maxRight; - } - } - } - } + // basic properties + if (properties.id !== undefined) {this.id = properties.id;} + if (properties.label !== undefined) {this.label = properties.label; this.originalLabel = properties.label;} + if (properties.title !== undefined) {this.title = properties.title;} + if (properties.x !== undefined) {this.x = properties.x;} + if (properties.y !== undefined) {this.y = properties.y;} + if (properties.value !== undefined) {this.value = properties.value;} + if (properties.level !== undefined) {this.level = properties.level; this.preassignedLevel = true;} - if (yAxisLeftUsed == true) { - this.yAxisLeft.setRange(minLeft, maxLeft); + // navigation controls properties + if (properties.horizontalAlignLeft !== undefined) {this.horizontalAlignLeft = properties.horizontalAlignLeft;} + if (properties.verticalAlignTop !== undefined) {this.verticalAlignTop = properties.verticalAlignTop;} + if (properties.triggerFunction !== undefined) {this.triggerFunction = properties.triggerFunction;} + + if (this.id === undefined) { + throw "Node must have an id"; + } + + // copy group properties + if (typeof properties.group === 'number' || (typeof properties.group === 'string' && properties.group != '')) { + var groupObj = this.grouplist.get(properties.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); + } + // individual shape properties + if (properties.radius !== undefined) {this.baseRadiusValue = this.options.radius;} + if (properties.color !== undefined) {this.options.color = util.parseColor(properties.color);} + + if (this.options.image !== undefined && this.options.image!= "") { + if (this.imagelist) { + this.imageObj = this.imagelist.load(this.options.image, this.options.brokenImage); } - if (yAxisRightUsed == true) { - this.yAxisRight.setRange(minRight, maxRight); + else { + throw "No imagelist provided"; } } - resized = this._toggleAxisVisiblity(yAxisLeftUsed , this.yAxisLeft) || resized; - resized = this._toggleAxisVisiblity(yAxisRightUsed, this.yAxisRight) || resized; - if (yAxisRightUsed == true && yAxisLeftUsed == true) { - this.yAxisLeft.drawIcons = true; - this.yAxisRight.drawIcons = true; + if (properties.allowedToMoveX !== undefined) { + this.xFixed = !properties.allowedToMoveX; + this.allowedToMoveX = properties.allowedToMoveX; } - else { - this.yAxisLeft.drawIcons = false; - this.yAxisRight.drawIcons = false; + else if (properties.x !== undefined && this.allowedToMoveX == false) { + this.xFixed = true; } - this.yAxisRight.master = !yAxisLeftUsed; - if (this.yAxisRight.master == false) { - if (yAxisRightUsed == true) {this.yAxisLeft.lineOffset = this.yAxisRight.width;} - else {this.yAxisLeft.lineOffset = 0;} - resized = this.yAxisLeft.redraw() || resized; - this.yAxisRight.stepPixelsForced = this.yAxisLeft.stepPixels; - this.yAxisRight.zeroCrossing = this.yAxisLeft.zeroCrossing; - resized = this.yAxisRight.redraw() || resized; + + if (properties.allowedToMoveY !== undefined) { + this.yFixed = !properties.allowedToMoveY; + this.allowedToMoveY = properties.allowedToMoveY; } - else { - resized = this.yAxisRight.redraw() || resized; + else if (properties.y !== undefined && this.allowedToMoveY == false) { + this.yFixed = true; } - // clean the accumulated lists - if (groupIds.indexOf('__barchartLeft') != -1) { - groupIds.splice(groupIds.indexOf('__barchartLeft'),1); + this.radiusFixed = this.radiusFixed || (properties.radius !== undefined); + + if (this.options.shape === 'image' || this.options.shape === 'circularImage') { + this.options.radiusMin = constants.nodes.widthMin; + this.options.radiusMax = constants.nodes.widthMax; } - if (groupIds.indexOf('__barchartRight') != -1) { - groupIds.splice(groupIds.indexOf('__barchartRight'),1); + + // choose draw method depending on the shape + switch (this.options.shape) { + case 'database': this.draw = this._drawDatabase; this.resize = this._resizeDatabase; break; + case 'box': this.draw = this._drawBox; this.resize = this._resizeBox; break; + case 'circle': this.draw = this._drawCircle; this.resize = this._resizeCircle; break; + case 'ellipse': this.draw = this._drawEllipse; this.resize = this._resizeEllipse; break; + // TODO: add diamond shape + case 'image': this.draw = this._drawImage; this.resize = this._resizeImage; break; + case 'circularImage': this.draw = this._drawCircularImage; this.resize = this._resizeCircularImage; break; + case 'text': this.draw = this._drawText; this.resize = this._resizeText; break; + case 'dot': this.draw = this._drawDot; this.resize = this._resizeShape; break; + case 'square': this.draw = this._drawSquare; this.resize = this._resizeShape; break; + case 'triangle': this.draw = this._drawTriangle; this.resize = this._resizeShape; break; + case 'triangleDown': this.draw = this._drawTriangleDown; this.resize = this._resizeShape; break; + case 'star': this.draw = this._drawStar; this.resize = this._resizeShape; break; + default: this.draw = this._drawEllipse; this.resize = this._resizeEllipse; break; } + // reset the size of the node, this can be changed + this._reset(); - return resized; }; + /** + * select this node + */ + Node.prototype.select = function() { + this.selected = true; + this._reset(); + }; /** - * This shows or hides the Y axis if needed. If there is a change, the changed event is emitted by the updateYAxis function - * - * @param {boolean} axisUsed - * @returns {boolean} - * @private - * @param axis + * unselect this node */ - LineGraph.prototype._toggleAxisVisiblity = function (axisUsed, axis) { - var changed = false; - if (axisUsed == false) { - if (axis.dom.frame.parentNode && axis.hidden == false) { - axis.hide() - changed = true; - } - } - else { - if (!axis.dom.frame.parentNode && axis.hidden == true) { - axis.show(); - changed = true; - } - } - return changed; + Node.prototype.unselect = function() { + this.selected = false; + this._reset(); }; /** - * This uses the DataAxis object to generate the correct X coordinate on the SVG window. It uses the - * util function toScreen to get the x coordinate from the timestamp. It also pre-filters the data and get the minMax ranges for - * the yAxis. - * - * @param datapoints - * @returns {Array} - * @private + * Reset the calculated size of the node, forces it to recalculate its size */ - LineGraph.prototype._convertXcoordinates = function (datapoints) { - var extractedData = []; - var xValue, yValue; - var toScreen = this.body.util.toScreen; - - for (var i = 0; i < datapoints.length; i++) { - xValue = toScreen(datapoints[i].x) + this.props.width; - yValue = datapoints[i].y; - extractedData.push({x: xValue, y: yValue}); - } + Node.prototype.clearSizeCache = function() { + this._reset(); + }; - return extractedData; + /** + * Reset the calculated size of the node, forces it to recalculate its size + * @private + */ + Node.prototype._reset = function() { + this.width = undefined; + this.height = undefined; }; + /** + * get the title of this node. + * @return {string} title The title of the node, or undefined when no title + * has been set. + */ + Node.prototype.getTitle = function() { + return typeof this.title === "function" ? this.title() : this.title; + }; /** - * This uses the DataAxis object to generate the correct X coordinate on the SVG window. It uses the - * util function toScreen to get the x coordinate from the timestamp. It also pre-filters the data and get the minMax ranges for - * the yAxis. - * - * @param datapoints - * @param group - * @returns {Array} - * @private + * 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 */ - LineGraph.prototype._convertYcoordinates = function (datapoints, group) { - var extractedData = []; - var xValue, yValue; - var toScreen = this.body.util.toScreen; - var axis = this.yAxisLeft; - var svgHeight = Number(this.svg.style.height.replace('px','')); - if (group.options.yAxisOrientation == 'right') { - axis = this.yAxisRight; - } + Node.prototype.distanceToBorder = function (ctx, angle) { + var borderWidth = 1; - for (var i = 0; i < datapoints.length; i++) { - xValue = toScreen(datapoints[i].x) + this.props.width; - yValue = Math.round(axis.convertValue(datapoints[i].y)); - extractedData.push({x: xValue, y: yValue}); + if (!this.width) { + this.resize(ctx); } - group.setZeroPosition(Math.min(svgHeight, axis.convertValue(0))); - - return extractedData; - }; + switch (this.options.shape) { + case 'circle': + case 'dot': + return this.options.radius+ borderWidth; + case 'ellipse': + 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); - module.exports = LineGraph; + // TODO: implement distanceToBorder for database + // TODO: implement distanceToBorder for triangle + // TODO: implement distanceToBorder for triangleDown + case 'box': + case 'image': + case 'text': + default: + if (this.width) { + return Math.min( + Math.abs(this.width / 2 / Math.cos(angle)), + Math.abs(this.height / 2 / Math.sin(angle))) + borderWidth; + // TODO: reckon with border radius too in case of box + } + else { + return 0; + } -/***/ }, -/* 44 */ -/***/ function(module, exports, __webpack_require__) { + } + // TODO: implement calculation of distance to border for all shapes + }; - var util = __webpack_require__(1); - var DOMutil = __webpack_require__(6); - var Component = __webpack_require__(23); - var DataStep = __webpack_require__(45); + /** + * Set forces acting on the node + * @param {number} fx Force in horizontal direction + * @param {number} fy Force in vertical direction + */ + Node.prototype._setForce = function(fx, fy) { + this.fx = fx; + this.fy = fy; + }; /** - * A horizontal time axis - * @param {Object} [options] See DataAxis.setOptions for the available - * options. - * @constructor DataAxis - * @extends Component - * @param body + * Add forces acting on the node + * @param {number} fx Force in horizontal direction + * @param {number} fy Force in vertical direction + * @private */ - function DataAxis (body, options, svg, linegraphOptions) { - this.id = util.randomUUID(); - this.body = body; + Node.prototype._addForce = function(fx, fy) { + this.fx += fx; + this.fy += fy; + }; - this.defaultOptions = { - orientation: 'left', // supported: 'left', 'right' - showMinorLabels: true, - showMajorLabels: true, - icons: true, - majorLinesOffset: 7, - minorLinesOffset: 4, - labelOffsetX: 10, - labelOffsetY: 2, - iconWidth: 20, - width: '40px', - visible: true, - alignZeros: true, - customRange: { - left: {min:undefined, max:undefined}, - right: {min:undefined, max:undefined} - }, - title: { - left: {text:undefined}, - right: {text:undefined} - }, - format: { - left: {decimals: undefined}, - right: {decimals: undefined} - } - }; + /** + * Store the state before the next step + */ + Node.prototype.storeState = function() { + this.previousState.x = this.x; + this.previousState.y = this.y; + this.previousState.vx = this.vx; + this.previousState.vy = this.vy; + } - this.linegraphOptions = linegraphOptions; - this.linegraphSVG = svg; - this.props = {}; - this.DOMelements = { // dynamic elements - lines: {}, - labels: {}, - title: {} - }; - - this.dom = {}; - - this.range = {start:0, end:0}; - - this.options = util.extend({}, this.defaultOptions); - this.conversionFactor = 1; - - this.setOptions(options); - this.width = Number(('' + this.options.width).replace("px","")); - this.minWidth = this.width; - this.height = this.linegraphSVG.offsetHeight; - this.hidden = false; - - this.stepPixels = 25; - this.stepPixelsForced = 25; - this.zeroCrossing = -1; - - this.lineOffset = 0; - this.master = true; - this.svgElements = {}; - this.iconsRemoved = false; - - - this.groups = {}; - this.amountOfGroups = 0; - - // create the HTML DOM - this._create(); - - var me = this; - this.body.emitter.on("verticalDrag", function() { - me.dom.lineContainer.style.top = me.body.domProps.scrollTop + 'px'; - }); - } - - DataAxis.prototype = new Component(); - - - DataAxis.prototype.addGroup = function(label, graphOptions) { - if (!this.groups.hasOwnProperty(label)) { - this.groups[label] = graphOptions; + /** + * Perform one discrete step for the node + * @param {number} interval Time interval in seconds + */ + Node.prototype.discreteStep = function(interval) { + this.storeState(); + if (!this.xFixed) { + var dx = this.damping * this.vx; // damping force + var ax = (this.fx - dx) / this.options.mass; // acceleration + this.vx += ax * interval; // velocity + this.x += this.vx * interval; // position + } + else { + this.fx = 0; + this.vx = 0; } - this.amountOfGroups += 1; - }; - - DataAxis.prototype.updateGroup = function(label, graphOptions) { - this.groups[label] = graphOptions; - }; - DataAxis.prototype.removeGroup = function(label) { - if (this.groups.hasOwnProperty(label)) { - delete this.groups[label]; - this.amountOfGroups -= 1; + if (!this.yFixed) { + var dy = this.damping * this.vy; // damping force + var ay = (this.fy - dy) / this.options.mass; // acceleration + this.vy += ay * interval; // velocity + this.y += this.vy * interval; // position + } + else { + this.fy = 0; + this.vy = 0; } }; - DataAxis.prototype.setOptions = function (options) { - if (options) { - var redraw = false; - if (this.options.orientation != options.orientation && options.orientation !== undefined) { - redraw = true; - } - var fields = [ - 'orientation', - 'showMinorLabels', - 'showMajorLabels', - 'icons', - 'majorLinesOffset', - 'minorLinesOffset', - 'labelOffsetX', - 'labelOffsetY', - 'iconWidth', - 'width', - 'visible', - 'customRange', - 'title', - 'format', - 'alignZeros' - ]; - util.selectiveExtend(fields, this.options, options); - this.minWidth = Number(('' + this.options.width).replace("px","")); + /** + * Perform one discrete step for the node + * @param {number} interval Time interval in seconds + * @param {number} maxVelocity The speed limit imposed on the velocity + */ + Node.prototype.discreteStepLimited = function(interval, maxVelocity) { + this.storeState(); + if (!this.xFixed) { + var dx = this.damping * this.vx; // damping force + var ax = (this.fx - dx) / this.options.mass; // acceleration + this.vx += ax * interval; // velocity + this.vx = (Math.abs(this.vx) > maxVelocity) ? ((this.vx > 0) ? maxVelocity : -maxVelocity) : this.vx; + this.x += this.vx * interval; // position + } + else { + this.fx = 0; + this.vx = 0; + } - if (redraw == true && this.dom.frame) { - this.hide(); - this.show(); - } + if (!this.yFixed) { + var dy = this.damping * this.vy; // damping force + var ay = (this.fy - dy) / this.options.mass; // acceleration + this.vy += ay * interval; // velocity + this.vy = (Math.abs(this.vy) > maxVelocity) ? ((this.vy > 0) ? maxVelocity : -maxVelocity) : this.vy; + this.y += this.vy * interval; // position + } + else { + this.fy = 0; + this.vy = 0; } }; - /** - * Create the HTML DOM for the DataAxis + * Check if this node has a fixed x and y position + * @return {boolean} true if fixed, false if not */ - DataAxis.prototype._create = function() { - this.dom.frame = document.createElement('div'); - this.dom.frame.style.width = this.options.width; - this.dom.frame.style.height = this.height; - - this.dom.lineContainer = document.createElement('div'); - this.dom.lineContainer.style.width = '100%'; - this.dom.lineContainer.style.height = this.height; - this.dom.lineContainer.style.position = 'relative'; - - // create svg element for graph drawing. - this.svg = document.createElementNS('http://www.w3.org/2000/svg',"svg"); - this.svg.style.position = "absolute"; - this.svg.style.top = '0px'; - this.svg.style.height = '100%'; - this.svg.style.width = '100%'; - this.svg.style.display = "block"; - this.dom.frame.appendChild(this.svg); + Node.prototype.isFixed = function() { + return (this.xFixed && this.yFixed); }; - DataAxis.prototype._redrawGroupIcons = function () { - DOMutil.prepareElements(this.svgElements); - - var x; - var iconWidth = this.options.iconWidth; - var iconHeight = 15; - var iconOffset = 4; - var y = iconOffset + 0.5 * iconHeight; + /** + * Check if this node is moving + * @param {number} vmin the minimum velocity considered as "moving" + * @return {boolean} true if moving, false if it has no velocity + */ + Node.prototype.isMoving = function(vmin) { + var velocity = Math.sqrt(Math.pow(this.vx,2) + Math.pow(this.vy,2)); + // this.velocity = Math.sqrt(Math.pow(this.vx,2) + Math.pow(this.vy,2)) + return (velocity > vmin); + }; - if (this.options.orientation == 'left') { - x = iconOffset; - } - else { - x = this.width - iconWidth - iconOffset; - } + /** + * check if this node is selecte + * @return {boolean} selected True if node is selected, else false + */ + Node.prototype.isSelected = function() { + return this.selected; + }; - for (var groupId in this.groups) { - if (this.groups.hasOwnProperty(groupId)) { - if (this.groups[groupId].visible == true && (this.linegraphOptions.visibility[groupId] === undefined || this.linegraphOptions.visibility[groupId] == true)) { - this.groups[groupId].drawIcon(x, y, this.svgElements, this.svg, iconWidth, iconHeight); - y += iconHeight + iconOffset; - } - } - } + /** + * Retrieve the value of the node. Can be undefined + * @return {Number} value + */ + Node.prototype.getValue = function() { + return this.value; + }; - DOMutil.cleanupElements(this.svgElements); - this.iconsRemoved = false; + /** + * Calculate the distance from the nodes location to the given location (x,y) + * @param {Number} x + * @param {Number} y + * @return {Number} value + */ + Node.prototype.getDistance = function(x, y) { + var dx = this.x - x, + dy = this.y - y; + return Math.sqrt(dx * dx + dy * dy); }; - DataAxis.prototype._cleanupIcons = function() { - if (this.iconsRemoved == false) { - DOMutil.prepareElements(this.svgElements); - DOMutil.cleanupElements(this.svgElements); - this.iconsRemoved = true; - } - } /** - * Create the HTML DOM for the DataAxis + * Adjust the value range of the node. The node will adjust it's radius + * based on its value. + * @param {Number} min + * @param {Number} max */ - DataAxis.prototype.show = function() { - this.hidden = false; - if (!this.dom.frame.parentNode) { - if (this.options.orientation == 'left') { - this.body.dom.left.appendChild(this.dom.frame); + Node.prototype.setValueRange = function(min, max) { + if (!this.radiusFixed && this.value !== undefined) { + if (max == min) { + this.options.radius= (this.options.radiusMin + this.options.radiusMax) / 2; } else { - this.body.dom.right.appendChild(this.dom.frame); + var scale = (this.options.radiusMax - this.options.radiusMin) / (max - min); + this.options.radius= (this.value - min) * scale + this.options.radiusMin; } } - - if (!this.dom.lineContainer.parentNode) { - this.body.dom.backgroundHorizontal.appendChild(this.dom.lineContainer); - } + this.baseRadiusValue = this.options.radius; }; /** - * Create the HTML DOM for the DataAxis + * Draw this node in the given canvas + * The 2d context of a HTML canvas can be retrieved by canvas.getContext("2d"); + * @param {CanvasRenderingContext2D} ctx */ - DataAxis.prototype.hide = function() { - this.hidden = true; - if (this.dom.frame.parentNode) { - this.dom.frame.parentNode.removeChild(this.dom.frame); - } - - if (this.dom.lineContainer.parentNode) { - this.dom.lineContainer.parentNode.removeChild(this.dom.lineContainer); - } + Node.prototype.draw = function(ctx) { + throw "Draw method not initialized for node"; }; /** - * Set a range (start and end) - * @param end - * @param start - * @param end + * 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 */ - DataAxis.prototype.setRange = function (start, end) { - if (this.master == false && this.options.alignZeros == true && this.zeroCrossing != -1) { - if (start > 0) { - start = 0; - } - } - this.range.start = start; - this.range.end = end; + Node.prototype.resize = function(ctx) { + throw "Resize method not initialized for node"; }; /** - * Repaint the component - * @return {boolean} Returns true if the component is resized + * 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 */ - DataAxis.prototype.redraw = function () { - var resized = false; - var activeGroups = 0; - - // Make sure the line container adheres to the vertical scrolling. - this.dom.lineContainer.style.top = this.body.domProps.scrollTop + 'px'; + Node.prototype.isOverlappingWith = function(obj) { + return (this.left < obj.right && + this.left + this.width > obj.left && + this.top < obj.bottom && + this.top + this.height > obj.top); + }; - for (var groupId in this.groups) { - if (this.groups.hasOwnProperty(groupId)) { - if (this.groups[groupId].visible == true && (this.linegraphOptions.visibility[groupId] === undefined || this.linegraphOptions.visibility[groupId] == true)) { - activeGroups++; + Node.prototype._resizeImage = function (ctx) { + // TODO: pre calculate the image size + + if (!this.width || !this.height) { // undefined or 0 + var width, height; + if (this.value) { + this.options.radius= this.baseRadiusValue; + var scale = this.imageObj.height / this.imageObj.width; + if (scale !== undefined) { + width = this.options.radius|| this.imageObj.width; + height = this.options.radius* scale || this.imageObj.height; + } + else { + width = 0; + height = 0; } } - } - if (this.amountOfGroups == 0 || activeGroups == 0) { - this.hide(); - } - else { - this.show(); - this.height = Number(this.linegraphSVG.style.height.replace("px","")); - - // svg offsetheight did not work in firefox and explorer... - this.dom.lineContainer.style.height = this.height + 'px'; - this.width = this.options.visible == true ? Number(('' + this.options.width).replace("px","")) : 0; - - var props = this.props; - var frame = this.dom.frame; - - // update classname - frame.className = 'dataaxis'; - - // calculate character width and height - this._calculateCharSize(); - - var orientation = this.options.orientation; - var showMinorLabels = this.options.showMinorLabels; - var showMajorLabels = this.options.showMajorLabels; - - // determine the width and height of the elements for the axis - props.minorLabelHeight = showMinorLabels ? props.minorCharHeight : 0; - props.majorLabelHeight = showMajorLabels ? props.majorCharHeight : 0; - - props.minorLineWidth = this.body.dom.backgroundHorizontal.offsetWidth - this.lineOffset - this.width + 2 * this.options.minorLinesOffset; - props.minorLineHeight = 1; - props.majorLineWidth = this.body.dom.backgroundHorizontal.offsetWidth - this.lineOffset - this.width + 2 * this.options.majorLinesOffset; - props.majorLineHeight = 1; - - // take frame offline while updating (is almost twice as fast) - if (orientation == 'left') { - frame.style.top = '0'; - frame.style.left = '0'; - frame.style.bottom = ''; - frame.style.width = this.width + 'px'; - frame.style.height = this.height + "px"; - this.props.width = this.body.domProps.left.width; - this.props.height = this.body.domProps.left.height; + else { + width = this.imageObj.width; + height = this.imageObj.height; } - else { // right - frame.style.top = ''; - frame.style.bottom = '0'; - frame.style.left = '0'; - frame.style.width = this.width + 'px'; - frame.style.height = this.height + "px"; - this.props.width = this.body.domProps.right.width; - this.props.height = this.body.domProps.right.height; + this.width = width; + this.height = height; + + this.growthIndicator = 0; + if (this.width > 0 && this.height > 0) { + this.width += Math.min(this.clusterSize - 1, this.maxNodeSizeIncrements) * this.clusterSizeWidthFactor; + this.height += Math.min(this.clusterSize - 1, this.maxNodeSizeIncrements) * this.clusterSizeHeightFactor; + this.options.radius+= Math.min(this.clusterSize - 1, this.maxNodeSizeIncrements) * this.clusterSizeRadiusFactor; + this.growthIndicator = this.width - width; } + } + }; - resized = this._redrawLabels(); - resized = this._isResized() || resized; + Node.prototype._drawImageAtPosition = function (ctx) { + if (this.imageObj.width != 0 ) { + // draw the shade + if (this.clusterSize > 1) { + var lineWidth = ((this.clusterSize > 1) ? 10 : 0.0); + lineWidth *= this.networkScaleInv; + lineWidth = Math.min(0.2 * this.width,lineWidth); - if (this.options.icons == true) { - this._redrawGroupIcons(); - } - else { - this._cleanupIcons(); + ctx.globalAlpha = 0.5; + ctx.drawImage(this.imageObj, this.left - lineWidth, this.top - lineWidth, this.width + 2*lineWidth, this.height + 2*lineWidth); } - this._redrawTitle(orientation); + // draw the image + ctx.globalAlpha = 1.0; + ctx.drawImage(this.imageObj, this.left, this.top, this.width, this.height); } - return resized; }; - /** - * Repaint major and minor text labels and vertical grid lines - * @private - */ - DataAxis.prototype._redrawLabels = function () { - var resized = false; - DOMutil.prepareElements(this.DOMelements.lines); - DOMutil.prepareElements(this.DOMelements.labels); - - var orientation = this.options['orientation']; + Node.prototype._drawImageLabel = function (ctx) { + var yLabel; + var offset = 0; + + if (this.height){ + offset = this.height / 2; + var labelDimensions = this.getTextSize(ctx); + + if (labelDimensions.lineCount >= 1){ + offset += labelDimensions.height / 2; + offset += 3; + } + } + + yLabel = this.y + offset; - // calculate range and step (step such that we have space for 7 characters per label) - var minimumStep = this.master ? this.props.majorCharHeight || 10 : this.stepPixelsForced; + this._label(ctx, this.label, this.x, yLabel, undefined); + }; - var step = new DataStep( - this.range.start, - this.range.end, - minimumStep, - this.dom.frame.offsetHeight, - this.options.customRange[this.options.orientation], - this.master == false && this.options.alignZeros // doess the step have to align zeros? only if not master and the options is on - ); + Node.prototype._drawImage = function (ctx) { + this._resizeImage(ctx); + this.left = this.x - this.width / 2; + this.top = this.y - this.height / 2; - this.step = step; - // get the distance in pixels for a step - // dead space is space that is "left over" after a step - var stepPixels = (this.dom.frame.offsetHeight - (step.deadSpace * (this.dom.frame.offsetHeight / step.marginRange))) / (((step.marginRange - step.deadSpace) / step.step)); + this._drawImageAtPosition(ctx); - this.stepPixels = stepPixels; + this.boundingBox.top = this.top; + this.boundingBox.left = this.left; + this.boundingBox.right = this.left + this.width; + this.boundingBox.bottom = this.top + this.height; - var amountOfSteps = this.height / stepPixels; - var stepDifference = 0; + this._drawImageLabel(ctx); + this.boundingBox.left = Math.min(this.boundingBox.left, this.labelDimensions.left); + this.boundingBox.right = Math.max(this.boundingBox.right, this.labelDimensions.left + this.labelDimensions.width); + this.boundingBox.bottom = Math.max(this.boundingBox.bottom, this.boundingBox.bottom + this.labelDimensions.height); + }; - // the slave axis needs to use the same horizontal lines as the master axis. - if (this.master == false) { - stepPixels = this.stepPixelsForced; - stepDifference = Math.round((this.dom.frame.offsetHeight / stepPixels) - amountOfSteps); - for (var i = 0; i < 0.5 * stepDifference; i++) { - step.previous(); - } - amountOfSteps = this.height / stepPixels; + Node.prototype._resizeCircularImage = function (ctx) { + if(!this.imageObj.src || !this.imageObj.width || !this.imageObj.height){ + if (!this.width) { + var diameter = this.options.radius * 2; + this.width = diameter; + this.height = diameter; - if (this.zeroCrossing != -1 && this.options.alignZeros == true) { - var zeroStepDifference = (step.marginEnd / step.step) - this.zeroCrossing; - if (zeroStepDifference > 0) { - for (var i = 0; i < zeroStepDifference; i++) {step.next();} - } - else if (zeroStepDifference < 0) { - for (var i = 0; i < -zeroStepDifference; i++) {step.previous();} - } + // scaling used for clustering + //this.width += Math.min(this.clusterSize - 1, this.maxNodeSizeIncrements) * 0.5 * this.clusterSizeWidthFactor; + //this.height += Math.min(this.clusterSize - 1, this.maxNodeSizeIncrements) * 0.5 * this.clusterSizeHeightFactor; + this.options.radius += Math.min(this.clusterSize - 1, this.maxNodeSizeIncrements) * 0.5 * this.clusterSizeRadiusFactor; + this.growthIndicator = this.options.radius- 0.5*diameter; + this._swapToImageResizeWhenImageLoaded = true; } } else { - amountOfSteps += 0.25; + if (this._swapToImageResizeWhenImageLoaded) { + this.width = 0; + this.height = 0; + delete this._swapToImageResizeWhenImageLoaded; + } + this._resizeImage(ctx); } + }; - this.valueAtZero = step.marginEnd; - var marginStartPos = 0; + Node.prototype._drawCircularImage = function (ctx) { + this._resizeCircularImage(ctx); - // do not draw the first label - var max = 1; + this.left = this.x - this.width / 2; + this.top = this.y - this.height / 2; + + var centerX = this.left + (this.width / 2); + var centerY = this.top + (this.height / 2); + var radius = Math.abs(this.height / 2); - // Get the number of decimal places - var decimals; - if(this.options.format[orientation] !== undefined) { - decimals = this.options.format[orientation].decimals; - } + this._drawRawCircle(ctx, centerX, centerY, radius); - this.maxLabelSize = 0; - var y = 0; - while (max < Math.round(amountOfSteps)) { - step.next(); - y = Math.round(max * stepPixels); - marginStartPos = max * stepPixels; - var isMajor = step.isMajor(); + ctx.save(); + ctx.circle(this.x, this.y, radius); + ctx.stroke(); + ctx.clip(); - if (this.options['showMinorLabels'] && isMajor == false || this.master == false && this.options['showMinorLabels'] == true) { - this._redrawLabel(y - 2, step.getCurrent(decimals), orientation, 'yAxis minor', this.props.minorCharHeight); - } + this._drawImageAtPosition(ctx); - if (isMajor && this.options['showMajorLabels'] && this.master == true || - this.options['showMinorLabels'] == false && this.master == false && isMajor == true) { - if (y >= 0) { - this._redrawLabel(y - 2, step.getCurrent(decimals), orientation, 'yAxis major', this.props.majorCharHeight); - } - this._redrawLine(y, orientation, 'grid horizontal major', this.options.majorLinesOffset, this.props.majorLineWidth); - } - else { - this._redrawLine(y, orientation, 'grid horizontal minor', this.options.minorLinesOffset, this.props.minorLineWidth); - } + ctx.restore(); - if (this.master == true && step.current == 0) { - this.zeroCrossing = max; - } + this.boundingBox.top = this.y - this.options.radius; + this.boundingBox.left = this.x - this.options.radius; + this.boundingBox.right = this.x + this.options.radius; + this.boundingBox.bottom = this.y + this.options.radius; - max++; - } + this._drawImageLabel(ctx); + + this.boundingBox.left = Math.min(this.boundingBox.left, this.labelDimensions.left); + this.boundingBox.right = Math.max(this.boundingBox.right, this.labelDimensions.left + this.labelDimensions.width); + this.boundingBox.bottom = Math.max(this.boundingBox.bottom, this.boundingBox.bottom + this.labelDimensions.height); + }; - if (this.master == false) { - this.conversionFactor = y / (this.valueAtZero - step.current); - } - else { - this.conversionFactor = this.dom.frame.offsetHeight / step.marginRange; - } + Node.prototype._resizeBox = function (ctx) { + if (!this.width) { + var margin = 5; + var textSize = this.getTextSize(ctx); + this.width = textSize.width + 2 * margin; + this.height = textSize.height + 2 * margin; - // Note that title is rotated, so we're using the height, not width! - var titleWidth = 0; - if (this.options.title[orientation] !== undefined && this.options.title[orientation].text !== undefined) { - titleWidth = this.props.titleCharHeight; - } - var offset = this.options.icons == true ? Math.max(this.options.iconWidth, titleWidth) + this.options.labelOffsetX + 15 : titleWidth + this.options.labelOffsetX + 15; + this.width += Math.min(this.clusterSize - 1, this.maxNodeSizeIncrements) * 0.5 * this.clusterSizeWidthFactor; + this.height += Math.min(this.clusterSize - 1, this.maxNodeSizeIncrements) * 0.5 * this.clusterSizeHeightFactor; + this.growthIndicator = this.width - (textSize.width + 2 * margin); + // this.options.radius+= Math.min(this.clusterSize - 1, this.maxNodeSizeIncrements) * 0.5 * this.clusterSizeRadiusFactor; - // this will resize the yAxis to accommodate the labels. - if (this.maxLabelSize > (this.width - offset) && this.options.visible == true) { - this.width = this.maxLabelSize + offset; - this.options.width = this.width + "px"; - DOMutil.cleanupElements(this.DOMelements.lines); - DOMutil.cleanupElements(this.DOMelements.labels); - this.redraw(); - resized = true; - } - // this will resize the yAxis if it is too big for the labels. - else if (this.maxLabelSize < (this.width - offset) && this.options.visible == true && this.width > this.minWidth) { - this.width = Math.max(this.minWidth,this.maxLabelSize + offset); - this.options.width = this.width + "px"; - DOMutil.cleanupElements(this.DOMelements.lines); - DOMutil.cleanupElements(this.DOMelements.labels); - this.redraw(); - resized = true; - } - else { - DOMutil.cleanupElements(this.DOMelements.lines); - DOMutil.cleanupElements(this.DOMelements.labels); - resized = false; } - - return resized; }; - DataAxis.prototype.convertValue = function (value) { - var invertedValue = this.valueAtZero - value; - var convertedValue = invertedValue * this.conversionFactor; - return convertedValue; - }; + Node.prototype._drawBox = function (ctx) { + this._resizeBox(ctx); - /** - * Create a label for the axis at position x - * @private - * @param y - * @param text - * @param orientation - * @param className - * @param characterHeight - */ - DataAxis.prototype._redrawLabel = function (y, text, orientation, className, characterHeight) { - // reuse redundant label - var label = DOMutil.getDOMElement('div',this.DOMelements.labels, this.dom.frame); //this.dom.redundant.labels.shift(); - label.className = className; - label.innerHTML = text; - if (orientation == 'left') { - label.style.left = '-' + this.options.labelOffsetX + 'px'; - label.style.textAlign = "right"; - } - else { - label.style.right = '-' + this.options.labelOffsetX + 'px'; - label.style.textAlign = "left"; - } + this.left = this.x - this.width / 2; + this.top = this.y - this.height / 2; - label.style.top = y - 0.5 * characterHeight + this.options.labelOffsetY + 'px'; + var clusterLineWidth = 2.5; + var borderWidth = this.options.borderWidth; + var selectionLineWidth = this.options.borderWidthSelected || 2 * this.options.borderWidth; - text += ''; + ctx.strokeStyle = this.selected ? this.options.color.highlight.border : this.hover ? this.options.color.hover.border : this.options.color.border; - var largestWidth = Math.max(this.props.majorCharWidth,this.props.minorCharWidth); - if (this.maxLabelSize < text.length * largestWidth) { - this.maxLabelSize = text.length * largestWidth; + // draw the outer border + if (this.clusterSize > 1) { + ctx.lineWidth = (this.selected ? selectionLineWidth : borderWidth) + ((this.clusterSize > 1) ? clusterLineWidth : 0.0); + ctx.lineWidth *= this.networkScaleInv; + ctx.lineWidth = Math.min(this.width,ctx.lineWidth); + + ctx.roundRect(this.left-2*ctx.lineWidth, this.top-2*ctx.lineWidth, this.width+4*ctx.lineWidth, this.height+4*ctx.lineWidth, this.options.radius); + ctx.stroke(); } + ctx.lineWidth = (this.selected ? selectionLineWidth : borderWidth) + ((this.clusterSize > 1) ? clusterLineWidth : 0.0); + ctx.lineWidth *= this.networkScaleInv; + ctx.lineWidth = Math.min(this.width,ctx.lineWidth); + + ctx.fillStyle = this.selected ? this.options.color.highlight.background : this.hover ? this.options.color.hover.background : this.options.color.background; + + ctx.roundRect(this.left, this.top, this.width, this.height, this.options.radius); + ctx.fill(); + ctx.stroke(); + + this.boundingBox.top = this.top; + this.boundingBox.left = this.left; + this.boundingBox.right = this.left + this.width; + this.boundingBox.bottom = this.top + this.height; + + this._label(ctx, this.label, this.x, this.y); }; - /** - * Create a minor line for the axis at position y - * @param y - * @param orientation - * @param className - * @param offset - * @param width - */ - DataAxis.prototype._redrawLine = function (y, orientation, className, offset, width) { - if (this.master == true) { - var line = DOMutil.getDOMElement('div',this.DOMelements.lines, this.dom.lineContainer);//this.dom.redundant.lines.shift(); - line.className = className; - line.innerHTML = ''; - if (orientation == 'left') { - line.style.left = (this.width - offset) + 'px'; - } - else { - line.style.right = (this.width - offset) + 'px'; - } + Node.prototype._resizeDatabase = function (ctx) { + if (!this.width) { + var margin = 5; + var textSize = this.getTextSize(ctx); + var size = textSize.width + 2 * margin; + this.width = size; + this.height = size; - line.style.width = width + 'px'; - line.style.top = y + 'px'; + // scaling used for clustering + this.width += Math.min(this.clusterSize - 1, this.maxNodeSizeIncrements) * this.clusterSizeWidthFactor; + this.height += Math.min(this.clusterSize - 1, this.maxNodeSizeIncrements) * this.clusterSizeHeightFactor; + this.options.radius+= Math.min(this.clusterSize - 1, this.maxNodeSizeIncrements) * this.clusterSizeRadiusFactor; + this.growthIndicator = this.width - size; } }; - /** - * Create a title for the axis - * @private - * @param orientation - */ - DataAxis.prototype._redrawTitle = function (orientation) { - DOMutil.prepareElements(this.DOMelements.title); + Node.prototype._drawDatabase = function (ctx) { + this._resizeDatabase(ctx); + this.left = this.x - this.width / 2; + this.top = this.y - this.height / 2; - // Check if the title is defined for this axes - if (this.options.title[orientation] !== undefined && this.options.title[orientation].text !== undefined) { - var title = DOMutil.getDOMElement('div', this.DOMelements.title, this.dom.frame); - title.className = 'yAxis title ' + orientation; - title.innerHTML = this.options.title[orientation].text; + var clusterLineWidth = 2.5; + var borderWidth = this.options.borderWidth; + var selectionLineWidth = this.options.borderWidthSelected || 2 * this.options.borderWidth; - // Add style - if provided - if (this.options.title[orientation].style !== undefined) { - util.addCssText(title, this.options.title[orientation].style); - } + ctx.strokeStyle = this.selected ? this.options.color.highlight.border : this.hover ? this.options.color.hover.border : this.options.color.border; - if (orientation == 'left') { - title.style.left = this.props.titleCharHeight + 'px'; - } - else { - title.style.right = this.props.titleCharHeight + 'px'; - } + // draw the outer border + if (this.clusterSize > 1) { + ctx.lineWidth = (this.selected ? selectionLineWidth : borderWidth) + ((this.clusterSize > 1) ? clusterLineWidth : 0.0); + ctx.lineWidth *= this.networkScaleInv; + ctx.lineWidth = Math.min(this.width,ctx.lineWidth); - title.style.width = this.height + 'px'; + ctx.database(this.x - this.width/2 - 2*ctx.lineWidth, this.y - this.height*0.5 - 2*ctx.lineWidth, this.width + 4*ctx.lineWidth, this.height + 4*ctx.lineWidth); + ctx.stroke(); } + ctx.lineWidth = (this.selected ? selectionLineWidth : borderWidth) + ((this.clusterSize > 1) ? clusterLineWidth : 0.0); + ctx.lineWidth *= this.networkScaleInv; + ctx.lineWidth = Math.min(this.width,ctx.lineWidth); - // we need to clean up in case we did not use all elements. - DOMutil.cleanupElements(this.DOMelements.title); - }; + ctx.fillStyle = this.selected ? this.options.color.highlight.background : this.hover ? this.options.color.hover.background : this.options.color.background; + ctx.database(this.x - this.width/2, this.y - this.height*0.5, this.width, this.height); + ctx.fill(); + ctx.stroke(); + this.boundingBox.top = this.top; + this.boundingBox.left = this.left; + this.boundingBox.right = this.left + this.width; + this.boundingBox.bottom = this.top + this.height; + this._label(ctx, this.label, this.x, this.y); + }; - /** - * Determine the size of text on the axis (both major and minor axis). - * The size is calculated only once and then cached in this.props. - * @private - */ - DataAxis.prototype._calculateCharSize = function () { - // determine the char width and height on the minor axis - if (!('minorCharHeight' in this.props)) { - var textMinor = document.createTextNode('0'); - var measureCharMinor = document.createElement('div'); - measureCharMinor.className = 'yAxis minor measure'; - measureCharMinor.appendChild(textMinor); - this.dom.frame.appendChild(measureCharMinor); + Node.prototype._resizeCircle = function (ctx) { + if (!this.width) { + var margin = 5; + var textSize = this.getTextSize(ctx); + var diameter = Math.max(textSize.width, textSize.height) + 2 * margin; + this.options.radius = diameter / 2; - this.props.minorCharHeight = measureCharMinor.clientHeight; - this.props.minorCharWidth = measureCharMinor.clientWidth; + this.width = diameter; + this.height = diameter; - this.dom.frame.removeChild(measureCharMinor); + // scaling used for clustering + // this.width += Math.min(this.clusterSize - 1, this.maxNodeSizeIncrements) * 0.5 * this.clusterSizeWidthFactor; + // this.height += Math.min(this.clusterSize - 1, this.maxNodeSizeIncrements) * 0.5 * this.clusterSizeHeightFactor; + this.options.radius += Math.min(this.clusterSize - 1, this.maxNodeSizeIncrements) * 0.5 * this.clusterSizeRadiusFactor; + this.growthIndicator = this.options.radius- 0.5*diameter; } + }; - if (!('majorCharHeight' in this.props)) { - var textMajor = document.createTextNode('0'); - var measureCharMajor = document.createElement('div'); - measureCharMajor.className = 'yAxis major measure'; - measureCharMajor.appendChild(textMajor); - this.dom.frame.appendChild(measureCharMajor); + Node.prototype._drawRawCircle = function (ctx, x, y, radius) { + var clusterLineWidth = 2.5; + var borderWidth = this.options.borderWidth; + var selectionLineWidth = this.options.borderWidthSelected || 2 * this.options.borderWidth; + + ctx.strokeStyle = this.selected ? this.options.color.highlight.border : this.hover ? this.options.color.hover.border : this.options.color.border; - this.props.majorCharHeight = measureCharMajor.clientHeight; - this.props.majorCharWidth = measureCharMajor.clientWidth; + // draw the outer border + if (this.clusterSize > 1) { + ctx.lineWidth = (this.selected ? selectionLineWidth : borderWidth) + ((this.clusterSize > 1) ? clusterLineWidth : 0.0); + ctx.lineWidth *= this.networkScaleInv; + ctx.lineWidth = Math.min(this.width,ctx.lineWidth); - this.dom.frame.removeChild(measureCharMajor); + ctx.circle(x, y, radius+2*ctx.lineWidth); + ctx.stroke(); } + ctx.lineWidth = (this.selected ? selectionLineWidth : borderWidth) + ((this.clusterSize > 1) ? clusterLineWidth : 0.0); + ctx.lineWidth *= this.networkScaleInv; + ctx.lineWidth = Math.min(this.width,ctx.lineWidth); - if (!('titleCharHeight' in this.props)) { - var textTitle = document.createTextNode('0'); - var measureCharTitle = document.createElement('div'); - measureCharTitle.className = 'yAxis title measure'; - measureCharTitle.appendChild(textTitle); - this.dom.frame.appendChild(measureCharTitle); + ctx.fillStyle = this.selected ? this.options.color.highlight.background : this.hover ? this.options.color.hover.background : this.options.color.background; + ctx.circle(this.x, this.y, radius); + ctx.fill(); + ctx.stroke(); + }; - this.props.titleCharHeight = measureCharTitle.clientHeight; - this.props.titleCharWidth = measureCharTitle.clientWidth; + Node.prototype._drawCircle = function (ctx) { + this._resizeCircle(ctx); + this.left = this.x - this.width / 2; + this.top = this.y - this.height / 2; - this.dom.frame.removeChild(measureCharTitle); - } - }; + this._drawRawCircle(ctx, this.x, this.y, this.options.radius); - /** - * Snap a date to a rounded value. - * The snap intervals are dependent on the current scale and step. - * @param {Date} date the date to be snapped. - * @return {Date} snappedDate - */ - DataAxis.prototype.snap = function(date) { - return this.step.snap(date); + this.boundingBox.top = this.y - this.options.radius; + this.boundingBox.left = this.x - this.options.radius; + this.boundingBox.right = this.x + this.options.radius; + this.boundingBox.bottom = this.y + this.options.radius; + + this._label(ctx, this.label, this.x, this.y); }; - module.exports = DataAxis; + Node.prototype._resizeEllipse = function (ctx) { + if (!this.width) { + var textSize = this.getTextSize(ctx); + this.width = textSize.width * 1.5; + this.height = textSize.height * 2; + if (this.width < this.height) { + this.width = this.height; + } + var defaultSize = this.width; -/***/ }, -/* 45 */ -/***/ function(module, exports, __webpack_require__) { + // scaling used for clustering + this.width += Math.min(this.clusterSize - 1, this.maxNodeSizeIncrements) * this.clusterSizeWidthFactor; + this.height += Math.min(this.clusterSize - 1, this.maxNodeSizeIncrements) * this.clusterSizeHeightFactor; + this.options.radius += Math.min(this.clusterSize - 1, this.maxNodeSizeIncrements) * this.clusterSizeRadiusFactor; + this.growthIndicator = this.width - defaultSize; + } + }; - /** - * @constructor DataStep - * The class DataStep is an iterator for data for the lineGraph. You provide a start data point and an - * end data point. The class itself determines the best scale (step size) based on the - * provided start Date, end Date, and minimumStep. - * - * If minimumStep is provided, the step size is chosen as close as possible - * to the minimumStep but larger than minimumStep. If minimumStep is not - * provided, the scale is set to 1 DAY. - * The minimumStep should correspond with the onscreen size of about 6 characters - * - * Alternatively, you can set a scale by hand. - * After creation, you can initialize the class by executing first(). Then you - * can iterate from the start date to the end date via next(). You can check if - * the end date is reached with the function hasNext(). After each step, you can - * retrieve the current date via getCurrent(). - * The DataStep has scales ranging from milliseconds, seconds, minutes, hours, - * days, to years. - * - * Version: 1.2 - * - * @param {Date} [start] The start date, for example new Date(2010, 9, 21) - * or new Date(2010, 9, 21, 23, 45, 00) - * @param {Date} [end] The end date - * @param {Number} [minimumStep] Optional. Minimum step size in milliseconds - */ - function DataStep(start, end, minimumStep, containerHeight, customRange, alignZeros) { - // variables - this.current = 0; + Node.prototype._drawEllipse = function (ctx) { + this._resizeEllipse(ctx); + this.left = this.x - this.width / 2; + this.top = this.y - this.height / 2; - this.autoScale = true; - this.stepIndex = 0; - this.step = 1; - this.scale = 1; + var clusterLineWidth = 2.5; + var borderWidth = this.options.borderWidth; + var selectionLineWidth = this.options.borderWidthSelected || 2 * this.options.borderWidth; - this.marginStart; - this.marginEnd; - this.deadSpace = 0; + ctx.strokeStyle = this.selected ? this.options.color.highlight.border : this.hover ? this.options.color.hover.border : this.options.color.border; - this.majorSteps = [1, 2, 5, 10]; - this.minorSteps = [0.25, 0.5, 1, 2]; + // draw the outer border + if (this.clusterSize > 1) { + ctx.lineWidth = (this.selected ? selectionLineWidth : borderWidth) + ((this.clusterSize > 1) ? clusterLineWidth : 0.0); + ctx.lineWidth *= this.networkScaleInv; + ctx.lineWidth = Math.min(this.width,ctx.lineWidth); - this.alignZeros = alignZeros; + ctx.ellipse(this.left-2*ctx.lineWidth, this.top-2*ctx.lineWidth, this.width+4*ctx.lineWidth, this.height+4*ctx.lineWidth); + ctx.stroke(); + } + ctx.lineWidth = (this.selected ? selectionLineWidth : borderWidth) + ((this.clusterSize > 1) ? clusterLineWidth : 0.0); + ctx.lineWidth *= this.networkScaleInv; + ctx.lineWidth = Math.min(this.width,ctx.lineWidth); - this.setRange(start, end, minimumStep, containerHeight, customRange); - } + ctx.fillStyle = this.selected ? this.options.color.highlight.background : this.hover ? this.options.color.hover.background : this.options.color.background; + ctx.ellipse(this.left, this.top, this.width, this.height); + ctx.fill(); + ctx.stroke(); + this.boundingBox.top = this.top; + this.boundingBox.left = this.left; + this.boundingBox.right = this.left + this.width; + this.boundingBox.bottom = this.top + this.height; - /** - * Set a new range - * If minimumStep is provided, the step size is chosen as close as possible - * to the minimumStep but larger than minimumStep. If minimumStep is not - * provided, the scale is set to 1 DAY. - * The minimumStep should correspond with the onscreen size of about 6 characters - * @param {Number} [start] The start date and time. - * @param {Number} [end] The end date and time. - * @param {Number} [minimumStep] Optional. Minimum step size in milliseconds - */ - DataStep.prototype.setRange = function(start, end, minimumStep, containerHeight, customRange) { - this._start = customRange.min === undefined ? start : customRange.min; - this._end = customRange.max === undefined ? end : customRange.max; + this._label(ctx, this.label, this.x, this.y); + }; - if (this._start == this._end) { - this._start -= 0.75; - this._end += 1; - } + Node.prototype._drawDot = function (ctx) { + this._drawShape(ctx, 'circle'); + }; - if (this.autoScale == true) { - this.setMinimumStep(minimumStep, containerHeight); - } + Node.prototype._drawTriangle = function (ctx) { + this._drawShape(ctx, 'triangle'); + }; - this.setFirst(customRange); + Node.prototype._drawTriangleDown = function (ctx) { + this._drawShape(ctx, 'triangleDown'); }; - /** - * Automatically determine the scale that bests fits the provided minimum step - * @param {Number} [minimumStep] The minimum step size in milliseconds - */ - DataStep.prototype.setMinimumStep = function(minimumStep, containerHeight) { - // round to floor - var size = this._end - this._start; - var safeSize = size * 1.2; - var minimumStepValue = minimumStep * (safeSize / containerHeight); - var orderOfMagnitude = Math.round(Math.log(safeSize)/Math.LN10); + Node.prototype._drawSquare = function (ctx) { + this._drawShape(ctx, 'square'); + }; - var minorStepIdx = -1; - var magnitudefactor = Math.pow(10,orderOfMagnitude); + Node.prototype._drawStar = function (ctx) { + this._drawShape(ctx, 'star'); + }; - var start = 0; - if (orderOfMagnitude < 0) { - start = orderOfMagnitude; - } + Node.prototype._resizeShape = function (ctx) { + if (!this.width) { + this.options.radius= this.baseRadiusValue; + var size = 2 * this.options.radius; + this.width = size; + this.height = size; - var solutionFound = false; - for (var i = start; Math.abs(i) <= Math.abs(orderOfMagnitude); i++) { - magnitudefactor = Math.pow(10,i); - for (var j = 0; j < this.minorSteps.length; j++) { - var stepSize = magnitudefactor * this.minorSteps[j]; - if (stepSize >= minimumStepValue) { - solutionFound = true; - minorStepIdx = j; - break; - } - } - if (solutionFound == true) { - break; - } + // scaling used for clustering + this.width += Math.min(this.clusterSize - 1, this.maxNodeSizeIncrements) * this.clusterSizeWidthFactor; + this.height += Math.min(this.clusterSize - 1, this.maxNodeSizeIncrements) * this.clusterSizeHeightFactor; + this.options.radius+= Math.min(this.clusterSize - 1, this.maxNodeSizeIncrements) * 0.5 * this.clusterSizeRadiusFactor; + this.growthIndicator = this.width - size; } - this.stepIndex = minorStepIdx; - this.scale = magnitudefactor; - this.step = magnitudefactor * this.minorSteps[minorStepIdx]; }; + Node.prototype._drawShape = function (ctx, shape) { + this._resizeShape(ctx); + this.left = this.x - this.width / 2; + this.top = this.y - this.height / 2; - /** - * Round the current date to the first minor date value - * This must be executed once when the current date is set to start Date - */ - DataStep.prototype.setFirst = function(customRange) { - if (customRange === undefined) { - customRange = {}; - } + var clusterLineWidth = 2.5; + var borderWidth = this.options.borderWidth; + var selectionLineWidth = this.options.borderWidthSelected || 2 * this.options.borderWidth; + var radiusMultiplier = 2; - var niceStart = customRange.min === undefined ? this._start - (this.scale * 2 * this.minorSteps[this.stepIndex]) : customRange.min; - var niceEnd = customRange.max === undefined ? this._end + (this.scale * this.minorSteps[this.stepIndex]) : customRange.max; + // choose draw method depending on the shape + switch (shape) { + case 'dot': radiusMultiplier = 2; break; + case 'square': radiusMultiplier = 2; break; + case 'triangle': radiusMultiplier = 3; break; + case 'triangleDown': radiusMultiplier = 3; break; + case 'star': radiusMultiplier = 4; break; + } - this.marginEnd = customRange.max === undefined ? this.roundToMinor(niceEnd) : customRange.max; - this.marginStart = customRange.min === undefined ? this.roundToMinor(niceStart) : customRange.min; + ctx.strokeStyle = this.selected ? this.options.color.highlight.border : this.hover ? this.options.color.hover.border : this.options.color.border; + // draw the outer border + if (this.clusterSize > 1) { + ctx.lineWidth = (this.selected ? selectionLineWidth : borderWidth) + ((this.clusterSize > 1) ? clusterLineWidth : 0.0); + ctx.lineWidth *= this.networkScaleInv; + ctx.lineWidth = Math.min(this.width,ctx.lineWidth); - // if we need to align the zero's we need to make sure that there is a zero to use. - if (this.alignZeros == true && (this.marginEnd - this.marginStart) % this.step != 0) { - this.marginEnd += this.marginEnd % this.step; + ctx[shape](this.x, this.y, this.options.radius+ radiusMultiplier * ctx.lineWidth); + ctx.stroke(); } + ctx.lineWidth = (this.selected ? selectionLineWidth : borderWidth) + ((this.clusterSize > 1) ? clusterLineWidth : 0.0); + ctx.lineWidth *= this.networkScaleInv; + ctx.lineWidth = Math.min(this.width,ctx.lineWidth); - this.deadSpace = this.roundToMinor(niceEnd) - niceEnd + this.roundToMinor(niceStart) - niceStart; - this.marginRange = this.marginEnd - this.marginStart; - + ctx.fillStyle = this.selected ? this.options.color.highlight.background : this.hover ? this.options.color.hover.background : this.options.color.background; + ctx[shape](this.x, this.y, this.options.radius); + ctx.fill(); + ctx.stroke(); - this.current = this.marginEnd; - }; + this.boundingBox.top = this.y - this.options.radius; + this.boundingBox.left = this.x - this.options.radius; + this.boundingBox.right = this.x + this.options.radius; + this.boundingBox.bottom = this.y + this.options.radius; - DataStep.prototype.roundToMinor = function(value) { - var rounded = value - (value % (this.scale * this.minorSteps[this.stepIndex])); - if (value % (this.scale * this.minorSteps[this.stepIndex]) > 0.5 * (this.scale * this.minorSteps[this.stepIndex])) { - return rounded + (this.scale * this.minorSteps[this.stepIndex]); - } - else { - return rounded; + if (this.label) { + this._label(ctx, this.label, this.x, this.y + this.height / 2, undefined, 'hanging',true); + this.boundingBox.left = Math.min(this.boundingBox.left, this.labelDimensions.left); + this.boundingBox.right = Math.max(this.boundingBox.right, this.labelDimensions.left + this.labelDimensions.width); + this.boundingBox.bottom = Math.max(this.boundingBox.bottom, this.boundingBox.bottom + this.labelDimensions.height); } - } - - - /** - * Check if the there is a next step - * @return {boolean} true if the current date has not passed the end date - */ - DataStep.prototype.hasNext = function () { - return (this.current >= this.marginStart); }; - /** - * Do the next step - */ - DataStep.prototype.next = function() { - var prev = this.current; - this.current -= this.step; + Node.prototype._resizeText = function (ctx) { + if (!this.width) { + var margin = 5; + var textSize = this.getTextSize(ctx); + this.width = textSize.width + 2 * margin; + this.height = textSize.height + 2 * margin; - // safety mechanism: if current time is still unchanged, move to the end - if (this.current == prev) { - this.current = this._end; + // scaling used for clustering + this.width += Math.min(this.clusterSize - 1, this.maxNodeSizeIncrements) * this.clusterSizeWidthFactor; + this.height += Math.min(this.clusterSize - 1, this.maxNodeSizeIncrements) * this.clusterSizeHeightFactor; + this.options.radius+= Math.min(this.clusterSize - 1, this.maxNodeSizeIncrements) * this.clusterSizeRadiusFactor; + this.growthIndicator = this.width - (textSize.width + 2 * margin); } }; - /** - * Do the next step - */ - DataStep.prototype.previous = function() { - this.current += this.step; - this.marginEnd += this.step; - this.marginRange = this.marginEnd - this.marginStart; - }; + Node.prototype._drawText = function (ctx) { + this._resizeText(ctx); + this.left = this.x - this.width / 2; + this.top = this.y - this.height / 2; + this._label(ctx, this.label, this.x, this.y); + this.boundingBox.top = this.top; + this.boundingBox.left = this.left; + this.boundingBox.right = this.left + this.width; + this.boundingBox.bottom = this.top + this.height; + }; - /** - * Get the current datetime - * @return {String} current The current date - */ - DataStep.prototype.getCurrent = function(decimals) { - // prevent round-off errors when close to zero - var current = (Math.abs(this.current) < this.step / 2) ? 0 : this.current; - var toPrecision = '' + Number(current).toPrecision(5); - // If decimals is specified, then limit or extend the string as required - if(decimals !== undefined && !isNaN(Number(decimals))) { - // If string includes exponent, then we need to add it to the end - var exp = ""; - var index = toPrecision.indexOf("e"); - if(index != -1) { - // Get the exponent - exp = toPrecision.slice(index); - // Remove the exponent in case we need to zero-extend - toPrecision = toPrecision.slice(0, index); + Node.prototype._label = function (ctx, text, x, y, align, baseline, labelUnderNode) { + if (text && Number(this.options.fontSize) * this.networkScale > this.fontDrawThreshold) { + ctx.font = (this.selected ? "bold " : "") + this.options.fontSize + "px " + this.options.fontFace; + + var lines = text.split('\n'); + var lineCount = lines.length; + var fontSize = Number(this.options.fontSize); + var yLine = y + (1 - lineCount) / 2 * fontSize; + if (labelUnderNode == true) { + yLine = y + (1 - lineCount) / (2 * fontSize); } - index = Math.max(toPrecision.indexOf(","), toPrecision.indexOf(".")); - if(index === -1) { - // No decimal found - if we want decimals, then we need to add it - if(decimals !== 0) { - toPrecision += '.'; - } - // Calculate how long the string should be - index = toPrecision.length + decimals; + + // font fill from edges now for nodes! + var 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; } - else if(decimals !== 0) { - // Calculate how long the string should be - accounting for the decimal place - index += decimals + 1; + var height = this.options.fontSize * lineCount; + var left = x - width / 2; + var top = y - height / 2; + if (baseline == "hanging") { + top += 0.5 * fontSize; + top += 4; // distance from node, required because we use hanging. Hanging has less difference between browsers + yLine += 4; // distance from node } - if(index > toPrecision.length) { - // We need to add zeros! - for(var cnt = index - toPrecision.length; cnt > 0; cnt--) { - toPrecision += '0'; - } + this.labelDimensions = {top:top,left:left,width:width,height:height,yLine:yLine}; + + // create the fontfill background + if (this.options.fontFill !== undefined && this.options.fontFill !== null && this.options.fontFill !== "none") { + ctx.fillStyle = this.options.fontFill; + ctx.fillRect(left, top, width, height); } - else { - // we need to remove characters - toPrecision = toPrecision.slice(0, index); + + // draw text + ctx.fillStyle = this.options.fontColor || "black"; + ctx.textAlign = align || "center"; + ctx.textBaseline = baseline || "middle"; + if (this.options.fontStrokeWidth > 0){ + ctx.lineWidth = this.options.fontStrokeWidth; + ctx.strokeStyle = this.options.fontStrokeColor; + ctx.lineJoin = 'round'; } - // Add the exponent if there is one - toPrecision += exp; - } - else { - if (toPrecision.indexOf(",") != -1 || toPrecision.indexOf(".") != -1) { - // If no decimal is specified, and there are decimal places, remove trailing zeros - for (var i = toPrecision.length - 1; i > 0; i--) { - if (toPrecision[i] == "0") { - toPrecision = toPrecision.slice(0, i); - } - else if (toPrecision[i] == "." || toPrecision[i] == ",") { - toPrecision = toPrecision.slice(0, i); - break; - } - else { - break; - } + for (var i = 0; i < lineCount; i++) { + if(this.options.fontStrokeWidth){ + ctx.strokeText(lines[i], x, yLine); } + ctx.fillText(lines[i], x, yLine); + yLine += fontSize; } } - - return toPrecision; }; + Node.prototype.getTextSize = function(ctx) { + if (this.label !== undefined) { + ctx.font = (this.selected ? "bold " : "") + this.options.fontSize + "px " + this.options.fontFace; - /** - * Snap a date to a rounded value. - * The snap intervals are dependent on the current scale and step. - * @param {Date} date the date to be snapped. - * @return {Date} snappedDate - */ - DataStep.prototype.snap = function(date) { + var lines = this.label.split('\n'), + height = (Number(this.options.fontSize) + 4) * lines.length, + width = 0; + + for (var i = 0, iMax = lines.length; i < iMax; i++) { + width = Math.max(width, ctx.measureText(lines[i]).width); + } + return {"width": width, "height": height, lineCount: lines.length}; + } + else { + return {"width": 0, "height": 0, lineCount: 0}; + } }; /** - * Check if the current value is a major value (for example when the step - * is DAY, a major value is each first day of the MONTH) - * @return {boolean} true if current date is major, else false. + * this is used to determine if a node is visible at all. this is used to determine when it needs to be drawn. + * there is a safety margin of 0.3 * width; + * + * @returns {boolean} */ - DataStep.prototype.isMajor = function() { - return (this.current % (this.scale * this.majorSteps[this.stepIndex]) == 0); + Node.prototype.inArea = function() { + if (this.width !== undefined) { + return (this.x + this.width *this.networkScaleInv >= this.canvasTopLeft.x && + this.x - this.width *this.networkScaleInv < this.canvasBottomRight.x && + this.y + this.height*this.networkScaleInv >= this.canvasTopLeft.y && + this.y - this.height*this.networkScaleInv < this.canvasBottomRight.y); + } + else { + return true; + } }; - module.exports = DataStep; - + /** + * checks if the core of the node is in the display area, this is used for opening clusters around zoom + * @returns {boolean} + */ + Node.prototype.inView = function() { + return (this.x >= this.canvasTopLeft.x && + this.x < this.canvasBottomRight.x && + this.y >= this.canvasTopLeft.y && + this.y < this.canvasBottomRight.y); + }; -/***/ }, -/* 46 */ -/***/ function(module, exports, __webpack_require__) { + /** + * This allows the zoom level of the network to influence the rendering + * We store the inverted scale and the coordinates of the top left, and bottom right points of the canvas + * + * @param scale + * @param canvasTopLeft + * @param canvasBottomRight + */ + Node.prototype.setScaleAndPos = function(scale,canvasTopLeft,canvasBottomRight) { + this.networkScaleInv = 1.0/scale; + this.networkScale = scale; + this.canvasTopLeft = canvasTopLeft; + this.canvasBottomRight = canvasBottomRight; + }; - var util = __webpack_require__(1); - var DOMutil = __webpack_require__(6); - var Line = __webpack_require__(47); - var Bar = __webpack_require__(49); - var Points = __webpack_require__(48); /** - * /** - * @param {object} group | the object of the group from the dataset - * @param {string} groupId | ID of the group - * @param {object} options | the default options - * @param {array} groupsUsingDefaultStyles | this array has one entree. - * It is passed as an array so it is passed by reference. - * It enumerates through the default styles - * @constructor + * This allows the zoom level of the network to influence the rendering + * + * @param scale */ - function GraphGroup (group, groupId, options, groupsUsingDefaultStyles) { - this.id = groupId; - var fields = ['sampling','style','sort','yAxisOrientation','barChart','drawPoints','shaded','catmullRom'] - this.options = util.selectiveBridgeObject(fields,options); - this.usingDefaultStyle = group.className === undefined; - this.groupsUsingDefaultStyles = groupsUsingDefaultStyles; - this.zeroPosition = 0; - this.update(group); - if (this.usingDefaultStyle == true) { - this.groupsUsingDefaultStyles[0] += 1; - } - this.itemsData = []; - this.visible = group.visible === undefined ? true : group.visible; - } + Node.prototype.setScale = function(scale) { + this.networkScaleInv = 1.0/scale; + this.networkScale = scale; + }; + /** - * this loads a reference to all items in this group into this group. - * @param {array} items + * set the velocity at 0. Is called when this node is contained in another during clustering */ - GraphGroup.prototype.setItems = function(items) { - if (items != null) { - this.itemsData = items; - if (this.options.sort == true) { - this.itemsData.sort(function (a,b) {return a.x - b.x;}) - } - } - else { - this.itemsData = []; - } + Node.prototype.clearVelocity = function() { + this.vx = 0; + this.vy = 0; }; /** - * this is used for plotting barcharts, this way, we only have to calculate it once. - * @param pos + * Basic preservation of (kinectic) energy + * + * @param massBeforeClustering */ - GraphGroup.prototype.setZeroPosition = function(pos) { - this.zeroPosition = pos; + Node.prototype.updateVelocity = function(massBeforeClustering) { + var energyBefore = this.vx * this.vx * massBeforeClustering; + //this.vx = (this.vx < 0) ? -Math.sqrt(energyBefore/this.options.mass) : Math.sqrt(energyBefore/this.options.mass); + this.vx = Math.sqrt(energyBefore/this.options.mass); + energyBefore = this.vy * this.vy * massBeforeClustering; + //this.vy = (this.vy < 0) ? -Math.sqrt(energyBefore/this.options.mass) : Math.sqrt(energyBefore/this.options.mass); + this.vy = Math.sqrt(energyBefore/this.options.mass); }; + module.exports = Node; + + +/***/ }, +/* 41 */ +/***/ function(module, exports, __webpack_require__) { /** - * set the options of the graph group over the default options. - * @param options + * 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. */ - GraphGroup.prototype.setOptions = function(options) { - if (options !== undefined) { - var fields = ['sampling','style','sort','yAxisOrientation','barChart']; - util.selectiveDeepExtend(fields, this.options, options); - - util.mergeOptions(this.options, options,'catmullRom'); - util.mergeOptions(this.options, options,'drawPoints'); - util.mergeOptions(this.options, options,'shaded'); + function Popup(container, x, y, text, style) { + if (container) { + this.container = container; + } + else { + this.container = document.body; + } - if (options.catmullRom) { - if (typeof options.catmullRom == 'object') { - if (options.catmullRom.parametrization) { - if (options.catmullRom.parametrization == 'uniform') { - this.options.catmullRom.alpha = 0; - } - else if (options.catmullRom.parametrization == 'chordal') { - this.options.catmullRom.alpha = 1.0; - } - else { - this.options.catmullRom.parametrization = 'centripetal'; - this.options.catmullRom.alpha = 0.5; - } + // x, y and text are optional, see if a style object was passed in their place + if (style === undefined) { + if (typeof x === "object") { + style = x; + x = undefined; + } else if (typeof text === "object") { + style = text; + text = undefined; + } else { + // for backwards compatibility, in case clients other than Network are creating Popup directly + style = { + fontColor: 'black', + fontSize: 14, // px + fontFace: 'verdana', + color: { + border: '#666', + background: '#FFFFC6' } } } } - if (this.options.style == 'line') { - this.type = new Line(this.id, this.options); - } - else if (this.options.style == 'bar') { - this.type = new Bar(this.id, this.options); + this.x = 0; + this.y = 0; + this.padding = 5; + + if (x !== undefined && y !== undefined ) { + this.setPosition(x, y); } - else if (this.options.style == 'points') { - this.type = new Points(this.id, this.options); + if (text !== undefined) { + this.setText(text); } - }; + // create the frame + this.frame = document.createElement("div"); + var styleAttr = this.frame.style; + styleAttr.position = "absolute"; + styleAttr.visibility = "hidden"; + styleAttr.border = "1px solid " + style.color.border; + styleAttr.color = style.fontColor; + styleAttr.fontSize = style.fontSize + "px"; + styleAttr.fontFamily = style.fontFace; + styleAttr.padding = this.padding + "px"; + styleAttr.backgroundColor = style.color.background; + styleAttr.borderRadius = "3px"; + styleAttr.MozBorderRadius = "3px"; + styleAttr.WebkitBorderRadius = "3px"; + styleAttr.boxShadow = "3px 3px 10px rgba(128, 128, 128, 0.5)"; + styleAttr.whiteSpace = "nowrap"; + this.container.appendChild(this.frame); + } /** - * this updates the current group class with the latest group dataset entree, used in _updateGroup in linegraph - * @param group + * @param {number} x Horizontal position of the popup window + * @param {number} y Vertical position of the popup window */ - GraphGroup.prototype.update = function(group) { - this.group = group; - this.content = group.content || 'graph'; - this.className = group.className || this.className || "graphGroup" + this.groupsUsingDefaultStyles[0] % 10; - this.visible = group.visible === undefined ? true : group.visible; - this.style = group.style; - this.setOptions(group.options); + Popup.prototype.setPosition = function(x, y) { + this.x = parseInt(x); + this.y = parseInt(y); }; - /** - * draw the icon for the legend. - * - * @param x - * @param y - * @param JSONcontainer - * @param SVGcontainer - * @param iconWidth - * @param iconHeight + * Set the content for the popup window. This can be HTML code or text. + * @param {string | Element} content */ - GraphGroup.prototype.drawIcon = function(x, y, JSONcontainer, SVGcontainer, iconWidth, iconHeight) { - var fillHeight = iconHeight * 0.5; - var path, fillPath; - - var outline = DOMutil.getSVGElement("rect", JSONcontainer, SVGcontainer); - outline.setAttributeNS(null, "x", x); - outline.setAttributeNS(null, "y", y - fillHeight); - outline.setAttributeNS(null, "width", iconWidth); - outline.setAttributeNS(null, "height", 2*fillHeight); - outline.setAttributeNS(null, "class", "outline"); - - if (this.options.style == 'line') { - path = DOMutil.getSVGElement("path", JSONcontainer, SVGcontainer); - path.setAttributeNS(null, "class", this.className); - if(this.style !== undefined) { - path.setAttributeNS(null, "style", this.style); - } - - path.setAttributeNS(null, "d", "M" + x + ","+y+" L" + (x + iconWidth) + ","+y+""); - if (this.options.shaded.enabled == true) { - fillPath = DOMutil.getSVGElement("path", JSONcontainer, SVGcontainer); - if (this.options.shaded.orientation == 'top') { - fillPath.setAttributeNS(null, "d", "M"+x+", " + (y - fillHeight) + - "L"+x+","+y+" L"+ (x + iconWidth) + ","+y+" L"+ (x + iconWidth) + "," + (y - fillHeight)); - } - else { - fillPath.setAttributeNS(null, "d", "M"+x+","+y+" " + - "L"+x+"," + (y + fillHeight) + " " + - "L"+ (x + iconWidth) + "," + (y + fillHeight) + - "L"+ (x + iconWidth) + ","+y); - } - fillPath.setAttributeNS(null, "class", this.className + " iconFill"); - } - - if (this.options.drawPoints.enabled == true) { - DOMutil.drawPoint(x + 0.5 * iconWidth,y, this, JSONcontainer, SVGcontainer); - } + Popup.prototype.setText = function(content) { + if (content instanceof Element) { + this.frame.innerHTML = ''; + this.frame.appendChild(content); } else { - var barWidth = Math.round(0.3 * iconWidth); - var bar1Height = Math.round(0.4 * iconHeight); - var bar2Height = Math.round(0.75 * iconHeight); - - var offset = Math.round((iconWidth - (2 * barWidth))/3); - - DOMutil.drawBar(x + 0.5*barWidth + offset , y + fillHeight - bar1Height - 1, barWidth, bar1Height, this.className + ' bar', JSONcontainer, SVGcontainer); - DOMutil.drawBar(x + 1.5*barWidth + offset + 2, y + fillHeight - bar2Height - 1, barWidth, bar2Height, this.className + ' bar', JSONcontainer, SVGcontainer); + this.frame.innerHTML = content; // string containing text or HTML } }; - /** - * return the legend entree for this group. - * - * @param iconWidth - * @param iconHeight - * @returns {{icon: HTMLElement, label: (group.content|*|string), orientation: (.options.yAxisOrientation|*)}} + * Show the popup window + * @param {boolean} show Optional. Show or hide the window */ - GraphGroup.prototype.getLegend = function(iconWidth, iconHeight) { - var svg = document.createElementNS('http://www.w3.org/2000/svg',"svg"); - this.drawIcon(0,0.5*iconHeight,[],svg,iconWidth,iconHeight); - return {icon: svg, label: this.content, orientation:this.options.yAxisOrientation}; - } - - GraphGroup.prototype.getYRange = function(groupData) { - return this.type.getYRange(groupData); - } - - GraphGroup.prototype.draw = function(dataset, group, framework) { - this.type.draw(dataset, group, framework); - } + Popup.prototype.show = function (show) { + if (show === undefined) { + show = true; + } + if (show) { + var height = this.frame.clientHeight; + var width = this.frame.clientWidth; + var maxHeight = this.frame.parentNode.clientHeight; + var maxWidth = this.frame.parentNode.clientWidth; - module.exports = GraphGroup; + 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; + } -/***/ }, -/* 47 */ -/***/ function(module, exports, __webpack_require__) { + this.frame.style.left = left + "px"; + this.frame.style.top = top + "px"; + this.frame.style.visibility = "visible"; + } + else { + this.hide(); + } + }; /** - * Created by Alex on 11/11/2014. + * Hide the popup window */ - var DOMutil = __webpack_require__(6); - var Points = __webpack_require__(48); + Popup.prototype.hide = function () { + this.frame.style.visibility = "hidden"; + }; - function Line(groupId, options) { - this.groupId = groupId; - this.options = options; - } + module.exports = Popup; - Line.prototype.getYRange = function(groupData) { - var yMin = groupData[0].y; - var yMax = groupData[0].y; - for (var j = 0; j < groupData.length; j++) { - yMin = yMin > groupData[j].y ? groupData[j].y : yMin; - yMax = yMax < groupData[j].y ? groupData[j].y : yMax; - } - return {min: yMin, max: yMax, yAxisOrientation: this.options.yAxisOrientation}; - }; +/***/ }, +/* 42 */ +/***/ function(module, exports, __webpack_require__) { /** - * draw a line graph + * Parse a text source containing data in DOT language into a JSON object. + * The object contains two lists: one with nodes and one with edges. * - * @param dataset - * @param group + * DOT language reference: http://www.graphviz.org/doc/info/lang.html + * + * @param {String} data Text containing a graph in DOT-notation + * @return {Object} graph An object containing two parameters: + * {Object[]} nodes + * {Object[]} edges */ - Line.prototype.draw = function (dataset, group, framework) { - if (dataset != null) { - if (dataset.length > 0) { - var path, d; - var svgHeight = Number(framework.svg.style.height.replace('px','')); - path = DOMutil.getSVGElement('path', framework.svgElements, framework.svg); - path.setAttributeNS(null, "class", group.className); - if(group.style !== undefined) { - path.setAttributeNS(null, "style", group.style); - } + function parseDOT (data) { + dot = data; + return parseGraph(); + } - // construct path from dataset - if (group.options.catmullRom.enabled == true) { - d = Line._catmullRom(dataset, group); - } - else { - d = Line._linear(dataset); - } + // token types enumeration + var TOKENTYPE = { + NULL : 0, + DELIMITER : 1, + IDENTIFIER: 2, + UNKNOWN : 3 + }; - // append with points for fill and finalize the path - if (group.options.shaded.enabled == true) { - var fillPath = DOMutil.getSVGElement('path', framework.svgElements, framework.svg); - var dFill; - if (group.options.shaded.orientation == 'top') { - dFill = 'M' + dataset[0].x + ',' + 0 + ' ' + d + 'L' + dataset[dataset.length - 1].x + ',' + 0; - } - else { - dFill = 'M' + dataset[0].x + ',' + svgHeight + ' ' + d + 'L' + dataset[dataset.length - 1].x + ',' + svgHeight; - } - fillPath.setAttributeNS(null, "class", group.className + " fill"); - if(group.options.shaded.style !== undefined) { - fillPath.setAttributeNS(null, "style", group.options.shaded.style); - } - fillPath.setAttributeNS(null, "d", dFill); - } - // copy properties to path for drawing. - path.setAttributeNS(null, 'd', 'M' + d); + // map with all delimiters + var DELIMITERS = { + '{': true, + '}': true, + '[': true, + ']': true, + ';': true, + '=': true, + ',': true, - // draw points - if (group.options.drawPoints.enabled == true) { - Points.draw(dataset, group, framework); - } - } - } + '->': true, + '--': true }; - + var dot = ''; // current dot file + var index = 0; // current index in dot file + var c = ''; // current token character in expr + var token = ''; // current token + var tokenType = TOKENTYPE.NULL; // type of the token /** - * This uses an uniform parametrization of the CatmullRom algorithm: - * 'On the Parameterization of Catmull-Rom Curves' by Cem Yuksel et al. - * @param data - * @returns {string} - * @private + * Get the first character from the dot file. + * The character is stored into the char c. If the end of the dot file is + * reached, the function puts an empty string in c. */ - Line._catmullRomUniform = function(data) { - // catmull rom - var p0, p1, p2, p3, bp1, bp2; - var d = Math.round(data[0].x) + ',' + Math.round(data[0].y) + ' '; - var normalization = 1/6; - var length = data.length; - for (var i = 0; i < length - 1; i++) { - - p0 = (i == 0) ? data[0] : data[i-1]; - p1 = data[i]; - p2 = data[i+1]; - p3 = (i + 2 < length) ? data[i+2] : p2; - - - // Catmull-Rom to Cubic Bezier conversion matrix - // 0 1 0 0 - // -1/6 1 1/6 0 - // 0 1/6 1 -1/6 - // 0 0 1 0 + function first() { + index = 0; + c = dot.charAt(0); + } - // bp0 = { x: p1.x, y: p1.y }; - bp1 = { x: ((-p0.x + 6*p1.x + p2.x) *normalization), y: ((-p0.y + 6*p1.y + p2.y) *normalization)}; - bp2 = { x: (( p1.x + 6*p2.x - p3.x) *normalization), y: (( p1.y + 6*p2.y - p3.y) *normalization)}; - // bp0 = { x: p2.x, y: p2.y }; + /** + * Get the next character from the dot file. + * The character is stored into the char c. If the end of the dot file is + * reached, the function puts an empty string in c. + */ + function next() { + index++; + c = dot.charAt(index); + } - d += 'C' + - bp1.x + ',' + - bp1.y + ' ' + - bp2.x + ',' + - bp2.y + ' ' + - p2.x + ',' + - p2.y + ' '; - } + /** + * Preview the next character from the dot file. + * @return {String} cNext + */ + function nextPreview() { + return dot.charAt(index + 1); + } - return d; - }; + /** + * Test whether given character is alphabetic or numeric + * @param {String} c + * @return {Boolean} isAlphaNumeric + */ + var regexAlphaNumeric = /[a-zA-Z_0-9.:#]/; + function isAlphaNumeric(c) { + return regexAlphaNumeric.test(c); + } /** - * This uses either the chordal or centripetal parameterization of the catmull-rom algorithm. - * By default, the centripetal parameterization is used because this gives the nicest results. - * These parameterizations are relatively heavy because the distance between 4 points have to be calculated. - * - * One optimization can be used to reuse distances since this is a sliding window approach. - * @param data - * @param group - * @returns {string} - * @private + * Merge all properties of object b into object b + * @param {Object} a + * @param {Object} b + * @return {Object} a */ - Line._catmullRom = function(data, group) { - var alpha = group.options.catmullRom.alpha; - if (alpha == 0 || alpha === undefined) { - return this._catmullRomUniform(data); + function merge (a, b) { + if (!a) { + a = {}; } - else { - var p0, p1, p2, p3, bp1, bp2, d1,d2,d3, A, B, N, M; - var d3powA, d2powA, d3pow2A, d2pow2A, d1pow2A, d1powA; - var d = Math.round(data[0].x) + ',' + Math.round(data[0].y) + ' '; - var length = data.length; - for (var i = 0; i < length - 1; i++) { - - p0 = (i == 0) ? data[0] : data[i-1]; - p1 = data[i]; - p2 = data[i+1]; - p3 = (i + 2 < length) ? data[i+2] : p2; - - d1 = Math.sqrt(Math.pow(p0.x - p1.x,2) + Math.pow(p0.y - p1.y,2)); - d2 = Math.sqrt(Math.pow(p1.x - p2.x,2) + Math.pow(p1.y - p2.y,2)); - d3 = Math.sqrt(Math.pow(p2.x - p3.x,2) + Math.pow(p2.y - p3.y,2)); - - // Catmull-Rom to Cubic Bezier conversion matrix - - // A = 2d1^2a + 3d1^a * d2^a + d3^2a - // B = 2d3^2a + 3d3^a * d2^a + d2^2a - - // [ 0 1 0 0 ] - // [ -d2^2a /N A/N d1^2a /N 0 ] - // [ 0 d3^2a /M B/M -d2^2a /M ] - // [ 0 0 1 0 ] - - d3powA = Math.pow(d3, alpha); - d3pow2A = Math.pow(d3,2*alpha); - d2powA = Math.pow(d2, alpha); - d2pow2A = Math.pow(d2,2*alpha); - d1powA = Math.pow(d1, alpha); - d1pow2A = Math.pow(d1,2*alpha); - A = 2*d1pow2A + 3*d1powA * d2powA + d2pow2A; - B = 2*d3pow2A + 3*d3powA * d2powA + d2pow2A; - N = 3*d1powA * (d1powA + d2powA); - if (N > 0) {N = 1 / N;} - M = 3*d3powA * (d3powA + d2powA); - if (M > 0) {M = 1 / M;} - - bp1 = { x: ((-d2pow2A * p0.x + A*p1.x + d1pow2A * p2.x) * N), - y: ((-d2pow2A * p0.y + A*p1.y + d1pow2A * p2.y) * N)}; - - bp2 = { x: (( d3pow2A * p1.x + B*p2.x - d2pow2A * p3.x) * M), - y: (( d3pow2A * p1.y + B*p2.y - d2pow2A * p3.y) * M)}; - - if (bp1.x == 0 && bp1.y == 0) {bp1 = p1;} - if (bp2.x == 0 && bp2.y == 0) {bp2 = p2;} - d += 'C' + - bp1.x + ',' + - bp1.y + ' ' + - bp2.x + ',' + - bp2.y + ' ' + - p2.x + ',' + - p2.y + ' '; + if (b) { + for (var name in b) { + if (b.hasOwnProperty(name)) { + a[name] = b[name]; + } } - - return d; } - }; + return a; + } /** - * this generates the SVG path for a linear drawing between datapoints. - * @param data - * @returns {string} - * @private + * Set a value in an object, where the provided parameter name can be a + * path with nested parameters. For example: + * + * var obj = {a: 2}; + * setValue(obj, 'b.c', 3); // obj = {a: 2, b: {c: 3}} + * + * @param {Object} obj + * @param {String} path A parameter name or dot-separated parameter path, + * like "color.highlight.border". + * @param {*} value */ - Line._linear = function(data) { - // linear - var d = ''; - for (var i = 0; i < data.length; i++) { - if (i == 0) { - d += data[i].x + ',' + data[i].y; + function setValue(obj, path, value) { + var keys = path.split('.'); + var o = obj; + while (keys.length) { + var key = keys.shift(); + if (keys.length) { + // this isn't the end point + if (!o[key]) { + o[key] = {}; + } + o = o[key]; } else { - d += ' ' + data[i].x + ',' + data[i].y; + // this is the end point + o[key] = value; } } - return d; - }; - - module.exports = Line; - - -/***/ }, -/* 48 */ -/***/ function(module, exports, __webpack_require__) { + } /** - * Created by Alex on 11/11/2014. + * Add a node to a graph object. If there is already a node with + * the same id, their attributes will be merged. + * @param {Object} graph + * @param {Object} node */ - var DOMutil = __webpack_require__(6); + function addNode(graph, node) { + var i, len; + var current = null; - function Points(groupId, options) { - this.groupId = groupId; - this.options = options; - } + // find root graph (in case of subgraph) + var graphs = [graph]; // list with all graphs from current graph to root graph + var root = graph; + while (root.parent) { + graphs.push(root.parent); + root = root.parent; + } + // find existing node (at root level) by its id + if (root.nodes) { + for (i = 0, len = root.nodes.length; i < len; i++) { + if (node.id === root.nodes[i].id) { + current = root.nodes[i]; + break; + } + } + } - Points.prototype.getYRange = function(groupData) { - var yMin = groupData[0].y; - var yMax = groupData[0].y; - for (var j = 0; j < groupData.length; j++) { - yMin = yMin > groupData[j].y ? groupData[j].y : yMin; - yMax = yMax < groupData[j].y ? groupData[j].y : yMax; + if (!current) { + // this is a new node + current = { + id: node.id + }; + if (graph.node) { + // clone default attributes + current.attr = merge(current.attr, graph.node); + } } - return {min: yMin, max: yMax, yAxisOrientation: this.options.yAxisOrientation}; - }; - Points.prototype.draw = function(dataset, group, framework, offset) { - Points.draw(dataset, group, framework, offset); + // add node to this (sub)graph and all its parent graphs + for (i = graphs.length - 1; i >= 0; i--) { + var g = graphs[i]; + + if (!g.nodes) { + g.nodes = []; + } + if (g.nodes.indexOf(current) == -1) { + g.nodes.push(current); + } + } + + // merge attributes + if (node.attr) { + current.attr = merge(current.attr, node.attr); + } } /** - * draw the data points - * - * @param {Array} dataset - * @param {Object} JSONcontainer - * @param {Object} svg | SVG DOM element - * @param {GraphGroup} group - * @param {Number} [offset] + * Add an edge to a graph object + * @param {Object} graph + * @param {Object} edge */ - Points.draw = function (dataset, group, framework, offset) { - if (offset === undefined) {offset = 0;} - for (var i = 0; i < dataset.length; i++) { - DOMutil.drawPoint(dataset[i].x + offset, dataset[i].y, group, framework.svgElements, framework.svg); + function addEdge(graph, edge) { + if (!graph.edges) { + graph.edges = []; } - }; - - - module.exports = Points; - -/***/ }, -/* 49 */ -/***/ function(module, exports, __webpack_require__) { + graph.edges.push(edge); + if (graph.edge) { + var attr = merge({}, graph.edge); // clone default attributes + edge.attr = merge(attr, edge.attr); // merge attributes + } + } /** - * Created by Alex on 11/11/2014. + * Create an edge to a graph object + * @param {Object} graph + * @param {String | Number | Object} from + * @param {String | Number | Object} to + * @param {String} type + * @param {Object | null} attr + * @return {Object} edge */ - var DOMutil = __webpack_require__(6); - var Points = __webpack_require__(48); - - function Bargraph(groupId, options) { - this.groupId = groupId; - this.options = options; - } + function createEdge(graph, from, to, type, attr) { + var edge = { + from: from, + to: to, + type: type + }; - Bargraph.prototype.getYRange = function(groupData) { - if (this.options.barChart.handleOverlap != 'stack') { - var yMin = groupData[0].y; - var yMax = groupData[0].y; - for (var j = 0; j < groupData.length; j++) { - yMin = yMin > groupData[j].y ? groupData[j].y : yMin; - yMax = yMax < groupData[j].y ? groupData[j].y : yMax; - } - return {min: yMin, max: yMax, yAxisOrientation: this.options.yAxisOrientation}; - } - else { - var barCombinedData = []; - for (var j = 0; j < groupData.length; j++) { - barCombinedData.push({ - x: groupData[j].x, - y: groupData[j].y, - groupId: this.groupId - }); - } - return barCombinedData; + if (graph.edge) { + edge.attr = merge({}, graph.edge); // clone default attributes } - }; - + edge.attr = merge(edge.attr || {}, attr); // merge attributes + return edge; + } /** - * draw a bar graph - * - * @param groupIds - * @param processedGroupData + * Get next token in the current dot file. + * The token and token type are available as token and tokenType */ - Bargraph.draw = function (groupIds, processedGroupData, framework) { - var combinedData = []; - var intersections = {}; - var coreDistance; - var key, drawData; - var group; - var i,j; - var barPoints = 0; + function getToken() { + tokenType = TOKENTYPE.NULL; + token = ''; - // combine all barchart data - for (i = 0; i < groupIds.length; i++) { - group = framework.groups[groupIds[i]]; - if (group.options.style == 'bar') { - if (group.visible == true && (framework.options.groups.visibility[groupIds[i]] === undefined || framework.options.groups.visibility[groupIds[i]] == true)) { - for (j = 0; j < processedGroupData[groupIds[i]].length; j++) { - combinedData.push({ - x: processedGroupData[groupIds[i]][j].x, - y: processedGroupData[groupIds[i]][j].y, - groupId: groupIds[i] - }); - barPoints += 1; - } - } - } + // skip over whitespaces + while (c == ' ' || c == '\t' || c == '\n' || c == '\r') { // space, tab, enter + next(); } - if (barPoints == 0) {return;} - - // sort by time and by group - combinedData.sort(function (a, b) { - if (a.x == b.x) { - return a.groupId - b.groupId; - } else { - return a.x - b.x; - } - }); - - // get intersections - Bargraph._getDataIntersections(intersections, combinedData); - - // plot barchart - for (i = 0; i < combinedData.length; i++) { - group = framework.groups[combinedData[i].groupId]; - var minWidth = 0.1 * group.options.barChart.width; + do { + var isComment = false; - key = combinedData[i].x; - var heightOffset = 0; - if (intersections[key] === undefined) { - if (i+1 < combinedData.length) {coreDistance = Math.abs(combinedData[i+1].x - key);} - if (i > 0) {coreDistance = Math.min(coreDistance,Math.abs(combinedData[i-1].x - key));} - drawData = Bargraph._getSafeDrawData(coreDistance, group, minWidth); + // skip comment + if (c == '#') { + // find the previous non-space character + var i = index - 1; + while (dot.charAt(i) == ' ' || dot.charAt(i) == '\t') { + i--; + } + if (dot.charAt(i) == '\n' || dot.charAt(i) == '') { + // the # is at the start of a line, this is indeed a line comment + while (c != '' && c != '\n') { + next(); + } + isComment = true; + } } - else { - var nextKey = i + (intersections[key].amount - intersections[key].resolved); - var prevKey = i - (intersections[key].resolved + 1); - if (nextKey < combinedData.length) {coreDistance = Math.abs(combinedData[nextKey].x - key);} - if (prevKey > 0) {coreDistance = Math.min(coreDistance,Math.abs(combinedData[prevKey].x - key));} - drawData = Bargraph._getSafeDrawData(coreDistance, group, minWidth); - intersections[key].resolved += 1; - - if (group.options.barChart.handleOverlap == 'stack') { - heightOffset = intersections[key].accumulated; - intersections[key].accumulated += group.zeroPosition - combinedData[i].y; + if (c == '/' && nextPreview() == '/') { + // skip line comment + while (c != '' && c != '\n') { + next(); } - else if (group.options.barChart.handleOverlap == 'sideBySide') { - drawData.width = drawData.width / intersections[key].amount; - drawData.offset += (intersections[key].resolved) * drawData.width - (0.5*drawData.width * (intersections[key].amount+1)); - if (group.options.barChart.align == 'left') {drawData.offset -= 0.5*drawData.width;} - else if (group.options.barChart.align == 'right') {drawData.offset += 0.5*drawData.width;} + isComment = true; + } + if (c == '/' && nextPreview() == '*') { + // skip block comment + while (c != '') { + if (c == '*' && nextPreview() == '/') { + // end of block comment found. skip these last two characters + next(); + next(); + break; + } + else { + next(); + } } + isComment = true; } - DOMutil.drawBar(combinedData[i].x + drawData.offset, combinedData[i].y - heightOffset, drawData.width, group.zeroPosition - combinedData[i].y, group.className + ' bar', framework.svgElements, framework.svg); - // draw points - if (group.options.drawPoints.enabled == true) { - DOMutil.drawPoint(combinedData[i].x + drawData.offset, combinedData[i].y, group, framework.svgElements, framework.svg); + + // skip over whitespaces + while (c == ' ' || c == '\t' || c == '\n' || c == '\r') { // space, tab, enter + next(); } } - }; + while (isComment); + // check for end of dot file + if (c == '') { + // token is still empty + tokenType = TOKENTYPE.DELIMITER; + return; + } - /** - * Fill the intersections object with counters of how many datapoints share the same x coordinates - * @param intersections - * @param combinedData - * @private - */ - Bargraph._getDataIntersections = function (intersections, combinedData) { - // get intersections - var coreDistance; - for (var i = 0; i < combinedData.length; i++) { - if (i + 1 < combinedData.length) { - coreDistance = Math.abs(combinedData[i + 1].x - combinedData[i].x); - } - if (i > 0) { - coreDistance = Math.min(coreDistance, Math.abs(combinedData[i - 1].x - combinedData[i].x)); - } - if (coreDistance == 0) { - if (intersections[combinedData[i].x] === undefined) { - intersections[combinedData[i].x] = {amount: 0, resolved: 0, accumulated: 0}; - } - intersections[combinedData[i].x].amount += 1; - } + // check for delimiters consisting of 2 characters + var c2 = c + nextPreview(); + if (DELIMITERS[c2]) { + tokenType = TOKENTYPE.DELIMITER; + token = c2; + next(); + next(); + return; } - }; + // check for delimiters consisting of 1 character + if (DELIMITERS[c]) { + tokenType = TOKENTYPE.DELIMITER; + token = c; + next(); + return; + } - /** - * Get the width and offset for bargraphs based on the coredistance between datapoints - * - * @param coreDistance - * @param group - * @param minWidth - * @returns {{width: Number, offset: Number}} - * @private - */ - Bargraph._getSafeDrawData = function (coreDistance, group, minWidth) { - var width, offset; - if (coreDistance < group.options.barChart.width && coreDistance > 0) { - width = coreDistance < minWidth ? minWidth : coreDistance; + // check for an identifier (number or string) + // TODO: more precise parsing of numbers/strings (and the port separator ':') + if (isAlphaNumeric(c) || c == '-') { + token += c; + next(); - offset = 0; // recalculate offset with the new width; - if (group.options.barChart.align == 'left') { - offset -= 0.5 * coreDistance; + while (isAlphaNumeric(c)) { + token += c; + next(); } - else if (group.options.barChart.align == 'right') { - offset += 0.5 * coreDistance; + if (token == 'false') { + token = false; // convert to boolean } - } - else { - // default settings - width = group.options.barChart.width; - offset = 0; - if (group.options.barChart.align == 'left') { - offset -= 0.5 * group.options.barChart.width; + else if (token == 'true') { + token = true; // convert to boolean } - else if (group.options.barChart.align == 'right') { - offset += 0.5 * group.options.barChart.width; + else if (!isNaN(Number(token))) { + token = Number(token); // convert to number } + tokenType = TOKENTYPE.IDENTIFIER; + return; } - return {width: width, offset: offset}; - }; - - Bargraph.getStackedBarYRange = function(barCombinedData, groupRanges, groupIds, groupLabel, orientation) { - if (barCombinedData.length > 0) { - // sort by time and by group - barCombinedData.sort(function (a, b) { - if (a.x == b.x) { - return a.groupId - b.groupId; - } else { - return a.x - b.x; + // check for a string enclosed by double quotes + if (c == '"') { + next(); + while (c != '' && (c != '"' || (c == '"' && nextPreview() == '"'))) { + token += c; + if (c == '"') { // skip the escape character + next(); } - }); - var intersections = {}; - - Bargraph._getDataIntersections(intersections, barCombinedData); - groupRanges[groupLabel] = Bargraph._getStackedBarYRange(intersections, barCombinedData); - groupRanges[groupLabel].yAxisOrientation = orientation; - groupIds.push(groupLabel); - } - } - - Bargraph._getStackedBarYRange = function (intersections, combinedData) { - var key; - var yMin = combinedData[0].y; - var yMax = combinedData[0].y; - for (var i = 0; i < combinedData.length; i++) { - key = combinedData[i].x; - if (intersections[key] === undefined) { - yMin = yMin > combinedData[i].y ? combinedData[i].y : yMin; - yMax = yMax < combinedData[i].y ? combinedData[i].y : yMax; - } - else { - intersections[key].accumulated += combinedData[i].y; + next(); } - } - for (var xpos in intersections) { - if (intersections.hasOwnProperty(xpos)) { - yMin = yMin > intersections[xpos].accumulated ? intersections[xpos].accumulated : yMin; - yMax = yMax < intersections[xpos].accumulated ? intersections[xpos].accumulated : yMax; + if (c != '"') { + throw newSyntaxError('End of string " expected'); } + next(); + tokenType = TOKENTYPE.IDENTIFIER; + return; } - return {min: yMin, max: yMax}; - }; - - module.exports = Bargraph; - -/***/ }, -/* 50 */ -/***/ function(module, exports, __webpack_require__) { - - var util = __webpack_require__(1); - var DOMutil = __webpack_require__(6); - var Component = __webpack_require__(23); + // something unknown is found, wrong characters, a syntax error + tokenType = TOKENTYPE.UNKNOWN; + while (c != '') { + token += c; + next(); + } + throw new SyntaxError('Syntax error in part "' + chop(token, 30) + '"'); + } /** - * Legend for Graph2d + * Parse a graph. + * @returns {Object} graph */ - function Legend(body, options, side, linegraphOptions) { - this.body = body; - this.defaultOptions = { - enabled: true, - icons: true, - iconSize: 20, - iconSpacing: 6, - left: { - visible: true, - position: 'top-left' // top/bottom - left,center,right - }, - right: { - visible: true, - position: 'top-left' // top/bottom - left,center,right - } - } - this.side = side; - this.options = util.extend({},this.defaultOptions); - this.linegraphOptions = linegraphOptions; - - this.svgElements = {}; - this.dom = {}; - this.groups = {}; - this.amountOfGroups = 0; - this._create(); + function parseGraph() { + var graph = {}; - this.setOptions(options); - } + first(); + getToken(); - Legend.prototype = new Component(); + // optional strict keyword + if (token == 'strict') { + graph.strict = true; + getToken(); + } - Legend.prototype.clear = function() { - this.groups = {}; - this.amountOfGroups = 0; - } + // graph or digraph keyword + if (token == 'graph' || token == 'digraph') { + graph.type = token; + getToken(); + } - Legend.prototype.addGroup = function(label, graphOptions) { + // optional graph id + if (tokenType == TOKENTYPE.IDENTIFIER) { + graph.id = token; + getToken(); + } - if (!this.groups.hasOwnProperty(label)) { - this.groups[label] = graphOptions; + // open angle bracket + if (token != '{') { + throw newSyntaxError('Angle bracket { expected'); } - this.amountOfGroups += 1; - }; + getToken(); - Legend.prototype.updateGroup = function(label, graphOptions) { - this.groups[label] = graphOptions; - }; + // statements + parseStatements(graph); - Legend.prototype.removeGroup = function(label) { - if (this.groups.hasOwnProperty(label)) { - delete this.groups[label]; - this.amountOfGroups -= 1; + // close angle bracket + if (token != '}') { + throw newSyntaxError('Angle bracket } expected'); } - }; - - Legend.prototype._create = function() { - this.dom.frame = document.createElement('div'); - this.dom.frame.className = 'legend'; - this.dom.frame.style.position = "absolute"; - this.dom.frame.style.top = "10px"; - this.dom.frame.style.display = "block"; + getToken(); - this.dom.textArea = document.createElement('div'); - this.dom.textArea.className = 'legendText'; - this.dom.textArea.style.position = "relative"; - this.dom.textArea.style.top = "0px"; + // end of file + if (token !== '') { + throw newSyntaxError('End of file expected'); + } + getToken(); - this.svg = document.createElementNS('http://www.w3.org/2000/svg',"svg"); - this.svg.style.position = 'absolute'; - this.svg.style.top = 0 +'px'; - this.svg.style.width = this.options.iconSize + 5 + 'px'; - this.svg.style.height = '100%'; + // remove temporary default properties + delete graph.node; + delete graph.edge; + delete graph.graph; - this.dom.frame.appendChild(this.svg); - this.dom.frame.appendChild(this.dom.textArea); - }; + return graph; + } /** - * Hide the component from the DOM + * Parse a list with statements. + * @param {Object} graph */ - Legend.prototype.hide = function() { - // remove the frame containing the items - if (this.dom.frame.parentNode) { - this.dom.frame.parentNode.removeChild(this.dom.frame); + function parseStatements (graph) { + while (token !== '' && token != '}') { + parseStatement(graph); + if (token == ';') { + getToken(); + } } - }; + } /** - * Show the component in the DOM (when not already visible). - * @return {Boolean} changed + * Parse a single statement. Can be a an attribute statement, node + * statement, a series of node statements and edge statements, or a + * parameter. + * @param {Object} graph */ - Legend.prototype.show = function() { - // show frame containing the items - if (!this.dom.frame.parentNode) { - this.body.dom.center.appendChild(this.dom.frame); + function parseStatement(graph) { + // parse subgraph + var subgraph = parseSubgraph(graph); + if (subgraph) { + // edge statements + parseEdge(graph, subgraph); + + return; } - }; - Legend.prototype.setOptions = function(options) { - var fields = ['enabled','orientation','icons','left','right']; - util.selectiveDeepExtend(fields, this.options, options); - }; + // parse an attribute statement + var attr = parseAttributeStatement(graph); + if (attr) { + return; + } - Legend.prototype.redraw = function() { - var activeGroups = 0; - for (var groupId in this.groups) { - if (this.groups.hasOwnProperty(groupId)) { - if (this.groups[groupId].visible == true && (this.linegraphOptions.visibility[groupId] === undefined || this.linegraphOptions.visibility[groupId] == true)) { - activeGroups++; - } - } + // parse node + if (tokenType != TOKENTYPE.IDENTIFIER) { + throw newSyntaxError('Identifier expected'); } + var id = token; // id can be a string or a number + getToken(); - if (this.options[this.side].visible == false || this.amountOfGroups == 0 || this.options.enabled == false || activeGroups == 0) { - this.hide(); + if (token == '=') { + // id statement + getToken(); + if (tokenType != TOKENTYPE.IDENTIFIER) { + throw newSyntaxError('Identifier expected'); + } + graph[id] = token; + getToken(); + // TODO: implement comma separated list with "a_list: ID=ID [','] [a_list] " } else { - this.show(); - if (this.options[this.side].position == 'top-left' || this.options[this.side].position == 'bottom-left') { - this.dom.frame.style.left = '4px'; - this.dom.frame.style.textAlign = "left"; - this.dom.textArea.style.textAlign = "left"; - this.dom.textArea.style.left = (this.options.iconSize + 15) + 'px'; - this.dom.textArea.style.right = ''; - this.svg.style.left = 0 +'px'; - this.svg.style.right = ''; - } - else { - this.dom.frame.style.right = '4px'; - this.dom.frame.style.textAlign = "right"; - this.dom.textArea.style.textAlign = "right"; - this.dom.textArea.style.right = (this.options.iconSize + 15) + 'px'; - this.dom.textArea.style.left = ''; - this.svg.style.right = 0 +'px'; - this.svg.style.left = ''; - } + parseNodeStatement(graph, id); + } + } - if (this.options[this.side].position == 'top-left' || this.options[this.side].position == 'top-right') { - this.dom.frame.style.top = 4 - Number(this.body.dom.center.style.top.replace("px","")) + 'px'; - this.dom.frame.style.bottom = ''; - } - else { - var scrollableHeight = this.body.domProps.center.height - this.body.domProps.centerContainer.height; - this.dom.frame.style.bottom = 4 + scrollableHeight + Number(this.body.dom.center.style.top.replace("px","")) + 'px'; - this.dom.frame.style.top = ''; - } + /** + * Parse a subgraph + * @param {Object} graph parent graph object + * @return {Object | null} subgraph + */ + function parseSubgraph (graph) { + var subgraph = null; - if (this.options.icons == false) { - this.dom.frame.style.width = this.dom.textArea.offsetWidth + 10 + 'px'; - this.dom.textArea.style.right = ''; - this.dom.textArea.style.left = ''; - this.svg.style.width = '0px'; - } - else { - this.dom.frame.style.width = this.options.iconSize + 15 + this.dom.textArea.offsetWidth + 10 + 'px' - this.drawLegendIcons(); - } + // optional subgraph keyword + if (token == 'subgraph') { + subgraph = {}; + subgraph.type = 'subgraph'; + getToken(); - var content = ''; - for (var groupId in this.groups) { - if (this.groups.hasOwnProperty(groupId)) { - if (this.groups[groupId].visible == true && (this.linegraphOptions.visibility[groupId] === undefined || this.linegraphOptions.visibility[groupId] == true)) { - content += this.groups[groupId].content + '
'; - } - } + // optional graph id + if (tokenType == TOKENTYPE.IDENTIFIER) { + subgraph.id = token; + getToken(); } - this.dom.textArea.innerHTML = content; - this.dom.textArea.style.lineHeight = ((0.75 * this.options.iconSize) + this.options.iconSpacing) + 'px'; } - }; - - Legend.prototype.drawLegendIcons = function() { - if (this.dom.frame.parentNode) { - DOMutil.prepareElements(this.svgElements); - var padding = window.getComputedStyle(this.dom.frame).paddingTop; - var iconOffset = Number(padding.replace('px','')); - var x = iconOffset; - var iconWidth = this.options.iconSize; - var iconHeight = 0.75 * this.options.iconSize; - var y = iconOffset + 0.5 * iconHeight + 3; - this.svg.style.width = iconWidth + 5 + iconOffset + 'px'; + // open angle bracket + if (token == '{') { + getToken(); - for (var groupId in this.groups) { - if (this.groups.hasOwnProperty(groupId)) { - if (this.groups[groupId].visible == true && (this.linegraphOptions.visibility[groupId] === undefined || this.linegraphOptions.visibility[groupId] == true)) { - this.groups[groupId].drawIcon(x, y, this.svgElements, this.svg, iconWidth, iconHeight); - y += iconHeight + this.options.iconSpacing; - } - } + if (!subgraph) { + subgraph = {}; } + subgraph.parent = graph; + subgraph.node = graph.node; + subgraph.edge = graph.edge; + subgraph.graph = graph.graph; - DOMutil.cleanupElements(this.svgElements); - } - }; - - module.exports = Legend; + // statements + parseStatements(subgraph); + // close angle bracket + if (token != '}') { + throw newSyntaxError('Angle bracket } expected'); + } + getToken(); -/***/ }, -/* 51 */ -/***/ function(module, exports, __webpack_require__) { + // remove temporary default properties + delete subgraph.node; + delete subgraph.edge; + delete subgraph.graph; + delete subgraph.parent; - var Emitter = __webpack_require__(11); - var Hammer = __webpack_require__(19); - var keycharm = __webpack_require__(36); - var util = __webpack_require__(1); - var hammerUtil = __webpack_require__(22); - var DataSet = __webpack_require__(7); - var DataView = __webpack_require__(9); - var dotparser = __webpack_require__(52); - var gephiParser = __webpack_require__(53); - var Groups = __webpack_require__(54); - var Images = __webpack_require__(55); - var Node = __webpack_require__(56); - var Edge = __webpack_require__(57); - var Popup = __webpack_require__(58); - var MixinLoader = __webpack_require__(59); - var Activator = __webpack_require__(35); - var locales = __webpack_require__(70); + // register at the parent graph + if (!graph.subgraphs) { + graph.subgraphs = []; + } + graph.subgraphs.push(subgraph); + } - // Load custom shapes into CanvasRenderingContext2D - __webpack_require__(71); + return subgraph; + } /** - * @constructor Network - * Create a network visualization, displaying nodes and edges. - * - * @param {Element} container The DOM element in which the Network will - * be created. Normally a div element. - * @param {Object} data An object containing parameters - * {Array} nodes - * {Array} edges - * @param {Object} options Options + * parse an attribute statement like "node [shape=circle fontSize=16]". + * Available keywords are 'node', 'edge', 'graph'. + * The previous list with default attributes will be replaced + * @param {Object} graph + * @returns {String | null} keyword Returns the name of the parsed attribute + * (node, edge, graph), or null if nothing + * is parsed. */ - function Network (container, data, options) { - if (!(this instanceof Network)) { - throw new SyntaxError('Constructor must be called with the new operator'); - } - - this._determineBrowserMethod(); - this._initializeMixinLoaders(); + function parseAttributeStatement (graph) { + // attribute statements + if (token == 'node') { + getToken(); - // create variables and set default values - this.containerElement = container; + // node attributes + graph.node = parseAttributeList(); + return 'node'; + } + else if (token == 'edge') { + getToken(); - // render and calculation settings - this.renderRefreshRate = 60; // hz (fps) - this.renderTimestep = 1000 / this.renderRefreshRate; // ms -- saves calculation later on - this.renderTime = 0; // measured time it takes to render a frame - this.physicsTime = 0; // measured time it takes to render a frame - this.runDoubleSpeed = false; - this.physicsDiscreteStepsize = 0.50; // discrete stepsize of the simulation + // edge attributes + graph.edge = parseAttributeList(); + return 'edge'; + } + else if (token == 'graph') { + getToken(); - this.initializing = true; + // graph attributes + graph.graph = parseAttributeList(); + return 'graph'; + } - this.triggerFunctions = {add:null,edit:null,editEdge:null,connect:null,del:null}; + return null; + } - // set constant values - this.defaultOptions = { - nodes: { - mass: 1, - radiusMin: 10, - radiusMax: 30, - radius: 10, - shape: 'ellipse', - image: undefined, - widthMin: 16, // px - widthMax: 64, // px - fontColor: 'black', - fontSize: 14, // px - fontFace: 'verdana', - fontFill: undefined, - fontStrokeWidth: 0, // px - fontStrokeColor: 'white', - level: -1, - color: { - border: '#2B7CE9', - background: '#97C2FC', - highlight: { - border: '#2B7CE9', - background: '#D2E5FF' - }, - hover: { - border: '#2B7CE9', - background: '#D2E5FF' - } - }, - group: undefined, - borderWidth: 1, - borderWidthSelected: undefined - }, - edges: { - widthMin: 1, // - widthMax: 15,// - width: 1, - widthSelectionMultiplier: 2, - hoverWidth: 1.5, - style: 'line', - color: { - color:'#848484', - highlight:'#848484', - hover: '#848484' - }, - fontColor: '#343434', - fontSize: 14, // px - fontFace: 'arial', - fontFill: 'white', - fontStrokeWidth: 0, // px - fontStrokeColor: 'white', - labelAlignment:'horizontal', - arrowScaleFactor: 1, - dash: { - length: 10, - gap: 5, - altLength: undefined - }, - inheritColor: "from" // to, from, false, true (== from) - }, - configurePhysics:false, - physics: { - barnesHut: { - enabled: true, - thetaInverted: 1 / 0.5, // inverted to save time during calculation - gravitationalConstant: -2000, - centralGravity: 0.3, - springLength: 95, - springConstant: 0.04, - damping: 0.09 - }, - repulsion: { - centralGravity: 0.0, - springLength: 200, - springConstant: 0.05, - nodeDistance: 100, - damping: 0.09 - }, - hierarchicalRepulsion: { - enabled: false, - centralGravity: 0.0, - springLength: 100, - springConstant: 0.01, - nodeDistance: 150, - damping: 0.09 - }, - damping: null, - centralGravity: null, - springLength: null, - springConstant: null - }, - clustering: { // Per Node in Cluster = PNiC - enabled: false, // (Boolean) | global on/off switch for clustering. - initialMaxNodes: 100, // (# nodes) | if the initial amount of nodes is larger than this, we cluster until the total number is less than this threshold. - clusterThreshold:500, // (# nodes) | during calculate forces, we check if the total number of nodes is larger than this. If it is, cluster until reduced to reduceToNodes - reduceToNodes:300, // (# nodes) | during calculate forces, we check if the total number of nodes is larger than clusterThreshold. If it is, cluster until reduced to this - chainThreshold: 0.4, // (% of all drawn nodes)| maximum percentage of allowed chainnodes (long strings of connected nodes) within all nodes. (lower means less chains). - clusterEdgeThreshold: 20, // (px) | edge length threshold. if smaller, this node is clustered. - sectorThreshold: 100, // (# nodes in cluster) | cluster size threshold. If larger, expanding in own sector. - screenSizeThreshold: 0.2, // (% of canvas) | relative size threshold. If the width or height of a clusternode takes up this much of the screen, decluster node. - fontSizeMultiplier: 4.0, // (px PNiC) | how much the cluster font size grows per node in cluster (in px). - maxFontSize: 1000, - forceAmplification: 0.1, // (multiplier PNiC) | factor of increase fo the repulsion force of a cluster (per node in cluster). - distanceAmplification: 0.1, // (multiplier PNiC) | factor how much the repulsion distance of a cluster increases (per node in cluster). - edgeGrowth: 20, // (px PNiC) | amount of clusterSize connected to the edge is multiplied with this and added to edgeLength. - nodeScaling: {width: 1, // (px PNiC) | growth of the width per node in cluster. - height: 1, // (px PNiC) | growth of the height per node in cluster. - radius: 1}, // (px PNiC) | growth of the radius per node in cluster. - maxNodeSizeIncrements: 600, // (# increments) | max growth of the width per node in cluster. - activeAreaBoxSize: 80, // (px) | box area around the curser where clusters are popped open. - clusterLevelDifference: 2 - }, - navigation: { - enabled: false - }, - keyboard: { - enabled: false, - speed: {x: 10, y: 10, zoom: 0.02}, - bindToWindow: true - }, - dataManipulation: { - enabled: false, - initiallyVisible: false - }, - hierarchicalLayout: { - enabled:false, - levelSeparation: 150, - nodeSpacing: 100, - direction: "UD", // UD, DU, LR, RL - layout: "hubsize" // hubsize, directed - }, - freezeForStabilization: false, - smoothCurves: { - enabled: true, - dynamic: true, - type: "continuous", - roundness: 0.5 - }, - maxVelocity: 30, - minVelocity: 0.1, // px/s - stabilize: true, // stabilize before displaying the network - stabilizationIterations: 1000, // maximum number of iteration to stabilize - zoomExtentOnStabilize: true, - locale: 'en', - locales: locales, - tooltip: { - delay: 300, - fontColor: 'black', - fontSize: 14, // px - fontFace: 'verdana', - color: { - border: '#666', - background: '#FFFFC6' - } - }, - dragNetwork: true, - dragNodes: true, - zoomable: true, - hover: false, - hideEdgesOnDrag: false, - hideNodesOnDrag: false, - width : '100%', - height : '100%', - selectable: true + /** + * parse a node statement + * @param {Object} graph + * @param {String | Number} id + */ + function parseNodeStatement(graph, id) { + // node statement + var node = { + id: id }; - this.constants = util.extend({}, this.defaultOptions); - this.pixelRatio = 1; - - - this.hoverObj = {nodes:{},edges:{}}; - this.controlNodesActive = false; - this.navigationHammers = {existing:[], _new: []}; + var attr = parseAttributeList(); + if (attr) { + node.attr = attr; + } + addNode(graph, node); - // animation properties - this.animationSpeed = 1/this.renderRefreshRate; - this.animationEasingFunction = "easeInOutQuint"; - this.animating = false; - this.easingTime = 0; - this.sourceScale = 0; - this.targetScale = 0; - this.sourceTranslation = 0; - this.targetTranslation = 0; - this.lockedOnNodeId = null; - this.lockedOnNodeOffset = null; - this.touchTime = 0; + // edge statements + parseEdge(graph, id); + } - // Node variables - var network = this; - this.groups = new Groups(); // object with groups - this.images = new Images(); // object with images - this.images.setOnloadCallback(function (status) { - network._redraw(); - }); + /** + * Parse an edge or a series of edges + * @param {Object} graph + * @param {String | Number} from Id of the from node + */ + function parseEdge(graph, from) { + while (token == '->' || token == '--') { + var to; + var type = token; + getToken(); - // keyboard navigation variables - this.xIncrement = 0; - this.yIncrement = 0; - this.zoomIncrement = 0; + var subgraph = parseSubgraph(graph); + if (subgraph) { + to = subgraph; + } + else { + if (tokenType != TOKENTYPE.IDENTIFIER) { + throw newSyntaxError('Identifier or subgraph expected'); + } + to = token; + addNode(graph, { + id: to + }); + getToken(); + } - // loading all the mixins: - // load the force calculation functions, grouped under the physics system. - this._loadPhysicsSystem(); - // create a frame and canvas - this._create(); - // load the sector system. (mandatory, fully integrated with Network) - this._loadSectorSystem(); - // load the cluster system. (mandatory, even when not using the cluster system, there are function calls to it) - this._loadClusterSystem(); - // load the selection system. (mandatory, required by Network) - this._loadSelectionSystem(); - // load the selection system. (mandatory, required by Network) - this._loadHierarchySystem(); + // parse edge attributes + var attr = parseAttributeList(); + // create edge + var edge = createEdge(graph, from, to, type, attr); + addEdge(graph, edge); - // apply options - this._setTranslation(this.frame.clientWidth / 2, this.frame.clientHeight / 2); - this._setScale(1); - this.setOptions(options); + from = to; + } + } - // other vars - this.freezeSimulation = false;// freeze the simulation - this.cachedFunctions = {}; - this.startedStabilization = false; - this.stabilized = false; - this.stabilizationIterations = null; - this.draggingNodes = false; + /** + * Parse a set with attributes, + * for example [label="1.000", shape=solid] + * @return {Object | null} attr + */ + function parseAttributeList() { + var attr = null; - // containers for nodes and edges - this.calculationNodes = {}; - this.calculationNodeIndices = []; - this.nodeIndices = []; // array with all the indices of the nodes. Used to speed up forces calculation - this.nodes = {}; // object with Node objects - this.edges = {}; // object with Edge objects + while (token == '[') { + getToken(); + attr = {}; + while (token !== '' && token != ']') { + if (tokenType != TOKENTYPE.IDENTIFIER) { + throw newSyntaxError('Attribute name expected'); + } + var name = token; - // position and scale variables and objects - this.canvasTopLeft = {"x": 0,"y": 0}; // coordinates of the top left of the canvas. they will be set during _redraw. - this.canvasBottomRight = {"x": 0,"y": 0}; // coordinates of the bottom right of the canvas. they will be set during _redraw - this.pointerPosition = {"x": 0,"y": 0}; // coordinates of the bottom right of the canvas. they will be set during _redraw - this.areaCenter = {}; // object with x and y elements used for determining the center of the zoom action - this.scale = 1; // defining the global scale variable in the constructor - this.previousScale = this.scale; // this is used to check if the zoom operation is zooming in or out + getToken(); + if (token != '=') { + throw newSyntaxError('Equal sign = expected'); + } + getToken(); - // datasets or dataviews - this.nodesData = null; // A DataSet or DataView - this.edgesData = null; // A DataSet or DataView + if (tokenType != TOKENTYPE.IDENTIFIER) { + throw newSyntaxError('Attribute value expected'); + } + var value = token; + setValue(attr, name, value); // name can be a path - // create event listeners used to subscribe on the DataSets of the nodes and edges - this.nodesListeners = { - 'add': function (event, params) { - network._addNodes(params.items); - network.start(); - }, - 'update': function (event, params) { - network._updateNodes(params.items, params.data); - network.start(); - }, - 'remove': function (event, params) { - network._removeNodes(params.items); - network.start(); - } - }; - this.edgesListeners = { - 'add': function (event, params) { - network._addEdges(params.items); - network.start(); - }, - 'update': function (event, params) { - network._updateEdges(params.items); - network.start(); - }, - 'remove': function (event, params) { - network._removeEdges(params.items); - network.start(); + getToken(); + if (token ==',') { + getToken(); + } } - }; - - // properties for the animation - this.moving = true; - this.timer = undefined; // Scheduling function. Is definded in this.start(); - - // load data (the disable start variable will be the same as the enabled clustering) - this.setData(data,this.constants.clustering.enabled || this.constants.hierarchicalLayout.enabled); - // hierarchical layout - this.initializing = false; - if (this.constants.hierarchicalLayout.enabled == true) { - this._setupHierarchicalLayout(); - } - else { - // zoom so all data will fit on the screen, if clustering is enabled, we do not want start to be called here. - if (this.constants.stabilize == false) { - this.zoomExtent(undefined, true,this.constants.clustering.enabled); + if (token != ']') { + throw newSyntaxError('Bracket ] expected'); } + getToken(); } - // if clustering is disabled, the simulation will have started in the setData function - if (this.constants.clustering.enabled) { - this.startWithClustering(); - } + return attr; } - // Extend Network with an Emitter mixin - Emitter(Network.prototype); - /** - * Determine if the browser requires a setTimeout or a requestAnimationFrame. This was required because - * some implementations (safari and IE9) did not support requestAnimationFrame - * @private + * Create a syntax error with extra information on current token and index. + * @param {String} message + * @returns {SyntaxError} err */ - Network.prototype._determineBrowserMethod = function() { - var browserType = navigator.userAgent.toLowerCase(); - this.requiresTimeout = false; - if (browserType.indexOf('msie 9.0') != -1) { // IE 9 - this.requiresTimeout = true; - } - else if (browserType.indexOf('safari') != -1) { // safari - if (browserType.indexOf('chrome') <= -1) { - this.requiresTimeout = true; - } - } + function newSyntaxError(message) { + return new SyntaxError(message + ', got "' + chop(token, 30) + '" (char ' + index + ')'); } - /** - * Get the script path where the vis.js library is located - * - * @returns {string | null} path Path or null when not found. Path does not - * end with a slash. - * @private + * Chop off text after a maximum length + * @param {String} text + * @param {Number} maxLength + * @returns {String} */ - Network.prototype._getScriptPath = function() { - var scripts = document.getElementsByTagName( 'script' ); - - // find script named vis.js or vis.min.js - for (var i = 0; i < scripts.length; i++) { - var src = scripts[i].src; - var match = src && /\/?vis(.min)?\.js$/.exec(src); - if (match) { - // return path without the script name - return src.substring(0, src.length - match[0].length); - } - } - - return null; - }; - + function chop (text, maxLength) { + return (text.length <= maxLength) ? text : (text.substr(0, 27) + '...'); + } /** - * Find the center position of the network - * @private - */ - Network.prototype._getRange = function() { - var minY = 1e9, maxY = -1e9, minX = 1e9, maxX = -1e9, node; - for (var nodeId in this.nodes) { - if (this.nodes.hasOwnProperty(nodeId)) { - node = this.nodes[nodeId]; - if (minX > (node.boundingBox.left)) {minX = node.boundingBox.left;} - if (maxX < (node.boundingBox.right)) {maxX = node.boundingBox.right;} - if (minY > (node.boundingBox.bottom)) {minY = node.boundingBox.top;} // top is negative, bottom is positive - if (maxY < (node.boundingBox.top)) {maxY = node.boundingBox.bottom;} // top is negative, bottom is positive - } - } - if (minX == 1e9 && maxX == -1e9 && minY == 1e9 && maxY == -1e9) { - minY = 0, maxY = 0, minX = 0, maxX = 0; - } - return {minX: minX, maxX: maxX, minY: minY, maxY: maxY}; - }; - - - /** - * @param {object} range = {minX: minX, maxX: maxX, minY: minY, maxY: maxY}; - * @returns {{x: number, y: number}} - * @private - */ - Network.prototype._findCenter = function(range) { - return {x: (0.5 * (range.maxX + range.minX)), - y: (0.5 * (range.maxY + range.minY))}; - }; - - - /** - * This function zooms out to fit all data on screen based on amount of nodes - * - * @param {Boolean} [initialZoom] | zoom based on fitted formula or range, true = fitted, default = false; - * @param {Boolean} [disableStart] | If true, start is not called. + * Execute a function fn for each pair of elements in two arrays + * @param {Array | *} array1 + * @param {Array | *} array2 + * @param {function} fn */ - Network.prototype.zoomExtent = function(animationOptions, initialZoom, disableStart) { - this._redraw(true); - - if (initialZoom === undefined) {initialZoom = false;} - if (disableStart === undefined) {disableStart = false;} - if (animationOptions === undefined) {animationOptions = false;} - - var range = this._getRange(); - var zoomLevel; - - if (initialZoom == true) { - var numberOfNodes = this.nodeIndices.length; - if (this.constants.smoothCurves == true) { - if (this.constants.clustering.enabled == true && - numberOfNodes >= this.constants.clustering.initialMaxNodes) { - zoomLevel = 49.07548 / (numberOfNodes + 142.05338) + 9.1444e-04; // this is obtained from fitting a dataset from 5 points with scale levels that looked good. - } - else { - zoomLevel = 12.662 / (numberOfNodes + 7.4147) + 0.0964822; // this is obtained from fitting a dataset from 5 points with scale levels that looked good. - } - } - else { - if (this.constants.clustering.enabled == true && - numberOfNodes >= this.constants.clustering.initialMaxNodes) { - zoomLevel = 77.5271985 / (numberOfNodes + 187.266146) + 4.76710517e-05; // this is obtained from fitting a dataset from 5 points with scale levels that looked good. + function forEach2(array1, array2, fn) { + if (Array.isArray(array1)) { + array1.forEach(function (elem1) { + if (Array.isArray(array2)) { + array2.forEach(function (elem2) { + fn(elem1, elem2); + }); } else { - zoomLevel = 30.5062972 / (numberOfNodes + 19.93597763) + 0.08413486; // this is obtained from fitting a dataset from 5 points with scale levels that looked good. + fn(elem1, array2); } - } - - // correct for larger canvasses. - var factor = Math.min(this.frame.canvas.clientWidth / 600, this.frame.canvas.clientHeight / 600); - zoomLevel *= factor; - } - else { - var xDistance = Math.abs(range.maxX - range.minX) * 1.1; - var yDistance = Math.abs(range.maxY - range.minY) * 1.1; - - var xZoomLevel = this.frame.canvas.clientWidth / xDistance; - var yZoomLevel = this.frame.canvas.clientHeight / yDistance; - zoomLevel = (xZoomLevel <= yZoomLevel) ? xZoomLevel : yZoomLevel; - } - - if (zoomLevel > 1.0) { - zoomLevel = 1.0; - } - - - var center = this._findCenter(range); - if (disableStart == false) { - var options = {position: center, scale: zoomLevel, animation: animationOptions}; - this.moveTo(options); - this.moving = true; - this.start(); - } - else { - center.x *= zoomLevel; - center.y *= zoomLevel; - center.x -= 0.5 * this.frame.canvas.clientWidth; - center.y -= 0.5 * this.frame.canvas.clientHeight; - this._setScale(zoomLevel); - this._setTranslation(-center.x,-center.y); - } - }; - - - /** - * Update the this.nodeIndices with the most recent node index list - * @private - */ - Network.prototype._updateNodeIndexList = function() { - this._clearNodeIndexList(); - for (var idx in this.nodes) { - if (this.nodes.hasOwnProperty(idx)) { - this.nodeIndices.push(idx); - } - } - }; - - - /** - * Set nodes and edges, and optionally options as well. - * - * @param {Object} data Object containing parameters: - * {Array | DataSet | DataView} [nodes] Array with nodes - * {Array | DataSet | DataView} [edges] Array with edges - * {String} [dot] String containing data in DOT format - * {String} [gephi] String containing data in gephi JSON format - * {Options} [options] Object with options - * @param {Boolean} [disableStart] | optional: disable the calling of the start function. - */ - Network.prototype.setData = function(data, disableStart) { - if (disableStart === undefined) { - disableStart = false; - } - // we set initializing to true to ensure that the hierarchical layout is not performed until both nodes and edges are added. - this.initializing = true; - - if (data && data.dot && (data.nodes || data.edges)) { - throw new SyntaxError('Data must contain either parameter "dot" or ' + - ' parameter pair "nodes" and "edges", but not both.'); - } - - // clean up in case there is anyone in an active mode of the manipulation. This is the same option as bound to the escape button. - if (this.constants.dataManipulation.enabled == true) { - this._createManipulatorBar(); - } - - // set options - this.setOptions(data && data.options); - // set all data - if (data && data.dot) { - // parse DOT file - if(data && data.dot) { - var dotData = dotparser.DOTToGraph(data.dot); - this.setData(dotData); - return; - } - } - else if (data && data.gephi) { - // parse DOT file - if(data && data.gephi) { - var gephiData = gephiParser.parseGephi(data.gephi); - this.setData(gephiData); - return; - } + }); } else { - this._setNodes(data && data.nodes); - this._setEdges(data && data.edges); - } - this._putDataInSector(); - if (disableStart == false) { - if (this.constants.hierarchicalLayout.enabled == true) { - this._resetLevels(); - this._setupHierarchicalLayout(); + if (Array.isArray(array2)) { + array2.forEach(function (elem2) { + fn(array1, elem2); + }); } else { - // find a stable position or start animating to a stable position - if (this.constants.stabilize) { - this._stabilize(); - } + fn(array1, array2); } - this.start(); } - this.initializing = false; - }; + } /** - * Set options - * @param {Object} options + * Convert a string containing a graph in DOT language into a map containing + * with nodes and edges in the format of graph. + * @param {String} data Text containing a graph in DOT-notation + * @return {Object} graphData */ - Network.prototype.setOptions = function (options) { - if (options) { - var prop; - var fields = ['nodes','edges','smoothCurves','hierarchicalLayout','clustering','navigation', - 'keyboard','dataManipulation','onAdd','onEdit','onEditEdge','onConnect','onDelete','clickToUse' - ]; - // extend all but the values in fields - util.selectiveNotDeepExtend(fields,this.constants, options); - util.selectiveNotDeepExtend(['color'],this.constants.nodes, options.nodes); - util.selectiveNotDeepExtend(['color','length'],this.constants.edges, options.edges); - - if (options.physics) { - util.mergeOptions(this.constants.physics, options.physics,'barnesHut'); - util.mergeOptions(this.constants.physics, options.physics,'repulsion'); + function DOTToGraph (data) { + // parse the DOT file + var dotData = parseDOT(data); + var graphData = { + nodes: [], + edges: [], + options: {} + }; - if (options.physics.hierarchicalRepulsion) { - this.constants.hierarchicalLayout.enabled = true; - this.constants.physics.hierarchicalRepulsion.enabled = true; - this.constants.physics.barnesHut.enabled = false; - for (prop in options.physics.hierarchicalRepulsion) { - if (options.physics.hierarchicalRepulsion.hasOwnProperty(prop)) { - this.constants.physics.hierarchicalRepulsion[prop] = options.physics.hierarchicalRepulsion[prop]; - } - } + // copy the nodes + if (dotData.nodes) { + dotData.nodes.forEach(function (dotNode) { + var graphNode = { + id: dotNode.id, + label: String(dotNode.label || dotNode.id) + }; + merge(graphNode, dotNode.attr); + if (graphNode.image) { + graphNode.shape = 'image'; } - } - - if (options.onAdd) {this.triggerFunctions.add = options.onAdd;} - if (options.onEdit) {this.triggerFunctions.edit = options.onEdit;} - if (options.onEditEdge) {this.triggerFunctions.editEdge = options.onEditEdge;} - if (options.onConnect) {this.triggerFunctions.connect = options.onConnect;} - if (options.onDelete) {this.triggerFunctions.del = options.onDelete;} - - util.mergeOptions(this.constants, options,'smoothCurves'); - util.mergeOptions(this.constants, options,'hierarchicalLayout'); - util.mergeOptions(this.constants, options,'clustering'); - util.mergeOptions(this.constants, options,'navigation'); - util.mergeOptions(this.constants, options,'keyboard'); - util.mergeOptions(this.constants, options,'dataManipulation'); - + graphData.nodes.push(graphNode); + }); + } - if (options.dataManipulation) { - this.editMode = this.constants.dataManipulation.initiallyVisible; + // copy the edges + if (dotData.edges) { + /** + * Convert an edge in DOT format to an edge with VisGraph format + * @param {Object} dotEdge + * @returns {Object} graphEdge + */ + var convertEdge = function (dotEdge) { + var graphEdge = { + from: dotEdge.from, + to: dotEdge.to + }; + merge(graphEdge, dotEdge.attr); + graphEdge.style = (dotEdge.type == '->') ? 'arrow' : 'line'; + return graphEdge; } - - // TODO: work out these options and document them - if (options.edges) { - if (options.edges.color !== undefined) { - if (util.isString(options.edges.color)) { - this.constants.edges.color = {}; - this.constants.edges.color.color = options.edges.color; - this.constants.edges.color.highlight = options.edges.color; - this.constants.edges.color.hover = options.edges.color; - } - else { - if (options.edges.color.color !== undefined) {this.constants.edges.color.color = options.edges.color.color;} - if (options.edges.color.highlight !== undefined) {this.constants.edges.color.highlight = options.edges.color.highlight;} - if (options.edges.color.hover !== undefined) {this.constants.edges.color.hover = options.edges.color.hover;} - } - this.constants.edges.inheritColor = false; + dotData.edges.forEach(function (dotEdge) { + var from, to; + if (dotEdge.from instanceof Object) { + from = dotEdge.from.nodes; } - - if (!options.edges.fontColor) { - if (options.edges.color !== undefined) { - if (util.isString(options.edges.color)) {this.constants.edges.fontColor = options.edges.color;} - else if (options.edges.color.color !== undefined) {this.constants.edges.fontColor = options.edges.color.color;} + else { + from = { + id: dotEdge.from } } - } - if (options.nodes) { - if (options.nodes.color) { - var newColorObj = util.parseColor(options.nodes.color); - this.constants.nodes.color.background = newColorObj.background; - this.constants.nodes.color.border = newColorObj.border; - this.constants.nodes.color.highlight.background = newColorObj.highlight.background; - this.constants.nodes.color.highlight.border = newColorObj.highlight.border; - this.constants.nodes.color.hover.background = newColorObj.hover.background; - this.constants.nodes.color.hover.border = newColorObj.hover.border; + if (dotEdge.to instanceof Object) { + to = dotEdge.to.nodes; } - } - if (options.groups) { - for (var groupname in options.groups) { - if (options.groups.hasOwnProperty(groupname)) { - var group = options.groups[groupname]; - this.groups.add(groupname, group); + else { + to = { + id: dotEdge.to } } - } - if (options.tooltip) { - for (prop in options.tooltip) { - if (options.tooltip.hasOwnProperty(prop)) { - this.constants.tooltip[prop] = options.tooltip[prop]; - } - } - if (options.tooltip.color) { - this.constants.tooltip.color = util.parseColor(options.tooltip.color); + if (dotEdge.from instanceof Object && dotEdge.from.edges) { + dotEdge.from.edges.forEach(function (subEdge) { + var graphEdge = convertEdge(subEdge); + graphData.edges.push(graphEdge); + }); } - } - if ('clickToUse' in options) { - if (options.clickToUse) { - if (!this.activator) { - this.activator = new Activator(this.frame); - this.activator.on('change', this._createKeyBinds.bind(this)); - } - } - else { - if (this.activator) { - this.activator.destroy(); - delete this.activator; - } + forEach2(from, to, function (from, to) { + var subEdge = createEdge(graphData, from.id, to.id, dotEdge.type, dotEdge.attr); + var graphEdge = convertEdge(subEdge); + graphData.edges.push(graphEdge); + }); + + if (dotEdge.to instanceof Object && dotEdge.to.edges) { + dotEdge.to.edges.forEach(function (subEdge) { + var graphEdge = convertEdge(subEdge); + graphData.edges.push(graphEdge); + }); } - } + }); + } - if (options.labels) { - throw new Error('Option "labels" is deprecated. Use options "locale" and "locales" instead.'); - } + // copy the options + if (dotData.attr) { + graphData.options = dotData.attr; + } + return graphData; + } - // (Re)loading the mixins that can be enabled or disabled in the options. - // load the force calculation functions, grouped under the physics system. - this._loadPhysicsSystem(); - // load the navigation system. - this._loadNavigationControls(); - // load the data manipulation system - this._loadManipulationSystem(); - // configure the smooth curves - this._configureSmoothCurves(); + // exports + exports.parseDOT = parseDOT; + exports.DOTToGraph = DOTToGraph; - // bind hammer - this._bindHammer(); - // bind keys. If disabled, this will not do anything; - this._createKeyBinds(); +/***/ }, +/* 43 */ +/***/ function(module, exports, __webpack_require__) { + + function parseGephi(gephiJSON, options) { + var edges = []; + var nodes = []; + this.options = { + edges: { + inheritColor: true + }, + nodes: { + allowedToMove: false, + parseColor: false + } + }; - this.setSize(this.constants.width, this.constants.height); - this.moving = true; - this.start(); + if (options !== undefined) { + this.options.nodes['allowedToMove'] = options.allowedToMove | false; + this.options.nodes['parseColor'] = options.parseColor | false; + this.options.edges['inheritColor'] = options.inheritColor | true; } - }; - + var gEdges = gephiJSON.edges; + var gNodes = gephiJSON.nodes; + for (var i = 0; i < gEdges.length; i++) { + var edge = {}; + var gEdge = gEdges[i]; + edge['id'] = gEdge.id; + edge['from'] = gEdge.source; + edge['to'] = gEdge.target; + edge['attributes'] = gEdge.attributes; + // edge['value'] = gEdge.attributes !== undefined ? gEdge.attributes.Weight : undefined; + // edge['width'] = edge['value'] !== undefined ? undefined : edgegEdge.size; + edge['color'] = gEdge.color; + edge['inheritColor'] = edge['color'] !== undefined ? false : this.options.inheritColor; + edges.push(edge); + } - /** - * Create the main frame for the Network. - * This function is executed once when a Network object is created. The frame - * contains a canvas, and this canvas contains all objects like the axis and - * nodes. - * @private - */ - Network.prototype._create = function () { - // remove all elements from the container element. - while (this.containerElement.hasChildNodes()) { - this.containerElement.removeChild(this.containerElement.firstChild); + for (var i = 0; i < gNodes.length; i++) { + var node = {}; + var gNode = gNodes[i]; + node['id'] = gNode.id; + node['attributes'] = gNode.attributes; + node['x'] = gNode.x; + node['y'] = gNode.y; + node['label'] = gNode.label; + if (this.options.nodes.parseColor == true) { + node['color'] = gNode.color; + } + else { + node['color'] = gNode.color !== undefined ? {background:gNode.color, border:gNode.color} : undefined; + } + node['radius'] = gNode.size; + node['allowedToMoveX'] = this.options.nodes.allowedToMove; + node['allowedToMoveY'] = this.options.nodes.allowedToMove; + nodes.push(node); } - this.frame = document.createElement('div'); - this.frame.className = 'vis network-frame'; - this.frame.style.position = 'relative'; - this.frame.style.overflow = 'hidden'; - this.frame.tabIndex = 900; + return {nodes:nodes, edges:edges}; + } + exports.parseGephi = parseGephi; - ////////////////////////////////////////////////////////////////// +/***/ }, +/* 44 */ +/***/ function(module, exports, __webpack_require__) { - this.frame.canvas = document.createElement("canvas"); - this.frame.canvas.style.position = 'relative'; - this.frame.appendChild(this.frame.canvas); + // first check if moment.js is already loaded in the browser window, if so, + // use this instance. Else, load via commonjs. + module.exports = (typeof window !== 'undefined') && window['moment'] || __webpack_require__(59); - if (!this.frame.canvas.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.frame.canvas.appendChild(noCanvas); - } - else { - var ctx = this.frame.canvas.getContext("2d"); - this.pixelRatio = (window.devicePixelRatio || 1) / (ctx.webkitBackingStorePixelRatio || - ctx.mozBackingStorePixelRatio || - ctx.msBackingStorePixelRatio || - ctx.oBackingStorePixelRatio || - ctx.backingStorePixelRatio || 1); - this.frame.canvas.getContext("2d").setTransform(this.pixelRatio, 0, 0, this.pixelRatio, 0, 0); +/***/ }, +/* 45 */ +/***/ function(module, exports, __webpack_require__) { + + // Only load hammer.js when in a browser environment + // (loading hammer.js in a node.js environment gives errors) + if (typeof window !== 'undefined') { + module.exports = window['Hammer'] || __webpack_require__(58); + } + else { + module.exports = function () { + throw Error('hammer.js is only available in a browser, not in node.js.'); } + } - this._bindHammer(); - }; +/***/ }, +/* 46 */ +/***/ function(module, exports, __webpack_require__) { + + var Emitter = __webpack_require__(56); + var Hammer = __webpack_require__(45); + var util = __webpack_require__(1); + var DataSet = __webpack_require__(3); + var DataView = __webpack_require__(4); + var Range = __webpack_require__(17); + var ItemSet = __webpack_require__(27); + var Activator = __webpack_require__(55); + var DateUtil = __webpack_require__(15); /** - * This function binds hammer, it can be repeated over and over due to the uniqueness check. - * @private + * Create a timeline visualization + * @param {HTMLElement} container + * @param {vis.DataSet | Array | google.visualization.DataTable} [items] + * @param {Object} [options] See Core.setOptions for the available options. + * @constructor */ - Network.prototype._bindHammer = function() { - var me = this; - if (this.hammer !== undefined) { - this.hammer.dispose(); - } - this.drag = {}; - this.pinch = {}; - this.hammer = Hammer(this.frame.canvas, { - prevent_default: true - }); - this.hammer.on('tap', me._onTap.bind(me) ); - this.hammer.on('doubletap', me._onDoubleTap.bind(me) ); - this.hammer.on('hold', me._onHold.bind(me) ); - this.hammer.on('touch', me._onTouch.bind(me) ); - this.hammer.on('dragstart', me._onDragStart.bind(me) ); - this.hammer.on('drag', me._onDrag.bind(me) ); - this.hammer.on('dragend', me._onDragEnd.bind(me) ); - - if (this.constants.zoomable == true) { - this.hammer.on('mousewheel', me._onMouseWheel.bind(me)); - this.hammer.on('DOMMouseScroll', me._onMouseWheel.bind(me)); // for FF - this.hammer.on('pinch', me._onPinch.bind(me) ); - } - - this.hammer.on('mousemove', me._onMouseMoveTitle.bind(me) ); - - this.hammerFrame = Hammer(this.frame, { - prevent_default: true - }); - this.hammerFrame.on('release', me._onRelease.bind(me) ); + function Core () {} - // add the frame to the container element - this.containerElement.appendChild(this.frame); - } + // turn Core into an event emitter + Emitter(Core.prototype); /** - * Binding the keys for keyboard navigation. These functions are defined in the NavigationMixin + * Create the main DOM for the Core: a root panel containing left, right, + * top, bottom, content, and background panel. + * @param {Element} container The container element where the Core will + * be attached. * @private */ - Network.prototype._createKeyBinds = function() { - var me = this; - if (this.keycharm !== undefined) { - this.keycharm.destroy(); - } + Core.prototype._create = function (container) { + this.dom = {}; - if (this.constants.keyboard.bindToWindow == true) { - this.keycharm = keycharm({container: window, preventDefault: false}); - } - else { - this.keycharm = keycharm({container: this.frame, preventDefault: false}); - } + this.dom.root = document.createElement('div'); + this.dom.background = document.createElement('div'); + this.dom.backgroundVertical = document.createElement('div'); + this.dom.backgroundHorizontal = document.createElement('div'); + this.dom.centerContainer = document.createElement('div'); + this.dom.leftContainer = document.createElement('div'); + this.dom.rightContainer = document.createElement('div'); + this.dom.center = document.createElement('div'); + this.dom.left = document.createElement('div'); + this.dom.right = document.createElement('div'); + this.dom.top = document.createElement('div'); + this.dom.bottom = document.createElement('div'); + this.dom.shadowTop = document.createElement('div'); + this.dom.shadowBottom = document.createElement('div'); + this.dom.shadowTopLeft = document.createElement('div'); + this.dom.shadowBottomLeft = document.createElement('div'); + this.dom.shadowTopRight = document.createElement('div'); + this.dom.shadowBottomRight = document.createElement('div'); - this.keycharm.reset(); + this.dom.root.className = 'vis timeline root'; + this.dom.background.className = 'vispanel background'; + this.dom.backgroundVertical.className = 'vispanel background vertical'; + this.dom.backgroundHorizontal.className = 'vispanel background horizontal'; + this.dom.centerContainer.className = 'vispanel center'; + this.dom.leftContainer.className = 'vispanel left'; + this.dom.rightContainer.className = 'vispanel right'; + this.dom.top.className = 'vispanel top'; + this.dom.bottom.className = 'vispanel bottom'; + this.dom.left.className = 'content'; + this.dom.center.className = 'content'; + this.dom.right.className = 'content'; + this.dom.shadowTop.className = 'shadow top'; + this.dom.shadowBottom.className = 'shadow bottom'; + this.dom.shadowTopLeft.className = 'shadow top'; + this.dom.shadowBottomLeft.className = 'shadow bottom'; + this.dom.shadowTopRight.className = 'shadow top'; + this.dom.shadowBottomRight.className = 'shadow bottom'; - if (this.constants.keyboard.enabled && this.isActive()) { - this.keycharm.bind("up", this._moveUp.bind(me) , "keydown"); - this.keycharm.bind("up", this._yStopMoving.bind(me), "keyup"); - this.keycharm.bind("down", this._moveDown.bind(me) , "keydown"); - this.keycharm.bind("down", this._yStopMoving.bind(me), "keyup"); - this.keycharm.bind("left", this._moveLeft.bind(me) , "keydown"); - this.keycharm.bind("left", this._xStopMoving.bind(me), "keyup"); - this.keycharm.bind("right",this._moveRight.bind(me), "keydown"); - this.keycharm.bind("right",this._xStopMoving.bind(me), "keyup"); - this.keycharm.bind("=", this._zoomIn.bind(me), "keydown"); - this.keycharm.bind("=", this._stopZoom.bind(me), "keyup"); - this.keycharm.bind("num+", this._zoomIn.bind(me), "keydown"); - this.keycharm.bind("num+", this._stopZoom.bind(me), "keyup"); - this.keycharm.bind("num-", this._zoomOut.bind(me), "keydown"); - this.keycharm.bind("num-", this._stopZoom.bind(me), "keyup"); - this.keycharm.bind("-", this._zoomOut.bind(me), "keydown"); - this.keycharm.bind("-", this._stopZoom.bind(me), "keyup"); - this.keycharm.bind("[", this._zoomIn.bind(me), "keydown"); - this.keycharm.bind("[", this._stopZoom.bind(me), "keyup"); - this.keycharm.bind("]", this._zoomOut.bind(me), "keydown"); - this.keycharm.bind("]", this._stopZoom.bind(me), "keyup"); - this.keycharm.bind("pageup",this._zoomIn.bind(me), "keydown"); - this.keycharm.bind("pageup",this._stopZoom.bind(me), "keyup"); - this.keycharm.bind("pagedown",this._zoomOut.bind(me),"keydown"); - this.keycharm.bind("pagedown",this._stopZoom.bind(me), "keyup"); - } + this.dom.root.appendChild(this.dom.background); + this.dom.root.appendChild(this.dom.backgroundVertical); + this.dom.root.appendChild(this.dom.backgroundHorizontal); + this.dom.root.appendChild(this.dom.centerContainer); + this.dom.root.appendChild(this.dom.leftContainer); + this.dom.root.appendChild(this.dom.rightContainer); + this.dom.root.appendChild(this.dom.top); + this.dom.root.appendChild(this.dom.bottom); - if (this.constants.dataManipulation.enabled == true) { - this.keycharm.bind("esc",this._createManipulatorBar.bind(me)); - this.keycharm.bind("delete",this._deleteSelected.bind(me)); - } - }; + this.dom.centerContainer.appendChild(this.dom.center); + this.dom.leftContainer.appendChild(this.dom.left); + this.dom.rightContainer.appendChild(this.dom.right); - /** - * Cleans up all bindings of the network, removing it fully from the memory IF the variable is set to null after calling this function. - * var network = new vis.Network(..); - * network.destroy(); - * network = null; - */ - Network.prototype.destroy = function() { - this.start = function () {}; - this.redraw = function () {}; - this.timer = false; + this.dom.centerContainer.appendChild(this.dom.shadowTop); + this.dom.centerContainer.appendChild(this.dom.shadowBottom); + this.dom.leftContainer.appendChild(this.dom.shadowTopLeft); + this.dom.leftContainer.appendChild(this.dom.shadowBottomLeft); + this.dom.rightContainer.appendChild(this.dom.shadowTopRight); + this.dom.rightContainer.appendChild(this.dom.shadowBottomRight); - // cleanup physicsConfiguration if it exists - this._cleanupPhysicsConfiguration(); + this.on('rangechange', this.redraw.bind(this)); + this.on('touch', this._onTouch.bind(this)); + this.on('pinch', this._onPinch.bind(this)); + this.on('dragstart', this._onDragStart.bind(this)); + this.on('drag', this._onDrag.bind(this)); - // remove keybindings - this.keycharm.reset(); + var me = this; + this.on('change', function (properties) { + if (properties && properties.queue == true) { + // redraw once on next tick + if (!me._redrawTimer) { + me._redrawTimer = setTimeout(function () { + me._redrawTimer = null; + me.redraw(); + }, 0) + } + } + else { + // redraw immediately + me.redraw(); + } + }); - // clear hammer bindings - this.hammer.dispose(); + // create event listeners for all interesting events, these events will be + // emitted via emitter + this.hammer = Hammer(this.dom.root, { + preventDefault: true + }); + this.listeners = {}; - // clear events - this.off(); + var events = [ + 'touch', 'pinch', + 'tap', 'doubletap', 'hold', + 'dragstart', 'drag', 'dragend', + 'mousewheel', 'DOMMouseScroll' // DOMMouseScroll is needed for Firefox + ]; + events.forEach(function (event) { + var listener = function () { + var args = [event].concat(Array.prototype.slice.call(arguments, 0)); + if (me.isActive()) { + me.emit.apply(me, args); + } + }; + me.hammer.on(event, listener); + me.listeners[event] = listener; + }); - this._recursiveDOMDelete(this.containerElement); - } + // size properties of each of the panels + this.props = { + root: {}, + background: {}, + centerContainer: {}, + leftContainer: {}, + rightContainer: {}, + center: {}, + left: {}, + right: {}, + top: {}, + bottom: {}, + border: {}, + scrollTop: 0, + scrollTopMin: 0 + }; + this.touch = {}; // store state information needed for touch events - Network.prototype._recursiveDOMDelete = function(DOMobject) { - while (DOMobject.hasChildNodes() == true) { - this._recursiveDOMDelete(DOMobject.firstChild); - DOMobject.removeChild(DOMobject.firstChild); - } - } + this.redrawCount = 0; - /** - * Get the pointer location from a touch location - * @param {{pageX: Number, pageY: Number}} touch - * @return {{x: Number, y: Number}} pointer - * @private - */ - Network.prototype._getPointer = function (touch) { - return { - x: touch.pageX - util.getAbsoluteLeft(this.frame.canvas), - y: touch.pageY - util.getAbsoluteTop(this.frame.canvas) - }; + // attach the root panel to the provided container + if (!container) throw new Error('No container provided'); + container.appendChild(this.dom.root); }; /** - * On start of a touch gesture, store the pointer - * @param event - * @private + * Set options. Options will be passed to all components loaded in the Timeline. + * @param {Object} [options] + * {String} orientation + * Vertical orientation for the Timeline, + * can be 'bottom' (default) or 'top'. + * {String | Number} width + * Width for the timeline, a number in pixels or + * a css string like '1000px' or '75%'. '100%' by default. + * {String | Number} height + * Fixed height for the Timeline, a number in pixels or + * a css string like '400px' or '75%'. If undefined, + * The Timeline will automatically size such that + * its contents fit. + * {String | Number} minHeight + * Minimum height for the Timeline, a number in pixels or + * a css string like '400px' or '75%'. + * {String | Number} maxHeight + * Maximum height for the Timeline, a number in pixels or + * a css string like '400px' or '75%'. + * {Number | Date | String} start + * Start date for the visible window + * {Number | Date | String} end + * End date for the visible window */ - Network.prototype._onTouch = function (event) { - if (new Date().valueOf() - this.touchTime > 100) { - this.drag.pointer = this._getPointer(event.gesture.center); - this.drag.pinched = false; - this.pinch.scale = this._getScale(); + Core.prototype.setOptions = function (options) { + if (options) { + // copy the known options + var fields = ['width', 'height', 'minHeight', 'maxHeight', 'autoResize', 'start', 'end', 'orientation', 'clickToUse', 'dataAttributes', 'hiddenDates']; + util.selectiveExtend(fields, this.options, options); - // to avoid double fireing of this event because we have two hammer instances. (on canvas and on frame) - this.touchTime = new Date().valueOf(); + if ('hiddenDates' in this.options) { + DateUtil.convertHiddenOptions(this.body, this.options.hiddenDates); + } - this._handleTouch(this.drag.pointer); + if ('clickToUse' in options) { + if (options.clickToUse) { + if (!this.activator) { + this.activator = new Activator(this.dom.root); + } + } + else { + if (this.activator) { + this.activator.destroy(); + delete this.activator; + } + } + } + + // enable/disable autoResize + this._initAutoResize(); } + + // propagate options to all components + this.components.forEach(function (component) { + component.setOptions(options); + }); + + // TODO: remove deprecation error one day (deprecated since version 0.8.0) + if (options && options.order) { + throw new Error('Option order is deprecated. There is no replacement for this feature.'); + } + + // redraw everything + this.redraw(); }; /** - * handle drag start event - * @private + * Returns true when the Timeline is active. + * @returns {boolean} */ - Network.prototype._onDragStart = function (event) { - this._handleDragStart(event); + Core.prototype.isActive = function () { + return !this.activator || this.activator.active; }; - /** - * This function is called by _onDragStart. - * It is separated out because we can then overload it for the datamanipulation system. - * - * @private + * Destroy the Core, clean up all DOM elements and event listeners. */ - Network.prototype._handleDragStart = function(event) { - // in case the touch event was triggered on an external div, do the initial touch now. - if (this.drag.pointer === undefined) { - this._onTouch(event); - } - - var node = this._getNodeAt(this.drag.pointer); - // note: drag.pointer is set in _onTouch to get the initial touch location - - this.drag.dragging = true; - this.drag.selection = []; - this.drag.translation = this._getTranslation(); - this.drag.nodeId = null; - this.draggingNodes = false; - - if (node != null && this.constants.dragNodes == true) { - this.draggingNodes = true; - this.drag.nodeId = node.id; - // select the clicked node if not yet selected - if (!node.isSelected()) { - this._selectObject(node,false); - } + Core.prototype.destroy = function () { + // unbind datasets + this.clear(); - this.emit("dragStart",{nodeIds:this.getSelection().nodes}); + // remove all event listeners + this.off(); - // create an array with the selected nodes and their original location and status - for (var objectId in this.selectionObj.nodes) { - if (this.selectionObj.nodes.hasOwnProperty(objectId)) { - var object = this.selectionObj.nodes[objectId]; - var s = { - id: object.id, - node: object, + // stop checking for changed size + this._stopAutoResize(); - // store original x, y, xFixed and yFixed, make the node temporarily Fixed - x: object.x, - y: object.y, - xFixed: object.xFixed, - yFixed: object.yFixed - }; + // remove from DOM + if (this.dom.root.parentNode) { + this.dom.root.parentNode.removeChild(this.dom.root); + } + this.dom = null; - object.xFixed = true; - object.yFixed = true; + // remove Activator + if (this.activator) { + this.activator.destroy(); + delete this.activator; + } - this.drag.selection.push(s); - } + // cleanup hammer touch events + for (var event in this.listeners) { + if (this.listeners.hasOwnProperty(event)) { + delete this.listeners[event]; } } + this.listeners = null; + this.hammer = null; + + // give all components the opportunity to cleanup + this.components.forEach(function (component) { + component.destroy(); + }); + + this.body = null; }; /** - * handle drag event - * @private + * Set a custom time bar + * @param {Date} time */ - Network.prototype._onDrag = function (event) { - this._handleOnDrag(event) - }; + Core.prototype.setCustomTime = function (time) { + if (!this.customTime) { + throw new Error('Cannot get custom time: Custom time bar is not enabled'); + } + this.customTime.setCustomTime(time); + }; /** - * This function is called by _onDrag. - * It is separated out because we can then overload it for the datamanipulation system. - * - * @private + * Retrieve the current custom time. + * @return {Date} customTime */ - Network.prototype._handleOnDrag = function(event) { - if (this.drag.pinched) { - return; + Core.prototype.getCustomTime = function() { + if (!this.customTime) { + throw new Error('Cannot get custom time: Custom time bar is not enabled'); } - // remove the focus on node if it is focussed on by the focusOnNode - this.releaseNode(); + return this.customTime.getCustomTime(); + }; - var pointer = this._getPointer(event.gesture.center); - var me = this; - var drag = this.drag; - var selection = drag.selection; - if (selection && selection.length && this.constants.dragNodes == true) { - // calculate delta's and new location - var deltaX = pointer.x - drag.pointer.x; - var deltaY = pointer.y - drag.pointer.y; - // update position of all selected nodes - selection.forEach(function (s) { - var node = s.node; + /** + * Get the id's of the currently visible items. + * @returns {Array} The ids of the visible items + */ + Core.prototype.getVisibleItems = function() { + return this.itemSet && this.itemSet.getVisibleItems() || []; + }; - if (!s.xFixed) { - node.x = me._XconvertDOMtoCanvas(me._XconvertCanvasToDOM(s.x) + deltaX); - } - if (!s.yFixed) { - node.y = me._YconvertDOMtoCanvas(me._YconvertCanvasToDOM(s.y) + deltaY); - } - }); + /** + * Clear the Core. By Default, items, groups and options are cleared. + * Example usage: + * + * timeline.clear(); // clear items, groups, and options + * timeline.clear({options: true}); // clear options only + * + * @param {Object} [what] Optionally specify what to clear. By default: + * {items: true, groups: true, options: true} + */ + Core.prototype.clear = function(what) { + // clear items + if (!what || what.items) { + this.setItems(null); + } - // start _animationStep if not yet running - if (!this.moving) { - this.moving = true; - this.start(); - } + // clear groups + if (!what || what.groups) { + this.setGroups(null); } - else { - // move the network - if (this.constants.dragNetwork == true) { - // if the drag was not started properly because the click started outside the network div, start it now. - if (this.drag.pointer === undefined) { - this._handleDragStart(event); - return; - } - var diffX = pointer.x - this.drag.pointer.x; - var diffY = pointer.y - this.drag.pointer.y; - this._setTranslation( - this.drag.translation.x + diffX, - this.drag.translation.y + diffY - ); - this._redraw(); - } + // clear options of timeline and of each of the components + if (!what || what.options) { + this.components.forEach(function (component) { + component.setOptions(component.defaultOptions); + }); + + this.setOptions(this.defaultOptions); // this will also do a redraw } }; /** - * handle drag start event - * @private + * Set Core window such that it fits all items + * @param {Object} [options] Available options: + * `animate: boolean | number` + * If true (default), the range is animated + * smoothly to the new window. + * If a number, the number is taken as duration + * for the animation. Default duration is 500 ms. */ - Network.prototype._onDragEnd = function (event) { - this._handleDragEnd(event); - }; - + Core.prototype.fit = function(options) { + var range = this._getDataRange(); - Network.prototype._handleDragEnd = function(event) { - this.drag.dragging = false; - var selection = this.drag.selection; - if (selection && selection.length) { - selection.forEach(function (s) { - // restore original xFixed and yFixed - s.node.xFixed = s.xFixed; - s.node.yFixed = s.yFixed; - }); - this.moving = true; - this.start(); - } - else { - this._redraw(); - } - if (this.draggingNodes == false) { - this.emit("dragEnd",{nodeIds:[]}); - } - else { - this.emit("dragEnd",{nodeIds:this.getSelection().nodes}); + // skip range set if there is no start and end date + if (range.start === null && range.end === null) { + return; } - } + var animate = (options && options.animate !== undefined) ? options.animate : true; + this.range.setRange(range.start, range.end, animate); + }; + /** - * handle tap/click event: select/unselect a node - * @private + * Calculate the data range of the items and applies a 5% window around it. + * @returns {{start: Date | null, end: Date | null}} + * @protected */ - Network.prototype._onTap = function (event) { - var pointer = this._getPointer(event.gesture.center); - this.pointerPosition = pointer; - this._handleTap(pointer); + Core.prototype._getDataRange = function() { + // apply the data range as range + var dataRange = this.getItemRange(); - }; + // add 5% space on both sides + var start = dataRange.min; + var end = dataRange.max; + if (start != null && end != null) { + var interval = (end.valueOf() - start.valueOf()); + if (interval <= 0) { + // prevent an empty interval + interval = 24 * 60 * 60 * 1000; // 1 day + } + start = new Date(start.valueOf() - interval * 0.05); + end = new Date(end.valueOf() + interval * 0.05); + } + return { + start: start, + end: end + } + }; /** - * handle doubletap event - * @private + * Set the visible window. Both parameters are optional, you can change only + * start or only end. Syntax: + * + * TimeLine.setWindow(start, end) + * TimeLine.setWindow(range) + * + * Where start and end can be a Date, number, or string, and range is an + * object with properties start and end. + * + * @param {Date | Number | String | Object} [start] Start date of visible window + * @param {Date | Number | String} [end] End date of visible window + * @param {Object} [options] Available options: + * `animate: boolean | number` + * If true (default), the range is animated + * smoothly to the new window. + * If a number, the number is taken as duration + * for the animation. Default duration is 500 ms. */ - Network.prototype._onDoubleTap = function (event) { - var pointer = this._getPointer(event.gesture.center); - this._handleDoubleTap(pointer); + Core.prototype.setWindow = function(start, end, options) { + var animate = (options && options.animate !== undefined) ? options.animate : true; + if (arguments.length == 1) { + var range = arguments[0]; + this.range.setRange(range.start, range.end, animate); + } + else { + this.range.setRange(start, end, animate); + } }; - /** - * handle long tap event: multi select nodes - * @private + * Move the window such that given time is centered on screen. + * @param {Date | Number | String} time + * @param {Object} [options] Available options: + * `animate: boolean | number` + * If true (default), the range is animated + * smoothly to the new window. + * If a number, the number is taken as duration + * for the animation. Default duration is 500 ms. */ - Network.prototype._onHold = function (event) { - var pointer = this._getPointer(event.gesture.center); - this.pointerPosition = pointer; - this._handleOnHold(pointer); + Core.prototype.moveTo = function(time, options) { + var interval = this.range.end - this.range.start; + var t = util.convert(time, 'Date').valueOf(); + + var start = t - interval / 2; + var end = t + interval / 2; + var animate = (options && options.animate !== undefined) ? options.animate : true; + + this.range.setRange(start, end, animate); }; /** - * handle the release of the screen - * - * @private + * Get the visible window + * @return {{start: Date, end: Date}} Visible range */ - Network.prototype._onRelease = function (event) { - var pointer = this._getPointer(event.gesture.center); - this._handleOnRelease(pointer); + Core.prototype.getWindow = function() { + var range = this.range.getRange(); + return { + start: new Date(range.start), + end: new Date(range.end) + }; }; /** - * Handle pinch event - * @param event - * @private + * Force a redraw of the Core. Can be useful to manually redraw when + * option autoResize=false */ - Network.prototype._onPinch = function (event) { - var pointer = this._getPointer(event.gesture.center); + Core.prototype.redraw = function() { + var resized = false; + var options = this.options; + var props = this.props; + var dom = this.dom; - this.drag.pinched = true; - if (!('scale' in this.pinch)) { - this.pinch.scale = 1; + if (!dom) return; // when destroyed + + DateUtil.updateHiddenDates(this.body, this.options.hiddenDates); + + // update class names + if (options.orientation == 'top') { + util.addClassName(dom.root, 'top'); + util.removeClassName(dom.root, 'bottom'); + } + else { + util.removeClassName(dom.root, 'top'); + util.addClassName(dom.root, 'bottom'); } - // TODO: enabled moving while pinching? - var scale = this.pinch.scale * event.gesture.scale; - this._zoom(scale, pointer) - }; + // update root width and height options + dom.root.style.maxHeight = util.option.asSize(options.maxHeight, ''); + dom.root.style.minHeight = util.option.asSize(options.minHeight, ''); + dom.root.style.width = util.option.asSize(options.width, ''); - /** - * Zoom the network in or out - * @param {Number} scale a number around 1, and between 0.01 and 10 - * @param {{x: Number, y: Number}} pointer Position on screen - * @return {Number} appliedScale scale is limited within the boundaries - * @private - */ - Network.prototype._zoom = function(scale, pointer) { - if (this.constants.zoomable == true) { - var scaleOld = this._getScale(); - if (scale < 0.00001) { - scale = 0.00001; - } - if (scale > 10) { - scale = 10; - } + // calculate border widths + props.border.left = (dom.centerContainer.offsetWidth - dom.centerContainer.clientWidth) / 2; + props.border.right = props.border.left; + props.border.top = (dom.centerContainer.offsetHeight - dom.centerContainer.clientHeight) / 2; + props.border.bottom = props.border.top; + var borderRootHeight= dom.root.offsetHeight - dom.root.clientHeight; + var borderRootWidth = dom.root.offsetWidth - dom.root.clientWidth; - var preScaleDragPointer = null; - if (this.drag !== undefined) { - if (this.drag.dragging == true) { - preScaleDragPointer = this.DOMtoCanvas(this.drag.pointer); - } - } - // + this.frame.canvas.clientHeight / 2 - var translation = this._getTranslation(); + // workaround for a bug in IE: the clientWidth of an element with + // a height:0px and overflow:hidden is not calculated and always has value 0 + if (dom.centerContainer.clientHeight === 0) { + props.border.left = props.border.top; + props.border.right = props.border.left; + } + if (dom.root.clientHeight === 0) { + borderRootWidth = borderRootHeight; + } - var scaleFrac = scale / scaleOld; - var tx = (1 - scaleFrac) * pointer.x + translation.x * scaleFrac; - var ty = (1 - scaleFrac) * pointer.y + translation.y * scaleFrac; + // calculate the heights. If any of the side panels is empty, we set the height to + // minus the border width, such that the border will be invisible + props.center.height = dom.center.offsetHeight; + props.left.height = dom.left.offsetHeight; + props.right.height = dom.right.offsetHeight; + props.top.height = dom.top.clientHeight || -props.border.top; + props.bottom.height = dom.bottom.clientHeight || -props.border.bottom; - this.areaCenter = {"x" : this._XconvertDOMtoCanvas(pointer.x), - "y" : this._YconvertDOMtoCanvas(pointer.y)}; + // TODO: compensate borders when any of the panels is empty. - this._setScale(scale); - this._setTranslation(tx, ty); - this.updateClustersDefault(); + // apply auto height + // TODO: only calculate autoHeight when needed (else we cause an extra reflow/repaint of the DOM) + var contentHeight = Math.max(props.left.height, props.center.height, props.right.height); + var autoHeight = props.top.height + contentHeight + props.bottom.height + + borderRootHeight + props.border.top + props.border.bottom; + dom.root.style.height = util.option.asSize(options.height, autoHeight + 'px'); - if (preScaleDragPointer != null) { - var postScaleDragPointer = this.canvasToDOM(preScaleDragPointer); - this.drag.pointer.x = postScaleDragPointer.x; - this.drag.pointer.y = postScaleDragPointer.y; - } + // calculate heights of the content panels + props.root.height = dom.root.offsetHeight; + props.background.height = props.root.height - borderRootHeight; + var containerHeight = props.root.height - props.top.height - props.bottom.height - + borderRootHeight; + props.centerContainer.height = containerHeight; + props.leftContainer.height = containerHeight; + props.rightContainer.height = props.leftContainer.height; - this._redraw(); + // calculate the widths of the panels + props.root.width = dom.root.offsetWidth; + props.background.width = props.root.width - borderRootWidth; + props.left.width = dom.leftContainer.clientWidth || -props.border.left; + props.leftContainer.width = props.left.width; + props.right.width = dom.rightContainer.clientWidth || -props.border.right; + props.rightContainer.width = props.right.width; + var centerWidth = props.root.width - props.left.width - props.right.width - borderRootWidth; + props.center.width = centerWidth; + props.centerContainer.width = centerWidth; + props.top.width = centerWidth; + props.bottom.width = centerWidth; - if (scaleOld < scale) { - this.emit("zoom", {direction:"+"}); + // resize the panels + dom.background.style.height = props.background.height + 'px'; + dom.backgroundVertical.style.height = props.background.height + 'px'; + dom.backgroundHorizontal.style.height = props.centerContainer.height + 'px'; + dom.centerContainer.style.height = props.centerContainer.height + 'px'; + dom.leftContainer.style.height = props.leftContainer.height + 'px'; + dom.rightContainer.style.height = props.rightContainer.height + 'px'; + + dom.background.style.width = props.background.width + 'px'; + dom.backgroundVertical.style.width = props.centerContainer.width + 'px'; + dom.backgroundHorizontal.style.width = props.background.width + 'px'; + dom.centerContainer.style.width = props.center.width + 'px'; + dom.top.style.width = props.top.width + 'px'; + dom.bottom.style.width = props.bottom.width + 'px'; + + // reposition the panels + dom.background.style.left = '0'; + dom.background.style.top = '0'; + dom.backgroundVertical.style.left = (props.left.width + props.border.left) + 'px'; + dom.backgroundVertical.style.top = '0'; + dom.backgroundHorizontal.style.left = '0'; + dom.backgroundHorizontal.style.top = props.top.height + 'px'; + dom.centerContainer.style.left = props.left.width + 'px'; + dom.centerContainer.style.top = props.top.height + 'px'; + dom.leftContainer.style.left = '0'; + dom.leftContainer.style.top = props.top.height + 'px'; + dom.rightContainer.style.left = (props.left.width + props.center.width) + 'px'; + dom.rightContainer.style.top = props.top.height + 'px'; + dom.top.style.left = props.left.width + 'px'; + dom.top.style.top = '0'; + dom.bottom.style.left = props.left.width + 'px'; + dom.bottom.style.top = (props.top.height + props.centerContainer.height) + 'px'; + + // update the scrollTop, feasible range for the offset can be changed + // when the height of the Core or of the contents of the center changed + this._updateScrollTop(); + + // reposition the scrollable contents + var offset = this.props.scrollTop; + if (options.orientation == 'bottom') { + offset += Math.max(this.props.centerContainer.height - this.props.center.height - + this.props.border.top - this.props.border.bottom, 0); + } + dom.center.style.left = '0'; + dom.center.style.top = offset + 'px'; + dom.left.style.left = '0'; + dom.left.style.top = offset + 'px'; + dom.right.style.left = '0'; + dom.right.style.top = offset + 'px'; + + // show shadows when vertical scrolling is available + var visibilityTop = this.props.scrollTop == 0 ? 'hidden' : ''; + var visibilityBottom = this.props.scrollTop == this.props.scrollTopMin ? 'hidden' : ''; + dom.shadowTop.style.visibility = visibilityTop; + dom.shadowBottom.style.visibility = visibilityBottom; + dom.shadowTopLeft.style.visibility = visibilityTop; + dom.shadowBottomLeft.style.visibility = visibilityBottom; + dom.shadowTopRight.style.visibility = visibilityTop; + dom.shadowBottomRight.style.visibility = visibilityBottom; + + // redraw all components + this.components.forEach(function (component) { + resized = component.redraw() || resized; + }); + if (resized) { + // keep repainting until all sizes are settled + var MAX_REDRAWS = 3; // maximum number of consecutive redraws + if (this.redrawCount < MAX_REDRAWS) { + this.redrawCount++; + this.redraw(); } else { - this.emit("zoom", {direction:"-"}); + console.log('WARNING: infinite loop in redraw?'); } - - return scale; + this.redrawCount = 0; } + + this.emit("finishedRedraw"); }; + // TODO: deprecated since version 1.1.0, remove some day + Core.prototype.repaint = function () { + throw new Error('Function repaint is deprecated. Use redraw instead.'); + }; /** - * Event handler for mouse wheel event, used to zoom the timeline - * See http://adomas.org/javascript-mouse-wheel/ - * https://github.com/EightMedia/hammer.js/issues/256 - * @param {MouseEvent} event - * @private + * Set a current time. This can be used for example to ensure that a client's + * time is synchronized with a shared server time. + * Only applicable when option `showCurrentTime` is true. + * @param {Date | String | Number} time A Date, unix timestamp, or + * ISO date string. */ - Network.prototype._onMouseWheel = function(event) { - // retrieve delta - var delta = 0; - if (event.wheelDelta) { /* IE/Opera. */ - delta = event.wheelDelta/120; - } else if (event.detail) { /* Mozilla case. */ - // In Mozilla, sign of delta is different than in IE. - // Also, delta is multiple of 3. - delta = -event.detail/3; + Core.prototype.setCurrentTime = function(time) { + if (!this.currentTime) { + throw new Error('Option showCurrentTime must be true'); } - // If delta is nonzero, handle it. - // Basically, delta is now positive if wheel was scrolled up, - // and negative, if wheel was scrolled down. - if (delta) { - - // calculate the new scale - var scale = this._getScale(); - var zoom = delta / 10; - if (delta < 0) { - zoom = zoom / (1 - zoom); - } - scale *= (1 + zoom); - - // calculate the pointer location - var gesture = hammerUtil.fakeGesture(this, event); - var pointer = this._getPointer(gesture.center); + this.currentTime.setCurrentTime(time); + }; - // apply the new scale - this._zoom(scale, pointer); + /** + * Get the current time. + * Only applicable when option `showCurrentTime` is true. + * @return {Date} Returns the current time. + */ + Core.prototype.getCurrentTime = function() { + if (!this.currentTime) { + throw new Error('Option showCurrentTime must be true'); } - // Prevent default actions caused by mouse wheel. - event.preventDefault(); + return this.currentTime.getCurrentTime(); }; - /** - * Mouse move handler for checking whether the title moves over a node with a title. - * @param {Event} event + * Convert a position on screen (pixels) to a datetime + * @param {int} x Position on the screen in pixels + * @return {Date} time The datetime the corresponds with given position x * @private */ - Network.prototype._onMouseMoveTitle = function (event) { - var gesture = hammerUtil.fakeGesture(this, event); - var pointer = this._getPointer(gesture.center); + // TODO: move this function to Range + Core.prototype._toTime = function(x) { + return DateUtil.toTime(this, x, this.props.center.width); + }; - // check if the previously selected node is still selected - if (this.popupObj) { - this._checkHidePopup(pointer); - } + /** + * Convert a position on the global screen (pixels) to a datetime + * @param {int} x Position on the screen in pixels + * @return {Date} time The datetime the corresponds with given position x + * @private + */ + // TODO: move this function to Range + Core.prototype._toGlobalTime = function(x) { + return DateUtil.toTime(this, x, this.props.root.width); + //var conversion = this.range.conversion(this.props.root.width); + //return new Date(x / conversion.scale + conversion.offset); + }; - // if we bind the keyboard to the div, we have to highlight it to use it. This highlights it on mouse over - if (this.constants.keyboard.bindToWindow == false && this.constants.keyboard.enabled == true) { - this.frame.focus(); - } + /** + * Convert a datetime (Date object) into a position on the screen + * @param {Date} time A date + * @return {int} x The position on the screen in pixels which corresponds + * with the given date. + * @private + */ + // TODO: move this function to Range + Core.prototype._toScreen = function(time) { + return DateUtil.toScreen(this, time, this.props.center.width); + }; - // start a timeout that will check if the mouse is positioned above - // an element - var me = this; - var checkShow = function() { - me._checkShowPopup(pointer); - }; - if (this.popupTimer) { - clearInterval(this.popupTimer); // stop any running calculationTimer - } - if (!this.drag.dragging) { - this.popupTimer = setTimeout(checkShow, this.constants.tooltip.delay); - } - /** - * Adding hover highlights - */ - if (this.constants.hover == true) { - // removing all hover highlights - for (var edgeId in this.hoverObj.edges) { - if (this.hoverObj.edges.hasOwnProperty(edgeId)) { - this.hoverObj.edges[edgeId].hover = false; - delete this.hoverObj.edges[edgeId]; - } - } + /** + * Convert a datetime (Date object) into a position on the root + * This is used to get the pixel density estimate for the screen, not the center panel + * @param {Date} time A date + * @return {int} x The position on root in pixels which corresponds + * with the given date. + * @private + */ + // TODO: move this function to Range + Core.prototype._toGlobalScreen = function(time) { + return DateUtil.toScreen(this, time, this.props.root.width); + //var conversion = this.range.conversion(this.props.root.width); + //return (time.valueOf() - conversion.offset) * conversion.scale; + }; - // adding hover highlights - var obj = this._getNodeAt(pointer); - if (obj == null) { - obj = this._getEdgeAt(pointer); - } - if (obj != null) { - this._hoverObject(obj); - } - // removing all node hover highlights except for the selected one. - for (var nodeId in this.hoverObj.nodes) { - if (this.hoverObj.nodes.hasOwnProperty(nodeId)) { - if (obj instanceof Node && obj.id != nodeId || obj instanceof Edge || obj == null) { - this._blurObject(this.hoverObj.nodes[nodeId]); - delete this.hoverObj.nodes[nodeId]; - } - } - } - this.redraw(); + /** + * Initialize watching when option autoResize is true + * @private + */ + Core.prototype._initAutoResize = function () { + if (this.options.autoResize == true) { + this._startAutoResize(); + } + else { + this._stopAutoResize(); } }; /** - * Check if there is an element on the given position in the network - * (a node or edge). If so, and if this element has a title, - * show a popup window with its title. - * - * @param {{x:Number, y:Number}} pointer + * Watch for changes in the size of the container. On resize, the Panel will + * automatically redraw itself. * @private */ - Network.prototype._checkShowPopup = function (pointer) { - var obj = { - left: this._XconvertDOMtoCanvas(pointer.x), - top: this._YconvertDOMtoCanvas(pointer.y), - right: this._XconvertDOMtoCanvas(pointer.x), - bottom: this._YconvertDOMtoCanvas(pointer.y) - }; + Core.prototype._startAutoResize = function () { + var me = this; - var id; - var lastPopupNode = this.popupObj; - var nodeUnderCursor = false; + this._stopAutoResize(); - if (this.popupObj == undefined) { - // search the nodes for overlap, select the top one in case of multiple nodes - var nodes = this.nodes; - var overlappingNodes = []; - for (id in nodes) { - if (nodes.hasOwnProperty(id)) { - var node = nodes[id]; - if (node.isOverlappingWith(obj)) { - if (node.getTitle() !== undefined) { - overlappingNodes.push(id); - } - } - } + this._onResize = function() { + if (me.options.autoResize != true) { + // stop watching when the option autoResize is changed to false + me._stopAutoResize(); + return; } - if (overlappingNodes.length > 0) { - // if there are overlapping nodes, select the last one, this is the - // one which is drawn on top of the others - this.popupObj = this.nodes[overlappingNodes[overlappingNodes.length - 1]]; - // if you hover over a node, the title of the edge is not supposed to be shown. - nodeUnderCursor = true; - } - } + if (me.dom.root) { + // check whether the frame is resized + // Note: we compare offsetWidth here, not clientWidth. For some reason, + // IE does not restore the clientWidth from 0 to the actual width after + // changing the timeline's container display style from none to visible + if ((me.dom.root.offsetWidth != me.props.lastWidth) || + (me.dom.root.offsetHeight != me.props.lastHeight)) { + me.props.lastWidth = me.dom.root.offsetWidth; + me.props.lastHeight = me.dom.root.offsetHeight; - if (this.popupObj === undefined && nodeUnderCursor == false) { - // search the edges for overlap - var edges = this.edges; - var overlappingEdges = []; - for (id in edges) { - if (edges.hasOwnProperty(id)) { - var edge = edges[id]; - if (edge.connected && (edge.getTitle() !== undefined) && - edge.isOverlappingWith(obj)) { - overlappingEdges.push(id); - } + me.emit('change'); } } + }; - if (overlappingEdges.length > 0) { - this.popupObj = this.edges[overlappingEdges[overlappingEdges.length - 1]]; - } - } - - if (this.popupObj) { - // show popup message window - if (this.popupObj != lastPopupNode) { - var me = this; - if (!me.popup) { - me.popup = new Popup(me.frame, me.constants.tooltip); - } + // add event listener to window resize + util.addEventListener(window, 'resize', this._onResize); - // adjust a small offset such that the mouse cursor is located in the - // bottom left location of the popup, and you can easily move over the - // popup area - me.popup.setPosition(pointer.x - 3, pointer.y - 3); - me.popup.setText(me.popupObj.getTitle()); - me.popup.show(); - } - } - else { - if (this.popup) { - this.popup.hide(); - } - } + this.watchTimer = setInterval(this._onResize, 1000); }; - /** - * Check if the popup must be hided, which is the case when the mouse is no - * longer hovering on the object - * @param {{x:Number, y:Number}} pointer + * Stop watching for a resize of the frame. * @private */ - Network.prototype._checkHidePopup = function (pointer) { - if (!this.popupObj || !this._getNodeAt(pointer) ) { - this.popupObj = undefined; - if (this.popup) { - this.popup.hide(); - } + Core.prototype._stopAutoResize = function () { + if (this.watchTimer) { + clearInterval(this.watchTimer); + this.watchTimer = undefined; } - }; + // remove event listener on window.resize + util.removeEventListener(window, 'resize', this._onResize); + this._onResize = null; + }; /** - * Set a new size for the network - * @param {string} width Width in pixels or percentage (for example '800px' - * or '50%') - * @param {string} height Height in pixels or percentage (for example '400px' - * or '30%') + * Start moving the timeline vertically + * @param {Event} event + * @private */ - Network.prototype.setSize = function(width, height) { - var emitEvent = false; - var oldWidth = this.frame.canvas.width; - var oldHeight = this.frame.canvas.height; - if (width != this.constants.width || height != this.constants.height || this.frame.style.width != width || this.frame.style.height != height) { - this.frame.style.width = width; - this.frame.style.height = height; - - this.frame.canvas.style.width = '100%'; - this.frame.canvas.style.height = '100%'; - - this.frame.canvas.width = this.frame.canvas.clientWidth * this.pixelRatio; - this.frame.canvas.height = this.frame.canvas.clientHeight * this.pixelRatio; - - this.constants.width = width; - this.constants.height = height; - - emitEvent = true; - } - else { - // this would adapt the width of the canvas to the width from 100% if and only if - // there is a change. - - if (this.frame.canvas.width != this.frame.canvas.clientWidth * this.pixelRatio) { - this.frame.canvas.width = this.frame.canvas.clientWidth * this.pixelRatio; - emitEvent = true; - } - if (this.frame.canvas.height != this.frame.canvas.clientHeight * this.pixelRatio) { - this.frame.canvas.height = this.frame.canvas.clientHeight * this.pixelRatio; - emitEvent = true; - } - } + Core.prototype._onTouch = function (event) { + this.touch.allowDragging = true; + }; - if (emitEvent == true) { - this.emit('resize', {width:this.frame.canvas.width * this.pixelRatio,height:this.frame.canvas.height * this.pixelRatio, oldWidth: oldWidth * this.pixelRatio, oldHeight: oldHeight * this.pixelRatio}); - } + /** + * Start moving the timeline vertically + * @param {Event} event + * @private + */ + Core.prototype._onPinch = function (event) { + this.touch.allowDragging = false; }; /** - * Set a data set with nodes for the network - * @param {Array | DataSet | DataView} nodes The data containing the nodes. + * Start moving the timeline vertically + * @param {Event} event * @private */ - Network.prototype._setNodes = function(nodes) { - var oldNodesData = this.nodesData; + Core.prototype._onDragStart = function (event) { + this.touch.initialScrollTop = this.props.scrollTop; + }; - if (nodes instanceof DataSet || nodes instanceof DataView) { - this.nodesData = nodes; - } - else if (Array.isArray(nodes)) { - this.nodesData = new DataSet(); - this.nodesData.add(nodes); - } - else if (!nodes) { - this.nodesData = new DataSet(); - } - else { - throw new TypeError('Array or DataSet expected'); - } + /** + * Move the timeline vertically + * @param {Event} event + * @private + */ + Core.prototype._onDrag = function (event) { + // refuse to drag when we where pinching to prevent the timeline make a jump + // when releasing the fingers in opposite order from the touch screen + if (!this.touch.allowDragging) return; - if (oldNodesData) { - // unsubscribe from old dataset - util.forEach(this.nodesListeners, function (callback, event) { - oldNodesData.off(event, callback); - }); - } + var delta = event.gesture.deltaY; - // remove drawn nodes - this.nodes = {}; + var oldScrollTop = this._getScrollTop(); + var newScrollTop = this._setScrollTop(this.touch.initialScrollTop + delta); - if (this.nodesData) { - // subscribe to new dataset - var me = this; - util.forEach(this.nodesListeners, function (callback, event) { - me.nodesData.on(event, callback); - }); - // draw all new nodes - var ids = this.nodesData.getIds(); - this._addNodes(ids); + if (newScrollTop != oldScrollTop) { + this.redraw(); // TODO: this causes two redraws when dragging, the other is triggered by rangechange already + this.emit("verticalDrag"); } - this._updateSelection(); }; /** - * Add nodes - * @param {Number[] | String[]} ids + * Apply a scrollTop + * @param {Number} scrollTop + * @returns {Number} scrollTop Returns the applied scrollTop * @private */ - Network.prototype._addNodes = function(ids) { - var id; - for (var i = 0, len = ids.length; i < len; i++) { - id = ids[i]; - var data = this.nodesData.get(id); - var node = new Node(data, this.images, this.groups, this.constants); - this.nodes[id] = node; // note: this may replace an existing node - if ((node.xFixed == false || node.yFixed == false) && (node.x === null || node.y === null)) { - var radius = 10 * 0.1*ids.length + 10; - var angle = 2 * Math.PI * Math.random(); - if (node.xFixed == false) {node.x = radius * Math.cos(angle);} - if (node.yFixed == false) {node.y = radius * Math.sin(angle);} - } - this.moving = true; - } - - this._updateNodeIndexList(); - if (this.constants.hierarchicalLayout.enabled == true && this.initializing == false) { - this._resetLevels(); - this._setupHierarchicalLayout(); - } - this._updateCalculationNodes(); - this._reconnectEdges(); - this._updateValueRange(this.nodes); - this.updateLabels(); + Core.prototype._setScrollTop = function (scrollTop) { + this.props.scrollTop = scrollTop; + this._updateScrollTop(); + return this.props.scrollTop; }; /** - * Update existing nodes, or create them when not yet existing - * @param {Number[] | String[]} ids + * Update the current scrollTop when the height of the containers has been changed + * @returns {Number} scrollTop Returns the applied scrollTop * @private */ - Network.prototype._updateNodes = function(ids,changedData) { - var nodes = this.nodes; - for (var i = 0, len = ids.length; i < len; i++) { - var id = ids[i]; - var node = nodes[id]; - var data = changedData[i]; - if (node) { - // update node - node.setProperties(data, this.constants); - } - else { - // create node - node = new Node(properties, this.images, this.groups, this.constants); - nodes[id] = node; + Core.prototype._updateScrollTop = function () { + // recalculate the scrollTopMin + var scrollTopMin = Math.min(this.props.centerContainer.height - this.props.center.height, 0); // is negative or zero + if (scrollTopMin != this.props.scrollTopMin) { + // in case of bottom orientation, change the scrollTop such that the contents + // do not move relative to the time axis at the bottom + if (this.options.orientation == 'bottom') { + this.props.scrollTop += (scrollTopMin - this.props.scrollTopMin); } + this.props.scrollTopMin = scrollTopMin; } - this.moving = true; - if (this.constants.hierarchicalLayout.enabled == true && this.initializing == false) { - this._resetLevels(); - this._setupHierarchicalLayout(); - } - this._updateNodeIndexList(); - this._updateValueRange(nodes); - }; - /** - * Remove existing nodes. If nodes do not exist, the method will just ignore it. - * @param {Number[] | String[]} ids - * @private - */ - Network.prototype._removeNodes = function(ids) { - var nodes = this.nodes; - for (var i = 0, len = ids.length; i < len; i++) { - var id = ids[i]; - delete nodes[id]; - } - this._updateNodeIndexList(); - if (this.constants.hierarchicalLayout.enabled == true && this.initializing == false) { - this._resetLevels(); - this._setupHierarchicalLayout(); - } - this._updateCalculationNodes(); - this._reconnectEdges(); - this._updateSelection(); - this._updateValueRange(nodes); + // limit the scrollTop to the feasible scroll range + if (this.props.scrollTop > 0) this.props.scrollTop = 0; + if (this.props.scrollTop < scrollTopMin) this.props.scrollTop = scrollTopMin; + + return this.props.scrollTop; }; /** - * Load edges by reading the data table - * @param {Array | DataSet | DataView} edges The data containing the edges. - * @private + * Get the current scrollTop + * @returns {number} scrollTop * @private */ - Network.prototype._setEdges = function(edges) { - var oldEdgesData = this.edgesData; - - if (edges instanceof DataSet || edges instanceof DataView) { - this.edgesData = edges; - } - else if (Array.isArray(edges)) { - this.edgesData = new DataSet(); - this.edgesData.add(edges); - } - else if (!edges) { - this.edgesData = new DataSet(); - } - else { - throw new TypeError('Array or DataSet expected'); - } - - if (oldEdgesData) { - // unsubscribe from old dataset - util.forEach(this.edgesListeners, function (callback, event) { - oldEdgesData.off(event, callback); - }); - } + Core.prototype._getScrollTop = function () { + return this.props.scrollTop; + }; - // remove drawn edges - this.edges = {}; + module.exports = Core; - if (this.edgesData) { - // subscribe to new dataset - var me = this; - util.forEach(this.edgesListeners, function (callback, event) { - me.edgesData.on(event, callback); - }); - // draw all new nodes - var ids = this.edgesData.getIds(); - this._addEdges(ids); - } +/***/ }, +/* 47 */ +/***/ function(module, exports, __webpack_require__) { - this._reconnectEdges(); - }; + var Hammer = __webpack_require__(45); /** - * Add edges - * @param {Number[] | String[]} ids - * @private + * Fake a hammer.js gesture. Event can be a ScrollEvent or MouseMoveEvent + * @param {Element} element + * @param {Event} event */ - Network.prototype._addEdges = function (ids) { - var edges = this.edges, - edgesData = this.edgesData; + exports.fakeGesture = function(element, event) { + var eventType = null; - for (var i = 0, len = ids.length; i < len; i++) { - var id = ids[i]; + // for hammer.js 1.0.5 + // var gesture = Hammer.event.collectEventData(this, eventType, event); - var oldEdge = edges[id]; - if (oldEdge) { - oldEdge.disconnect(); - } + // for hammer.js 1.0.6+ + var touches = Hammer.event.getTouchList(event, eventType); + var gesture = Hammer.event.collectEventData(this, eventType, touches, event); - var data = edgesData.get(id, {"showInternalIds" : true}); - edges[id] = new Edge(data, this, this.constants); + // on IE in standards mode, no touches are recognized by hammer.js, + // resulting in NaN values for center.pageX and center.pageY + if (isNaN(gesture.center.pageX)) { + gesture.center.pageX = event.pageX; } - this.moving = true; - this._updateValueRange(edges); - this._createBezierNodes(); - this._updateCalculationNodes(); - if (this.constants.hierarchicalLayout.enabled == true && this.initializing == false) { - this._resetLevels(); - this._setupHierarchicalLayout(); + if (isNaN(gesture.center.pageY)) { + gesture.center.pageY = event.pageY; } + + return gesture; }; - /** - * Update existing edges, or create them when not yet existing - * @param {Number[] | String[]} ids - * @private - */ - Network.prototype._updateEdges = function (ids) { - var edges = this.edges, - edgesData = this.edgesData; - for (var i = 0, len = ids.length; i < len; i++) { - var id = ids[i]; - var data = edgesData.get(id); - var edge = edges[id]; - if (edge) { - // update edge - edge.disconnect(); - edge.setProperties(data, this.constants); - edge.connect(); - } - else { - // create edge - edge = new Edge(data, this, this.constants); - this.edges[id] = edge; - } - } +/***/ }, +/* 48 */ +/***/ function(module, exports, __webpack_require__) { - this._createBezierNodes(); - if (this.constants.hierarchicalLayout.enabled == true && this.initializing == false) { - this._resetLevels(); - this._setupHierarchicalLayout(); - } - this.moving = true; - this._updateValueRange(edges); + // English + exports['en'] = { + current: 'current', + time: 'time' }; + exports['en_EN'] = exports['en']; + exports['en_US'] = exports['en']; - /** - * Remove existing edges. Non existing ids will be ignored - * @param {Number[] | String[]} ids - * @private - */ - Network.prototype._removeEdges = function (ids) { - var edges = this.edges; - for (var i = 0, len = ids.length; i < len; i++) { - var id = ids[i]; - var edge = edges[id]; - if (edge) { - if (edge.via != null) { - delete this.sectors['support']['nodes'][edge.via.id]; - } - edge.disconnect(); - delete edges[id]; - } - } - - this.moving = true; - this._updateValueRange(edges); - if (this.constants.hierarchicalLayout.enabled == true && this.initializing == false) { - this._resetLevels(); - this._setupHierarchicalLayout(); - } - this._updateCalculationNodes(); + // Dutch + exports['nl'] = { + custom: 'aangepaste', + time: 'tijd' }; + exports['nl_NL'] = exports['nl']; + exports['nl_BE'] = exports['nl']; - /** - * Reconnect all edges - * @private - */ - Network.prototype._reconnectEdges = function() { - var id, - nodes = this.nodes, - edges = this.edges; - for (id in nodes) { - if (nodes.hasOwnProperty(id)) { - nodes[id].edges = []; - nodes[id].dynamicEdges = []; - } - } - for (id in edges) { - if (edges.hasOwnProperty(id)) { - var edge = edges[id]; - edge.from = null; - edge.to = null; - edge.connect(); - } - } +/***/ }, +/* 49 */ +/***/ function(module, exports, __webpack_require__) { + + // English + exports['en'] = { + edit: 'Edit', + del: 'Delete selected', + back: 'Back', + addNode: 'Add Node', + addEdge: 'Add Edge', + editNode: 'Edit Node', + editEdge: 'Edit Edge', + addDescription: 'Click in an empty space to place a new node.', + edgeDescription: 'Click on a node and drag the edge to another node to connect them.', + editEdgeDescription: 'Click on the control points and drag them to a node to connect to it.', + createEdgeError: 'Cannot link edges to a cluster.', + deleteClusterError: 'Clusters cannot be deleted.' }; + exports['en_EN'] = exports['en']; + exports['en_US'] = exports['en']; - /** - * Update the values of all object in the given array according to the current - * value range of the objects in the array. - * @param {Object} obj An object containing a set of Edges or Nodes - * The objects must have a method getValue() and - * setValueRange(min, max). - * @private - */ - Network.prototype._updateValueRange = function(obj) { - var id; + // Dutch + exports['nl'] = { + edit: 'Wijzigen', + del: 'Selectie verwijderen', + back: 'Terug', + addNode: 'Node toevoegen', + addEdge: 'Link toevoegen', + editNode: 'Node wijzigen', + editEdge: 'Link wijzigen', + addDescription: 'Klik op een leeg gebied om een nieuwe node te maken.', + edgeDescription: 'Klik op een node en sleep de link naar een andere node om ze te verbinden.', + editEdgeDescription: 'Klik op de verbindingspunten en sleep ze naar een node om daarmee te verbinden.', + createEdgeError: 'Kan geen link maken naar een cluster.', + deleteClusterError: 'Clusters kunnen niet worden verwijderd.' + }; + exports['nl_NL'] = exports['nl']; + exports['nl_BE'] = exports['nl']; - // determine the range of the objects - var valueMin = undefined; - var valueMax = undefined; - for (id in obj) { - if (obj.hasOwnProperty(id)) { - var value = obj[id].getValue(); - if (value !== undefined) { - valueMin = (valueMin === undefined) ? value : Math.min(value, valueMin); - valueMax = (valueMax === undefined) ? value : Math.max(value, valueMax); - } - } - } - // adjust the range of all objects - if (valueMin !== undefined && valueMax !== undefined) { - for (id in obj) { - if (obj.hasOwnProperty(id)) { - obj[id].setValueRange(valueMin, valueMax); - } - } - } - }; +/***/ }, +/* 50 */ +/***/ function(module, exports, __webpack_require__) { /** - * Redraw the network with the current data - * chart will be resized too. + * Canvas shapes used by Network */ - Network.prototype.redraw = function() { - this.setSize(this.constants.width, this.constants.height); - this._redraw(); - }; + if (typeof CanvasRenderingContext2D !== 'undefined') { - /** - * Redraw the network with the current data - * @param hidden | used to get the first estimate of the node sizes. only the nodes are drawn after which they are quickly drawn over. - * @private - */ - Network.prototype._redraw = function(hidden) { - var ctx = this.frame.canvas.getContext('2d'); + /** + * Draw a circle shape + */ + CanvasRenderingContext2D.prototype.circle = function(x, y, r) { + this.beginPath(); + this.arc(x, y, r, 0, 2*Math.PI, false); + }; - ctx.setTransform(this.pixelRatio, 0, 0, this.pixelRatio, 0, 0); + /** + * Draw a square shape + * @param {Number} x horizontal center + * @param {Number} y vertical center + * @param {Number} r size, width and height of the square + */ + CanvasRenderingContext2D.prototype.square = function(x, y, r) { + this.beginPath(); + this.rect(x - r, y - r, r * 2, r * 2); + }; - // clear the canvas - var w = this.frame.canvas.width * this.pixelRatio; - var h = this.frame.canvas.height * this.pixelRatio; - ctx.clearRect(0, 0, w, h); + /** + * Draw a triangle shape + * @param {Number} x horizontal center + * @param {Number} y vertical center + * @param {Number} r radius, half the length of the sides of the triangle + */ + CanvasRenderingContext2D.prototype.triangle = function(x, y, r) { + // http://en.wikipedia.org/wiki/Equilateral_triangle + this.beginPath(); - // set scaling and translation - ctx.save(); - ctx.translate(this.translation.x, this.translation.y); - ctx.scale(this.scale, this.scale); + var s = r * 2; + var s2 = s / 2; + var ir = Math.sqrt(3) / 6 * s; // radius of inner circle + var h = Math.sqrt(s * s - s2 * s2); // height - this.canvasTopLeft = { - "x": this._XconvertDOMtoCanvas(0), - "y": this._YconvertDOMtoCanvas(0) + this.moveTo(x, y - (h - ir)); + this.lineTo(x + s2, y + ir); + this.lineTo(x - s2, y + ir); + this.lineTo(x, y - (h - ir)); + this.closePath(); }; - this.canvasBottomRight = { - "x": this._XconvertDOMtoCanvas(this.frame.canvas.clientWidth * this.pixelRatio), - "y": this._YconvertDOMtoCanvas(this.frame.canvas.clientHeight * this.pixelRatio) + + /** + * Draw a triangle shape in downward orientation + * @param {Number} x horizontal center + * @param {Number} y vertical center + * @param {Number} r radius + */ + CanvasRenderingContext2D.prototype.triangleDown = function(x, y, r) { + // http://en.wikipedia.org/wiki/Equilateral_triangle + this.beginPath(); + + var s = r * 2; + var s2 = s / 2; + var ir = Math.sqrt(3) / 6 * s; // radius of inner circle + var h = Math.sqrt(s * s - s2 * s2); // height + + this.moveTo(x, y + (h - ir)); + this.lineTo(x + s2, y - ir); + this.lineTo(x - s2, y - ir); + this.lineTo(x, y + (h - ir)); + this.closePath(); }; - if (!(hidden == true)) { - this._doInAllSectors("_drawAllSectorNodes", ctx); - if (this.drag.dragging == false || this.drag.dragging === undefined || this.constants.hideEdgesOnDrag == false) { - this._doInAllSectors("_drawEdges", ctx); + /** + * Draw a star shape, a star with 5 points + * @param {Number} x horizontal center + * @param {Number} y vertical center + * @param {Number} r radius, half the length of the sides of the triangle + */ + CanvasRenderingContext2D.prototype.star = function(x, y, r) { + // http://www.html5canvastutorials.com/labs/html5-canvas-star-spinner/ + this.beginPath(); + + for (var n = 0; n < 10; n++) { + var radius = (n % 2 === 0) ? r * 1.3 : r * 0.5; + this.lineTo( + x + radius * Math.sin(n * 2 * Math.PI / 10), + y - radius * Math.cos(n * 2 * Math.PI / 10) + ); } - } - if (this.drag.dragging == false || this.drag.dragging === undefined || this.constants.hideNodesOnDrag == false) { - this._doInAllSectors("_drawNodes",ctx,false); - } + this.closePath(); + }; - if (!(hidden == true)) { - if (this.controlNodesActive == true) { - this._doInAllSectors("_drawControlNodes", ctx); - } - } + /** + * http://stackoverflow.com/questions/1255512/how-to-draw-a-rounded-rectangle-on-html-canvas + */ + CanvasRenderingContext2D.prototype.roundRect = function(x, y, w, h, r) { + var r2d = Math.PI/180; + if( w - ( 2 * r ) < 0 ) { r = ( w / 2 ); } //ensure that the radius isn't too large for x + if( h - ( 2 * r ) < 0 ) { r = ( h / 2 ); } //ensure that the radius isn't too large for y + this.beginPath(); + this.moveTo(x+r,y); + this.lineTo(x+w-r,y); + this.arc(x+w-r,y+r,r,r2d*270,r2d*360,false); + this.lineTo(x+w,y+h-r); + this.arc(x+w-r,y+h-r,r,0,r2d*90,false); + this.lineTo(x+r,y+h); + this.arc(x+r,y+h-r,r,r2d*90,r2d*180,false); + this.lineTo(x,y+r); + this.arc(x+r,y+r,r,r2d*180,r2d*270,false); + }; - // this._doInSupportSector("_drawNodes",ctx,true); - // this._drawTree(ctx,"#F00F0F"); + /** + * http://stackoverflow.com/questions/2172798/how-to-draw-an-oval-in-html5-canvas + */ + CanvasRenderingContext2D.prototype.ellipse = function(x, y, w, h) { + var kappa = .5522848, + ox = (w / 2) * kappa, // control point offset horizontal + oy = (h / 2) * kappa, // control point offset vertical + xe = x + w, // x-end + ye = y + h, // y-end + xm = x + w / 2, // x-middle + ym = y + h / 2; // y-middle - // restore original scaling and translation - ctx.restore(); + this.beginPath(); + this.moveTo(x, ym); + this.bezierCurveTo(x, ym - oy, xm - ox, y, xm, y); + this.bezierCurveTo(xm + ox, y, xe, ym - oy, xe, ym); + this.bezierCurveTo(xe, ym + oy, xm + ox, ye, xm, ye); + this.bezierCurveTo(xm - ox, ye, x, ym + oy, x, ym); + }; - if (hidden == true) { - ctx.clearRect(0, 0, w, h); - } - }; - /** - * Set the translation of the network - * @param {Number} offsetX Horizontal offset - * @param {Number} offsetY Vertical offset - * @private - */ - Network.prototype._setTranslation = function(offsetX, offsetY) { - if (this.translation === undefined) { - this.translation = { - x: 0, - y: 0 - }; - } - if (offsetX !== undefined) { - this.translation.x = offsetX; - } - if (offsetY !== undefined) { - this.translation.y = offsetY; - } + /** + * http://stackoverflow.com/questions/2172798/how-to-draw-an-oval-in-html5-canvas + */ + CanvasRenderingContext2D.prototype.database = function(x, y, w, h) { + var f = 1/3; + var wEllipse = w; + var hEllipse = h * f; - this.emit('viewChanged'); - }; + var kappa = .5522848, + ox = (wEllipse / 2) * kappa, // control point offset horizontal + oy = (hEllipse / 2) * kappa, // control point offset vertical + xe = x + wEllipse, // x-end + ye = y + hEllipse, // y-end + xm = x + wEllipse / 2, // x-middle + ym = y + hEllipse / 2, // y-middle + ymb = y + (h - hEllipse/2), // y-midlle, bottom ellipse + yeb = y + h; // y-end, bottom ellipse - /** - * Get the translation of the network - * @return {Object} translation An object with parameters x and y, both a number - * @private - */ - Network.prototype._getTranslation = function() { - return { - x: this.translation.x, - y: this.translation.y + this.beginPath(); + this.moveTo(xe, ym); + + this.bezierCurveTo(xe, ym + oy, xm + ox, ye, xm, ye); + this.bezierCurveTo(xm - ox, ye, x, ym + oy, x, ym); + + this.bezierCurveTo(x, ym - oy, xm - ox, y, xm, y); + this.bezierCurveTo(xm + ox, y, xe, ym - oy, xe, ym); + + this.lineTo(xe, ymb); + + this.bezierCurveTo(xe, ymb + oy, xm + ox, yeb, xm, yeb); + this.bezierCurveTo(xm - ox, yeb, x, ymb + oy, x, ymb); + + this.lineTo(x, ym); }; - }; - /** - * Scale the network - * @param {Number} scale Scaling factor 1.0 is unscaled - * @private - */ - Network.prototype._setScale = function(scale) { - this.scale = scale; - }; - /** - * Get the current scale of the network - * @return {Number} scale Scaling factor 1.0 is unscaled - * @private - */ - Network.prototype._getScale = function() { - return this.scale; - }; + /** + * Draw an arrow point (no line) + */ + CanvasRenderingContext2D.prototype.arrow = function(x, y, angle, length) { + // tail + var xt = x - length * Math.cos(angle); + var yt = y - length * Math.sin(angle); - /** - * Convert the X coordinate in DOM-space (coordinate point in browser relative to the container div) to - * the X coordinate in canvas-space (the simulation sandbox, which the camera looks upon) - * @param {number} x - * @returns {number} - * @private - */ - Network.prototype._XconvertDOMtoCanvas = function(x) { - return (x - this.translation.x) / this.scale; - }; + // inner tail + // TODO: allow to customize different shapes + var xi = x - length * 0.9 * Math.cos(angle); + var yi = y - length * 0.9 * Math.sin(angle); - /** - * Convert the X coordinate in canvas-space (the simulation sandbox, which the camera looks upon) to - * the X coordinate in DOM-space (coordinate point in browser relative to the container div) - * @param {number} x - * @returns {number} - * @private - */ - Network.prototype._XconvertCanvasToDOM = function(x) { - return x * this.scale + this.translation.x; - }; + // left + var xl = xt + length / 3 * Math.cos(angle + 0.5 * Math.PI); + var yl = yt + length / 3 * Math.sin(angle + 0.5 * Math.PI); - /** - * Convert the Y coordinate in DOM-space (coordinate point in browser relative to the container div) to - * the Y coordinate in canvas-space (the simulation sandbox, which the camera looks upon) - * @param {number} y - * @returns {number} - * @private - */ - Network.prototype._YconvertDOMtoCanvas = function(y) { - return (y - this.translation.y) / this.scale; - }; + // right + var xr = xt + length / 3 * Math.cos(angle - 0.5 * Math.PI); + var yr = yt + length / 3 * Math.sin(angle - 0.5 * Math.PI); - /** - * Convert the Y coordinate in canvas-space (the simulation sandbox, which the camera looks upon) to - * the Y coordinate in DOM-space (coordinate point in browser relative to the container div) - * @param {number} y - * @returns {number} - * @private - */ - Network.prototype._YconvertCanvasToDOM = function(y) { - return y * this.scale + this.translation.y ; - }; + this.beginPath(); + this.moveTo(x, y); + this.lineTo(xl, yl); + this.lineTo(xi, yi); + this.lineTo(xr, yr); + this.closePath(); + }; + /** + * Sets up the dashedLine functionality for drawing + * Original code came from http://stackoverflow.com/questions/4576724/dotted-stroke-in-canvas + * @author David Jordan + * @date 2012-08-08 + */ + CanvasRenderingContext2D.prototype.dashedLine = function(x,y,x2,y2,dashArray){ + if (!dashArray) dashArray=[10,5]; + if (dashLength==0) dashLength = 0.001; // Hack for Safari + var dashCount = dashArray.length; + this.moveTo(x, y); + var dx = (x2-x), dy = (y2-y); + var slope = dy/dx; + var distRemaining = Math.sqrt( dx*dx + dy*dy ); + var dashIndex=0, draw=true; + while (distRemaining>=0.1){ + var dashLength = dashArray[dashIndex++%dashCount]; + if (dashLength > distRemaining) dashLength = distRemaining; + var xStep = Math.sqrt( dashLength*dashLength / (1 + slope*slope) ); + if (dx<0) xStep = -xStep; + x += xStep; + y += slope*xStep; + this[draw ? 'lineTo' : 'moveTo'](x,y); + distRemaining -= dashLength; + draw = !draw; + } + }; - /** - * - * @param {object} pos = {x: number, y: number} - * @returns {{x: number, y: number}} - * @constructor - */ - Network.prototype.canvasToDOM = function (pos) { - return {x: this._XconvertCanvasToDOM(pos.x), y: this._YconvertCanvasToDOM(pos.y)}; - }; + // TODO: add diamond shape + } + + +/***/ }, +/* 51 */ +/***/ function(module, exports, __webpack_require__) { /** - * - * @param {object} pos = {x: number, y: number} - * @returns {{x: number, y: number}} - * @constructor + * Created by Alex on 11/11/2014. */ - Network.prototype.DOMtoCanvas = function (pos) { - return {x: this._XconvertDOMtoCanvas(pos.x), y: this._YconvertDOMtoCanvas(pos.y)}; + var DOMutil = __webpack_require__(2); + var Points = __webpack_require__(53); + + function Line(groupId, options) { + this.groupId = groupId; + this.options = options; + } + + Line.prototype.getYRange = function(groupData) { + var yMin = groupData[0].y; + var yMax = groupData[0].y; + for (var j = 0; j < groupData.length; j++) { + yMin = yMin > groupData[j].y ? groupData[j].y : yMin; + yMax = yMax < groupData[j].y ? groupData[j].y : yMax; + } + return {min: yMin, max: yMax, yAxisOrientation: this.options.yAxisOrientation}; }; + /** - * Redraw all nodes - * The 2d context of a HTML canvas can be retrieved by canvas.getContext('2d'); - * @param {CanvasRenderingContext2D} ctx - * @param {Boolean} [alwaysShow] - * @private + * draw a line graph + * + * @param dataset + * @param group */ - Network.prototype._drawNodes = function(ctx,alwaysShow) { - if (alwaysShow === undefined) { - alwaysShow = false; - } - - // first draw the unselected nodes - var nodes = this.nodes; - var selected = []; + Line.prototype.draw = function (dataset, group, framework) { + if (dataset != null) { + if (dataset.length > 0) { + var path, d; + var svgHeight = Number(framework.svg.style.height.replace('px','')); + path = DOMutil.getSVGElement('path', framework.svgElements, framework.svg); + path.setAttributeNS(null, "class", group.className); + if(group.style !== undefined) { + path.setAttributeNS(null, "style", group.style); + } - for (var id in nodes) { - if (nodes.hasOwnProperty(id)) { - nodes[id].setScaleAndPos(this.scale,this.canvasTopLeft,this.canvasBottomRight); - if (nodes[id].isSelected()) { - selected.push(id); + // construct path from dataset + if (group.options.catmullRom.enabled == true) { + d = Line._catmullRom(dataset, group); } else { - if (nodes[id].inArea() || alwaysShow) { - nodes[id].draw(ctx); - } + d = Line._linear(dataset); } - } - } - // draw the selected nodes on top - for (var s = 0, sMax = selected.length; s < sMax; s++) { - if (nodes[selected[s]].inArea() || alwaysShow) { - nodes[selected[s]].draw(ctx); - } - } - }; + // append with points for fill and finalize the path + if (group.options.shaded.enabled == true) { + var fillPath = DOMutil.getSVGElement('path', framework.svgElements, framework.svg); + var dFill; + if (group.options.shaded.orientation == 'top') { + dFill = 'M' + dataset[0].x + ',' + 0 + ' ' + d + 'L' + dataset[dataset.length - 1].x + ',' + 0; + } + else { + dFill = 'M' + dataset[0].x + ',' + svgHeight + ' ' + d + 'L' + dataset[dataset.length - 1].x + ',' + svgHeight; + } + fillPath.setAttributeNS(null, "class", group.className + " fill"); + if(group.options.shaded.style !== undefined) { + fillPath.setAttributeNS(null, "style", group.options.shaded.style); + } + fillPath.setAttributeNS(null, "d", dFill); + } + // copy properties to path for drawing. + path.setAttributeNS(null, 'd', 'M' + d); - /** - * Redraw all edges - * The 2d context of a HTML canvas can be retrieved by canvas.getContext('2d'); - * @param {CanvasRenderingContext2D} ctx - * @private - */ - Network.prototype._drawEdges = function(ctx) { - var edges = this.edges; - for (var id in edges) { - if (edges.hasOwnProperty(id)) { - var edge = edges[id]; - edge.setScale(this.scale); - if (edge.connected) { - edges[id].draw(ctx); + // draw points + if (group.options.drawPoints.enabled == true) { + Points.draw(dataset, group, framework); } } } }; + + /** - * Redraw all edges - * The 2d context of a HTML canvas can be retrieved by canvas.getContext('2d'); - * @param {CanvasRenderingContext2D} ctx + * This uses an uniform parametrization of the CatmullRom algorithm: + * 'On the Parameterization of Catmull-Rom Curves' by Cem Yuksel et al. + * @param data + * @returns {string} * @private */ - Network.prototype._drawControlNodes = function(ctx) { - var edges = this.edges; - for (var id in edges) { - if (edges.hasOwnProperty(id)) { - edges[id]._drawControlNodes(ctx); - } + Line._catmullRomUniform = function(data) { + // catmull rom + var p0, p1, p2, p3, bp1, bp2; + var d = Math.round(data[0].x) + ',' + Math.round(data[0].y) + ' '; + var normalization = 1/6; + var length = data.length; + for (var i = 0; i < length - 1; i++) { + + p0 = (i == 0) ? data[0] : data[i-1]; + p1 = data[i]; + p2 = data[i+1]; + p3 = (i + 2 < length) ? data[i+2] : p2; + + + // Catmull-Rom to Cubic Bezier conversion matrix + // 0 1 0 0 + // -1/6 1 1/6 0 + // 0 1/6 1 -1/6 + // 0 0 1 0 + + // bp0 = { x: p1.x, y: p1.y }; + bp1 = { x: ((-p0.x + 6*p1.x + p2.x) *normalization), y: ((-p0.y + 6*p1.y + p2.y) *normalization)}; + bp2 = { x: (( p1.x + 6*p2.x - p3.x) *normalization), y: (( p1.y + 6*p2.y - p3.y) *normalization)}; + // bp0 = { x: p2.x, y: p2.y }; + + d += 'C' + + bp1.x + ',' + + bp1.y + ' ' + + bp2.x + ',' + + bp2.y + ' ' + + p2.x + ',' + + p2.y + ' '; } + + return d; }; /** - * Find a stable position for all nodes + * This uses either the chordal or centripetal parameterization of the catmull-rom algorithm. + * By default, the centripetal parameterization is used because this gives the nicest results. + * These parameterizations are relatively heavy because the distance between 4 points have to be calculated. + * + * One optimization can be used to reuse distances since this is a sliding window approach. + * @param data + * @param group + * @returns {string} * @private */ - Network.prototype._stabilize = function() { - if (this.constants.freezeForStabilization == true) { - this._freezeDefinedNodes(); + Line._catmullRom = function(data, group) { + var alpha = group.options.catmullRom.alpha; + if (alpha == 0 || alpha === undefined) { + return this._catmullRomUniform(data); } + else { + var p0, p1, p2, p3, bp1, bp2, d1,d2,d3, A, B, N, M; + var d3powA, d2powA, d3pow2A, d2pow2A, d1pow2A, d1powA; + var d = Math.round(data[0].x) + ',' + Math.round(data[0].y) + ' '; + var length = data.length; + for (var i = 0; i < length - 1; i++) { - // find stable position - var count = 0; - while (this.moving && count < this.constants.stabilizationIterations) { - this._physicsTick(); - count++; - } + p0 = (i == 0) ? data[0] : data[i-1]; + p1 = data[i]; + p2 = data[i+1]; + p3 = (i + 2 < length) ? data[i+2] : p2; - if (this.constants.zoomExtentOnStabilize == true) { - this.zoomExtent(undefined, false, true); - } + d1 = Math.sqrt(Math.pow(p0.x - p1.x,2) + Math.pow(p0.y - p1.y,2)); + d2 = Math.sqrt(Math.pow(p1.x - p2.x,2) + Math.pow(p1.y - p2.y,2)); + d3 = Math.sqrt(Math.pow(p2.x - p3.x,2) + Math.pow(p2.y - p3.y,2)); - if (this.constants.freezeForStabilization == true) { - this._restoreFrozenNodes(); + // Catmull-Rom to Cubic Bezier conversion matrix + + // A = 2d1^2a + 3d1^a * d2^a + d3^2a + // B = 2d3^2a + 3d3^a * d2^a + d2^2a + + // [ 0 1 0 0 ] + // [ -d2^2a /N A/N d1^2a /N 0 ] + // [ 0 d3^2a /M B/M -d2^2a /M ] + // [ 0 0 1 0 ] + + d3powA = Math.pow(d3, alpha); + d3pow2A = Math.pow(d3,2*alpha); + d2powA = Math.pow(d2, alpha); + d2pow2A = Math.pow(d2,2*alpha); + d1powA = Math.pow(d1, alpha); + d1pow2A = Math.pow(d1,2*alpha); + + A = 2*d1pow2A + 3*d1powA * d2powA + d2pow2A; + B = 2*d3pow2A + 3*d3powA * d2powA + d2pow2A; + N = 3*d1powA * (d1powA + d2powA); + if (N > 0) {N = 1 / N;} + M = 3*d3powA * (d3powA + d2powA); + if (M > 0) {M = 1 / M;} + + bp1 = { x: ((-d2pow2A * p0.x + A*p1.x + d1pow2A * p2.x) * N), + y: ((-d2pow2A * p0.y + A*p1.y + d1pow2A * p2.y) * N)}; + + bp2 = { x: (( d3pow2A * p1.x + B*p2.x - d2pow2A * p3.x) * M), + y: (( d3pow2A * p1.y + B*p2.y - d2pow2A * p3.y) * M)}; + + if (bp1.x == 0 && bp1.y == 0) {bp1 = p1;} + if (bp2.x == 0 && bp2.y == 0) {bp2 = p2;} + d += 'C' + + bp1.x + ',' + + bp1.y + ' ' + + bp2.x + ',' + + bp2.y + ' ' + + p2.x + ',' + + p2.y + ' '; + } + + return d; } }; /** - * When initializing and stabilizing, we can freeze nodes with a predefined position. This greatly speeds up stabilization - * because only the supportnodes for the smoothCurves have to settle. - * + * this generates the SVG path for a linear drawing between datapoints. + * @param data + * @returns {string} * @private */ - Network.prototype._freezeDefinedNodes = function() { - var nodes = this.nodes; - for (var id in nodes) { - if (nodes.hasOwnProperty(id)) { - if (nodes[id].x != null && nodes[id].y != null) { - nodes[id].fixedData.x = nodes[id].xFixed; - nodes[id].fixedData.y = nodes[id].yFixed; - nodes[id].xFixed = true; - nodes[id].yFixed = true; - } + Line._linear = function(data) { + // linear + var d = ''; + for (var i = 0; i < data.length; i++) { + if (i == 0) { + d += data[i].x + ',' + data[i].y; + } + else { + d += ' ' + data[i].x + ',' + data[i].y; } } + return d; }; + module.exports = Line; + + +/***/ }, +/* 52 */ +/***/ function(module, exports, __webpack_require__) { + /** - * Unfreezes the nodes that have been frozen by _freezeDefinedNodes. + * Created by Alex on 11/11/2014. + */ + var DOMutil = __webpack_require__(2); + var Points = __webpack_require__(53); + + function Bargraph(groupId, options) { + this.groupId = groupId; + this.options = options; + } + + Bargraph.prototype.getYRange = function(groupData) { + if (this.options.barChart.handleOverlap != 'stack') { + var yMin = groupData[0].y; + var yMax = groupData[0].y; + for (var j = 0; j < groupData.length; j++) { + yMin = yMin > groupData[j].y ? groupData[j].y : yMin; + yMax = yMax < groupData[j].y ? groupData[j].y : yMax; + } + return {min: yMin, max: yMax, yAxisOrientation: this.options.yAxisOrientation}; + } + else { + var barCombinedData = []; + for (var j = 0; j < groupData.length; j++) { + barCombinedData.push({ + x: groupData[j].x, + y: groupData[j].y, + groupId: this.groupId + }); + } + return barCombinedData; + } + }; + + + + /** + * draw a bar graph * - * @private + * @param groupIds + * @param processedGroupData */ - Network.prototype._restoreFrozenNodes = function() { - var nodes = this.nodes; - for (var id in nodes) { - if (nodes.hasOwnProperty(id)) { - if (nodes[id].fixedData.x != null) { - nodes[id].xFixed = nodes[id].fixedData.x; - nodes[id].yFixed = nodes[id].fixedData.y; + Bargraph.draw = function (groupIds, processedGroupData, framework) { + var combinedData = []; + var intersections = {}; + var coreDistance; + var key, drawData; + var group; + var i,j; + var barPoints = 0; + + // combine all barchart data + for (i = 0; i < groupIds.length; i++) { + group = framework.groups[groupIds[i]]; + if (group.options.style == 'bar') { + if (group.visible == true && (framework.options.groups.visibility[groupIds[i]] === undefined || framework.options.groups.visibility[groupIds[i]] == true)) { + for (j = 0; j < processedGroupData[groupIds[i]].length; j++) { + combinedData.push({ + x: processedGroupData[groupIds[i]][j].x, + y: processedGroupData[groupIds[i]][j].y, + groupId: groupIds[i] + }); + barPoints += 1; + } + } + } + } + + if (barPoints == 0) {return;} + + // sort by time and by group + combinedData.sort(function (a, b) { + if (a.x == b.x) { + return a.groupId - b.groupId; + } else { + return a.x - b.x; + } + }); + + // get intersections + Bargraph._getDataIntersections(intersections, combinedData); + + // plot barchart + for (i = 0; i < combinedData.length; i++) { + group = framework.groups[combinedData[i].groupId]; + var minWidth = 0.1 * group.options.barChart.width; + + key = combinedData[i].x; + var heightOffset = 0; + if (intersections[key] === undefined) { + if (i+1 < combinedData.length) {coreDistance = Math.abs(combinedData[i+1].x - key);} + if (i > 0) {coreDistance = Math.min(coreDistance,Math.abs(combinedData[i-1].x - key));} + drawData = Bargraph._getSafeDrawData(coreDistance, group, minWidth); + } + else { + var nextKey = i + (intersections[key].amount - intersections[key].resolved); + var prevKey = i - (intersections[key].resolved + 1); + if (nextKey < combinedData.length) {coreDistance = Math.abs(combinedData[nextKey].x - key);} + if (prevKey > 0) {coreDistance = Math.min(coreDistance,Math.abs(combinedData[prevKey].x - key));} + drawData = Bargraph._getSafeDrawData(coreDistance, group, minWidth); + intersections[key].resolved += 1; + + if (group.options.barChart.handleOverlap == 'stack') { + heightOffset = intersections[key].accumulated; + intersections[key].accumulated += group.zeroPosition - combinedData[i].y; + } + else if (group.options.barChart.handleOverlap == 'sideBySide') { + drawData.width = drawData.width / intersections[key].amount; + drawData.offset += (intersections[key].resolved) * drawData.width - (0.5*drawData.width * (intersections[key].amount+1)); + if (group.options.barChart.align == 'left') {drawData.offset -= 0.5*drawData.width;} + else if (group.options.barChart.align == 'right') {drawData.offset += 0.5*drawData.width;} } } + DOMutil.drawBar(combinedData[i].x + drawData.offset, combinedData[i].y - heightOffset, drawData.width, group.zeroPosition - combinedData[i].y, group.className + ' bar', framework.svgElements, framework.svg); + // draw points + if (group.options.drawPoints.enabled == true) { + DOMutil.drawPoint(combinedData[i].x + drawData.offset, combinedData[i].y, group, framework.svgElements, framework.svg); + } } }; /** - * Check if any of the nodes is still moving - * @param {number} vmin the minimum velocity considered as 'moving' - * @return {boolean} true if moving, false if non of the nodes is moving + * Fill the intersections object with counters of how many datapoints share the same x coordinates + * @param intersections + * @param combinedData * @private */ - Network.prototype._isMoving = function(vmin) { - var nodes = this.nodes; - for (var id in nodes) { - if (nodes.hasOwnProperty(id) && nodes[id].isMoving(vmin)) { - return true; + Bargraph._getDataIntersections = function (intersections, combinedData) { + // get intersections + var coreDistance; + for (var i = 0; i < combinedData.length; i++) { + if (i + 1 < combinedData.length) { + coreDistance = Math.abs(combinedData[i + 1].x - combinedData[i].x); + } + if (i > 0) { + coreDistance = Math.min(coreDistance, Math.abs(combinedData[i - 1].x - combinedData[i].x)); + } + if (coreDistance == 0) { + if (intersections[combinedData[i].x] === undefined) { + intersections[combinedData[i].x] = {amount: 0, resolved: 0, accumulated: 0}; + } + intersections[combinedData[i].x].amount += 1; } } - return false; }; /** - * /** - * Perform one discrete step for all nodes + * Get the width and offset for bargraphs based on the coredistance between datapoints * + * @param coreDistance + * @param group + * @param minWidth + * @returns {{width: Number, offset: Number}} * @private */ - Network.prototype._discreteStepNodes = function() { - var interval = this.physicsDiscreteStepsize; - var nodes = this.nodes; - var nodeId; - var nodesPresent = false; + Bargraph._getSafeDrawData = function (coreDistance, group, minWidth) { + var width, offset; + if (coreDistance < group.options.barChart.width && coreDistance > 0) { + width = coreDistance < minWidth ? minWidth : coreDistance; - if (this.constants.maxVelocity > 0) { - for (nodeId in nodes) { - if (nodes.hasOwnProperty(nodeId)) { - nodes[nodeId].discreteStepLimited(interval, this.constants.maxVelocity); - nodesPresent = true; - } + offset = 0; // recalculate offset with the new width; + if (group.options.barChart.align == 'left') { + offset -= 0.5 * coreDistance; + } + else if (group.options.barChart.align == 'right') { + offset += 0.5 * coreDistance; } } else { - for (nodeId in nodes) { - if (nodes.hasOwnProperty(nodeId)) { - nodes[nodeId].discreteStep(interval); - nodesPresent = true; - } + // default settings + width = group.options.barChart.width; + offset = 0; + if (group.options.barChart.align == 'left') { + offset -= 0.5 * group.options.barChart.width; + } + else if (group.options.barChart.align == 'right') { + offset += 0.5 * group.options.barChart.width; } } - if (nodesPresent == true) { - var vminCorrected = this.constants.minVelocity / Math.max(this.scale,0.05); - if (vminCorrected > 0.5*this.constants.maxVelocity) { - return true; + return {width: width, offset: offset}; + }; + + Bargraph.getStackedBarYRange = function(barCombinedData, groupRanges, groupIds, groupLabel, orientation) { + if (barCombinedData.length > 0) { + // sort by time and by group + barCombinedData.sort(function (a, b) { + if (a.x == b.x) { + return a.groupId - b.groupId; + } else { + return a.x - b.x; + } + }); + var intersections = {}; + + Bargraph._getDataIntersections(intersections, barCombinedData); + groupRanges[groupLabel] = Bargraph._getStackedBarYRange(intersections, barCombinedData); + groupRanges[groupLabel].yAxisOrientation = orientation; + groupIds.push(groupLabel); + } + } + + Bargraph._getStackedBarYRange = function (intersections, combinedData) { + var key; + var yMin = combinedData[0].y; + var yMax = combinedData[0].y; + for (var i = 0; i < combinedData.length; i++) { + key = combinedData[i].x; + if (intersections[key] === undefined) { + yMin = yMin > combinedData[i].y ? combinedData[i].y : yMin; + yMax = yMax < combinedData[i].y ? combinedData[i].y : yMax; } else { - return this._isMoving(vminCorrected); + intersections[key].accumulated += combinedData[i].y; } } - return false; + for (var xpos in intersections) { + if (intersections.hasOwnProperty(xpos)) { + yMin = yMin > intersections[xpos].accumulated ? intersections[xpos].accumulated : yMin; + yMax = yMax < intersections[xpos].accumulated ? intersections[xpos].accumulated : yMax; + } + } + + return {min: yMin, max: yMax}; }; + module.exports = Bargraph; + +/***/ }, +/* 53 */ +/***/ function(module, exports, __webpack_require__) { - Network.prototype._revertPhysicsState = function() { - var nodes = this.nodes; - for (var nodeId in nodes) { - if (nodes.hasOwnProperty(nodeId)) { - nodes[nodeId].revertPosition(); - } - } + /** + * Created by Alex on 11/11/2014. + */ + var DOMutil = __webpack_require__(2); + + function Points(groupId, options) { + this.groupId = groupId; + this.options = options; } - Network.prototype._revertPhysicsTick = function() { - this._doInAllActiveSectors("_revertPhysicsState"); - if (this.constants.smoothCurves.enabled == true && this.constants.smoothCurves.dynamic == true) { - this._doInSupportSector("_revertPhysicsState"); + + Points.prototype.getYRange = function(groupData) { + var yMin = groupData[0].y; + var yMax = groupData[0].y; + for (var j = 0; j < groupData.length; j++) { + yMin = yMin > groupData[j].y ? groupData[j].y : yMin; + yMax = yMax < groupData[j].y ? groupData[j].y : yMax; } + return {min: yMin, max: yMax, yAxisOrientation: this.options.yAxisOrientation}; + }; + + Points.prototype.draw = function(dataset, group, framework, offset) { + Points.draw(dataset, group, framework, offset); } /** - * A single simulation step (or "tick") in the physics simulation + * draw the data points * - * @private + * @param {Array} dataset + * @param {Object} JSONcontainer + * @param {Object} svg | SVG DOM element + * @param {GraphGroup} group + * @param {Number} [offset] */ - Network.prototype._physicsTick = function() { - if (!this.freezeSimulation) { - if (this.moving == true) { - var mainMovingStatus = false; - var supportMovingStatus = false; + Points.draw = function (dataset, group, framework, offset) { + if (offset === undefined) {offset = 0;} + for (var i = 0; i < dataset.length; i++) { + DOMutil.drawPoint(dataset[i].x + offset, dataset[i].y, group, framework.svgElements, framework.svg); + } + }; - this._doInAllActiveSectors("_initializeForceCalculation"); - var mainMoving = this._doInAllActiveSectors("_discreteStepNodes"); - if (this.constants.smoothCurves.enabled == true && this.constants.smoothCurves.dynamic == true) { - supportMovingStatus = this._doInSupportSector("_discreteStepNodes"); - } - // gather movement data from all sectors, if one moves, we are NOT stabilzied - for (var i = 0; i < mainMoving.length; i++) {mainMovingStatus = mainMoving[0] || mainMovingStatus;} + module.exports = Points; - // determine if the network has stabilzied - this.moving = mainMovingStatus || supportMovingStatus; +/***/ }, +/* 54 */ +/***/ function(module, exports, __webpack_require__) { - if (this.moving == false) { - this._revertPhysicsTick(); - } - else { - // this is here to ensure that there is no start event when the network is already stable. - if (this.startedStabilization == false) { - this.emit("startStabilization"); - this.startedStabilization = true; - } - } + var PhysicsMixin = __webpack_require__(66); + var ClusterMixin = __webpack_require__(60); + var SectorsMixin = __webpack_require__(61); + var SelectionMixin = __webpack_require__(62); + var ManipulationMixin = __webpack_require__(63); + var NavigationMixin = __webpack_require__(64); + var HierarchicalLayoutMixin = __webpack_require__(65); - this.stabilizationIterations++; + /** + * Load a mixin into the network object + * + * @param {Object} sourceVariable | this object has to contain functions. + * @private + */ + exports._loadMixin = function (sourceVariable) { + for (var mixinFunction in sourceVariable) { + if (sourceVariable.hasOwnProperty(mixinFunction)) { + this[mixinFunction] = sourceVariable[mixinFunction]; } } }; /** - * This function runs one step of the animation. It calls an x amount of physics ticks and one render tick. - * It reschedules itself at the beginning of the function + * removes a mixin from the network object. * + * @param {Object} sourceVariable | this object has to contain functions. * @private */ - Network.prototype._animationStep = function() { - // reset the timer so a new scheduled animation step can be set - this.timer = undefined; - - // handle the keyboad movement - this._handleNavigation(); - - // check if the physics have settled - if (this.moving == true) { - var startTime = Date.now(); - this._physicsTick(); - var physicsTime = Date.now() - startTime; - - // run double speed if it is a little graph - if ((this.renderTimestep - this.renderTime > 2 * physicsTime || this.runDoubleSpeed == true) && this.moving == true) { - this._physicsTick(); - - // this makes sure there is no jitter. The decision is taken once to run it at double speed. - if (this.renderTime != 0) { - this.runDoubleSpeed = true - } + exports._clearMixin = function (sourceVariable) { + for (var mixinFunction in sourceVariable) { + if (sourceVariable.hasOwnProperty(mixinFunction)) { + this[mixinFunction] = undefined; } } - - var renderStartTime = Date.now(); - this._redraw(); - this.renderTime = Date.now() - renderStartTime; - - // this schedules a new animation step - this.start(); }; - if (typeof window !== 'undefined') { - window.requestAnimationFrame = window.requestAnimationFrame || window.mozRequestAnimationFrame || - window.webkitRequestAnimationFrame || window.msRequestAnimationFrame; - } /** - * Schedule a animation step with the refreshrate interval. + * Mixin the physics system and initialize the parameters required. + * + * @private */ - Network.prototype.start = function() { - if (this.moving == true || this.xIncrement != 0 || this.yIncrement != 0 || this.zoomIncrement != 0 || this.animating == true) { - if (!this.timer) { - if (this.requiresTimeout == true) { - this.timer = window.setTimeout(this._animationStep.bind(this), this.renderTimestep); // wait this.renderTimeStep milliseconds and perform the animation step function - } - else { - this.timer = window.requestAnimationFrame(this._animationStep.bind(this)); // wait this.renderTimeStep milliseconds and perform the animation step function - } - } + exports._loadPhysicsSystem = function () { + this._loadMixin(PhysicsMixin); + this._loadSelectedForceSolver(); + if (this.constants.configurePhysics == true) { + this._loadPhysicsConfiguration(); } else { - this._redraw(); - // this check is to ensure that the network does not emit these events if it was already stabilized and setOptions is called (setting moving to true and calling start()) - if (this.stabilizationIterations > 1) { - // trigger the "stabilized" event. - // The event is triggered on the next tick, to prevent the case that - // it is fired while initializing the Network, in which case you would not - // be able to catch it - var me = this; - var params = { - iterations: me.stabilizationIterations - }; - this.stabilizationIterations = 0; - this.startedStabilization = false; - setTimeout(function () { - me.emit("stabilized", params); - }, 0); - } - else { - this.stabilizationIterations = 0; - } + this._cleanupPhysicsConfiguration(); } }; /** - * Move the network according to the keyboard presses. + * Mixin the cluster system and initialize the parameters required. * * @private */ - Network.prototype._handleNavigation = function() { - if (this.xIncrement != 0 || this.yIncrement != 0) { - var translation = this._getTranslation(); - this._setTranslation(translation.x+this.xIncrement, translation.y+this.yIncrement); - } - if (this.zoomIncrement != 0) { - var center = { - x: this.frame.canvas.clientWidth / 2, - y: this.frame.canvas.clientHeight / 2 - }; - this._zoom(this.scale*(1 + this.zoomIncrement), center); - } + exports._loadClusterSystem = function () { + this.clusterSession = 0; + this.hubThreshold = 5; + this._loadMixin(ClusterMixin); }; /** - * Freeze the _animationStep + * Mixin the sector system and initialize the parameters required + * + * @private */ - Network.prototype.toggleFreeze = function() { - if (this.freezeSimulation == false) { - this.freezeSimulation = true; - } - else { - this.freezeSimulation = false; - this.start(); - } + exports._loadSectorSystem = function () { + this.sectors = {}; + this.activeSector = ["default"]; + this.sectors["active"] = {}; + this.sectors["active"]["default"] = {"nodes": {}, + "edges": {}, + "nodeIndices": [], + "formationScale": 1.0, + "drawingNode": undefined }; + this.sectors["frozen"] = {}; + this.sectors["support"] = {"nodes": {}, + "edges": {}, + "nodeIndices": [], + "formationScale": 1.0, + "drawingNode": undefined }; + + this.nodeIndices = this.sectors["active"]["default"]["nodeIndices"]; // the node indices list is used to speed up the computation of the repulsion fields + + this._loadMixin(SectorsMixin); }; /** - * This function cleans the support nodes if they are not needed and adds them when they are. + * Mixin the selection system and initialize the parameters required * - * @param {boolean} [disableStart] * @private */ - Network.prototype._configureSmoothCurves = function(disableStart) { - if (disableStart === undefined) { - disableStart = true; - } - if (this.constants.smoothCurves.enabled == true && this.constants.smoothCurves.dynamic == true) { - this._createBezierNodes(); - // cleanup unused support nodes - for (var nodeId in this.sectors['support']['nodes']) { - if (this.sectors['support']['nodes'].hasOwnProperty(nodeId)) { - if (this.edges[this.sectors['support']['nodes'][nodeId].parentEdgeId] === undefined) { - delete this.sectors['support']['nodes'][nodeId]; - } + exports._loadSelectionSystem = function () { + this.selectionObj = {nodes: {}, edges: {}}; + + this._loadMixin(SelectionMixin); + }; + + + /** + * Mixin the navigationUI (User Interface) system and initialize the parameters required + * + * @private + */ + exports._loadManipulationSystem = function () { + // reset global variables -- these are used by the selection of nodes and edges. + this.blockConnectingEdgeSelection = false; + this.forceAppendSelection = false; + + if (this.constants.dataManipulation.enabled == true) { + // load the manipulator HTML elements. All styling done in css. + if (this.manipulationDiv === undefined) { + this.manipulationDiv = document.createElement('div'); + this.manipulationDiv.className = 'network-manipulationDiv'; + if (this.editMode == true) { + this.manipulationDiv.style.display = "block"; + } + else { + this.manipulationDiv.style.display = "none"; } + this.frame.appendChild(this.manipulationDiv); } - } - else { - // delete the support nodes - this.sectors['support']['nodes'] = {}; - for (var edgeId in this.edges) { - if (this.edges.hasOwnProperty(edgeId)) { - this.edges[edgeId].via = null; + + if (this.editModeDiv === undefined) { + this.editModeDiv = document.createElement('div'); + this.editModeDiv.className = 'network-manipulation-editMode'; + if (this.editMode == true) { + this.editModeDiv.style.display = "none"; } + else { + this.editModeDiv.style.display = "block"; + } + this.frame.appendChild(this.editModeDiv); + } + + if (this.closeDiv === undefined) { + this.closeDiv = document.createElement('div'); + this.closeDiv.className = 'network-manipulation-closeDiv'; + this.closeDiv.style.display = this.manipulationDiv.style.display; + this.frame.appendChild(this.closeDiv); } + + // load the manipulation functions + this._loadMixin(ManipulationMixin); + + // create the manipulator toolbar + this._createManipulatorBar(); } + else { + if (this.manipulationDiv !== undefined) { + // removes all the bindings and overloads + this._createManipulatorBar(); + // remove the manipulation divs + this.frame.removeChild(this.manipulationDiv); + this.frame.removeChild(this.editModeDiv); + this.frame.removeChild(this.closeDiv); - this._updateCalculationNodes(); - if (!disableStart) { - this.moving = true; - this.start(); + this.manipulationDiv = undefined; + this.editModeDiv = undefined; + this.closeDiv = undefined; + // remove the mixin functions + this._clearMixin(ManipulationMixin); + } } }; /** - * 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. + * Mixin the navigation (User Interface) system and initialize the parameters required * * @private */ - Network.prototype._createBezierNodes = function() { - if (this.constants.smoothCurves.enabled == true && this.constants.smoothCurves.dynamic == true) { - for (var edgeId in this.edges) { - if (this.edges.hasOwnProperty(edgeId)) { - var edge = this.edges[edgeId]; - if (edge.via == null) { - var nodeId = "edgeId:".concat(edge.id); - this.sectors['support']['nodes'][nodeId] = new Node( - {id:nodeId, - mass:1, - shape:'circle', - image:"", - internalMultiplier:1 - },{},{},this.constants); - edge.via = this.sectors['support']['nodes'][nodeId]; - edge.via.parentEdgeId = edge.id; - edge.positionBezierNode(); - } - } - } + exports._loadNavigationControls = function () { + this._loadMixin(NavigationMixin); + // the clean function removes the button divs, this is done to remove the bindings. + this._cleanNavigation(); + if (this.constants.navigation.enabled == true) { + this._loadNavigationElements(); } }; + /** - * load the functions that load the mixins into the prototype. + * Mixin the hierarchical layout system. * * @private */ - Network.prototype._initializeMixinLoaders = function () { - for (var mixin in MixinLoader) { - if (MixinLoader.hasOwnProperty(mixin)) { - Network.prototype[mixin] = MixinLoader[mixin]; + exports._loadHierarchySystem = function () { + this._loadMixin(HierarchicalLayoutMixin); + }; + + +/***/ }, +/* 55 */ +/***/ function(module, exports, __webpack_require__) { + + var keycharm = __webpack_require__(57); + var Emitter = __webpack_require__(56); + var Hammer = __webpack_require__(45); + var util = __webpack_require__(1); + + /** + * Turn an element into an clickToUse element. + * When not active, the element has a transparent overlay. When the overlay is + * clicked, the mode is changed to active. + * When active, the element is displayed with a blue border around it, and + * the interactive contents of the element can be used. When clicked outside + * the element, the elements mode is changed to inactive. + * @param {Element} container + * @constructor + */ + function Activator(container) { + this.active = false; + + this.dom = { + container: container + }; + + this.dom.overlay = document.createElement('div'); + this.dom.overlay.className = 'overlay'; + + this.dom.container.appendChild(this.dom.overlay); + + this.hammer = Hammer(this.dom.overlay, {prevent_default: false}); + this.hammer.on('tap', this._onTapOverlay.bind(this)); + + // block all touch events (except tap) + var me = this; + var events = [ + 'touch', 'pinch', + 'doubletap', 'hold', + 'dragstart', 'drag', 'dragend', + 'mousewheel', 'DOMMouseScroll' // DOMMouseScroll is needed for Firefox + ]; + events.forEach(function (event) { + me.hammer.on(event, function (event) { + event.stopPropagation(); + }); + }); + + // attach a tap event to the window, in order to deactivate when clicking outside the timeline + this.windowHammer = Hammer(window, {prevent_default: false}); + this.windowHammer.on('tap', function (event) { + // deactivate when clicked outside the container + if (!_hasParent(event.target, container)) { + me.deactivate(); } + }); + + if (this.keycharm !== undefined) { + this.keycharm.destroy(); } - }; + this.keycharm = keycharm(); + + // keycharm listener only bounded when active) + this.escListener = this.deactivate.bind(this); + } + + // turn into an event emitter + Emitter(Activator.prototype); + + // The currently active activator + Activator.current = null; /** - * Load the XY positions of the nodes into the dataset. + * Destroy the activator. Cleans up all created DOM and event listeners */ - Network.prototype.storePosition = function() { - console.log("storePosition is depricated: use .storePositions() from now on.") - this.storePositions(); + Activator.prototype.destroy = function () { + this.deactivate(); + + // remove dom + this.dom.overlay.parentNode.removeChild(this.dom.overlay); + + // cleanup hammer instances + this.hammer = null; + this.windowHammer = null; + // FIXME: cleaning up hammer instances doesn't work (Timeline not removed from memory) }; /** - * Load the XY positions of the nodes into the dataset. + * Activate the element + * Overlay is hidden, element is decorated with a blue shadow border */ - Network.prototype.storePositions = function() { - var dataArray = []; - for (var nodeId in this.nodes) { - if (this.nodes.hasOwnProperty(nodeId)) { - var node = this.nodes[nodeId]; - var allowedToMoveX = !this.nodes.xFixed; - var allowedToMoveY = !this.nodes.yFixed; - if (this.nodesData._data[nodeId].x != Math.round(node.x) || this.nodesData._data[nodeId].y != Math.round(node.y)) { - dataArray.push({id:nodeId,x:Math.round(node.x),y:Math.round(node.y),allowedToMoveX:allowedToMoveX,allowedToMoveY:allowedToMoveY}); - } - } + Activator.prototype.activate = function () { + // we allow only one active activator at a time + if (Activator.current) { + Activator.current.deactivate(); } - this.nodesData.update(dataArray); + Activator.current = this; + + this.active = true; + this.dom.overlay.style.display = 'none'; + util.addClassName(this.dom.container, 'vis-active'); + + this.emit('change'); + this.emit('activate'); + + // ugly hack: bind ESC after emitting the events, as the Network rebinds all + // keyboard events on a 'change' event + this.keycharm.bind('esc', this.escListener); }; /** - * Return the positions of the nodes. + * Deactivate the element + * Overlay is displayed on top of the element */ - Network.prototype.getPositions = function(ids) { - var dataArray = {}; - if (ids !== undefined) { - if (Array.isArray(ids) == true) { - for (var i = 0; i < ids.length; i++) { - if (this.nodes[ids[i]] !== undefined) { - var node = this.nodes[ids[i]]; - dataArray[ids[i]] = {x: Math.round(node.x), y: Math.round(node.y)}; - } - } - } - else { - if (this.nodes[ids] !== undefined) { - var node = this.nodes[ids]; - dataArray[ids] = {x: Math.round(node.x), y: Math.round(node.y)}; - } - } - } - else { - for (var nodeId in this.nodes) { - if (this.nodes.hasOwnProperty(nodeId)) { - var node = this.nodes[nodeId]; - dataArray[nodeId] = {x: Math.round(node.x), y: Math.round(node.y)}; - } + Activator.prototype.deactivate = function () { + this.active = false; + this.dom.overlay.style.display = ''; + util.removeClassName(this.dom.container, 'vis-active'); + this.keycharm.unbind('esc', this.escListener); + + this.emit('change'); + this.emit('deactivate'); + }; + + /** + * Handle a tap event: activate the container + * @param event + * @private + */ + Activator.prototype._onTapOverlay = function (event) { + // activate the container + this.activate(); + event.stopPropagation(); + }; + + /** + * Test whether the element has the requested parent element somewhere in + * its chain of parent nodes. + * @param {HTMLElement} element + * @param {HTMLElement} parent + * @returns {boolean} Returns true when the parent is found somewhere in the + * chain of parent nodes. + * @private + */ + function _hasParent(element, parent) { + while (element) { + if (element === parent) { + return true } + element = element.parentNode; } - return dataArray; - }; + return false; + } + + module.exports = Activator; +/***/ }, +/* 56 */ +/***/ function(module, exports, __webpack_require__) { + /** - * Center a node in view. + * Expose `Emitter`. + */ + + module.exports = Emitter; + + /** + * Initialize a new `Emitter`. * - * @param {Number} nodeId - * @param {Number} [options] + * @api public */ - Network.prototype.focusOnNode = function (nodeId, options) { - if (this.nodes.hasOwnProperty(nodeId)) { - if (options === undefined) { - options = {}; - } - var nodePosition = {x: this.nodes[nodeId].x, y: this.nodes[nodeId].y}; - options.position = nodePosition; - options.lockedOnNode = nodeId; - this.moveTo(options) - } - else { - console.log("This nodeId cannot be found."); - } + function Emitter(obj) { + if (obj) return mixin(obj); }; /** + * Mixin the emitter properties. * - * @param {Object} options | options.offset = {x:Number, y:Number} // offset from the center in DOM pixels - * | options.scale = Number // scale to move to - * | options.position = {x:Number, y:Number} // position to move to - * | options.animation = {duration:Number, easingFunction:String} || Boolean // position to move to + * @param {Object} obj + * @return {Object} + * @api private */ - Network.prototype.moveTo = function (options) { - if (options === undefined) { - options = {}; - return; + + function mixin(obj) { + for (var key in Emitter.prototype) { + obj[key] = Emitter.prototype[key]; } - if (options.offset === undefined) {options.offset = {x: 0, y: 0}; } - if (options.offset.x === undefined) {options.offset.x = 0; } - if (options.offset.y === undefined) {options.offset.y = 0; } - if (options.scale === undefined) {options.scale = this._getScale(); } - if (options.position === undefined) {options.position = this._getTranslation();} - if (options.animation === undefined) {options.animation = {duration:0}; } - if (options.animation === false ) {options.animation = {duration:0}; } - if (options.animation === true ) {options.animation = {}; } - if (options.animation.duration === undefined) {options.animation.duration = 1000; } // default duration - if (options.animation.easingFunction === undefined) {options.animation.easingFunction = "easeInOutQuad"; } // default easing function + return obj; + } - this.animateView(options); + /** + * 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 {Object} options | options.offset = {x:Number, y:Number} // offset from the center in DOM pixels - * | options.time = Number // animation time in milliseconds - * | options.scale = Number // scale to animate to - * | options.position = {x:Number, y:Number} // position to animate to - * | options.easingFunction = String // linear, easeInQuad, easeOutQuad, easeInOutQuad, - * // easeInCubic, easeOutCubic, easeInOutCubic, - * // easeInQuart, easeOutQuart, easeInOutQuart, - * // easeInQuint, easeOutQuint, easeInOutQuint + * @param {String} event + * @param {Function} fn + * @return {Emitter} + * @api public */ - Network.prototype.animateView = function (options) { - if (options === undefined) { - options = {}; - return; - } - // release if something focussed on the node - this.releaseNode(); - if (options.locked == true) { - this.lockedOnNodeId = options.lockedOnNode; - this.lockedOnNodeOffset = options.offset; - } + Emitter.prototype.once = function(event, fn){ + var self = this; + this._callbacks = this._callbacks || {}; - // forcefully complete the old animation if it was still running - if (this.easingTime != 0) { - this._transitionRedraw(1); // by setting easingtime to 1, we finish the animation. + function on() { + self.off(event, on); + fn.apply(this, arguments); } - this.sourceScale = this._getScale(); - this.sourceTranslation = this._getTranslation(); - this.targetScale = options.scale; - - // set the scale so the viewCenter is based on the correct zoom level. This is overridden in the transitionRedraw - // but at least then we'll have the target transition - this._setScale(this.targetScale); - var viewCenter = this.DOMtoCanvas({x: 0.5 * this.frame.canvas.clientWidth, y: 0.5 * this.frame.canvas.clientHeight}); - var distanceFromCenter = { // offset from view, distance view has to change by these x and y to center the node - x: viewCenter.x - options.position.x, - y: viewCenter.y - options.position.y - }; - this.targetTranslation = { - x: this.sourceTranslation.x + distanceFromCenter.x * this.targetScale + options.offset.x, - y: this.sourceTranslation.y + distanceFromCenter.y * this.targetScale + options.offset.y - }; - - // if the time is set to 0, don't do an animation - if (options.animation.duration == 0) { - if (this.lockedOnNodeId != null) { - this._classicRedraw = this._redraw; - this._redraw = this._lockedRedraw; - } - else { - this._setScale(this.targetScale); - this._setTranslation(this.targetTranslation.x, this.targetTranslation.y); - this._redraw(); - } - } - else { - this.animating = true; - this.animationSpeed = 1 / (this.renderRefreshRate * options.animation.duration * 0.001) || 1 / this.renderRefreshRate; - this.animationEasingFunction = options.animation.easingFunction; - this._classicRedraw = this._redraw; - this._redraw = this._transitionRedraw; - this._redraw(); - this.start(); - } + on.fn = fn; + this.on(event, on); + return this; }; /** - * used to animate smoothly by hijacking the redraw function. - * @private + * Remove the given callback for `event` or all + * registered callbacks. + * + * @param {String} event + * @param {Function} fn + * @return {Emitter} + * @api public */ - Network.prototype._lockedRedraw = function () { - var nodePosition = {x: this.nodes[this.lockedOnNodeId].x, y: this.nodes[this.lockedOnNodeId].y}; - var viewCenter = this.DOMtoCanvas({x: 0.5 * this.frame.canvas.clientWidth, y: 0.5 * this.frame.canvas.clientHeight}); - var distanceFromCenter = { // offset from view, distance view has to change by these x and y to center the node - x: viewCenter.x - nodePosition.x, - y: viewCenter.y - nodePosition.y - }; - var sourceTranslation = this._getTranslation(); - var targetTranslation = { - x: sourceTranslation.x + distanceFromCenter.x * this.scale + this.lockedOnNodeOffset.x, - y: sourceTranslation.y + distanceFromCenter.y * this.scale + this.lockedOnNodeOffset.y - }; - this._setTranslation(targetTranslation.x,targetTranslation.y); - this._classicRedraw(); - } + Emitter.prototype.off = + Emitter.prototype.removeListener = + Emitter.prototype.removeAllListeners = + Emitter.prototype.removeEventListener = function(event, fn){ + this._callbacks = this._callbacks || {}; - Network.prototype.releaseNode = function () { - if (this.lockedOnNodeId != null) { - this._redraw = this._classicRedraw; - this.lockedOnNodeId = null; - this.lockedOnNodeOffset = null; + // all + if (0 == arguments.length) { + this._callbacks = {}; + return this; } - } - - /** - * - * @param easingTime - * @private - */ - Network.prototype._transitionRedraw = function (easingTime) { - this.easingTime = easingTime || this.easingTime + this.animationSpeed; - this.easingTime += this.animationSpeed; - - var progress = util.easingFunctions[this.animationEasingFunction](this.easingTime); - this._setScale(this.sourceScale + (this.targetScale - this.sourceScale) * progress); - this._setTranslation( - this.sourceTranslation.x + (this.targetTranslation.x - this.sourceTranslation.x) * progress, - this.sourceTranslation.y + (this.targetTranslation.y - this.sourceTranslation.y) * progress - ); + // specific event + var callbacks = this._callbacks[event]; + if (!callbacks) return this; - this._classicRedraw(); + // remove all handlers + if (1 == arguments.length) { + delete this._callbacks[event]; + return this; + } - // cleanup - if (this.easingTime >= 1.0) { - this.animating = false; - this.easingTime = 0; - if (this.lockedOnNodeId != null) { - this._redraw = this._lockedRedraw; - } - else { - this._redraw = this._classicRedraw; + // 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; } - this.emit("animationFinished"); } - }; - - Network.prototype._classicRedraw = function () { - // placeholder function to be overloaded by animations; + return this; }; /** - * Returns true when the Network is active. - * @returns {boolean} + * Emit `event` with the given args. + * + * @param {String} event + * @param {Mixed} ... + * @return {Emitter} */ - Network.prototype.isActive = function () { - return !this.activator || this.activator.active; - }; + Emitter.prototype.emit = function(event){ + this._callbacks = this._callbacks || {}; + var args = [].slice.call(arguments, 1) + , callbacks = this._callbacks[event]; - /** - * Sets the scale - * @returns {Number} - */ - Network.prototype.setScale = function () { - return this._setScale(); - }; + if (callbacks) { + callbacks = callbacks.slice(0); + for (var i = 0, len = callbacks.length; i < len; ++i) { + callbacks[i].apply(this, args); + } + } + return this; + }; /** - * Returns the scale - * @returns {Number} + * Return array of callbacks for `event`. + * + * @param {String} event + * @return {Array} + * @api public */ - Network.prototype.getScale = function () { - return this._getScale(); - }; + Emitter.prototype.listeners = function(event){ + this._callbacks = this._callbacks || {}; + return this._callbacks[event] || []; + }; /** - * Returns the scale - * @returns {Number} + * Check if this emitter has `event` handlers. + * + * @param {String} event + * @return {Boolean} + * @api public */ - Network.prototype.getCenterCoordinates = function () { - return this.DOMtoCanvas({x: 0.5 * this.frame.canvas.clientWidth, y: 0.5 * this.frame.canvas.clientHeight}); - }; - - - Network.prototype.getBoundingBox = function(nodeId) { - if (this.nodes[nodeId] !== undefined) { - return this.nodes[nodeId].boundingBox; - } - } - module.exports = Network; + Emitter.prototype.hasListeners = function(event){ + return !! this.listeners(event).length; + }; /***/ }, -/* 52 */ +/* 57 */ /***/ function(module, exports, __webpack_require__) { + var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;"use strict"; /** - * Parse a text source containing data in DOT language into a JSON object. - * The object contains two lists: one with nodes and one with edges. - * - * DOT language reference: http://www.graphviz.org/doc/info/lang.html - * - * @param {String} data Text containing a graph in DOT-notation - * @return {Object} graph An object containing two parameters: - * {Object[]} nodes - * {Object[]} edges + * Created by Alex on 11/6/2014. */ - function parseDOT (data) { - dot = data; - return parseGraph(); - } - // token types enumeration - var TOKENTYPE = { - NULL : 0, - DELIMITER : 1, - IDENTIFIER: 2, - UNKNOWN : 3 - }; + // 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 () { - // map with all delimiters - var DELIMITERS = { - '{': true, - '}': true, - '[': true, - ']': true, - ';': true, - '=': true, - ',': true, + function keycharm(options) { + var preventDefault = options && options.preventDefault || false; - '->': true, - '--': true - }; + var container = options && options.container || window; + var _exportFunctions = {}; + var _bound = {keydown:{}, keyup:{}}; + var _keys = {}; + var i; - var dot = ''; // current dot file - var index = 0; // current index in dot file - var c = ''; // current token character in expr - var token = ''; // current token - var tokenType = TOKENTYPE.NULL; // type of the token + // 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};} - /** - * Get the first character from the dot file. - * The character is stored into the char c. If the end of the dot file is - * reached, the function puts an empty string in c. - */ - function first() { - index = 0; - c = dot.charAt(0); - } + // 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}; - /** - * Get the next character from the dot file. - * The character is stored into the char c. If the end of the dot file is - * reached, the function puts an empty string in c. - */ - function next() { - index++; - c = dot.charAt(index); - } - /** - * Preview the next character from the dot file. - * @return {String} cNext - */ - function nextPreview() { - return dot.charAt(index + 1); - } - /** - * Test whether given character is alphabetic or numeric - * @param {String} c - * @return {Boolean} isAlphaNumeric - */ - var regexAlphaNumeric = /[a-zA-Z_0-9.:#]/; - function isAlphaNumeric(c) { - return regexAlphaNumeric.test(c); - } + var down = function(event) {handleEvent(event,'keydown');}; + var up = function(event) {handleEvent(event,'keyup');}; - /** - * Merge all properties of object b into object b - * @param {Object} a - * @param {Object} b - * @return {Object} a - */ - function merge (a, b) { - if (!a) { - a = {}; - } + // 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 (b) { - for (var name in b) { - if (b.hasOwnProperty(name)) { - a[name] = b[name]; + if (preventDefault == true) { + event.preventDefault(); + } } - } - } - return a; - } + }; - /** - * Set a value in an object, where the provided parameter name can be a - * path with nested parameters. For example: - * - * var obj = {a: 2}; - * setValue(obj, 'b.c', 3); // obj = {a: 2, b: {c: 3}} - * - * @param {Object} obj - * @param {String} path A parameter name or dot-separated parameter path, - * like "color.highlight.border". - * @param {*} value - */ - function setValue(obj, path, value) { - var keys = path.split('.'); - var o = obj; - while (keys.length) { - var key = keys.shift(); - if (keys.length) { - // this isn't the end point - if (!o[key]) { - o[key] = {}; + // bind a key to a callback + _exportFunctions.bind = function(key, callback, type) { + if (type === undefined) { + type = 'keydown'; } - o = o[key]; - } - else { - // this is the end point - o[key] = value; - } - } - } + 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}); + }; - /** - * Add a node to a graph object. If there is already a node with - * the same id, their attributes will be merged. - * @param {Object} graph - * @param {Object} node - */ - function addNode(graph, node) { - var i, len; - var current = null; - // find root graph (in case of subgraph) - var graphs = [graph]; // list with all graphs from current graph to root graph - var root = graph; - while (root.parent) { - graphs.push(root.parent); - root = root.parent; - } + // 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); + } + } + }; - // find existing node (at root level) by its id - if (root.nodes) { - for (i = 0, len = root.nodes.length; i < len; i++) { - if (node.id === root.nodes[i].id) { - current = root.nodes[i]; - break; + // 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"; + }; - if (!current) { - // this is a new node - current = { - id: node.id + // 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] = []; + } }; - if (graph.node) { - // clone default attributes - current.attr = merge(current.attr, graph.node); - } - } - // add node to this (sub)graph and all its parent graphs - for (i = graphs.length - 1; i >= 0; i--) { - var g = graphs[i]; + // reset all bound variables. + _exportFunctions.reset = function() { + _bound = {keydown:{}, keyup:{}}; + }; - if (!g.nodes) { - g.nodes = []; - } - if (g.nodes.indexOf(current) == -1) { - g.nodes.push(current); - } - } + // unbind all listeners and reset all variables. + _exportFunctions.destroy = function() { + _bound = {keydown:{}, keyup:{}}; + container.removeEventListener('keydown', down, true); + container.removeEventListener('keyup', up, true); + }; - // merge attributes - if (node.attr) { - current.attr = merge(current.attr, node.attr); - } - } + // create listeners. + container.addEventListener('keydown',down,true); + container.addEventListener('keyup',up,true); - /** - * Add an edge to a graph object - * @param {Object} graph - * @param {Object} edge - */ - function addEdge(graph, edge) { - if (!graph.edges) { - graph.edges = []; - } - graph.edges.push(edge); - if (graph.edge) { - var attr = merge({}, graph.edge); // clone default attributes - edge.attr = merge(attr, edge.attr); // merge attributes + // return the public functions. + return _exportFunctions; } - } - /** - * Create an edge to a graph object - * @param {Object} graph - * @param {String | Number | Object} from - * @param {String | Number | Object} to - * @param {String} type - * @param {Object | null} attr - * @return {Object} edge - */ - function createEdge(graph, from, to, type, attr) { - var edge = { - from: from, - to: to, - type: type - }; + return keycharm; + })); - if (graph.edge) { - edge.attr = merge({}, graph.edge); // clone default attributes - } - edge.attr = merge(edge.attr || {}, attr); // merge attributes - return edge; - } - /** - * Get next token in the current dot file. - * The token and token type are available as token and tokenType - */ - function getToken() { - tokenType = TOKENTYPE.NULL; - token = ''; - // skip over whitespaces - while (c == ' ' || c == '\t' || c == '\n' || c == '\r') { // space, tab, enter - next(); - } +/***/ }, +/* 58 */ +/***/ function(module, exports, __webpack_require__) { - do { - var isComment = false; + var __WEBPACK_AMD_DEFINE_RESULT__;/*! Hammer.JS - v1.1.3 - 2014-05-20 + * http://eightmedia.github.io/hammer.js + * + * Copyright (c) 2014 Jorik Tangelder ; + * Licensed under the MIT license */ - // skip comment - if (c == '#') { - // find the previous non-space character - var i = index - 1; - while (dot.charAt(i) == ' ' || dot.charAt(i) == '\t') { - i--; - } - if (dot.charAt(i) == '\n' || dot.charAt(i) == '') { - // the # is at the start of a line, this is indeed a line comment - while (c != '' && c != '\n') { - next(); - } - isComment = true; - } - } - if (c == '/' && nextPreview() == '/') { - // skip line comment - while (c != '' && c != '\n') { - next(); - } - isComment = true; - } - if (c == '/' && nextPreview() == '*') { - // skip block comment - while (c != '') { - if (c == '*' && nextPreview() == '/') { - // end of block comment found. skip these last two characters - next(); - next(); - break; - } - else { - next(); - } - } - isComment = true; - } + (function(window, undefined) { + 'use strict'; - // skip over whitespaces - while (c == ' ' || c == '\t' || c == '\n' || c == '\r') { // space, tab, enter - next(); - } - } - while (isComment); + /** + * @main + * @module hammer + * + * @class Hammer + * @static + */ - // check for end of dot file - if (c == '') { - // token is still empty - tokenType = TOKENTYPE.DELIMITER; - return; - } + /** + * Hammer, use this to create instances + * ```` + * var hammertime = new Hammer(myElement); + * ```` + * + * @method Hammer + * @param {HTMLElement} element + * @param {Object} [options={}] + * @return {Hammer.Instance} + */ + var Hammer = function Hammer(element, options) { + return new Hammer.Instance(element, options || {}); + }; - // check for delimiters consisting of 2 characters - var c2 = c + nextPreview(); - if (DELIMITERS[c2]) { - tokenType = TOKENTYPE.DELIMITER; - token = c2; - next(); - next(); - return; - } + /** + * version, as defined in package.json + * the value will be set at each build + * @property VERSION + * @final + * @type {String} + */ + Hammer.VERSION = '1.1.3'; - // check for delimiters consisting of 1 character - if (DELIMITERS[c]) { - tokenType = TOKENTYPE.DELIMITER; - token = c; - next(); - return; - } + /** + * default settings. + * more settings are defined per gesture at `/gestures`. Each gesture can be disabled/enabled + * by setting it's name (like `swipe`) to false. + * You can set the defaults for all instances by changing this object before creating an instance. + * @example + * ```` + * Hammer.defaults.drag = false; + * Hammer.defaults.behavior.touchAction = 'pan-y'; + * delete Hammer.defaults.behavior.userSelect; + * ```` + * @property defaults + * @type {Object} + */ + Hammer.defaults = { + /** + * this setting object adds styles and attributes to the element to prevent the browser from doing + * its native behavior. The css properties are auto prefixed for the browsers when needed. + * @property defaults.behavior + * @type {Object} + */ + behavior: { + /** + * Disables text selection to improve the dragging gesture. When the value is `none` it also sets + * `onselectstart=false` for IE on the element. Mainly for desktop browsers. + * @property defaults.behavior.userSelect + * @type {String} + * @default 'none' + */ + userSelect: 'none', - // check for an identifier (number or string) - // TODO: more precise parsing of numbers/strings (and the port separator ':') - if (isAlphaNumeric(c) || c == '-') { - token += c; - next(); + /** + * Specifies whether and how a given region can be manipulated by the user (for instance, by panning or zooming). + * Used by Chrome 35> and IE10>. By default this makes the element blocking any touch event. + * @property defaults.behavior.touchAction + * @type {String} + * @default: 'pan-y' + */ + touchAction: 'pan-y', - while (isAlphaNumeric(c)) { - token += c; - next(); - } - if (token == 'false') { - token = false; // convert to boolean - } - else if (token == 'true') { - token = true; // convert to boolean - } - else if (!isNaN(Number(token))) { - token = Number(token); // convert to number - } - tokenType = TOKENTYPE.IDENTIFIER; - return; - } + /** + * Disables the default callout shown when you touch and hold a touch target. + * On iOS, when you touch and hold a touch target such as a link, Safari displays + * a callout containing information about the link. This property allows you to disable that callout. + * @property defaults.behavior.touchCallout + * @type {String} + * @default 'none' + */ + touchCallout: 'none', - // check for a string enclosed by double quotes - if (c == '"') { - next(); - while (c != '' && (c != '"' || (c == '"' && nextPreview() == '"'))) { - token += c; - if (c == '"') { // skip the escape character - next(); - } - next(); - } - if (c != '"') { - throw newSyntaxError('End of string " expected'); + /** + * Specifies whether zooming is enabled. Used by IE10> + * @property defaults.behavior.contentZooming + * @type {String} + * @default 'none' + */ + contentZooming: 'none', + + /** + * Specifies that an entire element should be draggable instead of its contents. + * Mainly for desktop browsers. + * @property defaults.behavior.userDrag + * @type {String} + * @default 'none' + */ + userDrag: 'none', + + /** + * Overrides the highlight color shown when the user taps a link or a JavaScript + * clickable element in Safari on iPhone. This property obeys the alpha value, if specified. + * + * If you don't specify an alpha value, Safari on iPhone applies a default alpha value + * to the color. To disable tap highlighting, set the alpha value to 0 (invisible). + * If you set the alpha value to 1.0 (opaque), the element is not visible when tapped. + * @property defaults.behavior.tapHighlightColor + * @type {String} + * @default 'rgba(0,0,0,0)' + */ + tapHighlightColor: 'rgba(0,0,0,0)' } - next(); - tokenType = TOKENTYPE.IDENTIFIER; - return; - } + }; - // something unknown is found, wrong characters, a syntax error - tokenType = TOKENTYPE.UNKNOWN; - while (c != '') { - token += c; - next(); - } - throw new SyntaxError('Syntax error in part "' + chop(token, 30) + '"'); - } + /** + * hammer document where the base events are added at + * @property DOCUMENT + * @type {HTMLElement} + * @default window.document + */ + Hammer.DOCUMENT = document; /** - * Parse a graph. - * @returns {Object} graph + * detect support for pointer events + * @property HAS_POINTEREVENTS + * @type {Boolean} */ - function parseGraph() { - var graph = {}; + Hammer.HAS_POINTEREVENTS = navigator.pointerEnabled || navigator.msPointerEnabled; - first(); - getToken(); + /** + * detect support for touch events + * @property HAS_TOUCHEVENTS + * @type {Boolean} + */ + Hammer.HAS_TOUCHEVENTS = ('ontouchstart' in window); - // optional strict keyword - if (token == 'strict') { - graph.strict = true; - getToken(); - } + /** + * detect mobile browsers + * @property IS_MOBILE + * @type {Boolean} + */ + Hammer.IS_MOBILE = /mobile|tablet|ip(ad|hone|od)|android|silk/i.test(navigator.userAgent); - // graph or digraph keyword - if (token == 'graph' || token == 'digraph') { - graph.type = token; - getToken(); - } + /** + * detect if we want to support mouseevents at all + * @property NO_MOUSEEVENTS + * @type {Boolean} + */ + Hammer.NO_MOUSEEVENTS = (Hammer.HAS_TOUCHEVENTS && Hammer.IS_MOBILE) || Hammer.HAS_POINTEREVENTS; - // optional graph id - if (tokenType == TOKENTYPE.IDENTIFIER) { - graph.id = token; - getToken(); - } + /** + * interval in which Hammer recalculates current velocity/direction/angle in ms + * @property CALCULATE_INTERVAL + * @type {Number} + * @default 25 + */ + Hammer.CALCULATE_INTERVAL = 25; - // open angle bracket - if (token != '{') { - throw newSyntaxError('Angle bracket { expected'); - } - getToken(); + /** + * eventtypes per touchevent (start, move, end) are filled by `Event.determineEventTypes` on `setup` + * the object contains the DOM event names per type (`EVENT_START`, `EVENT_MOVE`, `EVENT_END`) + * @property EVENT_TYPES + * @private + * @writeOnce + * @type {Object} + */ + var EVENT_TYPES = {}; - // statements - parseStatements(graph); + /** + * direction strings, for safe comparisons + * @property DIRECTION_DOWN|LEFT|UP|RIGHT + * @final + * @type {String} + * @default 'down' 'left' 'up' 'right' + */ + var DIRECTION_DOWN = Hammer.DIRECTION_DOWN = 'down'; + var DIRECTION_LEFT = Hammer.DIRECTION_LEFT = 'left'; + var DIRECTION_UP = Hammer.DIRECTION_UP = 'up'; + var DIRECTION_RIGHT = Hammer.DIRECTION_RIGHT = 'right'; - // close angle bracket - if (token != '}') { - throw newSyntaxError('Angle bracket } expected'); - } - getToken(); + /** + * pointertype strings, for safe comparisons + * @property POINTER_MOUSE|TOUCH|PEN + * @final + * @type {String} + * @default 'mouse' 'touch' 'pen' + */ + var POINTER_MOUSE = Hammer.POINTER_MOUSE = 'mouse'; + var POINTER_TOUCH = Hammer.POINTER_TOUCH = 'touch'; + var POINTER_PEN = Hammer.POINTER_PEN = 'pen'; - // end of file - if (token !== '') { - throw newSyntaxError('End of file expected'); - } - getToken(); + /** + * eventtypes + * @property EVENT_START|MOVE|END|RELEASE|TOUCH + * @final + * @type {String} + * @default 'start' 'change' 'move' 'end' 'release' 'touch' + */ + var EVENT_START = Hammer.EVENT_START = 'start'; + var EVENT_MOVE = Hammer.EVENT_MOVE = 'move'; + var EVENT_END = Hammer.EVENT_END = 'end'; + var EVENT_RELEASE = Hammer.EVENT_RELEASE = 'release'; + var EVENT_TOUCH = Hammer.EVENT_TOUCH = 'touch'; - // remove temporary default properties - delete graph.node; - delete graph.edge; - delete graph.graph; + /** + * if the window events are set... + * @property READY + * @writeOnce + * @type {Boolean} + * @default false + */ + Hammer.READY = false; - return graph; - } + /** + * plugins namespace + * @property plugins + * @type {Object} + */ + Hammer.plugins = Hammer.plugins || {}; /** - * Parse a list with statements. - * @param {Object} graph + * gestures namespace + * see `/gestures` for the definitions + * @property gestures + * @type {Object} */ - function parseStatements (graph) { - while (token !== '' && token != '}') { - parseStatement(graph); - if (token == ';') { - getToken(); - } - } - } + Hammer.gestures = Hammer.gestures || {}; /** - * Parse a single statement. Can be a an attribute statement, node - * statement, a series of node statements and edge statements, or a - * parameter. - * @param {Object} graph + * setup events to detect gestures on the document + * this function is called when creating an new instance + * @private */ - function parseStatement(graph) { - // parse subgraph - var subgraph = parseSubgraph(graph); - if (subgraph) { - // edge statements - parseEdge(graph, subgraph); + function setup() { + if(Hammer.READY) { + return; + } - return; - } + // find what eventtypes we add listeners to + Event.determineEventTypes(); - // parse an attribute statement - var attr = parseAttributeStatement(graph); - if (attr) { - return; - } + // Register all gestures inside Hammer.gestures + Utils.each(Hammer.gestures, function(gesture) { + Detection.register(gesture); + }); - // parse node - if (tokenType != TOKENTYPE.IDENTIFIER) { - throw newSyntaxError('Identifier expected'); - } - var id = token; // id can be a string or a number - getToken(); + // Add touch events on the document + Event.onTouch(Hammer.DOCUMENT, EVENT_MOVE, Detection.detect); + Event.onTouch(Hammer.DOCUMENT, EVENT_END, Detection.detect); - if (token == '=') { - // id statement - getToken(); - if (tokenType != TOKENTYPE.IDENTIFIER) { - throw newSyntaxError('Identifier expected'); - } - graph[id] = token; - getToken(); - // TODO: implement comma separated list with "a_list: ID=ID [','] [a_list] " - } - else { - parseNodeStatement(graph, id); - } + // Hammer is ready...! + Hammer.READY = true; } /** - * Parse a subgraph - * @param {Object} graph parent graph object - * @return {Object | null} subgraph + * @module hammer + * + * @class Utils + * @static */ - function parseSubgraph (graph) { - var subgraph = null; - - // optional subgraph keyword - if (token == 'subgraph') { - subgraph = {}; - subgraph.type = 'subgraph'; - getToken(); - - // optional graph id - if (tokenType == TOKENTYPE.IDENTIFIER) { - subgraph.id = token; - getToken(); - } - } - - // open angle bracket - if (token == '{') { - getToken(); + var Utils = Hammer.utils = { + /** + * extend method, could also be used for cloning when `dest` is an empty object. + * changes the dest object + * @method extend + * @param {Object} dest + * @param {Object} src + * @param {Boolean} [merge=false] do a merge + * @return {Object} dest + */ + extend: function extend(dest, src, merge) { + for(var key in src) { + if(!src.hasOwnProperty(key) || (dest[key] !== undefined && merge)) { + continue; + } + dest[key] = src[key]; + } + return dest; + }, - if (!subgraph) { - subgraph = {}; - } - subgraph.parent = graph; - subgraph.node = graph.node; - subgraph.edge = graph.edge; - subgraph.graph = graph.graph; + /** + * simple addEventListener wrapper + * @method on + * @param {HTMLElement} element + * @param {String} type + * @param {Function} handler + */ + on: function on(element, type, handler) { + element.addEventListener(type, handler, false); + }, - // statements - parseStatements(subgraph); + /** + * simple removeEventListener wrapper + * @method off + * @param {HTMLElement} element + * @param {String} type + * @param {Function} handler + */ + off: function off(element, type, handler) { + element.removeEventListener(type, handler, false); + }, - // close angle bracket - if (token != '}') { - throw newSyntaxError('Angle bracket } expected'); - } - getToken(); + /** + * forEach over arrays and objects + * @method each + * @param {Object|Array} obj + * @param {Function} iterator + * @param {any} iterator.item + * @param {Number} iterator.index + * @param {Object|Array} iterator.obj the source object + * @param {Object} context value to use as `this` in the iterator + */ + each: function each(obj, iterator, context) { + var i, len; - // remove temporary default properties - delete subgraph.node; - delete subgraph.edge; - delete subgraph.graph; - delete subgraph.parent; + // native forEach on arrays + if('forEach' in obj) { + obj.forEach(iterator, context); + // arrays + } else if(obj.length !== undefined) { + for(i = 0, len = obj.length; i < len; i++) { + if(iterator.call(context, obj[i], i, obj) === false) { + return; + } + } + // objects + } else { + for(i in obj) { + if(obj.hasOwnProperty(i) && + iterator.call(context, obj[i], i, obj) === false) { + return; + } + } + } + }, - // register at the parent graph - if (!graph.subgraphs) { - graph.subgraphs = []; - } - graph.subgraphs.push(subgraph); - } + /** + * find if a string contains the string using indexOf + * @method inStr + * @param {String} src + * @param {String} find + * @return {Boolean} found + */ + inStr: function inStr(src, find) { + return src.indexOf(find) > -1; + }, - return subgraph; - } + /** + * find if a array contains the object using indexOf or a simple polyfill + * @method inArray + * @param {String} src + * @param {String} find + * @return {Boolean|Number} false when not found, or the index + */ + inArray: function inArray(src, find) { + if(src.indexOf) { + var index = src.indexOf(find); + return (index === -1) ? false : index; + } else { + for(var i = 0, len = src.length; i < len; i++) { + if(src[i] === find) { + return i; + } + } + return false; + } + }, - /** - * parse an attribute statement like "node [shape=circle fontSize=16]". - * Available keywords are 'node', 'edge', 'graph'. - * The previous list with default attributes will be replaced - * @param {Object} graph - * @returns {String | null} keyword Returns the name of the parsed attribute - * (node, edge, graph), or null if nothing - * is parsed. - */ - function parseAttributeStatement (graph) { - // attribute statements - if (token == 'node') { - getToken(); + /** + * convert an array-like object (`arguments`, `touchlist`) to an array + * @method toArray + * @param {Object} obj + * @return {Array} + */ + toArray: function toArray(obj) { + return Array.prototype.slice.call(obj, 0); + }, - // node attributes - graph.node = parseAttributeList(); - return 'node'; - } - else if (token == 'edge') { - getToken(); + /** + * find if a node is in the given parent + * @method hasParent + * @param {HTMLElement} node + * @param {HTMLElement} parent + * @return {Boolean} found + */ + hasParent: function hasParent(node, parent) { + while(node) { + if(node == parent) { + return true; + } + node = node.parentNode; + } + return false; + }, - // edge attributes - graph.edge = parseAttributeList(); - return 'edge'; - } - else if (token == 'graph') { - getToken(); + /** + * get the center of all the touches + * @method getCenter + * @param {Array} touches + * @return {Object} center contains `pageX`, `pageY`, `clientX` and `clientY` properties + */ + getCenter: function getCenter(touches) { + var pageX = [], + pageY = [], + clientX = [], + clientY = [], + min = Math.min, + max = Math.max; - // graph attributes - graph.graph = parseAttributeList(); - return 'graph'; - } + // no need to loop when only one touch + if(touches.length === 1) { + return { + pageX: touches[0].pageX, + pageY: touches[0].pageY, + clientX: touches[0].clientX, + clientY: touches[0].clientY + }; + } - return null; - } + Utils.each(touches, function(touch) { + pageX.push(touch.pageX); + pageY.push(touch.pageY); + clientX.push(touch.clientX); + clientY.push(touch.clientY); + }); - /** - * parse a node statement - * @param {Object} graph - * @param {String | Number} id - */ - function parseNodeStatement(graph, id) { - // node statement - var node = { - id: id - }; - var attr = parseAttributeList(); - if (attr) { - node.attr = attr; - } - addNode(graph, node); + return { + pageX: (min.apply(Math, pageX) + max.apply(Math, pageX)) / 2, + pageY: (min.apply(Math, pageY) + max.apply(Math, pageY)) / 2, + clientX: (min.apply(Math, clientX) + max.apply(Math, clientX)) / 2, + clientY: (min.apply(Math, clientY) + max.apply(Math, clientY)) / 2 + }; + }, - // edge statements - parseEdge(graph, id); - } + /** + * calculate the velocity between two points. unit is in px per ms. + * @method getVelocity + * @param {Number} deltaTime + * @param {Number} deltaX + * @param {Number} deltaY + * @return {Object} velocity `x` and `y` + */ + getVelocity: function getVelocity(deltaTime, deltaX, deltaY) { + return { + x: Math.abs(deltaX / deltaTime) || 0, + y: Math.abs(deltaY / deltaTime) || 0 + }; + }, - /** - * Parse an edge or a series of edges - * @param {Object} graph - * @param {String | Number} from Id of the from node - */ - function parseEdge(graph, from) { - while (token == '->' || token == '--') { - var to; - var type = token; - getToken(); + /** + * calculate the angle between two coordinates + * @method getAngle + * @param {Touch} touch1 + * @param {Touch} touch2 + * @return {Number} angle + */ + getAngle: function getAngle(touch1, touch2) { + var x = touch2.clientX - touch1.clientX, + y = touch2.clientY - touch1.clientY; - var subgraph = parseSubgraph(graph); - if (subgraph) { - to = subgraph; - } - else { - if (tokenType != TOKENTYPE.IDENTIFIER) { - throw newSyntaxError('Identifier or subgraph expected'); - } - to = token; - addNode(graph, { - id: to - }); - getToken(); - } + return Math.atan2(y, x) * 180 / Math.PI; + }, - // parse edge attributes - var attr = parseAttributeList(); + /** + * do a small comparision to get the direction between two touches. + * @method getDirection + * @param {Touch} touch1 + * @param {Touch} touch2 + * @return {String} direction matches `DIRECTION_LEFT|RIGHT|UP|DOWN` + */ + getDirection: function getDirection(touch1, touch2) { + var x = Math.abs(touch1.clientX - touch2.clientX), + y = Math.abs(touch1.clientY - touch2.clientY); - // create edge - var edge = createEdge(graph, from, to, type, attr); - addEdge(graph, edge); + if(x >= y) { + return touch1.clientX - touch2.clientX > 0 ? DIRECTION_LEFT : DIRECTION_RIGHT; + } + return touch1.clientY - touch2.clientY > 0 ? DIRECTION_UP : DIRECTION_DOWN; + }, - from = to; - } - } + /** + * calculate the distance between two touches + * @method getDistance + * @param {Touch}touch1 + * @param {Touch} touch2 + * @return {Number} distance + */ + getDistance: function getDistance(touch1, touch2) { + var x = touch2.clientX - touch1.clientX, + y = touch2.clientY - touch1.clientY; - /** - * Parse a set with attributes, - * for example [label="1.000", shape=solid] - * @return {Object | null} attr - */ - function parseAttributeList() { - var attr = null; + return Math.sqrt((x * x) + (y * y)); + }, - while (token == '[') { - getToken(); - attr = {}; - while (token !== '' && token != ']') { - if (tokenType != TOKENTYPE.IDENTIFIER) { - throw newSyntaxError('Attribute name expected'); - } - var name = token; + /** + * calculate the scale factor between two touchLists + * no scale is 1, and goes down to 0 when pinched together, and bigger when pinched out + * @method getScale + * @param {Array} start array of touches + * @param {Array} end array of touches + * @return {Number} scale + */ + getScale: function getScale(start, end) { + // need two fingers... + if(start.length >= 2 && end.length >= 2) { + return this.getDistance(end[0], end[1]) / this.getDistance(start[0], start[1]); + } + return 1; + }, - getToken(); - if (token != '=') { - throw newSyntaxError('Equal sign = expected'); - } - getToken(); + /** + * calculate the rotation degrees between two touchLists + * @method getRotation + * @param {Array} start array of touches + * @param {Array} end array of touches + * @return {Number} rotation + */ + getRotation: function getRotation(start, end) { + // need two fingers + if(start.length >= 2 && end.length >= 2) { + return this.getAngle(end[1], end[0]) - this.getAngle(start[1], start[0]); + } + return 0; + }, - if (tokenType != TOKENTYPE.IDENTIFIER) { - throw newSyntaxError('Attribute value expected'); - } - var value = token; - setValue(attr, name, value); // name can be a path + /** + * find out if the direction is vertical * + * @method isVertical + * @param {String} direction matches `DIRECTION_UP|DOWN` + * @return {Boolean} is_vertical + */ + isVertical: function isVertical(direction) { + return direction == DIRECTION_UP || direction == DIRECTION_DOWN; + }, - getToken(); - if (token ==',') { - getToken(); - } - } + /** + * set css properties with their prefixes + * @param {HTMLElement} element + * @param {String} prop + * @param {String} value + * @param {Boolean} [toggle=true] + * @return {Boolean} + */ + setPrefixedCss: function setPrefixedCss(element, prop, value, toggle) { + var prefixes = ['', 'Webkit', 'Moz', 'O', 'ms']; + prop = Utils.toCamelCase(prop); - if (token != ']') { - throw newSyntaxError('Bracket ] expected'); - } - getToken(); - } + for(var i = 0; i < prefixes.length; i++) { + var p = prop; + // prefixes + if(prefixes[i]) { + p = prefixes[i] + p.slice(0, 1).toUpperCase() + p.slice(1); + } - return attr; - } + // test the style + if(p in element.style) { + element.style[p] = (toggle == null || toggle) && value || ''; + break; + } + } + }, - /** - * Create a syntax error with extra information on current token and index. - * @param {String} message - * @returns {SyntaxError} err - */ - function newSyntaxError(message) { - return new SyntaxError(message + ', got "' + chop(token, 30) + '" (char ' + index + ')'); - } + /** + * toggle browser default behavior by setting css properties. + * `userSelect='none'` also sets `element.onselectstart` to false + * `userDrag='none'` also sets `element.ondragstart` to false + * + * @method toggleBehavior + * @param {HtmlElement} element + * @param {Object} props + * @param {Boolean} [toggle=true] + */ + toggleBehavior: function toggleBehavior(element, props, toggle) { + if(!props || !element || !element.style) { + return; + } - /** - * Chop off text after a maximum length - * @param {String} text - * @param {Number} maxLength - * @returns {String} - */ - function chop (text, maxLength) { - return (text.length <= maxLength) ? text : (text.substr(0, 27) + '...'); - } + // set the css properties + Utils.each(props, function(value, prop) { + Utils.setPrefixedCss(element, prop, value, toggle); + }); - /** - * Execute a function fn for each pair of elements in two arrays - * @param {Array | *} array1 - * @param {Array | *} array2 - * @param {function} fn - */ - function forEach2(array1, array2, fn) { - if (Array.isArray(array1)) { - array1.forEach(function (elem1) { - if (Array.isArray(array2)) { - array2.forEach(function (elem2) { - fn(elem1, elem2); + var falseFn = toggle && function() { + return false; + }; + + // also the disable onselectstart + if(props.userSelect == 'none') { + element.onselectstart = falseFn; + } + // and disable ondragstart + if(props.userDrag == 'none') { + element.ondragstart = falseFn; + } + }, + + /** + * convert a string with underscores to camelCase + * so prevent_default becomes preventDefault + * @param {String} str + * @return {String} camelCaseStr + */ + toCamelCase: function toCamelCase(str) { + return str.replace(/[_-]([a-z])/g, function(s) { + return s[1].toUpperCase(); }); - } - else { - fn(elem1, array2); - } - }); - } - else { - if (Array.isArray(array2)) { - array2.forEach(function (elem2) { - fn(array1, elem2); - }); - } - else { - fn(array1, array2); } - } - } + }; + /** - * Convert a string containing a graph in DOT language into a map containing - * with nodes and edges in the format of graph. - * @param {String} data Text containing a graph in DOT-notation - * @return {Object} graphData + * @module hammer */ - function DOTToGraph (data) { - // parse the DOT file - var dotData = parseDOT(data); - var graphData = { - nodes: [], - edges: [], - options: {} - }; - - // copy the nodes - if (dotData.nodes) { - dotData.nodes.forEach(function (dotNode) { - var graphNode = { - id: dotNode.id, - label: String(dotNode.label || dotNode.id) - }; - merge(graphNode, dotNode.attr); - if (graphNode.image) { - graphNode.shape = 'image'; - } - graphData.nodes.push(graphNode); - }); - } + /** + * @class Event + * @static + */ + var Event = Hammer.event = { + /** + * when touch events have been fired, this is true + * this is used to stop mouse events + * @property prevent_mouseevents + * @private + * @type {Boolean} + */ + preventMouseEvents: false, - // copy the edges - if (dotData.edges) { /** - * Convert an edge in DOT format to an edge with VisGraph format - * @param {Object} dotEdge - * @returns {Object} graphEdge + * if EVENT_START has been fired + * @property started + * @private + * @type {Boolean} */ - var convertEdge = function (dotEdge) { - var graphEdge = { - from: dotEdge.from, - to: dotEdge.to - }; - merge(graphEdge, dotEdge.attr); - graphEdge.style = (dotEdge.type == '->') ? 'arrow' : 'line'; - return graphEdge; - } + started: false, - dotData.edges.forEach(function (dotEdge) { - var from, to; - if (dotEdge.from instanceof Object) { - from = dotEdge.from.nodes; - } - else { - from = { - id: dotEdge.from - } - } + /** + * when the mouse is hold down, this is true + * @property should_detect + * @private + * @type {Boolean} + */ + shouldDetect: false, - if (dotEdge.to instanceof Object) { - to = dotEdge.to.nodes; - } - else { - to = { - id: dotEdge.to - } - } + /** + * simple event binder with a hook and support for multiple types + * @method on + * @param {HTMLElement} element + * @param {String} type + * @param {Function} handler + * @param {Function} [hook] + * @param {Object} hook.type + */ + on: function on(element, type, handler, hook) { + var types = type.split(' '); + Utils.each(types, function(type) { + Utils.on(element, type, handler); + hook && hook(type); + }); + }, - if (dotEdge.from instanceof Object && dotEdge.from.edges) { - dotEdge.from.edges.forEach(function (subEdge) { - var graphEdge = convertEdge(subEdge); - graphData.edges.push(graphEdge); + /** + * simple event unbinder with a hook and support for multiple types + * @method off + * @param {HTMLElement} element + * @param {String} type + * @param {Function} handler + * @param {Function} [hook] + * @param {Object} hook.type + */ + off: function off(element, type, handler, hook) { + var types = type.split(' '); + Utils.each(types, function(type) { + Utils.off(element, type, handler); + hook && hook(type); }); - } + }, - forEach2(from, to, function (from, to) { - var subEdge = createEdge(graphData, from.id, to.id, dotEdge.type, dotEdge.attr); - var graphEdge = convertEdge(subEdge); - graphData.edges.push(graphEdge); - }); + /** + * the core touch event handler. + * this finds out if we should to detect gestures + * @method onTouch + * @param {HTMLElement} element + * @param {String} eventType matches `EVENT_START|MOVE|END` + * @param {Function} handler + * @return onTouchHandler {Function} the core event handler + */ + onTouch: function onTouch(element, eventType, handler) { + var self = this; - if (dotEdge.to instanceof Object && dotEdge.to.edges) { - dotEdge.to.edges.forEach(function (subEdge) { - var graphEdge = convertEdge(subEdge); - graphData.edges.push(graphEdge); - }); - } - }); - } + var onTouchHandler = function onTouchHandler(ev) { + var srcType = ev.type.toLowerCase(), + isPointer = Hammer.HAS_POINTEREVENTS, + isMouse = Utils.inStr(srcType, 'mouse'), + triggerType; - // copy the options - if (dotData.attr) { - graphData.options = dotData.attr; - } + // if we are in a mouseevent, but there has been a touchevent triggered in this session + // we want to do nothing. simply break out of the event. + if(isMouse && self.preventMouseEvents) { + return; - return graphData; - } + // mousebutton must be down + } else if(isMouse && eventType == EVENT_START && ev.button === 0) { + self.preventMouseEvents = false; + self.shouldDetect = true; + } else if(isPointer && eventType == EVENT_START) { + self.shouldDetect = (ev.buttons === 1 || PointerEvent.matchType(POINTER_TOUCH, ev)); + // just a valid start event, but no mouse + } else if(!isMouse && eventType == EVENT_START) { + self.preventMouseEvents = true; + self.shouldDetect = true; + } - // exports - exports.parseDOT = parseDOT; - exports.DOTToGraph = DOTToGraph; + // update the pointer event before entering the detection + if(isPointer && eventType != EVENT_END) { + PointerEvent.updatePointer(eventType, ev); + } + // we are in a touch/down state, so allowed detection of gestures + if(self.shouldDetect) { + triggerType = self.doDetect.call(self, ev, eventType, element, handler); + } -/***/ }, -/* 53 */ -/***/ function(module, exports, __webpack_require__) { + // ...and we are done with the detection + // so reset everything to start each detection totally fresh + if(triggerType == EVENT_END) { + self.preventMouseEvents = false; + self.shouldDetect = false; + PointerEvent.reset(); + // update the pointerevent object after the detection + } - - function parseGephi(gephiJSON, options) { - var edges = []; - var nodes = []; - this.options = { - edges: { - inheritColor: true + if(isPointer && eventType == EVENT_END) { + PointerEvent.updatePointer(eventType, ev); + } + }; + + this.on(element, EVENT_TYPES[eventType], onTouchHandler); + return onTouchHandler; }, - nodes: { - allowedToMove: false, - parseColor: false - } - }; - if (options !== undefined) { - this.options.nodes['allowedToMove'] = options.allowedToMove | false; - this.options.nodes['parseColor'] = options.parseColor | false; - this.options.edges['inheritColor'] = options.inheritColor | true; - } + /** + * the core detection method + * this finds out what hammer-touch-events to trigger + * @method doDetect + * @param {Object} ev + * @param {String} eventType matches `EVENT_START|MOVE|END` + * @param {HTMLElement} element + * @param {Function} handler + * @return {String} triggerType matches `EVENT_START|MOVE|END` + */ + doDetect: function doDetect(ev, eventType, element, handler) { + var touchList = this.getTouchList(ev, eventType); + var touchListLength = touchList.length; + var triggerType = eventType; + var triggerChange = touchList.trigger; // used by fakeMultitouch plugin + var changedLength = touchListLength; - var gEdges = gephiJSON.edges; - var gNodes = gephiJSON.nodes; - for (var i = 0; i < gEdges.length; i++) { - var edge = {}; - var gEdge = gEdges[i]; - edge['id'] = gEdge.id; - edge['from'] = gEdge.source; - edge['to'] = gEdge.target; - edge['attributes'] = gEdge.attributes; - // edge['value'] = gEdge.attributes !== undefined ? gEdge.attributes.Weight : undefined; - // edge['width'] = edge['value'] !== undefined ? undefined : edgegEdge.size; - edge['color'] = gEdge.color; - edge['inheritColor'] = edge['color'] !== undefined ? false : this.options.inheritColor; - edges.push(edge); - } + // at each touchstart-like event we want also want to trigger a TOUCH event... + if(eventType == EVENT_START) { + triggerChange = EVENT_TOUCH; + // ...the same for a touchend-like event + } else if(eventType == EVENT_END) { + triggerChange = EVENT_RELEASE; - for (var i = 0; i < gNodes.length; i++) { - var node = {}; - var gNode = gNodes[i]; - node['id'] = gNode.id; - node['attributes'] = gNode.attributes; - node['x'] = gNode.x; - node['y'] = gNode.y; - node['label'] = gNode.label; - if (this.options.nodes.parseColor == true) { - node['color'] = gNode.color; - } - else { - node['color'] = gNode.color !== undefined ? {background:gNode.color, border:gNode.color} : undefined; - } - node['radius'] = gNode.size; - node['allowedToMoveX'] = this.options.nodes.allowedToMove; - node['allowedToMoveY'] = this.options.nodes.allowedToMove; - nodes.push(node); - } + // keep track of how many touches have been removed + changedLength = touchList.length - ((ev.changedTouches) ? ev.changedTouches.length : 1); + } - return {nodes:nodes, edges:edges}; - } + // after there are still touches on the screen, + // we just want to trigger a MOVE event. so change the START or END to a MOVE + // but only after detection has been started, the first time we actualy want a START + if(changedLength > 0 && this.started) { + triggerType = EVENT_MOVE; + } - exports.parseGephi = parseGephi; + // detection has been started, we keep track of this, see above + this.started = true; -/***/ }, -/* 54 */ -/***/ function(module, exports, __webpack_require__) { + // generate some event data, some basic information + var evData = this.collectEventData(element, triggerType, touchList, ev); - var util = __webpack_require__(1); + // trigger the triggerType event before the change (TOUCH, RELEASE) events + // but the END event should be at last + if(eventType != EVENT_END) { + handler.call(Detection, evData); + } - /** - * @class Groups - * This class can store groups and properties specific for groups. - */ - function Groups() { - this.clear(); - this.defaultIndex = 0; - } + // trigger a change (TOUCH, RELEASE) event, this means the length of the touches changed + if(triggerChange) { + evData.changedLength = changedLength; + evData.eventType = triggerChange; + handler.call(Detection, evData); - /** - * default constants for group colors - */ - Groups.DEFAULT = [ - {border: "#2B7CE9", background: "#97C2FC", highlight: {border: "#2B7CE9", background: "#D2E5FF"}, hover: {border: "#2B7CE9", background: "#D2E5FF"}}, // blue - {border: "#FFA500", background: "#FFFF00", highlight: {border: "#FFA500", background: "#FFFFA3"}, hover: {border: "#FFA500", background: "#FFFFA3"}}, // yellow - {border: "#FA0A10", background: "#FB7E81", highlight: {border: "#FA0A10", background: "#FFAFB1"}, hover: {border: "#FA0A10", background: "#FFAFB1"}}, // red - {border: "#41A906", background: "#7BE141", highlight: {border: "#41A906", background: "#A1EC76"}, hover: {border: "#41A906", background: "#A1EC76"}}, // green - {border: "#E129F0", background: "#EB7DF4", highlight: {border: "#E129F0", background: "#F0B3F5"}, hover: {border: "#E129F0", background: "#F0B3F5"}}, // magenta - {border: "#7C29F0", background: "#AD85E4", highlight: {border: "#7C29F0", background: "#D3BDF0"}, hover: {border: "#7C29F0", background: "#D3BDF0"}}, // purple - {border: "#C37F00", background: "#FFA807", highlight: {border: "#C37F00", background: "#FFCA66"}, hover: {border: "#C37F00", background: "#FFCA66"}}, // orange - {border: "#4220FB", background: "#6E6EFD", highlight: {border: "#4220FB", background: "#9B9BFD"}, hover: {border: "#4220FB", background: "#9B9BFD"}}, // darkblue - {border: "#FD5A77", background: "#FFC0CB", highlight: {border: "#FD5A77", background: "#FFD1D9"}, hover: {border: "#FD5A77", background: "#FFD1D9"}}, // pink - {border: "#4AD63A", background: "#C2FABC", highlight: {border: "#4AD63A", background: "#E6FFE3"}, hover: {border: "#4AD63A", background: "#E6FFE3"}} // mint - ]; + evData.eventType = triggerType; + delete evData.changedLength; + } + // trigger the END event + if(triggerType == EVENT_END) { + handler.call(Detection, evData); - /** - * Clear all groups - */ - Groups.prototype.clear = function () { - this.groups = {}; - this.groups.length = function() - { - var i = 0; - for ( var p in this ) { - if (this.hasOwnProperty(p)) { - i++; - } - } - return i; - } - }; + // ...and we are done with the detection + // so reset everything to start each detection totally fresh + this.started = false; + } + return triggerType; + }, - /** - * get group properties of a groupname. If groupname is not found, a new group - * is added. - * @param {*} groupname Can be a number, string, Date, etc. - * @return {Object} group The created group, containing all group properties - */ - Groups.prototype.get = function (groupname) { - var group = this.groups[groupname]; - if (group == undefined) { - // create new group - var index = this.defaultIndex % Groups.DEFAULT.length; - this.defaultIndex++; - group = {}; - group.color = Groups.DEFAULT[index]; - this.groups[groupname] = group; - } + /** + * we have different events for each device/browser + * determine what we need and set them in the EVENT_TYPES constant + * the `onTouch` method is bind to these properties. + * @method determineEventTypes + * @return {Object} events + */ + determineEventTypes: function determineEventTypes() { + var types; + if(Hammer.HAS_POINTEREVENTS) { + if(window.PointerEvent) { + types = [ + 'pointerdown', + 'pointermove', + 'pointerup pointercancel lostpointercapture' + ]; + } else { + types = [ + 'MSPointerDown', + 'MSPointerMove', + 'MSPointerUp MSPointerCancel MSLostPointerCapture' + ]; + } + } else if(Hammer.NO_MOUSEEVENTS) { + types = [ + 'touchstart', + 'touchmove', + 'touchend touchcancel' + ]; + } else { + types = [ + 'touchstart mousedown', + 'touchmove mousemove', + 'touchend touchcancel mouseup' + ]; + } - return group; - }; + EVENT_TYPES[EVENT_START] = types[0]; + EVENT_TYPES[EVENT_MOVE] = types[1]; + EVENT_TYPES[EVENT_END] = types[2]; + return EVENT_TYPES; + }, - /** - * Add a custom group style - * @param {String} groupname - * @param {Object} style An object containing borderColor, - * backgroundColor, etc. - * @return {Object} group The created group object - */ - Groups.prototype.add = function (groupname, style) { - this.groups[groupname] = style; - return style; - }; + /** + * create touchList depending on the event + * @method getTouchList + * @param {Object} ev + * @param {String} eventType + * @return {Array} touches + */ + getTouchList: function getTouchList(ev, eventType) { + // get the fake pointerEvent touchlist + if(Hammer.HAS_POINTEREVENTS) { + return PointerEvent.getTouchList(); + } - module.exports = Groups; + // get the touchlist + if(ev.touches) { + if(eventType == EVENT_MOVE) { + return ev.touches; + } + var identifiers = []; + var concat = [].concat(Utils.toArray(ev.touches), Utils.toArray(ev.changedTouches)); + var touchList = []; -/***/ }, -/* 55 */ -/***/ function(module, exports, __webpack_require__) { + Utils.each(concat, function(touch) { + if(Utils.inArray(identifiers, touch.identifier) === false) { + touchList.push(touch); + } + identifiers.push(touch.identifier); + }); - /** - * @class Images - * This class loads images and keeps them stored. - */ - function Images() { - this.images = {}; - this.imageBroken = {}; - this.callback = undefined; - } + return touchList; + } - /** - * Set an onload callback function. This will be called each time an image - * is loaded - * @param {function} callback - */ - Images.prototype.setOnloadCallback = function(callback) { - this.callback = callback; - }; + // make fake touchList from mouse position + ev.identifier = 1; + return [ev]; + }, - /** - * - * @param {string} url Url of the image - * @param {string} url Url of an image to use if the url image is not found - * @return {Image} img The image object - */ - Images.prototype.load = function(url, brokenUrl) { - var img = this.images[url]; // make a pointer - if (img === undefined) { - // create the image - var me = this; - img = new Image(); - img.onload = function () { - // IE11 fix -- thanks dponch! - if (this.width == 0) { - document.body.appendChild(this); - this.width = this.offsetWidth; - this.height = this.offsetHeight; - document.body.removeChild(this); - } + /** + * collect basic event data + * @method collectEventData + * @param {HTMLElement} element + * @param {String} eventType matches `EVENT_START|MOVE|END` + * @param {Array} touches + * @param {Object} ev + * @return {Object} ev + */ + collectEventData: function collectEventData(element, eventType, touches, ev) { + // find out pointerType + var pointerType = POINTER_TOUCH; + if(Utils.inStr(ev.type, 'mouse') || PointerEvent.matchType(POINTER_MOUSE, ev)) { + pointerType = POINTER_MOUSE; + } else if(PointerEvent.matchType(POINTER_PEN, ev)) { + pointerType = POINTER_PEN; + } - if (me.callback) { - me.images[url] = img; - me.callback(this); - } - }; + return { + center: Utils.getCenter(touches), + timeStamp: Date.now(), + target: ev.target, + touches: touches, + eventType: eventType, + pointerType: pointerType, + srcEvent: ev, - img.onerror = function () { - if (brokenUrl === undefined) { - console.error("Could not load image:", url); - delete this.src; - if (me.callback) { - me.callback(this); - } - } - else { - if (me.imageBroken[url] === true) { - if (this.src == brokenUrl) { - console.error("Could not load brokenImage:", brokenUrl); - delete this.src; - if (me.callback) { - me.callback(this); - } - } - else { - console.error("Could not load image:", url); - this.src = brokenUrl; - } - } - else { - console.error("Could not load image:", url); - this.src = brokenUrl; - me.imageBroken[url] = true; - } - } - }; + /** + * prevent the browser default actions + * mostly used to disable scrolling of the browser + */ + preventDefault: function() { + var srcEvent = this.srcEvent; + srcEvent.preventManipulation && srcEvent.preventManipulation(); + srcEvent.preventDefault && srcEvent.preventDefault(); + }, - img.src = url; - } + /** + * stop bubbling the event up to its parents + */ + stopPropagation: function() { + this.srcEvent.stopPropagation(); + }, - return img; + /** + * immediately stop gesture detection + * might be useful after a swipe was detected + * @return {*} + */ + stopDetect: function() { + return Detection.stopDetect(); + } + }; + } }; - module.exports = Images; - - -/***/ }, -/* 56 */ -/***/ function(module, exports, __webpack_require__) { - - var util = __webpack_require__(1); /** - * @class Node - * A node. A node can be connected to other nodes via one or multiple edges. - * @param {object} properties An object containing properties for the node. All - * properties 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" - * {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 properties - * @param {Object} constants An object with default values for - * example for the color + * @module hammer * + * @class PointerEvent + * @static */ - function Node(properties, imagelist, grouplist, networkConstants) { - var constants = util.selectiveBridgeObject(['nodes'],networkConstants); - this.options = constants.nodes; + var PointerEvent = Hammer.PointerEvent = { + /** + * holds all pointers, by `identifier` + * @property pointers + * @type {Object} + */ + pointers: {}, - this.selected = false; - this.hover = false; + /** + * get the pointers as an array + * @method getTouchList + * @return {Array} touchlist + */ + getTouchList: function getTouchList() { + var touchlist = []; + // we can use forEach since pointerEvents only is in IE10 + Utils.each(this.pointers, function(pointer) { + touchlist.push(pointer); + }); + return touchlist; + }, - this.edges = []; // all edges connected to this node - this.dynamicEdges = []; - this.reroutedEdges = {}; + /** + * update the position of a pointer + * @method updatePointer + * @param {String} eventType matches `EVENT_START|MOVE|END` + * @param {Object} pointerEvent + */ + updatePointer: function updatePointer(eventType, pointerEvent) { + if(eventType == EVENT_END || (eventType != EVENT_END && pointerEvent.buttons !== 1)) { + delete this.pointers[pointerEvent.pointerId]; + } else { + pointerEvent.identifier = pointerEvent.pointerId; + this.pointers[pointerEvent.pointerId] = pointerEvent; + } + }, - this.fontDrawThreshold = 3; + /** + * check if ev matches pointertype + * @method matchType + * @param {String} pointerType matches `POINTER_MOUSE|TOUCH|PEN` + * @param {PointerEvent} ev + */ + matchType: function matchType(pointerType, ev) { + if(!ev.pointerType) { + return false; + } - // set defaults for the properties - this.id = undefined; - this.allowedToMoveX = false; - this.allowedToMoveY = false; - this.xFixed = false; - this.yFixed = false; - this.horizontalAlignLeft = true; // these are for the navigation controls - this.verticalAlignTop = true; // these are for the navigation controls - this.baseRadiusValue = networkConstants.nodes.radius; - this.radiusFixed = false; - this.level = -1; - this.preassignedLevel = false; - this.hierarchyEnumerated = false; - this.labelDimensions = {top:0, left:0, width:0, height:0, yLine:0}; // could be cached - this.boundingBox = {top:0, left:0, right:0, bottom:0}; + var pt = ev.pointerType, + types = {}; - this.imagelist = imagelist; - this.grouplist = grouplist; + types[POINTER_MOUSE] = (pt === (ev.MSPOINTER_TYPE_MOUSE || POINTER_MOUSE)); + types[POINTER_TOUCH] = (pt === (ev.MSPOINTER_TYPE_TOUCH || POINTER_TOUCH)); + types[POINTER_PEN] = (pt === (ev.MSPOINTER_TYPE_PEN || POINTER_PEN)); + return types[pointerType]; + }, - // physics properties - this.fx = 0.0; // external force x - this.fy = 0.0; // external force y - this.vx = 0.0; // velocity x - this.vy = 0.0; // velocity y - this.x = null; - this.y = null; + /** + * reset the stored pointers + * @method reset + */ + reset: function resetList() { + this.pointers = {}; + } + }; - // used for reverting to previous position on stabilization - this.previousState = {vx:0,vy:0,x:0,y:0}; - this.damping = networkConstants.physics.damping; // written every time gravity is calculated - this.fixedData = {x:null,y:null}; + /** + * @module hammer + * + * @class Detection + * @static + */ + var Detection = Hammer.detection = { + // contains all registred Hammer.gestures in the correct order + gestures: [], - this.setProperties(properties, constants); + // data of the current Hammer.gesture detection session + current: null, - // creating the variables for clustering - this.resetCluster(); - this.dynamicEdgesLength = 0; - this.clusterSession = 0; - this.clusterSizeWidthFactor = networkConstants.clustering.nodeScaling.width; - this.clusterSizeHeightFactor = networkConstants.clustering.nodeScaling.height; - this.clusterSizeRadiusFactor = networkConstants.clustering.nodeScaling.radius; - this.maxNodeSizeIncrements = networkConstants.clustering.maxNodeSizeIncrements; - this.growthIndicator = 0; + // the previous Hammer.gesture session data + // is a full clone of the previous gesture.current object + previous: null, - // variables to tell the node about the network. - this.networkScaleInv = 1; - this.networkScale = 1; - this.canvasTopLeft = {"x": -300, "y": -300}; - this.canvasBottomRight = {"x": 300, "y": 300}; - this.parentEdgeId = null; - } + // when this becomes true, no gestures are fired + stopped: false, + /** + * start Hammer.gesture detection + * @method startDetect + * @param {Hammer.Instance} inst + * @param {Object} eventData + */ + startDetect: function startDetect(inst, eventData) { + // already busy with a Hammer.gesture detection on an element + if(this.current) { + return; + } - /** - * Revert the position and velocity of the previous step. - */ - Node.prototype.revertPosition = function() { - this.x = this.previousState.x; - this.y = this.previousState.y; - this.vx = this.previousState.vx; - this.vy = this.previousState.vy; - } + this.stopped = false; + // holds current session + this.current = { + inst: inst, // reference to HammerInstance we're working for + startEvent: Utils.extend({}, eventData), // start eventData for distances, timing etc + lastEvent: false, // last eventData + lastCalcEvent: false, // last eventData for calculations. + futureCalcEvent: false, // last eventData for calculations. + lastCalcData: {}, // last lastCalcData + name: '' // current gesture we're in/detected, can be 'tap', 'hold' etc + }; - /** - * (re)setting the clustering variables and objects - */ - Node.prototype.resetCluster = function() { - // clustering variables - this.formationScale = undefined; // this is used to determine when to open the cluster - this.clusterSize = 1; // this signifies the total amount of nodes in this cluster - this.containedNodes = {}; - this.containedEdges = {}; - this.clusterSessions = []; - }; + this.detect(eventData); + }, - /** - * Attach a edge to the node - * @param {Edge} edge - */ - Node.prototype.attachEdge = function(edge) { - if (this.edges.indexOf(edge) == -1) { - this.edges.push(edge); - } - if (this.dynamicEdges.indexOf(edge) == -1) { - this.dynamicEdges.push(edge); - } - this.dynamicEdgesLength = this.dynamicEdges.length; - }; + /** + * Hammer.gesture detection + * @method detect + * @param {Object} eventData + * @return {any} + */ + detect: function detect(eventData) { + if(!this.current || this.stopped) { + return; + } - /** - * Detach a edge from the node - * @param {Edge} edge - */ - Node.prototype.detachEdge = function(edge) { - var index = this.edges.indexOf(edge); - if (index != -1) { - this.edges.splice(index, 1); - } - index = this.dynamicEdges.indexOf(edge); - if (index != -1) { - this.dynamicEdges.splice(index, 1); - } - this.dynamicEdgesLength = this.dynamicEdges.length; - }; + // extend event data with calculations about scale, distance etc + eventData = this.extendEventData(eventData); + // hammer instance and instance options + var inst = this.current.inst, + instOptions = inst.options; - /** - * Set or overwrite properties for the node - * @param {Object} properties an object with properties - * @param {Object} constants and object with default, global properties - */ - Node.prototype.setProperties = function(properties, constants) { - if (!properties) { - return; - } + // call Hammer.gesture handlers + Utils.each(this.gestures, function triggerGesture(gesture) { + // only when the instance options have enabled this gesture + if(!this.stopped && inst.enabled && instOptions[gesture.name]) { + gesture.handler.call(gesture, eventData, inst); + } + }, this); - var fields = ['borderWidth','borderWidthSelected','shape','image','brokenImage','radius','fontColor', - 'fontSize','fontFace','fontFill','fontStrokeWidth','fontStrokeColor','group','mass' - ]; - util.selectiveDeepExtend(fields, this.options, properties); + // store as previous event event + if(this.current) { + this.current.lastEvent = eventData; + } - // basic properties - if (properties.id !== undefined) {this.id = properties.id;} - if (properties.label !== undefined) {this.label = properties.label; this.originalLabel = properties.label;} - if (properties.title !== undefined) {this.title = properties.title;} - if (properties.x !== undefined) {this.x = properties.x;} - if (properties.y !== undefined) {this.y = properties.y;} - if (properties.value !== undefined) {this.value = properties.value;} - if (properties.level !== undefined) {this.level = properties.level; this.preassignedLevel = true;} + if(eventData.eventType == EVENT_END) { + this.stopDetect(); + } - // navigation controls properties - if (properties.horizontalAlignLeft !== undefined) {this.horizontalAlignLeft = properties.horizontalAlignLeft;} - if (properties.verticalAlignTop !== undefined) {this.verticalAlignTop = properties.verticalAlignTop;} - if (properties.triggerFunction !== undefined) {this.triggerFunction = properties.triggerFunction;} + return eventData; + }, - if (this.id === undefined) { - throw "Node must have an id"; - } + /** + * clear the Hammer.gesture vars + * this is called on endDetect, but can also be used when a final Hammer.gesture has been detected + * to stop other Hammer.gestures from being fired + * @method stopDetect + */ + stopDetect: function stopDetect() { + // clone current data to the store as the previous gesture + // used for the double tap gesture, since this is an other gesture detect session + this.previous = Utils.extend({}, this.current); - // copy group properties - if (typeof properties.group === 'number' || (typeof properties.group === 'string' && properties.group != '')) { - var groupObj = this.grouplist.get(properties.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); - } - // individual shape properties - if (properties.radius !== undefined) {this.baseRadiusValue = this.options.radius;} - if (properties.color !== undefined) {this.options.color = util.parseColor(properties.color);} + // reset the current + this.current = null; + this.stopped = true; + }, - 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"; - } - } + /** + * calculate velocity, angle and direction + * @method getVelocityData + * @param {Object} ev + * @param {Object} center + * @param {Number} deltaTime + * @param {Number} deltaX + * @param {Number} deltaY + */ + getCalculatedData: function getCalculatedData(ev, center, deltaTime, deltaX, deltaY) { + var cur = this.current, + recalc = false, + calcEv = cur.lastCalcEvent, + calcData = cur.lastCalcData; - if (properties.allowedToMoveX !== undefined) { - this.xFixed = !properties.allowedToMoveX; - this.allowedToMoveX = properties.allowedToMoveX; - } - else if (properties.x !== undefined && this.allowedToMoveX == false) { - this.xFixed = true; - } + if(calcEv && ev.timeStamp - calcEv.timeStamp > Hammer.CALCULATE_INTERVAL) { + center = calcEv.center; + deltaTime = ev.timeStamp - calcEv.timeStamp; + deltaX = ev.center.clientX - calcEv.center.clientX; + deltaY = ev.center.clientY - calcEv.center.clientY; + recalc = true; + } + if(ev.eventType == EVENT_TOUCH || ev.eventType == EVENT_RELEASE) { + cur.futureCalcEvent = ev; + } - if (properties.allowedToMoveY !== undefined) { - this.yFixed = !properties.allowedToMoveY; - this.allowedToMoveY = properties.allowedToMoveY; - } - else if (properties.y !== undefined && this.allowedToMoveY == false) { - this.yFixed = true; - } + if(!cur.lastCalcEvent || recalc) { + calcData.velocity = Utils.getVelocity(deltaTime, deltaX, deltaY); + calcData.angle = Utils.getAngle(center, ev.center); + calcData.direction = Utils.getDirection(center, ev.center); - this.radiusFixed = this.radiusFixed || (properties.radius !== undefined); + cur.lastCalcEvent = cur.futureCalcEvent || ev; + cur.futureCalcEvent = ev; + } - if (this.options.shape === 'image' || this.options.shape === 'circularImage') { - this.options.radiusMin = constants.nodes.widthMin; - this.options.radiusMax = constants.nodes.widthMax; - } + ev.velocityX = calcData.velocity.x; + ev.velocityY = calcData.velocity.y; + ev.interimAngle = calcData.angle; + ev.interimDirection = calcData.direction; + }, - // choose draw method depending on the shape - switch (this.options.shape) { - case 'database': this.draw = this._drawDatabase; this.resize = this._resizeDatabase; break; - case 'box': this.draw = this._drawBox; this.resize = this._resizeBox; break; - case 'circle': this.draw = this._drawCircle; this.resize = this._resizeCircle; break; - case 'ellipse': this.draw = this._drawEllipse; this.resize = this._resizeEllipse; break; - // TODO: add diamond shape - case 'image': this.draw = this._drawImage; this.resize = this._resizeImage; break; - case 'circularImage': this.draw = this._drawCircularImage; this.resize = this._resizeCircularImage; break; - case 'text': this.draw = this._drawText; this.resize = this._resizeText; break; - case 'dot': this.draw = this._drawDot; this.resize = this._resizeShape; break; - case 'square': this.draw = this._drawSquare; this.resize = this._resizeShape; break; - case 'triangle': this.draw = this._drawTriangle; this.resize = this._resizeShape; break; - case 'triangleDown': this.draw = this._drawTriangleDown; this.resize = this._resizeShape; break; - case 'star': this.draw = this._drawStar; this.resize = this._resizeShape; break; - default: this.draw = this._drawEllipse; this.resize = this._resizeEllipse; break; - } - // reset the size of the node, this can be changed - this._reset(); + /** + * extend eventData for Hammer.gestures + * @method extendEventData + * @param {Object} ev + * @return {Object} ev + */ + extendEventData: function extendEventData(ev) { + var cur = this.current, + startEv = cur.startEvent, + lastEv = cur.lastEvent || startEv; - }; + // update the start touchlist to calculate the scale/rotation + if(ev.eventType == EVENT_TOUCH || ev.eventType == EVENT_RELEASE) { + startEv.touches = []; + Utils.each(ev.touches, function(touch) { + startEv.touches.push({ + clientX: touch.clientX, + clientY: touch.clientY + }); + }); + } - /** - * select this node - */ - Node.prototype.select = function() { - this.selected = true; - this._reset(); - }; + var deltaTime = ev.timeStamp - startEv.timeStamp, + deltaX = ev.center.clientX - startEv.center.clientX, + deltaY = ev.center.clientY - startEv.center.clientY; - /** - * unselect this node - */ - Node.prototype.unselect = function() { - this.selected = false; - this._reset(); - }; + this.getCalculatedData(ev, lastEv.center, deltaTime, deltaX, deltaY); + Utils.extend(ev, { + startEvent: startEv, - /** - * Reset the calculated size of the node, forces it to recalculate its size - */ - Node.prototype.clearSizeCache = function() { - this._reset(); - }; + deltaTime: deltaTime, + deltaX: deltaX, + deltaY: deltaY, - /** - * Reset the calculated size of the node, forces it to recalculate its size - * @private - */ - Node.prototype._reset = function() { - this.width = undefined; - this.height = undefined; + distance: Utils.getDistance(startEv.center, ev.center), + angle: Utils.getAngle(startEv.center, ev.center), + direction: Utils.getDirection(startEv.center, ev.center), + scale: Utils.getScale(startEv.touches, ev.touches), + rotation: Utils.getRotation(startEv.touches, ev.touches) + }); + + return ev; + }, + + /** + * register new gesture + * @method register + * @param {Object} gesture object, see `gestures/` for documentation + * @return {Array} gestures + */ + register: function register(gesture) { + // add an enable gesture options if there is no given + var options = gesture.defaults || {}; + if(options[gesture.name] === undefined) { + options[gesture.name] = true; + } + + // extend Hammer default options with the Hammer.gesture options + Utils.extend(Hammer.defaults, options, true); + + // set its index + gesture.index = gesture.index || 1000; + + // add Hammer.gesture to the list + this.gestures.push(gesture); + + // sort the list by index + this.gestures.sort(function(a, b) { + if(a.index < b.index) { + return -1; + } + if(a.index > b.index) { + return 1; + } + return 0; + }); + + return this.gestures; + } }; + /** - * get the title of this node. - * @return {string} title The title of the node, or undefined when no title - * has been set. + * @module hammer */ - Node.prototype.getTitle = function() { - return typeof this.title === "function" ? this.title() : this.title; - }; /** - * 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 + * create new hammer instance + * all methods should return the instance itself, so it is chainable. + * + * @class Instance + * @constructor + * @param {HTMLElement} element + * @param {Object} [options={}] options are merged with `Hammer.defaults` + * @return {Hammer.Instance} */ - Node.prototype.distanceToBorder = function (ctx, angle) { - var borderWidth = 1; + Hammer.Instance = function(element, options) { + var self = this; - if (!this.width) { - this.resize(ctx); - } + // setup HammerJS window events and register all gestures + // this also sets up the default options + setup(); - switch (this.options.shape) { - case 'circle': - case 'dot': - return this.options.radius+ borderWidth; + /** + * @property element + * @type {HTMLElement} + */ + this.element = element; - case 'ellipse': - 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); + /** + * @property enabled + * @type {Boolean} + * @protected + */ + this.enabled = true; - // TODO: implement distanceToBorder for database - // TODO: implement distanceToBorder for triangle - // TODO: implement distanceToBorder for triangleDown + /** + * options, merged with the defaults + * options with an _ are converted to camelCase + * @property options + * @type {Object} + */ + Utils.each(options, function(value, name) { + delete options[name]; + options[Utils.toCamelCase(name)] = value; + }); - case 'box': - case 'image': - case 'text': - default: - if (this.width) { - return Math.min( - Math.abs(this.width / 2 / Math.cos(angle)), - Math.abs(this.height / 2 / Math.sin(angle))) + borderWidth; - // TODO: reckon with border radius too in case of box - } - else { - return 0; - } + this.options = Utils.extend(Utils.extend({}, Hammer.defaults), options || {}); - } - // TODO: implement calculation of distance to border for all shapes - }; + // add some css to the element to prevent the browser from doing its native behavoir + if(this.options.behavior) { + Utils.toggleBehavior(this.element, this.options.behavior, true); + } - /** - * Set forces acting on the node - * @param {number} fx Force in horizontal direction - * @param {number} fy Force in vertical direction - */ - Node.prototype._setForce = function(fx, fy) { - this.fx = fx; - this.fy = fy; - }; + /** + * event start handler on the element to start the detection + * @property eventStartHandler + * @type {Object} + */ + this.eventStartHandler = Event.onTouch(element, EVENT_START, function(ev) { + if(self.enabled && ev.eventType == EVENT_START) { + Detection.startDetect(self, ev); + } else if(ev.eventType == EVENT_TOUCH) { + Detection.detect(ev); + } + }); - /** - * Add forces acting on the node - * @param {number} fx Force in horizontal direction - * @param {number} fy Force in vertical direction - * @private - */ - Node.prototype._addForce = function(fx, fy) { - this.fx += fx; - this.fy += fy; + /** + * keep a list of user event handlers which needs to be removed when calling 'dispose' + * @property eventHandlers + * @type {Array} + */ + this.eventHandlers = []; }; - /** - * Store the state before the next step - */ - Node.prototype.storeState = function() { - this.previousState.x = this.x; - this.previousState.y = this.y; - this.previousState.vx = this.vx; - this.previousState.vy = this.vy; - } + Hammer.Instance.prototype = { + /** + * bind events to the instance + * @method on + * @chainable + * @param {String} gestures multiple gestures by splitting with a space + * @param {Function} handler + * @param {Object} handler.ev event object + */ + on: function onEvent(gestures, handler) { + var self = this; + Event.on(self.element, gestures, handler, function(type) { + self.eventHandlers.push({ gesture: type, handler: handler }); + }); + return self; + }, - /** - * Perform one discrete step for the node - * @param {number} interval Time interval in seconds - */ - Node.prototype.discreteStep = function(interval) { - this.storeState(); - if (!this.xFixed) { - var dx = this.damping * this.vx; // damping force - var ax = (this.fx - dx) / this.options.mass; // acceleration - this.vx += ax * interval; // velocity - this.x += this.vx * interval; // position - } - else { - this.fx = 0; - this.vx = 0; - } + /** + * unbind events to the instance + * @method off + * @chainable + * @param {String} gestures + * @param {Function} handler + */ + off: function offEvent(gestures, handler) { + var self = this; - if (!this.yFixed) { - var dy = this.damping * this.vy; // damping force - var ay = (this.fy - dy) / this.options.mass; // acceleration - this.vy += ay * interval; // velocity - this.y += this.vy * interval; // position - } - else { - this.fy = 0; - this.vy = 0; - } - }; + Event.off(self.element, gestures, handler, function(type) { + var index = Utils.inArray({ gesture: type, handler: handler }); + if(index !== false) { + self.eventHandlers.splice(index, 1); + } + }); + return self; + }, + + /** + * trigger gesture event + * @method trigger + * @chainable + * @param {String} gesture + * @param {Object} [eventData] + */ + trigger: function triggerEvent(gesture, eventData) { + // optional + if(!eventData) { + eventData = {}; + } + // create DOM event + var event = Hammer.DOCUMENT.createEvent('Event'); + event.initEvent(gesture, true, true); + event.gesture = eventData; + // trigger on the target if it is in the instance element, + // this is for event delegation tricks + var element = this.element; + if(Utils.hasParent(eventData.target, element)) { + element = eventData.target; + } - /** - * Perform one discrete step for the node - * @param {number} interval Time interval in seconds - * @param {number} maxVelocity The speed limit imposed on the velocity - */ - Node.prototype.discreteStepLimited = function(interval, maxVelocity) { - this.storeState(); - if (!this.xFixed) { - var dx = this.damping * this.vx; // damping force - var ax = (this.fx - dx) / this.options.mass; // acceleration - this.vx += ax * interval; // velocity - this.vx = (Math.abs(this.vx) > maxVelocity) ? ((this.vx > 0) ? maxVelocity : -maxVelocity) : this.vx; - this.x += this.vx * interval; // position - } - else { - this.fx = 0; - this.vx = 0; - } + element.dispatchEvent(event); + return this; + }, - if (!this.yFixed) { - var dy = this.damping * this.vy; // damping force - var ay = (this.fy - dy) / this.options.mass; // acceleration - this.vy += ay * interval; // velocity - this.vy = (Math.abs(this.vy) > maxVelocity) ? ((this.vy > 0) ? maxVelocity : -maxVelocity) : this.vy; - this.y += this.vy * interval; // position - } - else { - this.fy = 0; - this.vy = 0; - } + /** + * enable of disable hammer.js detection + * @method enable + * @chainable + * @param {Boolean} state + */ + enable: function enable(state) { + this.enabled = state; + return this; + }, + + /** + * dispose this hammer instance + * @method dispose + * @return {Null} + */ + dispose: function dispose() { + var i, eh; + + // undo all changes made by stop_browser_behavior + Utils.toggleBehavior(this.element, this.options.behavior, false); + + // unbind all custom event handlers + for(i = -1; (eh = this.eventHandlers[++i]);) { + Utils.off(this.element, eh.gesture, eh.handler); + } + + this.eventHandlers = []; + + // unbind the start event listener + Event.off(this.element, EVENT_TYPES[EVENT_START], this.eventStartHandler); + + return null; + } }; + /** - * Check if this node has a fixed x and y position - * @return {boolean} true if fixed, false if not + * @module gestures */ - Node.prototype.isFixed = function() { - return (this.xFixed && this.yFixed); - }; - /** - * Check if this node is moving - * @param {number} vmin the minimum velocity considered as "moving" - * @return {boolean} true if moving, false if it has no velocity + * Move with x fingers (default 1) around on the page. + * Preventing the default browser behavior is a good way to improve feel and working. + * ```` + * hammertime.on("drag", function(ev) { + * console.log(ev); + * ev.gesture.preventDefault(); + * }); + * ```` + * + * @class Drag + * @static */ - Node.prototype.isMoving = function(vmin) { - var velocity = Math.sqrt(Math.pow(this.vx,2) + Math.pow(this.vy,2)); - // this.velocity = Math.sqrt(Math.pow(this.vx,2) + Math.pow(this.vy,2)) - return (velocity > vmin); - }; - /** - * check if this node is selecte - * @return {boolean} selected True if node is selected, else false + * @event drag + * @param {Object} ev */ - Node.prototype.isSelected = function() { - return this.selected; - }; - /** - * Retrieve the value of the node. Can be undefined - * @return {Number} value + * @event dragstart + * @param {Object} ev */ - Node.prototype.getValue = function() { - return this.value; - }; - /** - * Calculate the distance from the nodes location to the given location (x,y) - * @param {Number} x - * @param {Number} y - * @return {Number} value + * @event dragend + * @param {Object} ev */ - Node.prototype.getDistance = function(x, y) { - var dx = this.x - x, - dy = this.y - y; - return Math.sqrt(dx * dx + dy * dy); - }; - - /** - * Adjust the value range of the node. The node will adjust it's radius - * based on its value. - * @param {Number} min - * @param {Number} max + * @event drapleft + * @param {Object} ev */ - Node.prototype.setValueRange = function(min, max) { - if (!this.radiusFixed && this.value !== undefined) { - if (max == min) { - this.options.radius= (this.options.radiusMin + this.options.radiusMax) / 2; - } - else { - var scale = (this.options.radiusMax - this.options.radiusMin) / (max - min); - this.options.radius= (this.value - min) * scale + this.options.radiusMin; - } - } - this.baseRadiusValue = this.options.radius; - }; - /** - * Draw this node in the given canvas - * The 2d context of a HTML canvas can be retrieved by canvas.getContext("2d"); - * @param {CanvasRenderingContext2D} ctx + * @event dragright + * @param {Object} ev */ - Node.prototype.draw = function(ctx) { - throw "Draw method not initialized for node"; - }; - /** - * 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 + * @event dragup + * @param {Object} ev */ - Node.prototype.resize = function(ctx) { - throw "Resize method not initialized for node"; - }; - /** - * 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 + * @event dragdown + * @param {Object} ev */ - Node.prototype.isOverlappingWith = function(obj) { - return (this.left < obj.right && - this.left + this.width > obj.left && - this.top < obj.bottom && - this.top + this.height > obj.top); - }; - Node.prototype._resizeImage = function (ctx) { - // TODO: pre calculate the image size + /** + * @param {String} name + */ + (function(name) { + var triggered = false; - if (!this.width || !this.height) { // undefined or 0 - var width, height; - if (this.value) { - this.options.radius= this.baseRadiusValue; - var scale = this.imageObj.height / this.imageObj.width; - if (scale !== undefined) { - width = this.options.radius|| this.imageObj.width; - height = this.options.radius* scale || this.imageObj.height; - } - else { - width = 0; - height = 0; - } - } - else { - width = this.imageObj.width; - height = this.imageObj.height; - } - this.width = width; - this.height = height; + function dragGesture(ev, inst) { + var cur = Detection.current; - this.growthIndicator = 0; - if (this.width > 0 && this.height > 0) { - this.width += Math.min(this.clusterSize - 1, this.maxNodeSizeIncrements) * this.clusterSizeWidthFactor; - this.height += Math.min(this.clusterSize - 1, this.maxNodeSizeIncrements) * this.clusterSizeHeightFactor; - this.options.radius+= Math.min(this.clusterSize - 1, this.maxNodeSizeIncrements) * this.clusterSizeRadiusFactor; - this.growthIndicator = this.width - width; - } - } - }; + // max touches + if(inst.options.dragMaxTouches > 0 && + ev.touches.length > inst.options.dragMaxTouches) { + return; + } - Node.prototype._drawImageAtPosition = function (ctx) { - if (this.imageObj.width != 0 ) { - // draw the shade - if (this.clusterSize > 1) { - var lineWidth = ((this.clusterSize > 1) ? 10 : 0.0); - lineWidth *= this.networkScaleInv; - lineWidth = Math.min(0.2 * this.width,lineWidth); + switch(ev.eventType) { + case EVENT_START: + triggered = false; + break; - ctx.globalAlpha = 0.5; - ctx.drawImage(this.imageObj, this.left - lineWidth, this.top - lineWidth, this.width + 2*lineWidth, this.height + 2*lineWidth); - } + case EVENT_MOVE: + // when the distance we moved is too small we skip this gesture + // or we can be already in dragging + if(ev.distance < inst.options.dragMinDistance && + cur.name != name) { + return; + } - // draw the image - ctx.globalAlpha = 1.0; - ctx.drawImage(this.imageObj, this.left, this.top, this.width, this.height); - } - }; + var startCenter = cur.startEvent.center; - Node.prototype._drawImageLabel = function (ctx) { - var yLabel; - var offset = 0; - - if (this.height){ - offset = this.height / 2; - var labelDimensions = this.getTextSize(ctx); - - if (labelDimensions.lineCount >= 1){ - offset += labelDimensions.height / 2; - offset += 3; - } - } - - yLabel = this.y + offset; - - this._label(ctx, this.label, this.x, yLabel, undefined); - }; - - Node.prototype._drawImage = function (ctx) { - this._resizeImage(ctx); - this.left = this.x - this.width / 2; - this.top = this.y - this.height / 2; + // we are dragging! + if(cur.name != name) { + cur.name = name; + if(inst.options.dragDistanceCorrection && ev.distance > 0) { + // When a drag is triggered, set the event center to dragMinDistance pixels from the original event center. + // Without this correction, the dragged distance would jumpstart at dragMinDistance pixels instead of at 0. + // It might be useful to save the original start point somewhere + var factor = Math.abs(inst.options.dragMinDistance / ev.distance); + startCenter.pageX += ev.deltaX * factor; + startCenter.pageY += ev.deltaY * factor; + startCenter.clientX += ev.deltaX * factor; + startCenter.clientY += ev.deltaY * factor; - this._drawImageAtPosition(ctx); + // recalculate event data using new start point + ev = Detection.extendEventData(ev); + } + } - this.boundingBox.top = this.top; - this.boundingBox.left = this.left; - this.boundingBox.right = this.left + this.width; - this.boundingBox.bottom = this.top + this.height; + // lock drag to axis? + if(cur.lastEvent.dragLockToAxis || + ( inst.options.dragLockToAxis && + inst.options.dragLockMinDistance <= ev.distance + )) { + ev.dragLockToAxis = true; + } - this._drawImageLabel(ctx); - this.boundingBox.left = Math.min(this.boundingBox.left, this.labelDimensions.left); - this.boundingBox.right = Math.max(this.boundingBox.right, this.labelDimensions.left + this.labelDimensions.width); - this.boundingBox.bottom = Math.max(this.boundingBox.bottom, this.boundingBox.bottom + this.labelDimensions.height); - }; + // keep direction on the axis that the drag gesture started on + var lastDirection = cur.lastEvent.direction; + if(ev.dragLockToAxis && lastDirection !== ev.direction) { + if(Utils.isVertical(lastDirection)) { + ev.direction = (ev.deltaY < 0) ? DIRECTION_UP : DIRECTION_DOWN; + } else { + ev.direction = (ev.deltaX < 0) ? DIRECTION_LEFT : DIRECTION_RIGHT; + } + } - Node.prototype._resizeCircularImage = function (ctx) { - if(!this.imageObj.src || !this.imageObj.width || !this.imageObj.height){ - if (!this.width) { - var diameter = this.options.radius * 2; - this.width = diameter; - this.height = diameter; + // first time, trigger dragstart event + if(!triggered) { + inst.trigger(name + 'start', ev); + triggered = true; + } - // scaling used for clustering - //this.width += Math.min(this.clusterSize - 1, this.maxNodeSizeIncrements) * 0.5 * this.clusterSizeWidthFactor; - //this.height += Math.min(this.clusterSize - 1, this.maxNodeSizeIncrements) * 0.5 * this.clusterSizeHeightFactor; - this.options.radius += Math.min(this.clusterSize - 1, this.maxNodeSizeIncrements) * 0.5 * this.clusterSizeRadiusFactor; - this.growthIndicator = this.options.radius- 0.5*diameter; - this._swapToImageResizeWhenImageLoaded = true; - } - } - else { - if (this._swapToImageResizeWhenImageLoaded) { - this.width = 0; - this.height = 0; - delete this._swapToImageResizeWhenImageLoaded; - } - this._resizeImage(ctx); - } + // trigger events + inst.trigger(name, ev); + inst.trigger(name + ev.direction, ev); - }; + var isVertical = Utils.isVertical(ev.direction); - Node.prototype._drawCircularImage = function (ctx) { - this._resizeCircularImage(ctx); + // block the browser events + if((inst.options.dragBlockVertical && isVertical) || + (inst.options.dragBlockHorizontal && !isVertical)) { + ev.preventDefault(); + } + break; - this.left = this.x - this.width / 2; - this.top = this.y - this.height / 2; - - var centerX = this.left + (this.width / 2); - var centerY = this.top + (this.height / 2); - var radius = Math.abs(this.height / 2); + case EVENT_RELEASE: + if(triggered && ev.changedLength <= inst.options.dragMaxTouches) { + inst.trigger(name + 'end', ev); + triggered = false; + } + break; - this._drawRawCircle(ctx, centerX, centerY, radius); + case EVENT_END: + triggered = false; + break; + } + } - ctx.save(); - ctx.circle(this.x, this.y, radius); - ctx.stroke(); - ctx.clip(); + Hammer.gestures.Drag = { + name: name, + index: 50, + handler: dragGesture, + defaults: { + /** + * minimal movement that have to be made before the drag event gets triggered + * @property dragMinDistance + * @type {Number} + * @default 10 + */ + dragMinDistance: 10, - this._drawImageAtPosition(ctx); + /** + * Set dragDistanceCorrection to true to make the starting point of the drag + * be calculated from where the drag was triggered, not from where the touch started. + * Useful to avoid a jerk-starting drag, which can make fine-adjustments + * through dragging difficult, and be visually unappealing. + * @property dragDistanceCorrection + * @type {Boolean} + * @default true + */ + dragDistanceCorrection: true, - ctx.restore(); + /** + * set 0 for unlimited, but this can conflict with transform + * @property dragMaxTouches + * @type {Number} + * @default 1 + */ + dragMaxTouches: 1, - this.boundingBox.top = this.y - this.options.radius; - this.boundingBox.left = this.x - this.options.radius; - this.boundingBox.right = this.x + this.options.radius; - this.boundingBox.bottom = this.y + this.options.radius; + /** + * prevent default browser behavior when dragging occurs + * be careful with it, it makes the element a blocking element + * when you are using the drag gesture, it is a good practice to set this true + * @property dragBlockHorizontal + * @type {Boolean} + * @default false + */ + dragBlockHorizontal: false, - this._drawImageLabel(ctx); - - this.boundingBox.left = Math.min(this.boundingBox.left, this.labelDimensions.left); - this.boundingBox.right = Math.max(this.boundingBox.right, this.labelDimensions.left + this.labelDimensions.width); - this.boundingBox.bottom = Math.max(this.boundingBox.bottom, this.boundingBox.bottom + this.labelDimensions.height); - }; + /** + * same as `dragBlockHorizontal`, but for vertical movement + * @property dragBlockVertical + * @type {Boolean} + * @default false + */ + dragBlockVertical: false, - Node.prototype._resizeBox = function (ctx) { - if (!this.width) { - var margin = 5; - var textSize = this.getTextSize(ctx); - this.width = textSize.width + 2 * margin; - this.height = textSize.height + 2 * margin; + /** + * dragLockToAxis keeps the drag gesture on the axis that it started on, + * It disallows vertical directions if the initial direction was horizontal, and vice versa. + * @property dragLockToAxis + * @type {Boolean} + * @default false + */ + dragLockToAxis: false, - this.width += Math.min(this.clusterSize - 1, this.maxNodeSizeIncrements) * 0.5 * this.clusterSizeWidthFactor; - this.height += Math.min(this.clusterSize - 1, this.maxNodeSizeIncrements) * 0.5 * this.clusterSizeHeightFactor; - this.growthIndicator = this.width - (textSize.width + 2 * margin); - // this.options.radius+= Math.min(this.clusterSize - 1, this.maxNodeSizeIncrements) * 0.5 * this.clusterSizeRadiusFactor; + /** + * drag lock only kicks in when distance > dragLockMinDistance + * This way, locking occurs only when the distance has become large enough to reliably determine the direction + * @property dragLockMinDistance + * @type {Number} + * @default 25 + */ + dragLockMinDistance: 25 + } + }; + })('drag'); - } + /** + * @module gestures + */ + /** + * trigger a simple gesture event, so you can do anything in your handler. + * only usable if you know what your doing... + * + * @class Gesture + * @static + */ + /** + * @event gesture + * @param {Object} ev + */ + Hammer.gestures.Gesture = { + name: 'gesture', + index: 1337, + handler: function releaseGesture(ev, inst) { + inst.trigger(this.name, ev); + } }; - Node.prototype._drawBox = function (ctx) { - this._resizeBox(ctx); - - this.left = this.x - this.width / 2; - this.top = this.y - this.height / 2; - - var clusterLineWidth = 2.5; - var borderWidth = this.options.borderWidth; - var selectionLineWidth = this.options.borderWidthSelected || 2 * this.options.borderWidth; + /** + * @module gestures + */ + /** + * Touch stays at the same place for x time + * + * @class Hold + * @static + */ + /** + * @event hold + * @param {Object} ev + */ - ctx.strokeStyle = this.selected ? this.options.color.highlight.border : this.hover ? this.options.color.hover.border : this.options.color.border; + /** + * @param {String} name + */ + (function(name) { + var timer; - // draw the outer border - if (this.clusterSize > 1) { - ctx.lineWidth = (this.selected ? selectionLineWidth : borderWidth) + ((this.clusterSize > 1) ? clusterLineWidth : 0.0); - ctx.lineWidth *= this.networkScaleInv; - ctx.lineWidth = Math.min(this.width,ctx.lineWidth); + function holdGesture(ev, inst) { + var options = inst.options, + current = Detection.current; - ctx.roundRect(this.left-2*ctx.lineWidth, this.top-2*ctx.lineWidth, this.width+4*ctx.lineWidth, this.height+4*ctx.lineWidth, this.options.radius); - ctx.stroke(); - } - ctx.lineWidth = (this.selected ? selectionLineWidth : borderWidth) + ((this.clusterSize > 1) ? clusterLineWidth : 0.0); - ctx.lineWidth *= this.networkScaleInv; - ctx.lineWidth = Math.min(this.width,ctx.lineWidth); + switch(ev.eventType) { + case EVENT_START: + clearTimeout(timer); - ctx.fillStyle = this.selected ? this.options.color.highlight.background : this.hover ? this.options.color.hover.background : this.options.color.background; + // set the gesture so we can check in the timeout if it still is + current.name = name; - ctx.roundRect(this.left, this.top, this.width, this.height, this.options.radius); - ctx.fill(); - ctx.stroke(); + // set timer and if after the timeout it still is hold, + // we trigger the hold event + timer = setTimeout(function() { + if(current && current.name == name) { + inst.trigger(name, ev); + } + }, options.holdTimeout); + break; - this.boundingBox.top = this.top; - this.boundingBox.left = this.left; - this.boundingBox.right = this.left + this.width; - this.boundingBox.bottom = this.top + this.height; + case EVENT_MOVE: + if(ev.distance > options.holdThreshold) { + clearTimeout(timer); + } + break; - this._label(ctx, this.label, this.x, this.y); - }; + case EVENT_RELEASE: + clearTimeout(timer); + break; + } + } + Hammer.gestures.Hold = { + name: name, + index: 10, + defaults: { + /** + * @property holdTimeout + * @type {Number} + * @default 500 + */ + holdTimeout: 500, - Node.prototype._resizeDatabase = function (ctx) { - if (!this.width) { - var margin = 5; - var textSize = this.getTextSize(ctx); - var size = textSize.width + 2 * margin; - this.width = size; - this.height = size; + /** + * movement allowed while holding + * @property holdThreshold + * @type {Number} + * @default 2 + */ + holdThreshold: 2 + }, + handler: holdGesture + }; + })('hold'); - // scaling used for clustering - this.width += Math.min(this.clusterSize - 1, this.maxNodeSizeIncrements) * this.clusterSizeWidthFactor; - this.height += Math.min(this.clusterSize - 1, this.maxNodeSizeIncrements) * this.clusterSizeHeightFactor; - this.options.radius+= Math.min(this.clusterSize - 1, this.maxNodeSizeIncrements) * this.clusterSizeRadiusFactor; - this.growthIndicator = this.width - size; - } + /** + * @module gestures + */ + /** + * when a touch is being released from the page + * + * @class Release + * @static + */ + /** + * @event release + * @param {Object} ev + */ + Hammer.gestures.Release = { + name: 'release', + index: Infinity, + handler: function releaseGesture(ev, inst) { + if(ev.eventType == EVENT_RELEASE) { + inst.trigger(this.name, ev); + } + } }; - Node.prototype._drawDatabase = function (ctx) { - this._resizeDatabase(ctx); - this.left = this.x - this.width / 2; - this.top = this.y - this.height / 2; - - var clusterLineWidth = 2.5; - var borderWidth = this.options.borderWidth; - var selectionLineWidth = this.options.borderWidthSelected || 2 * this.options.borderWidth; + /** + * @module gestures + */ + /** + * triggers swipe events when the end velocity is above the threshold + * for best usage, set `preventDefault` (on the drag gesture) to `true` + * ```` + * hammertime.on("dragleft swipeleft", function(ev) { + * console.log(ev); + * ev.gesture.preventDefault(); + * }); + * ```` + * + * @class Swipe + * @static + */ + /** + * @event swipe + * @param {Object} ev + */ + /** + * @event swipeleft + * @param {Object} ev + */ + /** + * @event swiperight + * @param {Object} ev + */ + /** + * @event swipeup + * @param {Object} ev + */ + /** + * @event swipedown + * @param {Object} ev + */ + Hammer.gestures.Swipe = { + name: 'swipe', + index: 40, + defaults: { + /** + * @property swipeMinTouches + * @type {Number} + * @default 1 + */ + swipeMinTouches: 1, - ctx.strokeStyle = this.selected ? this.options.color.highlight.border : this.hover ? this.options.color.hover.border : this.options.color.border; + /** + * @property swipeMaxTouches + * @type {Number} + * @default 1 + */ + swipeMaxTouches: 1, - // draw the outer border - if (this.clusterSize > 1) { - ctx.lineWidth = (this.selected ? selectionLineWidth : borderWidth) + ((this.clusterSize > 1) ? clusterLineWidth : 0.0); - ctx.lineWidth *= this.networkScaleInv; - ctx.lineWidth = Math.min(this.width,ctx.lineWidth); + /** + * horizontal swipe velocity + * @property swipeVelocityX + * @type {Number} + * @default 0.6 + */ + swipeVelocityX: 0.6, - ctx.database(this.x - this.width/2 - 2*ctx.lineWidth, this.y - this.height*0.5 - 2*ctx.lineWidth, this.width + 4*ctx.lineWidth, this.height + 4*ctx.lineWidth); - ctx.stroke(); - } - ctx.lineWidth = (this.selected ? selectionLineWidth : borderWidth) + ((this.clusterSize > 1) ? clusterLineWidth : 0.0); - ctx.lineWidth *= this.networkScaleInv; - ctx.lineWidth = Math.min(this.width,ctx.lineWidth); + /** + * vertical swipe velocity + * @property swipeVelocityY + * @type {Number} + * @default 0.6 + */ + swipeVelocityY: 0.6 + }, - ctx.fillStyle = this.selected ? this.options.color.highlight.background : this.hover ? this.options.color.hover.background : this.options.color.background; - ctx.database(this.x - this.width/2, this.y - this.height*0.5, this.width, this.height); - ctx.fill(); - ctx.stroke(); + handler: function swipeGesture(ev, inst) { + if(ev.eventType == EVENT_RELEASE) { + var touches = ev.touches.length, + options = inst.options; - this.boundingBox.top = this.top; - this.boundingBox.left = this.left; - this.boundingBox.right = this.left + this.width; - this.boundingBox.bottom = this.top + this.height; + // max touches + if(touches < options.swipeMinTouches || + touches > options.swipeMaxTouches) { + return; + } - this._label(ctx, this.label, this.x, this.y); + // when the distance we moved is too small we skip this gesture + // or we can be already in dragging + if(ev.velocityX > options.swipeVelocityX || + ev.velocityY > options.swipeVelocityY) { + // trigger swipe events + inst.trigger(this.name, ev); + inst.trigger(this.name + ev.direction, ev); + } + } + } }; + /** + * @module gestures + */ + /** + * Single tap and a double tap on a place + * + * @class Tap + * @static + */ + /** + * @event tap + * @param {Object} ev + */ + /** + * @event doubletap + * @param {Object} ev + */ - Node.prototype._resizeCircle = function (ctx) { - if (!this.width) { - var margin = 5; - var textSize = this.getTextSize(ctx); - var diameter = Math.max(textSize.width, textSize.height) + 2 * margin; - this.options.radius = diameter / 2; - - this.width = diameter; - this.height = diameter; + /** + * @param {String} name + */ + (function(name) { + var hasMoved = false; - // scaling used for clustering - // this.width += Math.min(this.clusterSize - 1, this.maxNodeSizeIncrements) * 0.5 * this.clusterSizeWidthFactor; - // this.height += Math.min(this.clusterSize - 1, this.maxNodeSizeIncrements) * 0.5 * this.clusterSizeHeightFactor; - this.options.radius += Math.min(this.clusterSize - 1, this.maxNodeSizeIncrements) * 0.5 * this.clusterSizeRadiusFactor; - this.growthIndicator = this.options.radius- 0.5*diameter; - } - }; + function tapGesture(ev, inst) { + var options = inst.options, + current = Detection.current, + prev = Detection.previous, + sincePrev, + didDoubleTap; - Node.prototype._drawRawCircle = function (ctx, x, y, radius) { - var clusterLineWidth = 2.5; - var borderWidth = this.options.borderWidth; - var selectionLineWidth = this.options.borderWidthSelected || 2 * this.options.borderWidth; - - ctx.strokeStyle = this.selected ? this.options.color.highlight.border : this.hover ? this.options.color.hover.border : this.options.color.border; + switch(ev.eventType) { + case EVENT_START: + hasMoved = false; + break; - // draw the outer border - if (this.clusterSize > 1) { - ctx.lineWidth = (this.selected ? selectionLineWidth : borderWidth) + ((this.clusterSize > 1) ? clusterLineWidth : 0.0); - ctx.lineWidth *= this.networkScaleInv; - ctx.lineWidth = Math.min(this.width,ctx.lineWidth); + case EVENT_MOVE: + hasMoved = hasMoved || (ev.distance > options.tapMaxDistance); + break; - ctx.circle(x, y, radius+2*ctx.lineWidth); - ctx.stroke(); - } - ctx.lineWidth = (this.selected ? selectionLineWidth : borderWidth) + ((this.clusterSize > 1) ? clusterLineWidth : 0.0); - ctx.lineWidth *= this.networkScaleInv; - ctx.lineWidth = Math.min(this.width,ctx.lineWidth); + case EVENT_END: + if(!Utils.inStr(ev.srcEvent.type, 'cancel') && ev.deltaTime < options.tapMaxTime && !hasMoved) { + // previous gesture, for the double tap since these are two different gesture detections + sincePrev = prev && prev.lastEvent && ev.timeStamp - prev.lastEvent.timeStamp; + didDoubleTap = false; - ctx.fillStyle = this.selected ? this.options.color.highlight.background : this.hover ? this.options.color.hover.background : this.options.color.background; - ctx.circle(this.x, this.y, radius); - ctx.fill(); - ctx.stroke(); - }; + // check if double tap + if(prev && prev.name == name && + (sincePrev && sincePrev < options.doubleTapInterval) && + ev.distance < options.doubleTapDistance) { + inst.trigger('doubletap', ev); + didDoubleTap = true; + } - Node.prototype._drawCircle = function (ctx) { - this._resizeCircle(ctx); - this.left = this.x - this.width / 2; - this.top = this.y - this.height / 2; + // do a single tap + if(!didDoubleTap || options.tapAlways) { + current.name = name; + inst.trigger(current.name, ev); + } + } + break; + } + } - this._drawRawCircle(ctx, this.x, this.y, this.options.radius); + Hammer.gestures.Tap = { + name: name, + index: 100, + handler: tapGesture, + defaults: { + /** + * max time of a tap, this is for the slow tappers + * @property tapMaxTime + * @type {Number} + * @default 250 + */ + tapMaxTime: 250, - this.boundingBox.top = this.y - this.options.radius; - this.boundingBox.left = this.x - this.options.radius; - this.boundingBox.right = this.x + this.options.radius; - this.boundingBox.bottom = this.y + this.options.radius; + /** + * max distance of movement of a tap, this is for the slow tappers + * @property tapMaxDistance + * @type {Number} + * @default 10 + */ + tapMaxDistance: 10, - this._label(ctx, this.label, this.x, this.y); - }; + /** + * always trigger the `tap` event, even while double-tapping + * @property tapAlways + * @type {Boolean} + * @default true + */ + tapAlways: true, - Node.prototype._resizeEllipse = function (ctx) { - if (!this.width) { - var textSize = this.getTextSize(ctx); + /** + * max distance between two taps + * @property doubleTapDistance + * @type {Number} + * @default 20 + */ + doubleTapDistance: 20, - this.width = textSize.width * 1.5; - this.height = textSize.height * 2; - if (this.width < this.height) { - this.width = this.height; - } - var defaultSize = this.width; + /** + * max time between two taps + * @property doubleTapInterval + * @type {Number} + * @default 300 + */ + doubleTapInterval: 300 + } + }; + })('tap'); - // scaling used for clustering - this.width += Math.min(this.clusterSize - 1, this.maxNodeSizeIncrements) * this.clusterSizeWidthFactor; - this.height += Math.min(this.clusterSize - 1, this.maxNodeSizeIncrements) * this.clusterSizeHeightFactor; - this.options.radius += Math.min(this.clusterSize - 1, this.maxNodeSizeIncrements) * this.clusterSizeRadiusFactor; - this.growthIndicator = this.width - defaultSize; - } - }; + /** + * @module gestures + */ + /** + * when a touch is being touched at the page + * + * @class Touch + * @static + */ + /** + * @event touch + * @param {Object} ev + */ + Hammer.gestures.Touch = { + name: 'touch', + index: -Infinity, + defaults: { + /** + * call preventDefault at touchstart, and makes the element blocking by disabling the scrolling of the page, + * but it improves gestures like transforming and dragging. + * be careful with using this, it can be very annoying for users to be stuck on the page + * @property preventDefault + * @type {Boolean} + * @default false + */ + preventDefault: false, - Node.prototype._drawEllipse = function (ctx) { - this._resizeEllipse(ctx); - this.left = this.x - this.width / 2; - this.top = this.y - this.height / 2; + /** + * disable mouse events, so only touch (or pen!) input triggers events + * @property preventMouse + * @type {Boolean} + * @default false + */ + preventMouse: false + }, + handler: function touchGesture(ev, inst) { + if(inst.options.preventMouse && ev.pointerType == POINTER_MOUSE) { + ev.stopDetect(); + return; + } - var clusterLineWidth = 2.5; - var borderWidth = this.options.borderWidth; - var selectionLineWidth = this.options.borderWidthSelected || 2 * this.options.borderWidth; + if(inst.options.preventDefault) { + ev.preventDefault(); + } - ctx.strokeStyle = this.selected ? this.options.color.highlight.border : this.hover ? this.options.color.hover.border : this.options.color.border; + if(ev.eventType == EVENT_TOUCH) { + inst.trigger('touch', ev); + } + } + }; - // draw the outer border - if (this.clusterSize > 1) { - ctx.lineWidth = (this.selected ? selectionLineWidth : borderWidth) + ((this.clusterSize > 1) ? clusterLineWidth : 0.0); - ctx.lineWidth *= this.networkScaleInv; - ctx.lineWidth = Math.min(this.width,ctx.lineWidth); + /** + * @module gestures + */ + /** + * User want to scale or rotate with 2 fingers + * Preventing the default browser behavior is a good way to improve feel and working. This can be done with the + * `preventDefault` option. + * + * @class Transform + * @static + */ + /** + * @event transform + * @param {Object} ev + */ + /** + * @event transformstart + * @param {Object} ev + */ + /** + * @event transformend + * @param {Object} ev + */ + /** + * @event pinchin + * @param {Object} ev + */ + /** + * @event pinchout + * @param {Object} ev + */ + /** + * @event rotate + * @param {Object} ev + */ - ctx.ellipse(this.left-2*ctx.lineWidth, this.top-2*ctx.lineWidth, this.width+4*ctx.lineWidth, this.height+4*ctx.lineWidth); - ctx.stroke(); - } - ctx.lineWidth = (this.selected ? selectionLineWidth : borderWidth) + ((this.clusterSize > 1) ? clusterLineWidth : 0.0); - ctx.lineWidth *= this.networkScaleInv; - ctx.lineWidth = Math.min(this.width,ctx.lineWidth); + /** + * @param {String} name + */ + (function(name) { + var triggered = false; - ctx.fillStyle = this.selected ? this.options.color.highlight.background : this.hover ? this.options.color.hover.background : this.options.color.background; + function transformGesture(ev, inst) { + switch(ev.eventType) { + case EVENT_START: + triggered = false; + break; - ctx.ellipse(this.left, this.top, this.width, this.height); - ctx.fill(); - ctx.stroke(); + case EVENT_MOVE: + // at least multitouch + if(ev.touches.length < 2) { + return; + } - this.boundingBox.top = this.top; - this.boundingBox.left = this.left; - this.boundingBox.right = this.left + this.width; - this.boundingBox.bottom = this.top + this.height; + var scaleThreshold = Math.abs(1 - ev.scale); + var rotationThreshold = Math.abs(ev.rotation); - this._label(ctx, this.label, this.x, this.y); - }; + // when the distance we moved is too small we skip this gesture + // or we can be already in dragging + if(scaleThreshold < inst.options.transformMinScale && + rotationThreshold < inst.options.transformMinRotation) { + return; + } - Node.prototype._drawDot = function (ctx) { - this._drawShape(ctx, 'circle'); - }; + // we are transforming! + Detection.current.name = name; - Node.prototype._drawTriangle = function (ctx) { - this._drawShape(ctx, 'triangle'); - }; + // first time, trigger dragstart event + if(!triggered) { + inst.trigger(name + 'start', ev); + triggered = true; + } - Node.prototype._drawTriangleDown = function (ctx) { - this._drawShape(ctx, 'triangleDown'); - }; + inst.trigger(name, ev); // basic transform event - Node.prototype._drawSquare = function (ctx) { - this._drawShape(ctx, 'square'); - }; + // trigger rotate event + if(rotationThreshold > inst.options.transformMinRotation) { + inst.trigger('rotate', ev); + } - Node.prototype._drawStar = function (ctx) { - this._drawShape(ctx, 'star'); - }; + // trigger pinch event + if(scaleThreshold > inst.options.transformMinScale) { + inst.trigger('pinch', ev); + inst.trigger('pinch' + (ev.scale < 1 ? 'in' : 'out'), ev); + } + break; - Node.prototype._resizeShape = function (ctx) { - if (!this.width) { - this.options.radius= this.baseRadiusValue; - var size = 2 * this.options.radius; - this.width = size; - this.height = size; + case EVENT_RELEASE: + if(triggered && ev.changedLength < 2) { + inst.trigger(name + 'end', ev); + triggered = false; + } + break; + } + } - // scaling used for clustering - this.width += Math.min(this.clusterSize - 1, this.maxNodeSizeIncrements) * this.clusterSizeWidthFactor; - this.height += Math.min(this.clusterSize - 1, this.maxNodeSizeIncrements) * this.clusterSizeHeightFactor; - this.options.radius+= Math.min(this.clusterSize - 1, this.maxNodeSizeIncrements) * 0.5 * this.clusterSizeRadiusFactor; - this.growthIndicator = this.width - size; - } - }; + Hammer.gestures.Transform = { + name: name, + index: 45, + defaults: { + /** + * minimal scale factor, no scale is 1, zoomin is to 0 and zoomout until higher then 1 + * @property transformMinScale + * @type {Number} + * @default 0.01 + */ + transformMinScale: 0.01, - Node.prototype._drawShape = function (ctx, shape) { - this._resizeShape(ctx); + /** + * rotation in degrees + * @property transformMinRotation + * @type {Number} + * @default 1 + */ + transformMinRotation: 1 + }, - this.left = this.x - this.width / 2; - this.top = this.y - this.height / 2; + handler: transformGesture + }; + })('transform'); - var clusterLineWidth = 2.5; - var borderWidth = this.options.borderWidth; - var selectionLineWidth = this.options.borderWidthSelected || 2 * this.options.borderWidth; - var radiusMultiplier = 2; + /** + * @module hammer + */ - // choose draw method depending on the shape - switch (shape) { - case 'dot': radiusMultiplier = 2; break; - case 'square': radiusMultiplier = 2; break; - case 'triangle': radiusMultiplier = 3; break; - case 'triangleDown': radiusMultiplier = 3; break; - case 'star': radiusMultiplier = 4; break; - } + // AMD export + if(true) { + !(__WEBPACK_AMD_DEFINE_RESULT__ = function() { + return Hammer; + }.call(exports, __webpack_require__, exports, module), __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__)); + // commonjs export + } else if(typeof module !== 'undefined' && module.exports) { + module.exports = Hammer; + // browser export + } else { + window.Hammer = Hammer; + } - ctx.strokeStyle = this.selected ? this.options.color.highlight.border : this.hover ? this.options.color.hover.border : this.options.color.border; - // draw the outer border - if (this.clusterSize > 1) { - ctx.lineWidth = (this.selected ? selectionLineWidth : borderWidth) + ((this.clusterSize > 1) ? clusterLineWidth : 0.0); - ctx.lineWidth *= this.networkScaleInv; - ctx.lineWidth = Math.min(this.width,ctx.lineWidth); + })(window); - ctx[shape](this.x, this.y, this.options.radius+ radiusMultiplier * ctx.lineWidth); - ctx.stroke(); - } - ctx.lineWidth = (this.selected ? selectionLineWidth : borderWidth) + ((this.clusterSize > 1) ? clusterLineWidth : 0.0); - ctx.lineWidth *= this.networkScaleInv; - ctx.lineWidth = Math.min(this.width,ctx.lineWidth); +/***/ }, +/* 59 */ +/***/ function(module, exports, __webpack_require__) { - ctx.fillStyle = this.selected ? this.options.color.highlight.background : this.hover ? this.options.color.hover.background : this.options.color.background; - ctx[shape](this.x, this.y, this.options.radius); - ctx.fill(); - ctx.stroke(); + var __WEBPACK_AMD_DEFINE_RESULT__;/* WEBPACK VAR INJECTION */(function(global, module) {//! moment.js + //! version : 2.9.0 + //! authors : Tim Wood, Iskren Chernev, Moment.js contributors + //! license : MIT + //! momentjs.com - this.boundingBox.top = this.y - this.options.radius; - this.boundingBox.left = this.x - this.options.radius; - this.boundingBox.right = this.x + this.options.radius; - this.boundingBox.bottom = this.y + this.options.radius; + (function (undefined) { + /************************************ + Constants + ************************************/ - if (this.label) { - this._label(ctx, this.label, this.x, this.y + this.height / 2, undefined, 'hanging',true); - this.boundingBox.left = Math.min(this.boundingBox.left, this.labelDimensions.left); - this.boundingBox.right = Math.max(this.boundingBox.right, this.labelDimensions.left + this.labelDimensions.width); - this.boundingBox.bottom = Math.max(this.boundingBox.bottom, this.boundingBox.bottom + this.labelDimensions.height); - } - }; + var moment, + VERSION = '2.9.0', + // the global-scope this is NOT the global object in Node.js + globalScope = (typeof global !== 'undefined' && (typeof window === 'undefined' || window === global.window)) ? global : this, + oldGlobalMoment, + round = Math.round, + hasOwnProperty = Object.prototype.hasOwnProperty, + i, - Node.prototype._resizeText = function (ctx) { - if (!this.width) { - var margin = 5; - var textSize = this.getTextSize(ctx); - this.width = textSize.width + 2 * margin; - this.height = textSize.height + 2 * margin; + YEAR = 0, + MONTH = 1, + DATE = 2, + HOUR = 3, + MINUTE = 4, + SECOND = 5, + MILLISECOND = 6, - // scaling used for clustering - this.width += Math.min(this.clusterSize - 1, this.maxNodeSizeIncrements) * this.clusterSizeWidthFactor; - this.height += Math.min(this.clusterSize - 1, this.maxNodeSizeIncrements) * this.clusterSizeHeightFactor; - this.options.radius+= Math.min(this.clusterSize - 1, this.maxNodeSizeIncrements) * this.clusterSizeRadiusFactor; - this.growthIndicator = this.width - (textSize.width + 2 * margin); - } - }; + // internal storage for locale config files + locales = {}, - Node.prototype._drawText = function (ctx) { - this._resizeText(ctx); - this.left = this.x - this.width / 2; - this.top = this.y - this.height / 2; + // extra moment internal properties (plugins register props here) + momentProperties = [], - this._label(ctx, this.label, this.x, this.y); + // check for nodeJS + hasModule = (typeof module !== 'undefined' && module && module.exports), - this.boundingBox.top = this.top; - this.boundingBox.left = this.left; - this.boundingBox.right = this.left + this.width; - this.boundingBox.bottom = this.top + this.height; - }; + // ASP.NET json date format regex + aspNetJsonRegex = /^\/?Date\((\-?\d+)/i, + aspNetTimeSpanJsonRegex = /(\-)?(?:(\d*)\.)?(\d+)\:(\d+)(?:\:(\d+)\.?(\d{3})?)?/, + // from http://docs.closure-library.googlecode.com/git/closure_goog_date_date.js.source.html + // somewhat more in line with 4.4.3.2 2004 spec, but allows decimal anywhere + isoDurationRegex = /^(-)?P(?:(?:([0-9,.]*)Y)?(?:([0-9,.]*)M)?(?:([0-9,.]*)D)?(?:T(?:([0-9,.]*)H)?(?:([0-9,.]*)M)?(?:([0-9,.]*)S)?)?|([0-9,.]*)W)$/, - Node.prototype._label = function (ctx, text, x, y, align, baseline, labelUnderNode) { - if (text && Number(this.options.fontSize) * this.networkScale > this.fontDrawThreshold) { - ctx.font = (this.selected ? "bold " : "") + this.options.fontSize + "px " + this.options.fontFace; + // format tokens + formattingTokens = /(\[[^\[]*\])|(\\)?(Mo|MM?M?M?|Do|DDDo|DD?D?D?|ddd?d?|do?|w[o|w]?|W[o|W]?|Q|YYYYYY|YYYYY|YYYY|YY|gg(ggg?)?|GG(GGG?)?|e|E|a|A|hh?|HH?|mm?|ss?|S{1,4}|x|X|zz?|ZZ?|.)/g, + localFormattingTokens = /(\[[^\[]*\])|(\\)?(LTS|LT|LL?L?L?|l{1,4})/g, - var lines = text.split('\n'); - var lineCount = lines.length; - var fontSize = Number(this.options.fontSize); - var yLine = y + (1 - lineCount) / 2 * fontSize; - if (labelUnderNode == true) { - yLine = y + (1 - lineCount) / (2 * fontSize); - } + // parsing token regexes + parseTokenOneOrTwoDigits = /\d\d?/, // 0 - 99 + parseTokenOneToThreeDigits = /\d{1,3}/, // 0 - 999 + parseTokenOneToFourDigits = /\d{1,4}/, // 0 - 9999 + parseTokenOneToSixDigits = /[+\-]?\d{1,6}/, // -999,999 - 999,999 + parseTokenDigits = /\d+/, // nonzero number of digits + parseTokenWord = /[0-9]*['a-z\u00A0-\u05FF\u0700-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]+|[\u0600-\u06FF\/]+(\s*?[\u0600-\u06FF]+){1,2}/i, // any word (or two) characters or numbers including two/three word month in arabic. + parseTokenTimezone = /Z|[\+\-]\d\d:?\d\d/gi, // +00:00 -00:00 +0000 -0000 or Z + parseTokenT = /T/i, // T (ISO separator) + parseTokenOffsetMs = /[\+\-]?\d+/, // 1234567890123 + parseTokenTimestampMs = /[\+\-]?\d+(\.\d{1,3})?/, // 123456789 123456789.123 - // font fill from edges now for nodes! - var 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; - } - var height = this.options.fontSize * lineCount; - var left = x - width / 2; - var top = y - height / 2; - if (baseline == "hanging") { - top += 0.5 * fontSize; - top += 4; // distance from node, required because we use hanging. Hanging has less difference between browsers - yLine += 4; // distance from node - } - this.labelDimensions = {top:top,left:left,width:width,height:height,yLine:yLine}; + //strict parsing regexes + parseTokenOneDigit = /\d/, // 0 - 9 + parseTokenTwoDigits = /\d\d/, // 00 - 99 + parseTokenThreeDigits = /\d{3}/, // 000 - 999 + parseTokenFourDigits = /\d{4}/, // 0000 - 9999 + parseTokenSixDigits = /[+-]?\d{6}/, // -999,999 - 999,999 + parseTokenSignedNumber = /[+-]?\d+/, // -inf - inf - // create the fontfill background - if (this.options.fontFill !== undefined && this.options.fontFill !== null && this.options.fontFill !== "none") { - ctx.fillStyle = this.options.fontFill; - ctx.fillRect(left, top, width, height); - } - - // draw text - ctx.fillStyle = this.options.fontColor || "black"; - ctx.textAlign = align || "center"; - ctx.textBaseline = baseline || "middle"; - if (this.options.fontStrokeWidth > 0){ - ctx.lineWidth = this.options.fontStrokeWidth; - ctx.strokeStyle = this.options.fontStrokeColor; - ctx.lineJoin = 'round'; - } - for (var i = 0; i < lineCount; i++) { - if(this.options.fontStrokeWidth){ - ctx.strokeText(lines[i], x, yLine); - } - ctx.fillText(lines[i], x, yLine); - yLine += fontSize; - } - } - }; + // iso 8601 regex + // 0000-00-00 0000-W00 or 0000-W00-0 + T + 00 or 00:00 or 00:00:00 or 00:00:00.000 + +00:00 or +0000 or +00) + isoRegex = /^\s*(?:[+-]\d{6}|\d{4})-(?:(\d\d-\d\d)|(W\d\d$)|(W\d\d-\d)|(\d\d\d))((T| )(\d\d(:\d\d(:\d\d(\.\d+)?)?)?)?([\+\-]\d\d(?::?\d\d)?|\s*Z)?)?$/, + isoFormat = 'YYYY-MM-DDTHH:mm:ssZ', - Node.prototype.getTextSize = function(ctx) { - if (this.label !== undefined) { - ctx.font = (this.selected ? "bold " : "") + this.options.fontSize + "px " + this.options.fontFace; + isoDates = [ + ['YYYYYY-MM-DD', /[+-]\d{6}-\d{2}-\d{2}/], + ['YYYY-MM-DD', /\d{4}-\d{2}-\d{2}/], + ['GGGG-[W]WW-E', /\d{4}-W\d{2}-\d/], + ['GGGG-[W]WW', /\d{4}-W\d{2}/], + ['YYYY-DDD', /\d{4}-\d{3}/] + ], - var lines = this.label.split('\n'), - height = (Number(this.options.fontSize) + 4) * lines.length, - width = 0; + // iso time formats and regexes + isoTimes = [ + ['HH:mm:ss.SSSS', /(T| )\d\d:\d\d:\d\d\.\d+/], + ['HH:mm:ss', /(T| )\d\d:\d\d:\d\d/], + ['HH:mm', /(T| )\d\d:\d\d/], + ['HH', /(T| )\d\d/] + ], - for (var i = 0, iMax = lines.length; i < iMax; i++) { - width = Math.max(width, ctx.measureText(lines[i]).width); - } + // timezone chunker '+10:00' > ['10', '00'] or '-1530' > ['-', '15', '30'] + parseTimezoneChunker = /([\+\-]|\d\d)/gi, - return {"width": width, "height": height, lineCount: lines.length}; - } - else { - return {"width": 0, "height": 0, lineCount: 0}; - } - }; + // getter and setter names + proxyGettersAndSetters = 'Date|Hours|Minutes|Seconds|Milliseconds'.split('|'), + unitMillisecondFactors = { + 'Milliseconds' : 1, + 'Seconds' : 1e3, + 'Minutes' : 6e4, + 'Hours' : 36e5, + 'Days' : 864e5, + 'Months' : 2592e6, + 'Years' : 31536e6 + }, - /** - * this is used to determine if a node is visible at all. this is used to determine when it needs to be drawn. - * there is a safety margin of 0.3 * width; - * - * @returns {boolean} - */ - Node.prototype.inArea = function() { - if (this.width !== undefined) { - return (this.x + this.width *this.networkScaleInv >= this.canvasTopLeft.x && - this.x - this.width *this.networkScaleInv < this.canvasBottomRight.x && - this.y + this.height*this.networkScaleInv >= this.canvasTopLeft.y && - this.y - this.height*this.networkScaleInv < this.canvasBottomRight.y); - } - else { - return true; - } - }; + unitAliases = { + ms : 'millisecond', + s : 'second', + m : 'minute', + h : 'hour', + d : 'day', + D : 'date', + w : 'week', + W : 'isoWeek', + M : 'month', + Q : 'quarter', + y : 'year', + DDD : 'dayOfYear', + e : 'weekday', + E : 'isoWeekday', + gg: 'weekYear', + GG: 'isoWeekYear' + }, - /** - * checks if the core of the node is in the display area, this is used for opening clusters around zoom - * @returns {boolean} - */ - Node.prototype.inView = function() { - return (this.x >= this.canvasTopLeft.x && - this.x < this.canvasBottomRight.x && - this.y >= this.canvasTopLeft.y && - this.y < this.canvasBottomRight.y); - }; + camelFunctions = { + dayofyear : 'dayOfYear', + isoweekday : 'isoWeekday', + isoweek : 'isoWeek', + weekyear : 'weekYear', + isoweekyear : 'isoWeekYear' + }, - /** - * This allows the zoom level of the network to influence the rendering - * We store the inverted scale and the coordinates of the top left, and bottom right points of the canvas - * - * @param scale - * @param canvasTopLeft - * @param canvasBottomRight - */ - Node.prototype.setScaleAndPos = function(scale,canvasTopLeft,canvasBottomRight) { - this.networkScaleInv = 1.0/scale; - this.networkScale = scale; - this.canvasTopLeft = canvasTopLeft; - this.canvasBottomRight = canvasBottomRight; - }; + // format function strings + formatFunctions = {}, + // default relative time thresholds + relativeTimeThresholds = { + s: 45, // seconds to minute + m: 45, // minutes to hour + h: 22, // hours to day + d: 26, // days to month + M: 11 // months to year + }, - /** - * This allows the zoom level of the network to influence the rendering - * - * @param scale - */ - Node.prototype.setScale = function(scale) { - this.networkScaleInv = 1.0/scale; - this.networkScale = scale; - }; + // tokens to ordinalize and pad + ordinalizeTokens = 'DDD w W M D d'.split(' '), + paddedTokens = 'M D H h m s w W'.split(' '), + formatTokenFunctions = { + M : function () { + return this.month() + 1; + }, + MMM : function (format) { + return this.localeData().monthsShort(this, format); + }, + MMMM : function (format) { + return this.localeData().months(this, format); + }, + D : function () { + return this.date(); + }, + DDD : function () { + return this.dayOfYear(); + }, + d : function () { + return this.day(); + }, + dd : function (format) { + return this.localeData().weekdaysMin(this, format); + }, + ddd : function (format) { + return this.localeData().weekdaysShort(this, format); + }, + dddd : function (format) { + return this.localeData().weekdays(this, format); + }, + w : function () { + return this.week(); + }, + W : function () { + return this.isoWeek(); + }, + YY : function () { + return leftZeroFill(this.year() % 100, 2); + }, + YYYY : function () { + return leftZeroFill(this.year(), 4); + }, + YYYYY : function () { + return leftZeroFill(this.year(), 5); + }, + YYYYYY : function () { + var y = this.year(), sign = y >= 0 ? '+' : '-'; + return sign + leftZeroFill(Math.abs(y), 6); + }, + gg : function () { + return leftZeroFill(this.weekYear() % 100, 2); + }, + gggg : function () { + return leftZeroFill(this.weekYear(), 4); + }, + ggggg : function () { + return leftZeroFill(this.weekYear(), 5); + }, + GG : function () { + return leftZeroFill(this.isoWeekYear() % 100, 2); + }, + GGGG : function () { + return leftZeroFill(this.isoWeekYear(), 4); + }, + GGGGG : function () { + return leftZeroFill(this.isoWeekYear(), 5); + }, + e : function () { + return this.weekday(); + }, + E : function () { + return this.isoWeekday(); + }, + a : function () { + return this.localeData().meridiem(this.hours(), this.minutes(), true); + }, + A : function () { + return this.localeData().meridiem(this.hours(), this.minutes(), false); + }, + H : function () { + return this.hours(); + }, + h : function () { + return this.hours() % 12 || 12; + }, + m : function () { + return this.minutes(); + }, + s : function () { + return this.seconds(); + }, + S : function () { + return toInt(this.milliseconds() / 100); + }, + SS : function () { + return leftZeroFill(toInt(this.milliseconds() / 10), 2); + }, + SSS : function () { + return leftZeroFill(this.milliseconds(), 3); + }, + SSSS : function () { + return leftZeroFill(this.milliseconds(), 3); + }, + Z : function () { + var a = this.utcOffset(), + b = '+'; + if (a < 0) { + a = -a; + b = '-'; + } + return b + leftZeroFill(toInt(a / 60), 2) + ':' + leftZeroFill(toInt(a) % 60, 2); + }, + ZZ : function () { + var a = this.utcOffset(), + b = '+'; + if (a < 0) { + a = -a; + b = '-'; + } + return b + leftZeroFill(toInt(a / 60), 2) + leftZeroFill(toInt(a) % 60, 2); + }, + z : function () { + return this.zoneAbbr(); + }, + zz : function () { + return this.zoneName(); + }, + x : function () { + return this.valueOf(); + }, + X : function () { + return this.unix(); + }, + Q : function () { + return this.quarter(); + } + }, + deprecations = {}, - /** - * set the velocity at 0. Is called when this node is contained in another during clustering - */ - Node.prototype.clearVelocity = function() { - this.vx = 0; - this.vy = 0; - }; + lists = ['months', 'monthsShort', 'weekdays', 'weekdaysShort', 'weekdaysMin'], + updateInProgress = false; - /** - * Basic preservation of (kinectic) energy - * - * @param massBeforeClustering - */ - Node.prototype.updateVelocity = function(massBeforeClustering) { - var energyBefore = this.vx * this.vx * massBeforeClustering; - //this.vx = (this.vx < 0) ? -Math.sqrt(energyBefore/this.options.mass) : Math.sqrt(energyBefore/this.options.mass); - this.vx = Math.sqrt(energyBefore/this.options.mass); - energyBefore = this.vy * this.vy * massBeforeClustering; - //this.vy = (this.vy < 0) ? -Math.sqrt(energyBefore/this.options.mass) : Math.sqrt(energyBefore/this.options.mass); - this.vy = Math.sqrt(energyBefore/this.options.mass); - }; + // Pick the first defined of two or three arguments. dfl comes from + // default. + function dfl(a, b, c) { + switch (arguments.length) { + case 2: return a != null ? a : b; + case 3: return a != null ? a : b != null ? b : c; + default: throw new Error('Implement me'); + } + } - module.exports = Node; + function hasOwnProp(a, b) { + return hasOwnProperty.call(a, b); + } + function defaultParsingFlags() { + // We need to deep clone this object, and es5 standard is not very + // helpful. + return { + empty : false, + unusedTokens : [], + unusedInput : [], + overflow : -2, + charsLeftOver : 0, + nullInput : false, + invalidMonth : null, + invalidFormat : false, + userInvalidated : false, + iso: false + }; + } -/***/ }, -/* 57 */ -/***/ function(module, exports, __webpack_require__) { + function printMsg(msg) { + if (moment.suppressDeprecationWarnings === false && + typeof console !== 'undefined' && console.warn) { + console.warn('Deprecation warning: ' + msg); + } + } - var util = __webpack_require__(1); - var Node = __webpack_require__(56); + function deprecate(msg, fn) { + var firstTime = true; + return extend(function () { + if (firstTime) { + printMsg(msg); + firstTime = false; + } + return fn.apply(this, arguments); + }, fn); + } - /** - * @class Edge - * - * A edge connects two nodes - * @param {Object} properties Object with properties. Must contain - * At least properties from and to. - * Available properties: 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 - */ - function Edge (properties, network, networkConstants) { - if (!network) { - throw "No network provided"; - } - var fields = ['edges','physics']; - var constants = util.selectiveBridgeObject(fields,networkConstants); - this.options = constants.edges; - this.physics = constants.physics; - this.options['smoothCurves'] = networkConstants['smoothCurves']; + function deprecateSimple(name, msg) { + if (!deprecations[name]) { + printMsg(msg); + deprecations[name] = true; + } + } + function padToken(func, count) { + return function (a) { + return leftZeroFill(func.call(this, a), count); + }; + } + function ordinalizeToken(func, period) { + return function (a) { + return this.localeData().ordinal(func.call(this, a), period); + }; + } - this.network = network; + function monthDiff(a, b) { + // difference in months + var wholeMonthDiff = ((b.year() - a.year()) * 12) + (b.month() - a.month()), + // b is in (anchor - 1 month, anchor + 1 month) + anchor = a.clone().add(wholeMonthDiff, 'months'), + anchor2, adjust; - // initialize variables - this.id = undefined; - this.fromId = undefined; - this.toId = undefined; - this.title = undefined; - this.widthSelected = this.options.width * this.options.widthSelectionMultiplier; - this.value = undefined; - this.selected = false; - this.hover = false; - this.labelDimensions = {top:0,left:0,width:0,height:0,yLine:0}; // could be cached - this.dirtyLabel = true; + if (b - anchor < 0) { + anchor2 = a.clone().add(wholeMonthDiff - 1, 'months'); + // linear across the month + adjust = (b - anchor) / (anchor - anchor2); + } else { + anchor2 = a.clone().add(wholeMonthDiff + 1, 'months'); + // linear across the month + adjust = (b - anchor) / (anchor2 - anchor); + } - this.from = null; // a node - this.to = null; // a node - this.via = null; // a temp node + return -(wholeMonthDiff + adjust); + } - this.fromBackup = null; // used to clean up after reconnect - this.toBackup = null;; // used to clean up after reconnect + while (ordinalizeTokens.length) { + i = ordinalizeTokens.pop(); + formatTokenFunctions[i + 'o'] = ordinalizeToken(formatTokenFunctions[i], i); + } + while (paddedTokens.length) { + i = paddedTokens.pop(); + formatTokenFunctions[i + i] = padToken(formatTokenFunctions[i], 2); + } + formatTokenFunctions.DDDD = padToken(formatTokenFunctions.DDD, 3); - // we use this to be able to reconnect the edge to a cluster if its node is put into a cluster - // by storing the original information we can revert to the original connection when the cluser is opened. - this.originalFromId = []; - this.originalToId = []; - this.connected = false; + function meridiemFixWrap(locale, hour, meridiem) { + var isPm; - this.widthFixed = false; - this.lengthFixed = false; + if (meridiem == null) { + // nothing to do + return hour; + } + if (locale.meridiemHour != null) { + return locale.meridiemHour(hour, meridiem); + } else if (locale.isPM != null) { + // Fallback + isPm = locale.isPM(meridiem); + if (isPm && hour < 12) { + hour += 12; + } + if (!isPm && hour === 12) { + hour = 0; + } + return hour; + } else { + // thie is not supposed to happen + return hour; + } + } - this.setProperties(properties); + /************************************ + Constructors + ************************************/ - this.controlNodesEnabled = false; - this.controlNodes = {from:null, to:null, positions:{}}; - this.connectedNode = null; - } + function Locale() { + } - /** - * Set or overwrite properties for the edge - * @param {Object} properties an object with properties - * @param {Object} constants and object with default, global properties - */ - Edge.prototype.setProperties = function(properties) { - if (!properties) { - return; - } + // Moment prototype object + function Moment(config, skipOverflow) { + if (skipOverflow !== false) { + checkOverflow(config); + } + copyConfig(this, config); + this._d = new Date(+config._d); + // Prevent infinite loop in case updateOffset creates new moment + // objects. + if (updateInProgress === false) { + updateInProgress = true; + moment.updateOffset(this); + updateInProgress = false; + } + } - var fields = ['style','fontSize','fontFace','fontColor','fontFill','fontStrokeWidth','fontStrokeColor','width', - 'widthSelectionMultiplier','hoverWidth','arrowScaleFactor','dash','inheritColor','labelAlignment' - ]; - util.selectiveDeepExtend(fields, this.options, properties); + // Duration Constructor + function Duration(duration) { + var normalizedInput = normalizeObjectUnits(duration), + years = normalizedInput.year || 0, + quarters = normalizedInput.quarter || 0, + months = normalizedInput.month || 0, + weeks = normalizedInput.week || 0, + days = normalizedInput.day || 0, + hours = normalizedInput.hour || 0, + minutes = normalizedInput.minute || 0, + seconds = normalizedInput.second || 0, + milliseconds = normalizedInput.millisecond || 0; - if (properties.from !== undefined) {this.fromId = properties.from;} - if (properties.to !== undefined) {this.toId = properties.to;} + // representation for dateAddRemove + this._milliseconds = +milliseconds + + seconds * 1e3 + // 1000 + minutes * 6e4 + // 1000 * 60 + hours * 36e5; // 1000 * 60 * 60 + // Because of dateAddRemove treats 24 hours as different from a + // day when working around DST, we need to store them separately + this._days = +days + + weeks * 7; + // It is impossible translate months into days without knowing + // which months you are are talking about, so we have to store + // it separately. + this._months = +months + + quarters * 3 + + years * 12; - if (properties.id !== undefined) {this.id = properties.id;} - if (properties.label !== undefined) {this.label = properties.label; this.dirtyLabel = true;} + this._data = {}; - if (properties.title !== undefined) {this.title = properties.title;} - if (properties.value !== undefined) {this.value = properties.value;} - if (properties.length !== undefined) {this.physics.springLength = properties.length;} + this._locale = moment.localeData(); - if (properties.color !== undefined) { - this.options.inheritColor = false; - if (util.isString(properties.color)) { - this.options.color.color = properties.color; - this.options.color.highlight = properties.color; + this._bubble(); } - else { - if (properties.color.color !== undefined) {this.options.color.color = properties.color.color;} - if (properties.color.highlight !== undefined) {this.options.color.highlight = properties.color.highlight;} - if (properties.color.hover !== undefined) {this.options.color.hover = properties.color.hover;} + + /************************************ + Helpers + ************************************/ + + + function extend(a, b) { + for (var i in b) { + if (hasOwnProp(b, i)) { + a[i] = b[i]; + } + } + + if (hasOwnProp(b, 'toString')) { + a.toString = b.toString; + } + + if (hasOwnProp(b, 'valueOf')) { + a.valueOf = b.valueOf; + } + + return a; } - } - // A node is connected when it has a from and to node. - this.connect(); + function copyConfig(to, from) { + var i, prop, val; - this.widthFixed = this.widthFixed || (properties.width !== undefined); - this.lengthFixed = this.lengthFixed || (properties.length !== undefined); + if (typeof from._isAMomentObject !== 'undefined') { + to._isAMomentObject = from._isAMomentObject; + } + if (typeof from._i !== 'undefined') { + to._i = from._i; + } + if (typeof from._f !== 'undefined') { + to._f = from._f; + } + if (typeof from._l !== 'undefined') { + to._l = from._l; + } + if (typeof from._strict !== 'undefined') { + to._strict = from._strict; + } + if (typeof from._tzm !== 'undefined') { + to._tzm = from._tzm; + } + if (typeof from._isUTC !== 'undefined') { + to._isUTC = from._isUTC; + } + if (typeof from._offset !== 'undefined') { + to._offset = from._offset; + } + if (typeof from._pf !== 'undefined') { + to._pf = from._pf; + } + if (typeof from._locale !== 'undefined') { + to._locale = from._locale; + } - this.widthSelected = this.options.width* this.options.widthSelectionMultiplier; + if (momentProperties.length > 0) { + for (i in momentProperties) { + prop = momentProperties[i]; + val = from[prop]; + if (typeof val !== 'undefined') { + to[prop] = val; + } + } + } - // set draw method based on style - switch (this.options.style) { - case 'line': this.draw = this._drawLine; break; - case 'arrow': this.draw = this._drawArrow; break; - case 'arrow-center': this.draw = this._drawArrowCenter; break; - case 'dash-line': this.draw = this._drawDashLine; break; - default: this.draw = this._drawLine; break; - } - - }; + return to; + } - /** - * Connect an edge to its nodes - */ - Edge.prototype.connect = function () { - this.disconnect(); + function absRound(number) { + if (number < 0) { + return Math.ceil(number); + } else { + return Math.floor(number); + } + } - this.from = this.network.nodes[this.fromId] || null; - this.to = this.network.nodes[this.toId] || null; - this.connected = (this.from && this.to); + // left zero fill a number + // see http://jsperf.com/left-zero-filling for performance comparison + function leftZeroFill(number, targetLength, forceSign) { + var output = '' + Math.abs(number), + sign = number >= 0; - if (this.connected) { - this.from.attachEdge(this); - this.to.attachEdge(this); - } - else { - if (this.from) { - this.from.detachEdge(this); + while (output.length < targetLength) { + output = '0' + output; + } + return (sign ? (forceSign ? '+' : '') : '-') + output; } - if (this.to) { - this.to.detachEdge(this); + + function positiveMomentsDifference(base, other) { + var res = {milliseconds: 0, months: 0}; + + res.months = other.month() - base.month() + + (other.year() - base.year()) * 12; + if (base.clone().add(res.months, 'M').isAfter(other)) { + --res.months; + } + + res.milliseconds = +other - +(base.clone().add(res.months, 'M')); + + return res; } - } - }; - /** - * Disconnect an edge from its nodes - */ - Edge.prototype.disconnect = function () { - if (this.from) { - this.from.detachEdge(this); - this.from = null; - } - if (this.to) { - this.to.detachEdge(this); - this.to = null; - } + function momentsDifference(base, other) { + var res; + other = makeAs(other, base); + if (base.isBefore(other)) { + res = positiveMomentsDifference(base, other); + } else { + res = positiveMomentsDifference(other, base); + res.milliseconds = -res.milliseconds; + res.months = -res.months; + } - this.connected = false; - }; + return res; + } - /** - * get the title of this edge. - * @return {string} title The title of the edge, or undefined when no title - * has been set. - */ - Edge.prototype.getTitle = function() { - return typeof this.title === "function" ? this.title() : this.title; - }; + // TODO: remove 'name' arg after deprecation is removed + function createAdder(direction, name) { + return function (val, period) { + var dur, tmp; + //invert the arguments, but complain about it + if (period !== null && !isNaN(+period)) { + deprecateSimple(name, 'moment().' + name + '(period, number) is deprecated. Please use moment().' + name + '(number, period).'); + tmp = val; val = period; period = tmp; + } + val = typeof val === 'string' ? +val : val; + dur = moment.duration(val, period); + addOrSubtractDurationFromMoment(this, dur, direction); + return this; + }; + } - /** - * Retrieve the value of the edge. Can be undefined - * @return {Number} value - */ - Edge.prototype.getValue = function() { - return this.value; - }; + function addOrSubtractDurationFromMoment(mom, duration, isAdding, updateOffset) { + var milliseconds = duration._milliseconds, + days = duration._days, + months = duration._months; + updateOffset = updateOffset == null ? true : updateOffset; - /** - * Adjust the value range of the edge. The edge will adjust it's width - * based on its value. - * @param {Number} min - * @param {Number} max - */ - Edge.prototype.setValueRange = function(min, max) { - if (!this.widthFixed && this.value !== undefined) { - var scale = (this.options.widthMax - this.options.widthMin) / (max - min); - this.options.width= (this.value - min) * scale + this.options.widthMin; - this.widthSelected = this.options.width* this.options.widthSelectionMultiplier; - } - }; + if (milliseconds) { + mom._d.setTime(+mom._d + milliseconds * isAdding); + } + if (days) { + rawSetter(mom, 'Date', rawGetter(mom, 'Date') + days * isAdding); + } + if (months) { + rawMonthSetter(mom, rawGetter(mom, 'Month') + months * isAdding); + } + if (updateOffset) { + moment.updateOffset(mom, days || months); + } + } - /** - * 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 - */ - Edge.prototype.draw = function(ctx) { - throw "Method draw not initialized in edge"; - }; + // check if is an array + function isArray(input) { + return Object.prototype.toString.call(input) === '[object Array]'; + } - /** - * 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 - */ - Edge.prototype.isOverlappingWith = function(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; + function isDate(input) { + return Object.prototype.toString.call(input) === '[object Date]' || + input instanceof Date; + } - var dist = this._getDistanceToEdge(xFrom, yFrom, xTo, yTo, xObj, yObj); + // compare two arrays, return the number of differences + function compareArrays(array1, array2, dontConvert) { + var len = Math.min(array1.length, array2.length), + lengthDiff = Math.abs(array1.length - array2.length), + diffs = 0, + i; + for (i = 0; i < len; i++) { + if ((dontConvert && array1[i] !== array2[i]) || + (!dontConvert && toInt(array1[i]) !== toInt(array2[i]))) { + diffs++; + } + } + return diffs + lengthDiff; + } - return (dist < distMax); - } - else { - return false - } - }; + function normalizeUnits(units) { + if (units) { + var lowered = units.toLowerCase().replace(/(.)s$/, '$1'); + units = unitAliases[units] || camelFunctions[lowered] || lowered; + } + return units; + } - Edge.prototype._getColor = function() { - var colorObj = this.options.color; - if (this.options.inheritColor == "to") { - colorObj = { - highlight: this.to.options.color.highlight.border, - hover: this.to.options.color.hover.border, - color: this.to.options.color.border - }; - } - else if (this.options.inheritColor == "from" || this.options.inheritColor == true) { - colorObj = { - highlight: this.from.options.color.highlight.border, - hover: this.from.options.color.hover.border, - color: this.from.options.color.border - }; - } + function normalizeObjectUnits(inputObject) { + var normalizedInput = {}, + normalizedProp, + prop; - if (this.selected == true) {return colorObj.highlight;} - else if (this.hover == true) {return colorObj.hover;} - else {return colorObj.color;} - }; + for (prop in inputObject) { + if (hasOwnProp(inputObject, prop)) { + normalizedProp = normalizeUnits(prop); + if (normalizedProp) { + normalizedInput[normalizedProp] = inputObject[prop]; + } + } + } + return normalizedInput; + } - /** - * Redraw a edge as a line - * Draw this edge in the given canvas - * The 2d context of a HTML canvas can be retrieved by canvas.getContext("2d"); - * @param {CanvasRenderingContext2D} ctx - * @private - */ - Edge.prototype._drawLine = function(ctx) { - // set style - ctx.strokeStyle = this._getColor(); - ctx.lineWidth = this._getLineWidth(); + function makeList(field) { + var count, setter; - if (this.from != this.to) { - // draw line - var via = this._line(ctx); + if (field.indexOf('week') === 0) { + count = 7; + setter = 'day'; + } + else if (field.indexOf('month') === 0) { + count = 12; + setter = 'month'; + } + else { + return; + } - // draw label - var point; - if (this.label) { - if (this.options.smoothCurves.enabled == true && via != null) { - var midpointX = 0.5*(0.5*(this.from.x + via.x) + 0.5*(this.to.x + via.x)); - var midpointY = 0.5*(0.5*(this.from.y + via.y) + 0.5*(this.to.y + via.y)); - point = {x:midpointX, y:midpointY}; - } - else { - point = this._pointOnLine(0.5); - } - this._label(ctx, this.label, point.x, point.y); + moment[field] = function (format, index) { + var i, getter, + method = moment._locale[field], + results = []; + + if (typeof format === 'number') { + index = format; + format = undefined; + } + + getter = function (i) { + var m = moment().utc().set(setter, i); + return method.call(moment._locale, m, format || ''); + }; + + if (index != null) { + return getter(index); + } + else { + for (i = 0; i < count; i++) { + results.push(getter(i)); + } + return results; + } + }; } - } - else { - var x, y; - var radius = this.physics.springLength / 4; - var node = this.from; - if (!node.width) { - node.resize(ctx); + + function toInt(argumentForCoercion) { + var coercedNumber = +argumentForCoercion, + value = 0; + + if (coercedNumber !== 0 && isFinite(coercedNumber)) { + if (coercedNumber >= 0) { + value = Math.floor(coercedNumber); + } else { + value = Math.ceil(coercedNumber); + } + } + + return value; } - if (node.width > node.height) { - x = node.x + node.width / 2; - y = node.y - radius; + + function daysInMonth(year, month) { + return new Date(Date.UTC(year, month + 1, 0)).getUTCDate(); } - else { - x = node.x + radius; - y = node.y - node.height / 2; + + function weeksInYear(year, dow, doy) { + return weekOfYear(moment([year, 11, 31 + dow - doy]), dow, doy).week; } - this._circle(ctx, x, y, radius); - point = this._pointOnCircle(x, y, radius, 0.5); - this._label(ctx, this.label, point.x, point.y); - } - }; - /** - * Get the line width of the edge. Depends on width and whether one of the - * connected nodes is selected. - * @return {Number} width - * @private - */ - Edge.prototype._getLineWidth = function() { - if (this.selected == true) { - return Math.max(Math.min(this.widthSelected, this.options.widthMax), 0.3*this.networkScaleInv); - } - else { - if (this.hover == true) { - return Math.max(Math.min(this.options.hoverWidth, this.options.widthMax), 0.3*this.networkScaleInv); + function daysInYear(year) { + return isLeapYear(year) ? 366 : 365; } - else { - return Math.max(this.options.width, 0.3*this.networkScaleInv); + + function isLeapYear(year) { + return (year % 4 === 0 && year % 100 !== 0) || year % 400 === 0; } - } - }; - Edge.prototype._getViaCoordinates = function () { - if (this.options.smoothCurves.dynamic == true && this.options.smoothCurves.enabled == true ) { - return this.via; - } - else if (this.options.smoothCurves.enabled == false) { - return {x:0,y:0}; - } - else { - var xVia = null; - var yVia = null; - var factor = this.options.smoothCurves.roundness; - var type = this.options.smoothCurves.type; + function checkOverflow(m) { + var overflow; + if (m._a && m._pf.overflow === -2) { + overflow = + m._a[MONTH] < 0 || m._a[MONTH] > 11 ? MONTH : + m._a[DATE] < 1 || m._a[DATE] > daysInMonth(m._a[YEAR], m._a[MONTH]) ? DATE : + m._a[HOUR] < 0 || m._a[HOUR] > 24 || + (m._a[HOUR] === 24 && (m._a[MINUTE] !== 0 || + m._a[SECOND] !== 0 || + m._a[MILLISECOND] !== 0)) ? HOUR : + m._a[MINUTE] < 0 || m._a[MINUTE] > 59 ? MINUTE : + m._a[SECOND] < 0 || m._a[SECOND] > 59 ? SECOND : + m._a[MILLISECOND] < 0 || m._a[MILLISECOND] > 999 ? MILLISECOND : + -1; - var dx = Math.abs(this.from.x - this.to.x); - var dy = Math.abs(this.from.y - this.to.y); - if (type == 'discrete' || type == 'diagonalCross') { - if (Math.abs(this.from.x - this.to.x) < Math.abs(this.from.y - this.to.y)) { - if (this.from.y > this.to.y) { - if (this.from.x < this.to.x) { - xVia = this.from.x + factor * dy; - yVia = this.from.y - factor * dy; - } - else if (this.from.x > this.to.x) { - xVia = this.from.x - factor * dy; - yVia = this.from.y - factor * dy; - } + if (m._pf._overflowDayOfYear && (overflow < YEAR || overflow > DATE)) { + overflow = DATE; + } + + m._pf.overflow = overflow; } - else if (this.from.y < this.to.y) { - if (this.from.x < this.to.x) { - xVia = this.from.x + factor * dy; - yVia = this.from.y + factor * dy; - } - else if (this.from.x > this.to.x) { - xVia = this.from.x - factor * dy; - yVia = this.from.y + factor * dy; - } + } + + function isValid(m) { + if (m._isValid == null) { + m._isValid = !isNaN(m._d.getTime()) && + m._pf.overflow < 0 && + !m._pf.empty && + !m._pf.invalidMonth && + !m._pf.nullInput && + !m._pf.invalidFormat && + !m._pf.userInvalidated; + + if (m._strict) { + m._isValid = m._isValid && + m._pf.charsLeftOver === 0 && + m._pf.unusedTokens.length === 0 && + m._pf.bigHour === undefined; + } + } + return m._isValid; + } + + function normalizeLocale(key) { + return key ? key.toLowerCase().replace('_', '-') : key; + } + + // pick the locale from the array + // try ['en-au', 'en-gb'] as 'en-au', 'en-gb', 'en', as in move through the list trying each + // substring from most specific to least, but move to the next array item if it's a more specific variant than the current root + function chooseLocale(names) { + var i = 0, j, next, locale, split; + + while (i < names.length) { + split = normalizeLocale(names[i]).split('-'); + j = split.length; + next = normalizeLocale(names[i + 1]); + next = next ? next.split('-') : null; + while (j > 0) { + locale = loadLocale(split.slice(0, j).join('-')); + if (locale) { + return locale; + } + if (next && next.length >= j && compareArrays(split, next, true) >= j - 1) { + //the next array item is better than a shallower substring of this one + break; + } + j--; + } + i++; + } + return null; + } + + function loadLocale(name) { + var oldLocale = null; + if (!locales[name] && hasModule) { + try { + oldLocale = moment.locale(); + !(function webpackMissingModule() { var e = new Error("Cannot find module \"./locale\""); e.code = 'MODULE_NOT_FOUND'; throw e; }()); + // because defineLocale currently also sets the global locale, we want to undo that for lazy loaded locales + moment.locale(oldLocale); + } catch (e) { } + } + return locales[name]; + } + + // Return a moment from input, that is local/utc/utcOffset equivalent to + // model. + function makeAs(input, model) { + var res, diff; + if (model._isUTC) { + res = model.clone(); + diff = (moment.isMoment(input) || isDate(input) ? + +input : +moment(input)) - (+res); + // Use low-level api, because this fn is low-level api. + res._d.setTime(+res._d + diff); + moment.updateOffset(res, false); + return res; + } else { + return moment(input).local(); + } + } + + /************************************ + Locale + ************************************/ + + + extend(Locale.prototype, { + + set : function (config) { + var prop, i; + for (i in config) { + prop = config[i]; + if (typeof prop === 'function') { + this[i] = prop; + } else { + this['_' + i] = prop; + } + } + // Lenient ordinal parsing accepts just a number in addition to + // number + (possibly) stuff coming from _ordinalParseLenient. + this._ordinalParseLenient = new RegExp(this._ordinalParse.source + '|' + /\d{1,2}/.source); + }, + + _months : 'January_February_March_April_May_June_July_August_September_October_November_December'.split('_'), + months : function (m) { + return this._months[m.month()]; + }, + + _monthsShort : 'Jan_Feb_Mar_Apr_May_Jun_Jul_Aug_Sep_Oct_Nov_Dec'.split('_'), + monthsShort : function (m) { + return this._monthsShort[m.month()]; + }, + + monthsParse : function (monthName, format, strict) { + var i, mom, regex; + + if (!this._monthsParse) { + this._monthsParse = []; + this._longMonthsParse = []; + this._shortMonthsParse = []; + } + + for (i = 0; i < 12; i++) { + // make the regex if we don't have it already + mom = moment.utc([2000, i]); + if (strict && !this._longMonthsParse[i]) { + this._longMonthsParse[i] = new RegExp('^' + this.months(mom, '').replace('.', '') + '$', 'i'); + this._shortMonthsParse[i] = new RegExp('^' + this.monthsShort(mom, '').replace('.', '') + '$', 'i'); + } + if (!strict && !this._monthsParse[i]) { + regex = '^' + this.months(mom, '') + '|^' + this.monthsShort(mom, ''); + this._monthsParse[i] = new RegExp(regex.replace('.', ''), 'i'); + } + // test the regex + if (strict && format === 'MMMM' && this._longMonthsParse[i].test(monthName)) { + return i; + } else if (strict && format === 'MMM' && this._shortMonthsParse[i].test(monthName)) { + return i; + } else if (!strict && this._monthsParse[i].test(monthName)) { + return i; + } + } + }, + + _weekdays : 'Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday'.split('_'), + weekdays : function (m) { + return this._weekdays[m.day()]; + }, + + _weekdaysShort : 'Sun_Mon_Tue_Wed_Thu_Fri_Sat'.split('_'), + weekdaysShort : function (m) { + return this._weekdaysShort[m.day()]; + }, + + _weekdaysMin : 'Su_Mo_Tu_We_Th_Fr_Sa'.split('_'), + weekdaysMin : function (m) { + return this._weekdaysMin[m.day()]; + }, + + weekdaysParse : function (weekdayName) { + var i, mom, regex; + + if (!this._weekdaysParse) { + this._weekdaysParse = []; + } + + for (i = 0; i < 7; i++) { + // make the regex if we don't have it already + if (!this._weekdaysParse[i]) { + mom = moment([2000, 1]).day(i); + regex = '^' + this.weekdays(mom, '') + '|^' + this.weekdaysShort(mom, '') + '|^' + this.weekdaysMin(mom, ''); + this._weekdaysParse[i] = new RegExp(regex.replace('.', ''), 'i'); + } + // test the regex + if (this._weekdaysParse[i].test(weekdayName)) { + return i; + } + } + }, + + _longDateFormat : { + LTS : 'h:mm:ss A', + LT : 'h:mm A', + L : 'MM/DD/YYYY', + LL : 'MMMM D, YYYY', + LLL : 'MMMM D, YYYY LT', + LLLL : 'dddd, MMMM D, YYYY LT' + }, + longDateFormat : function (key) { + var output = this._longDateFormat[key]; + if (!output && this._longDateFormat[key.toUpperCase()]) { + output = this._longDateFormat[key.toUpperCase()].replace(/MMMM|MM|DD|dddd/g, function (val) { + return val.slice(1); + }); + this._longDateFormat[key] = output; + } + return output; + }, + + isPM : function (input) { + // IE8 Quirks Mode & IE7 Standards Mode do not allow accessing strings like arrays + // Using charAt should be more compatible. + return ((input + '').toLowerCase().charAt(0) === 'p'); + }, + + _meridiemParse : /[ap]\.?m?\.?/i, + meridiem : function (hours, minutes, isLower) { + if (hours > 11) { + return isLower ? 'pm' : 'PM'; + } else { + return isLower ? 'am' : 'AM'; + } + }, + + + _calendar : { + sameDay : '[Today at] LT', + nextDay : '[Tomorrow at] LT', + nextWeek : 'dddd [at] LT', + lastDay : '[Yesterday at] LT', + lastWeek : '[Last] dddd [at] LT', + sameElse : 'L' + }, + calendar : function (key, mom, now) { + var output = this._calendar[key]; + return typeof output === 'function' ? output.apply(mom, [now]) : output; + }, + + _relativeTime : { + future : 'in %s', + past : '%s ago', + s : 'a few seconds', + m : 'a minute', + mm : '%d minutes', + h : 'an hour', + hh : '%d hours', + d : 'a day', + dd : '%d days', + M : 'a month', + MM : '%d months', + y : 'a year', + yy : '%d years' + }, + + relativeTime : function (number, withoutSuffix, string, isFuture) { + var output = this._relativeTime[string]; + return (typeof output === 'function') ? + output(number, withoutSuffix, string, isFuture) : + output.replace(/%d/i, number); + }, + + pastFuture : function (diff, output) { + var format = this._relativeTime[diff > 0 ? 'future' : 'past']; + return typeof format === 'function' ? format(output) : format.replace(/%s/i, output); + }, + + ordinal : function (number) { + return this._ordinal.replace('%d', number); + }, + _ordinal : '%d', + _ordinalParse : /\d{1,2}/, + + preparse : function (string) { + return string; + }, + + postformat : function (string) { + return string; + }, + + week : function (mom) { + return weekOfYear(mom, this._week.dow, this._week.doy).week; + }, + + _week : { + dow : 0, // Sunday is the first day of the week. + doy : 6 // The week that contains Jan 1st is the first week of the year. + }, + + firstDayOfWeek : function () { + return this._week.dow; + }, + + firstDayOfYear : function () { + return this._week.doy; + }, + + _invalidDate: 'Invalid date', + invalidDate: function () { + return this._invalidDate; + } + }); + + /************************************ + Formatting + ************************************/ + + + function removeFormattingTokens(input) { + if (input.match(/\[[\s\S]/)) { + return input.replace(/^\[|\]$/g, ''); + } + return input.replace(/\\/g, ''); + } + + function makeFormatFunction(format) { + var array = format.match(formattingTokens), i, length; + + for (i = 0, length = array.length; i < length; i++) { + if (formatTokenFunctions[array[i]]) { + array[i] = formatTokenFunctions[array[i]]; + } else { + array[i] = removeFormattingTokens(array[i]); + } + } + + return function (mom) { + var output = ''; + for (i = 0; i < length; i++) { + output += array[i] instanceof Function ? array[i].call(mom, format) : array[i]; + } + return output; + }; + } + + // format date using native date object + function formatMoment(m, format) { + if (!m.isValid()) { + return m.localeData().invalidDate(); + } + + format = expandFormat(format, m.localeData()); + + if (!formatFunctions[format]) { + formatFunctions[format] = makeFormatFunction(format); + } + + return formatFunctions[format](m); + } + + function expandFormat(format, locale) { + var i = 5; + + function replaceLongDateFormatTokens(input) { + return locale.longDateFormat(input) || input; + } + + localFormattingTokens.lastIndex = 0; + while (i >= 0 && localFormattingTokens.test(format)) { + format = format.replace(localFormattingTokens, replaceLongDateFormatTokens); + localFormattingTokens.lastIndex = 0; + i -= 1; + } + + return format; + } + + + /************************************ + Parsing + ************************************/ + + + // get the regex to find the next token + function getParseRegexForToken(token, config) { + var a, strict = config._strict; + switch (token) { + case 'Q': + return parseTokenOneDigit; + case 'DDDD': + return parseTokenThreeDigits; + case 'YYYY': + case 'GGGG': + case 'gggg': + return strict ? parseTokenFourDigits : parseTokenOneToFourDigits; + case 'Y': + case 'G': + case 'g': + return parseTokenSignedNumber; + case 'YYYYYY': + case 'YYYYY': + case 'GGGGG': + case 'ggggg': + return strict ? parseTokenSixDigits : parseTokenOneToSixDigits; + case 'S': + if (strict) { + return parseTokenOneDigit; + } + /* falls through */ + case 'SS': + if (strict) { + return parseTokenTwoDigits; + } + /* falls through */ + case 'SSS': + if (strict) { + return parseTokenThreeDigits; + } + /* falls through */ + case 'DDD': + return parseTokenOneToThreeDigits; + case 'MMM': + case 'MMMM': + case 'dd': + case 'ddd': + case 'dddd': + return parseTokenWord; + case 'a': + case 'A': + return config._locale._meridiemParse; + case 'x': + return parseTokenOffsetMs; + case 'X': + return parseTokenTimestampMs; + case 'Z': + case 'ZZ': + return parseTokenTimezone; + case 'T': + return parseTokenT; + case 'SSSS': + return parseTokenDigits; + case 'MM': + case 'DD': + case 'YY': + case 'GG': + case 'gg': + case 'HH': + case 'hh': + case 'mm': + case 'ss': + case 'ww': + case 'WW': + return strict ? parseTokenTwoDigits : parseTokenOneOrTwoDigits; + case 'M': + case 'D': + case 'd': + case 'H': + case 'h': + case 'm': + case 's': + case 'w': + case 'W': + case 'e': + case 'E': + return parseTokenOneOrTwoDigits; + case 'Do': + return strict ? config._locale._ordinalParse : config._locale._ordinalParseLenient; + default : + a = new RegExp(regexpEscape(unescapeFormat(token.replace('\\', '')), 'i')); + return a; + } + } + + function utcOffsetFromString(string) { + string = string || ''; + var possibleTzMatches = (string.match(parseTokenTimezone) || []), + tzChunk = possibleTzMatches[possibleTzMatches.length - 1] || [], + parts = (tzChunk + '').match(parseTimezoneChunker) || ['-', 0, 0], + minutes = +(parts[1] * 60) + toInt(parts[2]); + + return parts[0] === '+' ? minutes : -minutes; + } + + // function to convert string input to date + function addTimeToArrayFromToken(token, input, config) { + var a, datePartArray = config._a; + + switch (token) { + // QUARTER + case 'Q': + if (input != null) { + datePartArray[MONTH] = (toInt(input) - 1) * 3; + } + break; + // MONTH + case 'M' : // fall through to MM + case 'MM' : + if (input != null) { + datePartArray[MONTH] = toInt(input) - 1; + } + break; + case 'MMM' : // fall through to MMMM + case 'MMMM' : + a = config._locale.monthsParse(input, token, config._strict); + // if we didn't find a month name, mark the date as invalid. + if (a != null) { + datePartArray[MONTH] = a; + } else { + config._pf.invalidMonth = input; + } + break; + // DAY OF MONTH + case 'D' : // fall through to DD + case 'DD' : + if (input != null) { + datePartArray[DATE] = toInt(input); + } + break; + case 'Do' : + if (input != null) { + datePartArray[DATE] = toInt(parseInt( + input.match(/\d{1,2}/)[0], 10)); + } + break; + // DAY OF YEAR + case 'DDD' : // fall through to DDDD + case 'DDDD' : + if (input != null) { + config._dayOfYear = toInt(input); + } + + break; + // YEAR + case 'YY' : + datePartArray[YEAR] = moment.parseTwoDigitYear(input); + break; + case 'YYYY' : + case 'YYYYY' : + case 'YYYYYY' : + datePartArray[YEAR] = toInt(input); + break; + // AM / PM + case 'a' : // fall through to A + case 'A' : + config._meridiem = input; + // config._isPm = config._locale.isPM(input); + break; + // HOUR + case 'h' : // fall through to hh + case 'hh' : + config._pf.bigHour = true; + /* falls through */ + case 'H' : // fall through to HH + case 'HH' : + datePartArray[HOUR] = toInt(input); + break; + // MINUTE + case 'm' : // fall through to mm + case 'mm' : + datePartArray[MINUTE] = toInt(input); + break; + // SECOND + case 's' : // fall through to ss + case 'ss' : + datePartArray[SECOND] = toInt(input); + break; + // MILLISECOND + case 'S' : + case 'SS' : + case 'SSS' : + case 'SSSS' : + datePartArray[MILLISECOND] = toInt(('0.' + input) * 1000); + break; + // UNIX OFFSET (MILLISECONDS) + case 'x': + config._d = new Date(toInt(input)); + break; + // UNIX TIMESTAMP WITH MS + case 'X': + config._d = new Date(parseFloat(input) * 1000); + break; + // TIMEZONE + case 'Z' : // fall through to ZZ + case 'ZZ' : + config._useUTC = true; + config._tzm = utcOffsetFromString(input); + break; + // WEEKDAY - human + case 'dd': + case 'ddd': + case 'dddd': + a = config._locale.weekdaysParse(input); + // if we didn't get a weekday name, mark the date as invalid + if (a != null) { + config._w = config._w || {}; + config._w['d'] = a; + } else { + config._pf.invalidWeekday = input; + } + break; + // WEEK, WEEK DAY - numeric + case 'w': + case 'ww': + case 'W': + case 'WW': + case 'd': + case 'e': + case 'E': + token = token.substr(0, 1); + /* falls through */ + case 'gggg': + case 'GGGG': + case 'GGGGG': + token = token.substr(0, 2); + if (input) { + config._w = config._w || {}; + config._w[token] = toInt(input); + } + break; + case 'gg': + case 'GG': + config._w = config._w || {}; + config._w[token] = moment.parseTwoDigitYear(input); + } + } + + function dayOfYearFromWeekInfo(config) { + var w, weekYear, week, weekday, dow, doy, temp; + + w = config._w; + if (w.GG != null || w.W != null || w.E != null) { + dow = 1; + doy = 4; + + // TODO: We need to take the current isoWeekYear, but that depends on + // how we interpret now (local, utc, fixed offset). So create + // a now version of current config (take local/utc/offset flags, and + // create now). + weekYear = dfl(w.GG, config._a[YEAR], weekOfYear(moment(), 1, 4).year); + week = dfl(w.W, 1); + weekday = dfl(w.E, 1); + } else { + dow = config._locale._week.dow; + doy = config._locale._week.doy; + + weekYear = dfl(w.gg, config._a[YEAR], weekOfYear(moment(), dow, doy).year); + week = dfl(w.w, 1); + + if (w.d != null) { + // weekday -- low day numbers are considered next week + weekday = w.d; + if (weekday < dow) { + ++week; + } + } else if (w.e != null) { + // local weekday -- counting starts from begining of week + weekday = w.e + dow; + } else { + // default to begining of week + weekday = dow; + } + } + temp = dayOfYearFromWeeks(weekYear, week, weekday, doy, dow); + + config._a[YEAR] = temp.year; + config._dayOfYear = temp.dayOfYear; + } + + // convert an array to a date. + // the array should mirror the parameters below + // note: all values past the year are optional and will default to the lowest possible value. + // [year, month, day , hour, minute, second, millisecond] + function dateFromConfig(config) { + var i, date, input = [], currentDate, yearToUse; + + if (config._d) { + return; + } + + currentDate = currentDateArray(config); + + //compute day of the year from weeks and weekdays + if (config._w && config._a[DATE] == null && config._a[MONTH] == null) { + dayOfYearFromWeekInfo(config); + } + + //if the day of the year is set, figure out what it is + if (config._dayOfYear) { + yearToUse = dfl(config._a[YEAR], currentDate[YEAR]); + + if (config._dayOfYear > daysInYear(yearToUse)) { + config._pf._overflowDayOfYear = true; + } + + date = makeUTCDate(yearToUse, 0, config._dayOfYear); + config._a[MONTH] = date.getUTCMonth(); + config._a[DATE] = date.getUTCDate(); + } + + // Default to current date. + // * if no year, month, day of month are given, default to today + // * if day of month is given, default month and year + // * if month is given, default only year + // * if year is given, don't default anything + for (i = 0; i < 3 && config._a[i] == null; ++i) { + config._a[i] = input[i] = currentDate[i]; + } + + // Zero out whatever was not defaulted, including time + for (; i < 7; i++) { + config._a[i] = input[i] = (config._a[i] == null) ? (i === 2 ? 1 : 0) : config._a[i]; + } + + // Check for 24:00:00.000 + if (config._a[HOUR] === 24 && + config._a[MINUTE] === 0 && + config._a[SECOND] === 0 && + config._a[MILLISECOND] === 0) { + config._nextDay = true; + config._a[HOUR] = 0; } - if (type == "discrete") { - xVia = dx < factor * dy ? this.from.x : xVia; + + config._d = (config._useUTC ? makeUTCDate : makeDate).apply(null, input); + // Apply timezone offset from input. The actual utcOffset can be changed + // with parseZone. + if (config._tzm != null) { + config._d.setUTCMinutes(config._d.getUTCMinutes() - config._tzm); } - } - else if (Math.abs(this.from.x - this.to.x) > Math.abs(this.from.y - this.to.y)) { - if (this.from.y > this.to.y) { - if (this.from.x < this.to.x) { - xVia = this.from.x + factor * dx; - yVia = this.from.y - factor * dx; - } - else if (this.from.x > this.to.x) { - xVia = this.from.x - factor * dx; - yVia = this.from.y - factor * dx; - } + + if (config._nextDay) { + config._a[HOUR] = 24; } - else if (this.from.y < this.to.y) { - if (this.from.x < this.to.x) { - xVia = this.from.x + factor * dx; - yVia = this.from.y + factor * dx; - } - else if (this.from.x > this.to.x) { - xVia = this.from.x - factor * dx; - yVia = this.from.y + factor * dx; - } + } + + function dateFromObject(config) { + var normalizedInput; + + if (config._d) { + return; } - if (type == "discrete") { - yVia = dy < factor * dx ? this.from.y : yVia; + + normalizedInput = normalizeObjectUnits(config._i); + config._a = [ + normalizedInput.year, + normalizedInput.month, + normalizedInput.day || normalizedInput.date, + normalizedInput.hour, + normalizedInput.minute, + normalizedInput.second, + normalizedInput.millisecond + ]; + + dateFromConfig(config); + } + + function currentDateArray(config) { + var now = new Date(); + if (config._useUTC) { + return [ + now.getUTCFullYear(), + now.getUTCMonth(), + now.getUTCDate() + ]; + } else { + return [now.getFullYear(), now.getMonth(), now.getDate()]; } - } } - else if (type == "straightCross") { - if (Math.abs(this.from.x - this.to.x) < Math.abs(this.from.y - this.to.y)) { // up - down - xVia = this.from.x; - if (this.from.y < this.to.y) { - yVia = this.to.y - (1 - factor) * dy; + + // date from string and format string + function makeDateFromStringAndFormat(config) { + if (config._f === moment.ISO_8601) { + parseISO(config); + return; } - else { - yVia = this.to.y + (1 - factor) * dy; + + config._a = []; + config._pf.empty = true; + + // This array is used to make a Date, either with `new Date` or `Date.UTC` + var string = '' + config._i, + i, parsedInput, tokens, token, skipped, + stringLength = string.length, + totalParsedInputLength = 0; + + tokens = expandFormat(config._f, config._locale).match(formattingTokens) || []; + + for (i = 0; i < tokens.length; i++) { + token = tokens[i]; + parsedInput = (string.match(getParseRegexForToken(token, config)) || [])[0]; + if (parsedInput) { + skipped = string.substr(0, string.indexOf(parsedInput)); + if (skipped.length > 0) { + config._pf.unusedInput.push(skipped); + } + string = string.slice(string.indexOf(parsedInput) + parsedInput.length); + totalParsedInputLength += parsedInput.length; + } + // don't parse if it's not a known token + if (formatTokenFunctions[token]) { + if (parsedInput) { + config._pf.empty = false; + } + else { + config._pf.unusedTokens.push(token); + } + addTimeToArrayFromToken(token, parsedInput, config); + } + else if (config._strict && !parsedInput) { + config._pf.unusedTokens.push(token); + } } - } - else if (Math.abs(this.from.x - this.to.x) > Math.abs(this.from.y - this.to.y)) { // left - right - if (this.from.x < this.to.x) { - xVia = this.to.x - (1 - factor) * dx; + + // add remaining unparsed input length to the string + config._pf.charsLeftOver = stringLength - totalParsedInputLength; + if (string.length > 0) { + config._pf.unusedInput.push(string); } - else { - xVia = this.to.x + (1 - factor) * dx; + + // clear _12h flag if hour is <= 12 + if (config._pf.bigHour === true && config._a[HOUR] <= 12) { + config._pf.bigHour = undefined; } - yVia = this.from.y; - } + // handle meridiem + config._a[HOUR] = meridiemFixWrap(config._locale, config._a[HOUR], + config._meridiem); + dateFromConfig(config); + checkOverflow(config); } - else if (type == 'horizontal') { - if (this.from.x < this.to.x) { - xVia = this.to.x - (1 - factor) * dx; - } - else { - xVia = this.to.x + (1 - factor) * dx; - } - yVia = this.from.y; + + function unescapeFormat(s) { + return s.replace(/\\(\[)|\\(\])|\[([^\]\[]*)\]|\\(.)/g, function (matched, p1, p2, p3, p4) { + return p1 || p2 || p3 || p4; + }); } - else if (type == 'vertical') { - xVia = this.from.x; - if (this.from.y < this.to.y) { - yVia = this.to.y - (1 - factor) * dy; - } - else { - yVia = this.to.y + (1 - factor) * dy; - } + + // Code from http://stackoverflow.com/questions/3561493/is-there-a-regexp-escape-function-in-javascript + function regexpEscape(s) { + return s.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&'); } - else { // continuous - if (Math.abs(this.from.x - this.to.x) < Math.abs(this.from.y - this.to.y)) { - if (this.from.y > this.to.y) { - if (this.from.x < this.to.x) { - // console.log(1) - xVia = this.from.x + factor * dy; - yVia = this.from.y - factor * dy; - xVia = this.to.x < xVia ? this.to.x : xVia; - } - else if (this.from.x > this.to.x) { - // console.log(2) - xVia = this.from.x - factor * dy; - yVia = this.from.y - factor * dy; - xVia = this.to.x > xVia ? this.to.x : xVia; - } + + // date from string and array of format strings + function makeDateFromStringAndArray(config) { + var tempConfig, + bestMoment, + + scoreToBeat, + i, + currentScore; + + if (config._f.length === 0) { + config._pf.invalidFormat = true; + config._d = new Date(NaN); + return; } - else if (this.from.y < this.to.y) { - if (this.from.x < this.to.x) { - // console.log(3) - xVia = this.from.x + factor * dy; - yVia = this.from.y + factor * dy; - xVia = this.to.x < xVia ? this.to.x : xVia; - } - else if (this.from.x > this.to.x) { - // console.log(4, this.from.x, this.to.x) - xVia = this.from.x - factor * dy; - yVia = this.from.y + factor * dy; - xVia = this.to.x > xVia ? this.to.x : xVia; - } + + for (i = 0; i < config._f.length; i++) { + currentScore = 0; + tempConfig = copyConfig({}, config); + if (config._useUTC != null) { + tempConfig._useUTC = config._useUTC; + } + tempConfig._pf = defaultParsingFlags(); + tempConfig._f = config._f[i]; + makeDateFromStringAndFormat(tempConfig); + + if (!isValid(tempConfig)) { + continue; + } + + // if there is any input that was not parsed add a penalty for that format + currentScore += tempConfig._pf.charsLeftOver; + + //or tokens + currentScore += tempConfig._pf.unusedTokens.length * 10; + + tempConfig._pf.score = currentScore; + + if (scoreToBeat == null || currentScore < scoreToBeat) { + scoreToBeat = currentScore; + bestMoment = tempConfig; + } } - } - else if (Math.abs(this.from.x - this.to.x) > Math.abs(this.from.y - this.to.y)) { - if (this.from.y > this.to.y) { - if (this.from.x < this.to.x) { - // console.log(5) - xVia = this.from.x + factor * dx; - yVia = this.from.y - factor * dx; - yVia = this.to.y > yVia ? this.to.y : yVia; - } - else if (this.from.x > this.to.x) { - // console.log(6) - xVia = this.from.x - factor * dx; - yVia = this.from.y - factor * dx; - yVia = this.to.y > yVia ? this.to.y : yVia; - } + + extend(config, bestMoment || tempConfig); + } + + // date from iso format + function parseISO(config) { + var i, l, + string = config._i, + match = isoRegex.exec(string); + + if (match) { + config._pf.iso = true; + for (i = 0, l = isoDates.length; i < l; i++) { + if (isoDates[i][1].exec(string)) { + // match[5] should be 'T' or undefined + config._f = isoDates[i][0] + (match[6] || ' '); + break; + } + } + for (i = 0, l = isoTimes.length; i < l; i++) { + if (isoTimes[i][1].exec(string)) { + config._f += isoTimes[i][0]; + break; + } + } + if (string.match(parseTokenTimezone)) { + config._f += 'Z'; + } + makeDateFromStringAndFormat(config); + } else { + config._isValid = false; } - else if (this.from.y < this.to.y) { - if (this.from.x < this.to.x) { - // console.log(7) - xVia = this.from.x + factor * dx; - yVia = this.from.y + factor * dx; - yVia = this.to.y < yVia ? this.to.y : yVia; - } - else if (this.from.x > this.to.x) { - // console.log(8) - xVia = this.from.x - factor * dx; - yVia = this.from.y + factor * dx; - yVia = this.to.y < yVia ? this.to.y : yVia; - } + } + + // date from iso format or fallback + function makeDateFromString(config) { + parseISO(config); + if (config._isValid === false) { + delete config._isValid; + moment.createFromInputFallback(config); } - } } + function map(arr, fn) { + var res = [], i; + for (i = 0; i < arr.length; ++i) { + res.push(fn(arr[i], i)); + } + return res; + } - return {x: xVia, y: yVia}; - } - }; + function makeDateFromInput(config) { + var input = config._i, matched; + if (input === undefined) { + config._d = new Date(); + } else if (isDate(input)) { + config._d = new Date(+input); + } else if ((matched = aspNetJsonRegex.exec(input)) !== null) { + config._d = new Date(+matched[1]); + } else if (typeof input === 'string') { + makeDateFromString(config); + } else if (isArray(input)) { + config._a = map(input.slice(0), function (obj) { + return parseInt(obj, 10); + }); + dateFromConfig(config); + } else if (typeof(input) === 'object') { + dateFromObject(config); + } else if (typeof(input) === 'number') { + // from milliseconds + config._d = new Date(input); + } else { + moment.createFromInputFallback(config); + } + } - /** - * Draw a line between two nodes - * @param {CanvasRenderingContext2D} ctx - * @private - */ - Edge.prototype._line = function (ctx) { - // draw a straight line - ctx.beginPath(); - ctx.moveTo(this.from.x, this.from.y); - if (this.options.smoothCurves.enabled == true) { - if (this.options.smoothCurves.dynamic == false) { - var via = this._getViaCoordinates(); - if (via.x == null) { - ctx.lineTo(this.to.x, this.to.y); - ctx.stroke(); - return null; - } - else { - // this.via.x = via.x; - // this.via.y = via.y; - ctx.quadraticCurveTo(via.x,via.y,this.to.x, this.to.y); - ctx.stroke(); - return via; - } + function makeDate(y, m, d, h, M, s, ms) { + //can't just apply() to create a date: + //http://stackoverflow.com/questions/181348/instantiating-a-javascript-object-by-calling-prototype-constructor-apply + var date = new Date(y, m, d, h, M, s, ms); + + //the date constructor doesn't accept years < 1970 + if (y < 1970) { + date.setFullYear(y); + } + return date; } - else { - ctx.quadraticCurveTo(this.via.x,this.via.y,this.to.x, this.to.y); - ctx.stroke(); - return this.via; + + function makeUTCDate(y) { + var date = new Date(Date.UTC.apply(null, arguments)); + if (y < 1970) { + date.setUTCFullYear(y); + } + return date; } - } - else { - ctx.lineTo(this.to.x, this.to.y); - ctx.stroke(); - return null; - } - }; - /** - * Draw a line from a node to itself, a circle - * @param {CanvasRenderingContext2D} ctx - * @param {Number} x - * @param {Number} y - * @param {Number} radius - * @private - */ - Edge.prototype._circle = function (ctx, x, y, radius) { - // draw a circle - ctx.beginPath(); - ctx.arc(x, y, radius, 0, 2 * Math.PI, false); - ctx.stroke(); - }; + function parseWeekday(input, locale) { + if (typeof input === 'string') { + if (!isNaN(input)) { + input = parseInt(input, 10); + } + else { + input = locale.weekdaysParse(input); + if (typeof input !== 'number') { + return null; + } + } + } + return input; + } - /** - * Draw label with white background and with the middle at (x, y) - * @param {CanvasRenderingContext2D} ctx - * @param {String} text - * @param {Number} x - * @param {Number} y - * @private - */ - Edge.prototype._label = function (ctx, text, x, y) { - if (text) { - ctx.font = ((this.from.selected || this.to.selected) ? "bold " : "") + - this.options.fontSize + "px " + this.options.fontFace; - var yLine; + /************************************ + Relative Time + ************************************/ - if (this.dirtyLabel == true) { - var lines = String(text).split('\n'); - var lineCount = lines.length; - var fontSize = Number(this.options.fontSize); - yLine = y + (1 - lineCount) / 2 * fontSize; - var 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; - } - var height = this.options.fontSize * lineCount; - var left = x - width / 2; - var top = y - height / 2; + // helper function for moment.fn.from, moment.fn.fromNow, and moment.duration.fn.humanize + function substituteTimeAgo(string, number, withoutSuffix, isFuture, locale) { + return locale.relativeTime(number || 1, !!withoutSuffix, string, isFuture); + } - // cache - this.labelDimensions = {top:top,left:left,width:width,height:height,yLine:yLine}; + function relativeTime(posNegDuration, withoutSuffix, locale) { + var duration = moment.duration(posNegDuration).abs(), + seconds = round(duration.as('s')), + minutes = round(duration.as('m')), + hours = round(duration.as('h')), + days = round(duration.as('d')), + months = round(duration.as('M')), + years = round(duration.as('y')), + + args = seconds < relativeTimeThresholds.s && ['s', seconds] || + minutes === 1 && ['m'] || + minutes < relativeTimeThresholds.m && ['mm', minutes] || + hours === 1 && ['h'] || + hours < relativeTimeThresholds.h && ['hh', hours] || + days === 1 && ['d'] || + days < relativeTimeThresholds.d && ['dd', days] || + months === 1 && ['M'] || + months < relativeTimeThresholds.M && ['MM', months] || + years === 1 && ['y'] || ['yy', years]; + + args[2] = withoutSuffix; + args[3] = +posNegDuration > 0; + args[4] = locale; + return substituteTimeAgo.apply({}, args); } - var yLine = this.labelDimensions.yLine; - - ctx.save(); - - if (this.options.labelAlignment != "horizontal"){ - ctx.translate(x, yLine); - this._rotateForLabelAlignment(ctx); - x = 0; - yLine = 0; - } - - this._drawLabelRect(ctx); - this._drawLabelText(ctx,x,yLine, lines, lineCount, fontSize); - - ctx.restore(); - } - }; + /************************************ + Week of Year + ************************************/ - /** - * Rotates the canvas so the text is most readable - * @param {CanvasRenderingContext2D} ctx - * @private - */ - Edge.prototype._rotateForLabelAlignment = function(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); - }; + // firstDayOfWeek 0 = sun, 6 = sat + // the day of the week that starts the week + // (usually sunday or monday) + // firstDayOfWeekOfYear 0 = sun, 6 = sat + // the first week is the week that contains the first + // of this day of the week + // (eg. ISO weeks use thursday (4)) + function weekOfYear(mom, firstDayOfWeek, firstDayOfWeekOfYear) { + var end = firstDayOfWeekOfYear - firstDayOfWeek, + daysToDayOfWeek = firstDayOfWeekOfYear - mom.day(), + adjustedMoment; + - /** - * Draws the label rectangle - * @param {CanvasRenderingContext2D} ctx - * @param {String} labelAlignment - * @private - */ - Edge.prototype._drawLabelRect = function(ctx) { - if (this.options.fontFill !== undefined && this.options.fontFill !== null && this.options.fontFill !== "none") { - ctx.fillStyle = this.options.fontFill; - - var lineMargin = 2; + if (daysToDayOfWeek > end) { + daysToDayOfWeek -= 7; + } - if (this.options.labelAlignment == 'line-center') { - ctx.fillRect(-this.labelDimensions.width * 0.5, -this.labelDimensions.height * 0.5, this.labelDimensions.width, this.labelDimensions.height); - } - else if (this.options.labelAlignment == 'line-above') { - ctx.fillRect(-this.labelDimensions.width * 0.5, -(this.labelDimensions.height + lineMargin), this.labelDimensions.width, this.labelDimensions.height); - } - else if (this.options.labelAlignment == 'line-below') { - ctx.fillRect(-this.labelDimensions.width * 0.5, lineMargin, this.labelDimensions.width, this.labelDimensions.height); - } - else { - ctx.fillRect(this.labelDimensions.left, this.labelDimensions.top, this.labelDimensions.width, this.labelDimensions.height); + if (daysToDayOfWeek < end - 7) { + daysToDayOfWeek += 7; + } + + adjustedMoment = moment(mom).add(daysToDayOfWeek, 'd'); + return { + week: Math.ceil(adjustedMoment.dayOfYear() / 7), + year: adjustedMoment.year() + }; } - } - }; - /** - * Draws the label text - * @param {CanvasRenderingContext2D} ctx - * @param {Number} x - * @param {Number} yLine - * @param {Array} lines - * @param {Number} lineCount - * @param {Number} fontSize - * @private - */ - Edge.prototype._drawLabelText = function(ctx, x, yLine, lines, lineCount, fontSize) { - // draw text - ctx.fillStyle = this.options.fontColor || "black"; - ctx.textAlign = "center"; + //http://en.wikipedia.org/wiki/ISO_week_date#Calculating_a_date_given_the_year.2C_week_number_and_weekday + function dayOfYearFromWeeks(year, week, weekday, firstDayOfWeekOfYear, firstDayOfWeek) { + var d = makeUTCDate(year, 0, 1).getUTCDay(), daysToAdd, dayOfYear; - // check for label alignment - if (this.options.labelAlignment != 'horizontal') { - var lineMargin = 2; - if (this.options.labelAlignment == 'line-above') { - ctx.textBaseline = "alphabetic"; - yLine -= 2 * lineMargin; // distance from edge, required because we use alphabetic. Alphabetic has less difference between browsers - } - else if (this.options.labelAlignment == 'line-below') { - 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 = "middle"; - } + d = d === 0 ? 7 : d; + weekday = weekday != null ? weekday : firstDayOfWeek; + daysToAdd = firstDayOfWeek - d + (d > firstDayOfWeekOfYear ? 7 : 0) - (d < firstDayOfWeek ? 7 : 0); + dayOfYear = 7 * (week - 1) + (weekday - firstDayOfWeek) + daysToAdd + 1; - // check for strokeWidth - if (this.options.fontStrokeWidth > 0){ - ctx.lineWidth = this.options.fontStrokeWidth; - ctx.strokeStyle = this.options.fontStrokeColor; - ctx.lineJoin = 'round'; - } - for (var i = 0; i < lineCount; i++) { - if(this.options.fontStrokeWidth > 0){ - ctx.strokeText(lines[i], x, yLine); + return { + year: dayOfYear > 0 ? year : year - 1, + dayOfYear: dayOfYear > 0 ? dayOfYear : daysInYear(year - 1) + dayOfYear + }; } - ctx.fillText(lines[i], x, yLine); - yLine += fontSize; - } - }; - /** - * Redraw a edge as a dashed line - * Draw this edge in the given canvas - * @author David Jordan - * @date 2012-08-08 - * The 2d context of a HTML canvas can be retrieved by canvas.getContext("2d"); - * @param {CanvasRenderingContext2D} ctx - * @private - */ - Edge.prototype._drawDashLine = function(ctx) { - // set style - ctx.strokeStyle = this._getColor(); - ctx.lineWidth = this._getLineWidth(); + /************************************ + Top Level Functions + ************************************/ - var via = null; - // only firefox and chrome support this method, else we use the legacy one. - if (ctx.setLineDash !== undefined) { - ctx.save(); - // configure the dash pattern - var pattern = [0]; - if (this.options.dash.length !== undefined && this.options.dash.gap !== undefined) { - pattern = [this.options.dash.length,this.options.dash.gap]; - } - else { - pattern = [5,5]; - } + function makeMoment(config) { + var input = config._i, + format = config._f, + res; - // set dash settings for chrome or firefox - ctx.setLineDash(pattern); - ctx.lineDashOffset = 0; + config._locale = config._locale || moment.localeData(config._l); - // draw the line - via = this._line(ctx); + if (input === null || (format === undefined && input === '')) { + return moment.invalid({nullInput: true}); + } - // restore the dash settings. - ctx.setLineDash([0]); - ctx.lineDashOffset = 0; - ctx.restore(); - } - else { // unsupporting smooth lines - // draw dashed line - ctx.beginPath(); - ctx.lineCap = 'round'; - if (this.options.dash.altLength !== undefined) //If an alt dash value has been set add to the array this value - { - ctx.dashedLine(this.from.x,this.from.y,this.to.x,this.to.y, - [this.options.dash.length,this.options.dash.gap,this.options.dash.altLength,this.options.dash.gap]); - } - else if (this.options.dash.length !== undefined && this.options.dash.gap !== undefined) //If a dash and gap value has been set add to the array this value - { - ctx.dashedLine(this.from.x,this.from.y,this.to.x,this.to.y, - [this.options.dash.length,this.options.dash.gap]); - } - else //If all else fails draw a line - { - ctx.moveTo(this.from.x, this.from.y); - ctx.lineTo(this.to.x, this.to.y); - } - ctx.stroke(); - } + if (typeof input === 'string') { + config._i = input = config._locale.preparse(input); + } - // draw label - if (this.label) { - var point; - if (this.options.smoothCurves.enabled == true && via != null) { - var midpointX = 0.5*(0.5*(this.from.x + via.x) + 0.5*(this.to.x + via.x)); - var midpointY = 0.5*(0.5*(this.from.y + via.y) + 0.5*(this.to.y + via.y)); - point = {x:midpointX, y:midpointY}; - } - else { - point = this._pointOnLine(0.5); + if (moment.isMoment(input)) { + return new Moment(input, true); + } else if (format) { + if (isArray(format)) { + makeDateFromStringAndArray(config); + } else { + makeDateFromStringAndFormat(config); + } + } else { + makeDateFromInput(config); + } + + res = new Moment(config); + if (res._nextDay) { + // Adding is smart enough around DST + res.add(1, 'd'); + res._nextDay = undefined; + } + + return res; } - this._label(ctx, this.label, point.x, point.y); - } - }; - /** - * Get a point on a line - * @param {Number} percentage. Value between 0 (line start) and 1 (line end) - * @return {Object} point - * @private - */ - Edge.prototype._pointOnLine = function (percentage) { - return { - x: (1 - percentage) * this.from.x + percentage * this.to.x, - y: (1 - percentage) * this.from.y + percentage * this.to.y - } - }; + moment = function (input, format, locale, strict) { + var c; - /** - * 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 - */ - Edge.prototype._pointOnCircle = function (x, y, radius, percentage) { - var angle = (percentage - 3/8) * 2 * Math.PI; - return { - x: x + radius * Math.cos(angle), - y: y - radius * Math.sin(angle) - } - }; + if (typeof(locale) === 'boolean') { + strict = locale; + locale = undefined; + } + // object construction must be done this way. + // https://github.com/moment/moment/issues/1423 + c = {}; + c._isAMomentObject = true; + c._i = input; + c._f = format; + c._l = locale; + c._strict = strict; + c._isUTC = false; + c._pf = defaultParsingFlags(); - /** - * Redraw a edge as a line with an arrow halfway the line - * Draw this edge in the given canvas - * The 2d context of a HTML canvas can be retrieved by canvas.getContext("2d"); - * @param {CanvasRenderingContext2D} ctx - * @private - */ - Edge.prototype._drawArrowCenter = function(ctx) { - var point; - // set style - ctx.strokeStyle = this._getColor(); - ctx.fillStyle = ctx.strokeStyle; - ctx.lineWidth = this._getLineWidth(); + return makeMoment(c); + }; - if (this.from != this.to) { - // draw line - var via = this._line(ctx); + moment.suppressDeprecationWarnings = false; - var angle = Math.atan2((this.to.y - this.from.y), (this.to.x - this.from.x)); - var length = (10 + 5 * this.options.width) * this.options.arrowScaleFactor; - // draw an arrow halfway the line - if (this.options.smoothCurves.enabled == true && via != null) { - var midpointX = 0.5*(0.5*(this.from.x + via.x) + 0.5*(this.to.x + via.x)); - var midpointY = 0.5*(0.5*(this.from.y + via.y) + 0.5*(this.to.y + via.y)); - point = {x:midpointX, y:midpointY}; - } - else { - point = this._pointOnLine(0.5); + moment.createFromInputFallback = deprecate( + 'moment construction falls back to js Date. This is ' + + 'discouraged and will be removed in upcoming major ' + + 'release. Please refer to ' + + 'https://github.com/moment/moment/issues/1407 for more info.', + function (config) { + config._d = new Date(config._i + (config._useUTC ? ' UTC' : '')); + } + ); + + // Pick a moment m from moments so that m[fn](other) is true for all + // other. This relies on the function fn to be transitive. + // + // moments should either be an array of moment objects or an array, whose + // first element is an array of moment objects. + function pickBy(fn, moments) { + var res, i; + if (moments.length === 1 && isArray(moments[0])) { + moments = moments[0]; + } + if (!moments.length) { + return moment(); + } + res = moments[0]; + for (i = 1; i < moments.length; ++i) { + if (moments[i][fn](res)) { + res = moments[i]; + } + } + return res; } - ctx.arrow(point.x, point.y, angle, length); - ctx.fill(); - ctx.stroke(); + moment.min = function () { + var args = [].slice.call(arguments, 0); - // draw label - if (this.label) { - this._label(ctx, this.label, point.x, point.y); - } - } - else { - // draw circle - var x, y; - var radius = 0.25 * Math.max(100,this.physics.springLength); - var node = this.from; - if (!node.width) { - node.resize(ctx); - } - if (node.width > node.height) { - x = node.x + node.width * 0.5; - y = node.y - radius; - } - else { - x = node.x + radius; - y = node.y - node.height * 0.5; - } - this._circle(ctx, x, y, radius); + return pickBy('isBefore', args); + }; - // draw all arrows - var angle = 0.2 * Math.PI; - var length = (10 + 5 * this.options.width) * this.options.arrowScaleFactor; - point = this._pointOnCircle(x, y, radius, 0.5); - ctx.arrow(point.x, point.y, angle, length); - ctx.fill(); - ctx.stroke(); + moment.max = function () { + var args = [].slice.call(arguments, 0); - // draw label - if (this.label) { - point = this._pointOnCircle(x, y, radius, 0.5); - this._label(ctx, this.label, point.x, point.y); - } - } - }; + return pickBy('isAfter', args); + }; - Edge.prototype._pointOnBezier = function(t) { - var via = this._getViaCoordinates(); + // creating with utc + moment.utc = function (input, format, locale, strict) { + var c; - var x = Math.pow(1-t,2)*this.from.x + (2*t*(1 - t))*via.x + Math.pow(t,2)*this.to.x; - var y = Math.pow(1-t,2)*this.from.y + (2*t*(1 - t))*via.y + Math.pow(t,2)*this.to.y; + if (typeof(locale) === 'boolean') { + strict = locale; + locale = undefined; + } + // object construction must be done this way. + // https://github.com/moment/moment/issues/1423 + c = {}; + c._isAMomentObject = true; + c._useUTC = true; + c._isUTC = true; + c._l = locale; + c._i = input; + c._f = format; + c._strict = strict; + c._pf = defaultParsingFlags(); - return {x:x,y:y}; - } + return makeMoment(c).utc(); + }; - /** - * This function uses binary search to look for the point where the bezier curve crosses the border of the node. - * - * @param from - * @param ctx - * @returns {*} - * @private - */ - Edge.prototype._findBorderPosition = function(from,ctx) { - var maxIterations = 10; - var iteration = 0; - var low = 0; - var high = 1; - var pos,angle,distanceToBorder, distanceToNodes, difference; - var threshold = 0.2; - var node = this.to; - if (from == true) { - node = this.from; - } + // creating with unix timestamp (in seconds) + moment.unix = function (input) { + return moment(input * 1000); + }; - while (low <= high && iteration < maxIterations) { - var middle = (low + high) * 0.5; + // duration + moment.duration = function (input, key) { + var duration = input, + // matching against regexp is expensive, do it on demand + match = null, + sign, + ret, + parseIso, + diffRes; - pos = this._pointOnBezier(middle); - angle = Math.atan2((node.y - pos.y), (node.x - pos.x)); - distanceToBorder = node.distanceToBorder(ctx,angle); - distanceToNodes = Math.sqrt(Math.pow(pos.x-node.x,2) + Math.pow(pos.y-node.y,2)); - difference = distanceToBorder - distanceToNodes; - if (Math.abs(difference) < threshold) { - break; // found - } - else if (difference < 0) { // distance to nodes is larger than distance to border --> t needs to be bigger if we're looking at the to node. - if (from == false) { - low = middle; - } - else { - high = middle; - } - } - else { - if (from == false) { - high = middle; - } - else { - low = middle; - } - } + if (moment.isDuration(input)) { + duration = { + ms: input._milliseconds, + d: input._days, + M: input._months + }; + } else if (typeof input === 'number') { + duration = {}; + if (key) { + duration[key] = input; + } else { + duration.milliseconds = input; + } + } else if (!!(match = aspNetTimeSpanJsonRegex.exec(input))) { + sign = (match[1] === '-') ? -1 : 1; + duration = { + y: 0, + d: toInt(match[DATE]) * sign, + h: toInt(match[HOUR]) * sign, + m: toInt(match[MINUTE]) * sign, + s: toInt(match[SECOND]) * sign, + ms: toInt(match[MILLISECOND]) * sign + }; + } else if (!!(match = isoDurationRegex.exec(input))) { + sign = (match[1] === '-') ? -1 : 1; + parseIso = function (inp) { + // We'd normally use ~~inp for this, but unfortunately it also + // converts floats to ints. + // inp may be undefined, so careful calling replace on it. + var res = inp && parseFloat(inp.replace(',', '.')); + // apply sign while we're at it + return (isNaN(res) ? 0 : res) * sign; + }; + duration = { + y: parseIso(match[2]), + M: parseIso(match[3]), + d: parseIso(match[4]), + h: parseIso(match[5]), + m: parseIso(match[6]), + s: parseIso(match[7]), + w: parseIso(match[8]) + }; + } else if (duration == null) {// checks for null or undefined + duration = {}; + } else if (typeof duration === 'object' && + ('from' in duration || 'to' in duration)) { + diffRes = momentsDifference(moment(duration.from), moment(duration.to)); - iteration++; - } - pos.t = middle; + duration = {}; + duration.ms = diffRes.milliseconds; + duration.M = diffRes.months; + } - return pos; - }; + ret = new Duration(duration); - /** - * Redraw a edge as a line with an arrow - * Draw this edge in the given canvas - * The 2d context of a HTML canvas can be retrieved by canvas.getContext("2d"); - * @param {CanvasRenderingContext2D} ctx - * @private - */ - Edge.prototype._drawArrow = function(ctx) { - // set style - ctx.strokeStyle = this._getColor(); - ctx.fillStyle = ctx.strokeStyle; - ctx.lineWidth = this._getLineWidth(); + if (moment.isDuration(input) && hasOwnProp(input, '_locale')) { + ret._locale = input._locale; + } + + return ret; + }; - // set vars - var angle, length, arrowPos; + // version number + moment.version = VERSION; - // if not connected to itself - if (this.from != this.to) { - // draw line - this._line(ctx); + // default format + moment.defaultFormat = isoFormat; - // draw arrow head - if (this.options.smoothCurves.enabled == true) { - var via = this._getViaCoordinates(); - arrowPos = this._findBorderPosition(false, ctx); - var guidePos = this._pointOnBezier(Math.max(0.0, arrowPos.t - 0.1)) - angle = Math.atan2((arrowPos.y - guidePos.y), (arrowPos.x - guidePos.x)); - } - else { - angle = Math.atan2((this.to.y - this.from.y), (this.to.x - this.from.x)); - var dx = (this.to.x - this.from.x); - var dy = (this.to.y - this.from.y); - var edgeSegmentLength = Math.sqrt(dx * dx + dy * dy); - var toBorderDist = this.to.distanceToBorder(ctx, angle); - var toBorderPoint = (edgeSegmentLength - toBorderDist) / edgeSegmentLength; + // constant that refers to the ISO standard + moment.ISO_8601 = function () {}; - arrowPos = {}; - arrowPos.x = (1 - toBorderPoint) * this.from.x + toBorderPoint * this.to.x; - arrowPos.y = (1 - toBorderPoint) * this.from.y + toBorderPoint * this.to.y; - } + // Plugins that add properties should also add the key here (null value), + // so we can properly clone ourselves. + moment.momentProperties = momentProperties; - // draw arrow at the end of the line - length = (10 + 5 * this.options.width) * this.options.arrowScaleFactor; - ctx.arrow(arrowPos.x,arrowPos.y, angle, length); - ctx.fill(); - ctx.stroke(); + // This function will be called whenever a moment is mutated. + // It is intended to keep the offset in sync with the timezone. + moment.updateOffset = function () {}; - // draw label - if (this.label) { - var point; - if (this.options.smoothCurves.enabled == true && via != null) { - point = this._pointOnBezier(0.5); - } - else { - point = this._pointOnLine(0.5); - } - this._label(ctx, this.label, point.x, point.y); - } - } - else { - // draw circle - var node = this.from; - var x, y, arrow; - var radius = 0.25 * Math.max(100,this.physics.springLength); - if (!node.width) { - node.resize(ctx); - } - if (node.width > node.height) { - x = node.x + node.width * 0.5; - y = node.y - radius; - arrow = { - x: x, - y: node.y, - angle: 0.9 * Math.PI - }; - } - else { - x = node.x + radius; - y = node.y - node.height * 0.5; - arrow = { - x: node.x, - y: y, - angle: 0.6 * Math.PI - }; - } - ctx.beginPath(); - // TODO: similarly, for a line without arrows, draw to the border of the nodes instead of the center - ctx.arc(x, y, radius, 0, 2 * Math.PI, false); - ctx.stroke(); + // This function allows you to set a threshold for relative time strings + moment.relativeTimeThreshold = function (threshold, limit) { + if (relativeTimeThresholds[threshold] === undefined) { + return false; + } + if (limit === undefined) { + return relativeTimeThresholds[threshold]; + } + relativeTimeThresholds[threshold] = limit; + return true; + }; - // draw all arrows - var length = (10 + 5 * this.options.width) * this.options.arrowScaleFactor; - ctx.arrow(arrow.x, arrow.y, arrow.angle, length); - ctx.fill(); - ctx.stroke(); + moment.lang = deprecate( + 'moment.lang is deprecated. Use moment.locale instead.', + function (key, value) { + return moment.locale(key, value); + } + ); - // draw label - if (this.label) { - point = this._pointOnCircle(x, y, radius, 0.5); - this._label(ctx, this.label, point.x, point.y); - } - } - }; + // This function will load locale and then set the global locale. If + // no arguments are passed in, it will simply return the current global + // locale key. + moment.locale = function (key, values) { + var data; + if (key) { + if (typeof(values) !== 'undefined') { + data = moment.defineLocale(key, values); + } + else { + data = moment.localeData(key); + } - /** - * Calculate the distance between a point (x3,y3) and a line segment from - * (x1,y1) to (x2,y2). - * http://stackoverflow.com/questions/849211/shortest-distancae-between-a-point-and-a-line-segment - * @param {number} x1 - * @param {number} y1 - * @param {number} x2 - * @param {number} y2 - * @param {number} x3 - * @param {number} y3 - * @private - */ - Edge.prototype._getDistanceToEdge = function (x1,y1, x2,y2, x3,y3) { // x3,y3 is the point - var returnValue = 0; - if (this.from != this.to) { - if (this.options.smoothCurves.enabled == true) { - var xVia, yVia; - if (this.options.smoothCurves.enabled == true && this.options.smoothCurves.dynamic == true) { - xVia = this.via.x; - yVia = this.via.y; - } - else { - var via = this._getViaCoordinates(); - xVia = via.x; - yVia = via.y; - } - var minDistance = 1e9; - var distance; - var i,t,x,y, lastX, lastY; - for (i = 0; i < 10; i++) { - t = 0.1*i; - x = Math.pow(1-t,2)*x1 + (2*t*(1 - t))*xVia + Math.pow(t,2)*x2; - y = Math.pow(1-t,2)*y1 + (2*t*(1 - t))*yVia + Math.pow(t,2)*y2; - if (i > 0) { - distance = this._getDistanceToLine(lastX,lastY,x,y, x3,y3); - minDistance = distance < minDistance ? distance : minDistance; + if (data) { + moment.duration._locale = moment._locale = data; + } } - lastX = x; lastY = y; - } - returnValue = minDistance; - } - else { - returnValue = this._getDistanceToLine(x1,y1,x2,y2,x3,y3); - } - } - else { - var x, y, dx, dy; - var radius = 0.25 * this.physics.springLength; - var node = this.from; - if (node.width > node.height) { - x = node.x + 0.5 * node.width; - y = node.y - radius; - } - else { - x = node.x + radius; - y = node.y - 0.5 * node.height; - } - dx = x - x3; - dy = y - y3; - returnValue = Math.abs(Math.sqrt(dx*dx + dy*dy) - radius); - } - if (this.labelDimensions.left < x3 && - this.labelDimensions.left + this.labelDimensions.width > x3 && - this.labelDimensions.top < y3 && - this.labelDimensions.top + this.labelDimensions.height > y3) { - return 0; - } - else { - return returnValue; - } - }; + return moment._locale._abbr; + }; - Edge.prototype._getDistanceToLine = function(x1,y1,x2,y2,x3,y3) { - var px = x2-x1, - py = y2-y1, - something = px*px + py*py, - u = ((x3 - x1) * px + (y3 - y1) * py) / something; + moment.defineLocale = function (name, values) { + if (values !== null) { + values.abbr = name; + if (!locales[name]) { + locales[name] = new Locale(); + } + locales[name].set(values); - if (u > 1) { - u = 1; - } - else if (u < 0) { - u = 0; - } + // backwards compat for now: also set the locale + moment.locale(name); - var x = x1 + u * px, - y = y1 + u * py, - dx = x - x3, - dy = y - y3; + return locales[name]; + } else { + // useful for testing + delete locales[name]; + return null; + } + }; - //# Note: If the actual distance does not matter, - //# if you only want to compare what this function - //# returns to other results of this function, you - //# can just return the squared distance instead - //# (i.e. remove the sqrt) to gain a little performance + moment.langData = deprecate( + 'moment.langData is deprecated. Use moment.localeData instead.', + function (key) { + return moment.localeData(key); + } + ); - return Math.sqrt(dx*dx + dy*dy); - }; + // returns locale data + moment.localeData = function (key) { + var locale; - /** - * This allows the zoom level of the network to influence the rendering - * - * @param scale - */ - Edge.prototype.setScale = function(scale) { - this.networkScaleInv = 1.0/scale; - }; + if (key && key._locale && key._locale._abbr) { + key = key._locale._abbr; + } + if (!key) { + return moment._locale; + } - Edge.prototype.select = function() { - this.selected = true; - }; + if (!isArray(key)) { + //short-circuit everything else + locale = loadLocale(key); + if (locale) { + return locale; + } + key = [key]; + } - Edge.prototype.unselect = function() { - this.selected = false; - }; + return chooseLocale(key); + }; - Edge.prototype.positionBezierNode = function() { - if (this.via !== null && this.from !== null && this.to !== null) { - 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 !== null) { - this.via.x = 0; - this.via.y = 0; - } - }; + // compare moment object + moment.isMoment = function (obj) { + return obj instanceof Moment || + (obj != null && hasOwnProp(obj, '_isAMomentObject')); + }; - /** - * This function draws the control nodes for the manipulator. - * In order to enable this, only set the this.controlNodesEnabled to true. - * @param ctx - */ - Edge.prototype._drawControlNodes = function(ctx) { - if (this.controlNodesEnabled == true) { - if (this.controlNodes.from === null && this.controlNodes.to === null) { - var nodeIdFrom = "edgeIdFrom:".concat(this.id); - var nodeIdTo = "edgeIdTo:".concat(this.id); - var constants = { - nodes:{group:'', radius:7, borderWidth:2, borderWidthSelected: 2}, - physics:{damping:0}, - clustering: {maxNodeSizeIncrements: 0 ,nodeScaling: {width:0, height: 0, radius:0}} - }; - this.controlNodes.from = new Node( - {id:nodeIdFrom, - shape:'dot', - color:{background:'#ff0000', border:'#3c3c3c', highlight: {background:'#07f968'}} - },{},{},constants); - this.controlNodes.to = new Node( - {id:nodeIdTo, - shape:'dot', - color:{background:'#ff0000', border:'#3c3c3c', highlight: {background:'#07f968'}} - },{},{},constants); - } + // for typechecking Duration objects + moment.isDuration = function (obj) { + return obj instanceof Duration; + }; - this.controlNodes.positions = {}; - if (this.controlNodes.from.selected == false) { - this.controlNodes.positions.from = this.getControlNodeFromPosition(ctx); - this.controlNodes.from.x = this.controlNodes.positions.from.x; - this.controlNodes.from.y = this.controlNodes.positions.from.y; - } - if (this.controlNodes.to.selected == false) { - this.controlNodes.positions.to = this.getControlNodeToPosition(ctx); - this.controlNodes.to.x = this.controlNodes.positions.to.x; - this.controlNodes.to.y = this.controlNodes.positions.to.y; + for (i = lists.length - 1; i >= 0; --i) { + makeList(lists[i]); } - this.controlNodes.from.draw(ctx); - this.controlNodes.to.draw(ctx); - } - else { - this.controlNodes = {from:null, to:null, positions:{}}; - } - }; + moment.normalizeUnits = function (units) { + return normalizeUnits(units); + }; - /** - * Enable control nodes. - * @private - */ - Edge.prototype._enableControlNodes = function() { - this.fromBackup = this.from; - this.toBackup = this.to; - this.controlNodesEnabled = true; - }; + moment.invalid = function (flags) { + var m = moment.utc(NaN); + if (flags != null) { + extend(m._pf, flags); + } + else { + m._pf.userInvalidated = true; + } - /** - * disable control nodes and remove from dynamicEdges from old node - * @private - */ - Edge.prototype._disableControlNodes = function() { - this.fromId = this.from.id; - this.toId = this.to.id; - if (this.fromId != this.fromBackup.id) { // from was changed, remove edge from old 'from' node dynamic edges - this.fromBackup.detachEdge(this); - } - else if (this.toId != this.toBackup.id) { // to was changed, remove edge from old 'to' node dynamic edges - this.toBackup.detachEdge(this); - } + return m; + }; - this.fromBackup = null; - this.toBackup = null; - this.controlNodesEnabled = false; - }; + moment.parseZone = function () { + return moment.apply(null, arguments).parseZone(); + }; + moment.parseTwoDigitYear = function (input) { + return toInt(input) + (toInt(input) > 68 ? 1900 : 2000); + }; - /** - * This checks if one of the control nodes is selected and if so, returns the control node object. Else it returns null. - * @param x - * @param y - * @returns {null} - * @private - */ - Edge.prototype._getSelectedControlNode = function(x,y) { - var positions = this.controlNodes.positions; - var fromDistance = Math.sqrt(Math.pow(x - positions.from.x,2) + Math.pow(y - positions.from.y,2)); - var toDistance = Math.sqrt(Math.pow(x - positions.to.x ,2) + Math.pow(y - positions.to.y ,2)); + moment.isDate = isDate; - if (fromDistance < 15) { - this.connectedNode = this.from; - this.from = this.controlNodes.from; - return this.controlNodes.from; - } - else if (toDistance < 15) { - this.connectedNode = this.to; - this.to = this.controlNodes.to; - return this.controlNodes.to; - } - else { - return null; - } - }; + /************************************ + Moment Prototype + ************************************/ - /** - * this resets the control nodes to their original position. - * @private - */ - Edge.prototype._restoreControlNodes = function() { - if (this.controlNodes.from.selected == true) { - this.from = this.connectedNode; - this.connectedNode = null; - this.controlNodes.from.unselect(); - } - else if (this.controlNodes.to.selected == true) { - this.to = this.connectedNode; - this.connectedNode = null; - this.controlNodes.to.unselect(); - } - }; + extend(moment.fn = Moment.prototype, { - /** - * this calculates the position of the control nodes on the edges of the parent nodes. - * - * @param ctx - * @returns {x: *, y: *} - */ - Edge.prototype.getControlNodeFromPosition = function(ctx) { - // draw arrow head - var controlnodeFromPos; - if (this.options.smoothCurves.enabled == true) { - controlnodeFromPos = this._findBorderPosition(true, ctx); - } - else { - var angle = Math.atan2((this.to.y - this.from.y), (this.to.x - this.from.x)); - var dx = (this.to.x - this.from.x); - var dy = (this.to.y - this.from.y); - var edgeSegmentLength = Math.sqrt(dx * dx + dy * dy); + clone : function () { + return moment(this); + }, - var fromBorderDist = this.from.distanceToBorder(ctx, angle + Math.PI); - var fromBorderPoint = (edgeSegmentLength - fromBorderDist) / edgeSegmentLength; - controlnodeFromPos = {}; - controlnodeFromPos.x = (fromBorderPoint) * this.from.x + (1 - fromBorderPoint) * this.to.x; - controlnodeFromPos.y = (fromBorderPoint) * this.from.y + (1 - fromBorderPoint) * this.to.y; - } + valueOf : function () { + return +this._d - ((this._offset || 0) * 60000); + }, - return controlnodeFromPos; - }; + unix : function () { + return Math.floor(+this / 1000); + }, - /** - * this calculates the position of the control nodes on the edges of the parent nodes. - * - * @param ctx - * @returns {{from: {x: number, y: number}, to: {x: *, y: *}}} - */ - Edge.prototype.getControlNodeToPosition = function(ctx) { - // draw arrow head - var controlnodeFromPos,controlnodeToPos; - if (this.options.smoothCurves.enabled == true) { - controlnodeToPos = this._findBorderPosition(false, ctx); - } - else { - var angle = Math.atan2((this.to.y - this.from.y), (this.to.x - this.from.x)); - var dx = (this.to.x - this.from.x); - var dy = (this.to.y - this.from.y); - var edgeSegmentLength = Math.sqrt(dx * dx + dy * dy); - var toBorderDist = this.to.distanceToBorder(ctx, angle); - var toBorderPoint = (edgeSegmentLength - toBorderDist) / edgeSegmentLength; + toString : function () { + return this.clone().locale('en').format('ddd MMM DD YYYY HH:mm:ss [GMT]ZZ'); + }, - controlnodeToPos = {}; - controlnodeToPos.x = (1 - toBorderPoint) * this.from.x + toBorderPoint * this.to.x; - controlnodeToPos.y = (1 - toBorderPoint) * this.from.y + toBorderPoint * this.to.y; - } + toDate : function () { + return this._offset ? new Date(+this) : this._d; + }, + + toISOString : function () { + var m = moment(this).utc(); + if (0 < m.year() && m.year() <= 9999) { + if ('function' === typeof Date.prototype.toISOString) { + // native implementation is ~50x faster, use it when we can + return this.toDate().toISOString(); + } else { + return formatMoment(m, 'YYYY-MM-DD[T]HH:mm:ss.SSS[Z]'); + } + } else { + return formatMoment(m, 'YYYYYY-MM-DD[T]HH:mm:ss.SSS[Z]'); + } + }, + + toArray : function () { + var m = this; + return [ + m.year(), + m.month(), + m.date(), + m.hours(), + m.minutes(), + m.seconds(), + m.milliseconds() + ]; + }, - return controlnodeToPos; - }; + isValid : function () { + return isValid(this); + }, - module.exports = Edge; + isDSTShifted : function () { + if (this._a) { + return this.isValid() && compareArrays(this._a, (this._isUTC ? moment.utc(this._a) : moment(this._a)).toArray()) > 0; + } -/***/ }, -/* 58 */ -/***/ function(module, exports, __webpack_require__) { + return false; + }, - /** - * 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 Popup(container, x, y, text, style) { - if (container) { - this.container = container; - } - else { - this.container = document.body; - } + parsingFlags : function () { + return extend({}, this._pf); + }, - // x, y and text are optional, see if a style object was passed in their place - if (style === undefined) { - if (typeof x === "object") { - style = x; - x = undefined; - } else if (typeof text === "object") { - style = text; - text = undefined; - } else { - // for backwards compatibility, in case clients other than Network are creating Popup directly - style = { - fontColor: 'black', - fontSize: 14, // px - fontFace: 'verdana', - color: { - border: '#666', - background: '#FFFFC6' - } - } - } - } + invalidAt: function () { + return this._pf.overflow; + }, - this.x = 0; - this.y = 0; - this.padding = 5; + utc : function (keepLocalTime) { + return this.utcOffset(0, keepLocalTime); + }, - if (x !== undefined && y !== undefined ) { - this.setPosition(x, y); - } - if (text !== undefined) { - this.setText(text); - } + local : function (keepLocalTime) { + if (this._isUTC) { + this.utcOffset(0, keepLocalTime); + this._isUTC = false; - // create the frame - this.frame = document.createElement("div"); - var styleAttr = this.frame.style; - styleAttr.position = "absolute"; - styleAttr.visibility = "hidden"; - styleAttr.border = "1px solid " + style.color.border; - styleAttr.color = style.fontColor; - styleAttr.fontSize = style.fontSize + "px"; - styleAttr.fontFamily = style.fontFace; - styleAttr.padding = this.padding + "px"; - styleAttr.backgroundColor = style.color.background; - styleAttr.borderRadius = "3px"; - styleAttr.MozBorderRadius = "3px"; - styleAttr.WebkitBorderRadius = "3px"; - styleAttr.boxShadow = "3px 3px 10px rgba(128, 128, 128, 0.5)"; - styleAttr.whiteSpace = "nowrap"; - this.container.appendChild(this.frame); - } + if (keepLocalTime) { + this.subtract(this._dateUtcOffset(), 'm'); + } + } + return this; + }, - /** - * @param {number} x Horizontal position of the popup window - * @param {number} y Vertical position of the popup window - */ - Popup.prototype.setPosition = function(x, y) { - this.x = parseInt(x); - this.y = parseInt(y); - }; + format : function (inputString) { + var output = formatMoment(this, inputString || moment.defaultFormat); + return this.localeData().postformat(output); + }, - /** - * Set the content for the popup window. This can be HTML code or text. - * @param {string | Element} content - */ - Popup.prototype.setText = function(content) { - if (content instanceof Element) { - this.frame.innerHTML = ''; - this.frame.appendChild(content); - } - else { - this.frame.innerHTML = content; // string containing text or HTML - } - }; + add : createAdder(1, 'add'), - /** - * Show the popup window - * @param {boolean} show Optional. Show or hide the window - */ - Popup.prototype.show = function (show) { - if (show === undefined) { - show = true; - } + subtract : createAdder(-1, 'subtract'), - if (show) { - var height = this.frame.clientHeight; - var width = this.frame.clientWidth; - var maxHeight = this.frame.parentNode.clientHeight; - var maxWidth = this.frame.parentNode.clientWidth; + diff : function (input, units, asFloat) { + var that = makeAs(input, this), + zoneDiff = (that.utcOffset() - this.utcOffset()) * 6e4, + anchor, diff, output, daysAdjust; - var top = (this.y - height); - if (top + height + this.padding > maxHeight) { - top = maxHeight - height - this.padding; - } - if (top < this.padding) { - top = this.padding; - } + units = normalizeUnits(units); - var left = this.x; - if (left + width + this.padding > maxWidth) { - left = maxWidth - width - this.padding; - } - if (left < this.padding) { - left = this.padding; - } + if (units === 'year' || units === 'month' || units === 'quarter') { + output = monthDiff(this, that); + if (units === 'quarter') { + output = output / 3; + } else if (units === 'year') { + output = output / 12; + } + } else { + diff = this - that; + output = units === 'second' ? diff / 1e3 : // 1000 + units === 'minute' ? diff / 6e4 : // 1000 * 60 + units === 'hour' ? diff / 36e5 : // 1000 * 60 * 60 + units === 'day' ? (diff - zoneDiff) / 864e5 : // 1000 * 60 * 60 * 24, negate dst + units === 'week' ? (diff - zoneDiff) / 6048e5 : // 1000 * 60 * 60 * 24 * 7, negate dst + diff; + } + return asFloat ? output : absRound(output); + }, - this.frame.style.left = left + "px"; - this.frame.style.top = top + "px"; - this.frame.style.visibility = "visible"; - } - else { - this.hide(); - } - }; + from : function (time, withoutSuffix) { + return moment.duration({to: this, from: time}).locale(this.locale()).humanize(!withoutSuffix); + }, - /** - * Hide the popup window - */ - Popup.prototype.hide = function () { - this.frame.style.visibility = "hidden"; - }; + fromNow : function (withoutSuffix) { + return this.from(moment(), withoutSuffix); + }, - module.exports = Popup; + calendar : function (time) { + // We want to compare the start of today, vs this. + // Getting start-of-today depends on whether we're locat/utc/offset + // or not. + var now = time || moment(), + sod = makeAs(now, this).startOf('day'), + diff = this.diff(sod, 'days', true), + format = diff < -6 ? 'sameElse' : + diff < -1 ? 'lastWeek' : + diff < 0 ? 'lastDay' : + diff < 1 ? 'sameDay' : + diff < 2 ? 'nextDay' : + diff < 7 ? 'nextWeek' : 'sameElse'; + return this.format(this.localeData().calendar(format, this, moment(now))); + }, + isLeapYear : function () { + return isLeapYear(this.year()); + }, -/***/ }, -/* 59 */ -/***/ function(module, exports, __webpack_require__) { + isDST : function () { + return (this.utcOffset() > this.clone().month(0).utcOffset() || + this.utcOffset() > this.clone().month(5).utcOffset()); + }, - var PhysicsMixin = __webpack_require__(60); - var ClusterMixin = __webpack_require__(64); - var SectorsMixin = __webpack_require__(65); - var SelectionMixin = __webpack_require__(66); - var ManipulationMixin = __webpack_require__(67); - var NavigationMixin = __webpack_require__(68); - var HierarchicalLayoutMixin = __webpack_require__(69); + day : function (input) { + var day = this._isUTC ? this._d.getUTCDay() : this._d.getDay(); + if (input != null) { + input = parseWeekday(input, this.localeData()); + return this.add(input - day, 'd'); + } else { + return day; + } + }, - /** - * Load a mixin into the network object - * - * @param {Object} sourceVariable | this object has to contain functions. - * @private - */ - exports._loadMixin = function (sourceVariable) { - for (var mixinFunction in sourceVariable) { - if (sourceVariable.hasOwnProperty(mixinFunction)) { - this[mixinFunction] = sourceVariable[mixinFunction]; - } - } - }; + month : makeAccessor('Month', true), + startOf : function (units) { + units = normalizeUnits(units); + // the following switch intentionally omits break keywords + // to utilize falling through the cases. + switch (units) { + case 'year': + this.month(0); + /* falls through */ + case 'quarter': + case 'month': + this.date(1); + /* falls through */ + case 'week': + case 'isoWeek': + case 'day': + this.hours(0); + /* falls through */ + case 'hour': + this.minutes(0); + /* falls through */ + case 'minute': + this.seconds(0); + /* falls through */ + case 'second': + this.milliseconds(0); + /* falls through */ + } - /** - * removes a mixin from the network object. - * - * @param {Object} sourceVariable | this object has to contain functions. - * @private - */ - exports._clearMixin = function (sourceVariable) { - for (var mixinFunction in sourceVariable) { - if (sourceVariable.hasOwnProperty(mixinFunction)) { - this[mixinFunction] = undefined; - } - } - }; + // weeks are a special case + if (units === 'week') { + this.weekday(0); + } else if (units === 'isoWeek') { + this.isoWeekday(1); + } + // quarters are also special + if (units === 'quarter') { + this.month(Math.floor(this.month() / 3) * 3); + } - /** - * Mixin the physics system and initialize the parameters required. - * - * @private - */ - exports._loadPhysicsSystem = function () { - this._loadMixin(PhysicsMixin); - this._loadSelectedForceSolver(); - if (this.constants.configurePhysics == true) { - this._loadPhysicsConfiguration(); - } - else { - this._cleanupPhysicsConfiguration(); - } - }; + return this; + }, + endOf: function (units) { + units = normalizeUnits(units); + if (units === undefined || units === 'millisecond') { + return this; + } + return this.startOf(units).add(1, (units === 'isoWeek' ? 'week' : units)).subtract(1, 'ms'); + }, - /** - * Mixin the cluster system and initialize the parameters required. - * - * @private - */ - exports._loadClusterSystem = function () { - this.clusterSession = 0; - this.hubThreshold = 5; - this._loadMixin(ClusterMixin); - }; + isAfter: function (input, units) { + var inputMs; + units = normalizeUnits(typeof units !== 'undefined' ? units : 'millisecond'); + if (units === 'millisecond') { + input = moment.isMoment(input) ? input : moment(input); + return +this > +input; + } else { + inputMs = moment.isMoment(input) ? +input : +moment(input); + return inputMs < +this.clone().startOf(units); + } + }, + isBefore: function (input, units) { + var inputMs; + units = normalizeUnits(typeof units !== 'undefined' ? units : 'millisecond'); + if (units === 'millisecond') { + input = moment.isMoment(input) ? input : moment(input); + return +this < +input; + } else { + inputMs = moment.isMoment(input) ? +input : +moment(input); + return +this.clone().endOf(units) < inputMs; + } + }, - /** - * Mixin the sector system and initialize the parameters required - * - * @private - */ - exports._loadSectorSystem = function () { - this.sectors = {}; - this.activeSector = ["default"]; - this.sectors["active"] = {}; - this.sectors["active"]["default"] = {"nodes": {}, - "edges": {}, - "nodeIndices": [], - "formationScale": 1.0, - "drawingNode": undefined }; - this.sectors["frozen"] = {}; - this.sectors["support"] = {"nodes": {}, - "edges": {}, - "nodeIndices": [], - "formationScale": 1.0, - "drawingNode": undefined }; + isBetween: function (from, to, units) { + return this.isAfter(from, units) && this.isBefore(to, units); + }, - this.nodeIndices = this.sectors["active"]["default"]["nodeIndices"]; // the node indices list is used to speed up the computation of the repulsion fields + isSame: function (input, units) { + var inputMs; + units = normalizeUnits(units || 'millisecond'); + if (units === 'millisecond') { + input = moment.isMoment(input) ? input : moment(input); + return +this === +input; + } else { + inputMs = +moment(input); + return +(this.clone().startOf(units)) <= inputMs && inputMs <= +(this.clone().endOf(units)); + } + }, - this._loadMixin(SectorsMixin); - }; + min: deprecate( + 'moment().min is deprecated, use moment.min instead. https://github.com/moment/moment/issues/1548', + function (other) { + other = moment.apply(null, arguments); + return other < this ? this : other; + } + ), + max: deprecate( + 'moment().max is deprecated, use moment.max instead. https://github.com/moment/moment/issues/1548', + function (other) { + other = moment.apply(null, arguments); + return other > this ? this : other; + } + ), - /** - * Mixin the selection system and initialize the parameters required - * - * @private - */ - exports._loadSelectionSystem = function () { - this.selectionObj = {nodes: {}, edges: {}}; + zone : deprecate( + 'moment().zone is deprecated, use moment().utcOffset instead. ' + + 'https://github.com/moment/moment/issues/1779', + function (input, keepLocalTime) { + if (input != null) { + if (typeof input !== 'string') { + input = -input; + } - this._loadMixin(SelectionMixin); - }; + this.utcOffset(input, keepLocalTime); + return this; + } else { + return -this.utcOffset(); + } + } + ), - /** - * Mixin the navigationUI (User Interface) system and initialize the parameters required - * - * @private - */ - exports._loadManipulationSystem = function () { - // reset global variables -- these are used by the selection of nodes and edges. - this.blockConnectingEdgeSelection = false; - this.forceAppendSelection = false; + // keepLocalTime = true means only change the timezone, without + // affecting the local hour. So 5:31:26 +0300 --[utcOffset(2, true)]--> + // 5:31:26 +0200 It is possible that 5:31:26 doesn't exist with offset + // +0200, so we adjust the time as needed, to be valid. + // + // Keeping the time actually adds/subtracts (one hour) + // from the actual represented time. That is why we call updateOffset + // a second time. In case it wants us to change the offset again + // _changeInProgress == true case, then we have to adjust, because + // there is no such time in the given timezone. + utcOffset : function (input, keepLocalTime) { + var offset = this._offset || 0, + localAdjust; + if (input != null) { + if (typeof input === 'string') { + input = utcOffsetFromString(input); + } + if (Math.abs(input) < 16) { + input = input * 60; + } + if (!this._isUTC && keepLocalTime) { + localAdjust = this._dateUtcOffset(); + } + this._offset = input; + this._isUTC = true; + if (localAdjust != null) { + this.add(localAdjust, 'm'); + } + if (offset !== input) { + if (!keepLocalTime || this._changeInProgress) { + addOrSubtractDurationFromMoment(this, + moment.duration(input - offset, 'm'), 1, false); + } else if (!this._changeInProgress) { + this._changeInProgress = true; + moment.updateOffset(this, true); + this._changeInProgress = null; + } + } - if (this.constants.dataManipulation.enabled == true) { - // load the manipulator HTML elements. All styling done in css. - if (this.manipulationDiv === undefined) { - this.manipulationDiv = document.createElement('div'); - this.manipulationDiv.className = 'network-manipulationDiv'; - if (this.editMode == true) { - this.manipulationDiv.style.display = "block"; - } - else { - this.manipulationDiv.style.display = "none"; - } - this.frame.appendChild(this.manipulationDiv); - } + return this; + } else { + return this._isUTC ? offset : this._dateUtcOffset(); + } + }, - if (this.editModeDiv === undefined) { - this.editModeDiv = document.createElement('div'); - this.editModeDiv.className = 'network-manipulation-editMode'; - if (this.editMode == true) { - this.editModeDiv.style.display = "none"; - } - else { - this.editModeDiv.style.display = "block"; - } - this.frame.appendChild(this.editModeDiv); - } + isLocal : function () { + return !this._isUTC; + }, - if (this.closeDiv === undefined) { - this.closeDiv = document.createElement('div'); - this.closeDiv.className = 'network-manipulation-closeDiv'; - this.closeDiv.style.display = this.manipulationDiv.style.display; - this.frame.appendChild(this.closeDiv); - } + isUtcOffset : function () { + return this._isUTC; + }, - // load the manipulation functions - this._loadMixin(ManipulationMixin); + isUtc : function () { + return this._isUTC && this._offset === 0; + }, - // create the manipulator toolbar - this._createManipulatorBar(); - } - else { - if (this.manipulationDiv !== undefined) { - // removes all the bindings and overloads - this._createManipulatorBar(); + zoneAbbr : function () { + return this._isUTC ? 'UTC' : ''; + }, - // remove the manipulation divs - this.frame.removeChild(this.manipulationDiv); - this.frame.removeChild(this.editModeDiv); - this.frame.removeChild(this.closeDiv); + zoneName : function () { + return this._isUTC ? 'Coordinated Universal Time' : ''; + }, - this.manipulationDiv = undefined; - this.editModeDiv = undefined; - this.closeDiv = undefined; - // remove the mixin functions - this._clearMixin(ManipulationMixin); - } - } - }; + parseZone : function () { + if (this._tzm) { + this.utcOffset(this._tzm); + } else if (typeof this._i === 'string') { + this.utcOffset(utcOffsetFromString(this._i)); + } + return this; + }, + hasAlignedHourOffset : function (input) { + if (!input) { + input = 0; + } + else { + input = moment(input).utcOffset(); + } - /** - * Mixin the navigation (User Interface) system and initialize the parameters required - * - * @private - */ - exports._loadNavigationControls = function () { - this._loadMixin(NavigationMixin); - // the clean function removes the button divs, this is done to remove the bindings. - this._cleanNavigation(); - if (this.constants.navigation.enabled == true) { - this._loadNavigationElements(); - } - }; + return (this.utcOffset() - input) % 60 === 0; + }, + daysInMonth : function () { + return daysInMonth(this.year(), this.month()); + }, - /** - * Mixin the hierarchical layout system. - * - * @private - */ - exports._loadHierarchySystem = function () { - this._loadMixin(HierarchicalLayoutMixin); - }; + dayOfYear : function (input) { + var dayOfYear = round((moment(this).startOf('day') - moment(this).startOf('year')) / 864e5) + 1; + return input == null ? dayOfYear : this.add((input - dayOfYear), 'd'); + }, + quarter : function (input) { + return input == null ? Math.ceil((this.month() + 1) / 3) : this.month((input - 1) * 3 + this.month() % 3); + }, -/***/ }, -/* 60 */ -/***/ function(module, exports, __webpack_require__) { + weekYear : function (input) { + var year = weekOfYear(this, this.localeData()._week.dow, this.localeData()._week.doy).year; + return input == null ? year : this.add((input - year), 'y'); + }, - var util = __webpack_require__(1); - var RepulsionMixin = __webpack_require__(61); - var HierarchialRepulsionMixin = __webpack_require__(62); - var BarnesHutMixin = __webpack_require__(63); + isoWeekYear : function (input) { + var year = weekOfYear(this, 1, 4).year; + return input == null ? year : this.add((input - year), 'y'); + }, - /** - * Toggling barnes Hut calculation on and off. - * - * @private - */ - exports._toggleBarnesHut = function () { - this.constants.physics.barnesHut.enabled = !this.constants.physics.barnesHut.enabled; - this._loadSelectedForceSolver(); - this.moving = true; - this.start(); - }; + week : function (input) { + var week = this.localeData().week(this); + return input == null ? week : this.add((input - week) * 7, 'd'); + }, + isoWeek : function (input) { + var week = weekOfYear(this, 1, 4).week; + return input == null ? week : this.add((input - week) * 7, 'd'); + }, - /** - * This loads the node force solver based on the barnes hut or repulsion algorithm - * - * @private - */ - exports._loadSelectedForceSolver = function () { - // this overloads the this._calculateNodeForces - if (this.constants.physics.barnesHut.enabled == true) { - this._clearMixin(RepulsionMixin); - this._clearMixin(HierarchialRepulsionMixin); + weekday : function (input) { + var weekday = (this.day() + 7 - this.localeData()._week.dow) % 7; + return input == null ? weekday : this.add(input - weekday, 'd'); + }, - this.constants.physics.centralGravity = this.constants.physics.barnesHut.centralGravity; - this.constants.physics.springLength = this.constants.physics.barnesHut.springLength; - this.constants.physics.springConstant = this.constants.physics.barnesHut.springConstant; - this.constants.physics.damping = this.constants.physics.barnesHut.damping; + isoWeekday : function (input) { + // behaves the same as moment#day except + // as a getter, returns 7 instead of 0 (1-7 range instead of 0-6) + // as a setter, sunday should belong to the previous week. + return input == null ? this.day() || 7 : this.day(this.day() % 7 ? input : input - 7); + }, - this._loadMixin(BarnesHutMixin); - } - else if (this.constants.physics.hierarchicalRepulsion.enabled == true) { - this._clearMixin(BarnesHutMixin); - this._clearMixin(RepulsionMixin); + isoWeeksInYear : function () { + return weeksInYear(this.year(), 1, 4); + }, - this.constants.physics.centralGravity = this.constants.physics.hierarchicalRepulsion.centralGravity; - this.constants.physics.springLength = this.constants.physics.hierarchicalRepulsion.springLength; - this.constants.physics.springConstant = this.constants.physics.hierarchicalRepulsion.springConstant; - this.constants.physics.damping = this.constants.physics.hierarchicalRepulsion.damping; + weeksInYear : function () { + var weekInfo = this.localeData()._week; + return weeksInYear(this.year(), weekInfo.dow, weekInfo.doy); + }, - this._loadMixin(HierarchialRepulsionMixin); - } - else { - this._clearMixin(BarnesHutMixin); - this._clearMixin(HierarchialRepulsionMixin); - this.barnesHutTree = undefined; + get : function (units) { + units = normalizeUnits(units); + return this[units](); + }, - this.constants.physics.centralGravity = this.constants.physics.repulsion.centralGravity; - this.constants.physics.springLength = this.constants.physics.repulsion.springLength; - this.constants.physics.springConstant = this.constants.physics.repulsion.springConstant; - this.constants.physics.damping = this.constants.physics.repulsion.damping; + set : function (units, value) { + var unit; + if (typeof units === 'object') { + for (unit in units) { + this.set(unit, units[unit]); + } + } + else { + units = normalizeUnits(units); + if (typeof this[units] === 'function') { + this[units](value); + } + } + return this; + }, - this._loadMixin(RepulsionMixin); - } - }; + // If passed a locale key, it will set the locale for this + // instance. Otherwise, it will return the locale configuration + // variables for this instance. + locale : function (key) { + var newLocaleData; - /** - * Before calculating the forces, we check if we need to cluster to keep up performance and we check - * if there is more than one node. If it is just one node, we dont calculate anything. - * - * @private - */ - exports._initializeForceCalculation = function () { - // stop calculation if there is only one node - if (this.nodeIndices.length == 1) { - this.nodes[this.nodeIndices[0]]._setForce(0, 0); - } - else { - // if there are too many nodes on screen, we cluster without repositioning - if (this.nodeIndices.length > this.constants.clustering.clusterThreshold && this.constants.clustering.enabled == true) { - this.clusterToFit(this.constants.clustering.reduceToNodes, false); - } + if (key === undefined) { + return this._locale._abbr; + } else { + newLocaleData = moment.localeData(key); + if (newLocaleData != null) { + this._locale = newLocaleData; + } + return this; + } + }, - // we now start the force calculation - this._calculateForces(); - } - }; + lang : deprecate( + 'moment().lang() is deprecated. Instead, use moment().localeData() to get the language configuration. Use moment().locale() to change languages.', + function (key) { + if (key === undefined) { + return this.localeData(); + } else { + return this.locale(key); + } + } + ), + localeData : function () { + return this._locale; + }, - /** - * Calculate the external forces acting on the nodes - * Forces are caused by: edges, repulsing forces between nodes, gravity - * @private - */ - exports._calculateForces = function () { - // Gravity is required to keep separated groups from floating off - // the forces are reset to zero in this loop by using _setForce instead - // of _addForce + _dateUtcOffset : function () { + // On Firefox.24 Date#getTimezoneOffset returns a floating point. + // https://github.com/moment/moment/pull/1871 + return -Math.round(this._d.getTimezoneOffset() / 15) * 15; + } - this._calculateGravitationalForces(); - this._calculateNodeForces(); + }); - if (this.constants.physics.springConstant > 0) { - if (this.constants.smoothCurves.enabled == true && this.constants.smoothCurves.dynamic == true) { - this._calculateSpringForcesWithSupport(); - } - else { - if (this.constants.physics.hierarchicalRepulsion.enabled == true) { - this._calculateHierarchicalSpringForces(); - } - else { - this._calculateSpringForces(); - } - } - } - }; + function rawMonthSetter(mom, value) { + var dayOfMonth; + // TODO: Move this out of here! + if (typeof value === 'string') { + value = mom.localeData().monthsParse(value); + // TODO: Another silent failure? + if (typeof value !== 'number') { + return mom; + } + } - /** - * Smooth curves are created by adding invisible nodes in the center of the edges. These nodes are also - * handled in the calculateForces function. We then use a quadratic curve with the center node as control. - * This function joins the datanodes and invisible (called support) nodes into one object. - * We do this so we do not contaminate this.nodes with the support nodes. - * - * @private - */ - exports._updateCalculationNodes = function () { - if (this.constants.smoothCurves.enabled == true && this.constants.smoothCurves.dynamic == true) { - this.calculationNodes = {}; - this.calculationNodeIndices = []; + dayOfMonth = Math.min(mom.date(), + daysInMonth(mom.year(), value)); + mom._d['set' + (mom._isUTC ? 'UTC' : '') + 'Month'](value, dayOfMonth); + return mom; + } - for (var nodeId in this.nodes) { - if (this.nodes.hasOwnProperty(nodeId)) { - this.calculationNodes[nodeId] = this.nodes[nodeId]; - } + function rawGetter(mom, unit) { + return mom._d['get' + (mom._isUTC ? 'UTC' : '') + unit](); } - var supportNodes = this.sectors['support']['nodes']; - for (var supportNodeId in supportNodes) { - if (supportNodes.hasOwnProperty(supportNodeId)) { - if (this.edges.hasOwnProperty(supportNodes[supportNodeId].parentEdgeId)) { - this.calculationNodes[supportNodeId] = supportNodes[supportNodeId]; - } - else { - supportNodes[supportNodeId]._setForce(0, 0); + + function rawSetter(mom, unit, value) { + if (unit === 'Month') { + return rawMonthSetter(mom, value); + } else { + return mom._d['set' + (mom._isUTC ? 'UTC' : '') + unit](value); } - } } - for (var idx in this.calculationNodes) { - if (this.calculationNodes.hasOwnProperty(idx)) { - this.calculationNodeIndices.push(idx); - } + function makeAccessor(unit, keepTime) { + return function (value) { + if (value != null) { + rawSetter(this, unit, value); + moment.updateOffset(this, keepTime); + return this; + } else { + return rawGetter(this, unit); + } + }; } - } - else { - this.calculationNodes = this.nodes; - this.calculationNodeIndices = this.nodeIndices; - } - }; + moment.fn.millisecond = moment.fn.milliseconds = makeAccessor('Milliseconds', false); + moment.fn.second = moment.fn.seconds = makeAccessor('Seconds', false); + moment.fn.minute = moment.fn.minutes = makeAccessor('Minutes', false); + // Setting the hour should keep the time, because the user explicitly + // specified which hour he wants. So trying to maintain the same hour (in + // a new timezone) makes sense. Adding/subtracting hours does not follow + // this rule. + moment.fn.hour = moment.fn.hours = makeAccessor('Hours', true); + // moment.fn.month is defined separately + moment.fn.date = makeAccessor('Date', true); + moment.fn.dates = deprecate('dates accessor is deprecated. Use date instead.', makeAccessor('Date', true)); + moment.fn.year = makeAccessor('FullYear', true); + moment.fn.years = deprecate('years accessor is deprecated. Use year instead.', makeAccessor('FullYear', true)); - /** - * this function applies the central gravity effect to keep groups from floating off - * - * @private - */ - exports._calculateGravitationalForces = function () { - var dx, dy, distance, node, i; - var nodes = this.calculationNodes; - var gravity = this.constants.physics.centralGravity; - var gravityForce = 0; - - for (i = 0; i < this.calculationNodeIndices.length; i++) { - node = nodes[this.calculationNodeIndices[i]]; - node.damping = this.constants.physics.damping; // possibly add function to alter damping properties of clusters. - // gravity does not apply when we are in a pocket sector - if (this._sector() == "default" && gravity != 0) { - dx = -node.x; - dy = -node.y; - distance = Math.sqrt(dx * dx + dy * dy); + // add plural methods + moment.fn.days = moment.fn.day; + moment.fn.months = moment.fn.month; + moment.fn.weeks = moment.fn.week; + moment.fn.isoWeeks = moment.fn.isoWeek; + moment.fn.quarters = moment.fn.quarter; - gravityForce = (distance == 0) ? 0 : (gravity / distance); - node.fx = dx * gravityForce; - node.fy = dy * gravityForce; - } - else { - node.fx = 0; - node.fy = 0; - } - } - }; + // add aliased format methods + moment.fn.toJSON = moment.fn.toISOString; + // alias isUtc for dev-friendliness + moment.fn.isUTC = moment.fn.isUtc; + /************************************ + Duration Prototype + ************************************/ - /** - * this function calculates the effects of the springs in the case of unsmooth curves. - * - * @private - */ - exports._calculateSpringForces = function () { - var edgeLength, edge, edgeId; - var dx, dy, fx, fy, springForce, distance; - var edges = this.edges; + function daysToYears (days) { + // 400 years have 146097 days (taking into account leap year rules) + return days * 400 / 146097; + } - // forces caused by the edges, modelled as springs - for (edgeId in edges) { - if (edges.hasOwnProperty(edgeId)) { - edge = edges[edgeId]; - if (edge.connected) { - // only calculate forces if nodes are in the same sector - if (this.nodes.hasOwnProperty(edge.toId) && this.nodes.hasOwnProperty(edge.fromId)) { - edgeLength = edge.physics.springLength; - // this implies that the edges between big clusters are longer - edgeLength += (edge.to.clusterSize + edge.from.clusterSize - 2) * this.constants.clustering.edgeGrowth; + function yearsToDays (years) { + // years * 365 + absRound(years / 4) - + // absRound(years / 100) + absRound(years / 400); + return years * 146097 / 400; + } - dx = (edge.from.x - edge.to.x); - dy = (edge.from.y - edge.to.y); - distance = Math.sqrt(dx * dx + dy * dy); + extend(moment.duration.fn = Duration.prototype, { - if (distance == 0) { - distance = 0.01; - } + _bubble : function () { + var milliseconds = this._milliseconds, + days = this._days, + months = this._months, + data = this._data, + seconds, minutes, hours, years = 0; - // the 1/distance is so the fx and fy can be calculated without sine or cosine. - springForce = this.constants.physics.springConstant * (edgeLength - distance) / distance; + // The following code bubbles up values, see the tests for + // examples of what that means. + data.milliseconds = milliseconds % 1000; - fx = dx * springForce; - fy = dy * springForce; + seconds = absRound(milliseconds / 1000); + data.seconds = seconds % 60; - edge.from.fx += fx; - edge.from.fy += fy; - edge.to.fx -= fx; - edge.to.fy -= fy; - } - } - } - } - }; + minutes = absRound(seconds / 60); + data.minutes = minutes % 60; + hours = absRound(minutes / 60); + data.hours = hours % 24; + days += absRound(hours / 24); + // Accurately convert days to years, assume start from year 0. + years = absRound(daysToYears(days)); + days -= absRound(yearsToDays(years)); - /** - * This function calculates the springforces on the nodes, accounting for the support nodes. - * - * @private - */ - exports._calculateSpringForcesWithSupport = function () { - var edgeLength, edge, edgeId, combinedClusterSize; - var edges = this.edges; + // 30 days to a month + // TODO (iskren): Use anchor date (like 1st Jan) to compute this. + months += absRound(days / 30); + days %= 30; - // forces caused by the edges, modelled as springs - for (edgeId in edges) { - if (edges.hasOwnProperty(edgeId)) { - edge = edges[edgeId]; - if (edge.connected) { - // only calculate forces if nodes are in the same sector - if (this.nodes.hasOwnProperty(edge.toId) && this.nodes.hasOwnProperty(edge.fromId)) { - if (edge.via != null) { - var node1 = edge.to; - var node2 = edge.via; - var node3 = edge.from; + // 12 months -> 1 year + years += absRound(months / 12); + months %= 12; - edgeLength = edge.physics.springLength; + data.days = days; + data.months = months; + data.years = years; + }, - combinedClusterSize = node1.clusterSize + node3.clusterSize - 2; + abs : function () { + this._milliseconds = Math.abs(this._milliseconds); + this._days = Math.abs(this._days); + this._months = Math.abs(this._months); - // this implies that the edges between big clusters are longer - edgeLength += combinedClusterSize * this.constants.clustering.edgeGrowth; - this._calculateSpringForce(node1, node2, 0.5 * edgeLength); - this._calculateSpringForce(node2, node3, 0.5 * edgeLength); - } - } - } - } - } - }; + this._data.milliseconds = Math.abs(this._data.milliseconds); + this._data.seconds = Math.abs(this._data.seconds); + this._data.minutes = Math.abs(this._data.minutes); + this._data.hours = Math.abs(this._data.hours); + this._data.months = Math.abs(this._data.months); + this._data.years = Math.abs(this._data.years); + return this; + }, - /** - * This is the code actually performing the calculation for the function above. It is split out to avoid repetition. - * - * @param node1 - * @param node2 - * @param edgeLength - * @private - */ - exports._calculateSpringForce = function (node1, node2, edgeLength) { - var dx, dy, fx, fy, springForce, distance; + weeks : function () { + return absRound(this.days() / 7); + }, - dx = (node1.x - node2.x); - dy = (node1.y - node2.y); - distance = Math.sqrt(dx * dx + dy * dy); + valueOf : function () { + return this._milliseconds + + this._days * 864e5 + + (this._months % 12) * 2592e6 + + toInt(this._months / 12) * 31536e6; + }, - if (distance == 0) { - distance = 0.01; - } + humanize : function (withSuffix) { + var output = relativeTime(this, !withSuffix, this.localeData()); - // the 1/distance is so the fx and fy can be calculated without sine or cosine. - springForce = this.constants.physics.springConstant * (edgeLength - distance) / distance; + if (withSuffix) { + output = this.localeData().pastFuture(+this, output); + } - fx = dx * springForce; - fy = dy * springForce; + return this.localeData().postformat(output); + }, - node1.fx += fx; - node1.fy += fy; - node2.fx -= fx; - node2.fy -= fy; - }; + add : function (input, val) { + // supports only 2.0-style add(1, 's') or add(moment) + var dur = moment.duration(input, val); + this._milliseconds += dur._milliseconds; + this._days += dur._days; + this._months += dur._months; - exports._cleanupPhysicsConfiguration = function() { - if (this.physicsConfiguration !== undefined) { - while (this.physicsConfiguration.hasChildNodes()) { - this.physicsConfiguration.removeChild(this.physicsConfiguration.firstChild); - } + this._bubble(); - this.physicsConfiguration.parentNode.removeChild(this.physicsConfiguration); - this.physicsConfiguration = undefined; - } - } + return this; + }, - /** - * Load the HTML for the physics config and bind it - * @private - */ - exports._loadPhysicsConfiguration = function () { - if (this.physicsConfiguration === undefined) { - this.backupConstants = {}; - util.deepExtend(this.backupConstants,this.constants); + subtract : function (input, val) { + var dur = moment.duration(input, val); - var hierarchicalLayoutDirections = ["LR", "RL", "UD", "DU"]; - this.physicsConfiguration = document.createElement('div'); - this.physicsConfiguration.className = "PhysicsConfiguration"; - this.physicsConfiguration.innerHTML = '' + - '' + - '' + - '' + - '' + - '' + - '' + - '
Simulation Mode:
Barnes HutRepulsionHierarchical
' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '
Options:
' - this.containerElement.parentElement.insertBefore(this.physicsConfiguration, this.containerElement); - this.optionsDiv = document.createElement("div"); - this.optionsDiv.style.fontSize = "14px"; - this.optionsDiv.style.fontFamily = "verdana"; - this.containerElement.parentElement.insertBefore(this.optionsDiv, this.containerElement); + this._milliseconds -= dur._milliseconds; + this._days -= dur._days; + this._months -= dur._months; - var rangeElement; - rangeElement = document.getElementById('graph_BH_gc'); - rangeElement.onchange = showValueOfRange.bind(this, 'graph_BH_gc', -1, "physics_barnesHut_gravitationalConstant"); - rangeElement = document.getElementById('graph_BH_cg'); - rangeElement.onchange = showValueOfRange.bind(this, 'graph_BH_cg', 1, "physics_centralGravity"); - rangeElement = document.getElementById('graph_BH_sc'); - rangeElement.onchange = showValueOfRange.bind(this, 'graph_BH_sc', 1, "physics_springConstant"); - rangeElement = document.getElementById('graph_BH_sl'); - rangeElement.onchange = showValueOfRange.bind(this, 'graph_BH_sl', 1, "physics_springLength"); - rangeElement = document.getElementById('graph_BH_damp'); - rangeElement.onchange = showValueOfRange.bind(this, 'graph_BH_damp', 1, "physics_damping"); + this._bubble(); - rangeElement = document.getElementById('graph_R_nd'); - rangeElement.onchange = showValueOfRange.bind(this, 'graph_R_nd', 1, "physics_repulsion_nodeDistance"); - rangeElement = document.getElementById('graph_R_cg'); - rangeElement.onchange = showValueOfRange.bind(this, 'graph_R_cg', 1, "physics_centralGravity"); - rangeElement = document.getElementById('graph_R_sc'); - rangeElement.onchange = showValueOfRange.bind(this, 'graph_R_sc', 1, "physics_springConstant"); - rangeElement = document.getElementById('graph_R_sl'); - rangeElement.onchange = showValueOfRange.bind(this, 'graph_R_sl', 1, "physics_springLength"); - rangeElement = document.getElementById('graph_R_damp'); - rangeElement.onchange = showValueOfRange.bind(this, 'graph_R_damp', 1, "physics_damping"); + return this; + }, - rangeElement = document.getElementById('graph_H_nd'); - rangeElement.onchange = showValueOfRange.bind(this, 'graph_H_nd', 1, "physics_hierarchicalRepulsion_nodeDistance"); - rangeElement = document.getElementById('graph_H_cg'); - rangeElement.onchange = showValueOfRange.bind(this, 'graph_H_cg', 1, "physics_centralGravity"); - rangeElement = document.getElementById('graph_H_sc'); - rangeElement.onchange = showValueOfRange.bind(this, 'graph_H_sc', 1, "physics_springConstant"); - rangeElement = document.getElementById('graph_H_sl'); - rangeElement.onchange = showValueOfRange.bind(this, 'graph_H_sl', 1, "physics_springLength"); - rangeElement = document.getElementById('graph_H_damp'); - rangeElement.onchange = showValueOfRange.bind(this, 'graph_H_damp', 1, "physics_damping"); - rangeElement = document.getElementById('graph_H_direction'); - rangeElement.onchange = showValueOfRange.bind(this, 'graph_H_direction', hierarchicalLayoutDirections, "hierarchicalLayout_direction"); - rangeElement = document.getElementById('graph_H_levsep'); - rangeElement.onchange = showValueOfRange.bind(this, 'graph_H_levsep', 1, "hierarchicalLayout_levelSeparation"); - rangeElement = document.getElementById('graph_H_nspac'); - rangeElement.onchange = showValueOfRange.bind(this, 'graph_H_nspac', 1, "hierarchicalLayout_nodeSpacing"); + get : function (units) { + units = normalizeUnits(units); + return this[units.toLowerCase() + 's'](); + }, - var radioButton1 = document.getElementById("graph_physicsMethod1"); - var radioButton2 = document.getElementById("graph_physicsMethod2"); - var radioButton3 = document.getElementById("graph_physicsMethod3"); - radioButton2.checked = true; - if (this.constants.physics.barnesHut.enabled) { - radioButton1.checked = true; - } - if (this.constants.hierarchicalLayout.enabled) { - radioButton3.checked = true; - } + as : function (units) { + var days, months; + units = normalizeUnits(units); - var graph_toggleSmooth = document.getElementById("graph_toggleSmooth"); - var graph_repositionNodes = document.getElementById("graph_repositionNodes"); - var graph_generateOptions = document.getElementById("graph_generateOptions"); + if (units === 'month' || units === 'year') { + days = this._days + this._milliseconds / 864e5; + months = this._months + daysToYears(days) * 12; + return units === 'month' ? months : months / 12; + } else { + // handle milliseconds separately because of floating point math errors (issue #1867) + days = this._days + Math.round(yearsToDays(this._months / 12)); + switch (units) { + case 'week': return days / 7 + this._milliseconds / 6048e5; + case 'day': return days + this._milliseconds / 864e5; + case 'hour': return days * 24 + this._milliseconds / 36e5; + case 'minute': return days * 24 * 60 + this._milliseconds / 6e4; + case 'second': return days * 24 * 60 * 60 + this._milliseconds / 1000; + // Math.floor prevents floating point math errors here + case 'millisecond': return Math.floor(days * 24 * 60 * 60 * 1000) + this._milliseconds; + default: throw new Error('Unknown unit ' + units); + } + } + }, - graph_toggleSmooth.onclick = graphToggleSmoothCurves.bind(this); - graph_repositionNodes.onclick = graphRepositionNodes.bind(this); - graph_generateOptions.onclick = graphGenerateOptions.bind(this); - if (this.constants.smoothCurves == true && this.constants.dynamicSmoothCurves == false) { - graph_toggleSmooth.style.background = "#A4FF56"; - } - else { - graph_toggleSmooth.style.background = "#FF8532"; - } + lang : moment.fn.lang, + locale : moment.fn.locale, + toIsoString : deprecate( + 'toIsoString() is deprecated. Please use toISOString() instead ' + + '(notice the capitals)', + function () { + return this.toISOString(); + } + ), - switchConfigurations.apply(this); + toISOString : function () { + // inspired by https://github.com/dordille/moment-isoduration/blob/master/moment.isoduration.js + var years = Math.abs(this.years()), + months = Math.abs(this.months()), + days = Math.abs(this.days()), + hours = Math.abs(this.hours()), + minutes = Math.abs(this.minutes()), + seconds = Math.abs(this.seconds() + this.milliseconds() / 1000); - radioButton1.onchange = switchConfigurations.bind(this); - radioButton2.onchange = switchConfigurations.bind(this); - radioButton3.onchange = switchConfigurations.bind(this); - } - }; + if (!this.asSeconds()) { + // this is the same as C#'s (Noda) and python (isodate)... + // but not other JS (goog.date) + return 'P0D'; + } - /** - * This overwrites the this.constants. - * - * @param constantsVariableName - * @param value - * @private - */ - exports._overWriteGraphConstants = function (constantsVariableName, value) { - var nameArray = constantsVariableName.split("_"); - if (nameArray.length == 1) { - this.constants[nameArray[0]] = value; - } - else if (nameArray.length == 2) { - this.constants[nameArray[0]][nameArray[1]] = value; - } - else if (nameArray.length == 3) { - this.constants[nameArray[0]][nameArray[1]][nameArray[2]] = value; - } - }; + return (this.asSeconds() < 0 ? '-' : '') + + 'P' + + (years ? years + 'Y' : '') + + (months ? months + 'M' : '') + + (days ? days + 'D' : '') + + ((hours || minutes || seconds) ? 'T' : '') + + (hours ? hours + 'H' : '') + + (minutes ? minutes + 'M' : '') + + (seconds ? seconds + 'S' : ''); + }, + localeData : function () { + return this._locale; + }, - /** - * this function is bound to the toggle smooth curves button. That is also why it is not in the prototype. - */ - function graphToggleSmoothCurves () { - this.constants.smoothCurves.enabled = !this.constants.smoothCurves.enabled; - var graph_toggleSmooth = document.getElementById("graph_toggleSmooth"); - if (this.constants.smoothCurves.enabled == true) {graph_toggleSmooth.style.background = "#A4FF56";} - else {graph_toggleSmooth.style.background = "#FF8532";} + toJSON : function () { + return this.toISOString(); + } + }); - this._configureSmoothCurves(false); - } + moment.duration.fn.toString = moment.duration.fn.toISOString; - /** - * this function is used to scramble the nodes - * - */ - function graphRepositionNodes () { - for (var nodeId in this.calculationNodes) { - if (this.calculationNodes.hasOwnProperty(nodeId)) { - this.calculationNodes[nodeId].vx = 0; this.calculationNodes[nodeId].vy = 0; - this.calculationNodes[nodeId].fx = 0; this.calculationNodes[nodeId].fy = 0; + function makeDurationGetter(name) { + moment.duration.fn[name] = function () { + return this._data[name]; + }; } - } - if (this.constants.hierarchicalLayout.enabled == true) { - this._setupHierarchicalLayout(); - showValueOfRange.call(this, 'graph_H_nd', 1, "physics_hierarchicalRepulsion_nodeDistance"); - showValueOfRange.call(this, 'graph_H_cg', 1, "physics_centralGravity"); - showValueOfRange.call(this, 'graph_H_sc', 1, "physics_springConstant"); - showValueOfRange.call(this, 'graph_H_sl', 1, "physics_springLength"); - showValueOfRange.call(this, 'graph_H_damp', 1, "physics_damping"); - } - else { - this.repositionNodes(); - } - this.moving = true; - this.start(); - } - /** - * this is used to generate an options file from the playing with physics system. - */ - function graphGenerateOptions () { - var options = "No options are required, default values used."; - var optionsSpecific = []; - var radioButton1 = document.getElementById("graph_physicsMethod1"); - var radioButton2 = document.getElementById("graph_physicsMethod2"); - if (radioButton1.checked == true) { - if (this.constants.physics.barnesHut.gravitationalConstant != this.backupConstants.physics.barnesHut.gravitationalConstant) {optionsSpecific.push("gravitationalConstant: " + this.constants.physics.barnesHut.gravitationalConstant);} - if (this.constants.physics.centralGravity != this.backupConstants.physics.barnesHut.centralGravity) {optionsSpecific.push("centralGravity: " + this.constants.physics.centralGravity);} - if (this.constants.physics.springLength != this.backupConstants.physics.barnesHut.springLength) {optionsSpecific.push("springLength: " + this.constants.physics.springLength);} - if (this.constants.physics.springConstant != this.backupConstants.physics.barnesHut.springConstant) {optionsSpecific.push("springConstant: " + this.constants.physics.springConstant);} - if (this.constants.physics.damping != this.backupConstants.physics.barnesHut.damping) {optionsSpecific.push("damping: " + this.constants.physics.damping);} - if (optionsSpecific.length != 0) { - options = "var options = {"; - options += "physics: {barnesHut: {"; - for (var i = 0; i < optionsSpecific.length; i++) { - options += optionsSpecific[i]; - if (i < optionsSpecific.length - 1) { - options += ", " + for (i in unitMillisecondFactors) { + if (hasOwnProp(unitMillisecondFactors, i)) { + makeDurationGetter(i.toLowerCase()); } - } - options += '}}' - } - if (this.constants.smoothCurves.enabled != this.backupConstants.smoothCurves.enabled) { - if (optionsSpecific.length == 0) {options = "var options = {";} - else {options += ", "} - options += "smoothCurves: " + this.constants.smoothCurves.enabled; } - if (options != "No options are required, default values used.") { - options += '};' - } - } - else if (radioButton2.checked == true) { - options = "var options = {"; - options += "physics: {barnesHut: {enabled: false}"; - if (this.constants.physics.repulsion.nodeDistance != this.backupConstants.physics.repulsion.nodeDistance) {optionsSpecific.push("nodeDistance: " + this.constants.physics.repulsion.nodeDistance);} - if (this.constants.physics.centralGravity != this.backupConstants.physics.repulsion.centralGravity) {optionsSpecific.push("centralGravity: " + this.constants.physics.centralGravity);} - if (this.constants.physics.springLength != this.backupConstants.physics.repulsion.springLength) {optionsSpecific.push("springLength: " + this.constants.physics.springLength);} - if (this.constants.physics.springConstant != this.backupConstants.physics.repulsion.springConstant) {optionsSpecific.push("springConstant: " + this.constants.physics.springConstant);} - if (this.constants.physics.damping != this.backupConstants.physics.repulsion.damping) {optionsSpecific.push("damping: " + this.constants.physics.damping);} - if (optionsSpecific.length != 0) { - options += ", repulsion: {"; - for (var i = 0; i < optionsSpecific.length; i++) { - options += optionsSpecific[i]; - if (i < optionsSpecific.length - 1) { - options += ", " + + moment.duration.fn.asMilliseconds = function () { + return this.as('ms'); + }; + moment.duration.fn.asSeconds = function () { + return this.as('s'); + }; + moment.duration.fn.asMinutes = function () { + return this.as('m'); + }; + moment.duration.fn.asHours = function () { + return this.as('h'); + }; + moment.duration.fn.asDays = function () { + return this.as('d'); + }; + moment.duration.fn.asWeeks = function () { + return this.as('weeks'); + }; + moment.duration.fn.asMonths = function () { + return this.as('M'); + }; + moment.duration.fn.asYears = function () { + return this.as('y'); + }; + + /************************************ + Default Locale + ************************************/ + + + // Set default locale, other locale will inherit from English. + moment.locale('en', { + ordinalParse: /\d{1,2}(th|st|nd|rd)/, + ordinal : function (number) { + var b = number % 10, + output = (toInt(number % 100 / 10) === 1) ? 'th' : + (b === 1) ? 'st' : + (b === 2) ? 'nd' : + (b === 3) ? 'rd' : 'th'; + return number + output; } - } - options += '}}' - } - if (optionsSpecific.length == 0) {options += "}"} - if (this.constants.smoothCurves != this.backupConstants.smoothCurves) { - options += ", smoothCurves: " + this.constants.smoothCurves; - } - options += '};' - } - else { - options = "var options = {"; - if (this.constants.physics.hierarchicalRepulsion.nodeDistance != this.backupConstants.physics.hierarchicalRepulsion.nodeDistance) {optionsSpecific.push("nodeDistance: " + this.constants.physics.hierarchicalRepulsion.nodeDistance);} - if (this.constants.physics.centralGravity != this.backupConstants.physics.hierarchicalRepulsion.centralGravity) {optionsSpecific.push("centralGravity: " + this.constants.physics.centralGravity);} - if (this.constants.physics.springLength != this.backupConstants.physics.hierarchicalRepulsion.springLength) {optionsSpecific.push("springLength: " + this.constants.physics.springLength);} - if (this.constants.physics.springConstant != this.backupConstants.physics.hierarchicalRepulsion.springConstant) {optionsSpecific.push("springConstant: " + this.constants.physics.springConstant);} - if (this.constants.physics.damping != this.backupConstants.physics.hierarchicalRepulsion.damping) {optionsSpecific.push("damping: " + this.constants.physics.damping);} - if (optionsSpecific.length != 0) { - options += "physics: {hierarchicalRepulsion: {"; - for (var i = 0; i < optionsSpecific.length; i++) { - options += optionsSpecific[i]; - if (i < optionsSpecific.length - 1) { - options += ", "; + }); + + /* EMBED_LOCALES */ + + /************************************ + Exposing Moment + ************************************/ + + function makeGlobal(shouldDeprecate) { + /*global ender:false */ + if (typeof ender !== 'undefined') { + return; } - } - options += '}},'; - } - options += 'hierarchicalLayout: {'; - optionsSpecific = []; - if (this.constants.hierarchicalLayout.direction != this.backupConstants.hierarchicalLayout.direction) {optionsSpecific.push("direction: " + this.constants.hierarchicalLayout.direction);} - if (Math.abs(this.constants.hierarchicalLayout.levelSeparation) != this.backupConstants.hierarchicalLayout.levelSeparation) {optionsSpecific.push("levelSeparation: " + this.constants.hierarchicalLayout.levelSeparation);} - if (this.constants.hierarchicalLayout.nodeSpacing != this.backupConstants.hierarchicalLayout.nodeSpacing) {optionsSpecific.push("nodeSpacing: " + this.constants.hierarchicalLayout.nodeSpacing);} - if (optionsSpecific.length != 0) { - for (var i = 0; i < optionsSpecific.length; i++) { - options += optionsSpecific[i]; - if (i < optionsSpecific.length - 1) { - options += ", " + oldGlobalMoment = globalScope.moment; + if (shouldDeprecate) { + globalScope.moment = deprecate( + 'Accessing Moment through the global scope is ' + + 'deprecated, and will be removed in an upcoming ' + + 'release.', + moment); + } else { + globalScope.moment = moment; } - } - options += '}' } - else { - options += "enabled:true}"; + + // CommonJS module is defined + if (hasModule) { + module.exports = moment; + } else if (true) { + !(__WEBPACK_AMD_DEFINE_RESULT__ = function (require, exports, module) { + if (module.config && module.config() && module.config().noGlobal === true) { + // release the global variable + globalScope.moment = oldGlobalMoment; + } + + return moment; + }.call(exports, __webpack_require__, exports, module), __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__)); + makeGlobal(true); + } else { + makeGlobal(); } - options += '};' - } + }).call(this); + + /* WEBPACK VAR INJECTION */}.call(exports, (function() { return this; }()), __webpack_require__(71)(module))) +/***/ }, +/* 60 */ +/***/ function(module, exports, __webpack_require__) { - this.optionsDiv.innerHTML = options; - } + /** + * Creation of the ClusterMixin var. + * + * This contains all the functions the Network object can use to employ clustering + */ /** - * this is used to switch between barnesHut, repulsion and hierarchical. + * This is only called in the constructor of the network object + * + */ + exports.startWithClustering = function() { + // cluster if the data set is big + this.clusterToFit(this.constants.clustering.initialMaxNodes, true); + + // updates the lables after clustering + this.updateLabels(); + + // this is called here because if clusterin is disabled, the start and stabilize are called in + // the setData function. + if (this.stabilize) { + this._stabilize(); + } + this.start(); + }; + + /** + * This function clusters until the initialMaxNodes has been reached * + * @param {Number} maxNumberOfNodes + * @param {Boolean} reposition */ - function switchConfigurations () { - var ids = ["graph_BH_table", "graph_R_table", "graph_H_table"]; - var radioButton = document.querySelector('input[name="graph_physicsMethod"]:checked').value; - var tableId = "graph_" + radioButton + "_table"; - var table = document.getElementById(tableId); - table.style.display = "block"; - for (var i = 0; i < ids.length; i++) { - if (ids[i] != tableId) { - table = document.getElementById(ids[i]); - table.style.display = "none"; + exports.clusterToFit = function(maxNumberOfNodes, reposition) { + var numberOfNodes = this.nodeIndices.length; + + var maxLevels = 50; + var level = 0; + + // we first cluster the hubs, then we pull in the outliers, repeat + while (numberOfNodes > maxNumberOfNodes && level < maxLevels) { + if (level % 3 == 0) { + this.forceAggregateHubs(true); + this.normalizeClusterLevels(); } - } - this._restoreNodes(); - if (radioButton == "R") { - this.constants.hierarchicalLayout.enabled = false; - this.constants.physics.hierarchicalRepulsion.enabled = false; - this.constants.physics.barnesHut.enabled = false; - } - else if (radioButton == "H") { - if (this.constants.hierarchicalLayout.enabled == false) { - this.constants.hierarchicalLayout.enabled = true; - this.constants.physics.hierarchicalRepulsion.enabled = true; - this.constants.physics.barnesHut.enabled = false; - this.constants.smoothCurves.enabled = false; - this._setupHierarchicalLayout(); + else { + this.increaseClusterLevel(); // this also includes a cluster normalization } + + numberOfNodes = this.nodeIndices.length; + level += 1; } - else { - this.constants.hierarchicalLayout.enabled = false; - this.constants.physics.hierarchicalRepulsion.enabled = false; - this.constants.physics.barnesHut.enabled = true; - } - this._loadSelectedForceSolver(); - var graph_toggleSmooth = document.getElementById("graph_toggleSmooth"); - if (this.constants.smoothCurves.enabled == true) {graph_toggleSmooth.style.background = "#A4FF56";} - else {graph_toggleSmooth.style.background = "#FF8532";} - this.moving = true; - this.start(); - } + // after the clustering we reposition the nodes to reduce the initial chaos + if (level > 0 && reposition == true) { + this.repositionNodes(); + } + this._updateCalculationNodes(); + }; /** - * this generates the ranges depending on the iniital values. + * This function can be called to open up a specific cluster. It is only called by + * It will unpack the cluster back one level. * - * @param id - * @param map - * @param constantsVariableName + * @param node | Node object: cluster to open. */ - function showValueOfRange (id,map,constantsVariableName) { - var valueId = id + "_value"; - var rangeValue = document.getElementById(id).value; + exports.openCluster = function(node) { + var isMovingBeforeClustering = this.moving; + if (node.clusterSize > this.constants.clustering.sectorThreshold && this._nodeInActiveArea(node) && + !(this._sector() == "default" && this.nodeIndices.length == 1)) { + // this loads a new sector, loads the nodes and edges and nodeIndices of it. + this._addSector(node); + var level = 0; + + // we decluster until we reach a decent number of nodes + while ((this.nodeIndices.length < this.constants.clustering.initialMaxNodes) && (level < 10)) { + this.decreaseClusterLevel(); + level += 1; + } - if (Array.isArray(map)) { - document.getElementById(valueId).value = map[parseInt(rangeValue)]; - this._overWriteGraphConstants(constantsVariableName,map[parseInt(rangeValue)]); } else { - document.getElementById(valueId).value = parseInt(map) * parseFloat(rangeValue); - this._overWriteGraphConstants(constantsVariableName, parseInt(map) * parseFloat(rangeValue)); + this._expandClusterNode(node,false,true); + + // update the index list, dynamic edges and labels + this._updateNodeIndexList(); + this._updateDynamicEdges(); + this._updateCalculationNodes(); + this.updateLabels(); } - if (constantsVariableName == "hierarchicalLayout_direction" || - constantsVariableName == "hierarchicalLayout_levelSeparation" || - constantsVariableName == "hierarchicalLayout_nodeSpacing") { - this._setupHierarchicalLayout(); + // if the simulation was settled, we restart the simulation if a cluster has been formed or expanded + if (this.moving != isMovingBeforeClustering) { + this.start(); } - this.moving = true; - this.start(); - } + }; + /** + * This calls the updateClustes with default arguments + */ + exports.updateClustersDefault = function() { + if (this.constants.clustering.enabled == true) { + this.updateClusters(0,false,false); + } + }; -/***/ }, -/* 61 */ -/***/ function(module, exports, __webpack_require__) { + /** + * This function can be called to increase the cluster level. This means that the nodes with only one edge connection will + * be clustered with their connected node. This can be repeated as many times as needed. + * This can be called externally (by a keybind for instance) to reduce the complexity of big datasets. + */ + exports.increaseClusterLevel = function() { + this.updateClusters(-1,false,true); + }; + /** - * Calculate the forces the nodes apply on each other based on a repulsion field. - * This field is linearly approximated. - * - * @private + * This function can be called to decrease the cluster level. This means that the nodes with only one edge connection will + * be unpacked if they are a cluster. This can be repeated as many times as needed. + * This can be called externally (by a key-bind for instance) to look into clusters without zooming. */ - exports._calculateNodeForces = function () { - var dx, dy, angle, distance, fx, fy, combinedClusterSize, - repulsingForce, node1, node2, i, j; + exports.decreaseClusterLevel = function() { + this.updateClusters(1,false,true); + }; - var nodes = this.calculationNodes; - var nodeIndices = this.calculationNodeIndices; - // approximation constants - var a_base = -2 / 3; - var b = 4 / 3; + /** + * This is the main clustering function. It clusters and declusters on zoom or forced + * This function clusters on zoom, it can be called with a predefined zoom direction + * If out, check if we can form clusters, if in, check if we can open clusters. + * This function is only called from _zoom() + * + * @param {Number} zoomDirection | -1 / 0 / +1 for zoomOut / determineByZoom / zoomIn + * @param {Boolean} recursive | enabled or disable recursive calling of the opening of clusters + * @param {Boolean} force | enabled or disable forcing + * @param {Boolean} doNotStart | if true do not call start + * + */ + exports.updateClusters = function(zoomDirection,recursive,force,doNotStart) { + var isMovingBeforeClustering = this.moving; + var amountOfNodes = this.nodeIndices.length; - // repulsing forces between nodes - var nodeDistance = this.constants.physics.repulsion.nodeDistance; - var minimumDistance = nodeDistance; + // on zoom out collapse the sector if the scale is at the level the sector was made + if (this.previousScale > this.scale && zoomDirection == 0) { + this._collapseSector(); + } - // 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]]; - combinedClusterSize = node1.clusterSize + node2.clusterSize - 2; + // check if we zoom in or out + if (this.previousScale > this.scale || zoomDirection == -1) { // zoom out + // forming clusters when forced pulls outliers in. When not forced, the edge length of the + // outer nodes determines if it is being clustered + this._formClusters(force); + } + else if (this.previousScale < this.scale || zoomDirection == 1) { // zoom in + if (force == true) { + // _openClusters checks for each node if the formationScale of the cluster is smaller than + // the current scale and if so, declusters. When forced, all clusters are reduced by one step + this._openClusters(recursive,force); + } + else { + // if a cluster takes up a set percentage of the active window + this._openClustersBySize(); + } + } + this._updateNodeIndexList(); - dx = node2.x - node1.x; - dy = node2.y - node1.y; - distance = Math.sqrt(dx * dx + dy * dy); + // if a cluster was NOT formed and the user zoomed out, we try clustering by hubs + if (this.nodeIndices.length == amountOfNodes && (this.previousScale > this.scale || zoomDirection == -1)) { + this._aggregateHubs(force); + this._updateNodeIndexList(); + } - // same condition as BarnesHut, making sure nodes are never 100% overlapping. - if (distance == 0) { - distance = 0.1*Math.random(); - dx = distance; - } + // we now reduce chains. + if (this.previousScale > this.scale || zoomDirection == -1) { // zoom out + this.handleChains(); + this._updateNodeIndexList(); + } - minimumDistance = (combinedClusterSize == 0) ? nodeDistance : (nodeDistance * (1 + combinedClusterSize * this.constants.clustering.distanceAmplification)); - var a = a_base / minimumDistance; - if (distance < 2 * minimumDistance) { - if (distance < 0.5 * minimumDistance) { - repulsingForce = 1.0; - } - else { - repulsingForce = a * distance + b; // linear approx of 1 / (1 + Math.exp((distance / minimumDistance - 1) * steepness)) - } + this.previousScale = this.scale; - // amplify the repulsion for clusters. - repulsingForce *= (combinedClusterSize == 0) ? 1 : 1 + combinedClusterSize * this.constants.clustering.forceAmplification; - repulsingForce = repulsingForce / Math.max(distance,0.01*minimumDistance); + // rest of the update the index list, dynamic edges and labels + this._updateDynamicEdges(); + this.updateLabels(); - fx = dx * repulsingForce; - fy = dy * repulsingForce; - node1.fx -= fx; - node1.fy -= fy; - node2.fx += fx; - node2.fy += fy; + // if a cluster was formed, we increase the clusterSession + if (this.nodeIndices.length < amountOfNodes) { // this means a clustering operation has taken place + this.clusterSession += 1; + // if clusters have been made, we normalize the cluster level + this.normalizeClusterLevels(); + } - } + if (doNotStart == false || doNotStart === undefined) { + // if the simulation was settled, we restart the simulation if a cluster has been formed or expanded + if (this.moving != isMovingBeforeClustering) { + this.start(); } } + + this._updateCalculationNodes(); }; + /** + * This function handles the chains. It is called on every updateClusters(). + */ + exports.handleChains = function() { + // after clustering we check how many chains there are + var chainPercentage = this._getChainFraction(); + if (chainPercentage > this.constants.clustering.chainThreshold) { + this._reduceAmountOfChains(1 - this.constants.clustering.chainThreshold / chainPercentage) -/***/ }, -/* 62 */ -/***/ function(module, exports, __webpack_require__) { + } + }; /** - * Calculate the forces the nodes apply on eachother based on a repulsion field. - * This field is linearly approximated. + * this functions starts clustering by hubs + * The minimum hub threshold is set globally * * @private */ - exports._calculateNodeForces = function () { - var dx, dy, distance, fx, fy, - repulsingForce, node1, node2, i, j; + exports._aggregateHubs = function(force) { + this._getHubSize(); + this._formClustersByHub(force,false); + }; - var nodes = this.calculationNodes; - var nodeIndices = this.calculationNodeIndices; - // repulsing forces between nodes - var nodeDistance = this.constants.physics.hierarchicalRepulsion.nodeDistance; + /** + * This function is fired by keypress. It forces hubs to form. + * + */ + exports.forceAggregateHubs = function(doNotStart) { + var isMovingBeforeClustering = this.moving; + var amountOfNodes = this.nodeIndices.length; - // 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]]; + this._aggregateHubs(true); - // nodes only affect nodes on their level - if (node1.level == node2.level) { + // update the index list, dynamic edges and labels + this._updateNodeIndexList(); + this._updateDynamicEdges(); + this.updateLabels(); - dx = node2.x - node1.x; - dy = node2.y - node1.y; - distance = Math.sqrt(dx * dx + dy * dy); + // if a cluster was formed, we increase the clusterSession + if (this.nodeIndices.length != amountOfNodes) { + this.clusterSession += 1; + } + if (doNotStart == false || doNotStart === undefined) { + // if the simulation was settled, we restart the simulation if a cluster has been formed or expanded + if (this.moving != isMovingBeforeClustering) { + this.start(); + } + } + }; - var steepness = 0.05; - if (distance < nodeDistance) { - repulsingForce = -Math.pow(steepness*distance,2) + Math.pow(steepness*nodeDistance,2); - } - else { - repulsingForce = 0; + /** + * If a cluster takes up more than a set percentage of the screen, open the cluster + * + * @private + */ + exports._openClustersBySize = function() { + for (var nodeId in this.nodes) { + if (this.nodes.hasOwnProperty(nodeId)) { + var node = this.nodes[nodeId]; + if (node.inView() == true) { + if ((node.width*this.scale > this.constants.clustering.screenSizeThreshold * this.frame.canvas.clientWidth) || + (node.height*this.scale > this.constants.clustering.screenSizeThreshold * this.frame.canvas.clientHeight)) { + this.openCluster(node); } - // normalize force with - if (distance == 0) { - distance = 0.01; - } - else { - repulsingForce = repulsingForce / distance; - } - fx = dx * repulsingForce; - fy = dy * repulsingForce; - - node1.fx -= fx; - node1.fy -= fy; - node2.fx += fx; - node2.fy += fy; } } } @@ -30207,191 +29872,217 @@ return /******/ (function(modules) { // webpackBootstrap /** - * this function calculates the effects of the springs in the case of unsmooth curves. + * This function loops over all nodes in the nodeIndices list. For each node it checks if it is a cluster and if it + * has to be opened based on the current zoom level. * * @private */ - exports._calculateHierarchicalSpringForces = function () { - var edgeLength, edge, edgeId; - var dx, dy, fx, fy, springForce, distance; - var edges = this.edges; + exports._openClusters = function(recursive,force) { + for (var i = 0; i < this.nodeIndices.length; i++) { + var node = this.nodes[this.nodeIndices[i]]; + this._expandClusterNode(node,recursive,force); + this._updateCalculationNodes(); + } + }; - var nodes = this.calculationNodes; - var nodeIndices = this.calculationNodeIndices; + /** + * This function checks if a node has to be opened. This is done by checking the zoom level. + * If the node contains child nodes, this function is recursively called on the child nodes as well. + * This recursive behaviour is optional and can be set by the recursive argument. + * + * @param {Node} parentNode | to check for cluster and expand + * @param {Boolean} recursive | enabled or disable recursive calling + * @param {Boolean} force | enabled or disable forcing + * @param {Boolean} [openAll] | This will recursively force all nodes in the parent to be released + * @private + */ + exports._expandClusterNode = function(parentNode, recursive, force, openAll) { + // first check if node is a cluster + if (parentNode.clusterSize > 1) { + // this means that on a double tap event or a zoom event, the cluster fully unpacks if it is smaller than 20 + if (parentNode.clusterSize < this.constants.clustering.sectorThreshold) { + openAll = true; + } + recursive = openAll ? true : recursive; + // if the last child has been added on a smaller scale than current scale decluster + if (parentNode.formationScale < this.scale || force == true) { + // we will check if any of the contained child nodes should be removed from the cluster + for (var containedNodeId in parentNode.containedNodes) { + if (parentNode.containedNodes.hasOwnProperty(containedNodeId)) { + var childNode = parentNode.containedNodes[containedNodeId]; - for (var i = 0; i < nodeIndices.length; i++) { - var node1 = nodes[nodeIndices[i]]; - node1.springFx = 0; - node1.springFy = 0; + // force expand will expand the largest cluster size clusters. Since we cluster from outside in, we assume that + // the largest cluster is the one that comes from outside + if (force == true) { + if (childNode.clusterSession == parentNode.clusterSessions[parentNode.clusterSessions.length-1] + || openAll) { + this._expelChildFromParent(parentNode,containedNodeId,recursive,force,openAll); + } + } + else { + if (this._nodeInActiveArea(parentNode)) { + this._expelChildFromParent(parentNode,containedNodeId,recursive,force,openAll); + } + } + } + } + } } + }; + /** + * ONLY CALLED FROM _expandClusterNode + * + * This function will expel a child_node from a parent_node. This is to de-cluster the node. This function will remove + * the child node from the parent contained_node object and put it back into the global nodes object. + * The same holds for the edge that was connected to the child node. It is moved back into the global edges object. + * + * @param {Node} parentNode | the parent node + * @param {String} containedNodeId | child_node id as it is contained in the containedNodes object of the parent node + * @param {Boolean} recursive | This will also check if the child needs to be expanded. + * With force and recursive both true, the entire cluster is unpacked + * @param {Boolean} force | This will disregard the zoom level and will expel this child from the parent + * @param {Boolean} openAll | This will recursively force all nodes in the parent to be released + * @private + */ + exports._expelChildFromParent = function(parentNode, containedNodeId, recursive, force, openAll) { + var childNode = parentNode.containedNodes[containedNodeId]; - // forces caused by the edges, modelled as springs - for (edgeId in edges) { - if (edges.hasOwnProperty(edgeId)) { - edge = edges[edgeId]; - if (edge.connected) { - // only calculate forces if nodes are in the same sector - if (this.nodes.hasOwnProperty(edge.toId) && this.nodes.hasOwnProperty(edge.fromId)) { - edgeLength = edge.physics.springLength; - // this implies that the edges between big clusters are longer - edgeLength += (edge.to.clusterSize + edge.from.clusterSize - 2) * this.constants.clustering.edgeGrowth; - - dx = (edge.from.x - edge.to.x); - dy = (edge.from.y - edge.to.y); - distance = Math.sqrt(dx * dx + dy * dy); + // if child node has been added on smaller scale than current, kick out + if (childNode.formationScale < this.scale || force == true) { + // unselect all selected items + this._unselectAll(); - if (distance == 0) { - distance = 0.01; - } + // put the child node back in the global nodes object + this.nodes[containedNodeId] = childNode; - // the 1/distance is so the fx and fy can be calculated without sine or cosine. - springForce = this.constants.physics.springConstant * (edgeLength - distance) / distance; + // release the contained edges from this childNode back into the global edges + this._releaseContainedEdges(parentNode,childNode); - fx = dx * springForce; - fy = dy * springForce; + // reconnect rerouted edges to the childNode + this._connectEdgeBackToChild(parentNode,childNode); + // validate all edges in dynamicEdges + this._validateEdges(parentNode); + // undo the changes from the clustering operation on the parent node + parentNode.options.mass -= childNode.options.mass; + parentNode.clusterSize -= childNode.clusterSize; + parentNode.options.fontSize = Math.min(this.constants.clustering.maxFontSize, this.constants.nodes.fontSize + this.constants.clustering.fontSizeMultiplier*(parentNode.clusterSize-1)); + parentNode.dynamicEdgesLength = parentNode.dynamicEdges.length; - if (edge.to.level != edge.from.level) { - edge.to.springFx -= fx; - edge.to.springFy -= fy; - edge.from.springFx += fx; - edge.from.springFy += fy; - } - else { - var factor = 0.5; - edge.to.fx -= factor*fx; - edge.to.fy -= factor*fy; - edge.from.fx += factor*fx; - edge.from.fy += factor*fy; - } + // place the child node near the parent, not at the exact same location to avoid chaos in the system + childNode.x = parentNode.x + parentNode.growthIndicator * (0.5 - Math.random()); + childNode.y = parentNode.y + parentNode.growthIndicator * (0.5 - Math.random()); + + // remove node from the list + delete parentNode.containedNodes[containedNodeId]; + + // check if there are other childs with this clusterSession in the parent. + var othersPresent = false; + for (var childNodeId in parentNode.containedNodes) { + if (parentNode.containedNodes.hasOwnProperty(childNodeId)) { + if (parentNode.containedNodes[childNodeId].clusterSession == childNode.clusterSession) { + othersPresent = true; + break; } } } - } + // if there are no others, remove the cluster session from the list + if (othersPresent == false) { + parentNode.clusterSessions.pop(); + } - // normalize spring forces - var springForce = 1; - var springFx, springFy; - for (i = 0; i < nodeIndices.length; i++) { - var node = nodes[nodeIndices[i]]; - springFx = Math.min(springForce,Math.max(-springForce,node.springFx)); - springFy = Math.min(springForce,Math.max(-springForce,node.springFy)); + this._repositionBezierNodes(childNode); + // this._repositionBezierNodes(parentNode); - node.fx += springFx; - node.fy += springFy; - } + // remove the clusterSession from the child node + childNode.clusterSession = 0; - // retain energy balance - var totalFx = 0; - var totalFy = 0; - for (i = 0; i < nodeIndices.length; i++) { - var node = nodes[nodeIndices[i]]; - totalFx += node.fx; - totalFy += node.fy; - } - var correctionFx = totalFx / nodeIndices.length; - var correctionFy = totalFy / nodeIndices.length; + // recalculate the size of the node on the next time the node is rendered + parentNode.clearSizeCache(); - for (i = 0; i < nodeIndices.length; i++) { - var node = nodes[nodeIndices[i]]; - node.fx -= correctionFx; - node.fy -= correctionFy; + // restart the simulation to reorganise all nodes + this.moving = true; } + // check if a further expansion step is possible if recursivity is enabled + if (recursive == true) { + this._expandClusterNode(childNode,recursive,force,openAll); + } }; -/***/ }, -/* 63 */ -/***/ function(module, exports, __webpack_require__) { /** - * 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. + * position the bezier nodes at the center of the edges * + * @param node * @private */ - exports._calculateNodeForces = function() { - if (this.constants.physics.barnesHut.gravitationalConstant != 0) { - var node; - var nodes = this.calculationNodes; - var nodeIndices = this.calculationNodeIndices; - var nodeCount = nodeIndices.length; - - this._formBarnesHutTree(nodes,nodeIndices); + exports._repositionBezierNodes = function(node) { + for (var i = 0; i < node.dynamicEdges.length; i++) { + node.dynamicEdges[i].positionBezierNode(); + } + }; - var barnesHutTree = this.barnesHutTree; - // place the nodes one by one recursively - for (var i = 0; i < nodeCount; i++) { - node = nodes[nodeIndices[i]]; - if (node.options.mass > 0) { - // starting with root is irrelevant, it never passes the BarnesHut condition - this._getForceContribution(barnesHutTree.root.children.NW,node); - this._getForceContribution(barnesHutTree.root.children.NE,node); - this._getForceContribution(barnesHutTree.root.children.SW,node); - this._getForceContribution(barnesHutTree.root.children.SE,node); - } - } + /** + * This function checks if any nodes at the end of their trees have edges below a threshold length + * This function is called only from updateClusters() + * forceLevelCollapse ignores the length of the edge and collapses one level + * This means that a node with only one edge will be clustered with its connected node + * + * @private + * @param {Boolean} force + */ + exports._formClusters = function(force) { + if (force == false) { + this._formClustersByZoom(); + } + else { + this._forceClustersByZoom(); } }; /** - * 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. + * This function handles the clustering by zooming out, this is based on a minimum edge distance * - * @param parentBranch - * @param node * @private */ - exports._getForceContribution = function(parentBranch,node) { - // we get no force contribution from an empty region - if (parentBranch.childrenCount > 0) { - var dx,dy,distance; + exports._formClustersByZoom = function() { + var dx,dy,length, + minLength = this.constants.clustering.clusterEdgeThreshold/this.scale; - // 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); + // check if any edges are shorter than minLength and start the clustering + // the clustering favours the node with the larger mass + for (var edgeId in this.edges) { + if (this.edges.hasOwnProperty(edgeId)) { + var edge = this.edges[edgeId]; + if (edge.connected) { + if (edge.toId != edge.fromId) { + dx = (edge.to.x - edge.from.x); + dy = (edge.to.y - edge.from.y); + length = Math.sqrt(dx * dx + dy * dy); - // BarnesHut condition - // original condition : s/d < thetaInverted = passed === d/s > 1/theta = passed - // calcSize = 1/s --> d * 1/s > 1/theta = passed - if (distance * parentBranch.calcSize > this.constants.physics.barnesHut.thetaInverted) { - // duplicate code to reduce function calls to speed up program - if (distance == 0) { - distance = 0.1*Math.random(); - dx = distance; - } - var gravityForce = this.constants.physics.barnesHut.gravitationalConstant * parentBranch.mass * node.options.mass / (distance * distance * distance); - var fx = dx * gravityForce; - var fy = dy * gravityForce; - node.fx += fx; - node.fy += fy; - } - 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 - // duplicate code to reduce function calls to speed up program - if (distance == 0) { - distance = 0.5*Math.random(); - dx = distance; + + if (length < minLength) { + // first check which node is larger + var parentNode = edge.from; + var childNode = edge.to; + if (edge.to.options.mass > edge.from.options.mass) { + parentNode = edge.to; + childNode = edge.from; + } + + if (childNode.dynamicEdgesLength == 1) { + this._addToCluster(parentNode,childNode,false); + } + else if (parentNode.dynamicEdgesLength == 1) { + this._addToCluster(childNode,parentNode,false); + } } - var gravityForce = this.constants.physics.barnesHut.gravitationalConstant * parentBranch.mass * node.options.mass / (distance * distance * distance); - var fx = dx * gravityForce; - var fy = dy * gravityForce; - node.fx += fx; - node.fy += fy; } } } @@ -30399,3605 +30090,4015 @@ return /******/ (function(modules) { // webpackBootstrap }; /** - * This function constructs the barnesHut tree recursively. It creates the root, splits it and starts placing the nodes. + * This function forces the network to cluster all nodes with only one connecting edge to their + * connected node. * - * @param nodes - * @param nodeIndices * @private */ - exports._formBarnesHutTree = function(nodes,nodeIndices) { - var node; - var nodeCount = nodeIndices.length; + exports._forceClustersByZoom = function() { + for (var nodeId in this.nodes) { + // another node could have absorbed this child. + if (this.nodes.hasOwnProperty(nodeId)) { + var childNode = this.nodes[nodeId]; - var minX = Number.MAX_VALUE, - minY = Number.MAX_VALUE, - maxX =-Number.MAX_VALUE, - maxY =-Number.MAX_VALUE; + // the edges can be swallowed by another decrease + if (childNode.dynamicEdgesLength == 1 && childNode.dynamicEdges.length != 0) { + var edge = childNode.dynamicEdges[0]; + var parentNode = (edge.toId == childNode.id) ? this.nodes[edge.fromId] : this.nodes[edge.toId]; - // get the range of the nodes - for (var i = 0; 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; } + // group to the largest node + if (childNode.id != parentNode.id) { + if (parentNode.options.mass > childNode.options.mass) { + this._addToCluster(parentNode,childNode,true); + } + else { + this._addToCluster(childNode,parentNode,true); + } + } + } } } - // make the range a square - var sizeDiff = Math.abs(maxX - minX) - Math.abs(maxY - minY); // difference between X and Y - if (sizeDiff > 0) {minY -= 0.5 * sizeDiff; maxY += 0.5 * sizeDiff;} // xSize > ySize - else {minX += 0.5 * sizeDiff; maxX -= 0.5 * sizeDiff;} // xSize < ySize + }; - var minimumTreeSize = 1e-5; - var rootSize = Math.max(minimumTreeSize,Math.abs(maxX - minX)); - var halfRootSize = 0.5 * rootSize; - var centerX = 0.5 * (minX + maxX), centerY = 0.5 * (minY + maxY); + /** + * To keep the nodes of roughly equal size we normalize the cluster levels. + * This function clusters a node to its smallest connected neighbour. + * + * @param node + * @private + */ + exports._clusterToSmallestNeighbour = function(node) { + var smallestNeighbour = -1; + var smallestNeighbourNode = null; + for (var i = 0; i < node.dynamicEdges.length; i++) { + if (node.dynamicEdges[i] !== undefined) { + var neighbour = null; + if (node.dynamicEdges[i].fromId != node.id) { + neighbour = node.dynamicEdges[i].from; + } + else if (node.dynamicEdges[i].toId != node.id) { + neighbour = node.dynamicEdges[i].to; + } - // 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 (i = 0; i < nodeCount; i++) { - node = nodes[nodeIndices[i]]; - if (node.options.mass > 0) { - this._placeInTree(barnesHutTree.root,node); + if (neighbour != null && smallestNeighbour > neighbour.clusterSessions.length) { + smallestNeighbour = neighbour.clusterSessions.length; + smallestNeighbourNode = neighbour; + } } } - // make global - this.barnesHutTree = barnesHutTree + if (neighbour != null && this.nodes[neighbour.id] !== undefined) { + this._addToCluster(neighbour, node, true); + } }; /** - * this updates the mass of a branch. this is increased by adding a node. + * This function forms clusters from hubs, it loops over all nodes * - * @param parentBranch - * @param node + * @param {Boolean} force | Disregard zoom level + * @param {Boolean} onlyEqual | This only clusters a hub with a specific number of edges * @private */ - exports._updateBranchMass = function(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; - + exports._formClustersByHub = function(force, onlyEqual) { + // we loop over all nodes in the list + for (var nodeId in this.nodes) { + // we check if it is still available since it can be used by the clustering in this loop + if (this.nodes.hasOwnProperty(nodeId)) { + this._formClusterFromHub(this.nodes[nodeId],force,onlyEqual); + } + } }; - /** - * determine in which branch the node will be placed. + * This function forms a cluster from a specific preselected hub node * - * @param parentBranch - * @param node - * @param skipMassUpdate + * @param {Node} hubNode | the node we will cluster as a hub + * @param {Boolean} force | Disregard zoom level + * @param {Boolean} onlyEqual | This only clusters a hub with a specific number of edges + * @param {Number} [absorptionSizeOffset] | * @private */ - exports._placeInTree = function(parentBranch,node,skipMassUpdate) { - if (skipMassUpdate != true || skipMassUpdate === undefined) { - // update the mass of the branch. - this._updateBranchMass(parentBranch,node); + exports._formClusterFromHub = function(hubNode, force, onlyEqual, absorptionSizeOffset) { + if (absorptionSizeOffset === undefined) { + absorptionSizeOffset = 0; } + // we decide if the node is a hub + if ((hubNode.dynamicEdgesLength >= this.hubThreshold && onlyEqual == false) || + (hubNode.dynamicEdgesLength == this.hubThreshold && onlyEqual == true)) { + // initialize variables + var dx,dy,length; + var minLength = this.constants.clustering.clusterEdgeThreshold/this.scale; + var allowCluster = false; - 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"); + // we create a list of edges because the dynamicEdges change over the course of this loop + var edgesIdarray = []; + var amountOfInitialEdges = hubNode.dynamicEdges.length; + for (var j = 0; j < amountOfInitialEdges; j++) { + edgesIdarray.push(hubNode.dynamicEdges[j].id); } - } - else { // in NE or SE - if (parentBranch.children.NW.range.maxY > node.y) { // in NE - this._placeInRegion(parentBranch,node,"NE"); + + // if the hub clustering is not forces, we check if one of the edges connected + // to a cluster is small enough based on the constants.clustering.clusterEdgeThreshold + if (force == false) { + allowCluster = false; + for (j = 0; j < amountOfInitialEdges; j++) { + var edge = this.edges[edgesIdarray[j]]; + if (edge !== undefined) { + if (edge.connected) { + if (edge.toId != edge.fromId) { + dx = (edge.to.x - edge.from.x); + dy = (edge.to.y - edge.from.y); + length = Math.sqrt(dx * dx + dy * dy); + + if (length < minLength) { + allowCluster = true; + break; + } + } + } + } + } } - else { // in SE - this._placeInRegion(parentBranch,node,"SE"); + + // start the clustering if allowed + if ((!force && allowCluster) || force) { + // we loop over all edges INITIALLY connected to this hub + for (j = 0; j < amountOfInitialEdges; j++) { + edge = this.edges[edgesIdarray[j]]; + // the edge can be clustered by this function in a previous loop + if (edge !== undefined) { + var childNode = this.nodes[(edge.fromId == hubNode.id) ? edge.toId : edge.fromId]; + // we do not want hubs to merge with other hubs nor do we want to cluster itself. + if ((childNode.dynamicEdges.length <= (this.hubThreshold + absorptionSizeOffset)) && + (childNode.id != hubNode.id)) { + this._addToCluster(hubNode,childNode,force); + } + } + } } } }; + /** - * actually place the node in a region (or branch) + * This function adds the child node to the parent node, creating a cluster if it is not already. * - * @param parentBranch - * @param node - * @param region + * @param {Node} parentNode | this is the node that will house the child node + * @param {Node} childNode | this node will be deleted from the global this.nodes and stored in the parent node + * @param {Boolean} force | true will only update the remainingEdges at the very end of the clustering, ensuring single level collapse * @private */ - exports._placeInRegion = function(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; + exports._addToCluster = function(parentNode, childNode, force) { + // join child node in the parent node + parentNode.containedNodes[childNode.id] = childNode; + + // manage all the edges connected to the child and parent nodes + for (var i = 0; i < childNode.dynamicEdges.length; i++) { + var edge = childNode.dynamicEdges[i]; + if (edge.toId == parentNode.id || edge.fromId == parentNode.id) { // edge connected to parentNode + this._addToContainedEdges(parentNode,childNode,edge); + } + else { + this._connectEdgeToCluster(parentNode,childNode,edge); + } + } + // a contained node has no dynamic edges. + childNode.dynamicEdges = []; + + // remove circular edges from clusters + this._containCircularEdgesFromNode(parentNode,childNode); + + + // remove the childNode from the global nodes object + delete this.nodes[childNode.id]; + + // update the properties of the child and parent + var massBefore = parentNode.options.mass; + childNode.clusterSession = this.clusterSession; + parentNode.options.mass += childNode.options.mass; + parentNode.clusterSize += childNode.clusterSize; + parentNode.options.fontSize = Math.min(this.constants.clustering.maxFontSize, this.constants.nodes.fontSize + this.constants.clustering.fontSizeMultiplier*parentNode.clusterSize); + + // keep track of the clustersessions so we can open the cluster up as it has been formed. + if (parentNode.clusterSessions[parentNode.clusterSessions.length - 1] != this.clusterSession) { + parentNode.clusterSessions.push(this.clusterSession); + } + + // forced clusters only open from screen size and double tap + if (force == true) { + // parentNode.formationScale = Math.pow(1 - (1.0/11.0),this.clusterSession+3); + parentNode.formationScale = 0; } + else { + parentNode.formationScale = this.scale; // The latest child has been added on this scale + } + + // recalculate the size of the node on the next time the node is rendered + parentNode.clearSizeCache(); + + // set the pop-out scale for the childnode + parentNode.containedNodes[childNode.id].formationScale = parentNode.formationScale; + + // nullify the movement velocity of the child, this is to avoid hectic behaviour + childNode.clearVelocity(); + + // the mass has altered, preservation of energy dictates the velocity to be updated + parentNode.updateVelocity(massBefore); + + // restart the simulation to reorganise all nodes + this.moving = true; }; /** - * 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 + * This function will apply the changes made to the remainingEdges during the formation of the clusters. + * This is a seperate function to allow for level-wise collapsing of the node barnesHutTree. + * It has to be called if a level is collapsed. It is called by _formClusters(). * @private */ - exports._splitBranch = function(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"); + exports._updateDynamicEdges = function() { + for (var i = 0; i < this.nodeIndices.length; i++) { + var node = this.nodes[this.nodeIndices[i]]; + node.dynamicEdgesLength = node.dynamicEdges.length; - if (containedNode != null) { - this._placeInTree(parentBranch,containedNode); + // this corrects for multiple edges pointing at the same other node + var correction = 0; + if (node.dynamicEdgesLength > 1) { + for (var j = 0; j < node.dynamicEdgesLength - 1; j++) { + var edgeToId = node.dynamicEdges[j].toId; + var edgeFromId = node.dynamicEdges[j].fromId; + for (var k = j+1; k < node.dynamicEdgesLength; k++) { + if ((node.dynamicEdges[k].toId == edgeToId && node.dynamicEdges[k].fromId == edgeFromId) || + (node.dynamicEdges[k].fromId == edgeToId && node.dynamicEdges[k].toId == edgeFromId)) { + correction += 1; + } + } + } + } + node.dynamicEdgesLength -= correction; } }; /** - * This function subdivides the region into four new segments. - * Specifically, this inserts a single new segment. - * It fills the children section of the parentBranch + * This adds an edge from the childNode to the contained edges of the parent node * - * @param parentBranch - * @param region - * @param parentRange + * @param parentNode | Node object + * @param childNode | Node object + * @param edge | Edge object * @private */ - exports._insertRegion = function(parentBranch, region) { - var minX,maxX,minY,maxY; - 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; + exports._addToContainedEdges = function(parentNode, childNode, edge) { + // create an array object if it does not yet exist for this childNode + if (!(parentNode.containedEdges.hasOwnProperty(childNode.id))) { + parentNode.containedEdges[childNode.id] = [] } + // add this edge to the list + parentNode.containedEdges[childNode.id].push(edge); + // remove the edge from the global edges object + delete this.edges[edge.id]; - 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 - }; + // remove the edge from the parent object + for (var i = 0; i < parentNode.dynamicEdges.length; i++) { + if (parentNode.dynamicEdges[i].id == edge.id) { + parentNode.dynamicEdges.splice(i,1); + break; + } + } }; - /** - * This function is for debugging purposed, it draws the tree. + * This function connects an edge that was connected to a child node to the parent node. + * It keeps track of which nodes it has been connected to with the originalId array. * - * @param ctx - * @param color + * @param {Node} parentNode | Node object + * @param {Node} childNode | Node object + * @param {Edge} edge | Edge object * @private */ - exports._drawTree = function(ctx,color) { - if (this.barnesHutTree !== undefined) { + exports._connectEdgeToCluster = function(parentNode, childNode, edge) { + // handle circular edges + if (edge.toId == edge.fromId) { + this._addToContainedEdges(parentNode, childNode, edge); + } + else { + if (edge.toId == childNode.id) { // edge connected to other node on the "to" side + edge.originalToId.push(childNode.id); + edge.to = parentNode; + edge.toId = parentNode.id; + } + else { // edge connected to other node with the "from" side - ctx.lineWidth = 1; + edge.originalFromId.push(childNode.id); + edge.from = parentNode; + edge.fromId = parentNode.id; + } - this._drawBranch(this.barnesHutTree.root,ctx,color); + this._addToReroutedEdges(parentNode,childNode,edge); } }; /** - * This function is for debugging purposes. It draws the branches recursively. + * If a node is connected to itself, a circular edge is drawn. When clustering we want to contain + * these edges inside of the cluster. * - * @param branch - * @param ctx - * @param color + * @param parentNode + * @param childNode * @private */ - exports._drawBranch = function(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); + exports._containCircularEdgesFromNode = function(parentNode, childNode) { + // manage all the edges connected to the child and parent nodes + for (var i = 0; i < parentNode.dynamicEdges.length; i++) { + var edge = parentNode.dynamicEdges[i]; + // handle circular edges + if (edge.toId == edge.fromId) { + this._addToContainedEdges(parentNode, childNode, edge); + } } - 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(); - } - */ }; -/***/ }, -/* 64 */ -/***/ function(module, exports, __webpack_require__) { - /** - * Creation of the ClusterMixin var. + * This adds an edge from the childNode to the rerouted edges of the parent node * - * This contains all the functions the Network object can use to employ clustering + * @param parentNode | Node object + * @param childNode | Node object + * @param edge | Edge object + * @private */ + exports._addToReroutedEdges = function(parentNode, childNode, edge) { + // create an array object if it does not yet exist for this childNode + // we store the edge in the rerouted edges so we can restore it when the cluster pops open + if (!(parentNode.reroutedEdges.hasOwnProperty(childNode.id))) { + parentNode.reroutedEdges[childNode.id] = []; + } + parentNode.reroutedEdges[childNode.id].push(edge); - /** - * This is only called in the constructor of the network object - * - */ - exports.startWithClustering = function() { - // cluster if the data set is big - this.clusterToFit(this.constants.clustering.initialMaxNodes, true); + // this edge becomes part of the dynamicEdges of the cluster node + parentNode.dynamicEdges.push(edge); + }; - // updates the lables after clustering - this.updateLabels(); - // this is called here because if clusterin is disabled, the start and stabilize are called in - // the setData function. - if (this.stabilize) { - this._stabilize(); - } - this.start(); - }; /** - * This function clusters until the initialMaxNodes has been reached + * This function connects an edge that was connected to a cluster node back to the child node. * - * @param {Number} maxNumberOfNodes - * @param {Boolean} reposition + * @param parentNode | Node object + * @param childNode | Node object + * @private */ - exports.clusterToFit = function(maxNumberOfNodes, reposition) { - var numberOfNodes = this.nodeIndices.length; + exports._connectEdgeBackToChild = function(parentNode, childNode) { + if (parentNode.reroutedEdges.hasOwnProperty(childNode.id)) { + for (var i = 0; i < parentNode.reroutedEdges[childNode.id].length; i++) { + var edge = parentNode.reroutedEdges[childNode.id][i]; + if (edge.originalFromId[edge.originalFromId.length-1] == childNode.id) { + edge.originalFromId.pop(); + edge.fromId = childNode.id; + edge.from = childNode; + } + else { + edge.originalToId.pop(); + edge.toId = childNode.id; + edge.to = childNode; + } - var maxLevels = 50; - var level = 0; + // append this edge to the list of edges connecting to the childnode + childNode.dynamicEdges.push(edge); - // we first cluster the hubs, then we pull in the outliers, repeat - while (numberOfNodes > maxNumberOfNodes && level < maxLevels) { - if (level % 3 == 0) { - this.forceAggregateHubs(true); - this.normalizeClusterLevels(); - } - else { - this.increaseClusterLevel(); // this also includes a cluster normalization + // remove the edge from the parent object + for (var j = 0; j < parentNode.dynamicEdges.length; j++) { + if (parentNode.dynamicEdges[j].id == edge.id) { + parentNode.dynamicEdges.splice(j,1); + break; + } + } } - - numberOfNodes = this.nodeIndices.length; - level += 1; - } - - // after the clustering we reposition the nodes to reduce the initial chaos - if (level > 0 && reposition == true) { - this.repositionNodes(); + // remove the entry from the rerouted edges + delete parentNode.reroutedEdges[childNode.id]; } - this._updateCalculationNodes(); }; + /** - * This function can be called to open up a specific cluster. It is only called by - * It will unpack the cluster back one level. + * When loops are clustered, an edge can be both in the rerouted array and the contained array. + * This function is called last to verify that all edges in dynamicEdges are in fact connected to the + * parentNode * - * @param node | Node object: cluster to open. + * @param parentNode | Node object + * @private */ - exports.openCluster = function(node) { - var isMovingBeforeClustering = this.moving; - if (node.clusterSize > this.constants.clustering.sectorThreshold && this._nodeInActiveArea(node) && - !(this._sector() == "default" && this.nodeIndices.length == 1)) { - // this loads a new sector, loads the nodes and edges and nodeIndices of it. - this._addSector(node); - var level = 0; - - // we decluster until we reach a decent number of nodes - while ((this.nodeIndices.length < this.constants.clustering.initialMaxNodes) && (level < 10)) { - this.decreaseClusterLevel(); - level += 1; + exports._validateEdges = function(parentNode) { + for (var i = 0; i < parentNode.dynamicEdges.length; i++) { + var edge = parentNode.dynamicEdges[i]; + if (parentNode.id != edge.toId && parentNode.id != edge.fromId) { + parentNode.dynamicEdges.splice(i,1); } - - } - else { - this._expandClusterNode(node,false,true); - - // update the index list, dynamic edges and labels - this._updateNodeIndexList(); - this._updateDynamicEdges(); - this._updateCalculationNodes(); - this.updateLabels(); - } - - // if the simulation was settled, we restart the simulation if a cluster has been formed or expanded - if (this.moving != isMovingBeforeClustering) { - this.start(); } }; /** - * This calls the updateClustes with default arguments + * This function released the contained edges back into the global domain and puts them back into the + * dynamic edges of both parent and child. + * + * @param {Node} parentNode | + * @param {Node} childNode | + * @private */ - exports.updateClustersDefault = function() { - if (this.constants.clustering.enabled == true) { - this.updateClusters(0,false,false); + exports._releaseContainedEdges = function(parentNode, childNode) { + for (var i = 0; i < parentNode.containedEdges[childNode.id].length; i++) { + var edge = parentNode.containedEdges[childNode.id][i]; + + // put the edge back in the global edges object + this.edges[edge.id] = edge; + + // put the edge back in the dynamic edges of the child and parent + childNode.dynamicEdges.push(edge); + parentNode.dynamicEdges.push(edge); } + // remove the entry from the contained edges + delete parentNode.containedEdges[childNode.id]; + }; - /** - * This function can be called to increase the cluster level. This means that the nodes with only one edge connection will - * be clustered with their connected node. This can be repeated as many times as needed. - * This can be called externally (by a keybind for instance) to reduce the complexity of big datasets. - */ - exports.increaseClusterLevel = function() { - this.updateClusters(-1,false,true); - }; - /** - * This function can be called to decrease the cluster level. This means that the nodes with only one edge connection will - * be unpacked if they are a cluster. This can be repeated as many times as needed. - * This can be called externally (by a key-bind for instance) to look into clusters without zooming. - */ - exports.decreaseClusterLevel = function() { - this.updateClusters(1,false,true); - }; + // ------------------- UTILITY FUNCTIONS ---------------------------- // /** - * This is the main clustering function. It clusters and declusters on zoom or forced - * This function clusters on zoom, it can be called with a predefined zoom direction - * If out, check if we can form clusters, if in, check if we can open clusters. - * This function is only called from _zoom() - * - * @param {Number} zoomDirection | -1 / 0 / +1 for zoomOut / determineByZoom / zoomIn - * @param {Boolean} recursive | enabled or disable recursive calling of the opening of clusters - * @param {Boolean} force | enabled or disable forcing - * @param {Boolean} doNotStart | if true do not call start - * + * This updates the node labels for all nodes (for debugging purposes) */ - exports.updateClusters = function(zoomDirection,recursive,force,doNotStart) { - var isMovingBeforeClustering = this.moving; - var amountOfNodes = this.nodeIndices.length; - - // on zoom out collapse the sector if the scale is at the level the sector was made - if (this.previousScale > this.scale && zoomDirection == 0) { - this._collapseSector(); + exports.updateLabels = function() { + var nodeId; + // update node labels + for (nodeId in this.nodes) { + if (this.nodes.hasOwnProperty(nodeId)) { + var node = this.nodes[nodeId]; + if (node.clusterSize > 1) { + node.label = "[".concat(String(node.clusterSize),"]"); + } + } } - // check if we zoom in or out - if (this.previousScale > this.scale || zoomDirection == -1) { // zoom out - // forming clusters when forced pulls outliers in. When not forced, the edge length of the - // outer nodes determines if it is being clustered - this._formClusters(force); - } - else if (this.previousScale < this.scale || zoomDirection == 1) { // zoom in - if (force == true) { - // _openClusters checks for each node if the formationScale of the cluster is smaller than - // the current scale and if so, declusters. When forced, all clusters are reduced by one step - this._openClusters(recursive,force); - } - else { - // if a cluster takes up a set percentage of the active window - this._openClustersBySize(); + // update node labels + for (nodeId in this.nodes) { + if (this.nodes.hasOwnProperty(nodeId)) { + node = this.nodes[nodeId]; + if (node.clusterSize == 1) { + if (node.originalLabel !== undefined) { + node.label = node.originalLabel; + } + else { + node.label = String(node.id); + } + } } } - this._updateNodeIndexList(); - // if a cluster was NOT formed and the user zoomed out, we try clustering by hubs - if (this.nodeIndices.length == amountOfNodes && (this.previousScale > this.scale || zoomDirection == -1)) { - this._aggregateHubs(force); - this._updateNodeIndexList(); - } + // /* Debug Override */ + // for (nodeId in this.nodes) { + // if (this.nodes.hasOwnProperty(nodeId)) { + // node = this.nodes[nodeId]; + // node.label = String(node.level); + // } + // } - // we now reduce chains. - if (this.previousScale > this.scale || zoomDirection == -1) { // zoom out - this.handleChains(); - this._updateNodeIndexList(); - } + }; - this.previousScale = this.scale; - // rest of the update the index list, dynamic edges and labels - this._updateDynamicEdges(); - this.updateLabels(); + /** + * We want to keep the cluster level distribution rather small. This means we do not want unclustered nodes + * if the rest of the nodes are already a few cluster levels in. + * To fix this we use this function. It determines the min and max cluster level and sends nodes that have not + * clustered enough to the clusterToSmallestNeighbours function. + */ + exports.normalizeClusterLevels = function() { + var maxLevel = 0; + var minLevel = 1e9; + var clusterLevel = 0; + var nodeId; - // if a cluster was formed, we increase the clusterSession - if (this.nodeIndices.length < amountOfNodes) { // this means a clustering operation has taken place - this.clusterSession += 1; - // if clusters have been made, we normalize the cluster level - this.normalizeClusterLevels(); + // we loop over all nodes in the list + for (nodeId in this.nodes) { + if (this.nodes.hasOwnProperty(nodeId)) { + clusterLevel = this.nodes[nodeId].clusterSessions.length; + if (maxLevel < clusterLevel) {maxLevel = clusterLevel;} + if (minLevel > clusterLevel) {minLevel = clusterLevel;} + } } - if (doNotStart == false || doNotStart === undefined) { - // if the simulation was settled, we restart the simulation if a cluster has been formed or expanded - if (this.moving != isMovingBeforeClustering) { - this.start(); + if (maxLevel - minLevel > this.constants.clustering.clusterLevelDifference) { + var amountOfNodes = this.nodeIndices.length; + var targetLevel = maxLevel - this.constants.clustering.clusterLevelDifference; + // we loop over all nodes in the list + for (nodeId in this.nodes) { + if (this.nodes.hasOwnProperty(nodeId)) { + if (this.nodes[nodeId].clusterSessions.length < targetLevel) { + this._clusterToSmallestNeighbour(this.nodes[nodeId]); + } + } + } + this._updateNodeIndexList(); + this._updateDynamicEdges(); + // if a cluster was formed, we increase the clusterSession + if (this.nodeIndices.length != amountOfNodes) { + this.clusterSession += 1; } } - - this._updateCalculationNodes(); }; + + /** - * This function handles the chains. It is called on every updateClusters(). + * This function determines if the cluster we want to decluster is in the active area + * this means around the zoom center + * + * @param {Node} node + * @returns {boolean} + * @private */ - exports.handleChains = function() { - // after clustering we check how many chains there are - var chainPercentage = this._getChainFraction(); - if (chainPercentage > this.constants.clustering.chainThreshold) { - this._reduceAmountOfChains(1 - this.constants.clustering.chainThreshold / chainPercentage) - - } + exports._nodeInActiveArea = function(node) { + return ( + Math.abs(node.x - this.areaCenter.x) <= this.constants.clustering.activeAreaBoxSize/this.scale + && + Math.abs(node.y - this.areaCenter.y) <= this.constants.clustering.activeAreaBoxSize/this.scale + ) }; + /** - * this functions starts clustering by hubs - * The minimum hub threshold is set globally + * This is an adaptation of the original repositioning function. This is called if the system is clustered initially + * It puts large clusters away from the center and randomizes the order. * - * @private */ - exports._aggregateHubs = function(force) { - this._getHubSize(); - this._formClustersByHub(force,false); + exports.repositionNodes = function() { + for (var i = 0; i < this.nodeIndices.length; i++) { + var node = this.nodes[this.nodeIndices[i]]; + if ((node.xFixed == false || node.yFixed == false)) { + var radius = 10 * 0.1*this.nodeIndices.length * Math.min(100,node.options.mass); + var angle = 2 * Math.PI * Math.random(); + if (node.xFixed == false) {node.x = radius * Math.cos(angle);} + if (node.yFixed == false) {node.y = radius * Math.sin(angle);} + this._repositionBezierNodes(node); + } + } }; /** - * This function is fired by keypress. It forces hubs to form. + * We determine how many connections denote an important hub. + * We take the mean + 2*std as the important hub size. (Assuming a normal distribution of data, ~2.2%) * + * @private */ - exports.forceAggregateHubs = function(doNotStart) { - var isMovingBeforeClustering = this.moving; - var amountOfNodes = this.nodeIndices.length; + exports._getHubSize = function() { + var average = 0; + var averageSquared = 0; + var hubCounter = 0; + var largestHub = 0; + + for (var i = 0; i < this.nodeIndices.length; i++) { + + var node = this.nodes[this.nodeIndices[i]]; + if (node.dynamicEdgesLength > largestHub) { + largestHub = node.dynamicEdgesLength; + } + average += node.dynamicEdgesLength; + averageSquared += Math.pow(node.dynamicEdgesLength,2); + hubCounter += 1; + } + average = average / hubCounter; + averageSquared = averageSquared / hubCounter; + + var variance = averageSquared - Math.pow(average,2); - this._aggregateHubs(true); + var standardDeviation = Math.sqrt(variance); - // update the index list, dynamic edges and labels - this._updateNodeIndexList(); - this._updateDynamicEdges(); - this.updateLabels(); + this.hubThreshold = Math.floor(average + 2*standardDeviation); - // if a cluster was formed, we increase the clusterSession - if (this.nodeIndices.length != amountOfNodes) { - this.clusterSession += 1; + // always have at least one to cluster + if (this.hubThreshold > largestHub) { + this.hubThreshold = largestHub; } - if (doNotStart == false || doNotStart === undefined) { - // if the simulation was settled, we restart the simulation if a cluster has been formed or expanded - if (this.moving != isMovingBeforeClustering) { - this.start(); - } - } + // console.log("average",average,"averageSQ",averageSquared,"var",variance,"std",standardDeviation); + // console.log("hubThreshold:",this.hubThreshold); }; + /** - * If a cluster takes up more than a set percentage of the screen, open the cluster + * We reduce the amount of "extension nodes" or chains. These are not quickly clustered with the outliers and hubs methods + * with this amount we can cluster specifically on these chains. * + * @param {Number} fraction | between 0 and 1, the percentage of chains to reduce * @private */ - exports._openClustersBySize = function() { + exports._reduceAmountOfChains = function(fraction) { + this.hubThreshold = 2; + var reduceAmount = Math.floor(this.nodeIndices.length * fraction); for (var nodeId in this.nodes) { if (this.nodes.hasOwnProperty(nodeId)) { - var node = this.nodes[nodeId]; - if (node.inView() == true) { - if ((node.width*this.scale > this.constants.clustering.screenSizeThreshold * this.frame.canvas.clientWidth) || - (node.height*this.scale > this.constants.clustering.screenSizeThreshold * this.frame.canvas.clientHeight)) { - this.openCluster(node); + if (this.nodes[nodeId].dynamicEdgesLength == 2 && this.nodes[nodeId].dynamicEdges.length >= 2) { + if (reduceAmount > 0) { + this._formClusterFromHub(this.nodes[nodeId],true,true,1); + reduceAmount -= 1; } } } } }; - /** - * This function loops over all nodes in the nodeIndices list. For each node it checks if it is a cluster and if it - * has to be opened based on the current zoom level. + * We get the amount of "extension nodes" or chains. These are not quickly clustered with the outliers and hubs methods + * with this amount we can cluster specifically on these chains. * * @private */ - exports._openClusters = function(recursive,force) { - for (var i = 0; i < this.nodeIndices.length; i++) { - var node = this.nodes[this.nodeIndices[i]]; - this._expandClusterNode(node,recursive,force); - this._updateCalculationNodes(); + exports._getChainFraction = function() { + var chains = 0; + var total = 0; + for (var nodeId in this.nodes) { + if (this.nodes.hasOwnProperty(nodeId)) { + if (this.nodes[nodeId].dynamicEdgesLength == 2 && this.nodes[nodeId].dynamicEdges.length >= 2) { + chains += 1; + } + total += 1; + } } + return chains/total; }; + +/***/ }, +/* 61 */ +/***/ function(module, exports, __webpack_require__) { + + var util = __webpack_require__(1); + var Node = __webpack_require__(40); + /** - * This function checks if a node has to be opened. This is done by checking the zoom level. - * If the node contains child nodes, this function is recursively called on the child nodes as well. - * This recursive behaviour is optional and can be set by the recursive argument. + * Creation of the SectorMixin var. + * + * This contains all the functions the Network object can use to employ the sector system. + * The sector system is always used by Network, though the benefits only apply to the use of clustering. + * If clustering is not used, there is no overhead except for a duplicate object with references to nodes and edges. + */ + + /** + * This function is only called by the setData function of the Network object. + * This loads the global references into the active sector. This initializes the sector. * - * @param {Node} parentNode | to check for cluster and expand - * @param {Boolean} recursive | enabled or disable recursive calling - * @param {Boolean} force | enabled or disable forcing - * @param {Boolean} [openAll] | This will recursively force all nodes in the parent to be released * @private */ - exports._expandClusterNode = function(parentNode, recursive, force, openAll) { - // first check if node is a cluster - if (parentNode.clusterSize > 1) { - // this means that on a double tap event or a zoom event, the cluster fully unpacks if it is smaller than 20 - if (parentNode.clusterSize < this.constants.clustering.sectorThreshold) { - openAll = true; - } - recursive = openAll ? true : recursive; + exports._putDataInSector = function() { + this.sectors["active"][this._sector()].nodes = this.nodes; + this.sectors["active"][this._sector()].edges = this.edges; + this.sectors["active"][this._sector()].nodeIndices = this.nodeIndices; + }; - // if the last child has been added on a smaller scale than current scale decluster - if (parentNode.formationScale < this.scale || force == true) { - // we will check if any of the contained child nodes should be removed from the cluster - for (var containedNodeId in parentNode.containedNodes) { - if (parentNode.containedNodes.hasOwnProperty(containedNodeId)) { - var childNode = parentNode.containedNodes[containedNodeId]; - // force expand will expand the largest cluster size clusters. Since we cluster from outside in, we assume that - // the largest cluster is the one that comes from outside - if (force == true) { - if (childNode.clusterSession == parentNode.clusterSessions[parentNode.clusterSessions.length-1] - || openAll) { - this._expelChildFromParent(parentNode,containedNodeId,recursive,force,openAll); - } - } - else { - if (this._nodeInActiveArea(parentNode)) { - this._expelChildFromParent(parentNode,containedNodeId,recursive,force,openAll); - } - } - } - } - } + /** + * /** + * This function sets the global references to nodes, edges and nodeIndices back to + * those of the supplied (active) sector. If a type is defined, do the specific type + * + * @param {String} sectorId + * @param {String} [sectorType] | "active" or "frozen" + * @private + */ + exports._switchToSector = function(sectorId, sectorType) { + if (sectorType === undefined || sectorType == "active") { + this._switchToActiveSector(sectorId); + } + else { + this._switchToFrozenSector(sectorId); } }; + /** - * ONLY CALLED FROM _expandClusterNode - * - * This function will expel a child_node from a parent_node. This is to de-cluster the node. This function will remove - * the child node from the parent contained_node object and put it back into the global nodes object. - * The same holds for the edge that was connected to the child node. It is moved back into the global edges object. + * This function sets the global references to nodes, edges and nodeIndices back to + * those of the supplied active sector. * - * @param {Node} parentNode | the parent node - * @param {String} containedNodeId | child_node id as it is contained in the containedNodes object of the parent node - * @param {Boolean} recursive | This will also check if the child needs to be expanded. - * With force and recursive both true, the entire cluster is unpacked - * @param {Boolean} force | This will disregard the zoom level and will expel this child from the parent - * @param {Boolean} openAll | This will recursively force all nodes in the parent to be released + * @param sectorId * @private */ - exports._expelChildFromParent = function(parentNode, containedNodeId, recursive, force, openAll) { - var childNode = parentNode.containedNodes[containedNodeId]; - - // if child node has been added on smaller scale than current, kick out - if (childNode.formationScale < this.scale || force == true) { - // unselect all selected items - this._unselectAll(); - - // put the child node back in the global nodes object - this.nodes[containedNodeId] = childNode; - - // release the contained edges from this childNode back into the global edges - this._releaseContainedEdges(parentNode,childNode); - - // reconnect rerouted edges to the childNode - this._connectEdgeBackToChild(parentNode,childNode); - - // validate all edges in dynamicEdges - this._validateEdges(parentNode); - - // undo the changes from the clustering operation on the parent node - parentNode.options.mass -= childNode.options.mass; - parentNode.clusterSize -= childNode.clusterSize; - parentNode.options.fontSize = Math.min(this.constants.clustering.maxFontSize, this.constants.nodes.fontSize + this.constants.clustering.fontSizeMultiplier*(parentNode.clusterSize-1)); - parentNode.dynamicEdgesLength = parentNode.dynamicEdges.length; - - // place the child node near the parent, not at the exact same location to avoid chaos in the system - childNode.x = parentNode.x + parentNode.growthIndicator * (0.5 - Math.random()); - childNode.y = parentNode.y + parentNode.growthIndicator * (0.5 - Math.random()); - - // remove node from the list - delete parentNode.containedNodes[containedNodeId]; + exports._switchToActiveSector = function(sectorId) { + this.nodeIndices = this.sectors["active"][sectorId]["nodeIndices"]; + this.nodes = this.sectors["active"][sectorId]["nodes"]; + this.edges = this.sectors["active"][sectorId]["edges"]; + }; - // check if there are other childs with this clusterSession in the parent. - var othersPresent = false; - for (var childNodeId in parentNode.containedNodes) { - if (parentNode.containedNodes.hasOwnProperty(childNodeId)) { - if (parentNode.containedNodes[childNodeId].clusterSession == childNode.clusterSession) { - othersPresent = true; - break; - } - } - } - // if there are no others, remove the cluster session from the list - if (othersPresent == false) { - parentNode.clusterSessions.pop(); - } - this._repositionBezierNodes(childNode); - // this._repositionBezierNodes(parentNode); + /** + * This function sets the global references to nodes, edges and nodeIndices back to + * those of the supplied active sector. + * + * @private + */ + exports._switchToSupportSector = function() { + this.nodeIndices = this.sectors["support"]["nodeIndices"]; + this.nodes = this.sectors["support"]["nodes"]; + this.edges = this.sectors["support"]["edges"]; + }; - // remove the clusterSession from the child node - childNode.clusterSession = 0; - // recalculate the size of the node on the next time the node is rendered - parentNode.clearSizeCache(); + /** + * This function sets the global references to nodes, edges and nodeIndices back to + * those of the supplied frozen sector. + * + * @param sectorId + * @private + */ + exports._switchToFrozenSector = function(sectorId) { + this.nodeIndices = this.sectors["frozen"][sectorId]["nodeIndices"]; + this.nodes = this.sectors["frozen"][sectorId]["nodes"]; + this.edges = this.sectors["frozen"][sectorId]["edges"]; + }; - // restart the simulation to reorganise all nodes - this.moving = true; - } - // check if a further expansion step is possible if recursivity is enabled - if (recursive == true) { - this._expandClusterNode(childNode,recursive,force,openAll); - } + /** + * This function sets the global references to nodes, edges and nodeIndices back to + * those of the currently active sector. + * + * @private + */ + exports._loadLatestSector = function() { + this._switchToSector(this._sector()); }; /** - * position the bezier nodes at the center of the edges + * This function returns the currently active sector Id * - * @param node + * @returns {String} * @private */ - exports._repositionBezierNodes = function(node) { - for (var i = 0; i < node.dynamicEdges.length; i++) { - node.dynamicEdges[i].positionBezierNode(); - } + exports._sector = function() { + return this.activeSector[this.activeSector.length-1]; }; /** - * This function checks if any nodes at the end of their trees have edges below a threshold length - * This function is called only from updateClusters() - * forceLevelCollapse ignores the length of the edge and collapses one level - * This means that a node with only one edge will be clustered with its connected node + * This function returns the previously active sector Id * + * @returns {String} * @private - * @param {Boolean} force */ - exports._formClusters = function(force) { - if (force == false) { - this._formClustersByZoom(); + exports._previousSector = function() { + if (this.activeSector.length > 1) { + return this.activeSector[this.activeSector.length-2]; } else { - this._forceClustersByZoom(); + throw new TypeError('there are not enough sectors in the this.activeSector array.'); } }; /** - * This function handles the clustering by zooming out, this is based on a minimum edge distance + * We add the active sector at the end of the this.activeSector array + * This ensures it is the currently active sector returned by _sector() and it reaches the top + * of the activeSector stack. When we reverse our steps we move from the end to the beginning of this stack. * + * @param newId * @private */ - exports._formClustersByZoom = function() { - var dx,dy,length, - minLength = this.constants.clustering.clusterEdgeThreshold/this.scale; - - // check if any edges are shorter than minLength and start the clustering - // the clustering favours the node with the larger mass - for (var edgeId in this.edges) { - if (this.edges.hasOwnProperty(edgeId)) { - var edge = this.edges[edgeId]; - if (edge.connected) { - if (edge.toId != edge.fromId) { - dx = (edge.to.x - edge.from.x); - dy = (edge.to.y - edge.from.y); - length = Math.sqrt(dx * dx + dy * dy); - + exports._setActiveSector = function(newId) { + this.activeSector.push(newId); + }; - if (length < minLength) { - // first check which node is larger - var parentNode = edge.from; - var childNode = edge.to; - if (edge.to.options.mass > edge.from.options.mass) { - parentNode = edge.to; - childNode = edge.from; - } - if (childNode.dynamicEdgesLength == 1) { - this._addToCluster(parentNode,childNode,false); - } - else if (parentNode.dynamicEdgesLength == 1) { - this._addToCluster(childNode,parentNode,false); - } - } - } - } - } - } + /** + * We remove the currently active sector id from the active sector stack. This happens when + * we reactivate the previously active sector + * + * @private + */ + exports._forgetLastSector = function() { + this.activeSector.pop(); }; + /** - * This function forces the network to cluster all nodes with only one connecting edge to their - * connected node. + * This function creates a new active sector with the supplied newId. This newId + * is the expanding node id. * + * @param {String} newId | Id of the new active sector * @private */ - exports._forceClustersByZoom = function() { - for (var nodeId in this.nodes) { - // another node could have absorbed this child. - if (this.nodes.hasOwnProperty(nodeId)) { - var childNode = this.nodes[nodeId]; - - // the edges can be swallowed by another decrease - if (childNode.dynamicEdgesLength == 1 && childNode.dynamicEdges.length != 0) { - var edge = childNode.dynamicEdges[0]; - var parentNode = (edge.toId == childNode.id) ? this.nodes[edge.fromId] : this.nodes[edge.toId]; + exports._createNewSector = function(newId) { + // create the new sector + this.sectors["active"][newId] = {"nodes":{}, + "edges":{}, + "nodeIndices":[], + "formationScale": this.scale, + "drawingNode": undefined}; - // group to the largest node - if (childNode.id != parentNode.id) { - if (parentNode.options.mass > childNode.options.mass) { - this._addToCluster(parentNode,childNode,true); - } - else { - this._addToCluster(childNode,parentNode,true); - } + // create the new sector render node. This gives visual feedback that you are in a new sector. + this.sectors["active"][newId]['drawingNode'] = new Node( + {id:newId, + color: { + background: "#eaefef", + border: "495c5e" } - } - } - } + },{},{},this.constants); + this.sectors["active"][newId]['drawingNode'].clusterSize = 2; }; /** - * To keep the nodes of roughly equal size we normalize the cluster levels. - * This function clusters a node to its smallest connected neighbour. + * This function removes the currently active sector. This is called when we create a new + * active sector. * - * @param node + * @param {String} sectorId | Id of the active sector that will be removed * @private */ - exports._clusterToSmallestNeighbour = function(node) { - var smallestNeighbour = -1; - var smallestNeighbourNode = null; - for (var i = 0; i < node.dynamicEdges.length; i++) { - if (node.dynamicEdges[i] !== undefined) { - var neighbour = null; - if (node.dynamicEdges[i].fromId != node.id) { - neighbour = node.dynamicEdges[i].from; - } - else if (node.dynamicEdges[i].toId != node.id) { - neighbour = node.dynamicEdges[i].to; - } - + exports._deleteActiveSector = function(sectorId) { + delete this.sectors["active"][sectorId]; + }; - if (neighbour != null && smallestNeighbour > neighbour.clusterSessions.length) { - smallestNeighbour = neighbour.clusterSessions.length; - smallestNeighbourNode = neighbour; - } - } - } - if (neighbour != null && this.nodes[neighbour.id] !== undefined) { - this._addToCluster(neighbour, node, true); - } + /** + * This function removes the currently active sector. This is called when we reactivate + * the previously active sector. + * + * @param {String} sectorId | Id of the active sector that will be removed + * @private + */ + exports._deleteFrozenSector = function(sectorId) { + delete this.sectors["frozen"][sectorId]; }; /** - * This function forms clusters from hubs, it loops over all nodes + * Freezing an active sector means moving it from the "active" object to the "frozen" object. + * We copy the references, then delete the active entree. * - * @param {Boolean} force | Disregard zoom level - * @param {Boolean} onlyEqual | This only clusters a hub with a specific number of edges + * @param sectorId * @private */ - exports._formClustersByHub = function(force, onlyEqual) { - // we loop over all nodes in the list - for (var nodeId in this.nodes) { - // we check if it is still available since it can be used by the clustering in this loop - if (this.nodes.hasOwnProperty(nodeId)) { - this._formClusterFromHub(this.nodes[nodeId],force,onlyEqual); - } - } + exports._freezeSector = function(sectorId) { + // we move the set references from the active to the frozen stack. + this.sectors["frozen"][sectorId] = this.sectors["active"][sectorId]; + + // we have moved the sector data into the frozen set, we now remove it from the active set + this._deleteActiveSector(sectorId); }; + /** - * This function forms a cluster from a specific preselected hub node + * This is the reverse operation of _freezeSector. Activating means moving the sector from the "frozen" + * object to the "active" object. * - * @param {Node} hubNode | the node we will cluster as a hub - * @param {Boolean} force | Disregard zoom level - * @param {Boolean} onlyEqual | This only clusters a hub with a specific number of edges - * @param {Number} [absorptionSizeOffset] | + * @param sectorId * @private */ - exports._formClusterFromHub = function(hubNode, force, onlyEqual, absorptionSizeOffset) { - if (absorptionSizeOffset === undefined) { - absorptionSizeOffset = 0; - } - // we decide if the node is a hub - if ((hubNode.dynamicEdgesLength >= this.hubThreshold && onlyEqual == false) || - (hubNode.dynamicEdgesLength == this.hubThreshold && onlyEqual == true)) { - // initialize variables - var dx,dy,length; - var minLength = this.constants.clustering.clusterEdgeThreshold/this.scale; - var allowCluster = false; + exports._activateSector = function(sectorId) { + // we move the set references from the frozen to the active stack. + this.sectors["active"][sectorId] = this.sectors["frozen"][sectorId]; - // we create a list of edges because the dynamicEdges change over the course of this loop - var edgesIdarray = []; - var amountOfInitialEdges = hubNode.dynamicEdges.length; - for (var j = 0; j < amountOfInitialEdges; j++) { - edgesIdarray.push(hubNode.dynamicEdges[j].id); - } + // we have moved the sector data into the active set, we now remove it from the frozen stack + this._deleteFrozenSector(sectorId); + }; - // if the hub clustering is not forces, we check if one of the edges connected - // to a cluster is small enough based on the constants.clustering.clusterEdgeThreshold - if (force == false) { - allowCluster = false; - for (j = 0; j < amountOfInitialEdges; j++) { - var edge = this.edges[edgesIdarray[j]]; - if (edge !== undefined) { - if (edge.connected) { - if (edge.toId != edge.fromId) { - dx = (edge.to.x - edge.from.x); - dy = (edge.to.y - edge.from.y); - length = Math.sqrt(dx * dx + dy * dy); - if (length < minLength) { - allowCluster = true; - break; - } - } - } - } - } + /** + * This function merges the data from the currently active sector with a frozen sector. This is used + * in the process of reverting back to the previously active sector. + * The data that is placed in the frozen (the previously active) sector is the node that has been removed from it + * upon the creation of a new active sector. + * + * @param sectorId + * @private + */ + exports._mergeThisWithFrozen = function(sectorId) { + // copy all nodes + for (var nodeId in this.nodes) { + if (this.nodes.hasOwnProperty(nodeId)) { + this.sectors["frozen"][sectorId]["nodes"][nodeId] = this.nodes[nodeId]; } + } - // start the clustering if allowed - if ((!force && allowCluster) || force) { - // we loop over all edges INITIALLY connected to this hub - for (j = 0; j < amountOfInitialEdges; j++) { - edge = this.edges[edgesIdarray[j]]; - // the edge can be clustered by this function in a previous loop - if (edge !== undefined) { - var childNode = this.nodes[(edge.fromId == hubNode.id) ? edge.toId : edge.fromId]; - // we do not want hubs to merge with other hubs nor do we want to cluster itself. - if ((childNode.dynamicEdges.length <= (this.hubThreshold + absorptionSizeOffset)) && - (childNode.id != hubNode.id)) { - this._addToCluster(hubNode,childNode,force); - } - } - } + // copy all edges (if not fully clustered, else there are no edges) + for (var edgeId in this.edges) { + if (this.edges.hasOwnProperty(edgeId)) { + this.sectors["frozen"][sectorId]["edges"][edgeId] = this.edges[edgeId]; } } + + // merge the nodeIndices + for (var i = 0; i < this.nodeIndices.length; i++) { + this.sectors["frozen"][sectorId]["nodeIndices"].push(this.nodeIndices[i]); + } }; - /** - * This function adds the child node to the parent node, creating a cluster if it is not already. + * This clusters the sector to one cluster. It was a single cluster before this process started so + * we revert to that state. The clusterToFit function with a maximum size of 1 node does this. * - * @param {Node} parentNode | this is the node that will house the child node - * @param {Node} childNode | this node will be deleted from the global this.nodes and stored in the parent node - * @param {Boolean} force | true will only update the remainingEdges at the very end of the clustering, ensuring single level collapse * @private */ - exports._addToCluster = function(parentNode, childNode, force) { - // join child node in the parent node - parentNode.containedNodes[childNode.id] = childNode; - - // manage all the edges connected to the child and parent nodes - for (var i = 0; i < childNode.dynamicEdges.length; i++) { - var edge = childNode.dynamicEdges[i]; - if (edge.toId == parentNode.id || edge.fromId == parentNode.id) { // edge connected to parentNode - this._addToContainedEdges(parentNode,childNode,edge); - } - else { - this._connectEdgeToCluster(parentNode,childNode,edge); - } - } - // a contained node has no dynamic edges. - childNode.dynamicEdges = []; - - // remove circular edges from clusters - this._containCircularEdgesFromNode(parentNode,childNode); + exports._collapseThisToSingleCluster = function() { + this.clusterToFit(1,false); + }; - // remove the childNode from the global nodes object - delete this.nodes[childNode.id]; + /** + * We create a new active sector from the node that we want to open. + * + * @param node + * @private + */ + exports._addSector = function(node) { + // this is the currently active sector + var sector = this._sector(); - // update the properties of the child and parent - var massBefore = parentNode.options.mass; - childNode.clusterSession = this.clusterSession; - parentNode.options.mass += childNode.options.mass; - parentNode.clusterSize += childNode.clusterSize; - parentNode.options.fontSize = Math.min(this.constants.clustering.maxFontSize, this.constants.nodes.fontSize + this.constants.clustering.fontSizeMultiplier*parentNode.clusterSize); + // // this should allow me to select nodes from a frozen set. + // if (this.sectors['active'][sector]["nodes"].hasOwnProperty(node.id)) { + // console.log("the node is part of the active sector"); + // } + // else { + // console.log("I dont know what the fuck happened!!"); + // } - // keep track of the clustersessions so we can open the cluster up as it has been formed. - if (parentNode.clusterSessions[parentNode.clusterSessions.length - 1] != this.clusterSession) { - parentNode.clusterSessions.push(this.clusterSession); - } + // when we switch to a new sector, we remove the node that will be expanded from the current nodes list. + delete this.nodes[node.id]; - // forced clusters only open from screen size and double tap - if (force == true) { - // parentNode.formationScale = Math.pow(1 - (1.0/11.0),this.clusterSession+3); - parentNode.formationScale = 0; - } - else { - parentNode.formationScale = this.scale; // The latest child has been added on this scale - } + var unqiueIdentifier = util.randomUUID(); - // recalculate the size of the node on the next time the node is rendered - parentNode.clearSizeCache(); + // we fully freeze the currently active sector + this._freezeSector(sector); - // set the pop-out scale for the childnode - parentNode.containedNodes[childNode.id].formationScale = parentNode.formationScale; + // we create a new active sector. This sector has the Id of the node to ensure uniqueness + this._createNewSector(unqiueIdentifier); - // nullify the movement velocity of the child, this is to avoid hectic behaviour - childNode.clearVelocity(); + // we add the active sector to the sectors array to be able to revert these steps later on + this._setActiveSector(unqiueIdentifier); - // the mass has altered, preservation of energy dictates the velocity to be updated - parentNode.updateVelocity(massBefore); + // we redirect the global references to the new sector's references. this._sector() now returns unqiueIdentifier + this._switchToSector(this._sector()); - // restart the simulation to reorganise all nodes - this.moving = true; + // finally we add the node we removed from our previous active sector to the new active sector + this.nodes[node.id] = node; }; /** - * This function will apply the changes made to the remainingEdges during the formation of the clusters. - * This is a seperate function to allow for level-wise collapsing of the node barnesHutTree. - * It has to be called if a level is collapsed. It is called by _formClusters(). + * We close the sector that is currently open and revert back to the one before. + * If the active sector is the "default" sector, nothing happens. + * * @private */ - exports._updateDynamicEdges = function() { - for (var i = 0; i < this.nodeIndices.length; i++) { - var node = this.nodes[this.nodeIndices[i]]; - node.dynamicEdgesLength = node.dynamicEdges.length; + exports._collapseSector = function() { + // the currently active sector + var sector = this._sector(); - // this corrects for multiple edges pointing at the same other node - var correction = 0; - if (node.dynamicEdgesLength > 1) { - for (var j = 0; j < node.dynamicEdgesLength - 1; j++) { - var edgeToId = node.dynamicEdges[j].toId; - var edgeFromId = node.dynamicEdges[j].fromId; - for (var k = j+1; k < node.dynamicEdgesLength; k++) { - if ((node.dynamicEdges[k].toId == edgeToId && node.dynamicEdges[k].fromId == edgeFromId) || - (node.dynamicEdges[k].fromId == edgeToId && node.dynamicEdges[k].toId == edgeFromId)) { - correction += 1; - } - } - } + // we cannot collapse the default sector + if (sector != "default") { + if ((this.nodeIndices.length == 1) || + (this.sectors["active"][sector]["drawingNode"].width*this.scale < this.constants.clustering.screenSizeThreshold * this.frame.canvas.clientWidth) || + (this.sectors["active"][sector]["drawingNode"].height*this.scale < this.constants.clustering.screenSizeThreshold * this.frame.canvas.clientHeight)) { + var previousSector = this._previousSector(); + + // we collapse the sector back to a single cluster + this._collapseThisToSingleCluster(); + + // we move the remaining nodes, edges and nodeIndices to the previous sector. + // This previous sector is the one we will reactivate + this._mergeThisWithFrozen(previousSector); + + // the previously active (frozen) sector now has all the data from the currently active sector. + // we can now delete the active sector. + this._deleteActiveSector(sector); + + // we activate the previously active (and currently frozen) sector. + this._activateSector(previousSector); + + // we load the references from the newly active sector into the global references + this._switchToSector(previousSector); + + // we forget the previously active sector because we reverted to the one before + this._forgetLastSector(); + + // finally, we update the node index list. + this._updateNodeIndexList(); + + // we refresh the list with calulation nodes and calculation node indices. + this._updateCalculationNodes(); } - node.dynamicEdgesLength -= correction; } }; /** - * This adds an edge from the childNode to the contained edges of the parent node + * This runs a function in all active sectors. This is used in _redraw() and the _initializeForceCalculation(). * - * @param parentNode | Node object - * @param childNode | Node object - * @param edge | Edge object + * @param {String} runFunction | This is the NAME of a function we want to call in all active sectors + * | we dont pass the function itself because then the "this" is the window object + * | instead of the Network object + * @param {*} [argument] | Optional: arguments to pass to the runFunction * @private */ - exports._addToContainedEdges = function(parentNode, childNode, edge) { - // create an array object if it does not yet exist for this childNode - if (!(parentNode.containedEdges.hasOwnProperty(childNode.id))) { - parentNode.containedEdges[childNode.id] = [] + exports._doInAllActiveSectors = function(runFunction,argument) { + var returnValues = []; + if (argument === undefined) { + for (var sector in this.sectors["active"]) { + if (this.sectors["active"].hasOwnProperty(sector)) { + // switch the global references to those of this sector + this._switchToActiveSector(sector); + returnValues.push( this[runFunction]() ); + } + } } - // add this edge to the list - parentNode.containedEdges[childNode.id].push(edge); - - // remove the edge from the global edges object - delete this.edges[edge.id]; - - // remove the edge from the parent object - for (var i = 0; i < parentNode.dynamicEdges.length; i++) { - if (parentNode.dynamicEdges[i].id == edge.id) { - parentNode.dynamicEdges.splice(i,1); - break; + else { + for (var sector in this.sectors["active"]) { + if (this.sectors["active"].hasOwnProperty(sector)) { + // switch the global references to those of this sector + this._switchToActiveSector(sector); + var args = Array.prototype.splice.call(arguments, 1); + if (args.length > 1) { + returnValues.push( this[runFunction](args[0],args[1]) ); + } + else { + returnValues.push( this[runFunction](argument) ); + } + } } } + // we revert the global references back to our active sector + this._loadLatestSector(); + return returnValues; }; + /** - * This function connects an edge that was connected to a child node to the parent node. - * It keeps track of which nodes it has been connected to with the originalId array. + * This runs a function in all active sectors. This is used in _redraw() and the _initializeForceCalculation(). * - * @param {Node} parentNode | Node object - * @param {Node} childNode | Node object - * @param {Edge} edge | Edge object + * @param {String} runFunction | This is the NAME of a function we want to call in all active sectors + * | we dont pass the function itself because then the "this" is the window object + * | instead of the Network object + * @param {*} [argument] | Optional: arguments to pass to the runFunction * @private */ - exports._connectEdgeToCluster = function(parentNode, childNode, edge) { - // handle circular edges - if (edge.toId == edge.fromId) { - this._addToContainedEdges(parentNode, childNode, edge); + exports._doInSupportSector = function(runFunction,argument) { + var returnValues = false; + if (argument === undefined) { + this._switchToSupportSector(); + returnValues = this[runFunction](); } else { - if (edge.toId == childNode.id) { // edge connected to other node on the "to" side - edge.originalToId.push(childNode.id); - edge.to = parentNode; - edge.toId = parentNode.id; + this._switchToSupportSector(); + var args = Array.prototype.splice.call(arguments, 1); + if (args.length > 1) { + returnValues = this[runFunction](args[0],args[1]); } - else { // edge connected to other node with the "from" side - - edge.originalFromId.push(childNode.id); - edge.from = parentNode; - edge.fromId = parentNode.id; + else { + returnValues = this[runFunction](argument); } - - this._addToReroutedEdges(parentNode,childNode,edge); } + // we revert the global references back to our active sector + this._loadLatestSector(); + return returnValues; }; /** - * If a node is connected to itself, a circular edge is drawn. When clustering we want to contain - * these edges inside of the cluster. + * This runs a function in all frozen sectors. This is used in the _redraw(). * - * @param parentNode - * @param childNode + * @param {String} runFunction | This is the NAME of a function we want to call in all active sectors + * | we don't pass the function itself because then the "this" is the window object + * | instead of the Network object + * @param {*} [argument] | Optional: arguments to pass to the runFunction * @private */ - exports._containCircularEdgesFromNode = function(parentNode, childNode) { - // manage all the edges connected to the child and parent nodes - for (var i = 0; i < parentNode.dynamicEdges.length; i++) { - var edge = parentNode.dynamicEdges[i]; - // handle circular edges - if (edge.toId == edge.fromId) { - this._addToContainedEdges(parentNode, childNode, edge); + exports._doInAllFrozenSectors = function(runFunction,argument) { + if (argument === undefined) { + for (var sector in this.sectors["frozen"]) { + if (this.sectors["frozen"].hasOwnProperty(sector)) { + // switch the global references to those of this sector + this._switchToFrozenSector(sector); + this[runFunction](); + } + } + } + else { + for (var sector in this.sectors["frozen"]) { + if (this.sectors["frozen"].hasOwnProperty(sector)) { + // switch the global references to those of this sector + this._switchToFrozenSector(sector); + var args = Array.prototype.splice.call(arguments, 1); + if (args.length > 1) { + this[runFunction](args[0],args[1]); + } + else { + this[runFunction](argument); + } + } } } + this._loadLatestSector(); }; /** - * This adds an edge from the childNode to the rerouted edges of the parent node + * This runs a function in all sectors. This is used in the _redraw(). * - * @param parentNode | Node object - * @param childNode | Node object - * @param edge | Edge object + * @param {String} runFunction | This is the NAME of a function we want to call in all active sectors + * | we don't pass the function itself because then the "this" is the window object + * | instead of the Network object + * @param {*} [argument] | Optional: arguments to pass to the runFunction * @private */ - exports._addToReroutedEdges = function(parentNode, childNode, edge) { - // create an array object if it does not yet exist for this childNode - // we store the edge in the rerouted edges so we can restore it when the cluster pops open - if (!(parentNode.reroutedEdges.hasOwnProperty(childNode.id))) { - parentNode.reroutedEdges[childNode.id] = []; + exports._doInAllSectors = function(runFunction,argument) { + var args = Array.prototype.splice.call(arguments, 1); + if (argument === undefined) { + this._doInAllActiveSectors(runFunction); + this._doInAllFrozenSectors(runFunction); } - parentNode.reroutedEdges[childNode.id].push(edge); + else { + if (args.length > 1) { + this._doInAllActiveSectors(runFunction,args[0],args[1]); + this._doInAllFrozenSectors(runFunction,args[0],args[1]); + } + else { + this._doInAllActiveSectors(runFunction,argument); + this._doInAllFrozenSectors(runFunction,argument); + } + } + }; - // this edge becomes part of the dynamicEdges of the cluster node - parentNode.dynamicEdges.push(edge); - }; + /** + * This clears the nodeIndices list. We cannot use this.nodeIndices = [] because we would break the link with the + * active sector. Thus we clear the nodeIndices in the active sector, then reconnect the this.nodeIndices to it. + * + * @private + */ + exports._clearNodeIndexList = function() { + var sector = this._sector(); + this.sectors["active"][sector]["nodeIndices"] = []; + this.nodeIndices = this.sectors["active"][sector]["nodeIndices"]; + }; /** - * This function connects an edge that was connected to a cluster node back to the child node. + * Draw the encompassing sector node * - * @param parentNode | Node object - * @param childNode | Node object + * @param ctx + * @param sectorType * @private */ - exports._connectEdgeBackToChild = function(parentNode, childNode) { - if (parentNode.reroutedEdges.hasOwnProperty(childNode.id)) { - for (var i = 0; i < parentNode.reroutedEdges[childNode.id].length; i++) { - var edge = parentNode.reroutedEdges[childNode.id][i]; - if (edge.originalFromId[edge.originalFromId.length-1] == childNode.id) { - edge.originalFromId.pop(); - edge.fromId = childNode.id; - edge.from = childNode; - } - else { - edge.originalToId.pop(); - edge.toId = childNode.id; - edge.to = childNode; - } + exports._drawSectorNodes = function(ctx,sectorType) { + var minY = 1e9, maxY = -1e9, minX = 1e9, maxX = -1e9, node; + for (var sector in this.sectors[sectorType]) { + if (this.sectors[sectorType].hasOwnProperty(sector)) { + if (this.sectors[sectorType][sector]["drawingNode"] !== undefined) { - // append this edge to the list of edges connecting to the childnode - childNode.dynamicEdges.push(edge); + this._switchToSector(sector,sectorType); - // remove the edge from the parent object - for (var j = 0; j < parentNode.dynamicEdges.length; j++) { - if (parentNode.dynamicEdges[j].id == edge.id) { - parentNode.dynamicEdges.splice(j,1); - break; + minY = 1e9; maxY = -1e9; minX = 1e9; maxX = -1e9; + for (var nodeId in this.nodes) { + if (this.nodes.hasOwnProperty(nodeId)) { + node = this.nodes[nodeId]; + node.resize(ctx); + if (minX > node.x - 0.5 * node.width) {minX = node.x - 0.5 * node.width;} + if (maxX < node.x + 0.5 * node.width) {maxX = node.x + 0.5 * node.width;} + if (minY > node.y - 0.5 * node.height) {minY = node.y - 0.5 * node.height;} + if (maxY < node.y + 0.5 * node.height) {maxY = node.y + 0.5 * node.height;} + } } + node = this.sectors[sectorType][sector]["drawingNode"]; + node.x = 0.5 * (maxX + minX); + node.y = 0.5 * (maxY + minY); + node.width = 2 * (node.x - minX); + node.height = 2 * (node.y - minY); + node.options.radius = Math.sqrt(Math.pow(0.5*node.width,2) + Math.pow(0.5*node.height,2)); + node.setScale(this.scale); + node._drawCircle(ctx); } } - // remove the entry from the rerouted edges - delete parentNode.reroutedEdges[childNode.id]; } }; + exports._drawAllSectorNodes = function(ctx) { + this._drawSectorNodes(ctx,"frozen"); + this._drawSectorNodes(ctx,"active"); + this._loadLatestSector(); + }; + + +/***/ }, +/* 62 */ +/***/ function(module, exports, __webpack_require__) { + + var Node = __webpack_require__(40); /** - * When loops are clustered, an edge can be both in the rerouted array and the contained array. - * This function is called last to verify that all edges in dynamicEdges are in fact connected to the - * parentNode + * This function can be called from the _doInAllSectors function * - * @param parentNode | Node object + * @param object + * @param overlappingNodes * @private */ - exports._validateEdges = function(parentNode) { - for (var i = 0; i < parentNode.dynamicEdges.length; i++) { - var edge = parentNode.dynamicEdges[i]; - if (parentNode.id != edge.toId && parentNode.id != edge.fromId) { - parentNode.dynamicEdges.splice(i,1); + exports._getNodesOverlappingWith = function(object, overlappingNodes) { + var nodes = this.nodes; + for (var nodeId in nodes) { + if (nodes.hasOwnProperty(nodeId)) { + if (nodes[nodeId].isOverlappingWith(object)) { + overlappingNodes.push(nodeId); + } } } }; - /** - * This function released the contained edges back into the global domain and puts them back into the - * dynamic edges of both parent and child. - * - * @param {Node} parentNode | - * @param {Node} childNode | + * retrieve all nodes overlapping with given object + * @param {Object} object An object with parameters left, top, right, bottom + * @return {Number[]} An array with id's of the overlapping nodes * @private */ - exports._releaseContainedEdges = function(parentNode, childNode) { - for (var i = 0; i < parentNode.containedEdges[childNode.id].length; i++) { - var edge = parentNode.containedEdges[childNode.id][i]; + exports._getAllNodesOverlappingWith = function (object) { + var overlappingNodes = []; + this._doInAllActiveSectors("_getNodesOverlappingWith",object,overlappingNodes); + return overlappingNodes; + }; - // put the edge back in the global edges object - this.edges[edge.id] = edge; - // put the edge back in the dynamic edges of the child and parent - childNode.dynamicEdges.push(edge); - parentNode.dynamicEdges.push(edge); - } - // remove the entry from the contained edges - delete parentNode.containedEdges[childNode.id]; + /** + * Return a position object in canvasspace from a single point in screenspace + * + * @param pointer + * @returns {{left: number, top: number, right: number, bottom: number}} + * @private + */ + exports._pointerToPositionObject = function(pointer) { + var x = this._XconvertDOMtoCanvas(pointer.x); + var y = this._YconvertDOMtoCanvas(pointer.y); + return { + left: x, + top: y, + right: x, + bottom: y + }; }; + /** + * Get the top node at the a specific point (like a click) + * + * @param {{x: Number, y: Number}} pointer + * @return {Node | null} node + * @private + */ + exports._getNodeAt = function (pointer) { + // we first check if this is an navigation controls element + var positionObject = this._pointerToPositionObject(pointer); + var overlappingNodes = this._getAllNodesOverlappingWith(positionObject); - - // ------------------- UTILITY FUNCTIONS ---------------------------- // + // if there are overlapping nodes, select the last one, this is the + // one which is drawn on top of the others + if (overlappingNodes.length > 0) { + return this.nodes[overlappingNodes[overlappingNodes.length - 1]]; + } + else { + return null; + } + }; /** - * This updates the node labels for all nodes (for debugging purposes) + * retrieve all edges overlapping with given object, selector is around center + * @param {Object} object An object with parameters left, top, right, bottom + * @return {Number[]} An array with id's of the overlapping nodes + * @private */ - exports.updateLabels = function() { - var nodeId; - // update node labels - for (nodeId in this.nodes) { - if (this.nodes.hasOwnProperty(nodeId)) { - var node = this.nodes[nodeId]; - if (node.clusterSize > 1) { - node.label = "[".concat(String(node.clusterSize),"]"); - } - } - } - - // update node labels - for (nodeId in this.nodes) { - if (this.nodes.hasOwnProperty(nodeId)) { - node = this.nodes[nodeId]; - if (node.clusterSize == 1) { - if (node.originalLabel !== undefined) { - node.label = node.originalLabel; - } - else { - node.label = String(node.id); - } + exports._getEdgesOverlappingWith = function (object, overlappingEdges) { + var edges = this.edges; + for (var edgeId in edges) { + if (edges.hasOwnProperty(edgeId)) { + if (edges[edgeId].isOverlappingWith(object)) { + overlappingEdges.push(edgeId); } } } + }; - // /* Debug Override */ - // for (nodeId in this.nodes) { - // if (this.nodes.hasOwnProperty(nodeId)) { - // node = this.nodes[nodeId]; - // node.label = String(node.level); - // } - // } + /** + * retrieve all nodes overlapping with given object + * @param {Object} object An object with parameters left, top, right, bottom + * @return {Number[]} An array with id's of the overlapping nodes + * @private + */ + exports._getAllEdgesOverlappingWith = function (object) { + var overlappingEdges = []; + this._doInAllActiveSectors("_getEdgesOverlappingWith",object,overlappingEdges); + return overlappingEdges; }; - /** - * We want to keep the cluster level distribution rather small. This means we do not want unclustered nodes - * if the rest of the nodes are already a few cluster levels in. - * To fix this we use this function. It determines the min and max cluster level and sends nodes that have not - * clustered enough to the clusterToSmallestNeighbours function. + * Place holder. To implement change the _getNodeAt to a _getObjectAt. Have the _getObjectAt call + * _getNodeAt and _getEdgesAt, then priortize the selection to user preferences. + * + * @param pointer + * @returns {null} + * @private */ - exports.normalizeClusterLevels = function() { - var maxLevel = 0; - var minLevel = 1e9; - var clusterLevel = 0; - var nodeId; + exports._getEdgeAt = function(pointer) { + var positionObject = this._pointerToPositionObject(pointer); + var overlappingEdges = this._getAllEdgesOverlappingWith(positionObject); - // we loop over all nodes in the list - for (nodeId in this.nodes) { - if (this.nodes.hasOwnProperty(nodeId)) { - clusterLevel = this.nodes[nodeId].clusterSessions.length; - if (maxLevel < clusterLevel) {maxLevel = clusterLevel;} - if (minLevel > clusterLevel) {minLevel = clusterLevel;} - } + if (overlappingEdges.length > 0) { + return this.edges[overlappingEdges[overlappingEdges.length - 1]]; } - - if (maxLevel - minLevel > this.constants.clustering.clusterLevelDifference) { - var amountOfNodes = this.nodeIndices.length; - var targetLevel = maxLevel - this.constants.clustering.clusterLevelDifference; - // we loop over all nodes in the list - for (nodeId in this.nodes) { - if (this.nodes.hasOwnProperty(nodeId)) { - if (this.nodes[nodeId].clusterSessions.length < targetLevel) { - this._clusterToSmallestNeighbour(this.nodes[nodeId]); - } - } - } - this._updateNodeIndexList(); - this._updateDynamicEdges(); - // if a cluster was formed, we increase the clusterSession - if (this.nodeIndices.length != amountOfNodes) { - this.clusterSession += 1; - } + else { + return null; } }; - /** - * This function determines if the cluster we want to decluster is in the active area - * this means around the zoom center + * Add object to the selection array. * - * @param {Node} node - * @returns {boolean} + * @param obj * @private */ - exports._nodeInActiveArea = function(node) { - return ( - Math.abs(node.x - this.areaCenter.x) <= this.constants.clustering.activeAreaBoxSize/this.scale - && - Math.abs(node.y - this.areaCenter.y) <= this.constants.clustering.activeAreaBoxSize/this.scale - ) + exports._addToSelection = function(obj) { + if (obj instanceof Node) { + this.selectionObj.nodes[obj.id] = obj; + } + else { + this.selectionObj.edges[obj.id] = obj; + } }; - /** - * This is an adaptation of the original repositioning function. This is called if the system is clustered initially - * It puts large clusters away from the center and randomizes the order. + * Add object to the selection array. * + * @param obj + * @private */ - exports.repositionNodes = function() { - for (var i = 0; i < this.nodeIndices.length; i++) { - var node = this.nodes[this.nodeIndices[i]]; - if ((node.xFixed == false || node.yFixed == false)) { - var radius = 10 * 0.1*this.nodeIndices.length * Math.min(100,node.options.mass); - var angle = 2 * Math.PI * Math.random(); - if (node.xFixed == false) {node.x = radius * Math.cos(angle);} - if (node.yFixed == false) {node.y = radius * Math.sin(angle);} - this._repositionBezierNodes(node); - } + exports._addToHover = function(obj) { + if (obj instanceof Node) { + this.hoverObj.nodes[obj.id] = obj; + } + else { + this.hoverObj.edges[obj.id] = obj; } }; /** - * We determine how many connections denote an important hub. - * We take the mean + 2*std as the important hub size. (Assuming a normal distribution of data, ~2.2%) + * Remove a single option from selection. * + * @param {Object} obj * @private */ - exports._getHubSize = function() { - var average = 0; - var averageSquared = 0; - var hubCounter = 0; - var largestHub = 0; - - for (var i = 0; i < this.nodeIndices.length; i++) { + exports._removeFromSelection = function(obj) { + if (obj instanceof Node) { + delete this.selectionObj.nodes[obj.id]; + } + else { + delete this.selectionObj.edges[obj.id]; + } + }; - var node = this.nodes[this.nodeIndices[i]]; - if (node.dynamicEdgesLength > largestHub) { - largestHub = node.dynamicEdgesLength; + /** + * Unselect all. The selectionObj is useful for this. + * + * @param {Boolean} [doNotTrigger] | ignore trigger + * @private + */ + exports._unselectAll = function(doNotTrigger) { + if (doNotTrigger === undefined) { + doNotTrigger = false; + } + for(var nodeId in this.selectionObj.nodes) { + if(this.selectionObj.nodes.hasOwnProperty(nodeId)) { + this.selectionObj.nodes[nodeId].unselect(); + } + } + for(var edgeId in this.selectionObj.edges) { + if(this.selectionObj.edges.hasOwnProperty(edgeId)) { + this.selectionObj.edges[edgeId].unselect(); } - average += node.dynamicEdgesLength; - averageSquared += Math.pow(node.dynamicEdgesLength,2); - hubCounter += 1; } - average = average / hubCounter; - averageSquared = averageSquared / hubCounter; - var variance = averageSquared - Math.pow(average,2); + this.selectionObj = {nodes:{},edges:{}}; - var standardDeviation = Math.sqrt(variance); + if (doNotTrigger == false) { + this.emit('select', this.getSelection()); + } + }; - this.hubThreshold = Math.floor(average + 2*standardDeviation); + /** + * Unselect all clusters. The selectionObj is useful for this. + * + * @param {Boolean} [doNotTrigger] | ignore trigger + * @private + */ + exports._unselectClusters = function(doNotTrigger) { + if (doNotTrigger === undefined) { + doNotTrigger = false; + } - // always have at least one to cluster - if (this.hubThreshold > largestHub) { - this.hubThreshold = largestHub; + for (var nodeId in this.selectionObj.nodes) { + if (this.selectionObj.nodes.hasOwnProperty(nodeId)) { + if (this.selectionObj.nodes[nodeId].clusterSize > 1) { + this.selectionObj.nodes[nodeId].unselect(); + this._removeFromSelection(this.selectionObj.nodes[nodeId]); + } + } } - // console.log("average",average,"averageSQ",averageSquared,"var",variance,"std",standardDeviation); - // console.log("hubThreshold:",this.hubThreshold); + if (doNotTrigger == false) { + this.emit('select', this.getSelection()); + } }; /** - * We reduce the amount of "extension nodes" or chains. These are not quickly clustered with the outliers and hubs methods - * with this amount we can cluster specifically on these chains. + * return the number of selected nodes * - * @param {Number} fraction | between 0 and 1, the percentage of chains to reduce + * @returns {number} * @private */ - exports._reduceAmountOfChains = function(fraction) { - this.hubThreshold = 2; - var reduceAmount = Math.floor(this.nodeIndices.length * fraction); - for (var nodeId in this.nodes) { - if (this.nodes.hasOwnProperty(nodeId)) { - if (this.nodes[nodeId].dynamicEdgesLength == 2 && this.nodes[nodeId].dynamicEdges.length >= 2) { - if (reduceAmount > 0) { - this._formClusterFromHub(this.nodes[nodeId],true,true,1); - reduceAmount -= 1; - } - } + exports._getSelectedNodeCount = function() { + var count = 0; + for (var nodeId in this.selectionObj.nodes) { + if (this.selectionObj.nodes.hasOwnProperty(nodeId)) { + count += 1; } } + return count; }; /** - * We get the amount of "extension nodes" or chains. These are not quickly clustered with the outliers and hubs methods - * with this amount we can cluster specifically on these chains. + * return the selected node * + * @returns {number} * @private */ - exports._getChainFraction = function() { - var chains = 0; - var total = 0; - for (var nodeId in this.nodes) { - if (this.nodes.hasOwnProperty(nodeId)) { - if (this.nodes[nodeId].dynamicEdgesLength == 2 && this.nodes[nodeId].dynamicEdges.length >= 2) { - chains += 1; - } - total += 1; + exports._getSelectedNode = function() { + for (var nodeId in this.selectionObj.nodes) { + if (this.selectionObj.nodes.hasOwnProperty(nodeId)) { + return this.selectionObj.nodes[nodeId]; } } - return chains/total; + return null; }; - -/***/ }, -/* 65 */ -/***/ function(module, exports, __webpack_require__) { - - var util = __webpack_require__(1); - var Node = __webpack_require__(56); - /** - * Creation of the SectorMixin var. + * return the selected edge * - * This contains all the functions the Network object can use to employ the sector system. - * The sector system is always used by Network, though the benefits only apply to the use of clustering. - * If clustering is not used, there is no overhead except for a duplicate object with references to nodes and edges. + * @returns {number} + * @private */ + exports._getSelectedEdge = function() { + for (var edgeId in this.selectionObj.edges) { + if (this.selectionObj.edges.hasOwnProperty(edgeId)) { + return this.selectionObj.edges[edgeId]; + } + } + return null; + }; + /** - * This function is only called by the setData function of the Network object. - * This loads the global references into the active sector. This initializes the sector. + * return the number of selected edges * + * @returns {number} * @private */ - exports._putDataInSector = function() { - this.sectors["active"][this._sector()].nodes = this.nodes; - this.sectors["active"][this._sector()].edges = this.edges; - this.sectors["active"][this._sector()].nodeIndices = this.nodeIndices; + exports._getSelectedEdgeCount = function() { + var count = 0; + for (var edgeId in this.selectionObj.edges) { + if (this.selectionObj.edges.hasOwnProperty(edgeId)) { + count += 1; + } + } + return count; }; /** - * /** - * This function sets the global references to nodes, edges and nodeIndices back to - * those of the supplied (active) sector. If a type is defined, do the specific type + * return the number of selected objects. * - * @param {String} sectorId - * @param {String} [sectorType] | "active" or "frozen" + * @returns {number} * @private */ - exports._switchToSector = function(sectorId, sectorType) { - if (sectorType === undefined || sectorType == "active") { - this._switchToActiveSector(sectorId); + exports._getSelectedObjectCount = function() { + var count = 0; + for(var nodeId in this.selectionObj.nodes) { + if(this.selectionObj.nodes.hasOwnProperty(nodeId)) { + count += 1; + } } - else { - this._switchToFrozenSector(sectorId); + for(var edgeId in this.selectionObj.edges) { + if(this.selectionObj.edges.hasOwnProperty(edgeId)) { + count += 1; + } } + return count; }; - /** - * This function sets the global references to nodes, edges and nodeIndices back to - * those of the supplied active sector. + * Check if anything is selected * - * @param sectorId + * @returns {boolean} * @private */ - exports._switchToActiveSector = function(sectorId) { - this.nodeIndices = this.sectors["active"][sectorId]["nodeIndices"]; - this.nodes = this.sectors["active"][sectorId]["nodes"]; - this.edges = this.sectors["active"][sectorId]["edges"]; + exports._selectionIsEmpty = function() { + for(var nodeId in this.selectionObj.nodes) { + if(this.selectionObj.nodes.hasOwnProperty(nodeId)) { + return false; + } + } + for(var edgeId in this.selectionObj.edges) { + if(this.selectionObj.edges.hasOwnProperty(edgeId)) { + return false; + } + } + return true; }; /** - * This function sets the global references to nodes, edges and nodeIndices back to - * those of the supplied active sector. + * check if one of the selected nodes is a cluster. * + * @returns {boolean} * @private */ - exports._switchToSupportSector = function() { - this.nodeIndices = this.sectors["support"]["nodeIndices"]; - this.nodes = this.sectors["support"]["nodes"]; - this.edges = this.sectors["support"]["edges"]; + exports._clusterInSelection = function() { + for(var nodeId in this.selectionObj.nodes) { + if(this.selectionObj.nodes.hasOwnProperty(nodeId)) { + if (this.selectionObj.nodes[nodeId].clusterSize > 1) { + return true; + } + } + } + return false; }; - /** - * This function sets the global references to nodes, edges and nodeIndices back to - * those of the supplied frozen sector. + * select the edges connected to the node that is being selected * - * @param sectorId + * @param {Node} node * @private */ - exports._switchToFrozenSector = function(sectorId) { - this.nodeIndices = this.sectors["frozen"][sectorId]["nodeIndices"]; - this.nodes = this.sectors["frozen"][sectorId]["nodes"]; - this.edges = this.sectors["frozen"][sectorId]["edges"]; + exports._selectConnectedEdges = function(node) { + for (var i = 0; i < node.dynamicEdges.length; i++) { + var edge = node.dynamicEdges[i]; + edge.select(); + this._addToSelection(edge); + } }; - /** - * This function sets the global references to nodes, edges and nodeIndices back to - * those of the currently active sector. + * select the edges connected to the node that is being selected * + * @param {Node} node * @private */ - exports._loadLatestSector = function() { - this._switchToSector(this._sector()); + exports._hoverConnectedEdges = function(node) { + for (var i = 0; i < node.dynamicEdges.length; i++) { + var edge = node.dynamicEdges[i]; + edge.hover = true; + this._addToHover(edge); + } }; /** - * This function returns the currently active sector Id + * unselect the edges connected to the node that is being selected * - * @returns {String} + * @param {Node} node * @private */ - exports._sector = function() { - return this.activeSector[this.activeSector.length-1]; + exports._unselectConnectedEdges = function(node) { + for (var i = 0; i < node.dynamicEdges.length; i++) { + var edge = node.dynamicEdges[i]; + edge.unselect(); + this._removeFromSelection(edge); + } }; + + /** - * This function returns the previously active sector Id + * This is called when someone clicks on a node. either select or deselect it. + * If there is an existing selection and we don't want to append to it, clear the existing selection * - * @returns {String} + * @param {Node || Edge} object + * @param {Boolean} append + * @param {Boolean} [doNotTrigger] | ignore trigger * @private */ - exports._previousSector = function() { - if (this.activeSector.length > 1) { - return this.activeSector[this.activeSector.length-2]; + exports._selectObject = function(object, append, doNotTrigger, highlightEdges, overrideSelectable) { + if (doNotTrigger === undefined) { + doNotTrigger = false; + } + if (highlightEdges === undefined) { + highlightEdges = true; + } + + if (this._selectionIsEmpty() == false && append == false && this.forceAppendSelection == false) { + this._unselectAll(true); + } + + // selectable allows the object to be selected. Override can be used if needed to bypass this. + if (object.selected == false && (this.constants.selectable == true || overrideSelectable)) { + object.select(); + this._addToSelection(object); + if (object instanceof Node && this.blockConnectingEdgeSelection == false && highlightEdges == true) { + this._selectConnectedEdges(object); + } + } + // do not select the object if selectable is false, only add it to selection to allow drag to work + else if (object.selected == false) { + this._addToSelection(object); + doNotTrigger = true; } else { - throw new TypeError('there are not enough sectors in the this.activeSector array.'); + object.unselect(); + this._removeFromSelection(object); + } + + if (doNotTrigger == false) { + this.emit('select', this.getSelection()); } }; /** - * We add the active sector at the end of the this.activeSector array - * This ensures it is the currently active sector returned by _sector() and it reaches the top - * of the activeSector stack. When we reverse our steps we move from the end to the beginning of this stack. + * This is called when someone clicks on a node. either select or deselect it. + * If there is an existing selection and we don't want to append to it, clear the existing selection * - * @param newId + * @param {Node || Edge} object * @private */ - exports._setActiveSector = function(newId) { - this.activeSector.push(newId); + exports._blurObject = function(object) { + if (object.hover == true) { + object.hover = false; + this.emit("blurNode",{node:object.id}); + } }; - /** - * We remove the currently active sector id from the active sector stack. This happens when - * we reactivate the previously active sector + * This is called when someone clicks on a node. either select or deselect it. + * If there is an existing selection and we don't want to append to it, clear the existing selection * + * @param {Node || Edge} object * @private */ - exports._forgetLastSector = function() { - this.activeSector.pop(); + exports._hoverObject = function(object) { + if (object.hover == false) { + object.hover = true; + this._addToHover(object); + if (object instanceof Node) { + this.emit("hoverNode",{node:object.id}); + } + } + if (object instanceof Node) { + this._hoverConnectedEdges(object); + } }; /** - * This function creates a new active sector with the supplied newId. This newId - * is the expanding node id. + * handles the selection part of the touch, only for navigation controls elements; + * Touch is triggered before tap, also before hold. Hold triggers after a while. + * This is the most responsive solution * - * @param {String} newId | Id of the new active sector + * @param {Object} pointer * @private */ - exports._createNewSector = function(newId) { - // create the new sector - this.sectors["active"][newId] = {"nodes":{}, - "edges":{}, - "nodeIndices":[], - "formationScale": this.scale, - "drawingNode": undefined}; + exports._handleTouch = function(pointer) { + }; - // create the new sector render node. This gives visual feedback that you are in a new sector. - this.sectors["active"][newId]['drawingNode'] = new Node( - {id:newId, - color: { - background: "#eaefef", - border: "495c5e" - } - },{},{},this.constants); - this.sectors["active"][newId]['drawingNode'].clusterSize = 2; + + /** + * handles the selection part of the tap; + * + * @param {Object} pointer + * @private + */ + exports._handleTap = function(pointer) { + var node = this._getNodeAt(pointer); + if (node != null) { + this._selectObject(node, false); + } + else { + var edge = this._getEdgeAt(pointer); + if (edge != null) { + this._selectObject(edge, false); + } + else { + this._unselectAll(); + } + } + var properties = this.getSelection(); + properties['pointer'] = { + DOM: {x: pointer.x, y: pointer.y}, + canvas: {x: this._XconvertDOMtoCanvas(pointer.x), y: this._YconvertDOMtoCanvas(pointer.y)} + } + this.emit("click", properties); + this._redraw(); }; /** - * This function removes the currently active sector. This is called when we create a new - * active sector. + * handles the selection part of the double tap and opens a cluster if needed * - * @param {String} sectorId | Id of the active sector that will be removed + * @param {Object} pointer * @private */ - exports._deleteActiveSector = function(sectorId) { - delete this.sectors["active"][sectorId]; + exports._handleDoubleTap = function(pointer) { + var node = this._getNodeAt(pointer); + if (node != null && node !== undefined) { + // we reset the areaCenter here so the opening of the node will occur + this.areaCenter = {"x" : this._XconvertDOMtoCanvas(pointer.x), + "y" : this._YconvertDOMtoCanvas(pointer.y)}; + this.openCluster(node); + } + var properties = this.getSelection(); + properties['pointer'] = { + DOM: {x: pointer.x, y: pointer.y}, + canvas: {x: this._XconvertDOMtoCanvas(pointer.x), y: this._YconvertDOMtoCanvas(pointer.y)} + } + this.emit("doubleClick", properties); }; /** - * This function removes the currently active sector. This is called when we reactivate - * the previously active sector. + * Handle the onHold selection part * - * @param {String} sectorId | Id of the active sector that will be removed + * @param pointer * @private */ - exports._deleteFrozenSector = function(sectorId) { - delete this.sectors["frozen"][sectorId]; + exports._handleOnHold = function(pointer) { + var node = this._getNodeAt(pointer); + if (node != null) { + this._selectObject(node,true); + } + else { + var edge = this._getEdgeAt(pointer); + if (edge != null) { + this._selectObject(edge,true); + } + } + this._redraw(); }; /** - * Freezing an active sector means moving it from the "active" object to the "frozen" object. - * We copy the references, then delete the active entree. + * handle the onRelease event. These functions are here for the navigation controls module + * and data manipulation module. * - * @param sectorId - * @private + * @private */ - exports._freezeSector = function(sectorId) { - // we move the set references from the active to the frozen stack. - this.sectors["frozen"][sectorId] = this.sectors["active"][sectorId]; - - // we have moved the sector data into the frozen set, we now remove it from the active set - this._deleteActiveSector(sectorId); + exports._handleOnRelease = function(pointer) { + this._manipulationReleaseOverload(pointer); + this._navigationReleaseOverload(pointer); }; + exports._manipulationReleaseOverload = function (pointer) {}; + exports._navigationReleaseOverload = function (pointer) {}; /** - * This is the reverse operation of _freezeSector. Activating means moving the sector from the "frozen" - * object to the "active" object. * - * @param sectorId - * @private + * retrieve the currently selected objects + * @return {{nodes: Array., edges: Array.}} selection */ - exports._activateSector = function(sectorId) { - // we move the set references from the frozen to the active stack. - this.sectors["active"][sectorId] = this.sectors["frozen"][sectorId]; - - // we have moved the sector data into the active set, we now remove it from the frozen stack - this._deleteFrozenSector(sectorId); + exports.getSelection = function() { + var nodeIds = this.getSelectedNodes(); + var edgeIds = this.getSelectedEdges(); + return {nodes:nodeIds, edges:edgeIds}; }; - /** - * This function merges the data from the currently active sector with a frozen sector. This is used - * in the process of reverting back to the previously active sector. - * The data that is placed in the frozen (the previously active) sector is the node that has been removed from it - * upon the creation of a new active sector. * - * @param sectorId - * @private + * retrieve the currently selected nodes + * @return {String[]} selection An array with the ids of the + * selected nodes. */ - exports._mergeThisWithFrozen = function(sectorId) { - // copy all nodes - for (var nodeId in this.nodes) { - if (this.nodes.hasOwnProperty(nodeId)) { - this.sectors["frozen"][sectorId]["nodes"][nodeId] = this.nodes[nodeId]; + exports.getSelectedNodes = function() { + var idArray = []; + if (this.constants.selectable == true) { + for (var nodeId in this.selectionObj.nodes) { + if (this.selectionObj.nodes.hasOwnProperty(nodeId)) { + idArray.push(nodeId); + } } } + return idArray + }; - // copy all edges (if not fully clustered, else there are no edges) - for (var edgeId in this.edges) { - if (this.edges.hasOwnProperty(edgeId)) { - this.sectors["frozen"][sectorId]["edges"][edgeId] = this.edges[edgeId]; + /** + * + * retrieve the currently selected edges + * @return {Array} selection An array with the ids of the + * selected nodes. + */ + exports.getSelectedEdges = function() { + var idArray = []; + if (this.constants.selectable == true) { + for (var edgeId in this.selectionObj.edges) { + if (this.selectionObj.edges.hasOwnProperty(edgeId)) { + idArray.push(edgeId); + } } } - - // merge the nodeIndices - for (var i = 0; i < this.nodeIndices.length; i++) { - this.sectors["frozen"][sectorId]["nodeIndices"].push(this.nodeIndices[i]); - } + return idArray; }; /** - * This clusters the sector to one cluster. It was a single cluster before this process started so - * we revert to that state. The clusterToFit function with a maximum size of 1 node does this. - * - * @private + * select zero or more nodes DEPRICATED + * @param {Number[] | String[]} selection An array with the ids of the + * selected nodes. */ - exports._collapseThisToSingleCluster = function() { - this.clusterToFit(1,false); + exports.setSelection = function() { + console.log("setSelection is deprecated. Please use selectNodes instead.") }; /** - * We create a new active sector from the node that we want to open. - * - * @param node - * @private + * select zero or more nodes with the option to highlight edges + * @param {Number[] | String[]} selection An array with the ids of the + * selected nodes. + * @param {boolean} [highlightEdges] */ - exports._addSector = function(node) { - // this is the currently active sector - var sector = this._sector(); - - // // this should allow me to select nodes from a frozen set. - // if (this.sectors['active'][sector]["nodes"].hasOwnProperty(node.id)) { - // console.log("the node is part of the active sector"); - // } - // else { - // console.log("I dont know what the fuck happened!!"); - // } - - // when we switch to a new sector, we remove the node that will be expanded from the current nodes list. - delete this.nodes[node.id]; - - var unqiueIdentifier = util.randomUUID(); - - // we fully freeze the currently active sector - this._freezeSector(sector); + exports.selectNodes = function(selection, highlightEdges) { + var i, iMax, id; - // we create a new active sector. This sector has the Id of the node to ensure uniqueness - this._createNewSector(unqiueIdentifier); + if (!selection || (selection.length == undefined)) + throw 'Selection must be an array with ids'; - // we add the active sector to the sectors array to be able to revert these steps later on - this._setActiveSector(unqiueIdentifier); + // first unselect any selected node + this._unselectAll(true); - // we redirect the global references to the new sector's references. this._sector() now returns unqiueIdentifier - this._switchToSector(this._sector()); + for (i = 0, iMax = selection.length; i < iMax; i++) { + id = selection[i]; - // finally we add the node we removed from our previous active sector to the new active sector - this.nodes[node.id] = node; + var node = this.nodes[id]; + if (!node) { + throw new RangeError('Node with id "' + id + '" not found'); + } + this._selectObject(node,true,true,highlightEdges,true); + } + this.redraw(); }; /** - * We close the sector that is currently open and revert back to the one before. - * If the active sector is the "default" sector, nothing happens. - * - * @private + * select zero or more edges + * @param {Number[] | String[]} selection An array with the ids of the + * selected nodes. */ - exports._collapseSector = function() { - // the currently active sector - var sector = this._sector(); - - // we cannot collapse the default sector - if (sector != "default") { - if ((this.nodeIndices.length == 1) || - (this.sectors["active"][sector]["drawingNode"].width*this.scale < this.constants.clustering.screenSizeThreshold * this.frame.canvas.clientWidth) || - (this.sectors["active"][sector]["drawingNode"].height*this.scale < this.constants.clustering.screenSizeThreshold * this.frame.canvas.clientHeight)) { - var previousSector = this._previousSector(); - - // we collapse the sector back to a single cluster - this._collapseThisToSingleCluster(); - - // we move the remaining nodes, edges and nodeIndices to the previous sector. - // This previous sector is the one we will reactivate - this._mergeThisWithFrozen(previousSector); - - // the previously active (frozen) sector now has all the data from the currently active sector. - // we can now delete the active sector. - this._deleteActiveSector(sector); - - // we activate the previously active (and currently frozen) sector. - this._activateSector(previousSector); + exports.selectEdges = function(selection) { + var i, iMax, id; - // we load the references from the newly active sector into the global references - this._switchToSector(previousSector); + if (!selection || (selection.length == undefined)) + throw 'Selection must be an array with ids'; - // we forget the previously active sector because we reverted to the one before - this._forgetLastSector(); + // first unselect any selected node + this._unselectAll(true); - // finally, we update the node index list. - this._updateNodeIndexList(); + for (i = 0, iMax = selection.length; i < iMax; i++) { + id = selection[i]; - // we refresh the list with calulation nodes and calculation node indices. - this._updateCalculationNodes(); + var edge = this.edges[id]; + if (!edge) { + throw new RangeError('Edge with id "' + id + '" not found'); } + this._selectObject(edge,true,true,false,true); } + this.redraw(); }; - /** - * This runs a function in all active sectors. This is used in _redraw() and the _initializeForceCalculation(). - * - * @param {String} runFunction | This is the NAME of a function we want to call in all active sectors - * | we dont pass the function itself because then the "this" is the window object - * | instead of the Network object - * @param {*} [argument] | Optional: arguments to pass to the runFunction + * Validate the selection: remove ids of nodes which no longer exist * @private */ - exports._doInAllActiveSectors = function(runFunction,argument) { - var returnValues = []; - if (argument === undefined) { - for (var sector in this.sectors["active"]) { - if (this.sectors["active"].hasOwnProperty(sector)) { - // switch the global references to those of this sector - this._switchToActiveSector(sector); - returnValues.push( this[runFunction]() ); + exports._updateSelection = function () { + for(var nodeId in this.selectionObj.nodes) { + if(this.selectionObj.nodes.hasOwnProperty(nodeId)) { + if (!this.nodes.hasOwnProperty(nodeId)) { + delete this.selectionObj.nodes[nodeId]; } } } - else { - for (var sector in this.sectors["active"]) { - if (this.sectors["active"].hasOwnProperty(sector)) { - // switch the global references to those of this sector - this._switchToActiveSector(sector); - var args = Array.prototype.splice.call(arguments, 1); - if (args.length > 1) { - returnValues.push( this[runFunction](args[0],args[1]) ); - } - else { - returnValues.push( this[runFunction](argument) ); - } + for(var edgeId in this.selectionObj.edges) { + if(this.selectionObj.edges.hasOwnProperty(edgeId)) { + if (!this.edges.hasOwnProperty(edgeId)) { + delete this.selectionObj.edges[edgeId]; } } } - // we revert the global references back to our active sector - this._loadLatestSector(); - return returnValues; }; +/***/ }, +/* 63 */ +/***/ function(module, exports, __webpack_require__) { + + var util = __webpack_require__(1); + var Node = __webpack_require__(40); + var Edge = __webpack_require__(37); + /** - * This runs a function in all active sectors. This is used in _redraw() and the _initializeForceCalculation(). + * clears the toolbar div element of children * - * @param {String} runFunction | This is the NAME of a function we want to call in all active sectors - * | we dont pass the function itself because then the "this" is the window object - * | instead of the Network object - * @param {*} [argument] | Optional: arguments to pass to the runFunction * @private */ - exports._doInSupportSector = function(runFunction,argument) { - var returnValues = false; - if (argument === undefined) { - this._switchToSupportSector(); - returnValues = this[runFunction](); - } - else { - this._switchToSupportSector(); - var args = Array.prototype.splice.call(arguments, 1); - if (args.length > 1) { - returnValues = this[runFunction](args[0],args[1]); - } - else { - returnValues = this[runFunction](argument); - } - } - // we revert the global references back to our active sector - this._loadLatestSector(); - return returnValues; - }; + exports._clearManipulatorBar = function() { + this._recursiveDOMDelete(this.manipulationDiv); + this.manipulationDOM = {}; + this._manipulationReleaseOverload = function () {}; + delete this.sectors['support']['nodes']['targetNode']; + delete this.sectors['support']['nodes']['targetViaNode']; + this.controlNodesActive = false; + this.freezeSimulation = false; + }; /** - * This runs a function in all frozen sectors. This is used in the _redraw(). + * Manipulation UI temporarily overloads certain functions to extend or replace them. To be able to restore + * these functions to their original functionality, we saved them in this.cachedFunctions. + * This function restores these functions to their original function. * - * @param {String} runFunction | This is the NAME of a function we want to call in all active sectors - * | we don't pass the function itself because then the "this" is the window object - * | instead of the Network object - * @param {*} [argument] | Optional: arguments to pass to the runFunction * @private */ - exports._doInAllFrozenSectors = function(runFunction,argument) { - if (argument === undefined) { - for (var sector in this.sectors["frozen"]) { - if (this.sectors["frozen"].hasOwnProperty(sector)) { - // switch the global references to those of this sector - this._switchToFrozenSector(sector); - this[runFunction](); - } - } - } - else { - for (var sector in this.sectors["frozen"]) { - if (this.sectors["frozen"].hasOwnProperty(sector)) { - // switch the global references to those of this sector - this._switchToFrozenSector(sector); - var args = Array.prototype.splice.call(arguments, 1); - if (args.length > 1) { - this[runFunction](args[0],args[1]); - } - else { - this[runFunction](argument); - } - } + exports._restoreOverloadedFunctions = function() { + for (var functionName in this.cachedFunctions) { + if (this.cachedFunctions.hasOwnProperty(functionName)) { + this[functionName] = this.cachedFunctions[functionName]; + delete this.cachedFunctions[functionName]; } } - this._loadLatestSector(); }; - /** - * This runs a function in all sectors. This is used in the _redraw(). + * Enable or disable edit-mode. * - * @param {String} runFunction | This is the NAME of a function we want to call in all active sectors - * | we don't pass the function itself because then the "this" is the window object - * | instead of the Network object - * @param {*} [argument] | Optional: arguments to pass to the runFunction * @private */ - exports._doInAllSectors = function(runFunction,argument) { - var args = Array.prototype.splice.call(arguments, 1); - if (argument === undefined) { - this._doInAllActiveSectors(runFunction); - this._doInAllFrozenSectors(runFunction); + exports._toggleEditMode = function() { + this.editMode = !this.editMode; + var toolbar = this.manipulationDiv; + var closeDiv = this.closeDiv; + var editModeDiv = this.editModeDiv; + if (this.editMode == true) { + toolbar.style.display="block"; + closeDiv.style.display="block"; + editModeDiv.style.display="none"; + closeDiv.onclick = this._toggleEditMode.bind(this); } else { - if (args.length > 1) { - this._doInAllActiveSectors(runFunction,args[0],args[1]); - this._doInAllFrozenSectors(runFunction,args[0],args[1]); - } - else { - this._doInAllActiveSectors(runFunction,argument); - this._doInAllFrozenSectors(runFunction,argument); - } + toolbar.style.display="none"; + closeDiv.style.display="none"; + editModeDiv.style.display="block"; + closeDiv.onclick = null; } + this._createManipulatorBar() }; - /** - * This clears the nodeIndices list. We cannot use this.nodeIndices = [] because we would break the link with the - * active sector. Thus we clear the nodeIndices in the active sector, then reconnect the this.nodeIndices to it. + * main function, creates the main toolbar. Removes functions bound to the select event. Binds all the buttons of the toolbar. * * @private */ - exports._clearNodeIndexList = function() { - var sector = this._sector(); - this.sectors["active"][sector]["nodeIndices"] = []; - this.nodeIndices = this.sectors["active"][sector]["nodeIndices"]; - }; + exports._createManipulatorBar = function() { + // remove bound functions + if (this.boundFunction) { + this.off('select', this.boundFunction); + } + var locale = this.constants.locales[this.constants.locale]; - /** - * Draw the encompassing sector node - * - * @param ctx - * @param sectorType - * @private - */ - exports._drawSectorNodes = function(ctx,sectorType) { - var minY = 1e9, maxY = -1e9, minX = 1e9, maxX = -1e9, node; - for (var sector in this.sectors[sectorType]) { - if (this.sectors[sectorType].hasOwnProperty(sector)) { - if (this.sectors[sectorType][sector]["drawingNode"] !== undefined) { + if (this.edgeBeingEdited !== undefined) { + this.edgeBeingEdited._disableControlNodes(); + this.edgeBeingEdited = undefined; + this.selectedControlNode = null; + this.controlNodesActive = false; + this._redraw(); + } - this._switchToSector(sector,sectorType); + // restore overloaded functions + this._restoreOverloadedFunctions(); - minY = 1e9; maxY = -1e9; minX = 1e9; maxX = -1e9; - for (var nodeId in this.nodes) { - if (this.nodes.hasOwnProperty(nodeId)) { - node = this.nodes[nodeId]; - node.resize(ctx); - if (minX > node.x - 0.5 * node.width) {minX = node.x - 0.5 * node.width;} - if (maxX < node.x + 0.5 * node.width) {maxX = node.x + 0.5 * node.width;} - if (minY > node.y - 0.5 * node.height) {minY = node.y - 0.5 * node.height;} - if (maxY < node.y + 0.5 * node.height) {maxY = node.y + 0.5 * node.height;} - } - } - node = this.sectors[sectorType][sector]["drawingNode"]; - node.x = 0.5 * (maxX + minX); - node.y = 0.5 * (maxY + minY); - node.width = 2 * (node.x - minX); - node.height = 2 * (node.y - minY); - node.options.radius = Math.sqrt(Math.pow(0.5*node.width,2) + Math.pow(0.5*node.height,2)); - node.setScale(this.scale); - node._drawCircle(ctx); - } + // resume calculation + this.freezeSimulation = false; + + // reset global variables + this.blockConnectingEdgeSelection = false; + this.forceAppendSelection = false; + this.manipulationDOM = {}; + + if (this.editMode == true) { + while (this.manipulationDiv.hasChildNodes()) { + this.manipulationDiv.removeChild(this.manipulationDiv.firstChild); + } + + this.manipulationDOM['addNodeSpan'] = document.createElement('span'); + this.manipulationDOM['addNodeSpan'].className = 'network-manipulationUI add'; + this.manipulationDOM['addNodeLabelSpan'] = document.createElement('span'); + this.manipulationDOM['addNodeLabelSpan'].className = 'network-manipulationLabel'; + this.manipulationDOM['addNodeLabelSpan'].innerHTML = locale['addNode']; + this.manipulationDOM['addNodeSpan'].appendChild(this.manipulationDOM['addNodeLabelSpan']); + + this.manipulationDOM['seperatorLineDiv1'] = document.createElement('div'); + this.manipulationDOM['seperatorLineDiv1'].className = 'network-seperatorLine'; + + this.manipulationDOM['addEdgeSpan'] = document.createElement('span'); + this.manipulationDOM['addEdgeSpan'].className = 'network-manipulationUI connect'; + this.manipulationDOM['addEdgeLabelSpan'] = document.createElement('span'); + this.manipulationDOM['addEdgeLabelSpan'].className = 'network-manipulationLabel'; + this.manipulationDOM['addEdgeLabelSpan'].innerHTML = locale['addEdge']; + this.manipulationDOM['addEdgeSpan'].appendChild(this.manipulationDOM['addEdgeLabelSpan']); + + this.manipulationDiv.appendChild(this.manipulationDOM['addNodeSpan']); + this.manipulationDiv.appendChild(this.manipulationDOM['seperatorLineDiv1']); + this.manipulationDiv.appendChild(this.manipulationDOM['addEdgeSpan']); + + if (this._getSelectedNodeCount() == 1 && this.triggerFunctions.edit) { + this.manipulationDOM['seperatorLineDiv2'] = document.createElement('div'); + this.manipulationDOM['seperatorLineDiv2'].className = 'network-seperatorLine'; + + this.manipulationDOM['editNodeSpan'] = document.createElement('span'); + this.manipulationDOM['editNodeSpan'].className = 'network-manipulationUI edit'; + this.manipulationDOM['editNodeLabelSpan'] = document.createElement('span'); + this.manipulationDOM['editNodeLabelSpan'].className = 'network-manipulationLabel'; + this.manipulationDOM['editNodeLabelSpan'].innerHTML = locale['editNode']; + this.manipulationDOM['editNodeSpan'].appendChild(this.manipulationDOM['editNodeLabelSpan']); + + this.manipulationDiv.appendChild(this.manipulationDOM['seperatorLineDiv2']); + this.manipulationDiv.appendChild(this.manipulationDOM['editNodeSpan']); + } + else if (this._getSelectedEdgeCount() == 1 && this._getSelectedNodeCount() == 0) { + this.manipulationDOM['seperatorLineDiv3'] = document.createElement('div'); + this.manipulationDOM['seperatorLineDiv3'].className = 'network-seperatorLine'; + + this.manipulationDOM['editEdgeSpan'] = document.createElement('span'); + this.manipulationDOM['editEdgeSpan'].className = 'network-manipulationUI edit'; + this.manipulationDOM['editEdgeLabelSpan'] = document.createElement('span'); + this.manipulationDOM['editEdgeLabelSpan'].className = 'network-manipulationLabel'; + this.manipulationDOM['editEdgeLabelSpan'].innerHTML = locale['editEdge']; + this.manipulationDOM['editEdgeSpan'].appendChild(this.manipulationDOM['editEdgeLabelSpan']); + + this.manipulationDiv.appendChild(this.manipulationDOM['seperatorLineDiv3']); + this.manipulationDiv.appendChild(this.manipulationDOM['editEdgeSpan']); + } + if (this._selectionIsEmpty() == false) { + this.manipulationDOM['seperatorLineDiv4'] = document.createElement('div'); + this.manipulationDOM['seperatorLineDiv4'].className = 'network-seperatorLine'; + + this.manipulationDOM['deleteSpan'] = document.createElement('span'); + this.manipulationDOM['deleteSpan'].className = 'network-manipulationUI delete'; + this.manipulationDOM['deleteLabelSpan'] = document.createElement('span'); + this.manipulationDOM['deleteLabelSpan'].className = 'network-manipulationLabel'; + this.manipulationDOM['deleteLabelSpan'].innerHTML = locale['del']; + this.manipulationDOM['deleteSpan'].appendChild(this.manipulationDOM['deleteLabelSpan']); + + this.manipulationDiv.appendChild(this.manipulationDOM['seperatorLineDiv4']); + this.manipulationDiv.appendChild(this.manipulationDOM['deleteSpan']); } + + + // bind the icons + this.manipulationDOM['addNodeSpan'].onclick = this._createAddNodeToolbar.bind(this); + this.manipulationDOM['addEdgeSpan'].onclick = this._createAddEdgeToolbar.bind(this); + if (this._getSelectedNodeCount() == 1 && this.triggerFunctions.edit) { + this.manipulationDOM['editNodeSpan'].onclick = this._editNode.bind(this); + } + else if (this._getSelectedEdgeCount() == 1 && this._getSelectedNodeCount() == 0) { + this.manipulationDOM['editEdgeSpan'].onclick = this._createEditEdgeToolbar.bind(this); + } + if (this._selectionIsEmpty() == false) { + this.manipulationDOM['deleteSpan'].onclick = this._deleteSelected.bind(this); + } + this.closeDiv.onclick = this._toggleEditMode.bind(this); + + var me = this; + this.boundFunction = me._createManipulatorBar; + this.on('select', this.boundFunction); } - }; + else { + while (this.editModeDiv.hasChildNodes()) { + this.editModeDiv.removeChild(this.editModeDiv.firstChild); + } - exports._drawAllSectorNodes = function(ctx) { - this._drawSectorNodes(ctx,"frozen"); - this._drawSectorNodes(ctx,"active"); - this._loadLatestSector(); - }; + this.manipulationDOM['editModeSpan'] = document.createElement('span'); + this.manipulationDOM['editModeSpan'].className = 'network-manipulationUI edit editmode'; + this.manipulationDOM['editModeLabelSpan'] = document.createElement('span'); + this.manipulationDOM['editModeLabelSpan'].className = 'network-manipulationLabel'; + this.manipulationDOM['editModeLabelSpan'].innerHTML = locale['edit']; + this.manipulationDOM['editModeSpan'].appendChild(this.manipulationDOM['editModeLabelSpan']); + this.editModeDiv.appendChild(this.manipulationDOM['editModeSpan']); + + this.manipulationDOM['editModeSpan'].onclick = this._toggleEditMode.bind(this); + } + }; -/***/ }, -/* 66 */ -/***/ function(module, exports, __webpack_require__) { - var Node = __webpack_require__(56); /** - * This function can be called from the _doInAllSectors function + * Create the toolbar for adding Nodes * - * @param object - * @param overlappingNodes * @private */ - exports._getNodesOverlappingWith = function(object, overlappingNodes) { - var nodes = this.nodes; - for (var nodeId in nodes) { - if (nodes.hasOwnProperty(nodeId)) { - if (nodes[nodeId].isOverlappingWith(object)) { - overlappingNodes.push(nodeId); - } - } + exports._createAddNodeToolbar = function() { + // clear the toolbar + this._clearManipulatorBar(); + if (this.boundFunction) { + this.off('select', this.boundFunction); } - }; - /** - * retrieve all nodes overlapping with given object - * @param {Object} object An object with parameters left, top, right, bottom - * @return {Number[]} An array with id's of the overlapping nodes - * @private - */ - exports._getAllNodesOverlappingWith = function (object) { - var overlappingNodes = []; - this._doInAllActiveSectors("_getNodesOverlappingWith",object,overlappingNodes); - return overlappingNodes; - }; + var locale = this.constants.locales[this.constants.locale]; + + this.manipulationDOM = {}; + this.manipulationDOM['backSpan'] = document.createElement('span'); + this.manipulationDOM['backSpan'].className = 'network-manipulationUI back'; + this.manipulationDOM['backLabelSpan'] = document.createElement('span'); + this.manipulationDOM['backLabelSpan'].className = 'network-manipulationLabel'; + this.manipulationDOM['backLabelSpan'].innerHTML = locale['back']; + this.manipulationDOM['backSpan'].appendChild(this.manipulationDOM['backLabelSpan']); + this.manipulationDOM['seperatorLineDiv1'] = document.createElement('div'); + this.manipulationDOM['seperatorLineDiv1'].className = 'network-seperatorLine'; - /** - * Return a position object in canvasspace from a single point in screenspace - * - * @param pointer - * @returns {{left: number, top: number, right: number, bottom: number}} - * @private - */ - exports._pointerToPositionObject = function(pointer) { - var x = this._XconvertDOMtoCanvas(pointer.x); - var y = this._YconvertDOMtoCanvas(pointer.y); + this.manipulationDOM['descriptionSpan'] = document.createElement('span'); + this.manipulationDOM['descriptionSpan'].className = 'network-manipulationUI none'; + this.manipulationDOM['descriptionLabelSpan'] = document.createElement('span'); + this.manipulationDOM['descriptionLabelSpan'].className = 'network-manipulationLabel'; + this.manipulationDOM['descriptionLabelSpan'].innerHTML = locale['addDescription']; + this.manipulationDOM['descriptionSpan'].appendChild(this.manipulationDOM['descriptionLabelSpan']); - return { - left: x, - top: y, - right: x, - bottom: y - }; + this.manipulationDiv.appendChild(this.manipulationDOM['backSpan']); + this.manipulationDiv.appendChild(this.manipulationDOM['seperatorLineDiv1']); + this.manipulationDiv.appendChild(this.manipulationDOM['descriptionSpan']); + + // bind the icon + this.manipulationDOM['backSpan'].onclick = this._createManipulatorBar.bind(this); + + // we use the boundFunction so we can reference it when we unbind it from the "select" event. + var me = this; + this.boundFunction = me._addNode; + this.on('select', this.boundFunction); }; /** - * Get the top node at the a specific point (like a click) + * create the toolbar to connect nodes * - * @param {{x: Number, y: Number}} pointer - * @return {Node | null} node * @private */ - exports._getNodeAt = function (pointer) { - // we first check if this is an navigation controls element - var positionObject = this._pointerToPositionObject(pointer); - var overlappingNodes = this._getAllNodesOverlappingWith(positionObject); + exports._createAddEdgeToolbar = function() { + // clear the toolbar + this._clearManipulatorBar(); + this._unselectAll(true); + this.freezeSimulation = true; - // if there are overlapping nodes, select the last one, this is the - // one which is drawn on top of the others - if (overlappingNodes.length > 0) { - return this.nodes[overlappingNodes[overlappingNodes.length - 1]]; - } - else { - return null; + if (this.boundFunction) { + this.off('select', this.boundFunction); } - }; + var locale = this.constants.locales[this.constants.locale]; - /** - * retrieve all edges overlapping with given object, selector is around center - * @param {Object} object An object with parameters left, top, right, bottom - * @return {Number[]} An array with id's of the overlapping nodes - * @private - */ - exports._getEdgesOverlappingWith = function (object, overlappingEdges) { - var edges = this.edges; - for (var edgeId in edges) { - if (edges.hasOwnProperty(edgeId)) { - if (edges[edgeId].isOverlappingWith(object)) { - overlappingEdges.push(edgeId); - } - } - } - }; + this._unselectAll(); + this.forceAppendSelection = false; + this.blockConnectingEdgeSelection = true; + this.manipulationDOM = {}; + this.manipulationDOM['backSpan'] = document.createElement('span'); + this.manipulationDOM['backSpan'].className = 'network-manipulationUI back'; + this.manipulationDOM['backLabelSpan'] = document.createElement('span'); + this.manipulationDOM['backLabelSpan'].className = 'network-manipulationLabel'; + this.manipulationDOM['backLabelSpan'].innerHTML = locale['back']; + this.manipulationDOM['backSpan'].appendChild(this.manipulationDOM['backLabelSpan']); - /** - * retrieve all nodes overlapping with given object - * @param {Object} object An object with parameters left, top, right, bottom - * @return {Number[]} An array with id's of the overlapping nodes - * @private - */ - exports._getAllEdgesOverlappingWith = function (object) { - var overlappingEdges = []; - this._doInAllActiveSectors("_getEdgesOverlappingWith",object,overlappingEdges); - return overlappingEdges; + this.manipulationDOM['seperatorLineDiv1'] = document.createElement('div'); + this.manipulationDOM['seperatorLineDiv1'].className = 'network-seperatorLine'; + + this.manipulationDOM['descriptionSpan'] = document.createElement('span'); + this.manipulationDOM['descriptionSpan'].className = 'network-manipulationUI none'; + this.manipulationDOM['descriptionLabelSpan'] = document.createElement('span'); + this.manipulationDOM['descriptionLabelSpan'].className = 'network-manipulationLabel'; + this.manipulationDOM['descriptionLabelSpan'].innerHTML = locale['edgeDescription']; + this.manipulationDOM['descriptionSpan'].appendChild(this.manipulationDOM['descriptionLabelSpan']); + + this.manipulationDiv.appendChild(this.manipulationDOM['backSpan']); + this.manipulationDiv.appendChild(this.manipulationDOM['seperatorLineDiv1']); + this.manipulationDiv.appendChild(this.manipulationDOM['descriptionSpan']); + + // bind the icon + this.manipulationDOM['backSpan'].onclick = this._createManipulatorBar.bind(this); + + // we use the boundFunction so we can reference it when we unbind it from the "select" event. + var me = this; + this.boundFunction = me._handleConnect; + this.on('select', this.boundFunction); + + // temporarily overload functions + this.cachedFunctions["_handleTouch"] = this._handleTouch; + this.cachedFunctions["_manipulationReleaseOverload"] = this._manipulationReleaseOverload; + this.cachedFunctions["_handleDragStart"] = this._handleDragStart; + this.cachedFunctions["_handleDragEnd"] = this._handleDragEnd; + this._handleTouch = this._handleConnect; + this._manipulationReleaseOverload = function () {}; + this._handleDragStart = function () {}; + this._handleDragEnd = this._finishConnect; + + // redraw to show the unselect + this._redraw(); }; /** - * Place holder. To implement change the _getNodeAt to a _getObjectAt. Have the _getObjectAt call - * _getNodeAt and _getEdgesAt, then priortize the selection to user preferences. + * create the toolbar to edit edges * - * @param pointer - * @returns {null} * @private */ - exports._getEdgeAt = function(pointer) { - var positionObject = this._pointerToPositionObject(pointer); - var overlappingEdges = this._getAllEdgesOverlappingWith(positionObject); + exports._createEditEdgeToolbar = function() { + // clear the toolbar + this._clearManipulatorBar(); + this.controlNodesActive = true; - if (overlappingEdges.length > 0) { - return this.edges[overlappingEdges[overlappingEdges.length - 1]]; - } - else { - return null; + if (this.boundFunction) { + this.off('select', this.boundFunction); } + + this.edgeBeingEdited = this._getSelectedEdge(); + this.edgeBeingEdited._enableControlNodes(); + + var locale = this.constants.locales[this.constants.locale]; + + this.manipulationDOM = {}; + this.manipulationDOM['backSpan'] = document.createElement('span'); + this.manipulationDOM['backSpan'].className = 'network-manipulationUI back'; + this.manipulationDOM['backLabelSpan'] = document.createElement('span'); + this.manipulationDOM['backLabelSpan'].className = 'network-manipulationLabel'; + this.manipulationDOM['backLabelSpan'].innerHTML = locale['back']; + this.manipulationDOM['backSpan'].appendChild(this.manipulationDOM['backLabelSpan']); + + this.manipulationDOM['seperatorLineDiv1'] = document.createElement('div'); + this.manipulationDOM['seperatorLineDiv1'].className = 'network-seperatorLine'; + + this.manipulationDOM['descriptionSpan'] = document.createElement('span'); + this.manipulationDOM['descriptionSpan'].className = 'network-manipulationUI none'; + this.manipulationDOM['descriptionLabelSpan'] = document.createElement('span'); + this.manipulationDOM['descriptionLabelSpan'].className = 'network-manipulationLabel'; + this.manipulationDOM['descriptionLabelSpan'].innerHTML = locale['editEdgeDescription']; + this.manipulationDOM['descriptionSpan'].appendChild(this.manipulationDOM['descriptionLabelSpan']); + + this.manipulationDiv.appendChild(this.manipulationDOM['backSpan']); + this.manipulationDiv.appendChild(this.manipulationDOM['seperatorLineDiv1']); + this.manipulationDiv.appendChild(this.manipulationDOM['descriptionSpan']); + + // bind the icon + this.manipulationDOM['backSpan'].onclick = this._createManipulatorBar.bind(this); + + // temporarily overload functions + this.cachedFunctions["_handleTouch"] = this._handleTouch; + this.cachedFunctions["_manipulationReleaseOverload"] = this._manipulationReleaseOverload; + this.cachedFunctions["_handleTap"] = this._handleTap; + this.cachedFunctions["_handleDragStart"] = this._handleDragStart; + this.cachedFunctions["_handleOnDrag"] = this._handleOnDrag; + this._handleTouch = this._selectControlNode; + this._handleTap = function () {}; + this._handleOnDrag = this._controlNodeDrag; + this._handleDragStart = function () {} + this._manipulationReleaseOverload = this._releaseControlNode; + + // redraw to show the unselect + this._redraw(); }; /** - * Add object to the selection array. + * the function bound to the selection event. It checks if you want to connect a cluster and changes the description + * to walk the user through the process. * - * @param obj * @private */ - exports._addToSelection = function(obj) { - if (obj instanceof Node) { - this.selectionObj.nodes[obj.id] = obj; - } - else { - this.selectionObj.edges[obj.id] = obj; + exports._selectControlNode = function(pointer) { + this.edgeBeingEdited.controlNodes.from.unselect(); + this.edgeBeingEdited.controlNodes.to.unselect(); + this.selectedControlNode = this.edgeBeingEdited._getSelectedControlNode(this._XconvertDOMtoCanvas(pointer.x),this._YconvertDOMtoCanvas(pointer.y)); + if (this.selectedControlNode !== null) { + this.selectedControlNode.select(); + this.freezeSimulation = true; } + this._redraw(); }; + /** - * Add object to the selection array. + * the function bound to the selection event. It checks if you want to connect a cluster and changes the description + * to walk the user through the process. * - * @param obj * @private */ - exports._addToHover = function(obj) { - if (obj instanceof Node) { - this.hoverObj.nodes[obj.id] = obj; - } - else { - this.hoverObj.edges[obj.id] = obj; + exports._controlNodeDrag = function(event) { + var pointer = this._getPointer(event.gesture.center); + if (this.selectedControlNode !== null && this.selectedControlNode !== undefined) { + this.selectedControlNode.x = this._XconvertDOMtoCanvas(pointer.x); + this.selectedControlNode.y = this._YconvertDOMtoCanvas(pointer.y); } + this._redraw(); }; /** - * Remove a single option from selection. * - * @param {Object} obj + * @param pointer * @private */ - exports._removeFromSelection = function(obj) { - if (obj instanceof Node) { - delete this.selectionObj.nodes[obj.id]; + exports._releaseControlNode = function(pointer) { + var newNode = this._getNodeAt(pointer); + if (newNode !== null) { + if (this.edgeBeingEdited.controlNodes.from.selected == true) { + this.edgeBeingEdited._restoreControlNodes(); + this._editEdge(newNode.id, this.edgeBeingEdited.to.id); + this.edgeBeingEdited.controlNodes.from.unselect(); + } + if (this.edgeBeingEdited.controlNodes.to.selected == true) { + this.edgeBeingEdited._restoreControlNodes(); + this._editEdge(this.edgeBeingEdited.from.id, newNode.id); + this.edgeBeingEdited.controlNodes.to.unselect(); + } } else { - delete this.selectionObj.edges[obj.id]; + this.edgeBeingEdited._restoreControlNodes(); } + this.freezeSimulation = false; + this._redraw(); }; /** - * Unselect all. The selectionObj is useful for this. + * the function bound to the selection event. It checks if you want to connect a cluster and changes the description + * to walk the user through the process. * - * @param {Boolean} [doNotTrigger] | ignore trigger * @private */ - exports._unselectAll = function(doNotTrigger) { - if (doNotTrigger === undefined) { - doNotTrigger = false; - } - for(var nodeId in this.selectionObj.nodes) { - if(this.selectionObj.nodes.hasOwnProperty(nodeId)) { - this.selectionObj.nodes[nodeId].unselect(); - } - } - for(var edgeId in this.selectionObj.edges) { - if(this.selectionObj.edges.hasOwnProperty(edgeId)) { - this.selectionObj.edges[edgeId].unselect(); - } - } + exports._handleConnect = function(pointer) { + if (this._getSelectedNodeCount() == 0) { + var node = this._getNodeAt(pointer); - this.selectionObj = {nodes:{},edges:{}}; + if (node != null) { + if (node.clusterSize > 1) { + alert(this.constants.locales[this.constants.locale]['createEdgeError']) + } + else { + this._selectObject(node,false); + var supportNodes = this.sectors['support']['nodes']; - if (doNotTrigger == false) { - this.emit('select', this.getSelection()); - } - }; + // create a node the temporary line can look at + supportNodes['targetNode'] = new Node({id:'targetNode'},{},{},this.constants); + var targetNode = supportNodes['targetNode']; + targetNode.x = node.x; + targetNode.y = node.y; - /** - * Unselect all clusters. The selectionObj is useful for this. - * - * @param {Boolean} [doNotTrigger] | ignore trigger - * @private - */ - exports._unselectClusters = function(doNotTrigger) { - if (doNotTrigger === undefined) { - doNotTrigger = false; - } + // create a temporary edge + this.edges['connectionEdge'] = new Edge({id:"connectionEdge",from:node.id,to:targetNode.id}, this, this.constants); + var connectionEdge = this.edges['connectionEdge']; + connectionEdge.from = node; + connectionEdge.connected = true; + connectionEdge.options.smoothCurves = {enabled: true, + dynamic: false, + type: "continuous", + roundness: 0.5 + }; + connectionEdge.selected = true; + connectionEdge.to = targetNode; - for (var nodeId in this.selectionObj.nodes) { - if (this.selectionObj.nodes.hasOwnProperty(nodeId)) { - if (this.selectionObj.nodes[nodeId].clusterSize > 1) { - this.selectionObj.nodes[nodeId].unselect(); - this._removeFromSelection(this.selectionObj.nodes[nodeId]); + this.cachedFunctions["_handleOnDrag"] = this._handleOnDrag; + this._handleOnDrag = function(event) { + var pointer = this._getPointer(event.gesture.center); + var connectionEdge = this.edges['connectionEdge']; + connectionEdge.to.x = this._XconvertDOMtoCanvas(pointer.x); + connectionEdge.to.y = this._YconvertDOMtoCanvas(pointer.y); + }; + + this.moving = true; + this.start(); } } } + }; - if (doNotTrigger == false) { - this.emit('select', this.getSelection()); + exports._finishConnect = function(event) { + if (this._getSelectedNodeCount() == 1) { + var pointer = this._getPointer(event.gesture.center); + // restore the drag function + this._handleOnDrag = this.cachedFunctions["_handleOnDrag"]; + delete this.cachedFunctions["_handleOnDrag"]; + + // remember the edge id + var connectFromId = this.edges['connectionEdge'].fromId; + + // remove the temporary nodes and edge + delete this.edges['connectionEdge']; + delete this.sectors['support']['nodes']['targetNode']; + delete this.sectors['support']['nodes']['targetViaNode']; + + var node = this._getNodeAt(pointer); + if (node != null) { + if (node.clusterSize > 1) { + alert(this.constants.locales[this.constants.locale]["createEdgeError"]) + } + else { + this._createEdge(connectFromId,node.id); + this._createManipulatorBar(); + } + } + this._unselectAll(); } }; /** - * return the number of selected nodes - * - * @returns {number} - * @private + * Adds a node on the specified location */ - exports._getSelectedNodeCount = function() { - var count = 0; - for (var nodeId in this.selectionObj.nodes) { - if (this.selectionObj.nodes.hasOwnProperty(nodeId)) { - count += 1; + exports._addNode = function() { + if (this._selectionIsEmpty() && this.editMode == true) { + var positionObject = this._pointerToPositionObject(this.pointerPosition); + var defaultData = {id:util.randomUUID(),x:positionObject.left,y:positionObject.top,label:"new",allowedToMoveX:true,allowedToMoveY:true}; + if (this.triggerFunctions.add) { + if (this.triggerFunctions.add.length == 2) { + var me = this; + this.triggerFunctions.add(defaultData, function(finalizedData) { + me.nodesData.add(finalizedData); + me._createManipulatorBar(); + me.moving = true; + me.start(); + }); + } + else { + throw new Error('The function for add does not support two arguments (data,callback)'); + this._createManipulatorBar(); + this.moving = true; + this.start(); + } + } + else { + this.nodesData.add(defaultData); + this._createManipulatorBar(); + this.moving = true; + this.start(); } } - return count; }; + /** - * return the selected node + * connect two nodes with a new edge. * - * @returns {number} * @private */ - exports._getSelectedNode = function() { - for (var nodeId in this.selectionObj.nodes) { - if (this.selectionObj.nodes.hasOwnProperty(nodeId)) { - return this.selectionObj.nodes[nodeId]; + exports._createEdge = function(sourceNodeId,targetNodeId) { + if (this.editMode == true) { + var defaultData = {from:sourceNodeId, to:targetNodeId}; + if (this.triggerFunctions.connect) { + if (this.triggerFunctions.connect.length == 2) { + var me = this; + this.triggerFunctions.connect(defaultData, function(finalizedData) { + me.edgesData.add(finalizedData); + me.moving = true; + me.start(); + }); + } + else { + throw new Error('The function for connect does not support two arguments (data,callback)'); + this.moving = true; + this.start(); + } + } + else { + this.edgesData.add(defaultData); + this.moving = true; + this.start(); } } - return null; }; /** - * return the selected edge + * connect two nodes with a new edge. * - * @returns {number} * @private */ - exports._getSelectedEdge = function() { - for (var edgeId in this.selectionObj.edges) { - if (this.selectionObj.edges.hasOwnProperty(edgeId)) { - return this.selectionObj.edges[edgeId]; + exports._editEdge = function(sourceNodeId,targetNodeId) { + if (this.editMode == true) { + var defaultData = {id: this.edgeBeingEdited.id, from:sourceNodeId, to:targetNodeId}; + if (this.triggerFunctions.editEdge) { + if (this.triggerFunctions.editEdge.length == 2) { + var me = this; + this.triggerFunctions.editEdge(defaultData, function(finalizedData) { + me.edgesData.update(finalizedData); + me.moving = true; + me.start(); + }); + } + else { + throw new Error('The function for edit does not support two arguments (data, callback)'); + this.moving = true; + this.start(); + } + } + else { + this.edgesData.update(defaultData); + this.moving = true; + this.start(); } } - return null; }; - /** - * return the number of selected edges + * Create the toolbar to edit the selected node. The label and the color can be changed. Other colors are derived from the chosen color. * - * @returns {number} * @private */ - exports._getSelectedEdgeCount = function() { - var count = 0; - for (var edgeId in this.selectionObj.edges) { - if (this.selectionObj.edges.hasOwnProperty(edgeId)) { - count += 1; + exports._editNode = function() { + if (this.triggerFunctions.edit && this.editMode == true) { + var node = this._getSelectedNode(); + var data = {id:node.id, + label: node.label, + group: node.options.group, + shape: node.options.shape, + color: { + background:node.options.color.background, + border:node.options.color.border, + highlight: { + background:node.options.color.highlight.background, + border:node.options.color.highlight.border + } + }}; + if (this.triggerFunctions.edit.length == 2) { + var me = this; + this.triggerFunctions.edit(data, function (finalizedData) { + me.nodesData.update(finalizedData); + me._createManipulatorBar(); + me.moving = true; + me.start(); + }); + } + else { + throw new Error('The function for edit does not support two arguments (data, callback)'); } } - return count; + else { + throw new Error('No edit function has been bound to this button'); + } }; + + /** - * return the number of selected objects. + * delete everything in the selection * - * @returns {number} * @private */ - exports._getSelectedObjectCount = function() { - var count = 0; - for(var nodeId in this.selectionObj.nodes) { - if(this.selectionObj.nodes.hasOwnProperty(nodeId)) { - count += 1; + exports._deleteSelected = function() { + if (!this._selectionIsEmpty() && this.editMode == true) { + if (!this._clusterInSelection()) { + var selectedNodes = this.getSelectedNodes(); + var selectedEdges = this.getSelectedEdges(); + if (this.triggerFunctions.del) { + var me = this; + var data = {nodes: selectedNodes, edges: selectedEdges}; + if (this.triggerFunctions.del.length == 2) { + this.triggerFunctions.del(data, function (finalizedData) { + me.edgesData.remove(finalizedData.edges); + me.nodesData.remove(finalizedData.nodes); + me._unselectAll(); + me.moving = true; + me.start(); + }); + } + else { + throw new Error('The function for delete does not support two arguments (data, callback)') + } + } + else { + this.edgesData.remove(selectedEdges); + this.nodesData.remove(selectedNodes); + this._unselectAll(); + this.moving = true; + this.start(); + } } - } - for(var edgeId in this.selectionObj.edges) { - if(this.selectionObj.edges.hasOwnProperty(edgeId)) { - count += 1; + else { + alert(this.constants.locales[this.constants.locale]["deleteClusterError"]); } } - return count; }; - /** - * Check if anything is selected - * - * @returns {boolean} - * @private - */ - exports._selectionIsEmpty = function() { - for(var nodeId in this.selectionObj.nodes) { - if(this.selectionObj.nodes.hasOwnProperty(nodeId)) { - return false; + +/***/ }, +/* 64 */ +/***/ function(module, exports, __webpack_require__) { + + var util = __webpack_require__(1); + var Hammer = __webpack_require__(45); + + exports._cleanNavigation = function() { + // clean hammer bindings + if (this.navigationHammers.existing.length != 0) { + for (var i = 0; i < this.navigationHammers.existing.length; i++) { + this.navigationHammers.existing[i].dispose(); } + this.navigationHammers.existing = []; } - for(var edgeId in this.selectionObj.edges) { - if(this.selectionObj.edges.hasOwnProperty(edgeId)) { - return false; - } + + this._navigationReleaseOverload = function () {}; + + // clean up previous navigation items + if (this.navigationDivs && this.navigationDivs['wrapper'] && this.navigationDivs['wrapper'].parentNode) { + this.navigationDivs['wrapper'].parentNode.removeChild(this.navigationDivs['wrapper']); } - return true; }; - /** - * check if one of the selected nodes is a cluster. + * 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. * - * @returns {boolean} * @private */ - exports._clusterInSelection = function() { - for(var nodeId in this.selectionObj.nodes) { - if(this.selectionObj.nodes.hasOwnProperty(nodeId)) { - if (this.selectionObj.nodes[nodeId].clusterSize > 1) { - return true; - } - } + exports._loadNavigationElements = function() { + this._cleanNavigation(); + + this.navigationDivs = {}; + var navigationDivs = ['up','down','left','right','zoomIn','zoomOut','zoomExtends']; + var navigationDivActions = ['_moveUp','_moveDown','_moveLeft','_moveRight','_zoomIn','_zoomOut','_zoomExtent']; + + this.navigationDivs['wrapper'] = document.createElement('div'); + this.frame.appendChild(this.navigationDivs['wrapper']); + + for (var i = 0; i < navigationDivs.length; i++) { + this.navigationDivs[navigationDivs[i]] = document.createElement('div'); + this.navigationDivs[navigationDivs[i]].className = 'network-navigation ' + navigationDivs[i]; + this.navigationDivs['wrapper'].appendChild(this.navigationDivs[navigationDivs[i]]); + + var hammer = Hammer(this.navigationDivs[navigationDivs[i]], {prevent_default: true}); + hammer.on('touch', this[navigationDivActions[i]].bind(this)); + this.navigationHammers._new.push(hammer); } - return false; + + this._navigationReleaseOverload = this._stopMovement; + + this.navigationHammers.existing = this.navigationHammers._new; }; + /** - * select the edges connected to the node that is being selected + * this stops all movement induced by the navigation buttons * - * @param {Node} node * @private */ - exports._selectConnectedEdges = function(node) { - for (var i = 0; i < node.dynamicEdges.length; i++) { - var edge = node.dynamicEdges[i]; - edge.select(); - this._addToSelection(edge); - } + exports._zoomExtent = function(event) { + this.zoomExtent({duration:700}); + event.stopPropagation(); }; /** - * select the edges connected to the node that is being selected + * this stops all movement induced by the navigation buttons * - * @param {Node} node * @private */ - exports._hoverConnectedEdges = function(node) { - for (var i = 0; i < node.dynamicEdges.length; i++) { - var edge = node.dynamicEdges[i]; - edge.hover = true; - this._addToHover(edge); - } + exports._stopMovement = function() { + this._xStopMoving(); + this._yStopMoving(); + this._stopZoom(); }; /** - * unselect the edges connected to the node that is being selected + * move the screen up + * By using the increments, instead of adding a fixed number to the translation, we keep fluent and + * instant movement. The onKeypress event triggers immediately, then pauses, then triggers frequently + * To avoid this behaviour, we do the translation in the start loop. * - * @param {Node} node * @private */ - exports._unselectConnectedEdges = function(node) { - for (var i = 0; i < node.dynamicEdges.length; i++) { - var edge = node.dynamicEdges[i]; - edge.unselect(); - this._removeFromSelection(edge); - } + exports._moveUp = function(event) { + this.yIncrement = this.constants.keyboard.speed.y; + this.start(); // if there is no node movement, the calculation wont be done + event.preventDefault(); }; - - /** - * This is called when someone clicks on a node. either select or deselect it. - * If there is an existing selection and we don't want to append to it, clear the existing selection - * - * @param {Node || Edge} object - * @param {Boolean} append - * @param {Boolean} [doNotTrigger] | ignore trigger + * move the screen down * @private */ - exports._selectObject = function(object, append, doNotTrigger, highlightEdges, overrideSelectable) { - if (doNotTrigger === undefined) { - doNotTrigger = false; - } - if (highlightEdges === undefined) { - highlightEdges = true; - } - - if (this._selectionIsEmpty() == false && append == false && this.forceAppendSelection == false) { - this._unselectAll(true); - } - - // selectable allows the object to be selected. Override can be used if needed to bypass this. - if (object.selected == false && (this.constants.selectable == true || overrideSelectable)) { - object.select(); - this._addToSelection(object); - if (object instanceof Node && this.blockConnectingEdgeSelection == false && highlightEdges == true) { - this._selectConnectedEdges(object); - } - } - // do not select the object if selectable is false, only add it to selection to allow drag to work - else if (object.selected == false) { - this._addToSelection(object); - doNotTrigger = true; - } - else { - object.unselect(); - this._removeFromSelection(object); - } - - if (doNotTrigger == false) { - this.emit('select', this.getSelection()); - } + exports._moveDown = function(event) { + this.yIncrement = -this.constants.keyboard.speed.y; + this.start(); // if there is no node movement, the calculation wont be done + event.preventDefault(); }; /** - * This is called when someone clicks on a node. either select or deselect it. - * If there is an existing selection and we don't want to append to it, clear the existing selection - * - * @param {Node || Edge} object + * move the screen left * @private */ - exports._blurObject = function(object) { - if (object.hover == true) { - object.hover = false; - this.emit("blurNode",{node:object.id}); - } + exports._moveLeft = function(event) { + this.xIncrement = this.constants.keyboard.speed.x; + this.start(); // if there is no node movement, the calculation wont be done + event.preventDefault(); }; + /** - * This is called when someone clicks on a node. either select or deselect it. - * If there is an existing selection and we don't want to append to it, clear the existing selection - * - * @param {Node || Edge} object + * move the screen right * @private */ - exports._hoverObject = function(object) { - if (object.hover == false) { - object.hover = true; - this._addToHover(object); - if (object instanceof Node) { - this.emit("hoverNode",{node:object.id}); - } - } - if (object instanceof Node) { - this._hoverConnectedEdges(object); - } + exports._moveRight = function(event) { + this.xIncrement = -this.constants.keyboard.speed.y; + this.start(); // if there is no node movement, the calculation wont be done + event.preventDefault(); }; /** - * handles the selection part of the touch, only for navigation controls elements; - * Touch is triggered before tap, also before hold. Hold triggers after a while. - * This is the most responsive solution - * - * @param {Object} pointer + * Zoom in, using the same method as the movement. * @private */ - exports._handleTouch = function(pointer) { + exports._zoomIn = function(event) { + this.zoomIncrement = this.constants.keyboard.speed.zoom; + this.start(); // if there is no node movement, the calculation wont be done + event.preventDefault(); }; /** - * handles the selection part of the tap; - * - * @param {Object} pointer + * Zoom out * @private */ - exports._handleTap = function(pointer) { - var node = this._getNodeAt(pointer); - if (node != null) { - this._selectObject(node, false); - } - else { - var edge = this._getEdgeAt(pointer); - if (edge != null) { - this._selectObject(edge, false); - } - else { - this._unselectAll(); - } - } - var properties = this.getSelection(); - properties['pointer'] = { - DOM: {x: pointer.x, y: pointer.y}, - canvas: {x: this._XconvertDOMtoCanvas(pointer.x), y: this._YconvertDOMtoCanvas(pointer.y)} - } - this.emit("click", properties); - this._redraw(); + exports._zoomOut = function(event) { + this.zoomIncrement = -this.constants.keyboard.speed.zoom; + this.start(); // if there is no node movement, the calculation wont be done + event.preventDefault(); }; /** - * handles the selection part of the double tap and opens a cluster if needed - * - * @param {Object} pointer + * Stop zooming and unhighlight the zoom controls * @private */ - exports._handleDoubleTap = function(pointer) { - var node = this._getNodeAt(pointer); - if (node != null && node !== undefined) { - // we reset the areaCenter here so the opening of the node will occur - this.areaCenter = {"x" : this._XconvertDOMtoCanvas(pointer.x), - "y" : this._YconvertDOMtoCanvas(pointer.y)}; - this.openCluster(node); - } - var properties = this.getSelection(); - properties['pointer'] = { - DOM: {x: pointer.x, y: pointer.y}, - canvas: {x: this._XconvertDOMtoCanvas(pointer.x), y: this._YconvertDOMtoCanvas(pointer.y)} - } - this.emit("doubleClick", properties); + exports._stopZoom = function(event) { + this.zoomIncrement = 0; + event && event.preventDefault(); }; /** - * Handle the onHold selection part - * - * @param pointer + * Stop moving in the Y direction and unHighlight the up and down * @private */ - exports._handleOnHold = function(pointer) { - var node = this._getNodeAt(pointer); - if (node != null) { - this._selectObject(node,true); - } - else { - var edge = this._getEdgeAt(pointer); - if (edge != null) { - this._selectObject(edge,true); - } - } - this._redraw(); + exports._yStopMoving = function(event) { + this.yIncrement = 0; + event && event.preventDefault(); }; /** - * handle the onRelease event. These functions are here for the navigation controls module - * and data manipulation module. - * - * @private + * Stop moving in the X direction and unHighlight left and right. + * @private */ - exports._handleOnRelease = function(pointer) { - this._manipulationReleaseOverload(pointer); - this._navigationReleaseOverload(pointer); + exports._xStopMoving = function(event) { + this.xIncrement = 0; + event && event.preventDefault(); }; - exports._manipulationReleaseOverload = function (pointer) {}; - exports._navigationReleaseOverload = function (pointer) {}; - /** - * - * retrieve the currently selected objects - * @return {{nodes: Array., edges: Array.}} selection - */ - exports.getSelection = function() { - var nodeIds = this.getSelectedNodes(); - var edgeIds = this.getSelectedEdges(); - return {nodes:nodeIds, edges:edgeIds}; - }; +/***/ }, +/* 65 */ +/***/ function(module, exports, __webpack_require__) { - /** - * - * retrieve the currently selected nodes - * @return {String[]} selection An array with the ids of the - * selected nodes. - */ - exports.getSelectedNodes = function() { - var idArray = []; - if (this.constants.selectable == true) { - for (var nodeId in this.selectionObj.nodes) { - if (this.selectionObj.nodes.hasOwnProperty(nodeId)) { - idArray.push(nodeId); + exports._resetLevels = function() { + for (var nodeId in this.nodes) { + if (this.nodes.hasOwnProperty(nodeId)) { + var node = this.nodes[nodeId]; + if (node.preassignedLevel == false) { + node.level = -1; + node.hierarchyEnumerated = false; } } } - return idArray }; /** + * This is the main function to layout the nodes in a hierarchical way. + * It checks if the node details are supplied correctly * - * retrieve the currently selected edges - * @return {Array} selection An array with the ids of the - * selected nodes. + * @private */ - exports.getSelectedEdges = function() { - var idArray = []; - if (this.constants.selectable == true) { - for (var edgeId in this.selectionObj.edges) { - if (this.selectionObj.edges.hasOwnProperty(edgeId)) { - idArray.push(edgeId); + exports._setupHierarchicalLayout = function() { + if (this.constants.hierarchicalLayout.enabled == true && this.nodeIndices.length > 0) { + // get the size of the largest hubs and check if the user has defined a level for a node. + var hubsize = 0; + var node, nodeId; + var definedLevel = false; + var undefinedLevel = false; + + for (nodeId in this.nodes) { + if (this.nodes.hasOwnProperty(nodeId)) { + node = this.nodes[nodeId]; + if (node.level != -1) { + definedLevel = true; + } + else { + undefinedLevel = true; + } + if (hubsize < node.edges.length) { + hubsize = node.edges.length; + } } } - } - return idArray; - }; + // if the user defined some levels but not all, alert and run without hierarchical layout + if (undefinedLevel == true && definedLevel == true) { + throw new Error("To use the hierarchical layout, nodes require either no predefined levels or levels have to be defined for all nodes."); + this.zoomExtent(undefined,true,this.constants.clustering.enabled); + if (!this.constants.clustering.enabled) { + this.start(); + } + } + else { + // setup the system to use hierarchical method. + this._changeConstants(); + + // define levels if undefined by the users. Based on hubsize + if (undefinedLevel == true) { + if (this.constants.hierarchicalLayout.layout == "hubsize") { + this._determineLevels(hubsize); + } + else { + this._determineLevelsDirected(false); + } + + } + // check the distribution of the nodes per level. + var distribution = this._getDistribution(); - /** - * select zero or more nodes DEPRICATED - * @param {Number[] | String[]} selection An array with the ids of the - * selected nodes. - */ - exports.setSelection = function() { - console.log("setSelection is deprecated. Please use selectNodes instead.") + // place the nodes on the canvas. This also stablilizes the system. + this._placeNodesByHierarchy(distribution); + + // start the simulation. + this.start(); + } + } }; /** - * select zero or more nodes with the option to highlight edges - * @param {Number[] | String[]} selection An array with the ids of the - * selected nodes. - * @param {boolean} [highlightEdges] + * This function places the nodes on the canvas based on the hierarchial distribution. + * + * @param {Object} distribution | obtained by the function this._getDistribution() + * @private */ - exports.selectNodes = function(selection, highlightEdges) { - var i, iMax, id; + exports._placeNodesByHierarchy = function(distribution) { + var nodeId, node; - if (!selection || (selection.length == undefined)) - throw 'Selection must be an array with ids'; + // start placing all the level 0 nodes first. Then recursively position their branches. + for (var level in distribution) { + if (distribution.hasOwnProperty(level)) { - // first unselect any selected node - this._unselectAll(true); + for (nodeId in distribution[level].nodes) { + if (distribution[level].nodes.hasOwnProperty(nodeId)) { + node = distribution[level].nodes[nodeId]; + if (this.constants.hierarchicalLayout.direction == "UD" || this.constants.hierarchicalLayout.direction == "DU") { + if (node.xFixed) { + node.x = distribution[level].minPos; + node.xFixed = false; - for (i = 0, iMax = selection.length; i < iMax; i++) { - id = selection[i]; + distribution[level].minPos += distribution[level].nodeSpacing; + } + } + else { + if (node.yFixed) { + node.y = distribution[level].minPos; + node.yFixed = false; - var node = this.nodes[id]; - if (!node) { - throw new RangeError('Node with id "' + id + '" not found'); + distribution[level].minPos += distribution[level].nodeSpacing; + } + } + this._placeBranchNodes(node.edges,node.id,distribution,node.level); + } + } } - this._selectObject(node,true,true,highlightEdges,true); } - this.redraw(); + + // stabilize the system after positioning. This function calls zoomExtent. + this._stabilize(); }; /** - * select zero or more edges - * @param {Number[] | String[]} selection An array with the ids of the - * selected nodes. + * This function get the distribution of levels based on hubsize + * + * @returns {Object} + * @private */ - exports.selectEdges = function(selection) { - var i, iMax, id; - - if (!selection || (selection.length == undefined)) - throw 'Selection must be an array with ids'; + exports._getDistribution = function() { + var distribution = {}; + var nodeId, node, level; - // first unselect any selected node - this._unselectAll(true); + // we fix Y because the hierarchy is vertical, we fix X so we do not give a node an x position for a second time. + // the fix of X is removed after the x value has been set. + for (nodeId in this.nodes) { + if (this.nodes.hasOwnProperty(nodeId)) { + node = this.nodes[nodeId]; + node.xFixed = true; + node.yFixed = true; + if (this.constants.hierarchicalLayout.direction == "UD" || this.constants.hierarchicalLayout.direction == "DU") { + node.y = this.constants.hierarchicalLayout.levelSeparation*node.level; + } + else { + node.x = this.constants.hierarchicalLayout.levelSeparation*node.level; + } + if (distribution[node.level] === undefined) { + distribution[node.level] = {amount: 0, nodes: {}, minPos:0, nodeSpacing:0}; + } + distribution[node.level].amount += 1; + distribution[node.level].nodes[nodeId] = node; + } + } - for (i = 0, iMax = selection.length; i < iMax; i++) { - id = selection[i]; + // determine the largest amount of nodes of all levels + var maxCount = 0; + for (level in distribution) { + if (distribution.hasOwnProperty(level)) { + if (maxCount < distribution[level].amount) { + maxCount = distribution[level].amount; + } + } + } - var edge = this.edges[id]; - if (!edge) { - throw new RangeError('Edge with id "' + id + '" not found'); + // set the initial position and spacing of each nodes accordingly + for (level in distribution) { + if (distribution.hasOwnProperty(level)) { + distribution[level].nodeSpacing = (maxCount + 1) * this.constants.hierarchicalLayout.nodeSpacing; + distribution[level].nodeSpacing /= (distribution[level].amount + 1); + distribution[level].minPos = distribution[level].nodeSpacing - (0.5 * (distribution[level].amount + 1) * distribution[level].nodeSpacing); } - this._selectObject(edge,true,true,false,true); } - this.redraw(); + + return distribution; }; + /** - * Validate the selection: remove ids of nodes which no longer exist + * this function allocates nodes in levels based on the recursive branching from the largest hubs. + * + * @param hubsize * @private */ - exports._updateSelection = function () { - for(var nodeId in this.selectionObj.nodes) { - if(this.selectionObj.nodes.hasOwnProperty(nodeId)) { - if (!this.nodes.hasOwnProperty(nodeId)) { - delete this.selectionObj.nodes[nodeId]; + exports._determineLevels = function(hubsize) { + var nodeId, node; + + // determine hubs + for (nodeId in this.nodes) { + if (this.nodes.hasOwnProperty(nodeId)) { + node = this.nodes[nodeId]; + if (node.edges.length == hubsize) { + node.level = 0; } } } - for(var edgeId in this.selectionObj.edges) { - if(this.selectionObj.edges.hasOwnProperty(edgeId)) { - if (!this.edges.hasOwnProperty(edgeId)) { - delete this.selectionObj.edges[edgeId]; + + // branch from hubs + for (nodeId in this.nodes) { + if (this.nodes.hasOwnProperty(nodeId)) { + node = this.nodes[nodeId]; + if (node.level == 0) { + this._setLevel(1,node.edges,node.id); } } } }; -/***/ }, -/* 67 */ -/***/ function(module, exports, __webpack_require__) { - - var util = __webpack_require__(1); - var Node = __webpack_require__(56); - var Edge = __webpack_require__(57); /** - * clears the toolbar div element of children + * this function allocates nodes in levels based on the direction of the edges * + * @param hubsize * @private */ - exports._clearManipulatorBar = function() { - this._recursiveDOMDelete(this.manipulationDiv); - this.manipulationDOM = {}; + exports._determineLevelsDirected = function() { + var nodeId, node, firstNode; + var minLevel = 10000; - this._manipulationReleaseOverload = function () {}; - delete this.sectors['support']['nodes']['targetNode']; - delete this.sectors['support']['nodes']['targetViaNode']; - this.controlNodesActive = false; - this.freezeSimulation = false; - }; + // set first node to source + firstNode = this.nodes[this.nodeIndices[0]]; + firstNode.level = minLevel; + this._setLevelDirected(minLevel,firstNode.edges,firstNode.id); - /** - * Manipulation UI temporarily overloads certain functions to extend or replace them. To be able to restore - * these functions to their original functionality, we saved them in this.cachedFunctions. - * This function restores these functions to their original function. - * - * @private - */ - exports._restoreOverloadedFunctions = function() { - for (var functionName in this.cachedFunctions) { - if (this.cachedFunctions.hasOwnProperty(functionName)) { - this[functionName] = this.cachedFunctions[functionName]; - delete this.cachedFunctions[functionName]; + // get the minimum level + for (nodeId in this.nodes) { + if (this.nodes.hasOwnProperty(nodeId)) { + node = this.nodes[nodeId]; + minLevel = node.level < minLevel ? node.level : minLevel; + } + } + + // subtract the minimum from the set so we have a range starting from 0 + for (nodeId in this.nodes) { + if (this.nodes.hasOwnProperty(nodeId)) { + node = this.nodes[nodeId]; + node.level -= minLevel; } } }; + /** - * Enable or disable edit-mode. + * Since hierarchical layout does not support: + * - smooth curves (based on the physics), + * - clustering (based on dynamic node counts) + * + * We disable both features so there will be no problems. * * @private */ - exports._toggleEditMode = function() { - this.editMode = !this.editMode; - var toolbar = this.manipulationDiv; - var closeDiv = this.closeDiv; - var editModeDiv = this.editModeDiv; - if (this.editMode == true) { - toolbar.style.display="block"; - closeDiv.style.display="block"; - editModeDiv.style.display="none"; - closeDiv.onclick = this._toggleEditMode.bind(this); + exports._changeConstants = function() { + this.constants.clustering.enabled = false; + this.constants.physics.barnesHut.enabled = false; + this.constants.physics.hierarchicalRepulsion.enabled = true; + this._loadSelectedForceSolver(); + if (this.constants.smoothCurves.enabled == true) { + this.constants.smoothCurves.dynamic = false; + } + this._configureSmoothCurves(); + + var config = this.constants.hierarchicalLayout; + config.levelSeparation = Math.abs(config.levelSeparation); + if (config.direction == "RL" || config.direction == "DU") { + config.levelSeparation *= -1; + } + + if (config.direction == "RL" || config.direction == "LR") { + if (this.constants.smoothCurves.enabled == true) { + this.constants.smoothCurves.type = "vertical"; + } } else { - toolbar.style.display="none"; - closeDiv.style.display="none"; - editModeDiv.style.display="block"; - closeDiv.onclick = null; + if (this.constants.smoothCurves.enabled == true) { + this.constants.smoothCurves.type = "horizontal"; + } } - this._createManipulatorBar() }; + /** - * main function, creates the main toolbar. Removes functions bound to the select event. Binds all the buttons of the toolbar. + * This is a recursively called function to enumerate the branches from the largest hubs and place the nodes + * on a X position that ensures there will be no overlap. * + * @param edges + * @param parentId + * @param distribution + * @param parentLevel * @private */ - exports._createManipulatorBar = function() { - // remove bound functions - if (this.boundFunction) { - this.off('select', this.boundFunction); - } - - var locale = this.constants.locales[this.constants.locale]; - - if (this.edgeBeingEdited !== undefined) { - this.edgeBeingEdited._disableControlNodes(); - this.edgeBeingEdited = undefined; - this.selectedControlNode = null; - this.controlNodesActive = false; - this._redraw(); - } - - // restore overloaded functions - this._restoreOverloadedFunctions(); - - // resume calculation - this.freezeSimulation = false; - - // reset global variables - this.blockConnectingEdgeSelection = false; - this.forceAppendSelection = false; - this.manipulationDOM = {}; - - if (this.editMode == true) { - while (this.manipulationDiv.hasChildNodes()) { - this.manipulationDiv.removeChild(this.manipulationDiv.firstChild); + exports._placeBranchNodes = function(edges, parentId, distribution, parentLevel) { + for (var i = 0; i < edges.length; i++) { + var childNode = null; + if (edges[i].toId == parentId) { + childNode = edges[i].from; } - - this.manipulationDOM['addNodeSpan'] = document.createElement('span'); - this.manipulationDOM['addNodeSpan'].className = 'network-manipulationUI add'; - this.manipulationDOM['addNodeLabelSpan'] = document.createElement('span'); - this.manipulationDOM['addNodeLabelSpan'].className = 'network-manipulationLabel'; - this.manipulationDOM['addNodeLabelSpan'].innerHTML = locale['addNode']; - this.manipulationDOM['addNodeSpan'].appendChild(this.manipulationDOM['addNodeLabelSpan']); - - this.manipulationDOM['seperatorLineDiv1'] = document.createElement('div'); - this.manipulationDOM['seperatorLineDiv1'].className = 'network-seperatorLine'; - - this.manipulationDOM['addEdgeSpan'] = document.createElement('span'); - this.manipulationDOM['addEdgeSpan'].className = 'network-manipulationUI connect'; - this.manipulationDOM['addEdgeLabelSpan'] = document.createElement('span'); - this.manipulationDOM['addEdgeLabelSpan'].className = 'network-manipulationLabel'; - this.manipulationDOM['addEdgeLabelSpan'].innerHTML = locale['addEdge']; - this.manipulationDOM['addEdgeSpan'].appendChild(this.manipulationDOM['addEdgeLabelSpan']); - - this.manipulationDiv.appendChild(this.manipulationDOM['addNodeSpan']); - this.manipulationDiv.appendChild(this.manipulationDOM['seperatorLineDiv1']); - this.manipulationDiv.appendChild(this.manipulationDOM['addEdgeSpan']); - - if (this._getSelectedNodeCount() == 1 && this.triggerFunctions.edit) { - this.manipulationDOM['seperatorLineDiv2'] = document.createElement('div'); - this.manipulationDOM['seperatorLineDiv2'].className = 'network-seperatorLine'; - - this.manipulationDOM['editNodeSpan'] = document.createElement('span'); - this.manipulationDOM['editNodeSpan'].className = 'network-manipulationUI edit'; - this.manipulationDOM['editNodeLabelSpan'] = document.createElement('span'); - this.manipulationDOM['editNodeLabelSpan'].className = 'network-manipulationLabel'; - this.manipulationDOM['editNodeLabelSpan'].innerHTML = locale['editNode']; - this.manipulationDOM['editNodeSpan'].appendChild(this.manipulationDOM['editNodeLabelSpan']); - - this.manipulationDiv.appendChild(this.manipulationDOM['seperatorLineDiv2']); - this.manipulationDiv.appendChild(this.manipulationDOM['editNodeSpan']); + else { + childNode = edges[i].to; } - else if (this._getSelectedEdgeCount() == 1 && this._getSelectedNodeCount() == 0) { - this.manipulationDOM['seperatorLineDiv3'] = document.createElement('div'); - this.manipulationDOM['seperatorLineDiv3'].className = 'network-seperatorLine'; - this.manipulationDOM['editEdgeSpan'] = document.createElement('span'); - this.manipulationDOM['editEdgeSpan'].className = 'network-manipulationUI edit'; - this.manipulationDOM['editEdgeLabelSpan'] = document.createElement('span'); - this.manipulationDOM['editEdgeLabelSpan'].className = 'network-manipulationLabel'; - this.manipulationDOM['editEdgeLabelSpan'].innerHTML = locale['editEdge']; - this.manipulationDOM['editEdgeSpan'].appendChild(this.manipulationDOM['editEdgeLabelSpan']); + // if a node is conneceted to another node on the same level (or higher (means lower level))!, this is not handled here. + var nodeMoved = false; + if (this.constants.hierarchicalLayout.direction == "UD" || this.constants.hierarchicalLayout.direction == "DU") { + if (childNode.xFixed && childNode.level > parentLevel) { + childNode.xFixed = false; + childNode.x = distribution[childNode.level].minPos; + nodeMoved = true; + } + } + else { + if (childNode.yFixed && childNode.level > parentLevel) { + childNode.yFixed = false; + childNode.y = distribution[childNode.level].minPos; + nodeMoved = true; + } + } - this.manipulationDiv.appendChild(this.manipulationDOM['seperatorLineDiv3']); - this.manipulationDiv.appendChild(this.manipulationDOM['editEdgeSpan']); + if (nodeMoved == true) { + distribution[childNode.level].minPos += distribution[childNode.level].nodeSpacing; + if (childNode.edges.length > 1) { + this._placeBranchNodes(childNode.edges,childNode.id,distribution,childNode.level); + } } - if (this._selectionIsEmpty() == false) { - this.manipulationDOM['seperatorLineDiv4'] = document.createElement('div'); - this.manipulationDOM['seperatorLineDiv4'].className = 'network-seperatorLine'; + } + }; - this.manipulationDOM['deleteSpan'] = document.createElement('span'); - this.manipulationDOM['deleteSpan'].className = 'network-manipulationUI delete'; - this.manipulationDOM['deleteLabelSpan'] = document.createElement('span'); - this.manipulationDOM['deleteLabelSpan'].className = 'network-manipulationLabel'; - this.manipulationDOM['deleteLabelSpan'].innerHTML = locale['del']; - this.manipulationDOM['deleteSpan'].appendChild(this.manipulationDOM['deleteLabelSpan']); - this.manipulationDiv.appendChild(this.manipulationDOM['seperatorLineDiv4']); - this.manipulationDiv.appendChild(this.manipulationDOM['deleteSpan']); + /** + * this function is called recursively to enumerate the barnches of the largest hubs and give each node a level. + * + * @param level + * @param edges + * @param parentId + * @private + */ + exports._setLevel = function(level, edges, parentId) { + for (var i = 0; i < edges.length; i++) { + var childNode = null; + if (edges[i].toId == parentId) { + childNode = edges[i].from; + } + else { + childNode = edges[i].to; + } + if (childNode.level == -1 || childNode.level > level) { + childNode.level = level; + if (childNode.edges.length > 1) { + this._setLevel(level+1, childNode.edges, childNode.id); + } } + } + }; - // bind the icons - this.manipulationDOM['addNodeSpan'].onclick = this._createAddNodeToolbar.bind(this); - this.manipulationDOM['addEdgeSpan'].onclick = this._createAddEdgeToolbar.bind(this); - if (this._getSelectedNodeCount() == 1 && this.triggerFunctions.edit) { - this.manipulationDOM['editNodeSpan'].onclick = this._editNode.bind(this); + /** + * this function is called recursively to enumerate the branched of the first node and give each node a level based on edge direction + * + * @param level + * @param edges + * @param parentId + * @private + */ + exports._setLevelDirected = function(level, edges, parentId) { + this.nodes[parentId].hierarchyEnumerated = true; + var childNode, direction; + for (var i = 0; i < edges.length; i++) { + direction = 1; + if (edges[i].toId == parentId) { + childNode = edges[i].from; + direction = -1; } - else if (this._getSelectedEdgeCount() == 1 && this._getSelectedNodeCount() == 0) { - this.manipulationDOM['editEdgeSpan'].onclick = this._createEditEdgeToolbar.bind(this); + else { + childNode = edges[i].to; } - if (this._selectionIsEmpty() == false) { - this.manipulationDOM['deleteSpan'].onclick = this._deleteSelected.bind(this); + if (childNode.level == -1) { + childNode.level = level + direction; } - this.closeDiv.onclick = this._toggleEditMode.bind(this); - - var me = this; - this.boundFunction = me._createManipulatorBar; - this.on('select', this.boundFunction); } - else { - while (this.editModeDiv.hasChildNodes()) { - this.editModeDiv.removeChild(this.editModeDiv.firstChild); - } - - this.manipulationDOM['editModeSpan'] = document.createElement('span'); - this.manipulationDOM['editModeSpan'].className = 'network-manipulationUI edit editmode'; - this.manipulationDOM['editModeLabelSpan'] = document.createElement('span'); - this.manipulationDOM['editModeLabelSpan'].className = 'network-manipulationLabel'; - this.manipulationDOM['editModeLabelSpan'].innerHTML = locale['edit']; - this.manipulationDOM['editModeSpan'].appendChild(this.manipulationDOM['editModeLabelSpan']); - this.editModeDiv.appendChild(this.manipulationDOM['editModeSpan']); + for (var i = 0; i < edges.length; i++) { + if (edges[i].toId == parentId) {childNode = edges[i].from;} + else {childNode = edges[i].to;} - this.manipulationDOM['editModeSpan'].onclick = this._toggleEditMode.bind(this); + if (childNode.edges.length > 1 && childNode.hierarchyEnumerated === false) { + this._setLevelDirected(childNode.level, childNode.edges, childNode.id); + } } }; - /** - * Create the toolbar for adding Nodes + * Unfix nodes * * @private */ - exports._createAddNodeToolbar = function() { - // clear the toolbar - this._clearManipulatorBar(); - if (this.boundFunction) { - this.off('select', this.boundFunction); + exports._restoreNodes = function() { + for (var nodeId in this.nodes) { + if (this.nodes.hasOwnProperty(nodeId)) { + this.nodes[nodeId].xFixed = false; + this.nodes[nodeId].yFixed = false; + } } + }; - var locale = this.constants.locales[this.constants.locale]; - - this.manipulationDOM = {}; - this.manipulationDOM['backSpan'] = document.createElement('span'); - this.manipulationDOM['backSpan'].className = 'network-manipulationUI back'; - this.manipulationDOM['backLabelSpan'] = document.createElement('span'); - this.manipulationDOM['backLabelSpan'].className = 'network-manipulationLabel'; - this.manipulationDOM['backLabelSpan'].innerHTML = locale['back']; - this.manipulationDOM['backSpan'].appendChild(this.manipulationDOM['backLabelSpan']); - - this.manipulationDOM['seperatorLineDiv1'] = document.createElement('div'); - this.manipulationDOM['seperatorLineDiv1'].className = 'network-seperatorLine'; - - this.manipulationDOM['descriptionSpan'] = document.createElement('span'); - this.manipulationDOM['descriptionSpan'].className = 'network-manipulationUI none'; - this.manipulationDOM['descriptionLabelSpan'] = document.createElement('span'); - this.manipulationDOM['descriptionLabelSpan'].className = 'network-manipulationLabel'; - this.manipulationDOM['descriptionLabelSpan'].innerHTML = locale['addDescription']; - this.manipulationDOM['descriptionSpan'].appendChild(this.manipulationDOM['descriptionLabelSpan']); - this.manipulationDiv.appendChild(this.manipulationDOM['backSpan']); - this.manipulationDiv.appendChild(this.manipulationDOM['seperatorLineDiv1']); - this.manipulationDiv.appendChild(this.manipulationDOM['descriptionSpan']); +/***/ }, +/* 66 */ +/***/ function(module, exports, __webpack_require__) { - // bind the icon - this.manipulationDOM['backSpan'].onclick = this._createManipulatorBar.bind(this); + var util = __webpack_require__(1); + var RepulsionMixin = __webpack_require__(68); + var HierarchialRepulsionMixin = __webpack_require__(69); + var BarnesHutMixin = __webpack_require__(70); - // we use the boundFunction so we can reference it when we unbind it from the "select" event. - var me = this; - this.boundFunction = me._addNode; - this.on('select', this.boundFunction); + /** + * Toggling barnes Hut calculation on and off. + * + * @private + */ + exports._toggleBarnesHut = function () { + this.constants.physics.barnesHut.enabled = !this.constants.physics.barnesHut.enabled; + this._loadSelectedForceSolver(); + this.moving = true; + this.start(); }; /** - * create the toolbar to connect nodes + * This loads the node force solver based on the barnes hut or repulsion algorithm * * @private */ - exports._createAddEdgeToolbar = function() { - // clear the toolbar - this._clearManipulatorBar(); - this._unselectAll(true); - this.freezeSimulation = true; - - if (this.boundFunction) { - this.off('select', this.boundFunction); - } - - var locale = this.constants.locales[this.constants.locale]; - - this._unselectAll(); - this.forceAppendSelection = false; - this.blockConnectingEdgeSelection = true; - - this.manipulationDOM = {}; - this.manipulationDOM['backSpan'] = document.createElement('span'); - this.manipulationDOM['backSpan'].className = 'network-manipulationUI back'; - this.manipulationDOM['backLabelSpan'] = document.createElement('span'); - this.manipulationDOM['backLabelSpan'].className = 'network-manipulationLabel'; - this.manipulationDOM['backLabelSpan'].innerHTML = locale['back']; - this.manipulationDOM['backSpan'].appendChild(this.manipulationDOM['backLabelSpan']); - - this.manipulationDOM['seperatorLineDiv1'] = document.createElement('div'); - this.manipulationDOM['seperatorLineDiv1'].className = 'network-seperatorLine'; + exports._loadSelectedForceSolver = function () { + // this overloads the this._calculateNodeForces + if (this.constants.physics.barnesHut.enabled == true) { + this._clearMixin(RepulsionMixin); + this._clearMixin(HierarchialRepulsionMixin); - this.manipulationDOM['descriptionSpan'] = document.createElement('span'); - this.manipulationDOM['descriptionSpan'].className = 'network-manipulationUI none'; - this.manipulationDOM['descriptionLabelSpan'] = document.createElement('span'); - this.manipulationDOM['descriptionLabelSpan'].className = 'network-manipulationLabel'; - this.manipulationDOM['descriptionLabelSpan'].innerHTML = locale['edgeDescription']; - this.manipulationDOM['descriptionSpan'].appendChild(this.manipulationDOM['descriptionLabelSpan']); + this.constants.physics.centralGravity = this.constants.physics.barnesHut.centralGravity; + this.constants.physics.springLength = this.constants.physics.barnesHut.springLength; + this.constants.physics.springConstant = this.constants.physics.barnesHut.springConstant; + this.constants.physics.damping = this.constants.physics.barnesHut.damping; - this.manipulationDiv.appendChild(this.manipulationDOM['backSpan']); - this.manipulationDiv.appendChild(this.manipulationDOM['seperatorLineDiv1']); - this.manipulationDiv.appendChild(this.manipulationDOM['descriptionSpan']); + this._loadMixin(BarnesHutMixin); + } + else if (this.constants.physics.hierarchicalRepulsion.enabled == true) { + this._clearMixin(BarnesHutMixin); + this._clearMixin(RepulsionMixin); - // bind the icon - this.manipulationDOM['backSpan'].onclick = this._createManipulatorBar.bind(this); + this.constants.physics.centralGravity = this.constants.physics.hierarchicalRepulsion.centralGravity; + this.constants.physics.springLength = this.constants.physics.hierarchicalRepulsion.springLength; + this.constants.physics.springConstant = this.constants.physics.hierarchicalRepulsion.springConstant; + this.constants.physics.damping = this.constants.physics.hierarchicalRepulsion.damping; - // we use the boundFunction so we can reference it when we unbind it from the "select" event. - var me = this; - this.boundFunction = me._handleConnect; - this.on('select', this.boundFunction); + this._loadMixin(HierarchialRepulsionMixin); + } + else { + this._clearMixin(BarnesHutMixin); + this._clearMixin(HierarchialRepulsionMixin); + this.barnesHutTree = undefined; - // temporarily overload functions - this.cachedFunctions["_handleTouch"] = this._handleTouch; - this.cachedFunctions["_manipulationReleaseOverload"] = this._manipulationReleaseOverload; - this.cachedFunctions["_handleDragStart"] = this._handleDragStart; - this.cachedFunctions["_handleDragEnd"] = this._handleDragEnd; - this._handleTouch = this._handleConnect; - this._manipulationReleaseOverload = function () {}; - this._handleDragStart = function () {}; - this._handleDragEnd = this._finishConnect; + this.constants.physics.centralGravity = this.constants.physics.repulsion.centralGravity; + this.constants.physics.springLength = this.constants.physics.repulsion.springLength; + this.constants.physics.springConstant = this.constants.physics.repulsion.springConstant; + this.constants.physics.damping = this.constants.physics.repulsion.damping; - // redraw to show the unselect - this._redraw(); + this._loadMixin(RepulsionMixin); + } }; /** - * create the toolbar to edit edges + * Before calculating the forces, we check if we need to cluster to keep up performance and we check + * if there is more than one node. If it is just one node, we dont calculate anything. * * @private */ - exports._createEditEdgeToolbar = function() { - // clear the toolbar - this._clearManipulatorBar(); - this.controlNodesActive = true; - - if (this.boundFunction) { - this.off('select', this.boundFunction); + exports._initializeForceCalculation = function () { + // stop calculation if there is only one node + if (this.nodeIndices.length == 1) { + this.nodes[this.nodeIndices[0]]._setForce(0, 0); } + else { + // if there are too many nodes on screen, we cluster without repositioning + if (this.nodeIndices.length > this.constants.clustering.clusterThreshold && this.constants.clustering.enabled == true) { + this.clusterToFit(this.constants.clustering.reduceToNodes, false); + } - this.edgeBeingEdited = this._getSelectedEdge(); - this.edgeBeingEdited._enableControlNodes(); + // we now start the force calculation + this._calculateForces(); + } + }; - var locale = this.constants.locales[this.constants.locale]; - this.manipulationDOM = {}; - this.manipulationDOM['backSpan'] = document.createElement('span'); - this.manipulationDOM['backSpan'].className = 'network-manipulationUI back'; - this.manipulationDOM['backLabelSpan'] = document.createElement('span'); - this.manipulationDOM['backLabelSpan'].className = 'network-manipulationLabel'; - this.manipulationDOM['backLabelSpan'].innerHTML = locale['back']; - this.manipulationDOM['backSpan'].appendChild(this.manipulationDOM['backLabelSpan']); + /** + * Calculate the external forces acting on the nodes + * Forces are caused by: edges, repulsing forces between nodes, gravity + * @private + */ + exports._calculateForces = function () { + // Gravity is required to keep separated groups from floating off + // the forces are reset to zero in this loop by using _setForce instead + // of _addForce - this.manipulationDOM['seperatorLineDiv1'] = document.createElement('div'); - this.manipulationDOM['seperatorLineDiv1'].className = 'network-seperatorLine'; + this._calculateGravitationalForces(); + this._calculateNodeForces(); - this.manipulationDOM['descriptionSpan'] = document.createElement('span'); - this.manipulationDOM['descriptionSpan'].className = 'network-manipulationUI none'; - this.manipulationDOM['descriptionLabelSpan'] = document.createElement('span'); - this.manipulationDOM['descriptionLabelSpan'].className = 'network-manipulationLabel'; - this.manipulationDOM['descriptionLabelSpan'].innerHTML = locale['editEdgeDescription']; - this.manipulationDOM['descriptionSpan'].appendChild(this.manipulationDOM['descriptionLabelSpan']); + if (this.constants.physics.springConstant > 0) { + if (this.constants.smoothCurves.enabled == true && this.constants.smoothCurves.dynamic == true) { + this._calculateSpringForcesWithSupport(); + } + else { + if (this.constants.physics.hierarchicalRepulsion.enabled == true) { + this._calculateHierarchicalSpringForces(); + } + else { + this._calculateSpringForces(); + } + } + } + }; - this.manipulationDiv.appendChild(this.manipulationDOM['backSpan']); - this.manipulationDiv.appendChild(this.manipulationDOM['seperatorLineDiv1']); - this.manipulationDiv.appendChild(this.manipulationDOM['descriptionSpan']); - // bind the icon - this.manipulationDOM['backSpan'].onclick = this._createManipulatorBar.bind(this); + /** + * Smooth curves are created by adding invisible nodes in the center of the edges. These nodes are also + * handled in the calculateForces function. We then use a quadratic curve with the center node as control. + * This function joins the datanodes and invisible (called support) nodes into one object. + * We do this so we do not contaminate this.nodes with the support nodes. + * + * @private + */ + exports._updateCalculationNodes = function () { + if (this.constants.smoothCurves.enabled == true && this.constants.smoothCurves.dynamic == true) { + this.calculationNodes = {}; + this.calculationNodeIndices = []; - // temporarily overload functions - this.cachedFunctions["_handleTouch"] = this._handleTouch; - this.cachedFunctions["_manipulationReleaseOverload"] = this._manipulationReleaseOverload; - this.cachedFunctions["_handleTap"] = this._handleTap; - this.cachedFunctions["_handleDragStart"] = this._handleDragStart; - this.cachedFunctions["_handleOnDrag"] = this._handleOnDrag; - this._handleTouch = this._selectControlNode; - this._handleTap = function () {}; - this._handleOnDrag = this._controlNodeDrag; - this._handleDragStart = function () {} - this._manipulationReleaseOverload = this._releaseControlNode; + for (var nodeId in this.nodes) { + if (this.nodes.hasOwnProperty(nodeId)) { + this.calculationNodes[nodeId] = this.nodes[nodeId]; + } + } + var supportNodes = this.sectors['support']['nodes']; + for (var supportNodeId in supportNodes) { + if (supportNodes.hasOwnProperty(supportNodeId)) { + if (this.edges.hasOwnProperty(supportNodes[supportNodeId].parentEdgeId)) { + this.calculationNodes[supportNodeId] = supportNodes[supportNodeId]; + } + else { + supportNodes[supportNodeId]._setForce(0, 0); + } + } + } - // redraw to show the unselect - this._redraw(); + for (var idx in this.calculationNodes) { + if (this.calculationNodes.hasOwnProperty(idx)) { + this.calculationNodeIndices.push(idx); + } + } + } + else { + this.calculationNodes = this.nodes; + this.calculationNodeIndices = this.nodeIndices; + } }; /** - * the function bound to the selection event. It checks if you want to connect a cluster and changes the description - * to walk the user through the process. + * this function applies the central gravity effect to keep groups from floating off * * @private */ - exports._selectControlNode = function(pointer) { - this.edgeBeingEdited.controlNodes.from.unselect(); - this.edgeBeingEdited.controlNodes.to.unselect(); - this.selectedControlNode = this.edgeBeingEdited._getSelectedControlNode(this._XconvertDOMtoCanvas(pointer.x),this._YconvertDOMtoCanvas(pointer.y)); - if (this.selectedControlNode !== null) { - this.selectedControlNode.select(); - this.freezeSimulation = true; + exports._calculateGravitationalForces = function () { + var dx, dy, distance, node, i; + var nodes = this.calculationNodes; + var gravity = this.constants.physics.centralGravity; + var gravityForce = 0; + + for (i = 0; i < this.calculationNodeIndices.length; i++) { + node = nodes[this.calculationNodeIndices[i]]; + node.damping = this.constants.physics.damping; // possibly add function to alter damping properties of clusters. + // gravity does not apply when we are in a pocket sector + if (this._sector() == "default" && gravity != 0) { + dx = -node.x; + dy = -node.y; + distance = Math.sqrt(dx * dx + dy * dy); + + gravityForce = (distance == 0) ? 0 : (gravity / distance); + node.fx = dx * gravityForce; + node.fy = dy * gravityForce; + } + else { + node.fx = 0; + node.fy = 0; + } } - this._redraw(); }; + + /** - * the function bound to the selection event. It checks if you want to connect a cluster and changes the description - * to walk the user through the process. + * this function calculates the effects of the springs in the case of unsmooth curves. * * @private */ - exports._controlNodeDrag = function(event) { - var pointer = this._getPointer(event.gesture.center); - if (this.selectedControlNode !== null && this.selectedControlNode !== undefined) { - this.selectedControlNode.x = this._XconvertDOMtoCanvas(pointer.x); - this.selectedControlNode.y = this._YconvertDOMtoCanvas(pointer.y); + exports._calculateSpringForces = function () { + var edgeLength, edge, edgeId; + var dx, dy, fx, fy, springForce, distance; + var edges = this.edges; + + // forces caused by the edges, modelled as springs + for (edgeId in edges) { + if (edges.hasOwnProperty(edgeId)) { + edge = edges[edgeId]; + if (edge.connected) { + // only calculate forces if nodes are in the same sector + if (this.nodes.hasOwnProperty(edge.toId) && this.nodes.hasOwnProperty(edge.fromId)) { + edgeLength = edge.physics.springLength; + // this implies that the edges between big clusters are longer + edgeLength += (edge.to.clusterSize + edge.from.clusterSize - 2) * this.constants.clustering.edgeGrowth; + + dx = (edge.from.x - edge.to.x); + dy = (edge.from.y - edge.to.y); + distance = Math.sqrt(dx * dx + dy * dy); + + if (distance == 0) { + distance = 0.01; + } + + // the 1/distance is so the fx and fy can be calculated without sine or cosine. + springForce = this.constants.physics.springConstant * (edgeLength - distance) / distance; + + fx = dx * springForce; + fy = dy * springForce; + + edge.from.fx += fx; + edge.from.fy += fy; + edge.to.fx -= fx; + edge.to.fy -= fy; + } + } + } } - this._redraw(); }; + + /** + * This function calculates the springforces on the nodes, accounting for the support nodes. * - * @param pointer * @private */ - exports._releaseControlNode = function(pointer) { - var newNode = this._getNodeAt(pointer); - if (newNode !== null) { - if (this.edgeBeingEdited.controlNodes.from.selected == true) { - this.edgeBeingEdited._restoreControlNodes(); - this._editEdge(newNode.id, this.edgeBeingEdited.to.id); - this.edgeBeingEdited.controlNodes.from.unselect(); - } - if (this.edgeBeingEdited.controlNodes.to.selected == true) { - this.edgeBeingEdited._restoreControlNodes(); - this._editEdge(this.edgeBeingEdited.from.id, newNode.id); - this.edgeBeingEdited.controlNodes.to.unselect(); + exports._calculateSpringForcesWithSupport = function () { + var edgeLength, edge, edgeId, combinedClusterSize; + var edges = this.edges; + + // forces caused by the edges, modelled as springs + for (edgeId in edges) { + if (edges.hasOwnProperty(edgeId)) { + edge = edges[edgeId]; + if (edge.connected) { + // only calculate forces if nodes are in the same sector + if (this.nodes.hasOwnProperty(edge.toId) && this.nodes.hasOwnProperty(edge.fromId)) { + if (edge.via != null) { + var node1 = edge.to; + var node2 = edge.via; + var node3 = edge.from; + + edgeLength = edge.physics.springLength; + + combinedClusterSize = node1.clusterSize + node3.clusterSize - 2; + + // this implies that the edges between big clusters are longer + edgeLength += combinedClusterSize * this.constants.clustering.edgeGrowth; + this._calculateSpringForce(node1, node2, 0.5 * edgeLength); + this._calculateSpringForce(node2, node3, 0.5 * edgeLength); + } + } + } } } - else { - this.edgeBeingEdited._restoreControlNodes(); - } - this.freezeSimulation = false; - this._redraw(); }; + /** - * the function bound to the selection event. It checks if you want to connect a cluster and changes the description - * to walk the user through the process. + * This is the code actually performing the calculation for the function above. It is split out to avoid repetition. * + * @param node1 + * @param node2 + * @param edgeLength * @private */ - exports._handleConnect = function(pointer) { - if (this._getSelectedNodeCount() == 0) { - var node = this._getNodeAt(pointer); + exports._calculateSpringForce = function (node1, node2, edgeLength) { + var dx, dy, fx, fy, springForce, distance; - if (node != null) { - if (node.clusterSize > 1) { - alert(this.constants.locales[this.constants.locale]['createEdgeError']) - } - else { - this._selectObject(node,false); - var supportNodes = this.sectors['support']['nodes']; + dx = (node1.x - node2.x); + dy = (node1.y - node2.y); + distance = Math.sqrt(dx * dx + dy * dy); - // create a node the temporary line can look at - supportNodes['targetNode'] = new Node({id:'targetNode'},{},{},this.constants); - var targetNode = supportNodes['targetNode']; - targetNode.x = node.x; - targetNode.y = node.y; + if (distance == 0) { + distance = 0.01; + } - // create a temporary edge - this.edges['connectionEdge'] = new Edge({id:"connectionEdge",from:node.id,to:targetNode.id}, this, this.constants); - var connectionEdge = this.edges['connectionEdge']; - connectionEdge.from = node; - connectionEdge.connected = true; - connectionEdge.options.smoothCurves = {enabled: true, - dynamic: false, - type: "continuous", - roundness: 0.5 - }; - connectionEdge.selected = true; - connectionEdge.to = targetNode; + // the 1/distance is so the fx and fy can be calculated without sine or cosine. + springForce = this.constants.physics.springConstant * (edgeLength - distance) / distance; - this.cachedFunctions["_handleOnDrag"] = this._handleOnDrag; - this._handleOnDrag = function(event) { - var pointer = this._getPointer(event.gesture.center); - var connectionEdge = this.edges['connectionEdge']; - connectionEdge.to.x = this._XconvertDOMtoCanvas(pointer.x); - connectionEdge.to.y = this._YconvertDOMtoCanvas(pointer.y); - }; + fx = dx * springForce; + fy = dy * springForce; - this.moving = true; - this.start(); - } + node1.fx += fx; + node1.fy += fy; + node2.fx -= fx; + node2.fy -= fy; + }; + + + exports._cleanupPhysicsConfiguration = function() { + if (this.physicsConfiguration !== undefined) { + while (this.physicsConfiguration.hasChildNodes()) { + this.physicsConfiguration.removeChild(this.physicsConfiguration.firstChild); } + + this.physicsConfiguration.parentNode.removeChild(this.physicsConfiguration); + this.physicsConfiguration = undefined; } - }; + } - exports._finishConnect = function(event) { - if (this._getSelectedNodeCount() == 1) { - var pointer = this._getPointer(event.gesture.center); - // restore the drag function - this._handleOnDrag = this.cachedFunctions["_handleOnDrag"]; - delete this.cachedFunctions["_handleOnDrag"]; + /** + * Load the HTML for the physics config and bind it + * @private + */ + exports._loadPhysicsConfiguration = function () { + if (this.physicsConfiguration === undefined) { + this.backupConstants = {}; + util.deepExtend(this.backupConstants,this.constants); - // remember the edge id - var connectFromId = this.edges['connectionEdge'].fromId; + var hierarchicalLayoutDirections = ["LR", "RL", "UD", "DU"]; + this.physicsConfiguration = document.createElement('div'); + this.physicsConfiguration.className = "PhysicsConfiguration"; + this.physicsConfiguration.innerHTML = '' + + '' + + '' + + '' + + '' + + '' + + '' + + '
Simulation Mode:
Barnes HutRepulsionHierarchical
' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '
Options:
' + this.containerElement.parentElement.insertBefore(this.physicsConfiguration, this.containerElement); + this.optionsDiv = document.createElement("div"); + this.optionsDiv.style.fontSize = "14px"; + this.optionsDiv.style.fontFamily = "verdana"; + this.containerElement.parentElement.insertBefore(this.optionsDiv, this.containerElement); - // remove the temporary nodes and edge - delete this.edges['connectionEdge']; - delete this.sectors['support']['nodes']['targetNode']; - delete this.sectors['support']['nodes']['targetViaNode']; + var rangeElement; + rangeElement = document.getElementById('graph_BH_gc'); + rangeElement.onchange = showValueOfRange.bind(this, 'graph_BH_gc', -1, "physics_barnesHut_gravitationalConstant"); + rangeElement = document.getElementById('graph_BH_cg'); + rangeElement.onchange = showValueOfRange.bind(this, 'graph_BH_cg', 1, "physics_centralGravity"); + rangeElement = document.getElementById('graph_BH_sc'); + rangeElement.onchange = showValueOfRange.bind(this, 'graph_BH_sc', 1, "physics_springConstant"); + rangeElement = document.getElementById('graph_BH_sl'); + rangeElement.onchange = showValueOfRange.bind(this, 'graph_BH_sl', 1, "physics_springLength"); + rangeElement = document.getElementById('graph_BH_damp'); + rangeElement.onchange = showValueOfRange.bind(this, 'graph_BH_damp', 1, "physics_damping"); - var node = this._getNodeAt(pointer); - if (node != null) { - if (node.clusterSize > 1) { - alert(this.constants.locales[this.constants.locale]["createEdgeError"]) - } - else { - this._createEdge(connectFromId,node.id); - this._createManipulatorBar(); - } + rangeElement = document.getElementById('graph_R_nd'); + rangeElement.onchange = showValueOfRange.bind(this, 'graph_R_nd', 1, "physics_repulsion_nodeDistance"); + rangeElement = document.getElementById('graph_R_cg'); + rangeElement.onchange = showValueOfRange.bind(this, 'graph_R_cg', 1, "physics_centralGravity"); + rangeElement = document.getElementById('graph_R_sc'); + rangeElement.onchange = showValueOfRange.bind(this, 'graph_R_sc', 1, "physics_springConstant"); + rangeElement = document.getElementById('graph_R_sl'); + rangeElement.onchange = showValueOfRange.bind(this, 'graph_R_sl', 1, "physics_springLength"); + rangeElement = document.getElementById('graph_R_damp'); + rangeElement.onchange = showValueOfRange.bind(this, 'graph_R_damp', 1, "physics_damping"); + + rangeElement = document.getElementById('graph_H_nd'); + rangeElement.onchange = showValueOfRange.bind(this, 'graph_H_nd', 1, "physics_hierarchicalRepulsion_nodeDistance"); + rangeElement = document.getElementById('graph_H_cg'); + rangeElement.onchange = showValueOfRange.bind(this, 'graph_H_cg', 1, "physics_centralGravity"); + rangeElement = document.getElementById('graph_H_sc'); + rangeElement.onchange = showValueOfRange.bind(this, 'graph_H_sc', 1, "physics_springConstant"); + rangeElement = document.getElementById('graph_H_sl'); + rangeElement.onchange = showValueOfRange.bind(this, 'graph_H_sl', 1, "physics_springLength"); + rangeElement = document.getElementById('graph_H_damp'); + rangeElement.onchange = showValueOfRange.bind(this, 'graph_H_damp', 1, "physics_damping"); + rangeElement = document.getElementById('graph_H_direction'); + rangeElement.onchange = showValueOfRange.bind(this, 'graph_H_direction', hierarchicalLayoutDirections, "hierarchicalLayout_direction"); + rangeElement = document.getElementById('graph_H_levsep'); + rangeElement.onchange = showValueOfRange.bind(this, 'graph_H_levsep', 1, "hierarchicalLayout_levelSeparation"); + rangeElement = document.getElementById('graph_H_nspac'); + rangeElement.onchange = showValueOfRange.bind(this, 'graph_H_nspac', 1, "hierarchicalLayout_nodeSpacing"); + + var radioButton1 = document.getElementById("graph_physicsMethod1"); + var radioButton2 = document.getElementById("graph_physicsMethod2"); + var radioButton3 = document.getElementById("graph_physicsMethod3"); + radioButton2.checked = true; + if (this.constants.physics.barnesHut.enabled) { + radioButton1.checked = true; + } + if (this.constants.hierarchicalLayout.enabled) { + radioButton3.checked = true; } - this._unselectAll(); - } - }; + var graph_toggleSmooth = document.getElementById("graph_toggleSmooth"); + var graph_repositionNodes = document.getElementById("graph_repositionNodes"); + var graph_generateOptions = document.getElementById("graph_generateOptions"); - /** - * Adds a node on the specified location - */ - exports._addNode = function() { - if (this._selectionIsEmpty() && this.editMode == true) { - var positionObject = this._pointerToPositionObject(this.pointerPosition); - var defaultData = {id:util.randomUUID(),x:positionObject.left,y:positionObject.top,label:"new",allowedToMoveX:true,allowedToMoveY:true}; - if (this.triggerFunctions.add) { - if (this.triggerFunctions.add.length == 2) { - var me = this; - this.triggerFunctions.add(defaultData, function(finalizedData) { - me.nodesData.add(finalizedData); - me._createManipulatorBar(); - me.moving = true; - me.start(); - }); - } - else { - throw new Error('The function for add does not support two arguments (data,callback)'); - this._createManipulatorBar(); - this.moving = true; - this.start(); - } + graph_toggleSmooth.onclick = graphToggleSmoothCurves.bind(this); + graph_repositionNodes.onclick = graphRepositionNodes.bind(this); + graph_generateOptions.onclick = graphGenerateOptions.bind(this); + if (this.constants.smoothCurves == true && this.constants.dynamicSmoothCurves == false) { + graph_toggleSmooth.style.background = "#A4FF56"; } else { - this.nodesData.add(defaultData); - this._createManipulatorBar(); - this.moving = true; - this.start(); + graph_toggleSmooth.style.background = "#FF8532"; } + + + switchConfigurations.apply(this); + + radioButton1.onchange = switchConfigurations.bind(this); + radioButton2.onchange = switchConfigurations.bind(this); + radioButton3.onchange = switchConfigurations.bind(this); } }; - /** - * connect two nodes with a new edge. + * This overwrites the this.constants. * + * @param constantsVariableName + * @param value * @private */ - exports._createEdge = function(sourceNodeId,targetNodeId) { - if (this.editMode == true) { - var defaultData = {from:sourceNodeId, to:targetNodeId}; - if (this.triggerFunctions.connect) { - if (this.triggerFunctions.connect.length == 2) { - var me = this; - this.triggerFunctions.connect(defaultData, function(finalizedData) { - me.edgesData.add(finalizedData); - me.moving = true; - me.start(); - }); - } - else { - throw new Error('The function for connect does not support two arguments (data,callback)'); - this.moving = true; - this.start(); - } - } - else { - this.edgesData.add(defaultData); - this.moving = true; - this.start(); - } + exports._overWriteGraphConstants = function (constantsVariableName, value) { + var nameArray = constantsVariableName.split("_"); + if (nameArray.length == 1) { + this.constants[nameArray[0]] = value; + } + else if (nameArray.length == 2) { + this.constants[nameArray[0]][nameArray[1]] = value; + } + else if (nameArray.length == 3) { + this.constants[nameArray[0]][nameArray[1]][nameArray[2]] = value; } }; + /** - * connect two nodes with a new edge. - * - * @private + * this function is bound to the toggle smooth curves button. That is also why it is not in the prototype. */ - exports._editEdge = function(sourceNodeId,targetNodeId) { - if (this.editMode == true) { - var defaultData = {id: this.edgeBeingEdited.id, from:sourceNodeId, to:targetNodeId}; - if (this.triggerFunctions.editEdge) { - if (this.triggerFunctions.editEdge.length == 2) { - var me = this; - this.triggerFunctions.editEdge(defaultData, function(finalizedData) { - me.edgesData.update(finalizedData); - me.moving = true; - me.start(); - }); - } - else { - throw new Error('The function for edit does not support two arguments (data, callback)'); - this.moving = true; - this.start(); - } - } - else { - this.edgesData.update(defaultData); - this.moving = true; - this.start(); - } - } - }; + function graphToggleSmoothCurves () { + this.constants.smoothCurves.enabled = !this.constants.smoothCurves.enabled; + var graph_toggleSmooth = document.getElementById("graph_toggleSmooth"); + if (this.constants.smoothCurves.enabled == true) {graph_toggleSmooth.style.background = "#A4FF56";} + else {graph_toggleSmooth.style.background = "#FF8532";} + + this._configureSmoothCurves(false); + } /** - * Create the toolbar to edit the selected node. The label and the color can be changed. Other colors are derived from the chosen color. + * this function is used to scramble the nodes * - * @private */ - exports._editNode = function() { - if (this.triggerFunctions.edit && this.editMode == true) { - var node = this._getSelectedNode(); - var data = {id:node.id, - label: node.label, - group: node.options.group, - shape: node.options.shape, - color: { - background:node.options.color.background, - border:node.options.color.border, - highlight: { - background:node.options.color.highlight.background, - border:node.options.color.highlight.border - } - }}; - if (this.triggerFunctions.edit.length == 2) { - var me = this; - this.triggerFunctions.edit(data, function (finalizedData) { - me.nodesData.update(finalizedData); - me._createManipulatorBar(); - me.moving = true; - me.start(); - }); - } - else { - throw new Error('The function for edit does not support two arguments (data, callback)'); + function graphRepositionNodes () { + for (var nodeId in this.calculationNodes) { + if (this.calculationNodes.hasOwnProperty(nodeId)) { + this.calculationNodes[nodeId].vx = 0; this.calculationNodes[nodeId].vy = 0; + this.calculationNodes[nodeId].fx = 0; this.calculationNodes[nodeId].fy = 0; } } + if (this.constants.hierarchicalLayout.enabled == true) { + this._setupHierarchicalLayout(); + showValueOfRange.call(this, 'graph_H_nd', 1, "physics_hierarchicalRepulsion_nodeDistance"); + showValueOfRange.call(this, 'graph_H_cg', 1, "physics_centralGravity"); + showValueOfRange.call(this, 'graph_H_sc', 1, "physics_springConstant"); + showValueOfRange.call(this, 'graph_H_sl', 1, "physics_springLength"); + showValueOfRange.call(this, 'graph_H_damp', 1, "physics_damping"); + } else { - throw new Error('No edit function has been bound to this button'); + this.repositionNodes(); } - }; - - - + this.moving = true; + this.start(); + } /** - * delete everything in the selection - * - * @private + * this is used to generate an options file from the playing with physics system. */ - exports._deleteSelected = function() { - if (!this._selectionIsEmpty() && this.editMode == true) { - if (!this._clusterInSelection()) { - var selectedNodes = this.getSelectedNodes(); - var selectedEdges = this.getSelectedEdges(); - if (this.triggerFunctions.del) { - var me = this; - var data = {nodes: selectedNodes, edges: selectedEdges}; - if (this.triggerFunctions.del.length == 2) { - this.triggerFunctions.del(data, function (finalizedData) { - me.edgesData.remove(finalizedData.edges); - me.nodesData.remove(finalizedData.nodes); - me._unselectAll(); - me.moving = true; - me.start(); - }); - } - else { - throw new Error('The function for delete does not support two arguments (data, callback)') + function graphGenerateOptions () { + var options = "No options are required, default values used."; + var optionsSpecific = []; + var radioButton1 = document.getElementById("graph_physicsMethod1"); + var radioButton2 = document.getElementById("graph_physicsMethod2"); + if (radioButton1.checked == true) { + if (this.constants.physics.barnesHut.gravitationalConstant != this.backupConstants.physics.barnesHut.gravitationalConstant) {optionsSpecific.push("gravitationalConstant: " + this.constants.physics.barnesHut.gravitationalConstant);} + if (this.constants.physics.centralGravity != this.backupConstants.physics.barnesHut.centralGravity) {optionsSpecific.push("centralGravity: " + this.constants.physics.centralGravity);} + if (this.constants.physics.springLength != this.backupConstants.physics.barnesHut.springLength) {optionsSpecific.push("springLength: " + this.constants.physics.springLength);} + if (this.constants.physics.springConstant != this.backupConstants.physics.barnesHut.springConstant) {optionsSpecific.push("springConstant: " + this.constants.physics.springConstant);} + if (this.constants.physics.damping != this.backupConstants.physics.barnesHut.damping) {optionsSpecific.push("damping: " + this.constants.physics.damping);} + if (optionsSpecific.length != 0) { + options = "var options = {"; + options += "physics: {barnesHut: {"; + for (var i = 0; i < optionsSpecific.length; i++) { + options += optionsSpecific[i]; + if (i < optionsSpecific.length - 1) { + options += ", " } } - else { - this.edgesData.remove(selectedEdges); - this.nodesData.remove(selectedNodes); - this._unselectAll(); - this.moving = true; - this.start(); + options += '}}' + } + if (this.constants.smoothCurves.enabled != this.backupConstants.smoothCurves.enabled) { + if (optionsSpecific.length == 0) {options = "var options = {";} + else {options += ", "} + options += "smoothCurves: " + this.constants.smoothCurves.enabled; + } + if (options != "No options are required, default values used.") { + options += '};' + } + } + else if (radioButton2.checked == true) { + options = "var options = {"; + options += "physics: {barnesHut: {enabled: false}"; + if (this.constants.physics.repulsion.nodeDistance != this.backupConstants.physics.repulsion.nodeDistance) {optionsSpecific.push("nodeDistance: " + this.constants.physics.repulsion.nodeDistance);} + if (this.constants.physics.centralGravity != this.backupConstants.physics.repulsion.centralGravity) {optionsSpecific.push("centralGravity: " + this.constants.physics.centralGravity);} + if (this.constants.physics.springLength != this.backupConstants.physics.repulsion.springLength) {optionsSpecific.push("springLength: " + this.constants.physics.springLength);} + if (this.constants.physics.springConstant != this.backupConstants.physics.repulsion.springConstant) {optionsSpecific.push("springConstant: " + this.constants.physics.springConstant);} + if (this.constants.physics.damping != this.backupConstants.physics.repulsion.damping) {optionsSpecific.push("damping: " + this.constants.physics.damping);} + if (optionsSpecific.length != 0) { + options += ", repulsion: {"; + for (var i = 0; i < optionsSpecific.length; i++) { + options += optionsSpecific[i]; + if (i < optionsSpecific.length - 1) { + options += ", " + } } + options += '}}' } - else { - alert(this.constants.locales[this.constants.locale]["deleteClusterError"]); + if (optionsSpecific.length == 0) {options += "}"} + if (this.constants.smoothCurves != this.backupConstants.smoothCurves) { + options += ", smoothCurves: " + this.constants.smoothCurves; } + options += '};' } - }; - - -/***/ }, -/* 68 */ -/***/ function(module, exports, __webpack_require__) { - - var util = __webpack_require__(1); - var Hammer = __webpack_require__(19); - - exports._cleanNavigation = function() { - // clean hammer bindings - if (this.navigationHammers.existing.length != 0) { - for (var i = 0; i < this.navigationHammers.existing.length; i++) { - this.navigationHammers.existing[i].dispose(); + else { + options = "var options = {"; + if (this.constants.physics.hierarchicalRepulsion.nodeDistance != this.backupConstants.physics.hierarchicalRepulsion.nodeDistance) {optionsSpecific.push("nodeDistance: " + this.constants.physics.hierarchicalRepulsion.nodeDistance);} + if (this.constants.physics.centralGravity != this.backupConstants.physics.hierarchicalRepulsion.centralGravity) {optionsSpecific.push("centralGravity: " + this.constants.physics.centralGravity);} + if (this.constants.physics.springLength != this.backupConstants.physics.hierarchicalRepulsion.springLength) {optionsSpecific.push("springLength: " + this.constants.physics.springLength);} + if (this.constants.physics.springConstant != this.backupConstants.physics.hierarchicalRepulsion.springConstant) {optionsSpecific.push("springConstant: " + this.constants.physics.springConstant);} + if (this.constants.physics.damping != this.backupConstants.physics.hierarchicalRepulsion.damping) {optionsSpecific.push("damping: " + this.constants.physics.damping);} + if (optionsSpecific.length != 0) { + options += "physics: {hierarchicalRepulsion: {"; + for (var i = 0; i < optionsSpecific.length; i++) { + options += optionsSpecific[i]; + if (i < optionsSpecific.length - 1) { + options += ", "; + } + } + options += '}},'; } - this.navigationHammers.existing = []; + options += 'hierarchicalLayout: {'; + optionsSpecific = []; + if (this.constants.hierarchicalLayout.direction != this.backupConstants.hierarchicalLayout.direction) {optionsSpecific.push("direction: " + this.constants.hierarchicalLayout.direction);} + if (Math.abs(this.constants.hierarchicalLayout.levelSeparation) != this.backupConstants.hierarchicalLayout.levelSeparation) {optionsSpecific.push("levelSeparation: " + this.constants.hierarchicalLayout.levelSeparation);} + if (this.constants.hierarchicalLayout.nodeSpacing != this.backupConstants.hierarchicalLayout.nodeSpacing) {optionsSpecific.push("nodeSpacing: " + this.constants.hierarchicalLayout.nodeSpacing);} + if (optionsSpecific.length != 0) { + for (var i = 0; i < optionsSpecific.length; i++) { + options += optionsSpecific[i]; + if (i < optionsSpecific.length - 1) { + options += ", " + } + } + options += '}' + } + else { + options += "enabled:true}"; + } + options += '};' } - this._navigationReleaseOverload = function () {}; - // clean up previous navigation items - if (this.navigationDivs && this.navigationDivs['wrapper'] && this.navigationDivs['wrapper'].parentNode) { - this.navigationDivs['wrapper'].parentNode.removeChild(this.navigationDivs['wrapper']); - } - }; + this.optionsDiv.innerHTML = options; + } /** - * 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. + * this is used to switch between barnesHut, repulsion and hierarchical. * - * @private */ - exports._loadNavigationElements = function() { - this._cleanNavigation(); - - this.navigationDivs = {}; - var navigationDivs = ['up','down','left','right','zoomIn','zoomOut','zoomExtends']; - var navigationDivActions = ['_moveUp','_moveDown','_moveLeft','_moveRight','_zoomIn','_zoomOut','_zoomExtent']; - - this.navigationDivs['wrapper'] = document.createElement('div'); - this.frame.appendChild(this.navigationDivs['wrapper']); - - for (var i = 0; i < navigationDivs.length; i++) { - this.navigationDivs[navigationDivs[i]] = document.createElement('div'); - this.navigationDivs[navigationDivs[i]].className = 'network-navigation ' + navigationDivs[i]; - this.navigationDivs['wrapper'].appendChild(this.navigationDivs[navigationDivs[i]]); - - var hammer = Hammer(this.navigationDivs[navigationDivs[i]], {prevent_default: true}); - hammer.on('touch', this[navigationDivActions[i]].bind(this)); - this.navigationHammers._new.push(hammer); + function switchConfigurations () { + var ids = ["graph_BH_table", "graph_R_table", "graph_H_table"]; + var radioButton = document.querySelector('input[name="graph_physicsMethod"]:checked').value; + var tableId = "graph_" + radioButton + "_table"; + var table = document.getElementById(tableId); + table.style.display = "block"; + for (var i = 0; i < ids.length; i++) { + if (ids[i] != tableId) { + table = document.getElementById(ids[i]); + table.style.display = "none"; + } } - - this._navigationReleaseOverload = this._stopMovement; - - this.navigationHammers.existing = this.navigationHammers._new; - }; + this._restoreNodes(); + if (radioButton == "R") { + this.constants.hierarchicalLayout.enabled = false; + this.constants.physics.hierarchicalRepulsion.enabled = false; + this.constants.physics.barnesHut.enabled = false; + } + else if (radioButton == "H") { + if (this.constants.hierarchicalLayout.enabled == false) { + this.constants.hierarchicalLayout.enabled = true; + this.constants.physics.hierarchicalRepulsion.enabled = true; + this.constants.physics.barnesHut.enabled = false; + this.constants.smoothCurves.enabled = false; + this._setupHierarchicalLayout(); + } + } + else { + this.constants.hierarchicalLayout.enabled = false; + this.constants.physics.hierarchicalRepulsion.enabled = false; + this.constants.physics.barnesHut.enabled = true; + } + this._loadSelectedForceSolver(); + var graph_toggleSmooth = document.getElementById("graph_toggleSmooth"); + if (this.constants.smoothCurves.enabled == true) {graph_toggleSmooth.style.background = "#A4FF56";} + else {graph_toggleSmooth.style.background = "#FF8532";} + this.moving = true; + this.start(); + } /** - * this stops all movement induced by the navigation buttons + * this generates the ranges depending on the iniital values. * - * @private + * @param id + * @param map + * @param constantsVariableName */ - exports._zoomExtent = function(event) { - this.zoomExtent({duration:700}); - event.stopPropagation(); - }; + function showValueOfRange (id,map,constantsVariableName) { + var valueId = id + "_value"; + var rangeValue = document.getElementById(id).value; - /** - * this stops all movement induced by the navigation buttons - * - * @private - */ - exports._stopMovement = function() { - this._xStopMoving(); - this._yStopMoving(); - this._stopZoom(); - }; + if (Array.isArray(map)) { + document.getElementById(valueId).value = map[parseInt(rangeValue)]; + this._overWriteGraphConstants(constantsVariableName,map[parseInt(rangeValue)]); + } + else { + document.getElementById(valueId).value = parseInt(map) * parseFloat(rangeValue); + this._overWriteGraphConstants(constantsVariableName, parseInt(map) * parseFloat(rangeValue)); + } + if (constantsVariableName == "hierarchicalLayout_direction" || + constantsVariableName == "hierarchicalLayout_levelSeparation" || + constantsVariableName == "hierarchicalLayout_nodeSpacing") { + this._setupHierarchicalLayout(); + } + this.moving = true; + this.start(); + } - /** - * move the screen up - * By using the increments, instead of adding a fixed number to the translation, we keep fluent and - * instant movement. The onKeypress event triggers immediately, then pauses, then triggers frequently - * To avoid this behaviour, we do the translation in the start loop. - * - * @private - */ - exports._moveUp = function(event) { - this.yIncrement = this.constants.keyboard.speed.y; - this.start(); // if there is no node movement, the calculation wont be done - event.preventDefault(); - }; - /** - * move the screen down - * @private - */ - exports._moveDown = function(event) { - this.yIncrement = -this.constants.keyboard.speed.y; - this.start(); // if there is no node movement, the calculation wont be done - event.preventDefault(); - }; +/***/ }, +/* 67 */ +/***/ function(module, exports, __webpack_require__) { - /** - * move the screen left - * @private - */ - exports._moveLeft = function(event) { - this.xIncrement = this.constants.keyboard.speed.x; - this.start(); // if there is no node movement, the calculation wont be done - event.preventDefault(); - }; + function webpackContext(req) { + throw new Error("Cannot find module '" + req + "'."); + } + webpackContext.keys = function() { return []; }; + webpackContext.resolve = webpackContext; + module.exports = webpackContext; + webpackContext.id = 67; +/***/ }, +/* 68 */ +/***/ function(module, exports, __webpack_require__) { + /** - * move the screen right + * Calculate the forces the nodes apply on each other based on a repulsion field. + * This field is linearly approximated. + * * @private */ - exports._moveRight = function(event) { - this.xIncrement = -this.constants.keyboard.speed.y; - this.start(); // if there is no node movement, the calculation wont be done - event.preventDefault(); - }; + exports._calculateNodeForces = function () { + var dx, dy, angle, distance, fx, fy, combinedClusterSize, + repulsingForce, node1, node2, i, j; + var nodes = this.calculationNodes; + var nodeIndices = this.calculationNodeIndices; - /** - * Zoom in, using the same method as the movement. - * @private - */ - exports._zoomIn = function(event) { - this.zoomIncrement = this.constants.keyboard.speed.zoom; - this.start(); // if there is no node movement, the calculation wont be done - event.preventDefault(); - }; + // approximation constants + var a_base = -2 / 3; + var b = 4 / 3; + // repulsing forces between nodes + var nodeDistance = this.constants.physics.repulsion.nodeDistance; + var minimumDistance = nodeDistance; - /** - * Zoom out - * @private - */ - exports._zoomOut = function(event) { - this.zoomIncrement = -this.constants.keyboard.speed.zoom; - this.start(); // if there is no node movement, the calculation wont be done - event.preventDefault(); - }; + // 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]]; + combinedClusterSize = node1.clusterSize + node2.clusterSize - 2; + dx = node2.x - node1.x; + dy = node2.y - node1.y; + distance = Math.sqrt(dx * dx + dy * dy); - /** - * Stop zooming and unhighlight the zoom controls - * @private - */ - exports._stopZoom = function(event) { - this.zoomIncrement = 0; - event && event.preventDefault(); - }; + // same condition as BarnesHut, making sure nodes are never 100% overlapping. + if (distance == 0) { + distance = 0.1*Math.random(); + dx = distance; + } + minimumDistance = (combinedClusterSize == 0) ? nodeDistance : (nodeDistance * (1 + combinedClusterSize * this.constants.clustering.distanceAmplification)); + var a = a_base / minimumDistance; + if (distance < 2 * minimumDistance) { + if (distance < 0.5 * minimumDistance) { + repulsingForce = 1.0; + } + else { + repulsingForce = a * distance + b; // linear approx of 1 / (1 + Math.exp((distance / minimumDistance - 1) * steepness)) + } - /** - * Stop moving in the Y direction and unHighlight the up and down - * @private - */ - exports._yStopMoving = function(event) { - this.yIncrement = 0; - event && event.preventDefault(); - }; + // amplify the repulsion for clusters. + repulsingForce *= (combinedClusterSize == 0) ? 1 : 1 + combinedClusterSize * this.constants.clustering.forceAmplification; + repulsingForce = repulsingForce / Math.max(distance,0.01*minimumDistance); + fx = dx * repulsingForce; + fy = dy * repulsingForce; + node1.fx -= fx; + node1.fy -= fy; + node2.fx += fx; + node2.fy += fy; - /** - * Stop moving in the X direction and unHighlight left and right. - * @private - */ - exports._xStopMoving = function(event) { - this.xIncrement = 0; - event && event.preventDefault(); + } + } + } }; @@ -34005,676 +34106,579 @@ return /******/ (function(modules) { // webpackBootstrap /* 69 */ /***/ function(module, exports, __webpack_require__) { - exports._resetLevels = function() { - for (var nodeId in this.nodes) { - if (this.nodes.hasOwnProperty(nodeId)) { - var node = this.nodes[nodeId]; - if (node.preassignedLevel == false) { - node.level = -1; - node.hierarchyEnumerated = false; - } - } - } - }; - /** - * This is the main function to layout the nodes in a hierarchical way. - * It checks if the node details are supplied correctly + * Calculate the forces the nodes apply on eachother based on a repulsion field. + * This field is linearly approximated. * * @private */ - exports._setupHierarchicalLayout = function() { - if (this.constants.hierarchicalLayout.enabled == true && this.nodeIndices.length > 0) { - // get the size of the largest hubs and check if the user has defined a level for a node. - var hubsize = 0; - var node, nodeId; - var definedLevel = false; - var undefinedLevel = false; + exports._calculateNodeForces = function () { + var dx, dy, distance, fx, fy, + repulsingForce, node1, node2, i, j; - for (nodeId in this.nodes) { - if (this.nodes.hasOwnProperty(nodeId)) { - node = this.nodes[nodeId]; - if (node.level != -1) { - definedLevel = true; - } - else { - undefinedLevel = true; - } - if (hubsize < node.edges.length) { - hubsize = node.edges.length; - } - } - } + var nodes = this.calculationNodes; + var nodeIndices = this.calculationNodeIndices; - // if the user defined some levels but not all, alert and run without hierarchical layout - if (undefinedLevel == true && definedLevel == true) { - throw new Error("To use the hierarchical layout, nodes require either no predefined levels or levels have to be defined for all nodes."); - this.zoomExtent(undefined,true,this.constants.clustering.enabled); - if (!this.constants.clustering.enabled) { - this.start(); - } - } - else { - // setup the system to use hierarchical method. - this._changeConstants(); + // repulsing forces between nodes + var nodeDistance = this.constants.physics.hierarchicalRepulsion.nodeDistance; - // define levels if undefined by the users. Based on hubsize - if (undefinedLevel == true) { - if (this.constants.hierarchicalLayout.layout == "hubsize") { - this._determineLevels(hubsize); + // 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 { - this._determineLevelsDirected(false); + repulsingForce = 0; } + // normalize force with + if (distance == 0) { + distance = 0.01; + } + else { + repulsingForce = repulsingForce / distance; + } + fx = dx * repulsingForce; + fy = dy * repulsingForce; + node1.fx -= fx; + node1.fy -= fy; + node2.fx += fx; + node2.fy += fy; } - // check the distribution of the nodes per level. - var distribution = this._getDistribution(); - - // place the nodes on the canvas. This also stablilizes the system. - this._placeNodesByHierarchy(distribution); - - // start the simulation. - this.start(); } } }; /** - * This function places the nodes on the canvas based on the hierarchial distribution. + * this function calculates the effects of the springs in the case of unsmooth curves. * - * @param {Object} distribution | obtained by the function this._getDistribution() * @private */ - exports._placeNodesByHierarchy = function(distribution) { - var nodeId, node; + exports._calculateHierarchicalSpringForces = function () { + var edgeLength, edge, edgeId; + var dx, dy, fx, fy, springForce, distance; + var edges = this.edges; - // start placing all the level 0 nodes first. Then recursively position their branches. - for (var level in distribution) { - if (distribution.hasOwnProperty(level)) { + var nodes = this.calculationNodes; + var nodeIndices = this.calculationNodeIndices; - for (nodeId in distribution[level].nodes) { - if (distribution[level].nodes.hasOwnProperty(nodeId)) { - node = distribution[level].nodes[nodeId]; - if (this.constants.hierarchicalLayout.direction == "UD" || this.constants.hierarchicalLayout.direction == "DU") { - if (node.xFixed) { - node.x = distribution[level].minPos; - node.xFixed = false; - distribution[level].minPos += distribution[level].nodeSpacing; - } + for (var i = 0; i < nodeIndices.length; i++) { + var node1 = nodes[nodeIndices[i]]; + node1.springFx = 0; + node1.springFy = 0; + } + + + // forces caused by the edges, modelled as springs + for (edgeId in edges) { + if (edges.hasOwnProperty(edgeId)) { + edge = edges[edgeId]; + if (edge.connected) { + // only calculate forces if nodes are in the same sector + if (this.nodes.hasOwnProperty(edge.toId) && this.nodes.hasOwnProperty(edge.fromId)) { + edgeLength = edge.physics.springLength; + // this implies that the edges between big clusters are longer + edgeLength += (edge.to.clusterSize + edge.from.clusterSize - 2) * this.constants.clustering.edgeGrowth; + + dx = (edge.from.x - edge.to.x); + dy = (edge.from.y - edge.to.y); + distance = Math.sqrt(dx * dx + dy * dy); + + if (distance == 0) { + distance = 0.01; } - else { - if (node.yFixed) { - node.y = distribution[level].minPos; - node.yFixed = false; - distribution[level].minPos += distribution[level].nodeSpacing; - } + // the 1/distance is so the fx and fy can be calculated without sine or cosine. + springForce = this.constants.physics.springConstant * (edgeLength - distance) / distance; + + fx = dx * springForce; + fy = dy * springForce; + + + + if (edge.to.level != edge.from.level) { + edge.to.springFx -= fx; + edge.to.springFy -= fy; + edge.from.springFx += fx; + edge.from.springFy += fy; + } + else { + var factor = 0.5; + edge.to.fx -= factor*fx; + edge.to.fy -= factor*fy; + edge.from.fx += factor*fx; + edge.from.fy += factor*fy; } - this._placeBranchNodes(node.edges,node.id,distribution,node.level); } } } } - // stabilize the system after positioning. This function calls zoomExtent. - this._stabilize(); + // normalize spring forces + var springForce = 1; + var springFx, springFy; + for (i = 0; i < nodeIndices.length; i++) { + var node = nodes[nodeIndices[i]]; + springFx = Math.min(springForce,Math.max(-springForce,node.springFx)); + springFy = Math.min(springForce,Math.max(-springForce,node.springFy)); + + node.fx += springFx; + node.fy += springFy; + } + + // retain energy balance + var totalFx = 0; + var totalFy = 0; + for (i = 0; i < nodeIndices.length; i++) { + var node = nodes[nodeIndices[i]]; + totalFx += node.fx; + totalFy += node.fy; + } + var correctionFx = totalFx / nodeIndices.length; + var correctionFy = totalFy / nodeIndices.length; + + for (i = 0; i < nodeIndices.length; i++) { + var node = nodes[nodeIndices[i]]; + node.fx -= correctionFx; + node.fy -= correctionFy; + } + }; +/***/ }, +/* 70 */ +/***/ function(module, exports, __webpack_require__) { /** - * This function get the distribution of levels based on hubsize + * 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. * - * @returns {Object} * @private */ - exports._getDistribution = function() { - var distribution = {}; - var nodeId, node, level; + exports._calculateNodeForces = function() { + if (this.constants.physics.barnesHut.gravitationalConstant != 0) { + var node; + var nodes = this.calculationNodes; + var nodeIndices = this.calculationNodeIndices; + var nodeCount = nodeIndices.length; - // we fix Y because the hierarchy is vertical, we fix X so we do not give a node an x position for a second time. - // the fix of X is removed after the x value has been set. - for (nodeId in this.nodes) { - if (this.nodes.hasOwnProperty(nodeId)) { - node = this.nodes[nodeId]; - node.xFixed = true; - node.yFixed = true; - if (this.constants.hierarchicalLayout.direction == "UD" || this.constants.hierarchicalLayout.direction == "DU") { - node.y = this.constants.hierarchicalLayout.levelSeparation*node.level; - } - else { - node.x = this.constants.hierarchicalLayout.levelSeparation*node.level; - } - if (distribution[node.level] === undefined) { - distribution[node.level] = {amount: 0, nodes: {}, minPos:0, nodeSpacing:0}; - } - distribution[node.level].amount += 1; - distribution[node.level].nodes[nodeId] = node; - } - } + this._formBarnesHutTree(nodes,nodeIndices); - // determine the largest amount of nodes of all levels - var maxCount = 0; - for (level in distribution) { - if (distribution.hasOwnProperty(level)) { - if (maxCount < distribution[level].amount) { - maxCount = distribution[level].amount; - } - } - } + var barnesHutTree = this.barnesHutTree; - // set the initial position and spacing of each nodes accordingly - for (level in distribution) { - if (distribution.hasOwnProperty(level)) { - distribution[level].nodeSpacing = (maxCount + 1) * this.constants.hierarchicalLayout.nodeSpacing; - distribution[level].nodeSpacing /= (distribution[level].amount + 1); - distribution[level].minPos = distribution[level].nodeSpacing - (0.5 * (distribution[level].amount + 1) * distribution[level].nodeSpacing); + // 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 BarnesHut 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); + } } } - - return distribution; }; /** - * this function allocates nodes in levels based on the recursive branching from the largest hubs. + * 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 hubsize + * @param parentBranch + * @param node * @private */ - exports._determineLevels = function(hubsize) { - var nodeId, node; + exports._getForceContribution = function(parentBranch,node) { + // we get no force contribution from an empty region + if (parentBranch.childrenCount > 0) { + var dx,dy,distance; - // determine hubs - for (nodeId in this.nodes) { - if (this.nodes.hasOwnProperty(nodeId)) { - node = this.nodes[nodeId]; - if (node.edges.length == hubsize) { - node.level = 0; + // 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); + + // BarnesHut condition + // original condition : s/d < thetaInverted = passed === d/s > 1/theta = passed + // calcSize = 1/s --> d * 1/s > 1/theta = passed + if (distance * parentBranch.calcSize > this.constants.physics.barnesHut.thetaInverted) { + // duplicate code to reduce function calls to speed up program + if (distance == 0) { + distance = 0.1*Math.random(); + dx = distance; } + var gravityForce = this.constants.physics.barnesHut.gravitationalConstant * parentBranch.mass * node.options.mass / (distance * distance * distance); + var fx = dx * gravityForce; + var fy = dy * gravityForce; + node.fx += fx; + node.fy += fy; } - } - - // branch from hubs - for (nodeId in this.nodes) { - if (this.nodes.hasOwnProperty(nodeId)) { - node = this.nodes[nodeId]; - if (node.level == 0) { - this._setLevel(1,node.edges,node.id); + 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 + // duplicate code to reduce function calls to speed up program + if (distance == 0) { + distance = 0.5*Math.random(); + dx = distance; + } + var gravityForce = this.constants.physics.barnesHut.gravitationalConstant * parentBranch.mass * node.options.mass / (distance * distance * distance); + var fx = dx * gravityForce; + var fy = dy * gravityForce; + node.fx += fx; + node.fy += fy; + } } } } }; - - /** - * this function allocates nodes in levels based on the direction of the edges + * This function constructs the barnesHut tree recursively. It creates the root, splits it and starts placing the nodes. * - * @param hubsize + * @param nodes + * @param nodeIndices * @private */ - exports._determineLevelsDirected = function() { - var nodeId, node, firstNode; - var minLevel = 10000; + exports._formBarnesHutTree = function(nodes,nodeIndices) { + var node; + var nodeCount = nodeIndices.length; - // set first node to source - firstNode = this.nodes[this.nodeIndices[0]]; - firstNode.level = minLevel; - this._setLevelDirected(minLevel,firstNode.edges,firstNode.id); + var minX = Number.MAX_VALUE, + minY = Number.MAX_VALUE, + maxX =-Number.MAX_VALUE, + maxY =-Number.MAX_VALUE; - // get the minimum level - for (nodeId in this.nodes) { - if (this.nodes.hasOwnProperty(nodeId)) { - node = this.nodes[nodeId]; - minLevel = node.level < minLevel ? node.level : minLevel; + // get the range of the nodes + for (var i = 0; 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 - // subtract the minimum from the set so we have a range starting from 0 - for (nodeId in this.nodes) { - if (this.nodes.hasOwnProperty(nodeId)) { - node = this.nodes[nodeId]; - node.level -= minLevel; + + var minimumTreeSize = 1e-5; + var rootSize = Math.max(minimumTreeSize,Math.abs(maxX - minX)); + var halfRootSize = 0.5 * rootSize; + var centerX = 0.5 * (minX + maxX), centerY = 0.5 * (minY + maxY); + + // construct the barnesHutTree + var barnesHutTree = { + root:{ + centerOfMass: {x:0, y:0}, + mass:0, + range: { + minX: centerX-halfRootSize,maxX:centerX+halfRootSize, + minY: centerY-halfRootSize,maxY:centerY+halfRootSize + }, + size: rootSize, + calcSize: 1 / rootSize, + children: { data:null}, + maxWidth: 0, + level: 0, + childrenCount: 4 + } + }; + this._splitBranch(barnesHutTree.root); + + // place the nodes one by one recursively + for (i = 0; i < nodeCount; i++) { + node = nodes[nodeIndices[i]]; + if (node.options.mass > 0) { + this._placeInTree(barnesHutTree.root,node); } } + + // make global + this.barnesHutTree = barnesHutTree }; /** - * Since hierarchical layout does not support: - * - smooth curves (based on the physics), - * - clustering (based on dynamic node counts) - * - * We disable both features so there will be no problems. + * this updates the mass of a branch. this is increased by adding a node. * + * @param parentBranch + * @param node * @private */ - exports._changeConstants = function() { - this.constants.clustering.enabled = false; - this.constants.physics.barnesHut.enabled = false; - this.constants.physics.hierarchicalRepulsion.enabled = true; - this._loadSelectedForceSolver(); - if (this.constants.smoothCurves.enabled == true) { - this.constants.smoothCurves.dynamic = false; - } - this._configureSmoothCurves(); + exports._updateBranchMass = function(parentBranch, node) { + var totalMass = parentBranch.mass + node.options.mass; + var totalMassInv = 1/totalMass; - var config = this.constants.hierarchicalLayout; - config.levelSeparation = Math.abs(config.levelSeparation); - if (config.direction == "RL" || config.direction == "DU") { - config.levelSeparation *= -1; - } + 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; - if (config.direction == "RL" || config.direction == "LR") { - if (this.constants.smoothCurves.enabled == true) { - this.constants.smoothCurves.type = "vertical"; - } - } - else { - if (this.constants.smoothCurves.enabled == true) { - this.constants.smoothCurves.type = "horizontal"; - } - } }; /** - * This is a recursively called function to enumerate the branches from the largest hubs and place the nodes - * on a X position that ensures there will be no overlap. + * determine in which branch the node will be placed. * - * @param edges - * @param parentId - * @param distribution - * @param parentLevel + * @param parentBranch + * @param node + * @param skipMassUpdate * @private */ - exports._placeBranchNodes = function(edges, parentId, distribution, parentLevel) { - for (var i = 0; i < edges.length; i++) { - var childNode = null; - if (edges[i].toId == parentId) { - childNode = edges[i].from; - } - else { - childNode = edges[i].to; - } + exports._placeInTree = function(parentBranch,node,skipMassUpdate) { + if (skipMassUpdate != true || skipMassUpdate === undefined) { + // update the mass of the branch. + this._updateBranchMass(parentBranch,node); + } - // if a node is conneceted to another node on the same level (or higher (means lower level))!, this is not handled here. - var nodeMoved = false; - if (this.constants.hierarchicalLayout.direction == "UD" || this.constants.hierarchicalLayout.direction == "DU") { - if (childNode.xFixed && childNode.level > parentLevel) { - childNode.xFixed = false; - childNode.x = distribution[childNode.level].minPos; - nodeMoved = true; - } + 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 { - if (childNode.yFixed && childNode.level > parentLevel) { - childNode.yFixed = false; - childNode.y = distribution[childNode.level].minPos; - nodeMoved = true; - } + else { // in SW + this._placeInRegion(parentBranch,node,"SW"); } - - if (nodeMoved == true) { - distribution[childNode.level].minPos += distribution[childNode.level].nodeSpacing; - if (childNode.edges.length > 1) { - this._placeBranchNodes(childNode.edges,childNode.id,distribution,childNode.level); - } + } + 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"); } } }; /** - * this function is called recursively to enumerate the barnches of the largest hubs and give each node a level. + * actually place the node in a region (or branch) * - * @param level - * @param edges - * @param parentId + * @param parentBranch + * @param node + * @param region * @private */ - exports._setLevel = function(level, edges, parentId) { - for (var i = 0; i < edges.length; i++) { - var childNode = null; - if (edges[i].toId == parentId) { - childNode = edges[i].from; - } - else { - childNode = edges[i].to; - } - if (childNode.level == -1 || childNode.level > level) { - childNode.level = level; - if (childNode.edges.length > 1) { - this._setLevel(level+1, childNode.edges, childNode.id); + exports._placeInRegion = function(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; } }; /** - * this function is called recursively to enumerate the branched of the first node and give each node a level based on edge direction + * 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 level - * @param edges - * @param parentId + * @param parentBranch * @private */ - exports._setLevelDirected = function(level, edges, parentId) { - this.nodes[parentId].hierarchyEnumerated = true; - var childNode, direction; - for (var i = 0; i < edges.length; i++) { - direction = 1; - if (edges[i].toId == parentId) { - childNode = edges[i].from; - direction = -1; - } - else { - childNode = edges[i].to; - } - if (childNode.level == -1) { - childNode.level = level + direction; - } + exports._splitBranch = function(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"); - for (var i = 0; i < edges.length; i++) { - if (edges[i].toId == parentId) {childNode = edges[i].from;} - else {childNode = edges[i].to;} - - if (childNode.edges.length > 1 && childNode.hierarchyEnumerated === false) { - this._setLevelDirected(childNode.level, childNode.edges, childNode.id); - } + if (containedNode != null) { + this._placeInTree(parentBranch,containedNode); } }; /** - * Unfix nodes + * 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 */ - exports._restoreNodes = function() { - for (var nodeId in this.nodes) { - if (this.nodes.hasOwnProperty(nodeId)) { - this.nodes[nodeId].xFixed = false; - this.nodes[nodeId].yFixed = false; - } + exports._insertRegion = function(parentBranch, region) { + var minX,maxX,minY,maxY; + 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; } - }; - - -/***/ }, -/* 70 */ -/***/ function(module, exports, __webpack_require__) { - // English - exports['en'] = { - edit: 'Edit', - del: 'Delete selected', - back: 'Back', - addNode: 'Add Node', - addEdge: 'Add Edge', - editNode: 'Edit Node', - editEdge: 'Edit Edge', - addDescription: 'Click in an empty space to place a new node.', - edgeDescription: 'Click on a node and drag the edge to another node to connect them.', - editEdgeDescription: 'Click on the control points and drag them to a node to connect to it.', - createEdgeError: 'Cannot link edges to a cluster.', - deleteClusterError: 'Clusters cannot be deleted.' - }; - exports['en_EN'] = exports['en']; - exports['en_US'] = exports['en']; - // Dutch - exports['nl'] = { - edit: 'Wijzigen', - del: 'Selectie verwijderen', - back: 'Terug', - addNode: 'Node toevoegen', - addEdge: 'Link toevoegen', - editNode: 'Node wijzigen', - editEdge: 'Link wijzigen', - addDescription: 'Klik op een leeg gebied om een nieuwe node te maken.', - edgeDescription: 'Klik op een node en sleep de link naar een andere node om ze te verbinden.', - editEdgeDescription: 'Klik op de verbindingspunten en sleep ze naar een node om daarmee te verbinden.', - createEdgeError: 'Kan geen link maken naar een cluster.', - deleteClusterError: 'Clusters kunnen niet worden verwijderd.' + 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 + }; }; - exports['nl_NL'] = exports['nl']; - exports['nl_BE'] = exports['nl']; - -/***/ }, -/* 71 */ -/***/ function(module, exports, __webpack_require__) { /** - * Canvas shapes used by Network + * This function is for debugging purposed, it draws the tree. + * + * @param ctx + * @param color + * @private */ - if (typeof CanvasRenderingContext2D !== 'undefined') { - - /** - * Draw a circle shape - */ - CanvasRenderingContext2D.prototype.circle = function(x, y, r) { - this.beginPath(); - this.arc(x, y, r, 0, 2*Math.PI, false); - }; - - /** - * Draw a square shape - * @param {Number} x horizontal center - * @param {Number} y vertical center - * @param {Number} r size, width and height of the square - */ - CanvasRenderingContext2D.prototype.square = function(x, y, r) { - this.beginPath(); - this.rect(x - r, y - r, r * 2, r * 2); - }; - - /** - * Draw a triangle shape - * @param {Number} x horizontal center - * @param {Number} y vertical center - * @param {Number} r radius, half the length of the sides of the triangle - */ - CanvasRenderingContext2D.prototype.triangle = function(x, y, r) { - // http://en.wikipedia.org/wiki/Equilateral_triangle - this.beginPath(); - - var s = r * 2; - var s2 = s / 2; - var ir = Math.sqrt(3) / 6 * s; // radius of inner circle - var h = Math.sqrt(s * s - s2 * s2); // height - - this.moveTo(x, y - (h - ir)); - this.lineTo(x + s2, y + ir); - this.lineTo(x - s2, y + ir); - this.lineTo(x, y - (h - ir)); - this.closePath(); - }; - - /** - * Draw a triangle shape in downward orientation - * @param {Number} x horizontal center - * @param {Number} y vertical center - * @param {Number} r radius - */ - CanvasRenderingContext2D.prototype.triangleDown = function(x, y, r) { - // http://en.wikipedia.org/wiki/Equilateral_triangle - this.beginPath(); - - var s = r * 2; - var s2 = s / 2; - var ir = Math.sqrt(3) / 6 * s; // radius of inner circle - var h = Math.sqrt(s * s - s2 * s2); // height - - this.moveTo(x, y + (h - ir)); - this.lineTo(x + s2, y - ir); - this.lineTo(x - s2, y - ir); - this.lineTo(x, y + (h - ir)); - this.closePath(); - }; - - /** - * Draw a star shape, a star with 5 points - * @param {Number} x horizontal center - * @param {Number} y vertical center - * @param {Number} r radius, half the length of the sides of the triangle - */ - CanvasRenderingContext2D.prototype.star = function(x, y, r) { - // http://www.html5canvastutorials.com/labs/html5-canvas-star-spinner/ - this.beginPath(); - - for (var n = 0; n < 10; n++) { - var radius = (n % 2 === 0) ? r * 1.3 : r * 0.5; - this.lineTo( - x + radius * Math.sin(n * 2 * Math.PI / 10), - y - radius * Math.cos(n * 2 * Math.PI / 10) - ); - } - - this.closePath(); - }; - - /** - * http://stackoverflow.com/questions/1255512/how-to-draw-a-rounded-rectangle-on-html-canvas - */ - CanvasRenderingContext2D.prototype.roundRect = function(x, y, w, h, r) { - var r2d = Math.PI/180; - if( w - ( 2 * r ) < 0 ) { r = ( w / 2 ); } //ensure that the radius isn't too large for x - if( h - ( 2 * r ) < 0 ) { r = ( h / 2 ); } //ensure that the radius isn't too large for y - this.beginPath(); - this.moveTo(x+r,y); - this.lineTo(x+w-r,y); - this.arc(x+w-r,y+r,r,r2d*270,r2d*360,false); - this.lineTo(x+w,y+h-r); - this.arc(x+w-r,y+h-r,r,0,r2d*90,false); - this.lineTo(x+r,y+h); - this.arc(x+r,y+h-r,r,r2d*90,r2d*180,false); - this.lineTo(x,y+r); - this.arc(x+r,y+r,r,r2d*180,r2d*270,false); - }; - - /** - * http://stackoverflow.com/questions/2172798/how-to-draw-an-oval-in-html5-canvas - */ - CanvasRenderingContext2D.prototype.ellipse = function(x, y, w, h) { - var kappa = .5522848, - ox = (w / 2) * kappa, // control point offset horizontal - oy = (h / 2) * kappa, // control point offset vertical - xe = x + w, // x-end - ye = y + h, // y-end - xm = x + w / 2, // x-middle - ym = y + h / 2; // y-middle - - this.beginPath(); - this.moveTo(x, ym); - this.bezierCurveTo(x, ym - oy, xm - ox, y, xm, y); - this.bezierCurveTo(xm + ox, y, xe, ym - oy, xe, ym); - this.bezierCurveTo(xe, ym + oy, xm + ox, ye, xm, ye); - this.bezierCurveTo(xm - ox, ye, x, ym + oy, x, ym); - }; - - - - /** - * http://stackoverflow.com/questions/2172798/how-to-draw-an-oval-in-html5-canvas - */ - CanvasRenderingContext2D.prototype.database = function(x, y, w, h) { - var f = 1/3; - var wEllipse = w; - var hEllipse = h * f; + exports._drawTree = function(ctx,color) { + if (this.barnesHutTree !== undefined) { - var kappa = .5522848, - ox = (wEllipse / 2) * kappa, // control point offset horizontal - oy = (hEllipse / 2) * kappa, // control point offset vertical - xe = x + wEllipse, // x-end - ye = y + hEllipse, // y-end - xm = x + wEllipse / 2, // x-middle - ym = y + hEllipse / 2, // y-middle - ymb = y + (h - hEllipse/2), // y-midlle, bottom ellipse - yeb = y + h; // y-end, bottom ellipse + ctx.lineWidth = 1; - this.beginPath(); - this.moveTo(xe, ym); + this._drawBranch(this.barnesHutTree.root,ctx,color); + } + }; - this.bezierCurveTo(xe, ym + oy, xm + ox, ye, xm, ye); - this.bezierCurveTo(xm - ox, ye, x, ym + oy, x, ym); - this.bezierCurveTo(x, ym - oy, xm - ox, y, xm, y); - this.bezierCurveTo(xm + ox, y, xe, ym - oy, xe, ym); + /** + * This function is for debugging purposes. It draws the branches recursively. + * + * @param branch + * @param ctx + * @param color + * @private + */ + exports._drawBranch = function(branch,ctx,color) { + if (color === undefined) { + color = "#FF0000"; + } - this.lineTo(xe, ymb); + 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(); - this.bezierCurveTo(xe, ymb + oy, xm + ox, yeb, xm, yeb); - this.bezierCurveTo(xm - ox, yeb, x, ymb + oy, x, ymb); + ctx.beginPath(); + ctx.moveTo(branch.range.maxX,branch.range.minY); + ctx.lineTo(branch.range.maxX,branch.range.maxY); + ctx.stroke(); - this.lineTo(x, ym); - }; + 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(); - /** - * Draw an arrow point (no line) + /* + if (branch.mass > 0) { + ctx.circle(branch.centerOfMass.x, branch.centerOfMass.y, 3*branch.mass); + ctx.stroke(); + } */ - CanvasRenderingContext2D.prototype.arrow = function(x, y, angle, length) { - // tail - var xt = x - length * Math.cos(angle); - var yt = y - length * Math.sin(angle); - - // inner tail - // TODO: allow to customize different shapes - var xi = x - length * 0.9 * Math.cos(angle); - var yi = y - length * 0.9 * Math.sin(angle); - - // left - var xl = xt + length / 3 * Math.cos(angle + 0.5 * Math.PI); - var yl = yt + length / 3 * Math.sin(angle + 0.5 * Math.PI); - - // right - var xr = xt + length / 3 * Math.cos(angle - 0.5 * Math.PI); - var yr = yt + length / 3 * Math.sin(angle - 0.5 * Math.PI); + }; - this.beginPath(); - this.moveTo(x, y); - this.lineTo(xl, yl); - this.lineTo(xi, yi); - this.lineTo(xr, yr); - this.closePath(); - }; - /** - * Sets up the dashedLine functionality for drawing - * Original code came from http://stackoverflow.com/questions/4576724/dotted-stroke-in-canvas - * @author David Jordan - * @date 2012-08-08 - */ - CanvasRenderingContext2D.prototype.dashedLine = function(x,y,x2,y2,dashArray){ - if (!dashArray) dashArray=[10,5]; - if (dashLength==0) dashLength = 0.001; // Hack for Safari - var dashCount = dashArray.length; - this.moveTo(x, y); - var dx = (x2-x), dy = (y2-y); - var slope = dy/dx; - var distRemaining = Math.sqrt( dx*dx + dy*dy ); - var dashIndex=0, draw=true; - while (distRemaining>=0.1){ - var dashLength = dashArray[dashIndex++%dashCount]; - if (dashLength > distRemaining) dashLength = distRemaining; - var xStep = Math.sqrt( dashLength*dashLength / (1 + slope*slope) ); - if (dx<0) xStep = -xStep; - x += xStep; - y += slope*xStep; - this[draw ? 'lineTo' : 'moveTo'](x,y); - distRemaining -= dashLength; - draw = !draw; - } - }; +/***/ }, +/* 71 */ +/***/ function(module, exports, __webpack_require__) { - // TODO: add diamond shape + module.exports = function(module) { + if(!module.webpackPolyfill) { + module.deprecate = function() {}; + module.paths = []; + // module.parent = undefined by default + module.children = []; + module.webpackPolyfill = 1; + } + return module; } From b3e7d60bfb4f973ceb373762a51e1b32a30e3de4 Mon Sep 17 00:00:00 2001 From: AlexDM0 Date: Mon, 2 Feb 2015 10:53:39 +0100 Subject: [PATCH 3/7] avoided onHold event during addEdge --- dist/vis.js | 15518 +++++++++++----------- dist/vis.map | 2 +- dist/vis.min.js | 28 +- lib/network/mixins/ManipulationMixin.js | 4 +- 4 files changed, 7778 insertions(+), 7774 deletions(-) diff --git a/dist/vis.js b/dist/vis.js index 76672da9..0adbea51 100644 --- a/dist/vis.js +++ b/dist/vis.js @@ -5,7 +5,7 @@ * A dynamic, browser-based visualization library. * * @version 3.9.2-SNAPSHOT - * @date 2015-01-28 + * @date 2015-02-02 * * @license * Copyright (C) 2011-2014 Almende B.V, http://almende.com @@ -15422,7 +15422,7 @@ return /******/ (function(modules) { // webpackBootstrap var Emitter = __webpack_require__(56); var Hammer = __webpack_require__(45); - var keycharm = __webpack_require__(57); + var keycharm = __webpack_require__(63); var util = __webpack_require__(1); var hammerUtil = __webpack_require__(47); var DataSet = __webpack_require__(3); @@ -21918,7 +21918,7 @@ return /******/ (function(modules) { // webpackBootstrap // first check if moment.js is already loaded in the browser window, if so, // use this instance. Else, load via commonjs. - module.exports = (typeof window !== 'undefined') && window['moment'] || __webpack_require__(59); + module.exports = (typeof window !== 'undefined') && window['moment'] || __webpack_require__(65); /***/ }, @@ -21928,7 +21928,7 @@ return /******/ (function(modules) { // webpackBootstrap // Only load hammer.js when in a browser environment // (loading hammer.js in a node.js environment gives errors) if (typeof window !== 'undefined') { - module.exports = window['Hammer'] || __webpack_require__(58); + module.exports = window['Hammer'] || __webpack_require__(64); } else { module.exports = function () { @@ -23653,12 +23653,12 @@ return /******/ (function(modules) { // webpackBootstrap /***/ function(module, exports, __webpack_require__) { var PhysicsMixin = __webpack_require__(66); - var ClusterMixin = __webpack_require__(60); - var SectorsMixin = __webpack_require__(61); - var SelectionMixin = __webpack_require__(62); - var ManipulationMixin = __webpack_require__(63); - var NavigationMixin = __webpack_require__(64); - var HierarchicalLayoutMixin = __webpack_require__(65); + var ClusterMixin = __webpack_require__(57); + var SectorsMixin = __webpack_require__(58); + var SelectionMixin = __webpack_require__(59); + var ManipulationMixin = __webpack_require__(60); + var NavigationMixin = __webpack_require__(61); + var HierarchicalLayoutMixin = __webpack_require__(62); /** * Load a mixin into the network object @@ -23856,7 +23856,7 @@ return /******/ (function(modules) { // webpackBootstrap /* 55 */ /***/ function(module, exports, __webpack_require__) { - var keycharm = __webpack_require__(57); + var keycharm = __webpack_require__(63); var Emitter = __webpack_require__(56); var Hammer = __webpack_require__(45); var util = __webpack_require__(1); @@ -24183,9114 +24183,9116 @@ return /******/ (function(modules) { // webpackBootstrap /* 57 */ /***/ 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. + * Creation of the ClusterMixin var. + * + * This contains all the functions the Network object can use to employ clustering */ - // 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 () { + /** + * This is only called in the constructor of the network object + * + */ + exports.startWithClustering = function() { + // cluster if the data set is big + this.clusterToFit(this.constants.clustering.initialMaxNodes, true); - function keycharm(options) { - var preventDefault = options && options.preventDefault || false; + // updates the lables after clustering + this.updateLabels(); - var container = options && options.container || window; - var _exportFunctions = {}; - var _bound = {keydown:{}, keyup:{}}; - var _keys = {}; - var i; + // this is called here because if clusterin is disabled, the start and stabilize are called in + // the setData function. + if (this.stabilize) { + this._stabilize(); + } + this.start(); + }; - // 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};} + /** + * This function clusters until the initialMaxNodes has been reached + * + * @param {Number} maxNumberOfNodes + * @param {Boolean} reposition + */ + exports.clusterToFit = function(maxNumberOfNodes, reposition) { + var numberOfNodes = this.nodeIndices.length; - // numpad misc - _keys['num*'] = {code:106, shift: false}; - _keys['num+'] = {code:107, shift: false}; - _keys['num-'] = {code:109, shift: false}; - _keys['num/'] = {code:111, shift: false}; - _keys['num.'] = {code:110, shift: false}; - // arrows - _keys['left'] = {code:37, shift: false}; - _keys['up'] = {code:38, shift: false}; - _keys['right'] = {code:39, shift: false}; - _keys['down'] = {code:40, shift: false}; - // extra keys - _keys['space'] = {code:32, shift: false}; - _keys['enter'] = {code:13, shift: false}; - _keys['shift'] = {code:16, shift: undefined}; - _keys['esc'] = {code:27, shift: false}; - _keys['backspace'] = {code:8, shift: false}; - _keys['tab'] = {code:9, shift: false}; - _keys['ctrl'] = {code:17, shift: false}; - _keys['alt'] = {code:18, shift: false}; - _keys['delete'] = {code:46, shift: false}; - _keys['pageup'] = {code:33, shift: false}; - _keys['pagedown'] = {code:34, shift: false}; - // symbols - _keys['='] = {code:187, shift: false}; - _keys['-'] = {code:189, shift: false}; - _keys[']'] = {code:221, shift: false}; - _keys['['] = {code:219, shift: false}; + var maxLevels = 50; + var level = 0; + // we first cluster the hubs, then we pull in the outliers, repeat + while (numberOfNodes > maxNumberOfNodes && level < maxLevels) { + if (level % 3 == 0) { + this.forceAggregateHubs(true); + this.normalizeClusterLevels(); + } + else { + this.increaseClusterLevel(); // this also includes a cluster normalization + } + numberOfNodes = this.nodeIndices.length; + level += 1; + } - var down = function(event) {handleEvent(event,'keydown');}; - var up = function(event) {handleEvent(event,'keyup');}; + // after the clustering we reposition the nodes to reduce the initial chaos + if (level > 0 && reposition == true) { + this.repositionNodes(); + } + this._updateCalculationNodes(); + }; - // 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); - } - } + /** + * This function can be called to open up a specific cluster. It is only called by + * It will unpack the cluster back one level. + * + * @param node | Node object: cluster to open. + */ + exports.openCluster = function(node) { + var isMovingBeforeClustering = this.moving; + if (node.clusterSize > this.constants.clustering.sectorThreshold && this._nodeInActiveArea(node) && + !(this._sector() == "default" && this.nodeIndices.length == 1)) { + // this loads a new sector, loads the nodes and edges and nodeIndices of it. + this._addSector(node); + var level = 0; - if (preventDefault == true) { - event.preventDefault(); - } - } - }; + // we decluster until we reach a decent number of nodes + while ((this.nodeIndices.length < this.constants.clustering.initialMaxNodes) && (level < 10)) { + this.decreaseClusterLevel(); + level += 1; + } - // 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}); - }; + } + else { + this._expandClusterNode(node,false,true); + // update the index list, dynamic edges and labels + this._updateNodeIndexList(); + this._updateDynamicEdges(); + this._updateCalculationNodes(); + this.updateLabels(); + } - // 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); - } - } - }; + // if the simulation was settled, we restart the simulation if a cluster has been formed or expanded + if (this.moving != isMovingBeforeClustering) { + this.start(); + } + }; - // get the key label from an event - _exportFunctions.getKey = function(event) { - for (var key in _keys) { - if (_keys.hasOwnProperty(key)) { - if (event.shiftKey == true && _keys[key].shift == true && event.keyCode == _keys[key].code) { - return key; - } - else if (event.shiftKey == false && _keys[key].shift == false && event.keyCode == _keys[key].code) { - return key; - } - else if (event.keyCode == _keys[key].code && key == 'shift') { - return key; - } - } - } - return "unknown key, currently not supported"; - }; - // unbind either a specific callback from a key or all of them (by leaving callback undefined) - _exportFunctions.unbind = function(key, callback, type) { - if (type === undefined) { - type = 'keydown'; - } - if (_keys[key] === undefined) { - throw new Error("unsupported key: " + key); - } - if (callback !== undefined) { - var newBindings = []; - var bound = _bound[type][_keys[key].code]; - if (bound !== undefined) { - for (var i = 0; i < bound.length; i++) { - if (!(bound[i].fn == callback && bound[i].shift == _keys[key].shift)) { - newBindings.push(_bound[type][_keys[key].code][i]); - } - } - } - _bound[type][_keys[key].code] = newBindings; - } - else { - _bound[type][_keys[key].code] = []; - } - }; + /** + * This calls the updateClustes with default arguments + */ + exports.updateClustersDefault = function() { + if (this.constants.clustering.enabled == true) { + this.updateClusters(0,false,false); + } + }; - // reset all bound variables. - _exportFunctions.reset = function() { - _bound = {keydown:{}, keyup:{}}; - }; - // unbind all listeners and reset all variables. - _exportFunctions.destroy = function() { - _bound = {keydown:{}, keyup:{}}; - container.removeEventListener('keydown', down, true); - container.removeEventListener('keyup', up, true); - }; + /** + * This function can be called to increase the cluster level. This means that the nodes with only one edge connection will + * be clustered with their connected node. This can be repeated as many times as needed. + * This can be called externally (by a keybind for instance) to reduce the complexity of big datasets. + */ + exports.increaseClusterLevel = function() { + this.updateClusters(-1,false,true); + }; - // create listeners. - container.addEventListener('keydown',down,true); - container.addEventListener('keyup',up,true); - // return the public functions. - return _exportFunctions; + /** + * This function can be called to decrease the cluster level. This means that the nodes with only one edge connection will + * be unpacked if they are a cluster. This can be repeated as many times as needed. + * This can be called externally (by a key-bind for instance) to look into clusters without zooming. + */ + exports.decreaseClusterLevel = function() { + this.updateClusters(1,false,true); + }; + + + /** + * This is the main clustering function. It clusters and declusters on zoom or forced + * This function clusters on zoom, it can be called with a predefined zoom direction + * If out, check if we can form clusters, if in, check if we can open clusters. + * This function is only called from _zoom() + * + * @param {Number} zoomDirection | -1 / 0 / +1 for zoomOut / determineByZoom / zoomIn + * @param {Boolean} recursive | enabled or disable recursive calling of the opening of clusters + * @param {Boolean} force | enabled or disable forcing + * @param {Boolean} doNotStart | if true do not call start + * + */ + exports.updateClusters = function(zoomDirection,recursive,force,doNotStart) { + var isMovingBeforeClustering = this.moving; + var amountOfNodes = this.nodeIndices.length; + + // on zoom out collapse the sector if the scale is at the level the sector was made + if (this.previousScale > this.scale && zoomDirection == 0) { + this._collapseSector(); } - return keycharm; - })); + // check if we zoom in or out + if (this.previousScale > this.scale || zoomDirection == -1) { // zoom out + // forming clusters when forced pulls outliers in. When not forced, the edge length of the + // outer nodes determines if it is being clustered + this._formClusters(force); + } + else if (this.previousScale < this.scale || zoomDirection == 1) { // zoom in + if (force == true) { + // _openClusters checks for each node if the formationScale of the cluster is smaller than + // the current scale and if so, declusters. When forced, all clusters are reduced by one step + this._openClusters(recursive,force); + } + else { + // if a cluster takes up a set percentage of the active window + this._openClustersBySize(); + } + } + this._updateNodeIndexList(); + + // if a cluster was NOT formed and the user zoomed out, we try clustering by hubs + if (this.nodeIndices.length == amountOfNodes && (this.previousScale > this.scale || zoomDirection == -1)) { + this._aggregateHubs(force); + this._updateNodeIndexList(); + } + // we now reduce chains. + if (this.previousScale > this.scale || zoomDirection == -1) { // zoom out + this.handleChains(); + this._updateNodeIndexList(); + } + this.previousScale = this.scale; + // rest of the update the index list, dynamic edges and labels + this._updateDynamicEdges(); + this.updateLabels(); -/***/ }, -/* 58 */ -/***/ function(module, exports, __webpack_require__) { + // if a cluster was formed, we increase the clusterSession + if (this.nodeIndices.length < amountOfNodes) { // this means a clustering operation has taken place + this.clusterSession += 1; + // if clusters have been made, we normalize the cluster level + this.normalizeClusterLevels(); + } - var __WEBPACK_AMD_DEFINE_RESULT__;/*! Hammer.JS - v1.1.3 - 2014-05-20 - * http://eightmedia.github.io/hammer.js - * - * Copyright (c) 2014 Jorik Tangelder ; - * Licensed under the MIT license */ + if (doNotStart == false || doNotStart === undefined) { + // if the simulation was settled, we restart the simulation if a cluster has been formed or expanded + if (this.moving != isMovingBeforeClustering) { + this.start(); + } + } - (function(window, undefined) { - 'use strict'; + this._updateCalculationNodes(); + }; /** - * @main - * @module hammer - * - * @class Hammer - * @static + * This function handles the chains. It is called on every updateClusters(). */ + exports.handleChains = function() { + // after clustering we check how many chains there are + var chainPercentage = this._getChainFraction(); + if (chainPercentage > this.constants.clustering.chainThreshold) { + this._reduceAmountOfChains(1 - this.constants.clustering.chainThreshold / chainPercentage) + + } + }; /** - * Hammer, use this to create instances - * ```` - * var hammertime = new Hammer(myElement); - * ```` + * this functions starts clustering by hubs + * The minimum hub threshold is set globally * - * @method Hammer - * @param {HTMLElement} element - * @param {Object} [options={}] - * @return {Hammer.Instance} + * @private */ - var Hammer = function Hammer(element, options) { - return new Hammer.Instance(element, options || {}); + exports._aggregateHubs = function(force) { + this._getHubSize(); + this._formClustersByHub(force,false); }; - /** - * version, as defined in package.json - * the value will be set at each build - * @property VERSION - * @final - * @type {String} - */ - Hammer.VERSION = '1.1.3'; /** - * default settings. - * more settings are defined per gesture at `/gestures`. Each gesture can be disabled/enabled - * by setting it's name (like `swipe`) to false. - * You can set the defaults for all instances by changing this object before creating an instance. - * @example - * ```` - * Hammer.defaults.drag = false; - * Hammer.defaults.behavior.touchAction = 'pan-y'; - * delete Hammer.defaults.behavior.userSelect; - * ```` - * @property defaults - * @type {Object} + * This function is fired by keypress. It forces hubs to form. + * */ - Hammer.defaults = { - /** - * this setting object adds styles and attributes to the element to prevent the browser from doing - * its native behavior. The css properties are auto prefixed for the browsers when needed. - * @property defaults.behavior - * @type {Object} - */ - behavior: { - /** - * Disables text selection to improve the dragging gesture. When the value is `none` it also sets - * `onselectstart=false` for IE on the element. Mainly for desktop browsers. - * @property defaults.behavior.userSelect - * @type {String} - * @default 'none' - */ - userSelect: 'none', - - /** - * Specifies whether and how a given region can be manipulated by the user (for instance, by panning or zooming). - * Used by Chrome 35> and IE10>. By default this makes the element blocking any touch event. - * @property defaults.behavior.touchAction - * @type {String} - * @default: 'pan-y' - */ - touchAction: 'pan-y', + exports.forceAggregateHubs = function(doNotStart) { + var isMovingBeforeClustering = this.moving; + var amountOfNodes = this.nodeIndices.length; - /** - * Disables the default callout shown when you touch and hold a touch target. - * On iOS, when you touch and hold a touch target such as a link, Safari displays - * a callout containing information about the link. This property allows you to disable that callout. - * @property defaults.behavior.touchCallout - * @type {String} - * @default 'none' - */ - touchCallout: 'none', + this._aggregateHubs(true); - /** - * Specifies whether zooming is enabled. Used by IE10> - * @property defaults.behavior.contentZooming - * @type {String} - * @default 'none' - */ - contentZooming: 'none', + // update the index list, dynamic edges and labels + this._updateNodeIndexList(); + this._updateDynamicEdges(); + this.updateLabels(); - /** - * Specifies that an entire element should be draggable instead of its contents. - * Mainly for desktop browsers. - * @property defaults.behavior.userDrag - * @type {String} - * @default 'none' - */ - userDrag: 'none', + // if a cluster was formed, we increase the clusterSession + if (this.nodeIndices.length != amountOfNodes) { + this.clusterSession += 1; + } - /** - * Overrides the highlight color shown when the user taps a link or a JavaScript - * clickable element in Safari on iPhone. This property obeys the alpha value, if specified. - * - * If you don't specify an alpha value, Safari on iPhone applies a default alpha value - * to the color. To disable tap highlighting, set the alpha value to 0 (invisible). - * If you set the alpha value to 1.0 (opaque), the element is not visible when tapped. - * @property defaults.behavior.tapHighlightColor - * @type {String} - * @default 'rgba(0,0,0,0)' - */ - tapHighlightColor: 'rgba(0,0,0,0)' + if (doNotStart == false || doNotStart === undefined) { + // if the simulation was settled, we restart the simulation if a cluster has been formed or expanded + if (this.moving != isMovingBeforeClustering) { + this.start(); } + } }; /** - * hammer document where the base events are added at - * @property DOCUMENT - * @type {HTMLElement} - * @default window.document + * If a cluster takes up more than a set percentage of the screen, open the cluster + * + * @private */ - Hammer.DOCUMENT = document; + exports._openClustersBySize = function() { + for (var nodeId in this.nodes) { + if (this.nodes.hasOwnProperty(nodeId)) { + var node = this.nodes[nodeId]; + if (node.inView() == true) { + if ((node.width*this.scale > this.constants.clustering.screenSizeThreshold * this.frame.canvas.clientWidth) || + (node.height*this.scale > this.constants.clustering.screenSizeThreshold * this.frame.canvas.clientHeight)) { + this.openCluster(node); + } + } + } + } + }; - /** - * detect support for pointer events - * @property HAS_POINTEREVENTS - * @type {Boolean} - */ - Hammer.HAS_POINTEREVENTS = navigator.pointerEnabled || navigator.msPointerEnabled; /** - * detect support for touch events - * @property HAS_TOUCHEVENTS - * @type {Boolean} + * This function loops over all nodes in the nodeIndices list. For each node it checks if it is a cluster and if it + * has to be opened based on the current zoom level. + * + * @private */ - Hammer.HAS_TOUCHEVENTS = ('ontouchstart' in window); + exports._openClusters = function(recursive,force) { + for (var i = 0; i < this.nodeIndices.length; i++) { + var node = this.nodes[this.nodeIndices[i]]; + this._expandClusterNode(node,recursive,force); + this._updateCalculationNodes(); + } + }; /** - * detect mobile browsers - * @property IS_MOBILE - * @type {Boolean} + * This function checks if a node has to be opened. This is done by checking the zoom level. + * If the node contains child nodes, this function is recursively called on the child nodes as well. + * This recursive behaviour is optional and can be set by the recursive argument. + * + * @param {Node} parentNode | to check for cluster and expand + * @param {Boolean} recursive | enabled or disable recursive calling + * @param {Boolean} force | enabled or disable forcing + * @param {Boolean} [openAll] | This will recursively force all nodes in the parent to be released + * @private */ - Hammer.IS_MOBILE = /mobile|tablet|ip(ad|hone|od)|android|silk/i.test(navigator.userAgent); + exports._expandClusterNode = function(parentNode, recursive, force, openAll) { + // first check if node is a cluster + if (parentNode.clusterSize > 1) { + // this means that on a double tap event or a zoom event, the cluster fully unpacks if it is smaller than 20 + if (parentNode.clusterSize < this.constants.clustering.sectorThreshold) { + openAll = true; + } + recursive = openAll ? true : recursive; - /** - * detect if we want to support mouseevents at all - * @property NO_MOUSEEVENTS - * @type {Boolean} - */ - Hammer.NO_MOUSEEVENTS = (Hammer.HAS_TOUCHEVENTS && Hammer.IS_MOBILE) || Hammer.HAS_POINTEREVENTS; + // if the last child has been added on a smaller scale than current scale decluster + if (parentNode.formationScale < this.scale || force == true) { + // we will check if any of the contained child nodes should be removed from the cluster + for (var containedNodeId in parentNode.containedNodes) { + if (parentNode.containedNodes.hasOwnProperty(containedNodeId)) { + var childNode = parentNode.containedNodes[containedNodeId]; - /** - * interval in which Hammer recalculates current velocity/direction/angle in ms - * @property CALCULATE_INTERVAL - * @type {Number} - * @default 25 - */ - Hammer.CALCULATE_INTERVAL = 25; + // force expand will expand the largest cluster size clusters. Since we cluster from outside in, we assume that + // the largest cluster is the one that comes from outside + if (force == true) { + if (childNode.clusterSession == parentNode.clusterSessions[parentNode.clusterSessions.length-1] + || openAll) { + this._expelChildFromParent(parentNode,containedNodeId,recursive,force,openAll); + } + } + else { + if (this._nodeInActiveArea(parentNode)) { + this._expelChildFromParent(parentNode,containedNodeId,recursive,force,openAll); + } + } + } + } + } + } + }; /** - * eventtypes per touchevent (start, move, end) are filled by `Event.determineEventTypes` on `setup` - * the object contains the DOM event names per type (`EVENT_START`, `EVENT_MOVE`, `EVENT_END`) - * @property EVENT_TYPES + * ONLY CALLED FROM _expandClusterNode + * + * This function will expel a child_node from a parent_node. This is to de-cluster the node. This function will remove + * the child node from the parent contained_node object and put it back into the global nodes object. + * The same holds for the edge that was connected to the child node. It is moved back into the global edges object. + * + * @param {Node} parentNode | the parent node + * @param {String} containedNodeId | child_node id as it is contained in the containedNodes object of the parent node + * @param {Boolean} recursive | This will also check if the child needs to be expanded. + * With force and recursive both true, the entire cluster is unpacked + * @param {Boolean} force | This will disregard the zoom level and will expel this child from the parent + * @param {Boolean} openAll | This will recursively force all nodes in the parent to be released * @private - * @writeOnce - * @type {Object} */ - var EVENT_TYPES = {}; + exports._expelChildFromParent = function(parentNode, containedNodeId, recursive, force, openAll) { + var childNode = parentNode.containedNodes[containedNodeId]; + + // if child node has been added on smaller scale than current, kick out + if (childNode.formationScale < this.scale || force == true) { + // unselect all selected items + this._unselectAll(); + + // put the child node back in the global nodes object + this.nodes[containedNodeId] = childNode; + + // release the contained edges from this childNode back into the global edges + this._releaseContainedEdges(parentNode,childNode); + + // reconnect rerouted edges to the childNode + this._connectEdgeBackToChild(parentNode,childNode); + + // validate all edges in dynamicEdges + this._validateEdges(parentNode); + + // undo the changes from the clustering operation on the parent node + parentNode.options.mass -= childNode.options.mass; + parentNode.clusterSize -= childNode.clusterSize; + parentNode.options.fontSize = Math.min(this.constants.clustering.maxFontSize, this.constants.nodes.fontSize + this.constants.clustering.fontSizeMultiplier*(parentNode.clusterSize-1)); + parentNode.dynamicEdgesLength = parentNode.dynamicEdges.length; + + // place the child node near the parent, not at the exact same location to avoid chaos in the system + childNode.x = parentNode.x + parentNode.growthIndicator * (0.5 - Math.random()); + childNode.y = parentNode.y + parentNode.growthIndicator * (0.5 - Math.random()); + + // remove node from the list + delete parentNode.containedNodes[containedNodeId]; + + // check if there are other childs with this clusterSession in the parent. + var othersPresent = false; + for (var childNodeId in parentNode.containedNodes) { + if (parentNode.containedNodes.hasOwnProperty(childNodeId)) { + if (parentNode.containedNodes[childNodeId].clusterSession == childNode.clusterSession) { + othersPresent = true; + break; + } + } + } + // if there are no others, remove the cluster session from the list + if (othersPresent == false) { + parentNode.clusterSessions.pop(); + } + + this._repositionBezierNodes(childNode); + // this._repositionBezierNodes(parentNode); + + // remove the clusterSession from the child node + childNode.clusterSession = 0; + + // recalculate the size of the node on the next time the node is rendered + parentNode.clearSizeCache(); + + // restart the simulation to reorganise all nodes + this.moving = true; + } + + // check if a further expansion step is possible if recursivity is enabled + if (recursive == true) { + this._expandClusterNode(childNode,recursive,force,openAll); + } + }; + /** - * direction strings, for safe comparisons - * @property DIRECTION_DOWN|LEFT|UP|RIGHT - * @final - * @type {String} - * @default 'down' 'left' 'up' 'right' + * position the bezier nodes at the center of the edges + * + * @param node + * @private */ - var DIRECTION_DOWN = Hammer.DIRECTION_DOWN = 'down'; - var DIRECTION_LEFT = Hammer.DIRECTION_LEFT = 'left'; - var DIRECTION_UP = Hammer.DIRECTION_UP = 'up'; - var DIRECTION_RIGHT = Hammer.DIRECTION_RIGHT = 'right'; + exports._repositionBezierNodes = function(node) { + for (var i = 0; i < node.dynamicEdges.length; i++) { + node.dynamicEdges[i].positionBezierNode(); + } + }; + /** - * pointertype strings, for safe comparisons - * @property POINTER_MOUSE|TOUCH|PEN - * @final - * @type {String} - * @default 'mouse' 'touch' 'pen' + * This function checks if any nodes at the end of their trees have edges below a threshold length + * This function is called only from updateClusters() + * forceLevelCollapse ignores the length of the edge and collapses one level + * This means that a node with only one edge will be clustered with its connected node + * + * @private + * @param {Boolean} force */ - var POINTER_MOUSE = Hammer.POINTER_MOUSE = 'mouse'; - var POINTER_TOUCH = Hammer.POINTER_TOUCH = 'touch'; - var POINTER_PEN = Hammer.POINTER_PEN = 'pen'; + exports._formClusters = function(force) { + if (force == false) { + this._formClustersByZoom(); + } + else { + this._forceClustersByZoom(); + } + }; + /** - * eventtypes - * @property EVENT_START|MOVE|END|RELEASE|TOUCH - * @final - * @type {String} - * @default 'start' 'change' 'move' 'end' 'release' 'touch' + * This function handles the clustering by zooming out, this is based on a minimum edge distance + * + * @private */ - var EVENT_START = Hammer.EVENT_START = 'start'; - var EVENT_MOVE = Hammer.EVENT_MOVE = 'move'; - var EVENT_END = Hammer.EVENT_END = 'end'; - var EVENT_RELEASE = Hammer.EVENT_RELEASE = 'release'; - var EVENT_TOUCH = Hammer.EVENT_TOUCH = 'touch'; + exports._formClustersByZoom = function() { + var dx,dy,length, + minLength = this.constants.clustering.clusterEdgeThreshold/this.scale; + + // check if any edges are shorter than minLength and start the clustering + // the clustering favours the node with the larger mass + for (var edgeId in this.edges) { + if (this.edges.hasOwnProperty(edgeId)) { + var edge = this.edges[edgeId]; + if (edge.connected) { + if (edge.toId != edge.fromId) { + dx = (edge.to.x - edge.from.x); + dy = (edge.to.y - edge.from.y); + length = Math.sqrt(dx * dx + dy * dy); + + + if (length < minLength) { + // first check which node is larger + var parentNode = edge.from; + var childNode = edge.to; + if (edge.to.options.mass > edge.from.options.mass) { + parentNode = edge.to; + childNode = edge.from; + } + + if (childNode.dynamicEdgesLength == 1) { + this._addToCluster(parentNode,childNode,false); + } + else if (parentNode.dynamicEdgesLength == 1) { + this._addToCluster(childNode,parentNode,false); + } + } + } + } + } + } + }; /** - * if the window events are set... - * @property READY - * @writeOnce - * @type {Boolean} - * @default false + * This function forces the network to cluster all nodes with only one connecting edge to their + * connected node. + * + * @private */ - Hammer.READY = false; + exports._forceClustersByZoom = function() { + for (var nodeId in this.nodes) { + // another node could have absorbed this child. + if (this.nodes.hasOwnProperty(nodeId)) { + var childNode = this.nodes[nodeId]; + + // the edges can be swallowed by another decrease + if (childNode.dynamicEdgesLength == 1 && childNode.dynamicEdges.length != 0) { + var edge = childNode.dynamicEdges[0]; + var parentNode = (edge.toId == childNode.id) ? this.nodes[edge.fromId] : this.nodes[edge.toId]; + + // group to the largest node + if (childNode.id != parentNode.id) { + if (parentNode.options.mass > childNode.options.mass) { + this._addToCluster(parentNode,childNode,true); + } + else { + this._addToCluster(childNode,parentNode,true); + } + } + } + } + } + }; + /** - * plugins namespace - * @property plugins - * @type {Object} + * To keep the nodes of roughly equal size we normalize the cluster levels. + * This function clusters a node to its smallest connected neighbour. + * + * @param node + * @private */ - Hammer.plugins = Hammer.plugins || {}; + exports._clusterToSmallestNeighbour = function(node) { + var smallestNeighbour = -1; + var smallestNeighbourNode = null; + for (var i = 0; i < node.dynamicEdges.length; i++) { + if (node.dynamicEdges[i] !== undefined) { + var neighbour = null; + if (node.dynamicEdges[i].fromId != node.id) { + neighbour = node.dynamicEdges[i].from; + } + else if (node.dynamicEdges[i].toId != node.id) { + neighbour = node.dynamicEdges[i].to; + } + + + if (neighbour != null && smallestNeighbour > neighbour.clusterSessions.length) { + smallestNeighbour = neighbour.clusterSessions.length; + smallestNeighbourNode = neighbour; + } + } + } + + if (neighbour != null && this.nodes[neighbour.id] !== undefined) { + this._addToCluster(neighbour, node, true); + } + }; + /** - * gestures namespace - * see `/gestures` for the definitions - * @property gestures - * @type {Object} + * This function forms clusters from hubs, it loops over all nodes + * + * @param {Boolean} force | Disregard zoom level + * @param {Boolean} onlyEqual | This only clusters a hub with a specific number of edges + * @private */ - Hammer.gestures = Hammer.gestures || {}; + exports._formClustersByHub = function(force, onlyEqual) { + // we loop over all nodes in the list + for (var nodeId in this.nodes) { + // we check if it is still available since it can be used by the clustering in this loop + if (this.nodes.hasOwnProperty(nodeId)) { + this._formClusterFromHub(this.nodes[nodeId],force,onlyEqual); + } + } + }; /** - * setup events to detect gestures on the document - * this function is called when creating an new instance + * This function forms a cluster from a specific preselected hub node + * + * @param {Node} hubNode | the node we will cluster as a hub + * @param {Boolean} force | Disregard zoom level + * @param {Boolean} onlyEqual | This only clusters a hub with a specific number of edges + * @param {Number} [absorptionSizeOffset] | * @private */ - function setup() { - if(Hammer.READY) { - return; + exports._formClusterFromHub = function(hubNode, force, onlyEqual, absorptionSizeOffset) { + if (absorptionSizeOffset === undefined) { + absorptionSizeOffset = 0; + } + // we decide if the node is a hub + if ((hubNode.dynamicEdgesLength >= this.hubThreshold && onlyEqual == false) || + (hubNode.dynamicEdgesLength == this.hubThreshold && onlyEqual == true)) { + // initialize variables + var dx,dy,length; + var minLength = this.constants.clustering.clusterEdgeThreshold/this.scale; + var allowCluster = false; + + // we create a list of edges because the dynamicEdges change over the course of this loop + var edgesIdarray = []; + var amountOfInitialEdges = hubNode.dynamicEdges.length; + for (var j = 0; j < amountOfInitialEdges; j++) { + edgesIdarray.push(hubNode.dynamicEdges[j].id); } - // find what eventtypes we add listeners to - Event.determineEventTypes(); + // if the hub clustering is not forces, we check if one of the edges connected + // to a cluster is small enough based on the constants.clustering.clusterEdgeThreshold + if (force == false) { + allowCluster = false; + for (j = 0; j < amountOfInitialEdges; j++) { + var edge = this.edges[edgesIdarray[j]]; + if (edge !== undefined) { + if (edge.connected) { + if (edge.toId != edge.fromId) { + dx = (edge.to.x - edge.from.x); + dy = (edge.to.y - edge.from.y); + length = Math.sqrt(dx * dx + dy * dy); - // Register all gestures inside Hammer.gestures - Utils.each(Hammer.gestures, function(gesture) { - Detection.register(gesture); - }); + if (length < minLength) { + allowCluster = true; + break; + } + } + } + } + } + } + + // start the clustering if allowed + if ((!force && allowCluster) || force) { + // we loop over all edges INITIALLY connected to this hub + for (j = 0; j < amountOfInitialEdges; j++) { + edge = this.edges[edgesIdarray[j]]; + // the edge can be clustered by this function in a previous loop + if (edge !== undefined) { + var childNode = this.nodes[(edge.fromId == hubNode.id) ? edge.toId : edge.fromId]; + // we do not want hubs to merge with other hubs nor do we want to cluster itself. + if ((childNode.dynamicEdges.length <= (this.hubThreshold + absorptionSizeOffset)) && + (childNode.id != hubNode.id)) { + this._addToCluster(hubNode,childNode,force); + } + } + } + } + } + }; - // Add touch events on the document - Event.onTouch(Hammer.DOCUMENT, EVENT_MOVE, Detection.detect); - Event.onTouch(Hammer.DOCUMENT, EVENT_END, Detection.detect); - // Hammer is ready...! - Hammer.READY = true; - } /** - * @module hammer + * This function adds the child node to the parent node, creating a cluster if it is not already. * - * @class Utils - * @static + * @param {Node} parentNode | this is the node that will house the child node + * @param {Node} childNode | this node will be deleted from the global this.nodes and stored in the parent node + * @param {Boolean} force | true will only update the remainingEdges at the very end of the clustering, ensuring single level collapse + * @private */ - var Utils = Hammer.utils = { - /** - * extend method, could also be used for cloning when `dest` is an empty object. - * changes the dest object - * @method extend - * @param {Object} dest - * @param {Object} src - * @param {Boolean} [merge=false] do a merge - * @return {Object} dest - */ - extend: function extend(dest, src, merge) { - for(var key in src) { - if(!src.hasOwnProperty(key) || (dest[key] !== undefined && merge)) { - continue; - } - dest[key] = src[key]; - } - return dest; - }, + exports._addToCluster = function(parentNode, childNode, force) { + // join child node in the parent node + parentNode.containedNodes[childNode.id] = childNode; - /** - * simple addEventListener wrapper - * @method on - * @param {HTMLElement} element - * @param {String} type - * @param {Function} handler - */ - on: function on(element, type, handler) { - element.addEventListener(type, handler, false); - }, + // manage all the edges connected to the child and parent nodes + for (var i = 0; i < childNode.dynamicEdges.length; i++) { + var edge = childNode.dynamicEdges[i]; + if (edge.toId == parentNode.id || edge.fromId == parentNode.id) { // edge connected to parentNode + this._addToContainedEdges(parentNode,childNode,edge); + } + else { + this._connectEdgeToCluster(parentNode,childNode,edge); + } + } + // a contained node has no dynamic edges. + childNode.dynamicEdges = []; - /** - * simple removeEventListener wrapper - * @method off - * @param {HTMLElement} element - * @param {String} type - * @param {Function} handler - */ - off: function off(element, type, handler) { - element.removeEventListener(type, handler, false); - }, + // remove circular edges from clusters + this._containCircularEdgesFromNode(parentNode,childNode); - /** - * forEach over arrays and objects - * @method each - * @param {Object|Array} obj - * @param {Function} iterator - * @param {any} iterator.item - * @param {Number} iterator.index - * @param {Object|Array} iterator.obj the source object - * @param {Object} context value to use as `this` in the iterator - */ - each: function each(obj, iterator, context) { - var i, len; - // native forEach on arrays - if('forEach' in obj) { - obj.forEach(iterator, context); - // arrays - } else if(obj.length !== undefined) { - for(i = 0, len = obj.length; i < len; i++) { - if(iterator.call(context, obj[i], i, obj) === false) { - return; - } - } - // objects - } else { - for(i in obj) { - if(obj.hasOwnProperty(i) && - iterator.call(context, obj[i], i, obj) === false) { - return; - } - } - } - }, + // remove the childNode from the global nodes object + delete this.nodes[childNode.id]; - /** - * find if a string contains the string using indexOf - * @method inStr - * @param {String} src - * @param {String} find - * @return {Boolean} found - */ - inStr: function inStr(src, find) { - return src.indexOf(find) > -1; - }, + // update the properties of the child and parent + var massBefore = parentNode.options.mass; + childNode.clusterSession = this.clusterSession; + parentNode.options.mass += childNode.options.mass; + parentNode.clusterSize += childNode.clusterSize; + parentNode.options.fontSize = Math.min(this.constants.clustering.maxFontSize, this.constants.nodes.fontSize + this.constants.clustering.fontSizeMultiplier*parentNode.clusterSize); - /** - * find if a array contains the object using indexOf or a simple polyfill - * @method inArray - * @param {String} src - * @param {String} find - * @return {Boolean|Number} false when not found, or the index - */ - inArray: function inArray(src, find) { - if(src.indexOf) { - var index = src.indexOf(find); - return (index === -1) ? false : index; - } else { - for(var i = 0, len = src.length; i < len; i++) { - if(src[i] === find) { - return i; - } - } - return false; - } - }, + // keep track of the clustersessions so we can open the cluster up as it has been formed. + if (parentNode.clusterSessions[parentNode.clusterSessions.length - 1] != this.clusterSession) { + parentNode.clusterSessions.push(this.clusterSession); + } - /** - * convert an array-like object (`arguments`, `touchlist`) to an array - * @method toArray - * @param {Object} obj - * @return {Array} - */ - toArray: function toArray(obj) { - return Array.prototype.slice.call(obj, 0); - }, + // forced clusters only open from screen size and double tap + if (force == true) { + // parentNode.formationScale = Math.pow(1 - (1.0/11.0),this.clusterSession+3); + parentNode.formationScale = 0; + } + else { + parentNode.formationScale = this.scale; // The latest child has been added on this scale + } - /** - * find if a node is in the given parent - * @method hasParent - * @param {HTMLElement} node - * @param {HTMLElement} parent - * @return {Boolean} found - */ - hasParent: function hasParent(node, parent) { - while(node) { - if(node == parent) { - return true; - } - node = node.parentNode; - } - return false; - }, + // recalculate the size of the node on the next time the node is rendered + parentNode.clearSizeCache(); - /** - * get the center of all the touches - * @method getCenter - * @param {Array} touches - * @return {Object} center contains `pageX`, `pageY`, `clientX` and `clientY` properties - */ - getCenter: function getCenter(touches) { - var pageX = [], - pageY = [], - clientX = [], - clientY = [], - min = Math.min, - max = Math.max; + // set the pop-out scale for the childnode + parentNode.containedNodes[childNode.id].formationScale = parentNode.formationScale; - // no need to loop when only one touch - if(touches.length === 1) { - return { - pageX: touches[0].pageX, - pageY: touches[0].pageY, - clientX: touches[0].clientX, - clientY: touches[0].clientY - }; - } + // nullify the movement velocity of the child, this is to avoid hectic behaviour + childNode.clearVelocity(); - Utils.each(touches, function(touch) { - pageX.push(touch.pageX); - pageY.push(touch.pageY); - clientX.push(touch.clientX); - clientY.push(touch.clientY); - }); + // the mass has altered, preservation of energy dictates the velocity to be updated + parentNode.updateVelocity(massBefore); - return { - pageX: (min.apply(Math, pageX) + max.apply(Math, pageX)) / 2, - pageY: (min.apply(Math, pageY) + max.apply(Math, pageY)) / 2, - clientX: (min.apply(Math, clientX) + max.apply(Math, clientX)) / 2, - clientY: (min.apply(Math, clientY) + max.apply(Math, clientY)) / 2 - }; - }, + // restart the simulation to reorganise all nodes + this.moving = true; + }; - /** - * calculate the velocity between two points. unit is in px per ms. - * @method getVelocity - * @param {Number} deltaTime - * @param {Number} deltaX - * @param {Number} deltaY - * @return {Object} velocity `x` and `y` - */ - getVelocity: function getVelocity(deltaTime, deltaX, deltaY) { - return { - x: Math.abs(deltaX / deltaTime) || 0, - y: Math.abs(deltaY / deltaTime) || 0 - }; - }, - /** - * calculate the angle between two coordinates - * @method getAngle - * @param {Touch} touch1 - * @param {Touch} touch2 - * @return {Number} angle - */ - getAngle: function getAngle(touch1, touch2) { - var x = touch2.clientX - touch1.clientX, - y = touch2.clientY - touch1.clientY; + /** + * This function will apply the changes made to the remainingEdges during the formation of the clusters. + * This is a seperate function to allow for level-wise collapsing of the node barnesHutTree. + * It has to be called if a level is collapsed. It is called by _formClusters(). + * @private + */ + exports._updateDynamicEdges = function() { + for (var i = 0; i < this.nodeIndices.length; i++) { + var node = this.nodes[this.nodeIndices[i]]; + node.dynamicEdgesLength = node.dynamicEdges.length; - return Math.atan2(y, x) * 180 / Math.PI; - }, + // this corrects for multiple edges pointing at the same other node + var correction = 0; + if (node.dynamicEdgesLength > 1) { + for (var j = 0; j < node.dynamicEdgesLength - 1; j++) { + var edgeToId = node.dynamicEdges[j].toId; + var edgeFromId = node.dynamicEdges[j].fromId; + for (var k = j+1; k < node.dynamicEdgesLength; k++) { + if ((node.dynamicEdges[k].toId == edgeToId && node.dynamicEdges[k].fromId == edgeFromId) || + (node.dynamicEdges[k].fromId == edgeToId && node.dynamicEdges[k].toId == edgeFromId)) { + correction += 1; + } + } + } + } + node.dynamicEdgesLength -= correction; + } + }; - /** - * do a small comparision to get the direction between two touches. - * @method getDirection - * @param {Touch} touch1 - * @param {Touch} touch2 - * @return {String} direction matches `DIRECTION_LEFT|RIGHT|UP|DOWN` - */ - getDirection: function getDirection(touch1, touch2) { - var x = Math.abs(touch1.clientX - touch2.clientX), - y = Math.abs(touch1.clientY - touch2.clientY); - if(x >= y) { - return touch1.clientX - touch2.clientX > 0 ? DIRECTION_LEFT : DIRECTION_RIGHT; - } - return touch1.clientY - touch2.clientY > 0 ? DIRECTION_UP : DIRECTION_DOWN; - }, + /** + * This adds an edge from the childNode to the contained edges of the parent node + * + * @param parentNode | Node object + * @param childNode | Node object + * @param edge | Edge object + * @private + */ + exports._addToContainedEdges = function(parentNode, childNode, edge) { + // create an array object if it does not yet exist for this childNode + if (!(parentNode.containedEdges.hasOwnProperty(childNode.id))) { + parentNode.containedEdges[childNode.id] = [] + } + // add this edge to the list + parentNode.containedEdges[childNode.id].push(edge); - /** - * calculate the distance between two touches - * @method getDistance - * @param {Touch}touch1 - * @param {Touch} touch2 - * @return {Number} distance - */ - getDistance: function getDistance(touch1, touch2) { - var x = touch2.clientX - touch1.clientX, - y = touch2.clientY - touch1.clientY; + // remove the edge from the global edges object + delete this.edges[edge.id]; - return Math.sqrt((x * x) + (y * y)); - }, + // remove the edge from the parent object + for (var i = 0; i < parentNode.dynamicEdges.length; i++) { + if (parentNode.dynamicEdges[i].id == edge.id) { + parentNode.dynamicEdges.splice(i,1); + break; + } + } + }; - /** - * calculate the scale factor between two touchLists - * no scale is 1, and goes down to 0 when pinched together, and bigger when pinched out - * @method getScale - * @param {Array} start array of touches - * @param {Array} end array of touches - * @return {Number} scale - */ - getScale: function getScale(start, end) { - // need two fingers... - if(start.length >= 2 && end.length >= 2) { - return this.getDistance(end[0], end[1]) / this.getDistance(start[0], start[1]); - } - return 1; - }, + /** + * This function connects an edge that was connected to a child node to the parent node. + * It keeps track of which nodes it has been connected to with the originalId array. + * + * @param {Node} parentNode | Node object + * @param {Node} childNode | Node object + * @param {Edge} edge | Edge object + * @private + */ + exports._connectEdgeToCluster = function(parentNode, childNode, edge) { + // handle circular edges + if (edge.toId == edge.fromId) { + this._addToContainedEdges(parentNode, childNode, edge); + } + else { + if (edge.toId == childNode.id) { // edge connected to other node on the "to" side + edge.originalToId.push(childNode.id); + edge.to = parentNode; + edge.toId = parentNode.id; + } + else { // edge connected to other node with the "from" side - /** - * calculate the rotation degrees between two touchLists - * @method getRotation - * @param {Array} start array of touches - * @param {Array} end array of touches - * @return {Number} rotation - */ - getRotation: function getRotation(start, end) { - // need two fingers - if(start.length >= 2 && end.length >= 2) { - return this.getAngle(end[1], end[0]) - this.getAngle(start[1], start[0]); - } - return 0; - }, + edge.originalFromId.push(childNode.id); + edge.from = parentNode; + edge.fromId = parentNode.id; + } - /** - * find out if the direction is vertical * - * @method isVertical - * @param {String} direction matches `DIRECTION_UP|DOWN` - * @return {Boolean} is_vertical - */ - isVertical: function isVertical(direction) { - return direction == DIRECTION_UP || direction == DIRECTION_DOWN; - }, + this._addToReroutedEdges(parentNode,childNode,edge); + } + }; - /** - * set css properties with their prefixes - * @param {HTMLElement} element - * @param {String} prop - * @param {String} value - * @param {Boolean} [toggle=true] - * @return {Boolean} - */ - setPrefixedCss: function setPrefixedCss(element, prop, value, toggle) { - var prefixes = ['', 'Webkit', 'Moz', 'O', 'ms']; - prop = Utils.toCamelCase(prop); - for(var i = 0; i < prefixes.length; i++) { - var p = prop; - // prefixes - if(prefixes[i]) { - p = prefixes[i] + p.slice(0, 1).toUpperCase() + p.slice(1); - } + /** + * If a node is connected to itself, a circular edge is drawn. When clustering we want to contain + * these edges inside of the cluster. + * + * @param parentNode + * @param childNode + * @private + */ + exports._containCircularEdgesFromNode = function(parentNode, childNode) { + // manage all the edges connected to the child and parent nodes + for (var i = 0; i < parentNode.dynamicEdges.length; i++) { + var edge = parentNode.dynamicEdges[i]; + // handle circular edges + if (edge.toId == edge.fromId) { + this._addToContainedEdges(parentNode, childNode, edge); + } + } + }; - // test the style - if(p in element.style) { - element.style[p] = (toggle == null || toggle) && value || ''; - break; - } - } - }, - /** - * toggle browser default behavior by setting css properties. - * `userSelect='none'` also sets `element.onselectstart` to false - * `userDrag='none'` also sets `element.ondragstart` to false - * - * @method toggleBehavior - * @param {HtmlElement} element - * @param {Object} props - * @param {Boolean} [toggle=true] - */ - toggleBehavior: function toggleBehavior(element, props, toggle) { - if(!props || !element || !element.style) { - return; - } + /** + * This adds an edge from the childNode to the rerouted edges of the parent node + * + * @param parentNode | Node object + * @param childNode | Node object + * @param edge | Edge object + * @private + */ + exports._addToReroutedEdges = function(parentNode, childNode, edge) { + // create an array object if it does not yet exist for this childNode + // we store the edge in the rerouted edges so we can restore it when the cluster pops open + if (!(parentNode.reroutedEdges.hasOwnProperty(childNode.id))) { + parentNode.reroutedEdges[childNode.id] = []; + } + parentNode.reroutedEdges[childNode.id].push(edge); - // set the css properties - Utils.each(props, function(value, prop) { - Utils.setPrefixedCss(element, prop, value, toggle); - }); + // this edge becomes part of the dynamicEdges of the cluster node + parentNode.dynamicEdges.push(edge); + }; - var falseFn = toggle && function() { - return false; - }; - // also the disable onselectstart - if(props.userSelect == 'none') { - element.onselectstart = falseFn; - } - // and disable ondragstart - if(props.userDrag == 'none') { - element.ondragstart = falseFn; - } - }, - /** - * convert a string with underscores to camelCase - * so prevent_default becomes preventDefault - * @param {String} str - * @return {String} camelCaseStr - */ - toCamelCase: function toCamelCase(str) { - return str.replace(/[_-]([a-z])/g, function(s) { - return s[1].toUpperCase(); - }); + /** + * This function connects an edge that was connected to a cluster node back to the child node. + * + * @param parentNode | Node object + * @param childNode | Node object + * @private + */ + exports._connectEdgeBackToChild = function(parentNode, childNode) { + if (parentNode.reroutedEdges.hasOwnProperty(childNode.id)) { + for (var i = 0; i < parentNode.reroutedEdges[childNode.id].length; i++) { + var edge = parentNode.reroutedEdges[childNode.id][i]; + if (edge.originalFromId[edge.originalFromId.length-1] == childNode.id) { + edge.originalFromId.pop(); + edge.fromId = childNode.id; + edge.from = childNode; + } + else { + edge.originalToId.pop(); + edge.toId = childNode.id; + edge.to = childNode; + } + + // append this edge to the list of edges connecting to the childnode + childNode.dynamicEdges.push(edge); + + // remove the edge from the parent object + for (var j = 0; j < parentNode.dynamicEdges.length; j++) { + if (parentNode.dynamicEdges[j].id == edge.id) { + parentNode.dynamicEdges.splice(j,1); + break; + } + } } + // remove the entry from the rerouted edges + delete parentNode.reroutedEdges[childNode.id]; + } }; /** - * @module hammer + * When loops are clustered, an edge can be both in the rerouted array and the contained array. + * This function is called last to verify that all edges in dynamicEdges are in fact connected to the + * parentNode + * + * @param parentNode | Node object + * @private */ + exports._validateEdges = function(parentNode) { + for (var i = 0; i < parentNode.dynamicEdges.length; i++) { + var edge = parentNode.dynamicEdges[i]; + if (parentNode.id != edge.toId && parentNode.id != edge.fromId) { + parentNode.dynamicEdges.splice(i,1); + } + } + }; + + /** - * @class Event - * @static + * This function released the contained edges back into the global domain and puts them back into the + * dynamic edges of both parent and child. + * + * @param {Node} parentNode | + * @param {Node} childNode | + * @private */ - var Event = Hammer.event = { - /** - * when touch events have been fired, this is true - * this is used to stop mouse events - * @property prevent_mouseevents - * @private - * @type {Boolean} - */ - preventMouseEvents: false, + exports._releaseContainedEdges = function(parentNode, childNode) { + for (var i = 0; i < parentNode.containedEdges[childNode.id].length; i++) { + var edge = parentNode.containedEdges[childNode.id][i]; - /** - * if EVENT_START has been fired - * @property started - * @private - * @type {Boolean} - */ - started: false, + // put the edge back in the global edges object + this.edges[edge.id] = edge; - /** - * when the mouse is hold down, this is true - * @property should_detect - * @private - * @type {Boolean} - */ - shouldDetect: false, + // put the edge back in the dynamic edges of the child and parent + childNode.dynamicEdges.push(edge); + parentNode.dynamicEdges.push(edge); + } + // remove the entry from the contained edges + delete parentNode.containedEdges[childNode.id]; - /** - * simple event binder with a hook and support for multiple types - * @method on - * @param {HTMLElement} element - * @param {String} type - * @param {Function} handler - * @param {Function} [hook] - * @param {Object} hook.type - */ - on: function on(element, type, handler, hook) { - var types = type.split(' '); - Utils.each(types, function(type) { - Utils.on(element, type, handler); - hook && hook(type); - }); - }, + }; - /** - * simple event unbinder with a hook and support for multiple types - * @method off - * @param {HTMLElement} element - * @param {String} type - * @param {Function} handler - * @param {Function} [hook] - * @param {Object} hook.type - */ - off: function off(element, type, handler, hook) { - var types = type.split(' '); - Utils.each(types, function(type) { - Utils.off(element, type, handler); - hook && hook(type); - }); - }, - - /** - * the core touch event handler. - * this finds out if we should to detect gestures - * @method onTouch - * @param {HTMLElement} element - * @param {String} eventType matches `EVENT_START|MOVE|END` - * @param {Function} handler - * @return onTouchHandler {Function} the core event handler - */ - onTouch: function onTouch(element, eventType, handler) { - var self = this; - - var onTouchHandler = function onTouchHandler(ev) { - var srcType = ev.type.toLowerCase(), - isPointer = Hammer.HAS_POINTEREVENTS, - isMouse = Utils.inStr(srcType, 'mouse'), - triggerType; - - // if we are in a mouseevent, but there has been a touchevent triggered in this session - // we want to do nothing. simply break out of the event. - if(isMouse && self.preventMouseEvents) { - return; - - // mousebutton must be down - } else if(isMouse && eventType == EVENT_START && ev.button === 0) { - self.preventMouseEvents = false; - self.shouldDetect = true; - } else if(isPointer && eventType == EVENT_START) { - self.shouldDetect = (ev.buttons === 1 || PointerEvent.matchType(POINTER_TOUCH, ev)); - // just a valid start event, but no mouse - } else if(!isMouse && eventType == EVENT_START) { - self.preventMouseEvents = true; - self.shouldDetect = true; - } - - // update the pointer event before entering the detection - if(isPointer && eventType != EVENT_END) { - PointerEvent.updatePointer(eventType, ev); - } - - // we are in a touch/down state, so allowed detection of gestures - if(self.shouldDetect) { - triggerType = self.doDetect.call(self, ev, eventType, element, handler); - } - // ...and we are done with the detection - // so reset everything to start each detection totally fresh - if(triggerType == EVENT_END) { - self.preventMouseEvents = false; - self.shouldDetect = false; - PointerEvent.reset(); - // update the pointerevent object after the detection - } - if(isPointer && eventType == EVENT_END) { - PointerEvent.updatePointer(eventType, ev); - } - }; - this.on(element, EVENT_TYPES[eventType], onTouchHandler); - return onTouchHandler; - }, + // ------------------- UTILITY FUNCTIONS ---------------------------- // - /** - * the core detection method - * this finds out what hammer-touch-events to trigger - * @method doDetect - * @param {Object} ev - * @param {String} eventType matches `EVENT_START|MOVE|END` - * @param {HTMLElement} element - * @param {Function} handler - * @return {String} triggerType matches `EVENT_START|MOVE|END` - */ - doDetect: function doDetect(ev, eventType, element, handler) { - var touchList = this.getTouchList(ev, eventType); - var touchListLength = touchList.length; - var triggerType = eventType; - var triggerChange = touchList.trigger; // used by fakeMultitouch plugin - var changedLength = touchListLength; - // at each touchstart-like event we want also want to trigger a TOUCH event... - if(eventType == EVENT_START) { - triggerChange = EVENT_TOUCH; - // ...the same for a touchend-like event - } else if(eventType == EVENT_END) { - triggerChange = EVENT_RELEASE; + /** + * This updates the node labels for all nodes (for debugging purposes) + */ + exports.updateLabels = function() { + var nodeId; + // update node labels + for (nodeId in this.nodes) { + if (this.nodes.hasOwnProperty(nodeId)) { + var node = this.nodes[nodeId]; + if (node.clusterSize > 1) { + node.label = "[".concat(String(node.clusterSize),"]"); + } + } + } - // keep track of how many touches have been removed - changedLength = touchList.length - ((ev.changedTouches) ? ev.changedTouches.length : 1); + // update node labels + for (nodeId in this.nodes) { + if (this.nodes.hasOwnProperty(nodeId)) { + node = this.nodes[nodeId]; + if (node.clusterSize == 1) { + if (node.originalLabel !== undefined) { + node.label = node.originalLabel; } - - // after there are still touches on the screen, - // we just want to trigger a MOVE event. so change the START or END to a MOVE - // but only after detection has been started, the first time we actualy want a START - if(changedLength > 0 && this.started) { - triggerType = EVENT_MOVE; + else { + node.label = String(node.id); } + } + } + } - // detection has been started, we keep track of this, see above - this.started = true; + // /* Debug Override */ + // for (nodeId in this.nodes) { + // if (this.nodes.hasOwnProperty(nodeId)) { + // node = this.nodes[nodeId]; + // node.label = String(node.level); + // } + // } - // generate some event data, some basic information - var evData = this.collectEventData(element, triggerType, touchList, ev); + }; - // trigger the triggerType event before the change (TOUCH, RELEASE) events - // but the END event should be at last - if(eventType != EVENT_END) { - handler.call(Detection, evData); - } - // trigger a change (TOUCH, RELEASE) event, this means the length of the touches changed - if(triggerChange) { - evData.changedLength = changedLength; - evData.eventType = triggerChange; + /** + * We want to keep the cluster level distribution rather small. This means we do not want unclustered nodes + * if the rest of the nodes are already a few cluster levels in. + * To fix this we use this function. It determines the min and max cluster level and sends nodes that have not + * clustered enough to the clusterToSmallestNeighbours function. + */ + exports.normalizeClusterLevels = function() { + var maxLevel = 0; + var minLevel = 1e9; + var clusterLevel = 0; + var nodeId; - handler.call(Detection, evData); + // we loop over all nodes in the list + for (nodeId in this.nodes) { + if (this.nodes.hasOwnProperty(nodeId)) { + clusterLevel = this.nodes[nodeId].clusterSessions.length; + if (maxLevel < clusterLevel) {maxLevel = clusterLevel;} + if (minLevel > clusterLevel) {minLevel = clusterLevel;} + } + } - evData.eventType = triggerType; - delete evData.changedLength; + if (maxLevel - minLevel > this.constants.clustering.clusterLevelDifference) { + var amountOfNodes = this.nodeIndices.length; + var targetLevel = maxLevel - this.constants.clustering.clusterLevelDifference; + // we loop over all nodes in the list + for (nodeId in this.nodes) { + if (this.nodes.hasOwnProperty(nodeId)) { + if (this.nodes[nodeId].clusterSessions.length < targetLevel) { + this._clusterToSmallestNeighbour(this.nodes[nodeId]); } + } + } + this._updateNodeIndexList(); + this._updateDynamicEdges(); + // if a cluster was formed, we increase the clusterSession + if (this.nodeIndices.length != amountOfNodes) { + this.clusterSession += 1; + } + } + }; - // trigger the END event - if(triggerType == EVENT_END) { - handler.call(Detection, evData); - // ...and we are done with the detection - // so reset everything to start each detection totally fresh - this.started = false; - } - return triggerType; - }, + /** + * This function determines if the cluster we want to decluster is in the active area + * this means around the zoom center + * + * @param {Node} node + * @returns {boolean} + * @private + */ + exports._nodeInActiveArea = function(node) { + return ( + Math.abs(node.x - this.areaCenter.x) <= this.constants.clustering.activeAreaBoxSize/this.scale + && + Math.abs(node.y - this.areaCenter.y) <= this.constants.clustering.activeAreaBoxSize/this.scale + ) + }; - /** - * we have different events for each device/browser - * determine what we need and set them in the EVENT_TYPES constant - * the `onTouch` method is bind to these properties. - * @method determineEventTypes - * @return {Object} events - */ - determineEventTypes: function determineEventTypes() { - var types; - if(Hammer.HAS_POINTEREVENTS) { - if(window.PointerEvent) { - types = [ - 'pointerdown', - 'pointermove', - 'pointerup pointercancel lostpointercapture' - ]; - } else { - types = [ - 'MSPointerDown', - 'MSPointerMove', - 'MSPointerUp MSPointerCancel MSLostPointerCapture' - ]; - } - } else if(Hammer.NO_MOUSEEVENTS) { - types = [ - 'touchstart', - 'touchmove', - 'touchend touchcancel' - ]; - } else { - types = [ - 'touchstart mousedown', - 'touchmove mousemove', - 'touchend touchcancel mouseup' - ]; - } - EVENT_TYPES[EVENT_START] = types[0]; - EVENT_TYPES[EVENT_MOVE] = types[1]; - EVENT_TYPES[EVENT_END] = types[2]; - return EVENT_TYPES; - }, + /** + * This is an adaptation of the original repositioning function. This is called if the system is clustered initially + * It puts large clusters away from the center and randomizes the order. + * + */ + exports.repositionNodes = function() { + for (var i = 0; i < this.nodeIndices.length; i++) { + var node = this.nodes[this.nodeIndices[i]]; + if ((node.xFixed == false || node.yFixed == false)) { + var radius = 10 * 0.1*this.nodeIndices.length * Math.min(100,node.options.mass); + var angle = 2 * Math.PI * Math.random(); + if (node.xFixed == false) {node.x = radius * Math.cos(angle);} + if (node.yFixed == false) {node.y = radius * Math.sin(angle);} + this._repositionBezierNodes(node); + } + } + }; - /** - * create touchList depending on the event - * @method getTouchList - * @param {Object} ev - * @param {String} eventType - * @return {Array} touches - */ - getTouchList: function getTouchList(ev, eventType) { - // get the fake pointerEvent touchlist - if(Hammer.HAS_POINTEREVENTS) { - return PointerEvent.getTouchList(); - } - // get the touchlist - if(ev.touches) { - if(eventType == EVENT_MOVE) { - return ev.touches; - } + /** + * We determine how many connections denote an important hub. + * We take the mean + 2*std as the important hub size. (Assuming a normal distribution of data, ~2.2%) + * + * @private + */ + exports._getHubSize = function() { + var average = 0; + var averageSquared = 0; + var hubCounter = 0; + var largestHub = 0; - var identifiers = []; - var concat = [].concat(Utils.toArray(ev.touches), Utils.toArray(ev.changedTouches)); - var touchList = []; + for (var i = 0; i < this.nodeIndices.length; i++) { - Utils.each(concat, function(touch) { - if(Utils.inArray(identifiers, touch.identifier) === false) { - touchList.push(touch); - } - identifiers.push(touch.identifier); - }); + var node = this.nodes[this.nodeIndices[i]]; + if (node.dynamicEdgesLength > largestHub) { + largestHub = node.dynamicEdgesLength; + } + average += node.dynamicEdgesLength; + averageSquared += Math.pow(node.dynamicEdgesLength,2); + hubCounter += 1; + } + average = average / hubCounter; + averageSquared = averageSquared / hubCounter; - return touchList; - } + var variance = averageSquared - Math.pow(average,2); - // make fake touchList from mouse position - ev.identifier = 1; - return [ev]; - }, + var standardDeviation = Math.sqrt(variance); - /** - * collect basic event data - * @method collectEventData - * @param {HTMLElement} element - * @param {String} eventType matches `EVENT_START|MOVE|END` - * @param {Array} touches - * @param {Object} ev - * @return {Object} ev - */ - collectEventData: function collectEventData(element, eventType, touches, ev) { - // find out pointerType - var pointerType = POINTER_TOUCH; - if(Utils.inStr(ev.type, 'mouse') || PointerEvent.matchType(POINTER_MOUSE, ev)) { - pointerType = POINTER_MOUSE; - } else if(PointerEvent.matchType(POINTER_PEN, ev)) { - pointerType = POINTER_PEN; - } + this.hubThreshold = Math.floor(average + 2*standardDeviation); - return { - center: Utils.getCenter(touches), - timeStamp: Date.now(), - target: ev.target, - touches: touches, - eventType: eventType, - pointerType: pointerType, - srcEvent: ev, + // always have at least one to cluster + if (this.hubThreshold > largestHub) { + this.hubThreshold = largestHub; + } - /** - * prevent the browser default actions - * mostly used to disable scrolling of the browser - */ - preventDefault: function() { - var srcEvent = this.srcEvent; - srcEvent.preventManipulation && srcEvent.preventManipulation(); - srcEvent.preventDefault && srcEvent.preventDefault(); - }, + // console.log("average",average,"averageSQ",averageSquared,"var",variance,"std",standardDeviation); + // console.log("hubThreshold:",this.hubThreshold); + }; - /** - * stop bubbling the event up to its parents - */ - stopPropagation: function() { - this.srcEvent.stopPropagation(); - }, - /** - * immediately stop gesture detection - * might be useful after a swipe was detected - * @return {*} - */ - stopDetect: function() { - return Detection.stopDetect(); - } - }; + /** + * We reduce the amount of "extension nodes" or chains. These are not quickly clustered with the outliers and hubs methods + * with this amount we can cluster specifically on these chains. + * + * @param {Number} fraction | between 0 and 1, the percentage of chains to reduce + * @private + */ + exports._reduceAmountOfChains = function(fraction) { + this.hubThreshold = 2; + var reduceAmount = Math.floor(this.nodeIndices.length * fraction); + for (var nodeId in this.nodes) { + if (this.nodes.hasOwnProperty(nodeId)) { + if (this.nodes[nodeId].dynamicEdgesLength == 2 && this.nodes[nodeId].dynamicEdges.length >= 2) { + if (reduceAmount > 0) { + this._formClusterFromHub(this.nodes[nodeId],true,true,1); + reduceAmount -= 1; + } + } } + } }; - /** - * @module hammer + * We get the amount of "extension nodes" or chains. These are not quickly clustered with the outliers and hubs methods + * with this amount we can cluster specifically on these chains. * - * @class PointerEvent - * @static + * @private */ - var PointerEvent = Hammer.PointerEvent = { - /** - * holds all pointers, by `identifier` - * @property pointers - * @type {Object} - */ - pointers: {}, - - /** - * get the pointers as an array - * @method getTouchList - * @return {Array} touchlist - */ - getTouchList: function getTouchList() { - var touchlist = []; - // we can use forEach since pointerEvents only is in IE10 - Utils.each(this.pointers, function(pointer) { - touchlist.push(pointer); - }); - return touchlist; - }, + exports._getChainFraction = function() { + var chains = 0; + var total = 0; + for (var nodeId in this.nodes) { + if (this.nodes.hasOwnProperty(nodeId)) { + if (this.nodes[nodeId].dynamicEdgesLength == 2 && this.nodes[nodeId].dynamicEdges.length >= 2) { + chains += 1; + } + total += 1; + } + } + return chains/total; + }; - /** - * update the position of a pointer - * @method updatePointer - * @param {String} eventType matches `EVENT_START|MOVE|END` - * @param {Object} pointerEvent - */ - updatePointer: function updatePointer(eventType, pointerEvent) { - if(eventType == EVENT_END || (eventType != EVENT_END && pointerEvent.buttons !== 1)) { - delete this.pointers[pointerEvent.pointerId]; - } else { - pointerEvent.identifier = pointerEvent.pointerId; - this.pointers[pointerEvent.pointerId] = pointerEvent; - } - }, - /** - * check if ev matches pointertype - * @method matchType - * @param {String} pointerType matches `POINTER_MOUSE|TOUCH|PEN` - * @param {PointerEvent} ev - */ - matchType: function matchType(pointerType, ev) { - if(!ev.pointerType) { - return false; - } +/***/ }, +/* 58 */ +/***/ function(module, exports, __webpack_require__) { - var pt = ev.pointerType, - types = {}; + var util = __webpack_require__(1); + var Node = __webpack_require__(40); - types[POINTER_MOUSE] = (pt === (ev.MSPOINTER_TYPE_MOUSE || POINTER_MOUSE)); - types[POINTER_TOUCH] = (pt === (ev.MSPOINTER_TYPE_TOUCH || POINTER_TOUCH)); - types[POINTER_PEN] = (pt === (ev.MSPOINTER_TYPE_PEN || POINTER_PEN)); - return types[pointerType]; - }, + /** + * Creation of the SectorMixin var. + * + * This contains all the functions the Network object can use to employ the sector system. + * The sector system is always used by Network, though the benefits only apply to the use of clustering. + * If clustering is not used, there is no overhead except for a duplicate object with references to nodes and edges. + */ - /** - * reset the stored pointers - * @method reset - */ - reset: function resetList() { - this.pointers = {}; - } + /** + * This function is only called by the setData function of the Network object. + * This loads the global references into the active sector. This initializes the sector. + * + * @private + */ + exports._putDataInSector = function() { + this.sectors["active"][this._sector()].nodes = this.nodes; + this.sectors["active"][this._sector()].edges = this.edges; + this.sectors["active"][this._sector()].nodeIndices = this.nodeIndices; }; /** - * @module hammer + * /** + * This function sets the global references to nodes, edges and nodeIndices back to + * those of the supplied (active) sector. If a type is defined, do the specific type * - * @class Detection - * @static + * @param {String} sectorId + * @param {String} [sectorType] | "active" or "frozen" + * @private */ - var Detection = Hammer.detection = { - // contains all registred Hammer.gestures in the correct order - gestures: [], - - // data of the current Hammer.gesture detection session - current: null, - - // the previous Hammer.gesture session data - // is a full clone of the previous gesture.current object - previous: null, + exports._switchToSector = function(sectorId, sectorType) { + if (sectorType === undefined || sectorType == "active") { + this._switchToActiveSector(sectorId); + } + else { + this._switchToFrozenSector(sectorId); + } + }; - // when this becomes true, no gestures are fired - stopped: false, - /** - * start Hammer.gesture detection - * @method startDetect - * @param {Hammer.Instance} inst - * @param {Object} eventData - */ - startDetect: function startDetect(inst, eventData) { - // already busy with a Hammer.gesture detection on an element - if(this.current) { - return; - } + /** + * This function sets the global references to nodes, edges and nodeIndices back to + * those of the supplied active sector. + * + * @param sectorId + * @private + */ + exports._switchToActiveSector = function(sectorId) { + this.nodeIndices = this.sectors["active"][sectorId]["nodeIndices"]; + this.nodes = this.sectors["active"][sectorId]["nodes"]; + this.edges = this.sectors["active"][sectorId]["edges"]; + }; - this.stopped = false; - // holds current session - this.current = { - inst: inst, // reference to HammerInstance we're working for - startEvent: Utils.extend({}, eventData), // start eventData for distances, timing etc - lastEvent: false, // last eventData - lastCalcEvent: false, // last eventData for calculations. - futureCalcEvent: false, // last eventData for calculations. - lastCalcData: {}, // last lastCalcData - name: '' // current gesture we're in/detected, can be 'tap', 'hold' etc - }; + /** + * This function sets the global references to nodes, edges and nodeIndices back to + * those of the supplied active sector. + * + * @private + */ + exports._switchToSupportSector = function() { + this.nodeIndices = this.sectors["support"]["nodeIndices"]; + this.nodes = this.sectors["support"]["nodes"]; + this.edges = this.sectors["support"]["edges"]; + }; - this.detect(eventData); - }, - /** - * Hammer.gesture detection - * @method detect - * @param {Object} eventData - * @return {any} - */ - detect: function detect(eventData) { - if(!this.current || this.stopped) { - return; - } + /** + * This function sets the global references to nodes, edges and nodeIndices back to + * those of the supplied frozen sector. + * + * @param sectorId + * @private + */ + exports._switchToFrozenSector = function(sectorId) { + this.nodeIndices = this.sectors["frozen"][sectorId]["nodeIndices"]; + this.nodes = this.sectors["frozen"][sectorId]["nodes"]; + this.edges = this.sectors["frozen"][sectorId]["edges"]; + }; - // extend event data with calculations about scale, distance etc - eventData = this.extendEventData(eventData); - // hammer instance and instance options - var inst = this.current.inst, - instOptions = inst.options; + /** + * This function sets the global references to nodes, edges and nodeIndices back to + * those of the currently active sector. + * + * @private + */ + exports._loadLatestSector = function() { + this._switchToSector(this._sector()); + }; - // call Hammer.gesture handlers - Utils.each(this.gestures, function triggerGesture(gesture) { - // only when the instance options have enabled this gesture - if(!this.stopped && inst.enabled && instOptions[gesture.name]) { - gesture.handler.call(gesture, eventData, inst); - } - }, this); - // store as previous event event - if(this.current) { - this.current.lastEvent = eventData; - } + /** + * This function returns the currently active sector Id + * + * @returns {String} + * @private + */ + exports._sector = function() { + return this.activeSector[this.activeSector.length-1]; + }; - if(eventData.eventType == EVENT_END) { - this.stopDetect(); - } - return eventData; - }, + /** + * This function returns the previously active sector Id + * + * @returns {String} + * @private + */ + exports._previousSector = function() { + if (this.activeSector.length > 1) { + return this.activeSector[this.activeSector.length-2]; + } + else { + throw new TypeError('there are not enough sectors in the this.activeSector array.'); + } + }; - /** - * clear the Hammer.gesture vars - * this is called on endDetect, but can also be used when a final Hammer.gesture has been detected - * to stop other Hammer.gestures from being fired - * @method stopDetect - */ - stopDetect: function stopDetect() { - // clone current data to the store as the previous gesture - // used for the double tap gesture, since this is an other gesture detect session - this.previous = Utils.extend({}, this.current); - // reset the current - this.current = null; - this.stopped = true; - }, + /** + * We add the active sector at the end of the this.activeSector array + * This ensures it is the currently active sector returned by _sector() and it reaches the top + * of the activeSector stack. When we reverse our steps we move from the end to the beginning of this stack. + * + * @param newId + * @private + */ + exports._setActiveSector = function(newId) { + this.activeSector.push(newId); + }; - /** - * calculate velocity, angle and direction - * @method getVelocityData - * @param {Object} ev - * @param {Object} center - * @param {Number} deltaTime - * @param {Number} deltaX - * @param {Number} deltaY - */ - getCalculatedData: function getCalculatedData(ev, center, deltaTime, deltaX, deltaY) { - var cur = this.current, - recalc = false, - calcEv = cur.lastCalcEvent, - calcData = cur.lastCalcData; - if(calcEv && ev.timeStamp - calcEv.timeStamp > Hammer.CALCULATE_INTERVAL) { - center = calcEv.center; - deltaTime = ev.timeStamp - calcEv.timeStamp; - deltaX = ev.center.clientX - calcEv.center.clientX; - deltaY = ev.center.clientY - calcEv.center.clientY; - recalc = true; - } + /** + * We remove the currently active sector id from the active sector stack. This happens when + * we reactivate the previously active sector + * + * @private + */ + exports._forgetLastSector = function() { + this.activeSector.pop(); + }; - if(ev.eventType == EVENT_TOUCH || ev.eventType == EVENT_RELEASE) { - cur.futureCalcEvent = ev; - } - if(!cur.lastCalcEvent || recalc) { - calcData.velocity = Utils.getVelocity(deltaTime, deltaX, deltaY); - calcData.angle = Utils.getAngle(center, ev.center); - calcData.direction = Utils.getDirection(center, ev.center); + /** + * This function creates a new active sector with the supplied newId. This newId + * is the expanding node id. + * + * @param {String} newId | Id of the new active sector + * @private + */ + exports._createNewSector = function(newId) { + // create the new sector + this.sectors["active"][newId] = {"nodes":{}, + "edges":{}, + "nodeIndices":[], + "formationScale": this.scale, + "drawingNode": undefined}; - cur.lastCalcEvent = cur.futureCalcEvent || ev; - cur.futureCalcEvent = ev; + // create the new sector render node. This gives visual feedback that you are in a new sector. + this.sectors["active"][newId]['drawingNode'] = new Node( + {id:newId, + color: { + background: "#eaefef", + border: "495c5e" } + },{},{},this.constants); + this.sectors["active"][newId]['drawingNode'].clusterSize = 2; + }; - ev.velocityX = calcData.velocity.x; - ev.velocityY = calcData.velocity.y; - ev.interimAngle = calcData.angle; - ev.interimDirection = calcData.direction; - }, - - /** - * extend eventData for Hammer.gestures - * @method extendEventData - * @param {Object} ev - * @return {Object} ev - */ - extendEventData: function extendEventData(ev) { - var cur = this.current, - startEv = cur.startEvent, - lastEv = cur.lastEvent || startEv; - - // update the start touchlist to calculate the scale/rotation - if(ev.eventType == EVENT_TOUCH || ev.eventType == EVENT_RELEASE) { - startEv.touches = []; - Utils.each(ev.touches, function(touch) { - startEv.touches.push({ - clientX: touch.clientX, - clientY: touch.clientY - }); - }); - } - var deltaTime = ev.timeStamp - startEv.timeStamp, - deltaX = ev.center.clientX - startEv.center.clientX, - deltaY = ev.center.clientY - startEv.center.clientY; + /** + * This function removes the currently active sector. This is called when we create a new + * active sector. + * + * @param {String} sectorId | Id of the active sector that will be removed + * @private + */ + exports._deleteActiveSector = function(sectorId) { + delete this.sectors["active"][sectorId]; + }; - this.getCalculatedData(ev, lastEv.center, deltaTime, deltaX, deltaY); - Utils.extend(ev, { - startEvent: startEv, + /** + * This function removes the currently active sector. This is called when we reactivate + * the previously active sector. + * + * @param {String} sectorId | Id of the active sector that will be removed + * @private + */ + exports._deleteFrozenSector = function(sectorId) { + delete this.sectors["frozen"][sectorId]; + }; - deltaTime: deltaTime, - deltaX: deltaX, - deltaY: deltaY, - distance: Utils.getDistance(startEv.center, ev.center), - angle: Utils.getAngle(startEv.center, ev.center), - direction: Utils.getDirection(startEv.center, ev.center), - scale: Utils.getScale(startEv.touches, ev.touches), - rotation: Utils.getRotation(startEv.touches, ev.touches) - }); + /** + * Freezing an active sector means moving it from the "active" object to the "frozen" object. + * We copy the references, then delete the active entree. + * + * @param sectorId + * @private + */ + exports._freezeSector = function(sectorId) { + // we move the set references from the active to the frozen stack. + this.sectors["frozen"][sectorId] = this.sectors["active"][sectorId]; - return ev; - }, + // we have moved the sector data into the frozen set, we now remove it from the active set + this._deleteActiveSector(sectorId); + }; - /** - * register new gesture - * @method register - * @param {Object} gesture object, see `gestures/` for documentation - * @return {Array} gestures - */ - register: function register(gesture) { - // add an enable gesture options if there is no given - var options = gesture.defaults || {}; - if(options[gesture.name] === undefined) { - options[gesture.name] = true; - } - // extend Hammer default options with the Hammer.gesture options - Utils.extend(Hammer.defaults, options, true); + /** + * This is the reverse operation of _freezeSector. Activating means moving the sector from the "frozen" + * object to the "active" object. + * + * @param sectorId + * @private + */ + exports._activateSector = function(sectorId) { + // we move the set references from the frozen to the active stack. + this.sectors["active"][sectorId] = this.sectors["frozen"][sectorId]; - // set its index - gesture.index = gesture.index || 1000; + // we have moved the sector data into the active set, we now remove it from the frozen stack + this._deleteFrozenSector(sectorId); + }; - // add Hammer.gesture to the list - this.gestures.push(gesture); - // sort the list by index - this.gestures.sort(function(a, b) { - if(a.index < b.index) { - return -1; - } - if(a.index > b.index) { - return 1; - } - return 0; - }); + /** + * This function merges the data from the currently active sector with a frozen sector. This is used + * in the process of reverting back to the previously active sector. + * The data that is placed in the frozen (the previously active) sector is the node that has been removed from it + * upon the creation of a new active sector. + * + * @param sectorId + * @private + */ + exports._mergeThisWithFrozen = function(sectorId) { + // copy all nodes + for (var nodeId in this.nodes) { + if (this.nodes.hasOwnProperty(nodeId)) { + this.sectors["frozen"][sectorId]["nodes"][nodeId] = this.nodes[nodeId]; + } + } - return this.gestures; + // copy all edges (if not fully clustered, else there are no edges) + for (var edgeId in this.edges) { + if (this.edges.hasOwnProperty(edgeId)) { + this.sectors["frozen"][sectorId]["edges"][edgeId] = this.edges[edgeId]; } + } + + // merge the nodeIndices + for (var i = 0; i < this.nodeIndices.length; i++) { + this.sectors["frozen"][sectorId]["nodeIndices"].push(this.nodeIndices[i]); + } }; /** - * @module hammer + * This clusters the sector to one cluster. It was a single cluster before this process started so + * we revert to that state. The clusterToFit function with a maximum size of 1 node does this. + * + * @private */ + exports._collapseThisToSingleCluster = function() { + this.clusterToFit(1,false); + }; + /** - * create new hammer instance - * all methods should return the instance itself, so it is chainable. + * We create a new active sector from the node that we want to open. * - * @class Instance - * @constructor - * @param {HTMLElement} element - * @param {Object} [options={}] options are merged with `Hammer.defaults` - * @return {Hammer.Instance} + * @param node + * @private */ - Hammer.Instance = function(element, options) { - var self = this; + exports._addSector = function(node) { + // this is the currently active sector + var sector = this._sector(); - // setup HammerJS window events and register all gestures - // this also sets up the default options - setup(); + // // this should allow me to select nodes from a frozen set. + // if (this.sectors['active'][sector]["nodes"].hasOwnProperty(node.id)) { + // console.log("the node is part of the active sector"); + // } + // else { + // console.log("I dont know what the fuck happened!!"); + // } - /** - * @property element - * @type {HTMLElement} - */ - this.element = element; + // when we switch to a new sector, we remove the node that will be expanded from the current nodes list. + delete this.nodes[node.id]; - /** - * @property enabled - * @type {Boolean} - * @protected - */ - this.enabled = true; + var unqiueIdentifier = util.randomUUID(); - /** - * options, merged with the defaults - * options with an _ are converted to camelCase - * @property options - * @type {Object} - */ - Utils.each(options, function(value, name) { - delete options[name]; - options[Utils.toCamelCase(name)] = value; - }); + // we fully freeze the currently active sector + this._freezeSector(sector); - this.options = Utils.extend(Utils.extend({}, Hammer.defaults), options || {}); + // we create a new active sector. This sector has the Id of the node to ensure uniqueness + this._createNewSector(unqiueIdentifier); - // add some css to the element to prevent the browser from doing its native behavoir - if(this.options.behavior) { - Utils.toggleBehavior(this.element, this.options.behavior, true); - } + // we add the active sector to the sectors array to be able to revert these steps later on + this._setActiveSector(unqiueIdentifier); - /** - * event start handler on the element to start the detection - * @property eventStartHandler - * @type {Object} - */ - this.eventStartHandler = Event.onTouch(element, EVENT_START, function(ev) { - if(self.enabled && ev.eventType == EVENT_START) { - Detection.startDetect(self, ev); - } else if(ev.eventType == EVENT_TOUCH) { - Detection.detect(ev); - } - }); + // we redirect the global references to the new sector's references. this._sector() now returns unqiueIdentifier + this._switchToSector(this._sector()); - /** - * keep a list of user event handlers which needs to be removed when calling 'dispose' - * @property eventHandlers - * @type {Array} - */ - this.eventHandlers = []; + // finally we add the node we removed from our previous active sector to the new active sector + this.nodes[node.id] = node; }; - Hammer.Instance.prototype = { - /** - * bind events to the instance - * @method on - * @chainable - * @param {String} gestures multiple gestures by splitting with a space - * @param {Function} handler - * @param {Object} handler.ev event object - */ - on: function onEvent(gestures, handler) { - var self = this; - Event.on(self.element, gestures, handler, function(type) { - self.eventHandlers.push({ gesture: type, handler: handler }); - }); - return self; - }, - /** - * unbind events to the instance - * @method off - * @chainable - * @param {String} gestures - * @param {Function} handler - */ - off: function offEvent(gestures, handler) { - var self = this; + /** + * We close the sector that is currently open and revert back to the one before. + * If the active sector is the "default" sector, nothing happens. + * + * @private + */ + exports._collapseSector = function() { + // the currently active sector + var sector = this._sector(); - Event.off(self.element, gestures, handler, function(type) { - var index = Utils.inArray({ gesture: type, handler: handler }); - if(index !== false) { - self.eventHandlers.splice(index, 1); - } - }); - return self; - }, + // we cannot collapse the default sector + if (sector != "default") { + if ((this.nodeIndices.length == 1) || + (this.sectors["active"][sector]["drawingNode"].width*this.scale < this.constants.clustering.screenSizeThreshold * this.frame.canvas.clientWidth) || + (this.sectors["active"][sector]["drawingNode"].height*this.scale < this.constants.clustering.screenSizeThreshold * this.frame.canvas.clientHeight)) { + var previousSector = this._previousSector(); - /** - * trigger gesture event - * @method trigger - * @chainable - * @param {String} gesture - * @param {Object} [eventData] - */ - trigger: function triggerEvent(gesture, eventData) { - // optional - if(!eventData) { - eventData = {}; - } - - // create DOM event - var event = Hammer.DOCUMENT.createEvent('Event'); - event.initEvent(gesture, true, true); - event.gesture = eventData; - - // trigger on the target if it is in the instance element, - // this is for event delegation tricks - var element = this.element; - if(Utils.hasParent(eventData.target, element)) { - element = eventData.target; - } - - element.dispatchEvent(event); - return this; - }, + // we collapse the sector back to a single cluster + this._collapseThisToSingleCluster(); - /** - * enable of disable hammer.js detection - * @method enable - * @chainable - * @param {Boolean} state - */ - enable: function enable(state) { - this.enabled = state; - return this; - }, + // we move the remaining nodes, edges and nodeIndices to the previous sector. + // This previous sector is the one we will reactivate + this._mergeThisWithFrozen(previousSector); - /** - * dispose this hammer instance - * @method dispose - * @return {Null} - */ - dispose: function dispose() { - var i, eh; + // the previously active (frozen) sector now has all the data from the currently active sector. + // we can now delete the active sector. + this._deleteActiveSector(sector); - // undo all changes made by stop_browser_behavior - Utils.toggleBehavior(this.element, this.options.behavior, false); + // we activate the previously active (and currently frozen) sector. + this._activateSector(previousSector); - // unbind all custom event handlers - for(i = -1; (eh = this.eventHandlers[++i]);) { - Utils.off(this.element, eh.gesture, eh.handler); - } + // we load the references from the newly active sector into the global references + this._switchToSector(previousSector); - this.eventHandlers = []; + // we forget the previously active sector because we reverted to the one before + this._forgetLastSector(); - // unbind the start event listener - Event.off(this.element, EVENT_TYPES[EVENT_START], this.eventStartHandler); + // finally, we update the node index list. + this._updateNodeIndexList(); - return null; + // we refresh the list with calulation nodes and calculation node indices. + this._updateCalculationNodes(); } + } }; /** - * @module gestures - */ - /** - * Move with x fingers (default 1) around on the page. - * Preventing the default browser behavior is a good way to improve feel and working. - * ```` - * hammertime.on("drag", function(ev) { - * console.log(ev); - * ev.gesture.preventDefault(); - * }); - * ```` + * This runs a function in all active sectors. This is used in _redraw() and the _initializeForceCalculation(). * - * @class Drag - * @static - */ - /** - * @event drag - * @param {Object} ev - */ - /** - * @event dragstart - * @param {Object} ev - */ - /** - * @event dragend - * @param {Object} ev - */ - /** - * @event drapleft - * @param {Object} ev - */ - /** - * @event dragright - * @param {Object} ev - */ - /** - * @event dragup - * @param {Object} ev - */ - /** - * @event dragdown - * @param {Object} ev + * @param {String} runFunction | This is the NAME of a function we want to call in all active sectors + * | we dont pass the function itself because then the "this" is the window object + * | instead of the Network object + * @param {*} [argument] | Optional: arguments to pass to the runFunction + * @private */ + exports._doInAllActiveSectors = function(runFunction,argument) { + var returnValues = []; + if (argument === undefined) { + for (var sector in this.sectors["active"]) { + if (this.sectors["active"].hasOwnProperty(sector)) { + // switch the global references to those of this sector + this._switchToActiveSector(sector); + returnValues.push( this[runFunction]() ); + } + } + } + else { + for (var sector in this.sectors["active"]) { + if (this.sectors["active"].hasOwnProperty(sector)) { + // switch the global references to those of this sector + this._switchToActiveSector(sector); + var args = Array.prototype.splice.call(arguments, 1); + if (args.length > 1) { + returnValues.push( this[runFunction](args[0],args[1]) ); + } + else { + returnValues.push( this[runFunction](argument) ); + } + } + } + } + // we revert the global references back to our active sector + this._loadLatestSector(); + return returnValues; + }; + /** - * @param {String} name + * This runs a function in all active sectors. This is used in _redraw() and the _initializeForceCalculation(). + * + * @param {String} runFunction | This is the NAME of a function we want to call in all active sectors + * | we dont pass the function itself because then the "this" is the window object + * | instead of the Network object + * @param {*} [argument] | Optional: arguments to pass to the runFunction + * @private */ - (function(name) { - var triggered = false; + exports._doInSupportSector = function(runFunction,argument) { + var returnValues = false; + if (argument === undefined) { + this._switchToSupportSector(); + returnValues = this[runFunction](); + } + else { + this._switchToSupportSector(); + var args = Array.prototype.splice.call(arguments, 1); + if (args.length > 1) { + returnValues = this[runFunction](args[0],args[1]); + } + else { + returnValues = this[runFunction](argument); + } + } + // we revert the global references back to our active sector + this._loadLatestSector(); + return returnValues; + }; - function dragGesture(ev, inst) { - var cur = Detection.current; - // max touches - if(inst.options.dragMaxTouches > 0 && - ev.touches.length > inst.options.dragMaxTouches) { - return; + /** + * This runs a function in all frozen sectors. This is used in the _redraw(). + * + * @param {String} runFunction | This is the NAME of a function we want to call in all active sectors + * | we don't pass the function itself because then the "this" is the window object + * | instead of the Network object + * @param {*} [argument] | Optional: arguments to pass to the runFunction + * @private + */ + exports._doInAllFrozenSectors = function(runFunction,argument) { + if (argument === undefined) { + for (var sector in this.sectors["frozen"]) { + if (this.sectors["frozen"].hasOwnProperty(sector)) { + // switch the global references to those of this sector + this._switchToFrozenSector(sector); + this[runFunction](); + } + } + } + else { + for (var sector in this.sectors["frozen"]) { + if (this.sectors["frozen"].hasOwnProperty(sector)) { + // switch the global references to those of this sector + this._switchToFrozenSector(sector); + var args = Array.prototype.splice.call(arguments, 1); + if (args.length > 1) { + this[runFunction](args[0],args[1]); } + else { + this[runFunction](argument); + } + } + } + } + this._loadLatestSector(); + }; - switch(ev.eventType) { - case EVENT_START: - triggered = false; - break; - - case EVENT_MOVE: - // when the distance we moved is too small we skip this gesture - // or we can be already in dragging - if(ev.distance < inst.options.dragMinDistance && - cur.name != name) { - return; - } - - var startCenter = cur.startEvent.center; - - // we are dragging! - if(cur.name != name) { - cur.name = name; - if(inst.options.dragDistanceCorrection && ev.distance > 0) { - // When a drag is triggered, set the event center to dragMinDistance pixels from the original event center. - // Without this correction, the dragged distance would jumpstart at dragMinDistance pixels instead of at 0. - // It might be useful to save the original start point somewhere - var factor = Math.abs(inst.options.dragMinDistance / ev.distance); - startCenter.pageX += ev.deltaX * factor; - startCenter.pageY += ev.deltaY * factor; - startCenter.clientX += ev.deltaX * factor; - startCenter.clientY += ev.deltaY * factor; - - // recalculate event data using new start point - ev = Detection.extendEventData(ev); - } - } - - // lock drag to axis? - if(cur.lastEvent.dragLockToAxis || - ( inst.options.dragLockToAxis && - inst.options.dragLockMinDistance <= ev.distance - )) { - ev.dragLockToAxis = true; - } - // keep direction on the axis that the drag gesture started on - var lastDirection = cur.lastEvent.direction; - if(ev.dragLockToAxis && lastDirection !== ev.direction) { - if(Utils.isVertical(lastDirection)) { - ev.direction = (ev.deltaY < 0) ? DIRECTION_UP : DIRECTION_DOWN; - } else { - ev.direction = (ev.deltaX < 0) ? DIRECTION_LEFT : DIRECTION_RIGHT; - } - } + /** + * This runs a function in all sectors. This is used in the _redraw(). + * + * @param {String} runFunction | This is the NAME of a function we want to call in all active sectors + * | we don't pass the function itself because then the "this" is the window object + * | instead of the Network object + * @param {*} [argument] | Optional: arguments to pass to the runFunction + * @private + */ + exports._doInAllSectors = function(runFunction,argument) { + var args = Array.prototype.splice.call(arguments, 1); + if (argument === undefined) { + this._doInAllActiveSectors(runFunction); + this._doInAllFrozenSectors(runFunction); + } + else { + if (args.length > 1) { + this._doInAllActiveSectors(runFunction,args[0],args[1]); + this._doInAllFrozenSectors(runFunction,args[0],args[1]); + } + else { + this._doInAllActiveSectors(runFunction,argument); + this._doInAllFrozenSectors(runFunction,argument); + } + } + }; - // first time, trigger dragstart event - if(!triggered) { - inst.trigger(name + 'start', ev); - triggered = true; - } - // trigger events - inst.trigger(name, ev); - inst.trigger(name + ev.direction, ev); + /** + * This clears the nodeIndices list. We cannot use this.nodeIndices = [] because we would break the link with the + * active sector. Thus we clear the nodeIndices in the active sector, then reconnect the this.nodeIndices to it. + * + * @private + */ + exports._clearNodeIndexList = function() { + var sector = this._sector(); + this.sectors["active"][sector]["nodeIndices"] = []; + this.nodeIndices = this.sectors["active"][sector]["nodeIndices"]; + }; - var isVertical = Utils.isVertical(ev.direction); - // block the browser events - if((inst.options.dragBlockVertical && isVertical) || - (inst.options.dragBlockHorizontal && !isVertical)) { - ev.preventDefault(); - } - break; + /** + * Draw the encompassing sector node + * + * @param ctx + * @param sectorType + * @private + */ + exports._drawSectorNodes = function(ctx,sectorType) { + var minY = 1e9, maxY = -1e9, minX = 1e9, maxX = -1e9, node; + for (var sector in this.sectors[sectorType]) { + if (this.sectors[sectorType].hasOwnProperty(sector)) { + if (this.sectors[sectorType][sector]["drawingNode"] !== undefined) { - case EVENT_RELEASE: - if(triggered && ev.changedLength <= inst.options.dragMaxTouches) { - inst.trigger(name + 'end', ev); - triggered = false; - } - break; + this._switchToSector(sector,sectorType); - case EVENT_END: - triggered = false; - break; + minY = 1e9; maxY = -1e9; minX = 1e9; maxX = -1e9; + for (var nodeId in this.nodes) { + if (this.nodes.hasOwnProperty(nodeId)) { + node = this.nodes[nodeId]; + node.resize(ctx); + if (minX > node.x - 0.5 * node.width) {minX = node.x - 0.5 * node.width;} + if (maxX < node.x + 0.5 * node.width) {maxX = node.x + 0.5 * node.width;} + if (minY > node.y - 0.5 * node.height) {minY = node.y - 0.5 * node.height;} + if (maxY < node.y + 0.5 * node.height) {maxY = node.y + 0.5 * node.height;} + } } + node = this.sectors[sectorType][sector]["drawingNode"]; + node.x = 0.5 * (maxX + minX); + node.y = 0.5 * (maxY + minY); + node.width = 2 * (node.x - minX); + node.height = 2 * (node.y - minY); + node.options.radius = Math.sqrt(Math.pow(0.5*node.width,2) + Math.pow(0.5*node.height,2)); + node.setScale(this.scale); + node._drawCircle(ctx); + } } + } + }; - Hammer.gestures.Drag = { - name: name, - index: 50, - handler: dragGesture, - defaults: { - /** - * minimal movement that have to be made before the drag event gets triggered - * @property dragMinDistance - * @type {Number} - * @default 10 - */ - dragMinDistance: 10, - - /** - * Set dragDistanceCorrection to true to make the starting point of the drag - * be calculated from where the drag was triggered, not from where the touch started. - * Useful to avoid a jerk-starting drag, which can make fine-adjustments - * through dragging difficult, and be visually unappealing. - * @property dragDistanceCorrection - * @type {Boolean} - * @default true - */ - dragDistanceCorrection: true, - - /** - * set 0 for unlimited, but this can conflict with transform - * @property dragMaxTouches - * @type {Number} - * @default 1 - */ - dragMaxTouches: 1, - - /** - * prevent default browser behavior when dragging occurs - * be careful with it, it makes the element a blocking element - * when you are using the drag gesture, it is a good practice to set this true - * @property dragBlockHorizontal - * @type {Boolean} - * @default false - */ - dragBlockHorizontal: false, + exports._drawAllSectorNodes = function(ctx) { + this._drawSectorNodes(ctx,"frozen"); + this._drawSectorNodes(ctx,"active"); + this._loadLatestSector(); + }; - /** - * same as `dragBlockHorizontal`, but for vertical movement - * @property dragBlockVertical - * @type {Boolean} - * @default false - */ - dragBlockVertical: false, - /** - * dragLockToAxis keeps the drag gesture on the axis that it started on, - * It disallows vertical directions if the initial direction was horizontal, and vice versa. - * @property dragLockToAxis - * @type {Boolean} - * @default false - */ - dragLockToAxis: false, +/***/ }, +/* 59 */ +/***/ function(module, exports, __webpack_require__) { - /** - * drag lock only kicks in when distance > dragLockMinDistance - * This way, locking occurs only when the distance has become large enough to reliably determine the direction - * @property dragLockMinDistance - * @type {Number} - * @default 25 - */ - dragLockMinDistance: 25 - } - }; - })('drag'); + var Node = __webpack_require__(40); /** - * @module gestures - */ - /** - * trigger a simple gesture event, so you can do anything in your handler. - * only usable if you know what your doing... + * This function can be called from the _doInAllSectors function * - * @class Gesture - * @static - */ - /** - * @event gesture - * @param {Object} ev + * @param object + * @param overlappingNodes + * @private */ - Hammer.gestures.Gesture = { - name: 'gesture', - index: 1337, - handler: function releaseGesture(ev, inst) { - inst.trigger(this.name, ev); + exports._getNodesOverlappingWith = function(object, overlappingNodes) { + var nodes = this.nodes; + for (var nodeId in nodes) { + if (nodes.hasOwnProperty(nodeId)) { + if (nodes[nodeId].isOverlappingWith(object)) { + overlappingNodes.push(nodeId); + } } + } }; /** - * @module gestures - */ - /** - * Touch stays at the same place for x time - * - * @class Hold - * @static - */ - /** - * @event hold - * @param {Object} ev + * retrieve all nodes overlapping with given object + * @param {Object} object An object with parameters left, top, right, bottom + * @return {Number[]} An array with id's of the overlapping nodes + * @private */ + exports._getAllNodesOverlappingWith = function (object) { + var overlappingNodes = []; + this._doInAllActiveSectors("_getNodesOverlappingWith",object,overlappingNodes); + return overlappingNodes; + }; + /** - * @param {String} name + * Return a position object in canvasspace from a single point in screenspace + * + * @param pointer + * @returns {{left: number, top: number, right: number, bottom: number}} + * @private */ - (function(name) { - var timer; + exports._pointerToPositionObject = function(pointer) { + var x = this._XconvertDOMtoCanvas(pointer.x); + var y = this._YconvertDOMtoCanvas(pointer.y); - function holdGesture(ev, inst) { - var options = inst.options, - current = Detection.current; + return { + left: x, + top: y, + right: x, + bottom: y + }; + }; - switch(ev.eventType) { - case EVENT_START: - clearTimeout(timer); - // set the gesture so we can check in the timeout if it still is - current.name = name; + /** + * Get the top node at the a specific point (like a click) + * + * @param {{x: Number, y: Number}} pointer + * @return {Node | null} node + * @private + */ + exports._getNodeAt = function (pointer) { + // we first check if this is an navigation controls element + var positionObject = this._pointerToPositionObject(pointer); + var overlappingNodes = this._getAllNodesOverlappingWith(positionObject); - // set timer and if after the timeout it still is hold, - // we trigger the hold event - timer = setTimeout(function() { - if(current && current.name == name) { - inst.trigger(name, ev); - } - }, options.holdTimeout); - break; + // if there are overlapping nodes, select the last one, this is the + // one which is drawn on top of the others + if (overlappingNodes.length > 0) { + return this.nodes[overlappingNodes[overlappingNodes.length - 1]]; + } + else { + return null; + } + }; - case EVENT_MOVE: - if(ev.distance > options.holdThreshold) { - clearTimeout(timer); - } - break; - case EVENT_RELEASE: - clearTimeout(timer); - break; - } + /** + * retrieve all edges overlapping with given object, selector is around center + * @param {Object} object An object with parameters left, top, right, bottom + * @return {Number[]} An array with id's of the overlapping nodes + * @private + */ + exports._getEdgesOverlappingWith = function (object, overlappingEdges) { + var edges = this.edges; + for (var edgeId in edges) { + if (edges.hasOwnProperty(edgeId)) { + if (edges[edgeId].isOverlappingWith(object)) { + overlappingEdges.push(edgeId); + } } + } + }; - Hammer.gestures.Hold = { - name: name, - index: 10, - defaults: { - /** - * @property holdTimeout - * @type {Number} - * @default 500 - */ - holdTimeout: 500, - - /** - * movement allowed while holding - * @property holdThreshold - * @type {Number} - * @default 2 - */ - holdThreshold: 2 - }, - handler: holdGesture - }; - })('hold'); /** - * @module gestures + * retrieve all nodes overlapping with given object + * @param {Object} object An object with parameters left, top, right, bottom + * @return {Number[]} An array with id's of the overlapping nodes + * @private */ + exports._getAllEdgesOverlappingWith = function (object) { + var overlappingEdges = []; + this._doInAllActiveSectors("_getEdgesOverlappingWith",object,overlappingEdges); + return overlappingEdges; + }; + /** - * when a touch is being released from the page + * Place holder. To implement change the _getNodeAt to a _getObjectAt. Have the _getObjectAt call + * _getNodeAt and _getEdgesAt, then priortize the selection to user preferences. * - * @class Release - * @static + * @param pointer + * @returns {null} + * @private */ + exports._getEdgeAt = function(pointer) { + var positionObject = this._pointerToPositionObject(pointer); + var overlappingEdges = this._getAllEdgesOverlappingWith(positionObject); + + if (overlappingEdges.length > 0) { + return this.edges[overlappingEdges[overlappingEdges.length - 1]]; + } + else { + return null; + } + }; + + /** - * @event release - * @param {Object} ev + * Add object to the selection array. + * + * @param obj + * @private */ - Hammer.gestures.Release = { - name: 'release', - index: Infinity, - handler: function releaseGesture(ev, inst) { - if(ev.eventType == EVENT_RELEASE) { - inst.trigger(this.name, ev); - } - } + exports._addToSelection = function(obj) { + if (obj instanceof Node) { + this.selectionObj.nodes[obj.id] = obj; + } + else { + this.selectionObj.edges[obj.id] = obj; + } }; /** - * @module gestures + * Add object to the selection array. + * + * @param obj + * @private */ + exports._addToHover = function(obj) { + if (obj instanceof Node) { + this.hoverObj.nodes[obj.id] = obj; + } + else { + this.hoverObj.edges[obj.id] = obj; + } + }; + + /** - * triggers swipe events when the end velocity is above the threshold - * for best usage, set `preventDefault` (on the drag gesture) to `true` - * ```` - * hammertime.on("dragleft swipeleft", function(ev) { - * console.log(ev); - * ev.gesture.preventDefault(); - * }); - * ```` + * Remove a single option from selection. * - * @class Swipe - * @static + * @param {Object} obj + * @private */ + exports._removeFromSelection = function(obj) { + if (obj instanceof Node) { + delete this.selectionObj.nodes[obj.id]; + } + else { + delete this.selectionObj.edges[obj.id]; + } + }; + /** - * @event swipe - * @param {Object} ev + * Unselect all. The selectionObj is useful for this. + * + * @param {Boolean} [doNotTrigger] | ignore trigger + * @private */ + exports._unselectAll = function(doNotTrigger) { + if (doNotTrigger === undefined) { + doNotTrigger = false; + } + for(var nodeId in this.selectionObj.nodes) { + if(this.selectionObj.nodes.hasOwnProperty(nodeId)) { + this.selectionObj.nodes[nodeId].unselect(); + } + } + for(var edgeId in this.selectionObj.edges) { + if(this.selectionObj.edges.hasOwnProperty(edgeId)) { + this.selectionObj.edges[edgeId].unselect(); + } + } + + this.selectionObj = {nodes:{},edges:{}}; + + if (doNotTrigger == false) { + this.emit('select', this.getSelection()); + } + }; + /** - * @event swipeleft - * @param {Object} ev + * Unselect all clusters. The selectionObj is useful for this. + * + * @param {Boolean} [doNotTrigger] | ignore trigger + * @private */ + exports._unselectClusters = function(doNotTrigger) { + if (doNotTrigger === undefined) { + doNotTrigger = false; + } + + for (var nodeId in this.selectionObj.nodes) { + if (this.selectionObj.nodes.hasOwnProperty(nodeId)) { + if (this.selectionObj.nodes[nodeId].clusterSize > 1) { + this.selectionObj.nodes[nodeId].unselect(); + this._removeFromSelection(this.selectionObj.nodes[nodeId]); + } + } + } + + if (doNotTrigger == false) { + this.emit('select', this.getSelection()); + } + }; + + /** - * @event swiperight - * @param {Object} ev + * return the number of selected nodes + * + * @returns {number} + * @private */ + exports._getSelectedNodeCount = function() { + var count = 0; + for (var nodeId in this.selectionObj.nodes) { + if (this.selectionObj.nodes.hasOwnProperty(nodeId)) { + count += 1; + } + } + return count; + }; + /** - * @event swipeup - * @param {Object} ev + * return the selected node + * + * @returns {number} + * @private */ + exports._getSelectedNode = function() { + for (var nodeId in this.selectionObj.nodes) { + if (this.selectionObj.nodes.hasOwnProperty(nodeId)) { + return this.selectionObj.nodes[nodeId]; + } + } + return null; + }; + /** - * @event swipedown - * @param {Object} ev + * return the selected edge + * + * @returns {number} + * @private */ - Hammer.gestures.Swipe = { - name: 'swipe', - index: 40, - defaults: { - /** - * @property swipeMinTouches - * @type {Number} - * @default 1 - */ - swipeMinTouches: 1, - - /** - * @property swipeMaxTouches - * @type {Number} - * @default 1 - */ - swipeMaxTouches: 1, - - /** - * horizontal swipe velocity - * @property swipeVelocityX - * @type {Number} - * @default 0.6 - */ - swipeVelocityX: 0.6, + exports._getSelectedEdge = function() { + for (var edgeId in this.selectionObj.edges) { + if (this.selectionObj.edges.hasOwnProperty(edgeId)) { + return this.selectionObj.edges[edgeId]; + } + } + return null; + }; - /** - * vertical swipe velocity - * @property swipeVelocityY - * @type {Number} - * @default 0.6 - */ - swipeVelocityY: 0.6 - }, - handler: function swipeGesture(ev, inst) { - if(ev.eventType == EVENT_RELEASE) { - var touches = ev.touches.length, - options = inst.options; + /** + * return the number of selected edges + * + * @returns {number} + * @private + */ + exports._getSelectedEdgeCount = function() { + var count = 0; + for (var edgeId in this.selectionObj.edges) { + if (this.selectionObj.edges.hasOwnProperty(edgeId)) { + count += 1; + } + } + return count; + }; - // max touches - if(touches < options.swipeMinTouches || - touches > options.swipeMaxTouches) { - return; - } - // when the distance we moved is too small we skip this gesture - // or we can be already in dragging - if(ev.velocityX > options.swipeVelocityX || - ev.velocityY > options.swipeVelocityY) { - // trigger swipe events - inst.trigger(this.name, ev); - inst.trigger(this.name + ev.direction, ev); - } - } + /** + * return the number of selected objects. + * + * @returns {number} + * @private + */ + exports._getSelectedObjectCount = function() { + var count = 0; + for(var nodeId in this.selectionObj.nodes) { + if(this.selectionObj.nodes.hasOwnProperty(nodeId)) { + count += 1; + } + } + for(var edgeId in this.selectionObj.edges) { + if(this.selectionObj.edges.hasOwnProperty(edgeId)) { + count += 1; } + } + return count; }; /** - * @module gestures + * Check if anything is selected + * + * @returns {boolean} + * @private */ + exports._selectionIsEmpty = function() { + for(var nodeId in this.selectionObj.nodes) { + if(this.selectionObj.nodes.hasOwnProperty(nodeId)) { + return false; + } + } + for(var edgeId in this.selectionObj.edges) { + if(this.selectionObj.edges.hasOwnProperty(edgeId)) { + return false; + } + } + return true; + }; + + /** - * Single tap and a double tap on a place + * check if one of the selected nodes is a cluster. * - * @class Tap - * @static + * @returns {boolean} + * @private */ + exports._clusterInSelection = function() { + for(var nodeId in this.selectionObj.nodes) { + if(this.selectionObj.nodes.hasOwnProperty(nodeId)) { + if (this.selectionObj.nodes[nodeId].clusterSize > 1) { + return true; + } + } + } + return false; + }; + /** - * @event tap - * @param {Object} ev + * select the edges connected to the node that is being selected + * + * @param {Node} node + * @private */ + exports._selectConnectedEdges = function(node) { + for (var i = 0; i < node.dynamicEdges.length; i++) { + var edge = node.dynamicEdges[i]; + edge.select(); + this._addToSelection(edge); + } + }; + /** - * @event doubletap - * @param {Object} ev + * select the edges connected to the node that is being selected + * + * @param {Node} node + * @private */ + exports._hoverConnectedEdges = function(node) { + for (var i = 0; i < node.dynamicEdges.length; i++) { + var edge = node.dynamicEdges[i]; + edge.hover = true; + this._addToHover(edge); + } + }; + /** - * @param {String} name + * unselect the edges connected to the node that is being selected + * + * @param {Node} node + * @private */ - (function(name) { - var hasMoved = false; + exports._unselectConnectedEdges = function(node) { + for (var i = 0; i < node.dynamicEdges.length; i++) { + var edge = node.dynamicEdges[i]; + edge.unselect(); + this._removeFromSelection(edge); + } + }; - function tapGesture(ev, inst) { - var options = inst.options, - current = Detection.current, - prev = Detection.previous, - sincePrev, - didDoubleTap; - switch(ev.eventType) { - case EVENT_START: - hasMoved = false; - break; - case EVENT_MOVE: - hasMoved = hasMoved || (ev.distance > options.tapMaxDistance); - break; - case EVENT_END: - if(!Utils.inStr(ev.srcEvent.type, 'cancel') && ev.deltaTime < options.tapMaxTime && !hasMoved) { - // previous gesture, for the double tap since these are two different gesture detections - sincePrev = prev && prev.lastEvent && ev.timeStamp - prev.lastEvent.timeStamp; - didDoubleTap = false; + /** + * This is called when someone clicks on a node. either select or deselect it. + * If there is an existing selection and we don't want to append to it, clear the existing selection + * + * @param {Node || Edge} object + * @param {Boolean} append + * @param {Boolean} [doNotTrigger] | ignore trigger + * @private + */ + exports._selectObject = function(object, append, doNotTrigger, highlightEdges, overrideSelectable) { + if (doNotTrigger === undefined) { + doNotTrigger = false; + } + if (highlightEdges === undefined) { + highlightEdges = true; + } - // check if double tap - if(prev && prev.name == name && - (sincePrev && sincePrev < options.doubleTapInterval) && - ev.distance < options.doubleTapDistance) { - inst.trigger('doubletap', ev); - didDoubleTap = true; - } + if (this._selectionIsEmpty() == false && append == false && this.forceAppendSelection == false) { + this._unselectAll(true); + } - // do a single tap - if(!didDoubleTap || options.tapAlways) { - current.name = name; - inst.trigger(current.name, ev); - } - } - break; - } + // selectable allows the object to be selected. Override can be used if needed to bypass this. + if (object.selected == false && (this.constants.selectable == true || overrideSelectable)) { + object.select(); + this._addToSelection(object); + if (object instanceof Node && this.blockConnectingEdgeSelection == false && highlightEdges == true) { + this._selectConnectedEdges(object); } + } + // do not select the object if selectable is false, only add it to selection to allow drag to work + else if (object.selected == false) { + this._addToSelection(object); + doNotTrigger = true; + } + else { + object.unselect(); + this._removeFromSelection(object); + } - Hammer.gestures.Tap = { - name: name, - index: 100, - handler: tapGesture, - defaults: { - /** - * max time of a tap, this is for the slow tappers - * @property tapMaxTime - * @type {Number} - * @default 250 - */ - tapMaxTime: 250, + if (doNotTrigger == false) { + this.emit('select', this.getSelection()); + } + }; - /** - * max distance of movement of a tap, this is for the slow tappers - * @property tapMaxDistance - * @type {Number} - * @default 10 - */ - tapMaxDistance: 10, - - /** - * always trigger the `tap` event, even while double-tapping - * @property tapAlways - * @type {Boolean} - * @default true - */ - tapAlways: true, - - /** - * max distance between two taps - * @property doubleTapDistance - * @type {Number} - * @default 20 - */ - doubleTapDistance: 20, - - /** - * max time between two taps - * @property doubleTapInterval - * @type {Number} - * @default 300 - */ - doubleTapInterval: 300 - } - }; - })('tap'); /** - * @module gestures + * This is called when someone clicks on a node. either select or deselect it. + * If there is an existing selection and we don't want to append to it, clear the existing selection + * + * @param {Node || Edge} object + * @private */ + exports._blurObject = function(object) { + if (object.hover == true) { + object.hover = false; + this.emit("blurNode",{node:object.id}); + } + }; + /** - * when a touch is being touched at the page + * This is called when someone clicks on a node. either select or deselect it. + * If there is an existing selection and we don't want to append to it, clear the existing selection * - * @class Touch - * @static + * @param {Node || Edge} object + * @private */ + exports._hoverObject = function(object) { + if (object.hover == false) { + object.hover = true; + this._addToHover(object); + if (object instanceof Node) { + this.emit("hoverNode",{node:object.id}); + } + } + if (object instanceof Node) { + this._hoverConnectedEdges(object); + } + }; + + /** - * @event touch - * @param {Object} ev + * handles the selection part of the touch, only for navigation controls elements; + * Touch is triggered before tap, also before hold. Hold triggers after a while. + * This is the most responsive solution + * + * @param {Object} pointer + * @private */ - Hammer.gestures.Touch = { - name: 'touch', - index: -Infinity, - defaults: { - /** - * call preventDefault at touchstart, and makes the element blocking by disabling the scrolling of the page, - * but it improves gestures like transforming and dragging. - * be careful with using this, it can be very annoying for users to be stuck on the page - * @property preventDefault - * @type {Boolean} - * @default false - */ - preventDefault: false, - - /** - * disable mouse events, so only touch (or pen!) input triggers events - * @property preventMouse - * @type {Boolean} - * @default false - */ - preventMouse: false - }, - handler: function touchGesture(ev, inst) { - if(inst.options.preventMouse && ev.pointerType == POINTER_MOUSE) { - ev.stopDetect(); - return; - } + exports._handleTouch = function(pointer) { + }; - if(inst.options.preventDefault) { - ev.preventDefault(); - } - if(ev.eventType == EVENT_TOUCH) { - inst.trigger('touch', ev); - } + /** + * handles the selection part of the tap; + * + * @param {Object} pointer + * @private + */ + exports._handleTap = function(pointer) { + var node = this._getNodeAt(pointer); + if (node != null) { + this._selectObject(node, false); + } + else { + var edge = this._getEdgeAt(pointer); + if (edge != null) { + this._selectObject(edge, false); + } + else { + this._unselectAll(); } + } + var properties = this.getSelection(); + properties['pointer'] = { + DOM: {x: pointer.x, y: pointer.y}, + canvas: {x: this._XconvertDOMtoCanvas(pointer.x), y: this._YconvertDOMtoCanvas(pointer.y)} + } + this.emit("click", properties); + this._redraw(); }; + /** - * @module gestures - */ - /** - * User want to scale or rotate with 2 fingers - * Preventing the default browser behavior is a good way to improve feel and working. This can be done with the - * `preventDefault` option. + * handles the selection part of the double tap and opens a cluster if needed * - * @class Transform - * @static + * @param {Object} pointer + * @private */ + exports._handleDoubleTap = function(pointer) { + var node = this._getNodeAt(pointer); + if (node != null && node !== undefined) { + // we reset the areaCenter here so the opening of the node will occur + this.areaCenter = {"x" : this._XconvertDOMtoCanvas(pointer.x), + "y" : this._YconvertDOMtoCanvas(pointer.y)}; + this.openCluster(node); + } + var properties = this.getSelection(); + properties['pointer'] = { + DOM: {x: pointer.x, y: pointer.y}, + canvas: {x: this._XconvertDOMtoCanvas(pointer.x), y: this._YconvertDOMtoCanvas(pointer.y)} + } + this.emit("doubleClick", properties); + }; + + /** - * @event transform - * @param {Object} ev + * Handle the onHold selection part + * + * @param pointer + * @private */ + exports._handleOnHold = function(pointer) { + var node = this._getNodeAt(pointer); + if (node != null) { + this._selectObject(node,true); + } + else { + var edge = this._getEdgeAt(pointer); + if (edge != null) { + this._selectObject(edge,true); + } + } + this._redraw(); + }; + + /** - * @event transformstart - * @param {Object} ev + * handle the onRelease event. These functions are here for the navigation controls module + * and data manipulation module. + * + * @private */ + exports._handleOnRelease = function(pointer) { + this._manipulationReleaseOverload(pointer); + this._navigationReleaseOverload(pointer); + }; + + exports._manipulationReleaseOverload = function (pointer) {}; + exports._navigationReleaseOverload = function (pointer) {}; + /** - * @event transformend - * @param {Object} ev + * + * retrieve the currently selected objects + * @return {{nodes: Array., edges: Array.}} selection */ + exports.getSelection = function() { + var nodeIds = this.getSelectedNodes(); + var edgeIds = this.getSelectedEdges(); + return {nodes:nodeIds, edges:edgeIds}; + }; + /** - * @event pinchin - * @param {Object} ev + * + * retrieve the currently selected nodes + * @return {String[]} selection An array with the ids of the + * selected nodes. */ + exports.getSelectedNodes = function() { + var idArray = []; + if (this.constants.selectable == true) { + for (var nodeId in this.selectionObj.nodes) { + if (this.selectionObj.nodes.hasOwnProperty(nodeId)) { + idArray.push(nodeId); + } + } + } + return idArray + }; + /** - * @event pinchout - * @param {Object} ev + * + * retrieve the currently selected edges + * @return {Array} selection An array with the ids of the + * selected nodes. */ + exports.getSelectedEdges = function() { + var idArray = []; + if (this.constants.selectable == true) { + for (var edgeId in this.selectionObj.edges) { + if (this.selectionObj.edges.hasOwnProperty(edgeId)) { + idArray.push(edgeId); + } + } + } + return idArray; + }; + + /** - * @event rotate - * @param {Object} ev + * select zero or more nodes DEPRICATED + * @param {Number[] | String[]} selection An array with the ids of the + * selected nodes. */ + exports.setSelection = function() { + console.log("setSelection is deprecated. Please use selectNodes instead.") + }; + /** - * @param {String} name + * select zero or more nodes with the option to highlight edges + * @param {Number[] | String[]} selection An array with the ids of the + * selected nodes. + * @param {boolean} [highlightEdges] */ - (function(name) { - var triggered = false; + exports.selectNodes = function(selection, highlightEdges) { + var i, iMax, id; - function transformGesture(ev, inst) { - switch(ev.eventType) { - case EVENT_START: - triggered = false; - break; + if (!selection || (selection.length == undefined)) + throw 'Selection must be an array with ids'; - case EVENT_MOVE: - // at least multitouch - if(ev.touches.length < 2) { - return; - } + // first unselect any selected node + this._unselectAll(true); - var scaleThreshold = Math.abs(1 - ev.scale); - var rotationThreshold = Math.abs(ev.rotation); + for (i = 0, iMax = selection.length; i < iMax; i++) { + id = selection[i]; - // when the distance we moved is too small we skip this gesture - // or we can be already in dragging - if(scaleThreshold < inst.options.transformMinScale && - rotationThreshold < inst.options.transformMinRotation) { - return; - } + var node = this.nodes[id]; + if (!node) { + throw new RangeError('Node with id "' + id + '" not found'); + } + this._selectObject(node,true,true,highlightEdges,true); + } + this.redraw(); + }; - // we are transforming! - Detection.current.name = name; - // first time, trigger dragstart event - if(!triggered) { - inst.trigger(name + 'start', ev); - triggered = true; - } + /** + * select zero or more edges + * @param {Number[] | String[]} selection An array with the ids of the + * selected nodes. + */ + exports.selectEdges = function(selection) { + var i, iMax, id; - inst.trigger(name, ev); // basic transform event + if (!selection || (selection.length == undefined)) + throw 'Selection must be an array with ids'; - // trigger rotate event - if(rotationThreshold > inst.options.transformMinRotation) { - inst.trigger('rotate', ev); - } + // first unselect any selected node + this._unselectAll(true); - // trigger pinch event - if(scaleThreshold > inst.options.transformMinScale) { - inst.trigger('pinch', ev); - inst.trigger('pinch' + (ev.scale < 1 ? 'in' : 'out'), ev); - } - break; + for (i = 0, iMax = selection.length; i < iMax; i++) { + id = selection[i]; - case EVENT_RELEASE: - if(triggered && ev.changedLength < 2) { - inst.trigger(name + 'end', ev); - triggered = false; - } - break; - } + var edge = this.edges[id]; + if (!edge) { + throw new RangeError('Edge with id "' + id + '" not found'); } - - Hammer.gestures.Transform = { - name: name, - index: 45, - defaults: { - /** - * minimal scale factor, no scale is 1, zoomin is to 0 and zoomout until higher then 1 - * @property transformMinScale - * @type {Number} - * @default 0.01 - */ - transformMinScale: 0.01, - - /** - * rotation in degrees - * @property transformMinRotation - * @type {Number} - * @default 1 - */ - transformMinRotation: 1 - }, - - handler: transformGesture - }; - })('transform'); + this._selectObject(edge,true,true,false,true); + } + this.redraw(); + }; /** - * @module hammer + * Validate the selection: remove ids of nodes which no longer exist + * @private */ + exports._updateSelection = function () { + for(var nodeId in this.selectionObj.nodes) { + if(this.selectionObj.nodes.hasOwnProperty(nodeId)) { + if (!this.nodes.hasOwnProperty(nodeId)) { + delete this.selectionObj.nodes[nodeId]; + } + } + } + for(var edgeId in this.selectionObj.edges) { + if(this.selectionObj.edges.hasOwnProperty(edgeId)) { + if (!this.edges.hasOwnProperty(edgeId)) { + delete this.selectionObj.edges[edgeId]; + } + } + } + }; - // AMD export - if(true) { - !(__WEBPACK_AMD_DEFINE_RESULT__ = function() { - return Hammer; - }.call(exports, __webpack_require__, exports, module), __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__)); - // commonjs export - } else if(typeof module !== 'undefined' && module.exports) { - module.exports = Hammer; - // browser export - } else { - window.Hammer = Hammer; - } - - })(window); /***/ }, -/* 59 */ +/* 60 */ /***/ function(module, exports, __webpack_require__) { - var __WEBPACK_AMD_DEFINE_RESULT__;/* WEBPACK VAR INJECTION */(function(global, module) {//! moment.js - //! version : 2.9.0 - //! authors : Tim Wood, Iskren Chernev, Moment.js contributors - //! license : MIT - //! momentjs.com + var util = __webpack_require__(1); + var Node = __webpack_require__(40); + var Edge = __webpack_require__(37); - (function (undefined) { - /************************************ - Constants - ************************************/ + /** + * clears the toolbar div element of children + * + * @private + */ + exports._clearManipulatorBar = function() { + this._recursiveDOMDelete(this.manipulationDiv); + this.manipulationDOM = {}; - var moment, - VERSION = '2.9.0', - // the global-scope this is NOT the global object in Node.js - globalScope = (typeof global !== 'undefined' && (typeof window === 'undefined' || window === global.window)) ? global : this, - oldGlobalMoment, - round = Math.round, - hasOwnProperty = Object.prototype.hasOwnProperty, - i, + this._manipulationReleaseOverload = function () {}; + delete this.sectors['support']['nodes']['targetNode']; + delete this.sectors['support']['nodes']['targetViaNode']; + this.controlNodesActive = false; + this.freezeSimulation = false; + }; - YEAR = 0, - MONTH = 1, - DATE = 2, - HOUR = 3, - MINUTE = 4, - SECOND = 5, - MILLISECOND = 6, + /** + * Manipulation UI temporarily overloads certain functions to extend or replace them. To be able to restore + * these functions to their original functionality, we saved them in this.cachedFunctions. + * This function restores these functions to their original function. + * + * @private + */ + exports._restoreOverloadedFunctions = function() { + for (var functionName in this.cachedFunctions) { + if (this.cachedFunctions.hasOwnProperty(functionName)) { + this[functionName] = this.cachedFunctions[functionName]; + delete this.cachedFunctions[functionName]; + } + } + }; - // internal storage for locale config files - locales = {}, + /** + * Enable or disable edit-mode. + * + * @private + */ + exports._toggleEditMode = function() { + this.editMode = !this.editMode; + var toolbar = this.manipulationDiv; + var closeDiv = this.closeDiv; + var editModeDiv = this.editModeDiv; + if (this.editMode == true) { + toolbar.style.display="block"; + closeDiv.style.display="block"; + editModeDiv.style.display="none"; + closeDiv.onclick = this._toggleEditMode.bind(this); + } + else { + toolbar.style.display="none"; + closeDiv.style.display="none"; + editModeDiv.style.display="block"; + closeDiv.onclick = null; + } + this._createManipulatorBar() + }; - // extra moment internal properties (plugins register props here) - momentProperties = [], + /** + * main function, creates the main toolbar. Removes functions bound to the select event. Binds all the buttons of the toolbar. + * + * @private + */ + exports._createManipulatorBar = function() { + // remove bound functions + if (this.boundFunction) { + this.off('select', this.boundFunction); + } - // check for nodeJS - hasModule = (typeof module !== 'undefined' && module && module.exports), + var locale = this.constants.locales[this.constants.locale]; - // ASP.NET json date format regex - aspNetJsonRegex = /^\/?Date\((\-?\d+)/i, - aspNetTimeSpanJsonRegex = /(\-)?(?:(\d*)\.)?(\d+)\:(\d+)(?:\:(\d+)\.?(\d{3})?)?/, + if (this.edgeBeingEdited !== undefined) { + this.edgeBeingEdited._disableControlNodes(); + this.edgeBeingEdited = undefined; + this.selectedControlNode = null; + this.controlNodesActive = false; + this._redraw(); + } - // from http://docs.closure-library.googlecode.com/git/closure_goog_date_date.js.source.html - // somewhat more in line with 4.4.3.2 2004 spec, but allows decimal anywhere - isoDurationRegex = /^(-)?P(?:(?:([0-9,.]*)Y)?(?:([0-9,.]*)M)?(?:([0-9,.]*)D)?(?:T(?:([0-9,.]*)H)?(?:([0-9,.]*)M)?(?:([0-9,.]*)S)?)?|([0-9,.]*)W)$/, - - // format tokens - formattingTokens = /(\[[^\[]*\])|(\\)?(Mo|MM?M?M?|Do|DDDo|DD?D?D?|ddd?d?|do?|w[o|w]?|W[o|W]?|Q|YYYYYY|YYYYY|YYYY|YY|gg(ggg?)?|GG(GGG?)?|e|E|a|A|hh?|HH?|mm?|ss?|S{1,4}|x|X|zz?|ZZ?|.)/g, - localFormattingTokens = /(\[[^\[]*\])|(\\)?(LTS|LT|LL?L?L?|l{1,4})/g, - - // parsing token regexes - parseTokenOneOrTwoDigits = /\d\d?/, // 0 - 99 - parseTokenOneToThreeDigits = /\d{1,3}/, // 0 - 999 - parseTokenOneToFourDigits = /\d{1,4}/, // 0 - 9999 - parseTokenOneToSixDigits = /[+\-]?\d{1,6}/, // -999,999 - 999,999 - parseTokenDigits = /\d+/, // nonzero number of digits - parseTokenWord = /[0-9]*['a-z\u00A0-\u05FF\u0700-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]+|[\u0600-\u06FF\/]+(\s*?[\u0600-\u06FF]+){1,2}/i, // any word (or two) characters or numbers including two/three word month in arabic. - parseTokenTimezone = /Z|[\+\-]\d\d:?\d\d/gi, // +00:00 -00:00 +0000 -0000 or Z - parseTokenT = /T/i, // T (ISO separator) - parseTokenOffsetMs = /[\+\-]?\d+/, // 1234567890123 - parseTokenTimestampMs = /[\+\-]?\d+(\.\d{1,3})?/, // 123456789 123456789.123 - - //strict parsing regexes - parseTokenOneDigit = /\d/, // 0 - 9 - parseTokenTwoDigits = /\d\d/, // 00 - 99 - parseTokenThreeDigits = /\d{3}/, // 000 - 999 - parseTokenFourDigits = /\d{4}/, // 0000 - 9999 - parseTokenSixDigits = /[+-]?\d{6}/, // -999,999 - 999,999 - parseTokenSignedNumber = /[+-]?\d+/, // -inf - inf + // restore overloaded functions + this._restoreOverloadedFunctions(); - // iso 8601 regex - // 0000-00-00 0000-W00 or 0000-W00-0 + T + 00 or 00:00 or 00:00:00 or 00:00:00.000 + +00:00 or +0000 or +00) - isoRegex = /^\s*(?:[+-]\d{6}|\d{4})-(?:(\d\d-\d\d)|(W\d\d$)|(W\d\d-\d)|(\d\d\d))((T| )(\d\d(:\d\d(:\d\d(\.\d+)?)?)?)?([\+\-]\d\d(?::?\d\d)?|\s*Z)?)?$/, + // resume calculation + this.freezeSimulation = false; - isoFormat = 'YYYY-MM-DDTHH:mm:ssZ', + // reset global variables + this.blockConnectingEdgeSelection = false; + this.forceAppendSelection = false; + this.manipulationDOM = {}; - isoDates = [ - ['YYYYYY-MM-DD', /[+-]\d{6}-\d{2}-\d{2}/], - ['YYYY-MM-DD', /\d{4}-\d{2}-\d{2}/], - ['GGGG-[W]WW-E', /\d{4}-W\d{2}-\d/], - ['GGGG-[W]WW', /\d{4}-W\d{2}/], - ['YYYY-DDD', /\d{4}-\d{3}/] - ], + if (this.editMode == true) { + while (this.manipulationDiv.hasChildNodes()) { + this.manipulationDiv.removeChild(this.manipulationDiv.firstChild); + } - // iso time formats and regexes - isoTimes = [ - ['HH:mm:ss.SSSS', /(T| )\d\d:\d\d:\d\d\.\d+/], - ['HH:mm:ss', /(T| )\d\d:\d\d:\d\d/], - ['HH:mm', /(T| )\d\d:\d\d/], - ['HH', /(T| )\d\d/] - ], + this.manipulationDOM['addNodeSpan'] = document.createElement('span'); + this.manipulationDOM['addNodeSpan'].className = 'network-manipulationUI add'; + this.manipulationDOM['addNodeLabelSpan'] = document.createElement('span'); + this.manipulationDOM['addNodeLabelSpan'].className = 'network-manipulationLabel'; + this.manipulationDOM['addNodeLabelSpan'].innerHTML = locale['addNode']; + this.manipulationDOM['addNodeSpan'].appendChild(this.manipulationDOM['addNodeLabelSpan']); - // timezone chunker '+10:00' > ['10', '00'] or '-1530' > ['-', '15', '30'] - parseTimezoneChunker = /([\+\-]|\d\d)/gi, + this.manipulationDOM['seperatorLineDiv1'] = document.createElement('div'); + this.manipulationDOM['seperatorLineDiv1'].className = 'network-seperatorLine'; - // getter and setter names - proxyGettersAndSetters = 'Date|Hours|Minutes|Seconds|Milliseconds'.split('|'), - unitMillisecondFactors = { - 'Milliseconds' : 1, - 'Seconds' : 1e3, - 'Minutes' : 6e4, - 'Hours' : 36e5, - 'Days' : 864e5, - 'Months' : 2592e6, - 'Years' : 31536e6 - }, + this.manipulationDOM['addEdgeSpan'] = document.createElement('span'); + this.manipulationDOM['addEdgeSpan'].className = 'network-manipulationUI connect'; + this.manipulationDOM['addEdgeLabelSpan'] = document.createElement('span'); + this.manipulationDOM['addEdgeLabelSpan'].className = 'network-manipulationLabel'; + this.manipulationDOM['addEdgeLabelSpan'].innerHTML = locale['addEdge']; + this.manipulationDOM['addEdgeSpan'].appendChild(this.manipulationDOM['addEdgeLabelSpan']); - unitAliases = { - ms : 'millisecond', - s : 'second', - m : 'minute', - h : 'hour', - d : 'day', - D : 'date', - w : 'week', - W : 'isoWeek', - M : 'month', - Q : 'quarter', - y : 'year', - DDD : 'dayOfYear', - e : 'weekday', - E : 'isoWeekday', - gg: 'weekYear', - GG: 'isoWeekYear' - }, + this.manipulationDiv.appendChild(this.manipulationDOM['addNodeSpan']); + this.manipulationDiv.appendChild(this.manipulationDOM['seperatorLineDiv1']); + this.manipulationDiv.appendChild(this.manipulationDOM['addEdgeSpan']); - camelFunctions = { - dayofyear : 'dayOfYear', - isoweekday : 'isoWeekday', - isoweek : 'isoWeek', - weekyear : 'weekYear', - isoweekyear : 'isoWeekYear' - }, + if (this._getSelectedNodeCount() == 1 && this.triggerFunctions.edit) { + this.manipulationDOM['seperatorLineDiv2'] = document.createElement('div'); + this.manipulationDOM['seperatorLineDiv2'].className = 'network-seperatorLine'; - // format function strings - formatFunctions = {}, + this.manipulationDOM['editNodeSpan'] = document.createElement('span'); + this.manipulationDOM['editNodeSpan'].className = 'network-manipulationUI edit'; + this.manipulationDOM['editNodeLabelSpan'] = document.createElement('span'); + this.manipulationDOM['editNodeLabelSpan'].className = 'network-manipulationLabel'; + this.manipulationDOM['editNodeLabelSpan'].innerHTML = locale['editNode']; + this.manipulationDOM['editNodeSpan'].appendChild(this.manipulationDOM['editNodeLabelSpan']); - // default relative time thresholds - relativeTimeThresholds = { - s: 45, // seconds to minute - m: 45, // minutes to hour - h: 22, // hours to day - d: 26, // days to month - M: 11 // months to year - }, + this.manipulationDiv.appendChild(this.manipulationDOM['seperatorLineDiv2']); + this.manipulationDiv.appendChild(this.manipulationDOM['editNodeSpan']); + } + else if (this._getSelectedEdgeCount() == 1 && this._getSelectedNodeCount() == 0) { + this.manipulationDOM['seperatorLineDiv3'] = document.createElement('div'); + this.manipulationDOM['seperatorLineDiv3'].className = 'network-seperatorLine'; - // tokens to ordinalize and pad - ordinalizeTokens = 'DDD w W M D d'.split(' '), - paddedTokens = 'M D H h m s w W'.split(' '), + this.manipulationDOM['editEdgeSpan'] = document.createElement('span'); + this.manipulationDOM['editEdgeSpan'].className = 'network-manipulationUI edit'; + this.manipulationDOM['editEdgeLabelSpan'] = document.createElement('span'); + this.manipulationDOM['editEdgeLabelSpan'].className = 'network-manipulationLabel'; + this.manipulationDOM['editEdgeLabelSpan'].innerHTML = locale['editEdge']; + this.manipulationDOM['editEdgeSpan'].appendChild(this.manipulationDOM['editEdgeLabelSpan']); - formatTokenFunctions = { - M : function () { - return this.month() + 1; - }, - MMM : function (format) { - return this.localeData().monthsShort(this, format); - }, - MMMM : function (format) { - return this.localeData().months(this, format); - }, - D : function () { - return this.date(); - }, - DDD : function () { - return this.dayOfYear(); - }, - d : function () { - return this.day(); - }, - dd : function (format) { - return this.localeData().weekdaysMin(this, format); - }, - ddd : function (format) { - return this.localeData().weekdaysShort(this, format); - }, - dddd : function (format) { - return this.localeData().weekdays(this, format); - }, - w : function () { - return this.week(); - }, - W : function () { - return this.isoWeek(); - }, - YY : function () { - return leftZeroFill(this.year() % 100, 2); - }, - YYYY : function () { - return leftZeroFill(this.year(), 4); - }, - YYYYY : function () { - return leftZeroFill(this.year(), 5); - }, - YYYYYY : function () { - var y = this.year(), sign = y >= 0 ? '+' : '-'; - return sign + leftZeroFill(Math.abs(y), 6); - }, - gg : function () { - return leftZeroFill(this.weekYear() % 100, 2); - }, - gggg : function () { - return leftZeroFill(this.weekYear(), 4); - }, - ggggg : function () { - return leftZeroFill(this.weekYear(), 5); - }, - GG : function () { - return leftZeroFill(this.isoWeekYear() % 100, 2); - }, - GGGG : function () { - return leftZeroFill(this.isoWeekYear(), 4); - }, - GGGGG : function () { - return leftZeroFill(this.isoWeekYear(), 5); - }, - e : function () { - return this.weekday(); - }, - E : function () { - return this.isoWeekday(); - }, - a : function () { - return this.localeData().meridiem(this.hours(), this.minutes(), true); - }, - A : function () { - return this.localeData().meridiem(this.hours(), this.minutes(), false); - }, - H : function () { - return this.hours(); - }, - h : function () { - return this.hours() % 12 || 12; - }, - m : function () { - return this.minutes(); - }, - s : function () { - return this.seconds(); - }, - S : function () { - return toInt(this.milliseconds() / 100); - }, - SS : function () { - return leftZeroFill(toInt(this.milliseconds() / 10), 2); - }, - SSS : function () { - return leftZeroFill(this.milliseconds(), 3); - }, - SSSS : function () { - return leftZeroFill(this.milliseconds(), 3); - }, - Z : function () { - var a = this.utcOffset(), - b = '+'; - if (a < 0) { - a = -a; - b = '-'; - } - return b + leftZeroFill(toInt(a / 60), 2) + ':' + leftZeroFill(toInt(a) % 60, 2); - }, - ZZ : function () { - var a = this.utcOffset(), - b = '+'; - if (a < 0) { - a = -a; - b = '-'; - } - return b + leftZeroFill(toInt(a / 60), 2) + leftZeroFill(toInt(a) % 60, 2); - }, - z : function () { - return this.zoneAbbr(); - }, - zz : function () { - return this.zoneName(); - }, - x : function () { - return this.valueOf(); - }, - X : function () { - return this.unix(); - }, - Q : function () { - return this.quarter(); - } - }, + this.manipulationDiv.appendChild(this.manipulationDOM['seperatorLineDiv3']); + this.manipulationDiv.appendChild(this.manipulationDOM['editEdgeSpan']); + } + if (this._selectionIsEmpty() == false) { + this.manipulationDOM['seperatorLineDiv4'] = document.createElement('div'); + this.manipulationDOM['seperatorLineDiv4'].className = 'network-seperatorLine'; - deprecations = {}, + this.manipulationDOM['deleteSpan'] = document.createElement('span'); + this.manipulationDOM['deleteSpan'].className = 'network-manipulationUI delete'; + this.manipulationDOM['deleteLabelSpan'] = document.createElement('span'); + this.manipulationDOM['deleteLabelSpan'].className = 'network-manipulationLabel'; + this.manipulationDOM['deleteLabelSpan'].innerHTML = locale['del']; + this.manipulationDOM['deleteSpan'].appendChild(this.manipulationDOM['deleteLabelSpan']); - lists = ['months', 'monthsShort', 'weekdays', 'weekdaysShort', 'weekdaysMin'], + this.manipulationDiv.appendChild(this.manipulationDOM['seperatorLineDiv4']); + this.manipulationDiv.appendChild(this.manipulationDOM['deleteSpan']); + } - updateInProgress = false; - // Pick the first defined of two or three arguments. dfl comes from - // default. - function dfl(a, b, c) { - switch (arguments.length) { - case 2: return a != null ? a : b; - case 3: return a != null ? a : b != null ? b : c; - default: throw new Error('Implement me'); - } + // bind the icons + this.manipulationDOM['addNodeSpan'].onclick = this._createAddNodeToolbar.bind(this); + this.manipulationDOM['addEdgeSpan'].onclick = this._createAddEdgeToolbar.bind(this); + if (this._getSelectedNodeCount() == 1 && this.triggerFunctions.edit) { + this.manipulationDOM['editNodeSpan'].onclick = this._editNode.bind(this); } - - function hasOwnProp(a, b) { - return hasOwnProperty.call(a, b); + else if (this._getSelectedEdgeCount() == 1 && this._getSelectedNodeCount() == 0) { + this.manipulationDOM['editEdgeSpan'].onclick = this._createEditEdgeToolbar.bind(this); } - - function defaultParsingFlags() { - // We need to deep clone this object, and es5 standard is not very - // helpful. - return { - empty : false, - unusedTokens : [], - unusedInput : [], - overflow : -2, - charsLeftOver : 0, - nullInput : false, - invalidMonth : null, - invalidFormat : false, - userInvalidated : false, - iso: false - }; + if (this._selectionIsEmpty() == false) { + this.manipulationDOM['deleteSpan'].onclick = this._deleteSelected.bind(this); } + this.closeDiv.onclick = this._toggleEditMode.bind(this); - function printMsg(msg) { - if (moment.suppressDeprecationWarnings === false && - typeof console !== 'undefined' && console.warn) { - console.warn('Deprecation warning: ' + msg); - } + var me = this; + this.boundFunction = me._createManipulatorBar; + this.on('select', this.boundFunction); + } + else { + while (this.editModeDiv.hasChildNodes()) { + this.editModeDiv.removeChild(this.editModeDiv.firstChild); } - function deprecate(msg, fn) { - var firstTime = true; - return extend(function () { - if (firstTime) { - printMsg(msg); - firstTime = false; - } - return fn.apply(this, arguments); - }, fn); - } + this.manipulationDOM['editModeSpan'] = document.createElement('span'); + this.manipulationDOM['editModeSpan'].className = 'network-manipulationUI edit editmode'; + this.manipulationDOM['editModeLabelSpan'] = document.createElement('span'); + this.manipulationDOM['editModeLabelSpan'].className = 'network-manipulationLabel'; + this.manipulationDOM['editModeLabelSpan'].innerHTML = locale['edit']; + this.manipulationDOM['editModeSpan'].appendChild(this.manipulationDOM['editModeLabelSpan']); - function deprecateSimple(name, msg) { - if (!deprecations[name]) { - printMsg(msg); - deprecations[name] = true; - } - } + this.editModeDiv.appendChild(this.manipulationDOM['editModeSpan']); - function padToken(func, count) { - return function (a) { - return leftZeroFill(func.call(this, a), count); - }; - } - function ordinalizeToken(func, period) { - return function (a) { - return this.localeData().ordinal(func.call(this, a), period); - }; - } + this.manipulationDOM['editModeSpan'].onclick = this._toggleEditMode.bind(this); + } + }; - function monthDiff(a, b) { - // difference in months - var wholeMonthDiff = ((b.year() - a.year()) * 12) + (b.month() - a.month()), - // b is in (anchor - 1 month, anchor + 1 month) - anchor = a.clone().add(wholeMonthDiff, 'months'), - anchor2, adjust; - if (b - anchor < 0) { - anchor2 = a.clone().add(wholeMonthDiff - 1, 'months'); - // linear across the month - adjust = (b - anchor) / (anchor - anchor2); - } else { - anchor2 = a.clone().add(wholeMonthDiff + 1, 'months'); - // linear across the month - adjust = (b - anchor) / (anchor2 - anchor); - } - return -(wholeMonthDiff + adjust); - } + /** + * Create the toolbar for adding Nodes + * + * @private + */ + exports._createAddNodeToolbar = function() { + // clear the toolbar + this._clearManipulatorBar(); + if (this.boundFunction) { + this.off('select', this.boundFunction); + } - while (ordinalizeTokens.length) { - i = ordinalizeTokens.pop(); - formatTokenFunctions[i + 'o'] = ordinalizeToken(formatTokenFunctions[i], i); - } - while (paddedTokens.length) { - i = paddedTokens.pop(); - formatTokenFunctions[i + i] = padToken(formatTokenFunctions[i], 2); - } - formatTokenFunctions.DDDD = padToken(formatTokenFunctions.DDD, 3); + var locale = this.constants.locales[this.constants.locale]; + this.manipulationDOM = {}; + this.manipulationDOM['backSpan'] = document.createElement('span'); + this.manipulationDOM['backSpan'].className = 'network-manipulationUI back'; + this.manipulationDOM['backLabelSpan'] = document.createElement('span'); + this.manipulationDOM['backLabelSpan'].className = 'network-manipulationLabel'; + this.manipulationDOM['backLabelSpan'].innerHTML = locale['back']; + this.manipulationDOM['backSpan'].appendChild(this.manipulationDOM['backLabelSpan']); - function meridiemFixWrap(locale, hour, meridiem) { - var isPm; + this.manipulationDOM['seperatorLineDiv1'] = document.createElement('div'); + this.manipulationDOM['seperatorLineDiv1'].className = 'network-seperatorLine'; - if (meridiem == null) { - // nothing to do - return hour; - } - if (locale.meridiemHour != null) { - return locale.meridiemHour(hour, meridiem); - } else if (locale.isPM != null) { - // Fallback - isPm = locale.isPM(meridiem); - if (isPm && hour < 12) { - hour += 12; - } - if (!isPm && hour === 12) { - hour = 0; - } - return hour; - } else { - // thie is not supposed to happen - return hour; - } - } + this.manipulationDOM['descriptionSpan'] = document.createElement('span'); + this.manipulationDOM['descriptionSpan'].className = 'network-manipulationUI none'; + this.manipulationDOM['descriptionLabelSpan'] = document.createElement('span'); + this.manipulationDOM['descriptionLabelSpan'].className = 'network-manipulationLabel'; + this.manipulationDOM['descriptionLabelSpan'].innerHTML = locale['addDescription']; + this.manipulationDOM['descriptionSpan'].appendChild(this.manipulationDOM['descriptionLabelSpan']); - /************************************ - Constructors - ************************************/ + this.manipulationDiv.appendChild(this.manipulationDOM['backSpan']); + this.manipulationDiv.appendChild(this.manipulationDOM['seperatorLineDiv1']); + this.manipulationDiv.appendChild(this.manipulationDOM['descriptionSpan']); - function Locale() { - } + // bind the icon + this.manipulationDOM['backSpan'].onclick = this._createManipulatorBar.bind(this); - // Moment prototype object - function Moment(config, skipOverflow) { - if (skipOverflow !== false) { - checkOverflow(config); - } - copyConfig(this, config); - this._d = new Date(+config._d); - // Prevent infinite loop in case updateOffset creates new moment - // objects. - if (updateInProgress === false) { - updateInProgress = true; - moment.updateOffset(this); - updateInProgress = false; - } - } + // we use the boundFunction so we can reference it when we unbind it from the "select" event. + var me = this; + this.boundFunction = me._addNode; + this.on('select', this.boundFunction); + }; - // Duration Constructor - function Duration(duration) { - var normalizedInput = normalizeObjectUnits(duration), - years = normalizedInput.year || 0, - quarters = normalizedInput.quarter || 0, - months = normalizedInput.month || 0, - weeks = normalizedInput.week || 0, - days = normalizedInput.day || 0, - hours = normalizedInput.hour || 0, - minutes = normalizedInput.minute || 0, - seconds = normalizedInput.second || 0, - milliseconds = normalizedInput.millisecond || 0; - // representation for dateAddRemove - this._milliseconds = +milliseconds + - seconds * 1e3 + // 1000 - minutes * 6e4 + // 1000 * 60 - hours * 36e5; // 1000 * 60 * 60 - // Because of dateAddRemove treats 24 hours as different from a - // day when working around DST, we need to store them separately - this._days = +days + - weeks * 7; - // It is impossible translate months into days without knowing - // which months you are are talking about, so we have to store - // it separately. - this._months = +months + - quarters * 3 + - years * 12; - - this._data = {}; - - this._locale = moment.localeData(); + /** + * create the toolbar to connect nodes + * + * @private + */ + exports._createAddEdgeToolbar = function() { + // clear the toolbar + this._clearManipulatorBar(); + this._unselectAll(true); + this.freezeSimulation = true; - this._bubble(); - } + if (this.boundFunction) { + this.off('select', this.boundFunction); + } - /************************************ - Helpers - ************************************/ + var locale = this.constants.locales[this.constants.locale]; + this._unselectAll(); + this.forceAppendSelection = false; + this.blockConnectingEdgeSelection = true; - function extend(a, b) { - for (var i in b) { - if (hasOwnProp(b, i)) { - a[i] = b[i]; - } - } + this.manipulationDOM = {}; + this.manipulationDOM['backSpan'] = document.createElement('span'); + this.manipulationDOM['backSpan'].className = 'network-manipulationUI back'; + this.manipulationDOM['backLabelSpan'] = document.createElement('span'); + this.manipulationDOM['backLabelSpan'].className = 'network-manipulationLabel'; + this.manipulationDOM['backLabelSpan'].innerHTML = locale['back']; + this.manipulationDOM['backSpan'].appendChild(this.manipulationDOM['backLabelSpan']); - if (hasOwnProp(b, 'toString')) { - a.toString = b.toString; - } + this.manipulationDOM['seperatorLineDiv1'] = document.createElement('div'); + this.manipulationDOM['seperatorLineDiv1'].className = 'network-seperatorLine'; - if (hasOwnProp(b, 'valueOf')) { - a.valueOf = b.valueOf; - } + this.manipulationDOM['descriptionSpan'] = document.createElement('span'); + this.manipulationDOM['descriptionSpan'].className = 'network-manipulationUI none'; + this.manipulationDOM['descriptionLabelSpan'] = document.createElement('span'); + this.manipulationDOM['descriptionLabelSpan'].className = 'network-manipulationLabel'; + this.manipulationDOM['descriptionLabelSpan'].innerHTML = locale['edgeDescription']; + this.manipulationDOM['descriptionSpan'].appendChild(this.manipulationDOM['descriptionLabelSpan']); - return a; - } + this.manipulationDiv.appendChild(this.manipulationDOM['backSpan']); + this.manipulationDiv.appendChild(this.manipulationDOM['seperatorLineDiv1']); + this.manipulationDiv.appendChild(this.manipulationDOM['descriptionSpan']); - function copyConfig(to, from) { - var i, prop, val; + // bind the icon + this.manipulationDOM['backSpan'].onclick = this._createManipulatorBar.bind(this); - if (typeof from._isAMomentObject !== 'undefined') { - to._isAMomentObject = from._isAMomentObject; - } - if (typeof from._i !== 'undefined') { - to._i = from._i; - } - if (typeof from._f !== 'undefined') { - to._f = from._f; - } - if (typeof from._l !== 'undefined') { - to._l = from._l; - } - if (typeof from._strict !== 'undefined') { - to._strict = from._strict; - } - if (typeof from._tzm !== 'undefined') { - to._tzm = from._tzm; - } - if (typeof from._isUTC !== 'undefined') { - to._isUTC = from._isUTC; - } - if (typeof from._offset !== 'undefined') { - to._offset = from._offset; - } - if (typeof from._pf !== 'undefined') { - to._pf = from._pf; - } - if (typeof from._locale !== 'undefined') { - to._locale = from._locale; - } + // we use the boundFunction so we can reference it when we unbind it from the "select" event. + var me = this; + this.boundFunction = me._handleConnect; + this.on('select', this.boundFunction); - if (momentProperties.length > 0) { - for (i in momentProperties) { - prop = momentProperties[i]; - val = from[prop]; - if (typeof val !== 'undefined') { - to[prop] = val; - } - } - } + // temporarily overload functions + this.cachedFunctions["_handleTouch"] = this._handleTouch; + this.cachedFunctions["_manipulationReleaseOverload"] = this._manipulationReleaseOverload; + this.cachedFunctions["_handleDragStart"] = this._handleDragStart; + this.cachedFunctions["_handleDragEnd"] = this._handleDragEnd; + this.cachedFunctions["_handleOnHold"] = this._handleOnHold; + this._handleTouch = this._handleConnect; + this._manipulationReleaseOverload = function () {}; + this._handleOnHold = function () {}; + this._handleDragStart = function () {}; + this._handleDragEnd = this._finishConnect; - return to; - } + // redraw to show the unselect + this._redraw(); + }; - function absRound(number) { - if (number < 0) { - return Math.ceil(number); - } else { - return Math.floor(number); - } - } + /** + * create the toolbar to edit edges + * + * @private + */ + exports._createEditEdgeToolbar = function() { + // clear the toolbar + this._clearManipulatorBar(); + this.controlNodesActive = true; - // left zero fill a number - // see http://jsperf.com/left-zero-filling for performance comparison - function leftZeroFill(number, targetLength, forceSign) { - var output = '' + Math.abs(number), - sign = number >= 0; + if (this.boundFunction) { + this.off('select', this.boundFunction); + } - while (output.length < targetLength) { - output = '0' + output; - } - return (sign ? (forceSign ? '+' : '') : '-') + output; - } + this.edgeBeingEdited = this._getSelectedEdge(); + this.edgeBeingEdited._enableControlNodes(); - function positiveMomentsDifference(base, other) { - var res = {milliseconds: 0, months: 0}; + var locale = this.constants.locales[this.constants.locale]; - res.months = other.month() - base.month() + - (other.year() - base.year()) * 12; - if (base.clone().add(res.months, 'M').isAfter(other)) { - --res.months; - } + this.manipulationDOM = {}; + this.manipulationDOM['backSpan'] = document.createElement('span'); + this.manipulationDOM['backSpan'].className = 'network-manipulationUI back'; + this.manipulationDOM['backLabelSpan'] = document.createElement('span'); + this.manipulationDOM['backLabelSpan'].className = 'network-manipulationLabel'; + this.manipulationDOM['backLabelSpan'].innerHTML = locale['back']; + this.manipulationDOM['backSpan'].appendChild(this.manipulationDOM['backLabelSpan']); - res.milliseconds = +other - +(base.clone().add(res.months, 'M')); + this.manipulationDOM['seperatorLineDiv1'] = document.createElement('div'); + this.manipulationDOM['seperatorLineDiv1'].className = 'network-seperatorLine'; - return res; - } + this.manipulationDOM['descriptionSpan'] = document.createElement('span'); + this.manipulationDOM['descriptionSpan'].className = 'network-manipulationUI none'; + this.manipulationDOM['descriptionLabelSpan'] = document.createElement('span'); + this.manipulationDOM['descriptionLabelSpan'].className = 'network-manipulationLabel'; + this.manipulationDOM['descriptionLabelSpan'].innerHTML = locale['editEdgeDescription']; + this.manipulationDOM['descriptionSpan'].appendChild(this.manipulationDOM['descriptionLabelSpan']); - function momentsDifference(base, other) { - var res; - other = makeAs(other, base); - if (base.isBefore(other)) { - res = positiveMomentsDifference(base, other); - } else { - res = positiveMomentsDifference(other, base); - res.milliseconds = -res.milliseconds; - res.months = -res.months; - } + this.manipulationDiv.appendChild(this.manipulationDOM['backSpan']); + this.manipulationDiv.appendChild(this.manipulationDOM['seperatorLineDiv1']); + this.manipulationDiv.appendChild(this.manipulationDOM['descriptionSpan']); - return res; - } + // bind the icon + this.manipulationDOM['backSpan'].onclick = this._createManipulatorBar.bind(this); - // TODO: remove 'name' arg after deprecation is removed - function createAdder(direction, name) { - return function (val, period) { - var dur, tmp; - //invert the arguments, but complain about it - if (period !== null && !isNaN(+period)) { - deprecateSimple(name, 'moment().' + name + '(period, number) is deprecated. Please use moment().' + name + '(number, period).'); - tmp = val; val = period; period = tmp; - } + // temporarily overload functions + this.cachedFunctions["_handleTouch"] = this._handleTouch; + this.cachedFunctions["_manipulationReleaseOverload"] = this._manipulationReleaseOverload; + this.cachedFunctions["_handleTap"] = this._handleTap; + this.cachedFunctions["_handleDragStart"] = this._handleDragStart; + this.cachedFunctions["_handleOnDrag"] = this._handleOnDrag; + this._handleTouch = this._selectControlNode; + this._handleTap = function () {}; + this._handleOnDrag = this._controlNodeDrag; + this._handleDragStart = function () {} + this._manipulationReleaseOverload = this._releaseControlNode; - val = typeof val === 'string' ? +val : val; - dur = moment.duration(val, period); - addOrSubtractDurationFromMoment(this, dur, direction); - return this; - }; - } + // redraw to show the unselect + this._redraw(); + }; - function addOrSubtractDurationFromMoment(mom, duration, isAdding, updateOffset) { - var milliseconds = duration._milliseconds, - days = duration._days, - months = duration._months; - updateOffset = updateOffset == null ? true : updateOffset; - if (milliseconds) { - mom._d.setTime(+mom._d + milliseconds * isAdding); - } - if (days) { - rawSetter(mom, 'Date', rawGetter(mom, 'Date') + days * isAdding); - } - if (months) { - rawMonthSetter(mom, rawGetter(mom, 'Month') + months * isAdding); - } - if (updateOffset) { - moment.updateOffset(mom, days || months); - } - } + /** + * the function bound to the selection event. It checks if you want to connect a cluster and changes the description + * to walk the user through the process. + * + * @private + */ + exports._selectControlNode = function(pointer) { + this.edgeBeingEdited.controlNodes.from.unselect(); + this.edgeBeingEdited.controlNodes.to.unselect(); + this.selectedControlNode = this.edgeBeingEdited._getSelectedControlNode(this._XconvertDOMtoCanvas(pointer.x),this._YconvertDOMtoCanvas(pointer.y)); + if (this.selectedControlNode !== null) { + this.selectedControlNode.select(); + this.freezeSimulation = true; + } + this._redraw(); + }; - // check if is an array - function isArray(input) { - return Object.prototype.toString.call(input) === '[object Array]'; - } - function isDate(input) { - return Object.prototype.toString.call(input) === '[object Date]' || - input instanceof Date; - } + /** + * the function bound to the selection event. It checks if you want to connect a cluster and changes the description + * to walk the user through the process. + * + * @private + */ + exports._controlNodeDrag = function(event) { + var pointer = this._getPointer(event.gesture.center); + if (this.selectedControlNode !== null && this.selectedControlNode !== undefined) { + this.selectedControlNode.x = this._XconvertDOMtoCanvas(pointer.x); + this.selectedControlNode.y = this._YconvertDOMtoCanvas(pointer.y); + } + this._redraw(); + }; - // compare two arrays, return the number of differences - function compareArrays(array1, array2, dontConvert) { - var len = Math.min(array1.length, array2.length), - lengthDiff = Math.abs(array1.length - array2.length), - diffs = 0, - i; - for (i = 0; i < len; i++) { - if ((dontConvert && array1[i] !== array2[i]) || - (!dontConvert && toInt(array1[i]) !== toInt(array2[i]))) { - diffs++; - } - } - return diffs + lengthDiff; - } - function normalizeUnits(units) { - if (units) { - var lowered = units.toLowerCase().replace(/(.)s$/, '$1'); - units = unitAliases[units] || camelFunctions[lowered] || lowered; - } - return units; + /** + * + * @param pointer + * @private + */ + exports._releaseControlNode = function(pointer) { + var newNode = this._getNodeAt(pointer); + if (newNode !== null) { + if (this.edgeBeingEdited.controlNodes.from.selected == true) { + this.edgeBeingEdited._restoreControlNodes(); + this._editEdge(newNode.id, this.edgeBeingEdited.to.id); + this.edgeBeingEdited.controlNodes.from.unselect(); } - - function normalizeObjectUnits(inputObject) { - var normalizedInput = {}, - normalizedProp, - prop; - - for (prop in inputObject) { - if (hasOwnProp(inputObject, prop)) { - normalizedProp = normalizeUnits(prop); - if (normalizedProp) { - normalizedInput[normalizedProp] = inputObject[prop]; - } - } - } - - return normalizedInput; + if (this.edgeBeingEdited.controlNodes.to.selected == true) { + this.edgeBeingEdited._restoreControlNodes(); + this._editEdge(this.edgeBeingEdited.from.id, newNode.id); + this.edgeBeingEdited.controlNodes.to.unselect(); } + } + else { + this.edgeBeingEdited._restoreControlNodes(); + } + this.freezeSimulation = false; + this._redraw(); + }; - function makeList(field) { - var count, setter; - - if (field.indexOf('week') === 0) { - count = 7; - setter = 'day'; - } - else if (field.indexOf('month') === 0) { - count = 12; - setter = 'month'; - } - else { - return; - } + /** + * the function bound to the selection event. It checks if you want to connect a cluster and changes the description + * to walk the user through the process. + * + * @private + */ + exports._handleConnect = function(pointer) { + if (this._getSelectedNodeCount() == 0) { + var node = this._getNodeAt(pointer); - moment[field] = function (format, index) { - var i, getter, - method = moment._locale[field], - results = []; + if (node != null) { + if (node.clusterSize > 1) { + alert(this.constants.locales[this.constants.locale]['createEdgeError']) + } + else { + this._selectObject(node,false); + var supportNodes = this.sectors['support']['nodes']; - if (typeof format === 'number') { - index = format; - format = undefined; - } + // create a node the temporary line can look at + supportNodes['targetNode'] = new Node({id:'targetNode'},{},{},this.constants); + var targetNode = supportNodes['targetNode']; + targetNode.x = node.x; + targetNode.y = node.y; - getter = function (i) { - var m = moment().utc().set(setter, i); - return method.call(moment._locale, m, format || ''); - }; + // create a temporary edge + this.edges['connectionEdge'] = new Edge({id:"connectionEdge",from:node.id,to:targetNode.id}, this, this.constants); + var connectionEdge = this.edges['connectionEdge']; + connectionEdge.from = node; + connectionEdge.connected = true; + connectionEdge.options.smoothCurves = {enabled: true, + dynamic: false, + type: "continuous", + roundness: 0.5 + }; + connectionEdge.selected = true; + connectionEdge.to = targetNode; - if (index != null) { - return getter(index); - } - else { - for (i = 0; i < count; i++) { - results.push(getter(i)); - } - return results; - } + this.cachedFunctions["_handleOnDrag"] = this._handleOnDrag; + this._handleOnDrag = function(event) { + var pointer = this._getPointer(event.gesture.center); + var connectionEdge = this.edges['connectionEdge']; + connectionEdge.to.x = this._XconvertDOMtoCanvas(pointer.x); + connectionEdge.to.y = this._YconvertDOMtoCanvas(pointer.y); }; + + this.moving = true; + this.start(); + } } + } + }; - function toInt(argumentForCoercion) { - var coercedNumber = +argumentForCoercion, - value = 0; + exports._finishConnect = function(event) { + if (this._getSelectedNodeCount() == 1) { + var pointer = this._getPointer(event.gesture.center); + // restore the drag function + this._handleOnDrag = this.cachedFunctions["_handleOnDrag"]; + delete this.cachedFunctions["_handleOnDrag"]; - if (coercedNumber !== 0 && isFinite(coercedNumber)) { - if (coercedNumber >= 0) { - value = Math.floor(coercedNumber); - } else { - value = Math.ceil(coercedNumber); - } - } + // remember the edge id + var connectFromId = this.edges['connectionEdge'].fromId; - return value; - } + // remove the temporary nodes and edge + delete this.edges['connectionEdge']; + delete this.sectors['support']['nodes']['targetNode']; + delete this.sectors['support']['nodes']['targetViaNode']; - function daysInMonth(year, month) { - return new Date(Date.UTC(year, month + 1, 0)).getUTCDate(); + var node = this._getNodeAt(pointer); + if (node != null) { + if (node.clusterSize > 1) { + alert(this.constants.locales[this.constants.locale]["createEdgeError"]) + } + else { + this._createEdge(connectFromId,node.id); + this._createManipulatorBar(); + } } + this._unselectAll(); + } + }; - function weeksInYear(year, dow, doy) { - return weekOfYear(moment([year, 11, 31 + dow - doy]), dow, doy).week; - } - function daysInYear(year) { - return isLeapYear(year) ? 366 : 365; + /** + * Adds a node on the specified location + */ + exports._addNode = function() { + if (this._selectionIsEmpty() && this.editMode == true) { + var positionObject = this._pointerToPositionObject(this.pointerPosition); + var defaultData = {id:util.randomUUID(),x:positionObject.left,y:positionObject.top,label:"new",allowedToMoveX:true,allowedToMoveY:true}; + if (this.triggerFunctions.add) { + if (this.triggerFunctions.add.length == 2) { + var me = this; + this.triggerFunctions.add(defaultData, function(finalizedData) { + me.nodesData.add(finalizedData); + me._createManipulatorBar(); + me.moving = true; + me.start(); + }); + } + else { + throw new Error('The function for add does not support two arguments (data,callback)'); + this._createManipulatorBar(); + this.moving = true; + this.start(); + } } - - function isLeapYear(year) { - return (year % 4 === 0 && year % 100 !== 0) || year % 400 === 0; + else { + this.nodesData.add(defaultData); + this._createManipulatorBar(); + this.moving = true; + this.start(); } + } + }; - function checkOverflow(m) { - var overflow; - if (m._a && m._pf.overflow === -2) { - overflow = - m._a[MONTH] < 0 || m._a[MONTH] > 11 ? MONTH : - m._a[DATE] < 1 || m._a[DATE] > daysInMonth(m._a[YEAR], m._a[MONTH]) ? DATE : - m._a[HOUR] < 0 || m._a[HOUR] > 24 || - (m._a[HOUR] === 24 && (m._a[MINUTE] !== 0 || - m._a[SECOND] !== 0 || - m._a[MILLISECOND] !== 0)) ? HOUR : - m._a[MINUTE] < 0 || m._a[MINUTE] > 59 ? MINUTE : - m._a[SECOND] < 0 || m._a[SECOND] > 59 ? SECOND : - m._a[MILLISECOND] < 0 || m._a[MILLISECOND] > 999 ? MILLISECOND : - -1; - if (m._pf._overflowDayOfYear && (overflow < YEAR || overflow > DATE)) { - overflow = DATE; - } - - m._pf.overflow = overflow; - } + /** + * connect two nodes with a new edge. + * + * @private + */ + exports._createEdge = function(sourceNodeId,targetNodeId) { + if (this.editMode == true) { + var defaultData = {from:sourceNodeId, to:targetNodeId}; + if (this.triggerFunctions.connect) { + if (this.triggerFunctions.connect.length == 2) { + var me = this; + this.triggerFunctions.connect(defaultData, function(finalizedData) { + me.edgesData.add(finalizedData); + me.moving = true; + me.start(); + }); + } + else { + throw new Error('The function for connect does not support two arguments (data,callback)'); + this.moving = true; + this.start(); + } } - - function isValid(m) { - if (m._isValid == null) { - m._isValid = !isNaN(m._d.getTime()) && - m._pf.overflow < 0 && - !m._pf.empty && - !m._pf.invalidMonth && - !m._pf.nullInput && - !m._pf.invalidFormat && - !m._pf.userInvalidated; - - if (m._strict) { - m._isValid = m._isValid && - m._pf.charsLeftOver === 0 && - m._pf.unusedTokens.length === 0 && - m._pf.bigHour === undefined; - } - } - return m._isValid; + else { + this.edgesData.add(defaultData); + this.moving = true; + this.start(); } + } + }; - function normalizeLocale(key) { - return key ? key.toLowerCase().replace('_', '-') : key; + /** + * connect two nodes with a new edge. + * + * @private + */ + exports._editEdge = function(sourceNodeId,targetNodeId) { + if (this.editMode == true) { + var defaultData = {id: this.edgeBeingEdited.id, from:sourceNodeId, to:targetNodeId}; + if (this.triggerFunctions.editEdge) { + if (this.triggerFunctions.editEdge.length == 2) { + var me = this; + this.triggerFunctions.editEdge(defaultData, function(finalizedData) { + me.edgesData.update(finalizedData); + me.moving = true; + me.start(); + }); + } + else { + throw new Error('The function for edit does not support two arguments (data, callback)'); + this.moving = true; + this.start(); + } } - - // pick the locale from the array - // try ['en-au', 'en-gb'] as 'en-au', 'en-gb', 'en', as in move through the list trying each - // substring from most specific to least, but move to the next array item if it's a more specific variant than the current root - function chooseLocale(names) { - var i = 0, j, next, locale, split; - - while (i < names.length) { - split = normalizeLocale(names[i]).split('-'); - j = split.length; - next = normalizeLocale(names[i + 1]); - next = next ? next.split('-') : null; - while (j > 0) { - locale = loadLocale(split.slice(0, j).join('-')); - if (locale) { - return locale; - } - if (next && next.length >= j && compareArrays(split, next, true) >= j - 1) { - //the next array item is better than a shallower substring of this one - break; - } - j--; - } - i++; - } - return null; + else { + this.edgesData.update(defaultData); + this.moving = true; + this.start(); } + } + }; - function loadLocale(name) { - var oldLocale = null; - if (!locales[name] && hasModule) { - try { - oldLocale = moment.locale(); - !(function webpackMissingModule() { var e = new Error("Cannot find module \"./locale\""); e.code = 'MODULE_NOT_FOUND'; throw e; }()); - // because defineLocale currently also sets the global locale, we want to undo that for lazy loaded locales - moment.locale(oldLocale); - } catch (e) { } + /** + * Create the toolbar to edit the selected node. The label and the color can be changed. Other colors are derived from the chosen color. + * + * @private + */ + exports._editNode = function() { + if (this.triggerFunctions.edit && this.editMode == true) { + var node = this._getSelectedNode(); + var data = {id:node.id, + label: node.label, + group: node.options.group, + shape: node.options.shape, + color: { + background:node.options.color.background, + border:node.options.color.border, + highlight: { + background:node.options.color.highlight.background, + border:node.options.color.highlight.border } - return locales[name]; + }}; + if (this.triggerFunctions.edit.length == 2) { + var me = this; + this.triggerFunctions.edit(data, function (finalizedData) { + me.nodesData.update(finalizedData); + me._createManipulatorBar(); + me.moving = true; + me.start(); + }); } - - // Return a moment from input, that is local/utc/utcOffset equivalent to - // model. - function makeAs(input, model) { - var res, diff; - if (model._isUTC) { - res = model.clone(); - diff = (moment.isMoment(input) || isDate(input) ? - +input : +moment(input)) - (+res); - // Use low-level api, because this fn is low-level api. - res._d.setTime(+res._d + diff); - moment.updateOffset(res, false); - return res; - } else { - return moment(input).local(); - } + else { + throw new Error('The function for edit does not support two arguments (data, callback)'); } + } + else { + throw new Error('No edit function has been bound to this button'); + } + }; - /************************************ - Locale - ************************************/ - extend(Locale.prototype, { - set : function (config) { - var prop, i; - for (i in config) { - prop = config[i]; - if (typeof prop === 'function') { - this[i] = prop; - } else { - this['_' + i] = prop; - } - } - // Lenient ordinal parsing accepts just a number in addition to - // number + (possibly) stuff coming from _ordinalParseLenient. - this._ordinalParseLenient = new RegExp(this._ordinalParse.source + '|' + /\d{1,2}/.source); - }, + /** + * delete everything in the selection + * + * @private + */ + exports._deleteSelected = function() { + if (!this._selectionIsEmpty() && this.editMode == true) { + if (!this._clusterInSelection()) { + var selectedNodes = this.getSelectedNodes(); + var selectedEdges = this.getSelectedEdges(); + if (this.triggerFunctions.del) { + var me = this; + var data = {nodes: selectedNodes, edges: selectedEdges}; + if (this.triggerFunctions.del.length == 2) { + this.triggerFunctions.del(data, function (finalizedData) { + me.edgesData.remove(finalizedData.edges); + me.nodesData.remove(finalizedData.nodes); + me._unselectAll(); + me.moving = true; + me.start(); + }); + } + else { + throw new Error('The function for delete does not support two arguments (data, callback)') + } + } + else { + this.edgesData.remove(selectedEdges); + this.nodesData.remove(selectedNodes); + this._unselectAll(); + this.moving = true; + this.start(); + } + } + else { + alert(this.constants.locales[this.constants.locale]["deleteClusterError"]); + } + } + }; - _months : 'January_February_March_April_May_June_July_August_September_October_November_December'.split('_'), - months : function (m) { - return this._months[m.month()]; - }, - _monthsShort : 'Jan_Feb_Mar_Apr_May_Jun_Jul_Aug_Sep_Oct_Nov_Dec'.split('_'), - monthsShort : function (m) { - return this._monthsShort[m.month()]; - }, +/***/ }, +/* 61 */ +/***/ function(module, exports, __webpack_require__) { - monthsParse : function (monthName, format, strict) { - var i, mom, regex; + var util = __webpack_require__(1); + var Hammer = __webpack_require__(45); - if (!this._monthsParse) { - this._monthsParse = []; - this._longMonthsParse = []; - this._shortMonthsParse = []; - } + exports._cleanNavigation = function() { + // clean hammer bindings + if (this.navigationHammers.existing.length != 0) { + for (var i = 0; i < this.navigationHammers.existing.length; i++) { + this.navigationHammers.existing[i].dispose(); + } + this.navigationHammers.existing = []; + } - for (i = 0; i < 12; i++) { - // make the regex if we don't have it already - mom = moment.utc([2000, i]); - if (strict && !this._longMonthsParse[i]) { - this._longMonthsParse[i] = new RegExp('^' + this.months(mom, '').replace('.', '') + '$', 'i'); - this._shortMonthsParse[i] = new RegExp('^' + this.monthsShort(mom, '').replace('.', '') + '$', 'i'); - } - if (!strict && !this._monthsParse[i]) { - regex = '^' + this.months(mom, '') + '|^' + this.monthsShort(mom, ''); - this._monthsParse[i] = new RegExp(regex.replace('.', ''), 'i'); - } - // test the regex - if (strict && format === 'MMMM' && this._longMonthsParse[i].test(monthName)) { - return i; - } else if (strict && format === 'MMM' && this._shortMonthsParse[i].test(monthName)) { - return i; - } else if (!strict && this._monthsParse[i].test(monthName)) { - return i; - } - } - }, + this._navigationReleaseOverload = function () {}; - _weekdays : 'Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday'.split('_'), - weekdays : function (m) { - return this._weekdays[m.day()]; - }, + // clean up previous navigation items + if (this.navigationDivs && this.navigationDivs['wrapper'] && this.navigationDivs['wrapper'].parentNode) { + this.navigationDivs['wrapper'].parentNode.removeChild(this.navigationDivs['wrapper']); + } + }; - _weekdaysShort : 'Sun_Mon_Tue_Wed_Thu_Fri_Sat'.split('_'), - weekdaysShort : function (m) { - return this._weekdaysShort[m.day()]; - }, + /** + * 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 + */ + exports._loadNavigationElements = function() { + this._cleanNavigation(); - _weekdaysMin : 'Su_Mo_Tu_We_Th_Fr_Sa'.split('_'), - weekdaysMin : function (m) { - return this._weekdaysMin[m.day()]; - }, + this.navigationDivs = {}; + var navigationDivs = ['up','down','left','right','zoomIn','zoomOut','zoomExtends']; + var navigationDivActions = ['_moveUp','_moveDown','_moveLeft','_moveRight','_zoomIn','_zoomOut','_zoomExtent']; - weekdaysParse : function (weekdayName) { - var i, mom, regex; + this.navigationDivs['wrapper'] = document.createElement('div'); + this.frame.appendChild(this.navigationDivs['wrapper']); - if (!this._weekdaysParse) { - this._weekdaysParse = []; - } + for (var i = 0; i < navigationDivs.length; i++) { + this.navigationDivs[navigationDivs[i]] = document.createElement('div'); + this.navigationDivs[navigationDivs[i]].className = 'network-navigation ' + navigationDivs[i]; + this.navigationDivs['wrapper'].appendChild(this.navigationDivs[navigationDivs[i]]); - for (i = 0; i < 7; i++) { - // make the regex if we don't have it already - if (!this._weekdaysParse[i]) { - mom = moment([2000, 1]).day(i); - regex = '^' + this.weekdays(mom, '') + '|^' + this.weekdaysShort(mom, '') + '|^' + this.weekdaysMin(mom, ''); - this._weekdaysParse[i] = new RegExp(regex.replace('.', ''), 'i'); - } - // test the regex - if (this._weekdaysParse[i].test(weekdayName)) { - return i; - } - } - }, + var hammer = Hammer(this.navigationDivs[navigationDivs[i]], {prevent_default: true}); + hammer.on('touch', this[navigationDivActions[i]].bind(this)); + this.navigationHammers._new.push(hammer); + } - _longDateFormat : { - LTS : 'h:mm:ss A', - LT : 'h:mm A', - L : 'MM/DD/YYYY', - LL : 'MMMM D, YYYY', - LLL : 'MMMM D, YYYY LT', - LLLL : 'dddd, MMMM D, YYYY LT' - }, - longDateFormat : function (key) { - var output = this._longDateFormat[key]; - if (!output && this._longDateFormat[key.toUpperCase()]) { - output = this._longDateFormat[key.toUpperCase()].replace(/MMMM|MM|DD|dddd/g, function (val) { - return val.slice(1); - }); - this._longDateFormat[key] = output; - } - return output; - }, + this._navigationReleaseOverload = this._stopMovement; - isPM : function (input) { - // IE8 Quirks Mode & IE7 Standards Mode do not allow accessing strings like arrays - // Using charAt should be more compatible. - return ((input + '').toLowerCase().charAt(0) === 'p'); - }, + this.navigationHammers.existing = this.navigationHammers._new; + }; - _meridiemParse : /[ap]\.?m?\.?/i, - meridiem : function (hours, minutes, isLower) { - if (hours > 11) { - return isLower ? 'pm' : 'PM'; - } else { - return isLower ? 'am' : 'AM'; - } - }, + /** + * this stops all movement induced by the navigation buttons + * + * @private + */ + exports._zoomExtent = function(event) { + this.zoomExtent({duration:700}); + event.stopPropagation(); + }; - _calendar : { - sameDay : '[Today at] LT', - nextDay : '[Tomorrow at] LT', - nextWeek : 'dddd [at] LT', - lastDay : '[Yesterday at] LT', - lastWeek : '[Last] dddd [at] LT', - sameElse : 'L' - }, - calendar : function (key, mom, now) { - var output = this._calendar[key]; - return typeof output === 'function' ? output.apply(mom, [now]) : output; - }, + /** + * this stops all movement induced by the navigation buttons + * + * @private + */ + exports._stopMovement = function() { + this._xStopMoving(); + this._yStopMoving(); + this._stopZoom(); + }; - _relativeTime : { - future : 'in %s', - past : '%s ago', - s : 'a few seconds', - m : 'a minute', - mm : '%d minutes', - h : 'an hour', - hh : '%d hours', - d : 'a day', - dd : '%d days', - M : 'a month', - MM : '%d months', - y : 'a year', - yy : '%d years' - }, - relativeTime : function (number, withoutSuffix, string, isFuture) { - var output = this._relativeTime[string]; - return (typeof output === 'function') ? - output(number, withoutSuffix, string, isFuture) : - output.replace(/%d/i, number); - }, + /** + * move the screen up + * By using the increments, instead of adding a fixed number to the translation, we keep fluent and + * instant movement. The onKeypress event triggers immediately, then pauses, then triggers frequently + * To avoid this behaviour, we do the translation in the start loop. + * + * @private + */ + exports._moveUp = function(event) { + this.yIncrement = this.constants.keyboard.speed.y; + this.start(); // if there is no node movement, the calculation wont be done + event.preventDefault(); + }; - pastFuture : function (diff, output) { - var format = this._relativeTime[diff > 0 ? 'future' : 'past']; - return typeof format === 'function' ? format(output) : format.replace(/%s/i, output); - }, - ordinal : function (number) { - return this._ordinal.replace('%d', number); - }, - _ordinal : '%d', - _ordinalParse : /\d{1,2}/, + /** + * move the screen down + * @private + */ + exports._moveDown = function(event) { + this.yIncrement = -this.constants.keyboard.speed.y; + this.start(); // if there is no node movement, the calculation wont be done + event.preventDefault(); + }; - preparse : function (string) { - return string; - }, - postformat : function (string) { - return string; - }, + /** + * move the screen left + * @private + */ + exports._moveLeft = function(event) { + this.xIncrement = this.constants.keyboard.speed.x; + this.start(); // if there is no node movement, the calculation wont be done + event.preventDefault(); + }; - week : function (mom) { - return weekOfYear(mom, this._week.dow, this._week.doy).week; - }, - _week : { - dow : 0, // Sunday is the first day of the week. - doy : 6 // The week that contains Jan 1st is the first week of the year. - }, + /** + * move the screen right + * @private + */ + exports._moveRight = function(event) { + this.xIncrement = -this.constants.keyboard.speed.y; + this.start(); // if there is no node movement, the calculation wont be done + event.preventDefault(); + }; - firstDayOfWeek : function () { - return this._week.dow; - }, - firstDayOfYear : function () { - return this._week.doy; - }, + /** + * Zoom in, using the same method as the movement. + * @private + */ + exports._zoomIn = function(event) { + this.zoomIncrement = this.constants.keyboard.speed.zoom; + this.start(); // if there is no node movement, the calculation wont be done + event.preventDefault(); + }; - _invalidDate: 'Invalid date', - invalidDate: function () { - return this._invalidDate; - } - }); - /************************************ - Formatting - ************************************/ + /** + * Zoom out + * @private + */ + exports._zoomOut = function(event) { + this.zoomIncrement = -this.constants.keyboard.speed.zoom; + this.start(); // if there is no node movement, the calculation wont be done + event.preventDefault(); + }; - function removeFormattingTokens(input) { - if (input.match(/\[[\s\S]/)) { - return input.replace(/^\[|\]$/g, ''); - } - return input.replace(/\\/g, ''); - } + /** + * Stop zooming and unhighlight the zoom controls + * @private + */ + exports._stopZoom = function(event) { + this.zoomIncrement = 0; + event && event.preventDefault(); + }; - function makeFormatFunction(format) { - var array = format.match(formattingTokens), i, length; - for (i = 0, length = array.length; i < length; i++) { - if (formatTokenFunctions[array[i]]) { - array[i] = formatTokenFunctions[array[i]]; - } else { - array[i] = removeFormattingTokens(array[i]); - } - } + /** + * Stop moving in the Y direction and unHighlight the up and down + * @private + */ + exports._yStopMoving = function(event) { + this.yIncrement = 0; + event && event.preventDefault(); + }; - return function (mom) { - var output = ''; - for (i = 0; i < length; i++) { - output += array[i] instanceof Function ? array[i].call(mom, format) : array[i]; - } - return output; - }; - } - // format date using native date object - function formatMoment(m, format) { - if (!m.isValid()) { - return m.localeData().invalidDate(); - } + /** + * Stop moving in the X direction and unHighlight left and right. + * @private + */ + exports._xStopMoving = function(event) { + this.xIncrement = 0; + event && event.preventDefault(); + }; - format = expandFormat(format, m.localeData()); - if (!formatFunctions[format]) { - formatFunctions[format] = makeFormatFunction(format); - } +/***/ }, +/* 62 */ +/***/ function(module, exports, __webpack_require__) { - return formatFunctions[format](m); + exports._resetLevels = function() { + for (var nodeId in this.nodes) { + if (this.nodes.hasOwnProperty(nodeId)) { + var node = this.nodes[nodeId]; + if (node.preassignedLevel == false) { + node.level = -1; + node.hierarchyEnumerated = false; + } } + } + }; - function expandFormat(format, locale) { - var i = 5; + /** + * This is the main function to layout the nodes in a hierarchical way. + * It checks if the node details are supplied correctly + * + * @private + */ + exports._setupHierarchicalLayout = function() { + if (this.constants.hierarchicalLayout.enabled == true && this.nodeIndices.length > 0) { + // get the size of the largest hubs and check if the user has defined a level for a node. + var hubsize = 0; + var node, nodeId; + var definedLevel = false; + var undefinedLevel = false; - function replaceLongDateFormatTokens(input) { - return locale.longDateFormat(input) || input; + for (nodeId in this.nodes) { + if (this.nodes.hasOwnProperty(nodeId)) { + node = this.nodes[nodeId]; + if (node.level != -1) { + definedLevel = true; } - - localFormattingTokens.lastIndex = 0; - while (i >= 0 && localFormattingTokens.test(format)) { - format = format.replace(localFormattingTokens, replaceLongDateFormatTokens); - localFormattingTokens.lastIndex = 0; - i -= 1; + else { + undefinedLevel = true; + } + if (hubsize < node.edges.length) { + hubsize = node.edges.length; } + } + } - return format; + // if the user defined some levels but not all, alert and run without hierarchical layout + if (undefinedLevel == true && definedLevel == true) { + throw new Error("To use the hierarchical layout, nodes require either no predefined levels or levels have to be defined for all nodes."); + this.zoomExtent(undefined,true,this.constants.clustering.enabled); + if (!this.constants.clustering.enabled) { + this.start(); + } } + else { + // setup the system to use hierarchical method. + this._changeConstants(); + // define levels if undefined by the users. Based on hubsize + if (undefinedLevel == true) { + if (this.constants.hierarchicalLayout.layout == "hubsize") { + this._determineLevels(hubsize); + } + else { + this._determineLevelsDirected(false); + } - /************************************ - Parsing - ************************************/ + } + // check the distribution of the nodes per level. + var distribution = this._getDistribution(); + // place the nodes on the canvas. This also stablilizes the system. + this._placeNodesByHierarchy(distribution); - // get the regex to find the next token - function getParseRegexForToken(token, config) { - var a, strict = config._strict; - switch (token) { - case 'Q': - return parseTokenOneDigit; - case 'DDDD': - return parseTokenThreeDigits; - case 'YYYY': - case 'GGGG': - case 'gggg': - return strict ? parseTokenFourDigits : parseTokenOneToFourDigits; - case 'Y': - case 'G': - case 'g': - return parseTokenSignedNumber; - case 'YYYYYY': - case 'YYYYY': - case 'GGGGG': - case 'ggggg': - return strict ? parseTokenSixDigits : parseTokenOneToSixDigits; - case 'S': - if (strict) { - return parseTokenOneDigit; - } - /* falls through */ - case 'SS': - if (strict) { - return parseTokenTwoDigits; - } - /* falls through */ - case 'SSS': - if (strict) { - return parseTokenThreeDigits; - } - /* falls through */ - case 'DDD': - return parseTokenOneToThreeDigits; - case 'MMM': - case 'MMMM': - case 'dd': - case 'ddd': - case 'dddd': - return parseTokenWord; - case 'a': - case 'A': - return config._locale._meridiemParse; - case 'x': - return parseTokenOffsetMs; - case 'X': - return parseTokenTimestampMs; - case 'Z': - case 'ZZ': - return parseTokenTimezone; - case 'T': - return parseTokenT; - case 'SSSS': - return parseTokenDigits; - case 'MM': - case 'DD': - case 'YY': - case 'GG': - case 'gg': - case 'HH': - case 'hh': - case 'mm': - case 'ss': - case 'ww': - case 'WW': - return strict ? parseTokenTwoDigits : parseTokenOneOrTwoDigits; - case 'M': - case 'D': - case 'd': - case 'H': - case 'h': - case 'm': - case 's': - case 'w': - case 'W': - case 'e': - case 'E': - return parseTokenOneOrTwoDigits; - case 'Do': - return strict ? config._locale._ordinalParse : config._locale._ordinalParseLenient; - default : - a = new RegExp(regexpEscape(unescapeFormat(token.replace('\\', '')), 'i')); - return a; - } + // start the simulation. + this.start(); } + } + }; - function utcOffsetFromString(string) { - string = string || ''; - var possibleTzMatches = (string.match(parseTokenTimezone) || []), - tzChunk = possibleTzMatches[possibleTzMatches.length - 1] || [], - parts = (tzChunk + '').match(parseTimezoneChunker) || ['-', 0, 0], - minutes = +(parts[1] * 60) + toInt(parts[2]); - return parts[0] === '+' ? minutes : -minutes; - } + /** + * This function places the nodes on the canvas based on the hierarchial distribution. + * + * @param {Object} distribution | obtained by the function this._getDistribution() + * @private + */ + exports._placeNodesByHierarchy = function(distribution) { + var nodeId, node; - // function to convert string input to date - function addTimeToArrayFromToken(token, input, config) { - var a, datePartArray = config._a; + // start placing all the level 0 nodes first. Then recursively position their branches. + for (var level in distribution) { + if (distribution.hasOwnProperty(level)) { - switch (token) { - // QUARTER - case 'Q': - if (input != null) { - datePartArray[MONTH] = (toInt(input) - 1) * 3; - } - break; - // MONTH - case 'M' : // fall through to MM - case 'MM' : - if (input != null) { - datePartArray[MONTH] = toInt(input) - 1; - } - break; - case 'MMM' : // fall through to MMMM - case 'MMMM' : - a = config._locale.monthsParse(input, token, config._strict); - // if we didn't find a month name, mark the date as invalid. - if (a != null) { - datePartArray[MONTH] = a; - } else { - config._pf.invalidMonth = input; - } - break; - // DAY OF MONTH - case 'D' : // fall through to DD - case 'DD' : - if (input != null) { - datePartArray[DATE] = toInt(input); - } - break; - case 'Do' : - if (input != null) { - datePartArray[DATE] = toInt(parseInt( - input.match(/\d{1,2}/)[0], 10)); - } - break; - // DAY OF YEAR - case 'DDD' : // fall through to DDDD - case 'DDDD' : - if (input != null) { - config._dayOfYear = toInt(input); - } + for (nodeId in distribution[level].nodes) { + if (distribution[level].nodes.hasOwnProperty(nodeId)) { + node = distribution[level].nodes[nodeId]; + if (this.constants.hierarchicalLayout.direction == "UD" || this.constants.hierarchicalLayout.direction == "DU") { + if (node.xFixed) { + node.x = distribution[level].minPos; + node.xFixed = false; - break; - // YEAR - case 'YY' : - datePartArray[YEAR] = moment.parseTwoDigitYear(input); - break; - case 'YYYY' : - case 'YYYYY' : - case 'YYYYYY' : - datePartArray[YEAR] = toInt(input); - break; - // AM / PM - case 'a' : // fall through to A - case 'A' : - config._meridiem = input; - // config._isPm = config._locale.isPM(input); - break; - // HOUR - case 'h' : // fall through to hh - case 'hh' : - config._pf.bigHour = true; - /* falls through */ - case 'H' : // fall through to HH - case 'HH' : - datePartArray[HOUR] = toInt(input); - break; - // MINUTE - case 'm' : // fall through to mm - case 'mm' : - datePartArray[MINUTE] = toInt(input); - break; - // SECOND - case 's' : // fall through to ss - case 'ss' : - datePartArray[SECOND] = toInt(input); - break; - // MILLISECOND - case 'S' : - case 'SS' : - case 'SSS' : - case 'SSSS' : - datePartArray[MILLISECOND] = toInt(('0.' + input) * 1000); - break; - // UNIX OFFSET (MILLISECONDS) - case 'x': - config._d = new Date(toInt(input)); - break; - // UNIX TIMESTAMP WITH MS - case 'X': - config._d = new Date(parseFloat(input) * 1000); - break; - // TIMEZONE - case 'Z' : // fall through to ZZ - case 'ZZ' : - config._useUTC = true; - config._tzm = utcOffsetFromString(input); - break; - // WEEKDAY - human - case 'dd': - case 'ddd': - case 'dddd': - a = config._locale.weekdaysParse(input); - // if we didn't get a weekday name, mark the date as invalid - if (a != null) { - config._w = config._w || {}; - config._w['d'] = a; - } else { - config._pf.invalidWeekday = input; + distribution[level].minPos += distribution[level].nodeSpacing; } - break; - // WEEK, WEEK DAY - numeric - case 'w': - case 'ww': - case 'W': - case 'WW': - case 'd': - case 'e': - case 'E': - token = token.substr(0, 1); - /* falls through */ - case 'gggg': - case 'GGGG': - case 'GGGGG': - token = token.substr(0, 2); - if (input) { - config._w = config._w || {}; - config._w[token] = toInt(input); + } + else { + if (node.yFixed) { + node.y = distribution[level].minPos; + node.yFixed = false; + + distribution[level].minPos += distribution[level].nodeSpacing; } - break; - case 'gg': - case 'GG': - config._w = config._w || {}; - config._w[token] = moment.parseTwoDigitYear(input); + } + this._placeBranchNodes(node.edges,node.id,distribution,node.level); } + } } + } - function dayOfYearFromWeekInfo(config) { - var w, weekYear, week, weekday, dow, doy, temp; - - w = config._w; - if (w.GG != null || w.W != null || w.E != null) { - dow = 1; - doy = 4; - - // TODO: We need to take the current isoWeekYear, but that depends on - // how we interpret now (local, utc, fixed offset). So create - // a now version of current config (take local/utc/offset flags, and - // create now). - weekYear = dfl(w.GG, config._a[YEAR], weekOfYear(moment(), 1, 4).year); - week = dfl(w.W, 1); - weekday = dfl(w.E, 1); - } else { - dow = config._locale._week.dow; - doy = config._locale._week.doy; + // stabilize the system after positioning. This function calls zoomExtent. + this._stabilize(); + }; - weekYear = dfl(w.gg, config._a[YEAR], weekOfYear(moment(), dow, doy).year); - week = dfl(w.w, 1); - if (w.d != null) { - // weekday -- low day numbers are considered next week - weekday = w.d; - if (weekday < dow) { - ++week; - } - } else if (w.e != null) { - // local weekday -- counting starts from begining of week - weekday = w.e + dow; - } else { - // default to begining of week - weekday = dow; - } - } - temp = dayOfYearFromWeeks(weekYear, week, weekday, doy, dow); + /** + * This function get the distribution of levels based on hubsize + * + * @returns {Object} + * @private + */ + exports._getDistribution = function() { + var distribution = {}; + var nodeId, node, level; - config._a[YEAR] = temp.year; - config._dayOfYear = temp.dayOfYear; + // we fix Y because the hierarchy is vertical, we fix X so we do not give a node an x position for a second time. + // the fix of X is removed after the x value has been set. + for (nodeId in this.nodes) { + if (this.nodes.hasOwnProperty(nodeId)) { + node = this.nodes[nodeId]; + node.xFixed = true; + node.yFixed = true; + if (this.constants.hierarchicalLayout.direction == "UD" || this.constants.hierarchicalLayout.direction == "DU") { + node.y = this.constants.hierarchicalLayout.levelSeparation*node.level; + } + else { + node.x = this.constants.hierarchicalLayout.levelSeparation*node.level; + } + if (distribution[node.level] === undefined) { + distribution[node.level] = {amount: 0, nodes: {}, minPos:0, nodeSpacing:0}; + } + distribution[node.level].amount += 1; + distribution[node.level].nodes[nodeId] = node; } + } - // convert an array to a date. - // the array should mirror the parameters below - // note: all values past the year are optional and will default to the lowest possible value. - // [year, month, day , hour, minute, second, millisecond] - function dateFromConfig(config) { - var i, date, input = [], currentDate, yearToUse; + // determine the largest amount of nodes of all levels + var maxCount = 0; + for (level in distribution) { + if (distribution.hasOwnProperty(level)) { + if (maxCount < distribution[level].amount) { + maxCount = distribution[level].amount; + } + } + } - if (config._d) { - return; - } + // set the initial position and spacing of each nodes accordingly + for (level in distribution) { + if (distribution.hasOwnProperty(level)) { + distribution[level].nodeSpacing = (maxCount + 1) * this.constants.hierarchicalLayout.nodeSpacing; + distribution[level].nodeSpacing /= (distribution[level].amount + 1); + distribution[level].minPos = distribution[level].nodeSpacing - (0.5 * (distribution[level].amount + 1) * distribution[level].nodeSpacing); + } + } - currentDate = currentDateArray(config); + return distribution; + }; - //compute day of the year from weeks and weekdays - if (config._w && config._a[DATE] == null && config._a[MONTH] == null) { - dayOfYearFromWeekInfo(config); - } - //if the day of the year is set, figure out what it is - if (config._dayOfYear) { - yearToUse = dfl(config._a[YEAR], currentDate[YEAR]); + /** + * this function allocates nodes in levels based on the recursive branching from the largest hubs. + * + * @param hubsize + * @private + */ + exports._determineLevels = function(hubsize) { + var nodeId, node; - if (config._dayOfYear > daysInYear(yearToUse)) { - config._pf._overflowDayOfYear = true; - } + // determine hubs + for (nodeId in this.nodes) { + if (this.nodes.hasOwnProperty(nodeId)) { + node = this.nodes[nodeId]; + if (node.edges.length == hubsize) { + node.level = 0; + } + } + } - date = makeUTCDate(yearToUse, 0, config._dayOfYear); - config._a[MONTH] = date.getUTCMonth(); - config._a[DATE] = date.getUTCDate(); - } + // branch from hubs + for (nodeId in this.nodes) { + if (this.nodes.hasOwnProperty(nodeId)) { + node = this.nodes[nodeId]; + if (node.level == 0) { + this._setLevel(1,node.edges,node.id); + } + } + } + }; - // Default to current date. - // * if no year, month, day of month are given, default to today - // * if day of month is given, default month and year - // * if month is given, default only year - // * if year is given, don't default anything - for (i = 0; i < 3 && config._a[i] == null; ++i) { - config._a[i] = input[i] = currentDate[i]; - } - // Zero out whatever was not defaulted, including time - for (; i < 7; i++) { - config._a[i] = input[i] = (config._a[i] == null) ? (i === 2 ? 1 : 0) : config._a[i]; - } - // Check for 24:00:00.000 - if (config._a[HOUR] === 24 && - config._a[MINUTE] === 0 && - config._a[SECOND] === 0 && - config._a[MILLISECOND] === 0) { - config._nextDay = true; - config._a[HOUR] = 0; - } + /** + * this function allocates nodes in levels based on the direction of the edges + * + * @param hubsize + * @private + */ + exports._determineLevelsDirected = function() { + var nodeId, node, firstNode; + var minLevel = 10000; - config._d = (config._useUTC ? makeUTCDate : makeDate).apply(null, input); - // Apply timezone offset from input. The actual utcOffset can be changed - // with parseZone. - if (config._tzm != null) { - config._d.setUTCMinutes(config._d.getUTCMinutes() - config._tzm); - } + // set first node to source + firstNode = this.nodes[this.nodeIndices[0]]; + firstNode.level = minLevel; + this._setLevelDirected(minLevel,firstNode.edges,firstNode.id); - if (config._nextDay) { - config._a[HOUR] = 24; - } + // get the minimum level + for (nodeId in this.nodes) { + if (this.nodes.hasOwnProperty(nodeId)) { + node = this.nodes[nodeId]; + minLevel = node.level < minLevel ? node.level : minLevel; } + } - function dateFromObject(config) { - var normalizedInput; + // subtract the minimum from the set so we have a range starting from 0 + for (nodeId in this.nodes) { + if (this.nodes.hasOwnProperty(nodeId)) { + node = this.nodes[nodeId]; + node.level -= minLevel; + } + } + }; - if (config._d) { - return; - } - normalizedInput = normalizeObjectUnits(config._i); - config._a = [ - normalizedInput.year, - normalizedInput.month, - normalizedInput.day || normalizedInput.date, - normalizedInput.hour, - normalizedInput.minute, - normalizedInput.second, - normalizedInput.millisecond - ]; + /** + * Since hierarchical layout does not support: + * - smooth curves (based on the physics), + * - clustering (based on dynamic node counts) + * + * We disable both features so there will be no problems. + * + * @private + */ + exports._changeConstants = function() { + this.constants.clustering.enabled = false; + this.constants.physics.barnesHut.enabled = false; + this.constants.physics.hierarchicalRepulsion.enabled = true; + this._loadSelectedForceSolver(); + if (this.constants.smoothCurves.enabled == true) { + this.constants.smoothCurves.dynamic = false; + } + this._configureSmoothCurves(); - dateFromConfig(config); - } + var config = this.constants.hierarchicalLayout; + config.levelSeparation = Math.abs(config.levelSeparation); + if (config.direction == "RL" || config.direction == "DU") { + config.levelSeparation *= -1; + } - function currentDateArray(config) { - var now = new Date(); - if (config._useUTC) { - return [ - now.getUTCFullYear(), - now.getUTCMonth(), - now.getUTCDate() - ]; - } else { - return [now.getFullYear(), now.getMonth(), now.getDate()]; - } + if (config.direction == "RL" || config.direction == "LR") { + if (this.constants.smoothCurves.enabled == true) { + this.constants.smoothCurves.type = "vertical"; + } + } + else { + if (this.constants.smoothCurves.enabled == true) { + this.constants.smoothCurves.type = "horizontal"; } + } + }; - // date from string and format string - function makeDateFromStringAndFormat(config) { - if (config._f === moment.ISO_8601) { - parseISO(config); - return; - } - config._a = []; - config._pf.empty = true; + /** + * This is a recursively called function to enumerate the branches from the largest hubs and place the nodes + * on a X position that ensures there will be no overlap. + * + * @param edges + * @param parentId + * @param distribution + * @param parentLevel + * @private + */ + exports._placeBranchNodes = function(edges, parentId, distribution, parentLevel) { + for (var i = 0; i < edges.length; i++) { + var childNode = null; + if (edges[i].toId == parentId) { + childNode = edges[i].from; + } + else { + childNode = edges[i].to; + } - // This array is used to make a Date, either with `new Date` or `Date.UTC` - var string = '' + config._i, - i, parsedInput, tokens, token, skipped, - stringLength = string.length, - totalParsedInputLength = 0; + // if a node is conneceted to another node on the same level (or higher (means lower level))!, this is not handled here. + var nodeMoved = false; + if (this.constants.hierarchicalLayout.direction == "UD" || this.constants.hierarchicalLayout.direction == "DU") { + if (childNode.xFixed && childNode.level > parentLevel) { + childNode.xFixed = false; + childNode.x = distribution[childNode.level].minPos; + nodeMoved = true; + } + } + else { + if (childNode.yFixed && childNode.level > parentLevel) { + childNode.yFixed = false; + childNode.y = distribution[childNode.level].minPos; + nodeMoved = true; + } + } - tokens = expandFormat(config._f, config._locale).match(formattingTokens) || []; + if (nodeMoved == true) { + distribution[childNode.level].minPos += distribution[childNode.level].nodeSpacing; + if (childNode.edges.length > 1) { + this._placeBranchNodes(childNode.edges,childNode.id,distribution,childNode.level); + } + } + } + }; - for (i = 0; i < tokens.length; i++) { - token = tokens[i]; - parsedInput = (string.match(getParseRegexForToken(token, config)) || [])[0]; - if (parsedInput) { - skipped = string.substr(0, string.indexOf(parsedInput)); - if (skipped.length > 0) { - config._pf.unusedInput.push(skipped); - } - string = string.slice(string.indexOf(parsedInput) + parsedInput.length); - totalParsedInputLength += parsedInput.length; - } - // don't parse if it's not a known token - if (formatTokenFunctions[token]) { - if (parsedInput) { - config._pf.empty = false; - } - else { - config._pf.unusedTokens.push(token); - } - addTimeToArrayFromToken(token, parsedInput, config); - } - else if (config._strict && !parsedInput) { - config._pf.unusedTokens.push(token); - } - } - // add remaining unparsed input length to the string - config._pf.charsLeftOver = stringLength - totalParsedInputLength; - if (string.length > 0) { - config._pf.unusedInput.push(string); - } + /** + * this function is called recursively to enumerate the barnches of the largest hubs and give each node a level. + * + * @param level + * @param edges + * @param parentId + * @private + */ + exports._setLevel = function(level, edges, parentId) { + for (var i = 0; i < edges.length; i++) { + var childNode = null; + if (edges[i].toId == parentId) { + childNode = edges[i].from; + } + else { + childNode = edges[i].to; + } + if (childNode.level == -1 || childNode.level > level) { + childNode.level = level; + if (childNode.edges.length > 1) { + this._setLevel(level+1, childNode.edges, childNode.id); + } + } + } + }; - // clear _12h flag if hour is <= 12 - if (config._pf.bigHour === true && config._a[HOUR] <= 12) { - config._pf.bigHour = undefined; - } - // handle meridiem - config._a[HOUR] = meridiemFixWrap(config._locale, config._a[HOUR], - config._meridiem); - dateFromConfig(config); - checkOverflow(config); + + /** + * this function is called recursively to enumerate the branched of the first node and give each node a level based on edge direction + * + * @param level + * @param edges + * @param parentId + * @private + */ + exports._setLevelDirected = function(level, edges, parentId) { + this.nodes[parentId].hierarchyEnumerated = true; + var childNode, direction; + for (var i = 0; i < edges.length; i++) { + direction = 1; + if (edges[i].toId == parentId) { + childNode = edges[i].from; + direction = -1; + } + else { + childNode = edges[i].to; + } + if (childNode.level == -1) { + childNode.level = level + direction; } + } - function unescapeFormat(s) { - return s.replace(/\\(\[)|\\(\])|\[([^\]\[]*)\]|\\(.)/g, function (matched, p1, p2, p3, p4) { - return p1 || p2 || p3 || p4; - }); + for (var i = 0; i < edges.length; i++) { + if (edges[i].toId == parentId) {childNode = edges[i].from;} + else {childNode = edges[i].to;} + + if (childNode.edges.length > 1 && childNode.hierarchyEnumerated === false) { + this._setLevelDirected(childNode.level, childNode.edges, childNode.id); } + } + }; - // Code from http://stackoverflow.com/questions/3561493/is-there-a-regexp-escape-function-in-javascript - function regexpEscape(s) { - return s.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&'); + + /** + * Unfix nodes + * + * @private + */ + exports._restoreNodes = function() { + for (var nodeId in this.nodes) { + if (this.nodes.hasOwnProperty(nodeId)) { + this.nodes[nodeId].xFixed = false; + this.nodes[nodeId].yFixed = false; } + } + }; - // date from string and array of format strings - function makeDateFromStringAndArray(config) { - var tempConfig, - bestMoment, - scoreToBeat, - i, - currentScore; +/***/ }, +/* 63 */ +/***/ function(module, exports, __webpack_require__) { - if (config._f.length === 0) { - config._pf.invalidFormat = true; - config._d = new Date(NaN); - return; - } + var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;"use strict"; + /** + * Created by Alex on 11/6/2014. + */ - for (i = 0; i < config._f.length; i++) { - currentScore = 0; - tempConfig = copyConfig({}, config); - if (config._useUTC != null) { - tempConfig._useUTC = config._useUTC; - } - tempConfig._pf = defaultParsingFlags(); - tempConfig._f = config._f[i]; - makeDateFromStringAndFormat(tempConfig); + // 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 () { - if (!isValid(tempConfig)) { - continue; - } + function keycharm(options) { + var preventDefault = options && options.preventDefault || false; - // if there is any input that was not parsed add a penalty for that format - currentScore += tempConfig._pf.charsLeftOver; + var container = options && options.container || window; + var _exportFunctions = {}; + var _bound = {keydown:{}, keyup:{}}; + var _keys = {}; + var i; - //or tokens - currentScore += tempConfig._pf.unusedTokens.length * 10; + // 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};} - tempConfig._pf.score = currentScore; + // 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}; - if (scoreToBeat == null || currentScore < scoreToBeat) { - scoreToBeat = currentScore; - bestMoment = tempConfig; - } - } - extend(config, bestMoment || tempConfig); - } - // date from iso format - function parseISO(config) { - var i, l, - string = config._i, - match = isoRegex.exec(string); + var down = function(event) {handleEvent(event,'keydown');}; + var up = function(event) {handleEvent(event,'keyup');}; - if (match) { - config._pf.iso = true; - for (i = 0, l = isoDates.length; i < l; i++) { - if (isoDates[i][1].exec(string)) { - // match[5] should be 'T' or undefined - config._f = isoDates[i][0] + (match[6] || ' '); - break; - } - } - for (i = 0, l = isoTimes.length; i < l; i++) { - if (isoTimes[i][1].exec(string)) { - config._f += isoTimes[i][0]; - break; - } - } - if (string.match(parseTokenTimezone)) { - config._f += 'Z'; - } - makeDateFromStringAndFormat(config); - } else { - config._isValid = false; + // 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); + } } - } - // date from iso format or fallback - function makeDateFromString(config) { - parseISO(config); - if (config._isValid === false) { - delete config._isValid; - moment.createFromInputFallback(config); + if (preventDefault == true) { + event.preventDefault(); } - } + } + }; - function map(arr, fn) { - var res = [], i; - for (i = 0; i < arr.length; ++i) { - res.push(fn(arr[i], i)); + // bind a key to a callback + _exportFunctions.bind = function(key, callback, type) { + if (type === undefined) { + type = 'keydown'; + } + if (_keys[key] === undefined) { + throw new Error("unsupported key: " + key); + } + if (_bound[type][_keys[key].code] === undefined) { + _bound[type][_keys[key].code] = []; + } + _bound[type][_keys[key].code].push({fn:callback, shift:_keys[key].shift}); + }; + + + // bind all keys to a call back (demo purposes) + _exportFunctions.bindAll = function(callback, type) { + if (type === undefined) { + type = 'keydown'; + } + for (var key in _keys) { + if (_keys.hasOwnProperty(key)) { + _exportFunctions.bind(key,callback,type); } - return res; - } + } + }; - function makeDateFromInput(config) { - var input = config._i, matched; - if (input === undefined) { - config._d = new Date(); - } else if (isDate(input)) { - config._d = new Date(+input); - } else if ((matched = aspNetJsonRegex.exec(input)) !== null) { - config._d = new Date(+matched[1]); - } else if (typeof input === 'string') { - makeDateFromString(config); - } else if (isArray(input)) { - config._a = map(input.slice(0), function (obj) { - return parseInt(obj, 10); - }); - dateFromConfig(config); - } else if (typeof(input) === 'object') { - dateFromObject(config); - } else if (typeof(input) === 'number') { - // from milliseconds - config._d = new Date(input); - } else { - moment.createFromInputFallback(config); + // 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; + } } - } - - function makeDate(y, m, d, h, M, s, ms) { - //can't just apply() to create a date: - //http://stackoverflow.com/questions/181348/instantiating-a-javascript-object-by-calling-prototype-constructor-apply - var date = new Date(y, m, d, h, M, s, ms); + } + return "unknown key, currently not supported"; + }; - //the date constructor doesn't accept years < 1970 - if (y < 1970) { - date.setFullYear(y); + // 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]); + } + } } - return date; - } + _bound[type][_keys[key].code] = newBindings; + } + else { + _bound[type][_keys[key].code] = []; + } + }; - function makeUTCDate(y) { - var date = new Date(Date.UTC.apply(null, arguments)); - if (y < 1970) { - date.setUTCFullYear(y); - } - return date; - } + // reset all bound variables. + _exportFunctions.reset = function() { + _bound = {keydown:{}, keyup:{}}; + }; - function parseWeekday(input, locale) { - if (typeof input === 'string') { - if (!isNaN(input)) { - input = parseInt(input, 10); - } - else { - input = locale.weekdaysParse(input); - if (typeof input !== 'number') { - return null; - } - } - } - return input; - } + // unbind all listeners and reset all variables. + _exportFunctions.destroy = function() { + _bound = {keydown:{}, keyup:{}}; + container.removeEventListener('keydown', down, true); + container.removeEventListener('keyup', up, true); + }; - /************************************ - Relative Time - ************************************/ + // create listeners. + container.addEventListener('keydown',down,true); + container.addEventListener('keyup',up,true); + // return the public functions. + return _exportFunctions; + } - // helper function for moment.fn.from, moment.fn.fromNow, and moment.duration.fn.humanize - function substituteTimeAgo(string, number, withoutSuffix, isFuture, locale) { - return locale.relativeTime(number || 1, !!withoutSuffix, string, isFuture); - } + return keycharm; + })); - function relativeTime(posNegDuration, withoutSuffix, locale) { - var duration = moment.duration(posNegDuration).abs(), - seconds = round(duration.as('s')), - minutes = round(duration.as('m')), - hours = round(duration.as('h')), - days = round(duration.as('d')), - months = round(duration.as('M')), - years = round(duration.as('y')), - args = seconds < relativeTimeThresholds.s && ['s', seconds] || - minutes === 1 && ['m'] || - minutes < relativeTimeThresholds.m && ['mm', minutes] || - hours === 1 && ['h'] || - hours < relativeTimeThresholds.h && ['hh', hours] || - days === 1 && ['d'] || - days < relativeTimeThresholds.d && ['dd', days] || - months === 1 && ['M'] || - months < relativeTimeThresholds.M && ['MM', months] || - years === 1 && ['y'] || ['yy', years]; - args[2] = withoutSuffix; - args[3] = +posNegDuration > 0; - args[4] = locale; - return substituteTimeAgo.apply({}, args); - } +/***/ }, +/* 64 */ +/***/ function(module, exports, __webpack_require__) { - /************************************ - Week of Year - ************************************/ + var __WEBPACK_AMD_DEFINE_RESULT__;/*! Hammer.JS - v1.1.3 - 2014-05-20 + * http://eightmedia.github.io/hammer.js + * + * Copyright (c) 2014 Jorik Tangelder ; + * Licensed under the MIT license */ + (function(window, undefined) { + 'use strict'; - // firstDayOfWeek 0 = sun, 6 = sat - // the day of the week that starts the week - // (usually sunday or monday) - // firstDayOfWeekOfYear 0 = sun, 6 = sat - // the first week is the week that contains the first - // of this day of the week - // (eg. ISO weeks use thursday (4)) - function weekOfYear(mom, firstDayOfWeek, firstDayOfWeekOfYear) { - var end = firstDayOfWeekOfYear - firstDayOfWeek, - daysToDayOfWeek = firstDayOfWeekOfYear - mom.day(), - adjustedMoment; + /** + * @main + * @module hammer + * + * @class Hammer + * @static + */ + /** + * Hammer, use this to create instances + * ```` + * var hammertime = new Hammer(myElement); + * ```` + * + * @method Hammer + * @param {HTMLElement} element + * @param {Object} [options={}] + * @return {Hammer.Instance} + */ + var Hammer = function Hammer(element, options) { + return new Hammer.Instance(element, options || {}); + }; - if (daysToDayOfWeek > end) { - daysToDayOfWeek -= 7; - } + /** + * version, as defined in package.json + * the value will be set at each build + * @property VERSION + * @final + * @type {String} + */ + Hammer.VERSION = '1.1.3'; - if (daysToDayOfWeek < end - 7) { - daysToDayOfWeek += 7; - } + /** + * default settings. + * more settings are defined per gesture at `/gestures`. Each gesture can be disabled/enabled + * by setting it's name (like `swipe`) to false. + * You can set the defaults for all instances by changing this object before creating an instance. + * @example + * ```` + * Hammer.defaults.drag = false; + * Hammer.defaults.behavior.touchAction = 'pan-y'; + * delete Hammer.defaults.behavior.userSelect; + * ```` + * @property defaults + * @type {Object} + */ + Hammer.defaults = { + /** + * this setting object adds styles and attributes to the element to prevent the browser from doing + * its native behavior. The css properties are auto prefixed for the browsers when needed. + * @property defaults.behavior + * @type {Object} + */ + behavior: { + /** + * Disables text selection to improve the dragging gesture. When the value is `none` it also sets + * `onselectstart=false` for IE on the element. Mainly for desktop browsers. + * @property defaults.behavior.userSelect + * @type {String} + * @default 'none' + */ + userSelect: 'none', - adjustedMoment = moment(mom).add(daysToDayOfWeek, 'd'); - return { - week: Math.ceil(adjustedMoment.dayOfYear() / 7), - year: adjustedMoment.year() - }; - } + /** + * Specifies whether and how a given region can be manipulated by the user (for instance, by panning or zooming). + * Used by Chrome 35> and IE10>. By default this makes the element blocking any touch event. + * @property defaults.behavior.touchAction + * @type {String} + * @default: 'pan-y' + */ + touchAction: 'pan-y', - //http://en.wikipedia.org/wiki/ISO_week_date#Calculating_a_date_given_the_year.2C_week_number_and_weekday - function dayOfYearFromWeeks(year, week, weekday, firstDayOfWeekOfYear, firstDayOfWeek) { - var d = makeUTCDate(year, 0, 1).getUTCDay(), daysToAdd, dayOfYear; + /** + * Disables the default callout shown when you touch and hold a touch target. + * On iOS, when you touch and hold a touch target such as a link, Safari displays + * a callout containing information about the link. This property allows you to disable that callout. + * @property defaults.behavior.touchCallout + * @type {String} + * @default 'none' + */ + touchCallout: 'none', - d = d === 0 ? 7 : d; - weekday = weekday != null ? weekday : firstDayOfWeek; - daysToAdd = firstDayOfWeek - d + (d > firstDayOfWeekOfYear ? 7 : 0) - (d < firstDayOfWeek ? 7 : 0); - dayOfYear = 7 * (week - 1) + (weekday - firstDayOfWeek) + daysToAdd + 1; + /** + * Specifies whether zooming is enabled. Used by IE10> + * @property defaults.behavior.contentZooming + * @type {String} + * @default 'none' + */ + contentZooming: 'none', - return { - year: dayOfYear > 0 ? year : year - 1, - dayOfYear: dayOfYear > 0 ? dayOfYear : daysInYear(year - 1) + dayOfYear - }; + /** + * Specifies that an entire element should be draggable instead of its contents. + * Mainly for desktop browsers. + * @property defaults.behavior.userDrag + * @type {String} + * @default 'none' + */ + userDrag: 'none', + + /** + * Overrides the highlight color shown when the user taps a link or a JavaScript + * clickable element in Safari on iPhone. This property obeys the alpha value, if specified. + * + * If you don't specify an alpha value, Safari on iPhone applies a default alpha value + * to the color. To disable tap highlighting, set the alpha value to 0 (invisible). + * If you set the alpha value to 1.0 (opaque), the element is not visible when tapped. + * @property defaults.behavior.tapHighlightColor + * @type {String} + * @default 'rgba(0,0,0,0)' + */ + tapHighlightColor: 'rgba(0,0,0,0)' } + }; - /************************************ - Top Level Functions - ************************************/ + /** + * hammer document where the base events are added at + * @property DOCUMENT + * @type {HTMLElement} + * @default window.document + */ + Hammer.DOCUMENT = document; - function makeMoment(config) { - var input = config._i, - format = config._f, - res; + /** + * detect support for pointer events + * @property HAS_POINTEREVENTS + * @type {Boolean} + */ + Hammer.HAS_POINTEREVENTS = navigator.pointerEnabled || navigator.msPointerEnabled; - config._locale = config._locale || moment.localeData(config._l); + /** + * detect support for touch events + * @property HAS_TOUCHEVENTS + * @type {Boolean} + */ + Hammer.HAS_TOUCHEVENTS = ('ontouchstart' in window); - if (input === null || (format === undefined && input === '')) { - return moment.invalid({nullInput: true}); - } + /** + * detect mobile browsers + * @property IS_MOBILE + * @type {Boolean} + */ + Hammer.IS_MOBILE = /mobile|tablet|ip(ad|hone|od)|android|silk/i.test(navigator.userAgent); - if (typeof input === 'string') { - config._i = input = config._locale.preparse(input); - } + /** + * detect if we want to support mouseevents at all + * @property NO_MOUSEEVENTS + * @type {Boolean} + */ + Hammer.NO_MOUSEEVENTS = (Hammer.HAS_TOUCHEVENTS && Hammer.IS_MOBILE) || Hammer.HAS_POINTEREVENTS; - if (moment.isMoment(input)) { - return new Moment(input, true); - } else if (format) { - if (isArray(format)) { - makeDateFromStringAndArray(config); - } else { - makeDateFromStringAndFormat(config); - } - } else { - makeDateFromInput(config); - } + /** + * interval in which Hammer recalculates current velocity/direction/angle in ms + * @property CALCULATE_INTERVAL + * @type {Number} + * @default 25 + */ + Hammer.CALCULATE_INTERVAL = 25; - res = new Moment(config); - if (res._nextDay) { - // Adding is smart enough around DST - res.add(1, 'd'); - res._nextDay = undefined; - } + /** + * eventtypes per touchevent (start, move, end) are filled by `Event.determineEventTypes` on `setup` + * the object contains the DOM event names per type (`EVENT_START`, `EVENT_MOVE`, `EVENT_END`) + * @property EVENT_TYPES + * @private + * @writeOnce + * @type {Object} + */ + var EVENT_TYPES = {}; - return res; - } + /** + * direction strings, for safe comparisons + * @property DIRECTION_DOWN|LEFT|UP|RIGHT + * @final + * @type {String} + * @default 'down' 'left' 'up' 'right' + */ + var DIRECTION_DOWN = Hammer.DIRECTION_DOWN = 'down'; + var DIRECTION_LEFT = Hammer.DIRECTION_LEFT = 'left'; + var DIRECTION_UP = Hammer.DIRECTION_UP = 'up'; + var DIRECTION_RIGHT = Hammer.DIRECTION_RIGHT = 'right'; - moment = function (input, format, locale, strict) { - var c; + /** + * pointertype strings, for safe comparisons + * @property POINTER_MOUSE|TOUCH|PEN + * @final + * @type {String} + * @default 'mouse' 'touch' 'pen' + */ + var POINTER_MOUSE = Hammer.POINTER_MOUSE = 'mouse'; + var POINTER_TOUCH = Hammer.POINTER_TOUCH = 'touch'; + var POINTER_PEN = Hammer.POINTER_PEN = 'pen'; - if (typeof(locale) === 'boolean') { - strict = locale; - locale = undefined; - } - // object construction must be done this way. - // https://github.com/moment/moment/issues/1423 - c = {}; - c._isAMomentObject = true; - c._i = input; - c._f = format; - c._l = locale; - c._strict = strict; - c._isUTC = false; - c._pf = defaultParsingFlags(); + /** + * eventtypes + * @property EVENT_START|MOVE|END|RELEASE|TOUCH + * @final + * @type {String} + * @default 'start' 'change' 'move' 'end' 'release' 'touch' + */ + var EVENT_START = Hammer.EVENT_START = 'start'; + var EVENT_MOVE = Hammer.EVENT_MOVE = 'move'; + var EVENT_END = Hammer.EVENT_END = 'end'; + var EVENT_RELEASE = Hammer.EVENT_RELEASE = 'release'; + var EVENT_TOUCH = Hammer.EVENT_TOUCH = 'touch'; - return makeMoment(c); - }; + /** + * if the window events are set... + * @property READY + * @writeOnce + * @type {Boolean} + * @default false + */ + Hammer.READY = false; - moment.suppressDeprecationWarnings = false; + /** + * plugins namespace + * @property plugins + * @type {Object} + */ + Hammer.plugins = Hammer.plugins || {}; - moment.createFromInputFallback = deprecate( - 'moment construction falls back to js Date. This is ' + - 'discouraged and will be removed in upcoming major ' + - 'release. Please refer to ' + - 'https://github.com/moment/moment/issues/1407 for more info.', - function (config) { - config._d = new Date(config._i + (config._useUTC ? ' UTC' : '')); - } - ); + /** + * gestures namespace + * see `/gestures` for the definitions + * @property gestures + * @type {Object} + */ + Hammer.gestures = Hammer.gestures || {}; - // Pick a moment m from moments so that m[fn](other) is true for all - // other. This relies on the function fn to be transitive. - // - // moments should either be an array of moment objects or an array, whose - // first element is an array of moment objects. - function pickBy(fn, moments) { - var res, i; - if (moments.length === 1 && isArray(moments[0])) { - moments = moments[0]; - } - if (!moments.length) { - return moment(); - } - res = moments[0]; - for (i = 1; i < moments.length; ++i) { - if (moments[i][fn](res)) { - res = moments[i]; - } - } - return res; + /** + * setup events to detect gestures on the document + * this function is called when creating an new instance + * @private + */ + function setup() { + if(Hammer.READY) { + return; } - moment.min = function () { - var args = [].slice.call(arguments, 0); - - return pickBy('isBefore', args); - }; + // find what eventtypes we add listeners to + Event.determineEventTypes(); - moment.max = function () { - var args = [].slice.call(arguments, 0); + // Register all gestures inside Hammer.gestures + Utils.each(Hammer.gestures, function(gesture) { + Detection.register(gesture); + }); - return pickBy('isAfter', args); - }; + // Add touch events on the document + Event.onTouch(Hammer.DOCUMENT, EVENT_MOVE, Detection.detect); + Event.onTouch(Hammer.DOCUMENT, EVENT_END, Detection.detect); - // creating with utc - moment.utc = function (input, format, locale, strict) { - var c; + // Hammer is ready...! + Hammer.READY = true; + } - if (typeof(locale) === 'boolean') { - strict = locale; - locale = undefined; - } - // object construction must be done this way. - // https://github.com/moment/moment/issues/1423 - c = {}; - c._isAMomentObject = true; - c._useUTC = true; - c._isUTC = true; - c._l = locale; - c._i = input; - c._f = format; - c._strict = strict; - c._pf = defaultParsingFlags(); + /** + * @module hammer + * + * @class Utils + * @static + */ + var Utils = Hammer.utils = { + /** + * extend method, could also be used for cloning when `dest` is an empty object. + * changes the dest object + * @method extend + * @param {Object} dest + * @param {Object} src + * @param {Boolean} [merge=false] do a merge + * @return {Object} dest + */ + extend: function extend(dest, src, merge) { + for(var key in src) { + if(!src.hasOwnProperty(key) || (dest[key] !== undefined && merge)) { + continue; + } + dest[key] = src[key]; + } + return dest; + }, - return makeMoment(c).utc(); - }; + /** + * simple addEventListener wrapper + * @method on + * @param {HTMLElement} element + * @param {String} type + * @param {Function} handler + */ + on: function on(element, type, handler) { + element.addEventListener(type, handler, false); + }, - // creating with unix timestamp (in seconds) - moment.unix = function (input) { - return moment(input * 1000); - }; + /** + * simple removeEventListener wrapper + * @method off + * @param {HTMLElement} element + * @param {String} type + * @param {Function} handler + */ + off: function off(element, type, handler) { + element.removeEventListener(type, handler, false); + }, - // duration - moment.duration = function (input, key) { - var duration = input, - // matching against regexp is expensive, do it on demand - match = null, - sign, - ret, - parseIso, - diffRes; + /** + * forEach over arrays and objects + * @method each + * @param {Object|Array} obj + * @param {Function} iterator + * @param {any} iterator.item + * @param {Number} iterator.index + * @param {Object|Array} iterator.obj the source object + * @param {Object} context value to use as `this` in the iterator + */ + each: function each(obj, iterator, context) { + var i, len; - if (moment.isDuration(input)) { - duration = { - ms: input._milliseconds, - d: input._days, - M: input._months - }; - } else if (typeof input === 'number') { - duration = {}; - if (key) { - duration[key] = input; - } else { - duration.milliseconds = input; + // native forEach on arrays + if('forEach' in obj) { + obj.forEach(iterator, context); + // arrays + } else if(obj.length !== undefined) { + for(i = 0, len = obj.length; i < len; i++) { + if(iterator.call(context, obj[i], i, obj) === false) { + return; + } + } + // objects + } else { + for(i in obj) { + if(obj.hasOwnProperty(i) && + iterator.call(context, obj[i], i, obj) === false) { + return; + } } - } else if (!!(match = aspNetTimeSpanJsonRegex.exec(input))) { - sign = (match[1] === '-') ? -1 : 1; - duration = { - y: 0, - d: toInt(match[DATE]) * sign, - h: toInt(match[HOUR]) * sign, - m: toInt(match[MINUTE]) * sign, - s: toInt(match[SECOND]) * sign, - ms: toInt(match[MILLISECOND]) * sign - }; - } else if (!!(match = isoDurationRegex.exec(input))) { - sign = (match[1] === '-') ? -1 : 1; - parseIso = function (inp) { - // We'd normally use ~~inp for this, but unfortunately it also - // converts floats to ints. - // inp may be undefined, so careful calling replace on it. - var res = inp && parseFloat(inp.replace(',', '.')); - // apply sign while we're at it - return (isNaN(res) ? 0 : res) * sign; - }; - duration = { - y: parseIso(match[2]), - M: parseIso(match[3]), - d: parseIso(match[4]), - h: parseIso(match[5]), - m: parseIso(match[6]), - s: parseIso(match[7]), - w: parseIso(match[8]) - }; - } else if (duration == null) {// checks for null or undefined - duration = {}; - } else if (typeof duration === 'object' && - ('from' in duration || 'to' in duration)) { - diffRes = momentsDifference(moment(duration.from), moment(duration.to)); - - duration = {}; - duration.ms = diffRes.milliseconds; - duration.M = diffRes.months; } + }, - ret = new Duration(duration); + /** + * find if a string contains the string using indexOf + * @method inStr + * @param {String} src + * @param {String} find + * @return {Boolean} found + */ + inStr: function inStr(src, find) { + return src.indexOf(find) > -1; + }, - if (moment.isDuration(input) && hasOwnProp(input, '_locale')) { - ret._locale = input._locale; + /** + * find if a array contains the object using indexOf or a simple polyfill + * @method inArray + * @param {String} src + * @param {String} find + * @return {Boolean|Number} false when not found, or the index + */ + inArray: function inArray(src, find) { + if(src.indexOf) { + var index = src.indexOf(find); + return (index === -1) ? false : index; + } else { + for(var i = 0, len = src.length; i < len; i++) { + if(src[i] === find) { + return i; + } + } + return false; } + }, - return ret; - }; + /** + * convert an array-like object (`arguments`, `touchlist`) to an array + * @method toArray + * @param {Object} obj + * @return {Array} + */ + toArray: function toArray(obj) { + return Array.prototype.slice.call(obj, 0); + }, - // version number - moment.version = VERSION; + /** + * find if a node is in the given parent + * @method hasParent + * @param {HTMLElement} node + * @param {HTMLElement} parent + * @return {Boolean} found + */ + hasParent: function hasParent(node, parent) { + while(node) { + if(node == parent) { + return true; + } + node = node.parentNode; + } + return false; + }, - // default format - moment.defaultFormat = isoFormat; + /** + * get the center of all the touches + * @method getCenter + * @param {Array} touches + * @return {Object} center contains `pageX`, `pageY`, `clientX` and `clientY` properties + */ + getCenter: function getCenter(touches) { + var pageX = [], + pageY = [], + clientX = [], + clientY = [], + min = Math.min, + max = Math.max; - // constant that refers to the ISO standard - moment.ISO_8601 = function () {}; + // no need to loop when only one touch + if(touches.length === 1) { + return { + pageX: touches[0].pageX, + pageY: touches[0].pageY, + clientX: touches[0].clientX, + clientY: touches[0].clientY + }; + } - // Plugins that add properties should also add the key here (null value), - // so we can properly clone ourselves. - moment.momentProperties = momentProperties; + Utils.each(touches, function(touch) { + pageX.push(touch.pageX); + pageY.push(touch.pageY); + clientX.push(touch.clientX); + clientY.push(touch.clientY); + }); - // This function will be called whenever a moment is mutated. - // It is intended to keep the offset in sync with the timezone. - moment.updateOffset = function () {}; + return { + pageX: (min.apply(Math, pageX) + max.apply(Math, pageX)) / 2, + pageY: (min.apply(Math, pageY) + max.apply(Math, pageY)) / 2, + clientX: (min.apply(Math, clientX) + max.apply(Math, clientX)) / 2, + clientY: (min.apply(Math, clientY) + max.apply(Math, clientY)) / 2 + }; + }, - // This function allows you to set a threshold for relative time strings - moment.relativeTimeThreshold = function (threshold, limit) { - if (relativeTimeThresholds[threshold] === undefined) { - return false; - } - if (limit === undefined) { - return relativeTimeThresholds[threshold]; - } - relativeTimeThresholds[threshold] = limit; - return true; - }; + /** + * calculate the velocity between two points. unit is in px per ms. + * @method getVelocity + * @param {Number} deltaTime + * @param {Number} deltaX + * @param {Number} deltaY + * @return {Object} velocity `x` and `y` + */ + getVelocity: function getVelocity(deltaTime, deltaX, deltaY) { + return { + x: Math.abs(deltaX / deltaTime) || 0, + y: Math.abs(deltaY / deltaTime) || 0 + }; + }, - moment.lang = deprecate( - 'moment.lang is deprecated. Use moment.locale instead.', - function (key, value) { - return moment.locale(key, value); - } - ); + /** + * calculate the angle between two coordinates + * @method getAngle + * @param {Touch} touch1 + * @param {Touch} touch2 + * @return {Number} angle + */ + getAngle: function getAngle(touch1, touch2) { + var x = touch2.clientX - touch1.clientX, + y = touch2.clientY - touch1.clientY; - // This function will load locale and then set the global locale. If - // no arguments are passed in, it will simply return the current global - // locale key. - moment.locale = function (key, values) { - var data; - if (key) { - if (typeof(values) !== 'undefined') { - data = moment.defineLocale(key, values); - } - else { - data = moment.localeData(key); - } + return Math.atan2(y, x) * 180 / Math.PI; + }, - if (data) { - moment.duration._locale = moment._locale = data; - } - } + /** + * do a small comparision to get the direction between two touches. + * @method getDirection + * @param {Touch} touch1 + * @param {Touch} touch2 + * @return {String} direction matches `DIRECTION_LEFT|RIGHT|UP|DOWN` + */ + getDirection: function getDirection(touch1, touch2) { + var x = Math.abs(touch1.clientX - touch2.clientX), + y = Math.abs(touch1.clientY - touch2.clientY); - return moment._locale._abbr; - }; + if(x >= y) { + return touch1.clientX - touch2.clientX > 0 ? DIRECTION_LEFT : DIRECTION_RIGHT; + } + return touch1.clientY - touch2.clientY > 0 ? DIRECTION_UP : DIRECTION_DOWN; + }, - moment.defineLocale = function (name, values) { - if (values !== null) { - values.abbr = name; - if (!locales[name]) { - locales[name] = new Locale(); - } - locales[name].set(values); + /** + * calculate the distance between two touches + * @method getDistance + * @param {Touch}touch1 + * @param {Touch} touch2 + * @return {Number} distance + */ + getDistance: function getDistance(touch1, touch2) { + var x = touch2.clientX - touch1.clientX, + y = touch2.clientY - touch1.clientY; - // backwards compat for now: also set the locale - moment.locale(name); + return Math.sqrt((x * x) + (y * y)); + }, - return locales[name]; - } else { - // useful for testing - delete locales[name]; - return null; + /** + * calculate the scale factor between two touchLists + * no scale is 1, and goes down to 0 when pinched together, and bigger when pinched out + * @method getScale + * @param {Array} start array of touches + * @param {Array} end array of touches + * @return {Number} scale + */ + getScale: function getScale(start, end) { + // need two fingers... + if(start.length >= 2 && end.length >= 2) { + return this.getDistance(end[0], end[1]) / this.getDistance(start[0], start[1]); } - }; + return 1; + }, - moment.langData = deprecate( - 'moment.langData is deprecated. Use moment.localeData instead.', - function (key) { - return moment.localeData(key); + /** + * calculate the rotation degrees between two touchLists + * @method getRotation + * @param {Array} start array of touches + * @param {Array} end array of touches + * @return {Number} rotation + */ + getRotation: function getRotation(start, end) { + // need two fingers + if(start.length >= 2 && end.length >= 2) { + return this.getAngle(end[1], end[0]) - this.getAngle(start[1], start[0]); } - ); + return 0; + }, - // returns locale data - moment.localeData = function (key) { - var locale; + /** + * find out if the direction is vertical * + * @method isVertical + * @param {String} direction matches `DIRECTION_UP|DOWN` + * @return {Boolean} is_vertical + */ + isVertical: function isVertical(direction) { + return direction == DIRECTION_UP || direction == DIRECTION_DOWN; + }, - if (key && key._locale && key._locale._abbr) { - key = key._locale._abbr; - } + /** + * set css properties with their prefixes + * @param {HTMLElement} element + * @param {String} prop + * @param {String} value + * @param {Boolean} [toggle=true] + * @return {Boolean} + */ + setPrefixedCss: function setPrefixedCss(element, prop, value, toggle) { + var prefixes = ['', 'Webkit', 'Moz', 'O', 'ms']; + prop = Utils.toCamelCase(prop); - if (!key) { - return moment._locale; - } + for(var i = 0; i < prefixes.length; i++) { + var p = prop; + // prefixes + if(prefixes[i]) { + p = prefixes[i] + p.slice(0, 1).toUpperCase() + p.slice(1); + } - if (!isArray(key)) { - //short-circuit everything else - locale = loadLocale(key); - if (locale) { - return locale; + // test the style + if(p in element.style) { + element.style[p] = (toggle == null || toggle) && value || ''; + break; } - key = [key]; } + }, - return chooseLocale(key); - }; - - // compare moment object - moment.isMoment = function (obj) { - return obj instanceof Moment || - (obj != null && hasOwnProp(obj, '_isAMomentObject')); - }; - - // for typechecking Duration objects - moment.isDuration = function (obj) { - return obj instanceof Duration; - }; + /** + * toggle browser default behavior by setting css properties. + * `userSelect='none'` also sets `element.onselectstart` to false + * `userDrag='none'` also sets `element.ondragstart` to false + * + * @method toggleBehavior + * @param {HtmlElement} element + * @param {Object} props + * @param {Boolean} [toggle=true] + */ + toggleBehavior: function toggleBehavior(element, props, toggle) { + if(!props || !element || !element.style) { + return; + } - for (i = lists.length - 1; i >= 0; --i) { - makeList(lists[i]); - } + // set the css properties + Utils.each(props, function(value, prop) { + Utils.setPrefixedCss(element, prop, value, toggle); + }); - moment.normalizeUnits = function (units) { - return normalizeUnits(units); - }; + var falseFn = toggle && function() { + return false; + }; - moment.invalid = function (flags) { - var m = moment.utc(NaN); - if (flags != null) { - extend(m._pf, flags); + // also the disable onselectstart + if(props.userSelect == 'none') { + element.onselectstart = falseFn; } - else { - m._pf.userInvalidated = true; + // and disable ondragstart + if(props.userDrag == 'none') { + element.ondragstart = falseFn; } + }, - return m; - }; - - moment.parseZone = function () { - return moment.apply(null, arguments).parseZone(); - }; - - moment.parseTwoDigitYear = function (input) { - return toInt(input) + (toInt(input) > 68 ? 1900 : 2000); - }; - - moment.isDate = isDate; - - /************************************ - Moment Prototype - ************************************/ - + /** + * convert a string with underscores to camelCase + * so prevent_default becomes preventDefault + * @param {String} str + * @return {String} camelCaseStr + */ + toCamelCase: function toCamelCase(str) { + return str.replace(/[_-]([a-z])/g, function(s) { + return s[1].toUpperCase(); + }); + } + }; - extend(moment.fn = Moment.prototype, { - clone : function () { - return moment(this); - }, + /** + * @module hammer + */ + /** + * @class Event + * @static + */ + var Event = Hammer.event = { + /** + * when touch events have been fired, this is true + * this is used to stop mouse events + * @property prevent_mouseevents + * @private + * @type {Boolean} + */ + preventMouseEvents: false, - valueOf : function () { - return +this._d - ((this._offset || 0) * 60000); - }, + /** + * if EVENT_START has been fired + * @property started + * @private + * @type {Boolean} + */ + started: false, - unix : function () { - return Math.floor(+this / 1000); - }, + /** + * when the mouse is hold down, this is true + * @property should_detect + * @private + * @type {Boolean} + */ + shouldDetect: false, - toString : function () { - return this.clone().locale('en').format('ddd MMM DD YYYY HH:mm:ss [GMT]ZZ'); - }, + /** + * simple event binder with a hook and support for multiple types + * @method on + * @param {HTMLElement} element + * @param {String} type + * @param {Function} handler + * @param {Function} [hook] + * @param {Object} hook.type + */ + on: function on(element, type, handler, hook) { + var types = type.split(' '); + Utils.each(types, function(type) { + Utils.on(element, type, handler); + hook && hook(type); + }); + }, - toDate : function () { - return this._offset ? new Date(+this) : this._d; - }, + /** + * simple event unbinder with a hook and support for multiple types + * @method off + * @param {HTMLElement} element + * @param {String} type + * @param {Function} handler + * @param {Function} [hook] + * @param {Object} hook.type + */ + off: function off(element, type, handler, hook) { + var types = type.split(' '); + Utils.each(types, function(type) { + Utils.off(element, type, handler); + hook && hook(type); + }); + }, - toISOString : function () { - var m = moment(this).utc(); - if (0 < m.year() && m.year() <= 9999) { - if ('function' === typeof Date.prototype.toISOString) { - // native implementation is ~50x faster, use it when we can - return this.toDate().toISOString(); - } else { - return formatMoment(m, 'YYYY-MM-DD[T]HH:mm:ss.SSS[Z]'); - } - } else { - return formatMoment(m, 'YYYYYY-MM-DD[T]HH:mm:ss.SSS[Z]'); - } - }, + /** + * the core touch event handler. + * this finds out if we should to detect gestures + * @method onTouch + * @param {HTMLElement} element + * @param {String} eventType matches `EVENT_START|MOVE|END` + * @param {Function} handler + * @return onTouchHandler {Function} the core event handler + */ + onTouch: function onTouch(element, eventType, handler) { + var self = this; - toArray : function () { - var m = this; - return [ - m.year(), - m.month(), - m.date(), - m.hours(), - m.minutes(), - m.seconds(), - m.milliseconds() - ]; - }, + var onTouchHandler = function onTouchHandler(ev) { + var srcType = ev.type.toLowerCase(), + isPointer = Hammer.HAS_POINTEREVENTS, + isMouse = Utils.inStr(srcType, 'mouse'), + triggerType; - isValid : function () { - return isValid(this); - }, + // if we are in a mouseevent, but there has been a touchevent triggered in this session + // we want to do nothing. simply break out of the event. + if(isMouse && self.preventMouseEvents) { + return; - isDSTShifted : function () { - if (this._a) { - return this.isValid() && compareArrays(this._a, (this._isUTC ? moment.utc(this._a) : moment(this._a)).toArray()) > 0; + // mousebutton must be down + } else if(isMouse && eventType == EVENT_START && ev.button === 0) { + self.preventMouseEvents = false; + self.shouldDetect = true; + } else if(isPointer && eventType == EVENT_START) { + self.shouldDetect = (ev.buttons === 1 || PointerEvent.matchType(POINTER_TOUCH, ev)); + // just a valid start event, but no mouse + } else if(!isMouse && eventType == EVENT_START) { + self.preventMouseEvents = true; + self.shouldDetect = true; } - return false; - }, + // update the pointer event before entering the detection + if(isPointer && eventType != EVENT_END) { + PointerEvent.updatePointer(eventType, ev); + } - parsingFlags : function () { - return extend({}, this._pf); - }, + // we are in a touch/down state, so allowed detection of gestures + if(self.shouldDetect) { + triggerType = self.doDetect.call(self, ev, eventType, element, handler); + } - invalidAt: function () { - return this._pf.overflow; - }, + // ...and we are done with the detection + // so reset everything to start each detection totally fresh + if(triggerType == EVENT_END) { + self.preventMouseEvents = false; + self.shouldDetect = false; + PointerEvent.reset(); + // update the pointerevent object after the detection + } - utc : function (keepLocalTime) { - return this.utcOffset(0, keepLocalTime); - }, + if(isPointer && eventType == EVENT_END) { + PointerEvent.updatePointer(eventType, ev); + } + }; - local : function (keepLocalTime) { - if (this._isUTC) { - this.utcOffset(0, keepLocalTime); - this._isUTC = false; + this.on(element, EVENT_TYPES[eventType], onTouchHandler); + return onTouchHandler; + }, - if (keepLocalTime) { - this.subtract(this._dateUtcOffset(), 'm'); - } - } - return this; - }, + /** + * the core detection method + * this finds out what hammer-touch-events to trigger + * @method doDetect + * @param {Object} ev + * @param {String} eventType matches `EVENT_START|MOVE|END` + * @param {HTMLElement} element + * @param {Function} handler + * @return {String} triggerType matches `EVENT_START|MOVE|END` + */ + doDetect: function doDetect(ev, eventType, element, handler) { + var touchList = this.getTouchList(ev, eventType); + var touchListLength = touchList.length; + var triggerType = eventType; + var triggerChange = touchList.trigger; // used by fakeMultitouch plugin + var changedLength = touchListLength; - format : function (inputString) { - var output = formatMoment(this, inputString || moment.defaultFormat); - return this.localeData().postformat(output); - }, + // at each touchstart-like event we want also want to trigger a TOUCH event... + if(eventType == EVENT_START) { + triggerChange = EVENT_TOUCH; + // ...the same for a touchend-like event + } else if(eventType == EVENT_END) { + triggerChange = EVENT_RELEASE; - add : createAdder(1, 'add'), + // keep track of how many touches have been removed + changedLength = touchList.length - ((ev.changedTouches) ? ev.changedTouches.length : 1); + } - subtract : createAdder(-1, 'subtract'), + // after there are still touches on the screen, + // we just want to trigger a MOVE event. so change the START or END to a MOVE + // but only after detection has been started, the first time we actualy want a START + if(changedLength > 0 && this.started) { + triggerType = EVENT_MOVE; + } - diff : function (input, units, asFloat) { - var that = makeAs(input, this), - zoneDiff = (that.utcOffset() - this.utcOffset()) * 6e4, - anchor, diff, output, daysAdjust; + // detection has been started, we keep track of this, see above + this.started = true; - units = normalizeUnits(units); + // generate some event data, some basic information + var evData = this.collectEventData(element, triggerType, touchList, ev); - if (units === 'year' || units === 'month' || units === 'quarter') { - output = monthDiff(this, that); - if (units === 'quarter') { - output = output / 3; - } else if (units === 'year') { - output = output / 12; - } - } else { - diff = this - that; - output = units === 'second' ? diff / 1e3 : // 1000 - units === 'minute' ? diff / 6e4 : // 1000 * 60 - units === 'hour' ? diff / 36e5 : // 1000 * 60 * 60 - units === 'day' ? (diff - zoneDiff) / 864e5 : // 1000 * 60 * 60 * 24, negate dst - units === 'week' ? (diff - zoneDiff) / 6048e5 : // 1000 * 60 * 60 * 24 * 7, negate dst - diff; - } - return asFloat ? output : absRound(output); - }, + // trigger the triggerType event before the change (TOUCH, RELEASE) events + // but the END event should be at last + if(eventType != EVENT_END) { + handler.call(Detection, evData); + } - from : function (time, withoutSuffix) { - return moment.duration({to: this, from: time}).locale(this.locale()).humanize(!withoutSuffix); - }, + // trigger a change (TOUCH, RELEASE) event, this means the length of the touches changed + if(triggerChange) { + evData.changedLength = changedLength; + evData.eventType = triggerChange; - fromNow : function (withoutSuffix) { - return this.from(moment(), withoutSuffix); - }, + handler.call(Detection, evData); - calendar : function (time) { - // We want to compare the start of today, vs this. - // Getting start-of-today depends on whether we're locat/utc/offset - // or not. - var now = time || moment(), - sod = makeAs(now, this).startOf('day'), - diff = this.diff(sod, 'days', true), - format = diff < -6 ? 'sameElse' : - diff < -1 ? 'lastWeek' : - diff < 0 ? 'lastDay' : - diff < 1 ? 'sameDay' : - diff < 2 ? 'nextDay' : - diff < 7 ? 'nextWeek' : 'sameElse'; - return this.format(this.localeData().calendar(format, this, moment(now))); - }, + evData.eventType = triggerType; + delete evData.changedLength; + } - isLeapYear : function () { - return isLeapYear(this.year()); - }, + // trigger the END event + if(triggerType == EVENT_END) { + handler.call(Detection, evData); - isDST : function () { - return (this.utcOffset() > this.clone().month(0).utcOffset() || - this.utcOffset() > this.clone().month(5).utcOffset()); - }, + // ...and we are done with the detection + // so reset everything to start each detection totally fresh + this.started = false; + } - day : function (input) { - var day = this._isUTC ? this._d.getUTCDay() : this._d.getDay(); - if (input != null) { - input = parseWeekday(input, this.localeData()); - return this.add(input - day, 'd'); + return triggerType; + }, + + /** + * we have different events for each device/browser + * determine what we need and set them in the EVENT_TYPES constant + * the `onTouch` method is bind to these properties. + * @method determineEventTypes + * @return {Object} events + */ + determineEventTypes: function determineEventTypes() { + var types; + if(Hammer.HAS_POINTEREVENTS) { + if(window.PointerEvent) { + types = [ + 'pointerdown', + 'pointermove', + 'pointerup pointercancel lostpointercapture' + ]; } else { - return day; + types = [ + 'MSPointerDown', + 'MSPointerMove', + 'MSPointerUp MSPointerCancel MSLostPointerCapture' + ]; } - }, + } else if(Hammer.NO_MOUSEEVENTS) { + types = [ + 'touchstart', + 'touchmove', + 'touchend touchcancel' + ]; + } else { + types = [ + 'touchstart mousedown', + 'touchmove mousemove', + 'touchend touchcancel mouseup' + ]; + } - month : makeAccessor('Month', true), + EVENT_TYPES[EVENT_START] = types[0]; + EVENT_TYPES[EVENT_MOVE] = types[1]; + EVENT_TYPES[EVENT_END] = types[2]; + return EVENT_TYPES; + }, - startOf : function (units) { - units = normalizeUnits(units); - // the following switch intentionally omits break keywords - // to utilize falling through the cases. - switch (units) { - case 'year': - this.month(0); - /* falls through */ - case 'quarter': - case 'month': - this.date(1); - /* falls through */ - case 'week': - case 'isoWeek': - case 'day': - this.hours(0); - /* falls through */ - case 'hour': - this.minutes(0); - /* falls through */ - case 'minute': - this.seconds(0); - /* falls through */ - case 'second': - this.milliseconds(0); - /* falls through */ - } + /** + * create touchList depending on the event + * @method getTouchList + * @param {Object} ev + * @param {String} eventType + * @return {Array} touches + */ + getTouchList: function getTouchList(ev, eventType) { + // get the fake pointerEvent touchlist + if(Hammer.HAS_POINTEREVENTS) { + return PointerEvent.getTouchList(); + } - // weeks are a special case - if (units === 'week') { - this.weekday(0); - } else if (units === 'isoWeek') { - this.isoWeekday(1); + // get the touchlist + if(ev.touches) { + if(eventType == EVENT_MOVE) { + return ev.touches; } - // quarters are also special - if (units === 'quarter') { - this.month(Math.floor(this.month() / 3) * 3); - } + var identifiers = []; + var concat = [].concat(Utils.toArray(ev.touches), Utils.toArray(ev.changedTouches)); + var touchList = []; - return this; - }, + Utils.each(concat, function(touch) { + if(Utils.inArray(identifiers, touch.identifier) === false) { + touchList.push(touch); + } + identifiers.push(touch.identifier); + }); - endOf: function (units) { - units = normalizeUnits(units); - if (units === undefined || units === 'millisecond') { - return this; - } - return this.startOf(units).add(1, (units === 'isoWeek' ? 'week' : units)).subtract(1, 'ms'); - }, + return touchList; + } - isAfter: function (input, units) { - var inputMs; - units = normalizeUnits(typeof units !== 'undefined' ? units : 'millisecond'); - if (units === 'millisecond') { - input = moment.isMoment(input) ? input : moment(input); - return +this > +input; - } else { - inputMs = moment.isMoment(input) ? +input : +moment(input); - return inputMs < +this.clone().startOf(units); - } - }, + // make fake touchList from mouse position + ev.identifier = 1; + return [ev]; + }, - isBefore: function (input, units) { - var inputMs; - units = normalizeUnits(typeof units !== 'undefined' ? units : 'millisecond'); - if (units === 'millisecond') { - input = moment.isMoment(input) ? input : moment(input); - return +this < +input; - } else { - inputMs = moment.isMoment(input) ? +input : +moment(input); - return +this.clone().endOf(units) < inputMs; - } - }, + /** + * collect basic event data + * @method collectEventData + * @param {HTMLElement} element + * @param {String} eventType matches `EVENT_START|MOVE|END` + * @param {Array} touches + * @param {Object} ev + * @return {Object} ev + */ + collectEventData: function collectEventData(element, eventType, touches, ev) { + // find out pointerType + var pointerType = POINTER_TOUCH; + if(Utils.inStr(ev.type, 'mouse') || PointerEvent.matchType(POINTER_MOUSE, ev)) { + pointerType = POINTER_MOUSE; + } else if(PointerEvent.matchType(POINTER_PEN, ev)) { + pointerType = POINTER_PEN; + } - isBetween: function (from, to, units) { - return this.isAfter(from, units) && this.isBefore(to, units); - }, + return { + center: Utils.getCenter(touches), + timeStamp: Date.now(), + target: ev.target, + touches: touches, + eventType: eventType, + pointerType: pointerType, + srcEvent: ev, - isSame: function (input, units) { - var inputMs; - units = normalizeUnits(units || 'millisecond'); - if (units === 'millisecond') { - input = moment.isMoment(input) ? input : moment(input); - return +this === +input; - } else { - inputMs = +moment(input); - return +(this.clone().startOf(units)) <= inputMs && inputMs <= +(this.clone().endOf(units)); + /** + * prevent the browser default actions + * mostly used to disable scrolling of the browser + */ + preventDefault: function() { + var srcEvent = this.srcEvent; + srcEvent.preventManipulation && srcEvent.preventManipulation(); + srcEvent.preventDefault && srcEvent.preventDefault(); + }, + + /** + * stop bubbling the event up to its parents + */ + stopPropagation: function() { + this.srcEvent.stopPropagation(); + }, + + /** + * immediately stop gesture detection + * might be useful after a swipe was detected + * @return {*} + */ + stopDetect: function() { + return Detection.stopDetect(); } - }, + }; + } + }; - min: deprecate( - 'moment().min is deprecated, use moment.min instead. https://github.com/moment/moment/issues/1548', - function (other) { - other = moment.apply(null, arguments); - return other < this ? this : other; - } - ), - max: deprecate( - 'moment().max is deprecated, use moment.max instead. https://github.com/moment/moment/issues/1548', - function (other) { - other = moment.apply(null, arguments); - return other > this ? this : other; - } - ), - - zone : deprecate( - 'moment().zone is deprecated, use moment().utcOffset instead. ' + - 'https://github.com/moment/moment/issues/1779', - function (input, keepLocalTime) { - if (input != null) { - if (typeof input !== 'string') { - input = -input; - } - - this.utcOffset(input, keepLocalTime); + /** + * @module hammer + * + * @class PointerEvent + * @static + */ + var PointerEvent = Hammer.PointerEvent = { + /** + * holds all pointers, by `identifier` + * @property pointers + * @type {Object} + */ + pointers: {}, - return this; - } else { - return -this.utcOffset(); - } - } - ), + /** + * get the pointers as an array + * @method getTouchList + * @return {Array} touchlist + */ + getTouchList: function getTouchList() { + var touchlist = []; + // we can use forEach since pointerEvents only is in IE10 + Utils.each(this.pointers, function(pointer) { + touchlist.push(pointer); + }); + return touchlist; + }, - // keepLocalTime = true means only change the timezone, without - // affecting the local hour. So 5:31:26 +0300 --[utcOffset(2, true)]--> - // 5:31:26 +0200 It is possible that 5:31:26 doesn't exist with offset - // +0200, so we adjust the time as needed, to be valid. - // - // Keeping the time actually adds/subtracts (one hour) - // from the actual represented time. That is why we call updateOffset - // a second time. In case it wants us to change the offset again - // _changeInProgress == true case, then we have to adjust, because - // there is no such time in the given timezone. - utcOffset : function (input, keepLocalTime) { - var offset = this._offset || 0, - localAdjust; - if (input != null) { - if (typeof input === 'string') { - input = utcOffsetFromString(input); - } - if (Math.abs(input) < 16) { - input = input * 60; - } - if (!this._isUTC && keepLocalTime) { - localAdjust = this._dateUtcOffset(); - } - this._offset = input; - this._isUTC = true; - if (localAdjust != null) { - this.add(localAdjust, 'm'); - } - if (offset !== input) { - if (!keepLocalTime || this._changeInProgress) { - addOrSubtractDurationFromMoment(this, - moment.duration(input - offset, 'm'), 1, false); - } else if (!this._changeInProgress) { - this._changeInProgress = true; - moment.updateOffset(this, true); - this._changeInProgress = null; - } - } + /** + * update the position of a pointer + * @method updatePointer + * @param {String} eventType matches `EVENT_START|MOVE|END` + * @param {Object} pointerEvent + */ + updatePointer: function updatePointer(eventType, pointerEvent) { + if(eventType == EVENT_END || (eventType != EVENT_END && pointerEvent.buttons !== 1)) { + delete this.pointers[pointerEvent.pointerId]; + } else { + pointerEvent.identifier = pointerEvent.pointerId; + this.pointers[pointerEvent.pointerId] = pointerEvent; + } + }, - return this; - } else { - return this._isUTC ? offset : this._dateUtcOffset(); - } - }, + /** + * check if ev matches pointertype + * @method matchType + * @param {String} pointerType matches `POINTER_MOUSE|TOUCH|PEN` + * @param {PointerEvent} ev + */ + matchType: function matchType(pointerType, ev) { + if(!ev.pointerType) { + return false; + } - isLocal : function () { - return !this._isUTC; - }, + var pt = ev.pointerType, + types = {}; - isUtcOffset : function () { - return this._isUTC; - }, + types[POINTER_MOUSE] = (pt === (ev.MSPOINTER_TYPE_MOUSE || POINTER_MOUSE)); + types[POINTER_TOUCH] = (pt === (ev.MSPOINTER_TYPE_TOUCH || POINTER_TOUCH)); + types[POINTER_PEN] = (pt === (ev.MSPOINTER_TYPE_PEN || POINTER_PEN)); + return types[pointerType]; + }, - isUtc : function () { - return this._isUTC && this._offset === 0; - }, + /** + * reset the stored pointers + * @method reset + */ + reset: function resetList() { + this.pointers = {}; + } + }; - zoneAbbr : function () { - return this._isUTC ? 'UTC' : ''; - }, - zoneName : function () { - return this._isUTC ? 'Coordinated Universal Time' : ''; - }, + /** + * @module hammer + * + * @class Detection + * @static + */ + var Detection = Hammer.detection = { + // contains all registred Hammer.gestures in the correct order + gestures: [], - parseZone : function () { - if (this._tzm) { - this.utcOffset(this._tzm); - } else if (typeof this._i === 'string') { - this.utcOffset(utcOffsetFromString(this._i)); - } - return this; - }, + // data of the current Hammer.gesture detection session + current: null, - hasAlignedHourOffset : function (input) { - if (!input) { - input = 0; - } - else { - input = moment(input).utcOffset(); - } + // the previous Hammer.gesture session data + // is a full clone of the previous gesture.current object + previous: null, - return (this.utcOffset() - input) % 60 === 0; - }, + // when this becomes true, no gestures are fired + stopped: false, - daysInMonth : function () { - return daysInMonth(this.year(), this.month()); - }, + /** + * start Hammer.gesture detection + * @method startDetect + * @param {Hammer.Instance} inst + * @param {Object} eventData + */ + startDetect: function startDetect(inst, eventData) { + // already busy with a Hammer.gesture detection on an element + if(this.current) { + return; + } - dayOfYear : function (input) { - var dayOfYear = round((moment(this).startOf('day') - moment(this).startOf('year')) / 864e5) + 1; - return input == null ? dayOfYear : this.add((input - dayOfYear), 'd'); - }, + this.stopped = false; - quarter : function (input) { - return input == null ? Math.ceil((this.month() + 1) / 3) : this.month((input - 1) * 3 + this.month() % 3); - }, + // holds current session + this.current = { + inst: inst, // reference to HammerInstance we're working for + startEvent: Utils.extend({}, eventData), // start eventData for distances, timing etc + lastEvent: false, // last eventData + lastCalcEvent: false, // last eventData for calculations. + futureCalcEvent: false, // last eventData for calculations. + lastCalcData: {}, // last lastCalcData + name: '' // current gesture we're in/detected, can be 'tap', 'hold' etc + }; - weekYear : function (input) { - var year = weekOfYear(this, this.localeData()._week.dow, this.localeData()._week.doy).year; - return input == null ? year : this.add((input - year), 'y'); - }, + this.detect(eventData); + }, - isoWeekYear : function (input) { - var year = weekOfYear(this, 1, 4).year; - return input == null ? year : this.add((input - year), 'y'); - }, + /** + * Hammer.gesture detection + * @method detect + * @param {Object} eventData + * @return {any} + */ + detect: function detect(eventData) { + if(!this.current || this.stopped) { + return; + } - week : function (input) { - var week = this.localeData().week(this); - return input == null ? week : this.add((input - week) * 7, 'd'); - }, + // extend event data with calculations about scale, distance etc + eventData = this.extendEventData(eventData); - isoWeek : function (input) { - var week = weekOfYear(this, 1, 4).week; - return input == null ? week : this.add((input - week) * 7, 'd'); - }, + // hammer instance and instance options + var inst = this.current.inst, + instOptions = inst.options; - weekday : function (input) { - var weekday = (this.day() + 7 - this.localeData()._week.dow) % 7; - return input == null ? weekday : this.add(input - weekday, 'd'); - }, + // call Hammer.gesture handlers + Utils.each(this.gestures, function triggerGesture(gesture) { + // only when the instance options have enabled this gesture + if(!this.stopped && inst.enabled && instOptions[gesture.name]) { + gesture.handler.call(gesture, eventData, inst); + } + }, this); - isoWeekday : function (input) { - // behaves the same as moment#day except - // as a getter, returns 7 instead of 0 (1-7 range instead of 0-6) - // as a setter, sunday should belong to the previous week. - return input == null ? this.day() || 7 : this.day(this.day() % 7 ? input : input - 7); - }, + // store as previous event event + if(this.current) { + this.current.lastEvent = eventData; + } - isoWeeksInYear : function () { - return weeksInYear(this.year(), 1, 4); - }, + if(eventData.eventType == EVENT_END) { + this.stopDetect(); + } - weeksInYear : function () { - var weekInfo = this.localeData()._week; - return weeksInYear(this.year(), weekInfo.dow, weekInfo.doy); - }, + return eventData; + }, - get : function (units) { - units = normalizeUnits(units); - return this[units](); - }, + /** + * clear the Hammer.gesture vars + * this is called on endDetect, but can also be used when a final Hammer.gesture has been detected + * to stop other Hammer.gestures from being fired + * @method stopDetect + */ + stopDetect: function stopDetect() { + // clone current data to the store as the previous gesture + // used for the double tap gesture, since this is an other gesture detect session + this.previous = Utils.extend({}, this.current); - set : function (units, value) { - var unit; - if (typeof units === 'object') { - for (unit in units) { - this.set(unit, units[unit]); - } - } - else { - units = normalizeUnits(units); - if (typeof this[units] === 'function') { - this[units](value); - } - } - return this; - }, + // reset the current + this.current = null; + this.stopped = true; + }, - // If passed a locale key, it will set the locale for this - // instance. Otherwise, it will return the locale configuration - // variables for this instance. - locale : function (key) { - var newLocaleData; + /** + * calculate velocity, angle and direction + * @method getVelocityData + * @param {Object} ev + * @param {Object} center + * @param {Number} deltaTime + * @param {Number} deltaX + * @param {Number} deltaY + */ + getCalculatedData: function getCalculatedData(ev, center, deltaTime, deltaX, deltaY) { + var cur = this.current, + recalc = false, + calcEv = cur.lastCalcEvent, + calcData = cur.lastCalcData; - if (key === undefined) { - return this._locale._abbr; - } else { - newLocaleData = moment.localeData(key); - if (newLocaleData != null) { - this._locale = newLocaleData; - } - return this; - } - }, + if(calcEv && ev.timeStamp - calcEv.timeStamp > Hammer.CALCULATE_INTERVAL) { + center = calcEv.center; + deltaTime = ev.timeStamp - calcEv.timeStamp; + deltaX = ev.center.clientX - calcEv.center.clientX; + deltaY = ev.center.clientY - calcEv.center.clientY; + recalc = true; + } - lang : deprecate( - 'moment().lang() is deprecated. Instead, use moment().localeData() to get the language configuration. Use moment().locale() to change languages.', - function (key) { - if (key === undefined) { - return this.localeData(); - } else { - return this.locale(key); - } - } - ), + if(ev.eventType == EVENT_TOUCH || ev.eventType == EVENT_RELEASE) { + cur.futureCalcEvent = ev; + } - localeData : function () { - return this._locale; - }, + if(!cur.lastCalcEvent || recalc) { + calcData.velocity = Utils.getVelocity(deltaTime, deltaX, deltaY); + calcData.angle = Utils.getAngle(center, ev.center); + calcData.direction = Utils.getDirection(center, ev.center); - _dateUtcOffset : function () { - // On Firefox.24 Date#getTimezoneOffset returns a floating point. - // https://github.com/moment/moment/pull/1871 - return -Math.round(this._d.getTimezoneOffset() / 15) * 15; + cur.lastCalcEvent = cur.futureCalcEvent || ev; + cur.futureCalcEvent = ev; } - }); + ev.velocityX = calcData.velocity.x; + ev.velocityY = calcData.velocity.y; + ev.interimAngle = calcData.angle; + ev.interimDirection = calcData.direction; + }, - function rawMonthSetter(mom, value) { - var dayOfMonth; + /** + * extend eventData for Hammer.gestures + * @method extendEventData + * @param {Object} ev + * @return {Object} ev + */ + extendEventData: function extendEventData(ev) { + var cur = this.current, + startEv = cur.startEvent, + lastEv = cur.lastEvent || startEv; - // TODO: Move this out of here! - if (typeof value === 'string') { - value = mom.localeData().monthsParse(value); - // TODO: Another silent failure? - if (typeof value !== 'number') { - return mom; - } + // update the start touchlist to calculate the scale/rotation + if(ev.eventType == EVENT_TOUCH || ev.eventType == EVENT_RELEASE) { + startEv.touches = []; + Utils.each(ev.touches, function(touch) { + startEv.touches.push({ + clientX: touch.clientX, + clientY: touch.clientY + }); + }); } - dayOfMonth = Math.min(mom.date(), - daysInMonth(mom.year(), value)); - mom._d['set' + (mom._isUTC ? 'UTC' : '') + 'Month'](value, dayOfMonth); - return mom; - } + var deltaTime = ev.timeStamp - startEv.timeStamp, + deltaX = ev.center.clientX - startEv.center.clientX, + deltaY = ev.center.clientY - startEv.center.clientY; - function rawGetter(mom, unit) { - return mom._d['get' + (mom._isUTC ? 'UTC' : '') + unit](); - } + this.getCalculatedData(ev, lastEv.center, deltaTime, deltaX, deltaY); - function rawSetter(mom, unit, value) { - if (unit === 'Month') { - return rawMonthSetter(mom, value); - } else { - return mom._d['set' + (mom._isUTC ? 'UTC' : '') + unit](value); - } - } + Utils.extend(ev, { + startEvent: startEv, - function makeAccessor(unit, keepTime) { - return function (value) { - if (value != null) { - rawSetter(this, unit, value); - moment.updateOffset(this, keepTime); - return this; - } else { - return rawGetter(this, unit); - } - }; - } + deltaTime: deltaTime, + deltaX: deltaX, + deltaY: deltaY, - moment.fn.millisecond = moment.fn.milliseconds = makeAccessor('Milliseconds', false); - moment.fn.second = moment.fn.seconds = makeAccessor('Seconds', false); - moment.fn.minute = moment.fn.minutes = makeAccessor('Minutes', false); - // Setting the hour should keep the time, because the user explicitly - // specified which hour he wants. So trying to maintain the same hour (in - // a new timezone) makes sense. Adding/subtracting hours does not follow - // this rule. - moment.fn.hour = moment.fn.hours = makeAccessor('Hours', true); - // moment.fn.month is defined separately - moment.fn.date = makeAccessor('Date', true); - moment.fn.dates = deprecate('dates accessor is deprecated. Use date instead.', makeAccessor('Date', true)); - moment.fn.year = makeAccessor('FullYear', true); - moment.fn.years = deprecate('years accessor is deprecated. Use year instead.', makeAccessor('FullYear', true)); + distance: Utils.getDistance(startEv.center, ev.center), + angle: Utils.getAngle(startEv.center, ev.center), + direction: Utils.getDirection(startEv.center, ev.center), + scale: Utils.getScale(startEv.touches, ev.touches), + rotation: Utils.getRotation(startEv.touches, ev.touches) + }); - // add plural methods - moment.fn.days = moment.fn.day; - moment.fn.months = moment.fn.month; - moment.fn.weeks = moment.fn.week; - moment.fn.isoWeeks = moment.fn.isoWeek; - moment.fn.quarters = moment.fn.quarter; + return ev; + }, - // add aliased format methods - moment.fn.toJSON = moment.fn.toISOString; + /** + * register new gesture + * @method register + * @param {Object} gesture object, see `gestures/` for documentation + * @return {Array} gestures + */ + register: function register(gesture) { + // add an enable gesture options if there is no given + var options = gesture.defaults || {}; + if(options[gesture.name] === undefined) { + options[gesture.name] = true; + } - // alias isUtc for dev-friendliness - moment.fn.isUTC = moment.fn.isUtc; + // extend Hammer default options with the Hammer.gesture options + Utils.extend(Hammer.defaults, options, true); - /************************************ - Duration Prototype - ************************************/ + // set its index + gesture.index = gesture.index || 1000; + // add Hammer.gesture to the list + this.gestures.push(gesture); - function daysToYears (days) { - // 400 years have 146097 days (taking into account leap year rules) - return days * 400 / 146097; + // sort the list by index + this.gestures.sort(function(a, b) { + if(a.index < b.index) { + return -1; + } + if(a.index > b.index) { + return 1; + } + return 0; + }); + + return this.gestures; } + }; - function yearsToDays (years) { - // years * 365 + absRound(years / 4) - - // absRound(years / 100) + absRound(years / 400); - return years * 146097 / 400; - } - extend(moment.duration.fn = Duration.prototype, { + /** + * @module hammer + */ - _bubble : function () { - var milliseconds = this._milliseconds, - days = this._days, - months = this._months, - data = this._data, - seconds, minutes, hours, years = 0; + /** + * create new hammer instance + * all methods should return the instance itself, so it is chainable. + * + * @class Instance + * @constructor + * @param {HTMLElement} element + * @param {Object} [options={}] options are merged with `Hammer.defaults` + * @return {Hammer.Instance} + */ + Hammer.Instance = function(element, options) { + var self = this; - // The following code bubbles up values, see the tests for - // examples of what that means. - data.milliseconds = milliseconds % 1000; + // setup HammerJS window events and register all gestures + // this also sets up the default options + setup(); - seconds = absRound(milliseconds / 1000); - data.seconds = seconds % 60; + /** + * @property element + * @type {HTMLElement} + */ + this.element = element; - minutes = absRound(seconds / 60); - data.minutes = minutes % 60; + /** + * @property enabled + * @type {Boolean} + * @protected + */ + this.enabled = true; - hours = absRound(minutes / 60); - data.hours = hours % 24; + /** + * options, merged with the defaults + * options with an _ are converted to camelCase + * @property options + * @type {Object} + */ + Utils.each(options, function(value, name) { + delete options[name]; + options[Utils.toCamelCase(name)] = value; + }); - days += absRound(hours / 24); + this.options = Utils.extend(Utils.extend({}, Hammer.defaults), options || {}); - // Accurately convert days to years, assume start from year 0. - years = absRound(daysToYears(days)); - days -= absRound(yearsToDays(years)); + // add some css to the element to prevent the browser from doing its native behavoir + if(this.options.behavior) { + Utils.toggleBehavior(this.element, this.options.behavior, true); + } - // 30 days to a month - // TODO (iskren): Use anchor date (like 1st Jan) to compute this. - months += absRound(days / 30); - days %= 30; + /** + * event start handler on the element to start the detection + * @property eventStartHandler + * @type {Object} + */ + this.eventStartHandler = Event.onTouch(element, EVENT_START, function(ev) { + if(self.enabled && ev.eventType == EVENT_START) { + Detection.startDetect(self, ev); + } else if(ev.eventType == EVENT_TOUCH) { + Detection.detect(ev); + } + }); - // 12 months -> 1 year - years += absRound(months / 12); - months %= 12; + /** + * keep a list of user event handlers which needs to be removed when calling 'dispose' + * @property eventHandlers + * @type {Array} + */ + this.eventHandlers = []; + }; - data.days = days; - data.months = months; - data.years = years; - }, + Hammer.Instance.prototype = { + /** + * bind events to the instance + * @method on + * @chainable + * @param {String} gestures multiple gestures by splitting with a space + * @param {Function} handler + * @param {Object} handler.ev event object + */ + on: function onEvent(gestures, handler) { + var self = this; + Event.on(self.element, gestures, handler, function(type) { + self.eventHandlers.push({ gesture: type, handler: handler }); + }); + return self; + }, - abs : function () { - this._milliseconds = Math.abs(this._milliseconds); - this._days = Math.abs(this._days); - this._months = Math.abs(this._months); + /** + * unbind events to the instance + * @method off + * @chainable + * @param {String} gestures + * @param {Function} handler + */ + off: function offEvent(gestures, handler) { + var self = this; - this._data.milliseconds = Math.abs(this._data.milliseconds); - this._data.seconds = Math.abs(this._data.seconds); - this._data.minutes = Math.abs(this._data.minutes); - this._data.hours = Math.abs(this._data.hours); - this._data.months = Math.abs(this._data.months); - this._data.years = Math.abs(this._data.years); + Event.off(self.element, gestures, handler, function(type) { + var index = Utils.inArray({ gesture: type, handler: handler }); + if(index !== false) { + self.eventHandlers.splice(index, 1); + } + }); + return self; + }, - return this; - }, + /** + * trigger gesture event + * @method trigger + * @chainable + * @param {String} gesture + * @param {Object} [eventData] + */ + trigger: function triggerEvent(gesture, eventData) { + // optional + if(!eventData) { + eventData = {}; + } - weeks : function () { - return absRound(this.days() / 7); - }, + // create DOM event + var event = Hammer.DOCUMENT.createEvent('Event'); + event.initEvent(gesture, true, true); + event.gesture = eventData; - valueOf : function () { - return this._milliseconds + - this._days * 864e5 + - (this._months % 12) * 2592e6 + - toInt(this._months / 12) * 31536e6; - }, + // trigger on the target if it is in the instance element, + // this is for event delegation tricks + var element = this.element; + if(Utils.hasParent(eventData.target, element)) { + element = eventData.target; + } - humanize : function (withSuffix) { - var output = relativeTime(this, !withSuffix, this.localeData()); + element.dispatchEvent(event); + return this; + }, - if (withSuffix) { - output = this.localeData().pastFuture(+this, output); - } + /** + * enable of disable hammer.js detection + * @method enable + * @chainable + * @param {Boolean} state + */ + enable: function enable(state) { + this.enabled = state; + return this; + }, - return this.localeData().postformat(output); - }, + /** + * dispose this hammer instance + * @method dispose + * @return {Null} + */ + dispose: function dispose() { + var i, eh; - add : function (input, val) { - // supports only 2.0-style add(1, 's') or add(moment) - var dur = moment.duration(input, val); + // undo all changes made by stop_browser_behavior + Utils.toggleBehavior(this.element, this.options.behavior, false); - this._milliseconds += dur._milliseconds; - this._days += dur._days; - this._months += dur._months; + // unbind all custom event handlers + for(i = -1; (eh = this.eventHandlers[++i]);) { + Utils.off(this.element, eh.gesture, eh.handler); + } - this._bubble(); + this.eventHandlers = []; - return this; - }, + // unbind the start event listener + Event.off(this.element, EVENT_TYPES[EVENT_START], this.eventStartHandler); - subtract : function (input, val) { - var dur = moment.duration(input, val); + return null; + } + }; - this._milliseconds -= dur._milliseconds; - this._days -= dur._days; - this._months -= dur._months; - this._bubble(); + /** + * @module gestures + */ + /** + * Move with x fingers (default 1) around on the page. + * Preventing the default browser behavior is a good way to improve feel and working. + * ```` + * hammertime.on("drag", function(ev) { + * console.log(ev); + * ev.gesture.preventDefault(); + * }); + * ```` + * + * @class Drag + * @static + */ + /** + * @event drag + * @param {Object} ev + */ + /** + * @event dragstart + * @param {Object} ev + */ + /** + * @event dragend + * @param {Object} ev + */ + /** + * @event drapleft + * @param {Object} ev + */ + /** + * @event dragright + * @param {Object} ev + */ + /** + * @event dragup + * @param {Object} ev + */ + /** + * @event dragdown + * @param {Object} ev + */ - return this; - }, + /** + * @param {String} name + */ + (function(name) { + var triggered = false; - get : function (units) { - units = normalizeUnits(units); - return this[units.toLowerCase() + 's'](); - }, + function dragGesture(ev, inst) { + var cur = Detection.current; - as : function (units) { - var days, months; - units = normalizeUnits(units); + // max touches + if(inst.options.dragMaxTouches > 0 && + ev.touches.length > inst.options.dragMaxTouches) { + return; + } - if (units === 'month' || units === 'year') { - days = this._days + this._milliseconds / 864e5; - months = this._months + daysToYears(days) * 12; - return units === 'month' ? months : months / 12; - } else { - // handle milliseconds separately because of floating point math errors (issue #1867) - days = this._days + Math.round(yearsToDays(this._months / 12)); - switch (units) { - case 'week': return days / 7 + this._milliseconds / 6048e5; - case 'day': return days + this._milliseconds / 864e5; - case 'hour': return days * 24 + this._milliseconds / 36e5; - case 'minute': return days * 24 * 60 + this._milliseconds / 6e4; - case 'second': return days * 24 * 60 * 60 + this._milliseconds / 1000; - // Math.floor prevents floating point math errors here - case 'millisecond': return Math.floor(days * 24 * 60 * 60 * 1000) + this._milliseconds; - default: throw new Error('Unknown unit ' + units); + switch(ev.eventType) { + case EVENT_START: + triggered = false; + break; + + case EVENT_MOVE: + // when the distance we moved is too small we skip this gesture + // or we can be already in dragging + if(ev.distance < inst.options.dragMinDistance && + cur.name != name) { + return; } - } - }, - lang : moment.fn.lang, - locale : moment.fn.locale, + var startCenter = cur.startEvent.center; - toIsoString : deprecate( - 'toIsoString() is deprecated. Please use toISOString() instead ' + - '(notice the capitals)', - function () { - return this.toISOString(); - } - ), + // we are dragging! + if(cur.name != name) { + cur.name = name; + if(inst.options.dragDistanceCorrection && ev.distance > 0) { + // When a drag is triggered, set the event center to dragMinDistance pixels from the original event center. + // Without this correction, the dragged distance would jumpstart at dragMinDistance pixels instead of at 0. + // It might be useful to save the original start point somewhere + var factor = Math.abs(inst.options.dragMinDistance / ev.distance); + startCenter.pageX += ev.deltaX * factor; + startCenter.pageY += ev.deltaY * factor; + startCenter.clientX += ev.deltaX * factor; + startCenter.clientY += ev.deltaY * factor; - toISOString : function () { - // inspired by https://github.com/dordille/moment-isoduration/blob/master/moment.isoduration.js - var years = Math.abs(this.years()), - months = Math.abs(this.months()), - days = Math.abs(this.days()), - hours = Math.abs(this.hours()), - minutes = Math.abs(this.minutes()), - seconds = Math.abs(this.seconds() + this.milliseconds() / 1000); + // recalculate event data using new start point + ev = Detection.extendEventData(ev); + } + } - if (!this.asSeconds()) { - // this is the same as C#'s (Noda) and python (isodate)... - // but not other JS (goog.date) - return 'P0D'; - } + // lock drag to axis? + if(cur.lastEvent.dragLockToAxis || + ( inst.options.dragLockToAxis && + inst.options.dragLockMinDistance <= ev.distance + )) { + ev.dragLockToAxis = true; + } - return (this.asSeconds() < 0 ? '-' : '') + - 'P' + - (years ? years + 'Y' : '') + - (months ? months + 'M' : '') + - (days ? days + 'D' : '') + - ((hours || minutes || seconds) ? 'T' : '') + - (hours ? hours + 'H' : '') + - (minutes ? minutes + 'M' : '') + - (seconds ? seconds + 'S' : ''); - }, + // keep direction on the axis that the drag gesture started on + var lastDirection = cur.lastEvent.direction; + if(ev.dragLockToAxis && lastDirection !== ev.direction) { + if(Utils.isVertical(lastDirection)) { + ev.direction = (ev.deltaY < 0) ? DIRECTION_UP : DIRECTION_DOWN; + } else { + ev.direction = (ev.deltaX < 0) ? DIRECTION_LEFT : DIRECTION_RIGHT; + } + } - localeData : function () { - return this._locale; - }, + // first time, trigger dragstart event + if(!triggered) { + inst.trigger(name + 'start', ev); + triggered = true; + } - toJSON : function () { - return this.toISOString(); - } - }); + // trigger events + inst.trigger(name, ev); + inst.trigger(name + ev.direction, ev); - moment.duration.fn.toString = moment.duration.fn.toISOString; + var isVertical = Utils.isVertical(ev.direction); - function makeDurationGetter(name) { - moment.duration.fn[name] = function () { - return this._data[name]; - }; - } + // block the browser events + if((inst.options.dragBlockVertical && isVertical) || + (inst.options.dragBlockHorizontal && !isVertical)) { + ev.preventDefault(); + } + break; - for (i in unitMillisecondFactors) { - if (hasOwnProp(unitMillisecondFactors, i)) { - makeDurationGetter(i.toLowerCase()); + case EVENT_RELEASE: + if(triggered && ev.changedLength <= inst.options.dragMaxTouches) { + inst.trigger(name + 'end', ev); + triggered = false; + } + break; + + case EVENT_END: + triggered = false; + break; } } - moment.duration.fn.asMilliseconds = function () { - return this.as('ms'); - }; - moment.duration.fn.asSeconds = function () { - return this.as('s'); - }; - moment.duration.fn.asMinutes = function () { - return this.as('m'); - }; - moment.duration.fn.asHours = function () { - return this.as('h'); - }; - moment.duration.fn.asDays = function () { - return this.as('d'); - }; - moment.duration.fn.asWeeks = function () { - return this.as('weeks'); - }; - moment.duration.fn.asMonths = function () { - return this.as('M'); - }; - moment.duration.fn.asYears = function () { - return this.as('y'); - }; + Hammer.gestures.Drag = { + name: name, + index: 50, + handler: dragGesture, + defaults: { + /** + * minimal movement that have to be made before the drag event gets triggered + * @property dragMinDistance + * @type {Number} + * @default 10 + */ + dragMinDistance: 10, - /************************************ - Default Locale - ************************************/ + /** + * Set dragDistanceCorrection to true to make the starting point of the drag + * be calculated from where the drag was triggered, not from where the touch started. + * Useful to avoid a jerk-starting drag, which can make fine-adjustments + * through dragging difficult, and be visually unappealing. + * @property dragDistanceCorrection + * @type {Boolean} + * @default true + */ + dragDistanceCorrection: true, + /** + * set 0 for unlimited, but this can conflict with transform + * @property dragMaxTouches + * @type {Number} + * @default 1 + */ + dragMaxTouches: 1, - // Set default locale, other locale will inherit from English. - moment.locale('en', { - ordinalParse: /\d{1,2}(th|st|nd|rd)/, - ordinal : function (number) { - var b = number % 10, - output = (toInt(number % 100 / 10) === 1) ? 'th' : - (b === 1) ? 'st' : - (b === 2) ? 'nd' : - (b === 3) ? 'rd' : 'th'; - return number + output; - } - }); + /** + * prevent default browser behavior when dragging occurs + * be careful with it, it makes the element a blocking element + * when you are using the drag gesture, it is a good practice to set this true + * @property dragBlockHorizontal + * @type {Boolean} + * @default false + */ + dragBlockHorizontal: false, - /* EMBED_LOCALES */ + /** + * same as `dragBlockHorizontal`, but for vertical movement + * @property dragBlockVertical + * @type {Boolean} + * @default false + */ + dragBlockVertical: false, - /************************************ - Exposing Moment - ************************************/ + /** + * dragLockToAxis keeps the drag gesture on the axis that it started on, + * It disallows vertical directions if the initial direction was horizontal, and vice versa. + * @property dragLockToAxis + * @type {Boolean} + * @default false + */ + dragLockToAxis: false, - function makeGlobal(shouldDeprecate) { - /*global ender:false */ - if (typeof ender !== 'undefined') { - return; - } - oldGlobalMoment = globalScope.moment; - if (shouldDeprecate) { - globalScope.moment = deprecate( - 'Accessing Moment through the global scope is ' + - 'deprecated, and will be removed in an upcoming ' + - 'release.', - moment); - } else { - globalScope.moment = moment; + /** + * drag lock only kicks in when distance > dragLockMinDistance + * This way, locking occurs only when the distance has become large enough to reliably determine the direction + * @property dragLockMinDistance + * @type {Number} + * @default 25 + */ + dragLockMinDistance: 25 } - } - - // CommonJS module is defined - if (hasModule) { - module.exports = moment; - } else if (true) { - !(__WEBPACK_AMD_DEFINE_RESULT__ = function (require, exports, module) { - if (module.config && module.config() && module.config().noGlobal === true) { - // release the global variable - globalScope.moment = oldGlobalMoment; - } - - return moment; - }.call(exports, __webpack_require__, exports, module), __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__)); - makeGlobal(true); - } else { - makeGlobal(); - } - }).call(this); - - /* WEBPACK VAR INJECTION */}.call(exports, (function() { return this; }()), __webpack_require__(71)(module))) - -/***/ }, -/* 60 */ -/***/ function(module, exports, __webpack_require__) { + }; + })('drag'); /** - * Creation of the ClusterMixin var. + * @module gestures + */ + /** + * trigger a simple gesture event, so you can do anything in your handler. + * only usable if you know what your doing... * - * This contains all the functions the Network object can use to employ clustering + * @class Gesture + * @static */ - /** - * This is only called in the constructor of the network object - * - */ - exports.startWithClustering = function() { - // cluster if the data set is big - this.clusterToFit(this.constants.clustering.initialMaxNodes, true); - - // updates the lables after clustering - this.updateLabels(); - - // this is called here because if clusterin is disabled, the start and stabilize are called in - // the setData function. - if (this.stabilize) { - this._stabilize(); - } - this.start(); + * @event gesture + * @param {Object} ev + */ + Hammer.gestures.Gesture = { + name: 'gesture', + index: 1337, + handler: function releaseGesture(ev, inst) { + inst.trigger(this.name, ev); + } }; /** - * This function clusters until the initialMaxNodes has been reached + * @module gestures + */ + /** + * Touch stays at the same place for x time * - * @param {Number} maxNumberOfNodes - * @param {Boolean} reposition + * @class Hold + * @static + */ + /** + * @event hold + * @param {Object} ev */ - exports.clusterToFit = function(maxNumberOfNodes, reposition) { - var numberOfNodes = this.nodeIndices.length; - - var maxLevels = 50; - var level = 0; - // we first cluster the hubs, then we pull in the outliers, repeat - while (numberOfNodes > maxNumberOfNodes && level < maxLevels) { - if (level % 3 == 0) { - this.forceAggregateHubs(true); - this.normalizeClusterLevels(); - } - else { - this.increaseClusterLevel(); // this also includes a cluster normalization - } + /** + * @param {String} name + */ + (function(name) { + var timer; - numberOfNodes = this.nodeIndices.length; - level += 1; - } + function holdGesture(ev, inst) { + var options = inst.options, + current = Detection.current; - // after the clustering we reposition the nodes to reduce the initial chaos - if (level > 0 && reposition == true) { - this.repositionNodes(); - } - this._updateCalculationNodes(); - }; + switch(ev.eventType) { + case EVENT_START: + clearTimeout(timer); - /** - * This function can be called to open up a specific cluster. It is only called by - * It will unpack the cluster back one level. - * - * @param node | Node object: cluster to open. - */ - exports.openCluster = function(node) { - var isMovingBeforeClustering = this.moving; - if (node.clusterSize > this.constants.clustering.sectorThreshold && this._nodeInActiveArea(node) && - !(this._sector() == "default" && this.nodeIndices.length == 1)) { - // this loads a new sector, loads the nodes and edges and nodeIndices of it. - this._addSector(node); - var level = 0; + // set the gesture so we can check in the timeout if it still is + current.name = name; - // we decluster until we reach a decent number of nodes - while ((this.nodeIndices.length < this.constants.clustering.initialMaxNodes) && (level < 10)) { - this.decreaseClusterLevel(); - level += 1; - } + // set timer and if after the timeout it still is hold, + // we trigger the hold event + timer = setTimeout(function() { + if(current && current.name == name) { + inst.trigger(name, ev); + } + }, options.holdTimeout); + break; - } - else { - this._expandClusterNode(node,false,true); + case EVENT_MOVE: + if(ev.distance > options.holdThreshold) { + clearTimeout(timer); + } + break; - // update the index list, dynamic edges and labels - this._updateNodeIndexList(); - this._updateDynamicEdges(); - this._updateCalculationNodes(); - this.updateLabels(); - } + case EVENT_RELEASE: + clearTimeout(timer); + break; + } + } - // if the simulation was settled, we restart the simulation if a cluster has been formed or expanded - if (this.moving != isMovingBeforeClustering) { - this.start(); - } - }; + Hammer.gestures.Hold = { + name: name, + index: 10, + defaults: { + /** + * @property holdTimeout + * @type {Number} + * @default 500 + */ + holdTimeout: 500, + /** + * movement allowed while holding + * @property holdThreshold + * @type {Number} + * @default 2 + */ + holdThreshold: 2 + }, + handler: holdGesture + }; + })('hold'); /** - * This calls the updateClustes with default arguments + * @module gestures */ - exports.updateClustersDefault = function() { - if (this.constants.clustering.enabled == true) { - this.updateClusters(0,false,false); - } - }; - - /** - * This function can be called to increase the cluster level. This means that the nodes with only one edge connection will - * be clustered with their connected node. This can be repeated as many times as needed. - * This can be called externally (by a keybind for instance) to reduce the complexity of big datasets. + * when a touch is being released from the page + * + * @class Release + * @static */ - exports.increaseClusterLevel = function() { - this.updateClusters(-1,false,true); - }; - - /** - * This function can be called to decrease the cluster level. This means that the nodes with only one edge connection will - * be unpacked if they are a cluster. This can be repeated as many times as needed. - * This can be called externally (by a key-bind for instance) to look into clusters without zooming. + * @event release + * @param {Object} ev */ - exports.decreaseClusterLevel = function() { - this.updateClusters(1,false,true); + Hammer.gestures.Release = { + name: 'release', + index: Infinity, + handler: function releaseGesture(ev, inst) { + if(ev.eventType == EVENT_RELEASE) { + inst.trigger(this.name, ev); + } + } }; - /** - * This is the main clustering function. It clusters and declusters on zoom or forced - * This function clusters on zoom, it can be called with a predefined zoom direction - * If out, check if we can form clusters, if in, check if we can open clusters. - * This function is only called from _zoom() - * - * @param {Number} zoomDirection | -1 / 0 / +1 for zoomOut / determineByZoom / zoomIn - * @param {Boolean} recursive | enabled or disable recursive calling of the opening of clusters - * @param {Boolean} force | enabled or disable forcing - * @param {Boolean} doNotStart | if true do not call start + * @module gestures + */ + /** + * triggers swipe events when the end velocity is above the threshold + * for best usage, set `preventDefault` (on the drag gesture) to `true` + * ```` + * hammertime.on("dragleft swipeleft", function(ev) { + * console.log(ev); + * ev.gesture.preventDefault(); + * }); + * ```` * + * @class Swipe + * @static */ - exports.updateClusters = function(zoomDirection,recursive,force,doNotStart) { - var isMovingBeforeClustering = this.moving; - var amountOfNodes = this.nodeIndices.length; - - // on zoom out collapse the sector if the scale is at the level the sector was made - if (this.previousScale > this.scale && zoomDirection == 0) { - this._collapseSector(); - } - - // check if we zoom in or out - if (this.previousScale > this.scale || zoomDirection == -1) { // zoom out - // forming clusters when forced pulls outliers in. When not forced, the edge length of the - // outer nodes determines if it is being clustered - this._formClusters(force); - } - else if (this.previousScale < this.scale || zoomDirection == 1) { // zoom in - if (force == true) { - // _openClusters checks for each node if the formationScale of the cluster is smaller than - // the current scale and if so, declusters. When forced, all clusters are reduced by one step - this._openClusters(recursive,force); - } - else { - // if a cluster takes up a set percentage of the active window - this._openClustersBySize(); - } - } - this._updateNodeIndexList(); + /** + * @event swipe + * @param {Object} ev + */ + /** + * @event swipeleft + * @param {Object} ev + */ + /** + * @event swiperight + * @param {Object} ev + */ + /** + * @event swipeup + * @param {Object} ev + */ + /** + * @event swipedown + * @param {Object} ev + */ + Hammer.gestures.Swipe = { + name: 'swipe', + index: 40, + defaults: { + /** + * @property swipeMinTouches + * @type {Number} + * @default 1 + */ + swipeMinTouches: 1, - // if a cluster was NOT formed and the user zoomed out, we try clustering by hubs - if (this.nodeIndices.length == amountOfNodes && (this.previousScale > this.scale || zoomDirection == -1)) { - this._aggregateHubs(force); - this._updateNodeIndexList(); - } + /** + * @property swipeMaxTouches + * @type {Number} + * @default 1 + */ + swipeMaxTouches: 1, - // we now reduce chains. - if (this.previousScale > this.scale || zoomDirection == -1) { // zoom out - this.handleChains(); - this._updateNodeIndexList(); - } + /** + * horizontal swipe velocity + * @property swipeVelocityX + * @type {Number} + * @default 0.6 + */ + swipeVelocityX: 0.6, - this.previousScale = this.scale; + /** + * vertical swipe velocity + * @property swipeVelocityY + * @type {Number} + * @default 0.6 + */ + swipeVelocityY: 0.6 + }, - // rest of the update the index list, dynamic edges and labels - this._updateDynamicEdges(); - this.updateLabels(); + handler: function swipeGesture(ev, inst) { + if(ev.eventType == EVENT_RELEASE) { + var touches = ev.touches.length, + options = inst.options; - // if a cluster was formed, we increase the clusterSession - if (this.nodeIndices.length < amountOfNodes) { // this means a clustering operation has taken place - this.clusterSession += 1; - // if clusters have been made, we normalize the cluster level - this.normalizeClusterLevels(); - } + // max touches + if(touches < options.swipeMinTouches || + touches > options.swipeMaxTouches) { + return; + } - if (doNotStart == false || doNotStart === undefined) { - // if the simulation was settled, we restart the simulation if a cluster has been formed or expanded - if (this.moving != isMovingBeforeClustering) { - this.start(); + // when the distance we moved is too small we skip this gesture + // or we can be already in dragging + if(ev.velocityX > options.swipeVelocityX || + ev.velocityY > options.swipeVelocityY) { + // trigger swipe events + inst.trigger(this.name, ev); + inst.trigger(this.name + ev.direction, ev); + } + } } - } - - this._updateCalculationNodes(); }; /** - * This function handles the chains. It is called on every updateClusters(). + * @module gestures */ - exports.handleChains = function() { - // after clustering we check how many chains there are - var chainPercentage = this._getChainFraction(); - if (chainPercentage > this.constants.clustering.chainThreshold) { - this._reduceAmountOfChains(1 - this.constants.clustering.chainThreshold / chainPercentage) - - } - }; - /** - * this functions starts clustering by hubs - * The minimum hub threshold is set globally + * Single tap and a double tap on a place * - * @private + * @class Tap + * @static + */ + /** + * @event tap + * @param {Object} ev + */ + /** + * @event doubletap + * @param {Object} ev */ - exports._aggregateHubs = function(force) { - this._getHubSize(); - this._formClustersByHub(force,false); - }; - /** - * This function is fired by keypress. It forces hubs to form. - * + * @param {String} name */ - exports.forceAggregateHubs = function(doNotStart) { - var isMovingBeforeClustering = this.moving; - var amountOfNodes = this.nodeIndices.length; + (function(name) { + var hasMoved = false; - this._aggregateHubs(true); + function tapGesture(ev, inst) { + var options = inst.options, + current = Detection.current, + prev = Detection.previous, + sincePrev, + didDoubleTap; - // update the index list, dynamic edges and labels - this._updateNodeIndexList(); - this._updateDynamicEdges(); - this.updateLabels(); + switch(ev.eventType) { + case EVENT_START: + hasMoved = false; + break; - // if a cluster was formed, we increase the clusterSession - if (this.nodeIndices.length != amountOfNodes) { - this.clusterSession += 1; - } + case EVENT_MOVE: + hasMoved = hasMoved || (ev.distance > options.tapMaxDistance); + break; - if (doNotStart == false || doNotStart === undefined) { - // if the simulation was settled, we restart the simulation if a cluster has been formed or expanded - if (this.moving != isMovingBeforeClustering) { - this.start(); - } - } - }; + case EVENT_END: + if(!Utils.inStr(ev.srcEvent.type, 'cancel') && ev.deltaTime < options.tapMaxTime && !hasMoved) { + // previous gesture, for the double tap since these are two different gesture detections + sincePrev = prev && prev.lastEvent && ev.timeStamp - prev.lastEvent.timeStamp; + didDoubleTap = false; - /** - * If a cluster takes up more than a set percentage of the screen, open the cluster - * - * @private - */ - exports._openClustersBySize = function() { - for (var nodeId in this.nodes) { - if (this.nodes.hasOwnProperty(nodeId)) { - var node = this.nodes[nodeId]; - if (node.inView() == true) { - if ((node.width*this.scale > this.constants.clustering.screenSizeThreshold * this.frame.canvas.clientWidth) || - (node.height*this.scale > this.constants.clustering.screenSizeThreshold * this.frame.canvas.clientHeight)) { - this.openCluster(node); + // check if double tap + if(prev && prev.name == name && + (sincePrev && sincePrev < options.doubleTapInterval) && + ev.distance < options.doubleTapDistance) { + inst.trigger('doubletap', ev); + didDoubleTap = true; + } + + // do a single tap + if(!didDoubleTap || options.tapAlways) { + current.name = name; + inst.trigger(current.name, ev); + } + } + break; } - } } - } - }; + Hammer.gestures.Tap = { + name: name, + index: 100, + handler: tapGesture, + defaults: { + /** + * max time of a tap, this is for the slow tappers + * @property tapMaxTime + * @type {Number} + * @default 250 + */ + tapMaxTime: 250, + + /** + * max distance of movement of a tap, this is for the slow tappers + * @property tapMaxDistance + * @type {Number} + * @default 10 + */ + tapMaxDistance: 10, + + /** + * always trigger the `tap` event, even while double-tapping + * @property tapAlways + * @type {Boolean} + * @default true + */ + tapAlways: true, + + /** + * max distance between two taps + * @property doubleTapDistance + * @type {Number} + * @default 20 + */ + doubleTapDistance: 20, + + /** + * max time between two taps + * @property doubleTapInterval + * @type {Number} + * @default 300 + */ + doubleTapInterval: 300 + } + }; + })('tap'); /** - * This function loops over all nodes in the nodeIndices list. For each node it checks if it is a cluster and if it - * has to be opened based on the current zoom level. - * - * @private + * @module gestures */ - exports._openClusters = function(recursive,force) { - for (var i = 0; i < this.nodeIndices.length; i++) { - var node = this.nodes[this.nodeIndices[i]]; - this._expandClusterNode(node,recursive,force); - this._updateCalculationNodes(); - } - }; - /** - * This function checks if a node has to be opened. This is done by checking the zoom level. - * If the node contains child nodes, this function is recursively called on the child nodes as well. - * This recursive behaviour is optional and can be set by the recursive argument. + * when a touch is being touched at the page * - * @param {Node} parentNode | to check for cluster and expand - * @param {Boolean} recursive | enabled or disable recursive calling - * @param {Boolean} force | enabled or disable forcing - * @param {Boolean} [openAll] | This will recursively force all nodes in the parent to be released - * @private + * @class Touch + * @static */ - exports._expandClusterNode = function(parentNode, recursive, force, openAll) { - // first check if node is a cluster - if (parentNode.clusterSize > 1) { - // this means that on a double tap event or a zoom event, the cluster fully unpacks if it is smaller than 20 - if (parentNode.clusterSize < this.constants.clustering.sectorThreshold) { - openAll = true; - } - recursive = openAll ? true : recursive; + /** + * @event touch + * @param {Object} ev + */ + Hammer.gestures.Touch = { + name: 'touch', + index: -Infinity, + defaults: { + /** + * call preventDefault at touchstart, and makes the element blocking by disabling the scrolling of the page, + * but it improves gestures like transforming and dragging. + * be careful with using this, it can be very annoying for users to be stuck on the page + * @property preventDefault + * @type {Boolean} + * @default false + */ + preventDefault: false, - // if the last child has been added on a smaller scale than current scale decluster - if (parentNode.formationScale < this.scale || force == true) { - // we will check if any of the contained child nodes should be removed from the cluster - for (var containedNodeId in parentNode.containedNodes) { - if (parentNode.containedNodes.hasOwnProperty(containedNodeId)) { - var childNode = parentNode.containedNodes[containedNodeId]; + /** + * disable mouse events, so only touch (or pen!) input triggers events + * @property preventMouse + * @type {Boolean} + * @default false + */ + preventMouse: false + }, + handler: function touchGesture(ev, inst) { + if(inst.options.preventMouse && ev.pointerType == POINTER_MOUSE) { + ev.stopDetect(); + return; + } - // force expand will expand the largest cluster size clusters. Since we cluster from outside in, we assume that - // the largest cluster is the one that comes from outside - if (force == true) { - if (childNode.clusterSession == parentNode.clusterSessions[parentNode.clusterSessions.length-1] - || openAll) { - this._expelChildFromParent(parentNode,containedNodeId,recursive,force,openAll); - } - } - else { - if (this._nodeInActiveArea(parentNode)) { - this._expelChildFromParent(parentNode,containedNodeId,recursive,force,openAll); - } - } + if(inst.options.preventDefault) { + ev.preventDefault(); + } + + if(ev.eventType == EVENT_TOUCH) { + inst.trigger('touch', ev); } - } } - } }; /** - * ONLY CALLED FROM _expandClusterNode - * - * This function will expel a child_node from a parent_node. This is to de-cluster the node. This function will remove - * the child node from the parent contained_node object and put it back into the global nodes object. - * The same holds for the edge that was connected to the child node. It is moved back into the global edges object. + * @module gestures + */ + /** + * User want to scale or rotate with 2 fingers + * Preventing the default browser behavior is a good way to improve feel and working. This can be done with the + * `preventDefault` option. * - * @param {Node} parentNode | the parent node - * @param {String} containedNodeId | child_node id as it is contained in the containedNodes object of the parent node - * @param {Boolean} recursive | This will also check if the child needs to be expanded. - * With force and recursive both true, the entire cluster is unpacked - * @param {Boolean} force | This will disregard the zoom level and will expel this child from the parent - * @param {Boolean} openAll | This will recursively force all nodes in the parent to be released - * @private + * @class Transform + * @static + */ + /** + * @event transform + * @param {Object} ev + */ + /** + * @event transformstart + * @param {Object} ev + */ + /** + * @event transformend + * @param {Object} ev + */ + /** + * @event pinchin + * @param {Object} ev + */ + /** + * @event pinchout + * @param {Object} ev + */ + /** + * @event rotate + * @param {Object} ev */ - exports._expelChildFromParent = function(parentNode, containedNodeId, recursive, force, openAll) { - var childNode = parentNode.containedNodes[containedNodeId]; - // if child node has been added on smaller scale than current, kick out - if (childNode.formationScale < this.scale || force == true) { - // unselect all selected items - this._unselectAll(); + /** + * @param {String} name + */ + (function(name) { + var triggered = false; - // put the child node back in the global nodes object - this.nodes[containedNodeId] = childNode; + function transformGesture(ev, inst) { + switch(ev.eventType) { + case EVENT_START: + triggered = false; + break; - // release the contained edges from this childNode back into the global edges - this._releaseContainedEdges(parentNode,childNode); + case EVENT_MOVE: + // at least multitouch + if(ev.touches.length < 2) { + return; + } - // reconnect rerouted edges to the childNode - this._connectEdgeBackToChild(parentNode,childNode); + var scaleThreshold = Math.abs(1 - ev.scale); + var rotationThreshold = Math.abs(ev.rotation); - // validate all edges in dynamicEdges - this._validateEdges(parentNode); + // when the distance we moved is too small we skip this gesture + // or we can be already in dragging + if(scaleThreshold < inst.options.transformMinScale && + rotationThreshold < inst.options.transformMinRotation) { + return; + } - // undo the changes from the clustering operation on the parent node - parentNode.options.mass -= childNode.options.mass; - parentNode.clusterSize -= childNode.clusterSize; - parentNode.options.fontSize = Math.min(this.constants.clustering.maxFontSize, this.constants.nodes.fontSize + this.constants.clustering.fontSizeMultiplier*(parentNode.clusterSize-1)); - parentNode.dynamicEdgesLength = parentNode.dynamicEdges.length; + // we are transforming! + Detection.current.name = name; - // place the child node near the parent, not at the exact same location to avoid chaos in the system - childNode.x = parentNode.x + parentNode.growthIndicator * (0.5 - Math.random()); - childNode.y = parentNode.y + parentNode.growthIndicator * (0.5 - Math.random()); + // first time, trigger dragstart event + if(!triggered) { + inst.trigger(name + 'start', ev); + triggered = true; + } - // remove node from the list - delete parentNode.containedNodes[containedNodeId]; + inst.trigger(name, ev); // basic transform event - // check if there are other childs with this clusterSession in the parent. - var othersPresent = false; - for (var childNodeId in parentNode.containedNodes) { - if (parentNode.containedNodes.hasOwnProperty(childNodeId)) { - if (parentNode.containedNodes[childNodeId].clusterSession == childNode.clusterSession) { - othersPresent = true; - break; + // trigger rotate event + if(rotationThreshold > inst.options.transformMinRotation) { + inst.trigger('rotate', ev); + } + + // trigger pinch event + if(scaleThreshold > inst.options.transformMinScale) { + inst.trigger('pinch', ev); + inst.trigger('pinch' + (ev.scale < 1 ? 'in' : 'out'), ev); + } + break; + + case EVENT_RELEASE: + if(triggered && ev.changedLength < 2) { + inst.trigger(name + 'end', ev); + triggered = false; + } + break; } - } - } - // if there are no others, remove the cluster session from the list - if (othersPresent == false) { - parentNode.clusterSessions.pop(); } - this._repositionBezierNodes(childNode); - // this._repositionBezierNodes(parentNode); + Hammer.gestures.Transform = { + name: name, + index: 45, + defaults: { + /** + * minimal scale factor, no scale is 1, zoomin is to 0 and zoomout until higher then 1 + * @property transformMinScale + * @type {Number} + * @default 0.01 + */ + transformMinScale: 0.01, - // remove the clusterSession from the child node - childNode.clusterSession = 0; + /** + * rotation in degrees + * @property transformMinRotation + * @type {Number} + * @default 1 + */ + transformMinRotation: 1 + }, - // recalculate the size of the node on the next time the node is rendered - parentNode.clearSizeCache(); + handler: transformGesture + }; + })('transform'); - // restart the simulation to reorganise all nodes - this.moving = true; - } + /** + * @module hammer + */ - // check if a further expansion step is possible if recursivity is enabled - if (recursive == true) { - this._expandClusterNode(childNode,recursive,force,openAll); - } - }; + // AMD export + if(true) { + !(__WEBPACK_AMD_DEFINE_RESULT__ = function() { + return Hammer; + }.call(exports, __webpack_require__, exports, module), __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__)); + // commonjs export + } else if(typeof module !== 'undefined' && module.exports) { + module.exports = Hammer; + // browser export + } else { + window.Hammer = Hammer; + } + })(window); - /** - * position the bezier nodes at the center of the edges - * - * @param node - * @private - */ - exports._repositionBezierNodes = function(node) { - for (var i = 0; i < node.dynamicEdges.length; i++) { - node.dynamicEdges[i].positionBezierNode(); - } - }; +/***/ }, +/* 65 */ +/***/ function(module, exports, __webpack_require__) { + var __WEBPACK_AMD_DEFINE_RESULT__;/* WEBPACK VAR INJECTION */(function(global, module) {//! moment.js + //! version : 2.9.0 + //! authors : Tim Wood, Iskren Chernev, Moment.js contributors + //! license : MIT + //! momentjs.com - /** - * This function checks if any nodes at the end of their trees have edges below a threshold length - * This function is called only from updateClusters() - * forceLevelCollapse ignores the length of the edge and collapses one level - * This means that a node with only one edge will be clustered with its connected node - * - * @private - * @param {Boolean} force - */ - exports._formClusters = function(force) { - if (force == false) { - this._formClustersByZoom(); - } - else { - this._forceClustersByZoom(); - } - }; + (function (undefined) { + /************************************ + Constants + ************************************/ + var moment, + VERSION = '2.9.0', + // the global-scope this is NOT the global object in Node.js + globalScope = (typeof global !== 'undefined' && (typeof window === 'undefined' || window === global.window)) ? global : this, + oldGlobalMoment, + round = Math.round, + hasOwnProperty = Object.prototype.hasOwnProperty, + i, - /** - * This function handles the clustering by zooming out, this is based on a minimum edge distance - * - * @private - */ - exports._formClustersByZoom = function() { - var dx,dy,length, - minLength = this.constants.clustering.clusterEdgeThreshold/this.scale; + YEAR = 0, + MONTH = 1, + DATE = 2, + HOUR = 3, + MINUTE = 4, + SECOND = 5, + MILLISECOND = 6, - // check if any edges are shorter than minLength and start the clustering - // the clustering favours the node with the larger mass - for (var edgeId in this.edges) { - if (this.edges.hasOwnProperty(edgeId)) { - var edge = this.edges[edgeId]; - if (edge.connected) { - if (edge.toId != edge.fromId) { - dx = (edge.to.x - edge.from.x); - dy = (edge.to.y - edge.from.y); - length = Math.sqrt(dx * dx + dy * dy); + // internal storage for locale config files + locales = {}, + // extra moment internal properties (plugins register props here) + momentProperties = [], - if (length < minLength) { - // first check which node is larger - var parentNode = edge.from; - var childNode = edge.to; - if (edge.to.options.mass > edge.from.options.mass) { - parentNode = edge.to; - childNode = edge.from; - } + // check for nodeJS + hasModule = (typeof module !== 'undefined' && module && module.exports), - if (childNode.dynamicEdgesLength == 1) { - this._addToCluster(parentNode,childNode,false); - } - else if (parentNode.dynamicEdgesLength == 1) { - this._addToCluster(childNode,parentNode,false); - } - } - } - } - } - } - }; + // ASP.NET json date format regex + aspNetJsonRegex = /^\/?Date\((\-?\d+)/i, + aspNetTimeSpanJsonRegex = /(\-)?(?:(\d*)\.)?(\d+)\:(\d+)(?:\:(\d+)\.?(\d{3})?)?/, - /** - * This function forces the network to cluster all nodes with only one connecting edge to their - * connected node. - * - * @private - */ - exports._forceClustersByZoom = function() { - for (var nodeId in this.nodes) { - // another node could have absorbed this child. - if (this.nodes.hasOwnProperty(nodeId)) { - var childNode = this.nodes[nodeId]; + // from http://docs.closure-library.googlecode.com/git/closure_goog_date_date.js.source.html + // somewhat more in line with 4.4.3.2 2004 spec, but allows decimal anywhere + isoDurationRegex = /^(-)?P(?:(?:([0-9,.]*)Y)?(?:([0-9,.]*)M)?(?:([0-9,.]*)D)?(?:T(?:([0-9,.]*)H)?(?:([0-9,.]*)M)?(?:([0-9,.]*)S)?)?|([0-9,.]*)W)$/, - // the edges can be swallowed by another decrease - if (childNode.dynamicEdgesLength == 1 && childNode.dynamicEdges.length != 0) { - var edge = childNode.dynamicEdges[0]; - var parentNode = (edge.toId == childNode.id) ? this.nodes[edge.fromId] : this.nodes[edge.toId]; + // format tokens + formattingTokens = /(\[[^\[]*\])|(\\)?(Mo|MM?M?M?|Do|DDDo|DD?D?D?|ddd?d?|do?|w[o|w]?|W[o|W]?|Q|YYYYYY|YYYYY|YYYY|YY|gg(ggg?)?|GG(GGG?)?|e|E|a|A|hh?|HH?|mm?|ss?|S{1,4}|x|X|zz?|ZZ?|.)/g, + localFormattingTokens = /(\[[^\[]*\])|(\\)?(LTS|LT|LL?L?L?|l{1,4})/g, - // group to the largest node - if (childNode.id != parentNode.id) { - if (parentNode.options.mass > childNode.options.mass) { - this._addToCluster(parentNode,childNode,true); - } - else { - this._addToCluster(childNode,parentNode,true); - } - } - } - } - } - }; + // parsing token regexes + parseTokenOneOrTwoDigits = /\d\d?/, // 0 - 99 + parseTokenOneToThreeDigits = /\d{1,3}/, // 0 - 999 + parseTokenOneToFourDigits = /\d{1,4}/, // 0 - 9999 + parseTokenOneToSixDigits = /[+\-]?\d{1,6}/, // -999,999 - 999,999 + parseTokenDigits = /\d+/, // nonzero number of digits + parseTokenWord = /[0-9]*['a-z\u00A0-\u05FF\u0700-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]+|[\u0600-\u06FF\/]+(\s*?[\u0600-\u06FF]+){1,2}/i, // any word (or two) characters or numbers including two/three word month in arabic. + parseTokenTimezone = /Z|[\+\-]\d\d:?\d\d/gi, // +00:00 -00:00 +0000 -0000 or Z + parseTokenT = /T/i, // T (ISO separator) + parseTokenOffsetMs = /[\+\-]?\d+/, // 1234567890123 + parseTokenTimestampMs = /[\+\-]?\d+(\.\d{1,3})?/, // 123456789 123456789.123 + //strict parsing regexes + parseTokenOneDigit = /\d/, // 0 - 9 + parseTokenTwoDigits = /\d\d/, // 00 - 99 + parseTokenThreeDigits = /\d{3}/, // 000 - 999 + parseTokenFourDigits = /\d{4}/, // 0000 - 9999 + parseTokenSixDigits = /[+-]?\d{6}/, // -999,999 - 999,999 + parseTokenSignedNumber = /[+-]?\d+/, // -inf - inf - /** - * To keep the nodes of roughly equal size we normalize the cluster levels. - * This function clusters a node to its smallest connected neighbour. - * - * @param node - * @private - */ - exports._clusterToSmallestNeighbour = function(node) { - var smallestNeighbour = -1; - var smallestNeighbourNode = null; - for (var i = 0; i < node.dynamicEdges.length; i++) { - if (node.dynamicEdges[i] !== undefined) { - var neighbour = null; - if (node.dynamicEdges[i].fromId != node.id) { - neighbour = node.dynamicEdges[i].from; - } - else if (node.dynamicEdges[i].toId != node.id) { - neighbour = node.dynamicEdges[i].to; - } + // iso 8601 regex + // 0000-00-00 0000-W00 or 0000-W00-0 + T + 00 or 00:00 or 00:00:00 or 00:00:00.000 + +00:00 or +0000 or +00) + isoRegex = /^\s*(?:[+-]\d{6}|\d{4})-(?:(\d\d-\d\d)|(W\d\d$)|(W\d\d-\d)|(\d\d\d))((T| )(\d\d(:\d\d(:\d\d(\.\d+)?)?)?)?([\+\-]\d\d(?::?\d\d)?|\s*Z)?)?$/, + isoFormat = 'YYYY-MM-DDTHH:mm:ssZ', - if (neighbour != null && smallestNeighbour > neighbour.clusterSessions.length) { - smallestNeighbour = neighbour.clusterSessions.length; - smallestNeighbourNode = neighbour; - } - } - } + isoDates = [ + ['YYYYYY-MM-DD', /[+-]\d{6}-\d{2}-\d{2}/], + ['YYYY-MM-DD', /\d{4}-\d{2}-\d{2}/], + ['GGGG-[W]WW-E', /\d{4}-W\d{2}-\d/], + ['GGGG-[W]WW', /\d{4}-W\d{2}/], + ['YYYY-DDD', /\d{4}-\d{3}/] + ], - if (neighbour != null && this.nodes[neighbour.id] !== undefined) { - this._addToCluster(neighbour, node, true); - } - }; + // iso time formats and regexes + isoTimes = [ + ['HH:mm:ss.SSSS', /(T| )\d\d:\d\d:\d\d\.\d+/], + ['HH:mm:ss', /(T| )\d\d:\d\d:\d\d/], + ['HH:mm', /(T| )\d\d:\d\d/], + ['HH', /(T| )\d\d/] + ], + // timezone chunker '+10:00' > ['10', '00'] or '-1530' > ['-', '15', '30'] + parseTimezoneChunker = /([\+\-]|\d\d)/gi, - /** - * This function forms clusters from hubs, it loops over all nodes - * - * @param {Boolean} force | Disregard zoom level - * @param {Boolean} onlyEqual | This only clusters a hub with a specific number of edges - * @private - */ - exports._formClustersByHub = function(force, onlyEqual) { - // we loop over all nodes in the list - for (var nodeId in this.nodes) { - // we check if it is still available since it can be used by the clustering in this loop - if (this.nodes.hasOwnProperty(nodeId)) { - this._formClusterFromHub(this.nodes[nodeId],force,onlyEqual); + // getter and setter names + proxyGettersAndSetters = 'Date|Hours|Minutes|Seconds|Milliseconds'.split('|'), + unitMillisecondFactors = { + 'Milliseconds' : 1, + 'Seconds' : 1e3, + 'Minutes' : 6e4, + 'Hours' : 36e5, + 'Days' : 864e5, + 'Months' : 2592e6, + 'Years' : 31536e6 + }, + + unitAliases = { + ms : 'millisecond', + s : 'second', + m : 'minute', + h : 'hour', + d : 'day', + D : 'date', + w : 'week', + W : 'isoWeek', + M : 'month', + Q : 'quarter', + y : 'year', + DDD : 'dayOfYear', + e : 'weekday', + E : 'isoWeekday', + gg: 'weekYear', + GG: 'isoWeekYear' + }, + + camelFunctions = { + dayofyear : 'dayOfYear', + isoweekday : 'isoWeekday', + isoweek : 'isoWeek', + weekyear : 'weekYear', + isoweekyear : 'isoWeekYear' + }, + + // format function strings + formatFunctions = {}, + + // default relative time thresholds + relativeTimeThresholds = { + s: 45, // seconds to minute + m: 45, // minutes to hour + h: 22, // hours to day + d: 26, // days to month + M: 11 // months to year + }, + + // tokens to ordinalize and pad + ordinalizeTokens = 'DDD w W M D d'.split(' '), + paddedTokens = 'M D H h m s w W'.split(' '), + + formatTokenFunctions = { + M : function () { + return this.month() + 1; + }, + MMM : function (format) { + return this.localeData().monthsShort(this, format); + }, + MMMM : function (format) { + return this.localeData().months(this, format); + }, + D : function () { + return this.date(); + }, + DDD : function () { + return this.dayOfYear(); + }, + d : function () { + return this.day(); + }, + dd : function (format) { + return this.localeData().weekdaysMin(this, format); + }, + ddd : function (format) { + return this.localeData().weekdaysShort(this, format); + }, + dddd : function (format) { + return this.localeData().weekdays(this, format); + }, + w : function () { + return this.week(); + }, + W : function () { + return this.isoWeek(); + }, + YY : function () { + return leftZeroFill(this.year() % 100, 2); + }, + YYYY : function () { + return leftZeroFill(this.year(), 4); + }, + YYYYY : function () { + return leftZeroFill(this.year(), 5); + }, + YYYYYY : function () { + var y = this.year(), sign = y >= 0 ? '+' : '-'; + return sign + leftZeroFill(Math.abs(y), 6); + }, + gg : function () { + return leftZeroFill(this.weekYear() % 100, 2); + }, + gggg : function () { + return leftZeroFill(this.weekYear(), 4); + }, + ggggg : function () { + return leftZeroFill(this.weekYear(), 5); + }, + GG : function () { + return leftZeroFill(this.isoWeekYear() % 100, 2); + }, + GGGG : function () { + return leftZeroFill(this.isoWeekYear(), 4); + }, + GGGGG : function () { + return leftZeroFill(this.isoWeekYear(), 5); + }, + e : function () { + return this.weekday(); + }, + E : function () { + return this.isoWeekday(); + }, + a : function () { + return this.localeData().meridiem(this.hours(), this.minutes(), true); + }, + A : function () { + return this.localeData().meridiem(this.hours(), this.minutes(), false); + }, + H : function () { + return this.hours(); + }, + h : function () { + return this.hours() % 12 || 12; + }, + m : function () { + return this.minutes(); + }, + s : function () { + return this.seconds(); + }, + S : function () { + return toInt(this.milliseconds() / 100); + }, + SS : function () { + return leftZeroFill(toInt(this.milliseconds() / 10), 2); + }, + SSS : function () { + return leftZeroFill(this.milliseconds(), 3); + }, + SSSS : function () { + return leftZeroFill(this.milliseconds(), 3); + }, + Z : function () { + var a = this.utcOffset(), + b = '+'; + if (a < 0) { + a = -a; + b = '-'; + } + return b + leftZeroFill(toInt(a / 60), 2) + ':' + leftZeroFill(toInt(a) % 60, 2); + }, + ZZ : function () { + var a = this.utcOffset(), + b = '+'; + if (a < 0) { + a = -a; + b = '-'; + } + return b + leftZeroFill(toInt(a / 60), 2) + leftZeroFill(toInt(a) % 60, 2); + }, + z : function () { + return this.zoneAbbr(); + }, + zz : function () { + return this.zoneName(); + }, + x : function () { + return this.valueOf(); + }, + X : function () { + return this.unix(); + }, + Q : function () { + return this.quarter(); + } + }, + + deprecations = {}, + + lists = ['months', 'monthsShort', 'weekdays', 'weekdaysShort', 'weekdaysMin'], + + updateInProgress = false; + + // Pick the first defined of two or three arguments. dfl comes from + // default. + function dfl(a, b, c) { + switch (arguments.length) { + case 2: return a != null ? a : b; + case 3: return a != null ? a : b != null ? b : c; + default: throw new Error('Implement me'); + } } - } - }; - /** - * This function forms a cluster from a specific preselected hub node - * - * @param {Node} hubNode | the node we will cluster as a hub - * @param {Boolean} force | Disregard zoom level - * @param {Boolean} onlyEqual | This only clusters a hub with a specific number of edges - * @param {Number} [absorptionSizeOffset] | - * @private - */ - exports._formClusterFromHub = function(hubNode, force, onlyEqual, absorptionSizeOffset) { - if (absorptionSizeOffset === undefined) { - absorptionSizeOffset = 0; - } - // we decide if the node is a hub - if ((hubNode.dynamicEdgesLength >= this.hubThreshold && onlyEqual == false) || - (hubNode.dynamicEdgesLength == this.hubThreshold && onlyEqual == true)) { - // initialize variables - var dx,dy,length; - var minLength = this.constants.clustering.clusterEdgeThreshold/this.scale; - var allowCluster = false; + function hasOwnProp(a, b) { + return hasOwnProperty.call(a, b); + } - // we create a list of edges because the dynamicEdges change over the course of this loop - var edgesIdarray = []; - var amountOfInitialEdges = hubNode.dynamicEdges.length; - for (var j = 0; j < amountOfInitialEdges; j++) { - edgesIdarray.push(hubNode.dynamicEdges[j].id); + function defaultParsingFlags() { + // We need to deep clone this object, and es5 standard is not very + // helpful. + return { + empty : false, + unusedTokens : [], + unusedInput : [], + overflow : -2, + charsLeftOver : 0, + nullInput : false, + invalidMonth : null, + invalidFormat : false, + userInvalidated : false, + iso: false + }; } - // if the hub clustering is not forces, we check if one of the edges connected - // to a cluster is small enough based on the constants.clustering.clusterEdgeThreshold - if (force == false) { - allowCluster = false; - for (j = 0; j < amountOfInitialEdges; j++) { - var edge = this.edges[edgesIdarray[j]]; - if (edge !== undefined) { - if (edge.connected) { - if (edge.toId != edge.fromId) { - dx = (edge.to.x - edge.from.x); - dy = (edge.to.y - edge.from.y); - length = Math.sqrt(dx * dx + dy * dy); + function printMsg(msg) { + if (moment.suppressDeprecationWarnings === false && + typeof console !== 'undefined' && console.warn) { + console.warn('Deprecation warning: ' + msg); + } + } - if (length < minLength) { - allowCluster = true; - break; - } + function deprecate(msg, fn) { + var firstTime = true; + return extend(function () { + if (firstTime) { + printMsg(msg); + firstTime = false; } - } - } - } + return fn.apply(this, arguments); + }, fn); } - // start the clustering if allowed - if ((!force && allowCluster) || force) { - // we loop over all edges INITIALLY connected to this hub - for (j = 0; j < amountOfInitialEdges; j++) { - edge = this.edges[edgesIdarray[j]]; - // the edge can be clustered by this function in a previous loop - if (edge !== undefined) { - var childNode = this.nodes[(edge.fromId == hubNode.id) ? edge.toId : edge.fromId]; - // we do not want hubs to merge with other hubs nor do we want to cluster itself. - if ((childNode.dynamicEdges.length <= (this.hubThreshold + absorptionSizeOffset)) && - (childNode.id != hubNode.id)) { - this._addToCluster(hubNode,childNode,force); - } + function deprecateSimple(name, msg) { + if (!deprecations[name]) { + printMsg(msg); + deprecations[name] = true; } - } } - } - }; + function padToken(func, count) { + return function (a) { + return leftZeroFill(func.call(this, a), count); + }; + } + function ordinalizeToken(func, period) { + return function (a) { + return this.localeData().ordinal(func.call(this, a), period); + }; + } + + function monthDiff(a, b) { + // difference in months + var wholeMonthDiff = ((b.year() - a.year()) * 12) + (b.month() - a.month()), + // b is in (anchor - 1 month, anchor + 1 month) + anchor = a.clone().add(wholeMonthDiff, 'months'), + anchor2, adjust; + if (b - anchor < 0) { + anchor2 = a.clone().add(wholeMonthDiff - 1, 'months'); + // linear across the month + adjust = (b - anchor) / (anchor - anchor2); + } else { + anchor2 = a.clone().add(wholeMonthDiff + 1, 'months'); + // linear across the month + adjust = (b - anchor) / (anchor2 - anchor); + } - /** - * This function adds the child node to the parent node, creating a cluster if it is not already. - * - * @param {Node} parentNode | this is the node that will house the child node - * @param {Node} childNode | this node will be deleted from the global this.nodes and stored in the parent node - * @param {Boolean} force | true will only update the remainingEdges at the very end of the clustering, ensuring single level collapse - * @private - */ - exports._addToCluster = function(parentNode, childNode, force) { - // join child node in the parent node - parentNode.containedNodes[childNode.id] = childNode; + return -(wholeMonthDiff + adjust); + } - // manage all the edges connected to the child and parent nodes - for (var i = 0; i < childNode.dynamicEdges.length; i++) { - var edge = childNode.dynamicEdges[i]; - if (edge.toId == parentNode.id || edge.fromId == parentNode.id) { // edge connected to parentNode - this._addToContainedEdges(parentNode,childNode,edge); + while (ordinalizeTokens.length) { + i = ordinalizeTokens.pop(); + formatTokenFunctions[i + 'o'] = ordinalizeToken(formatTokenFunctions[i], i); } - else { - this._connectEdgeToCluster(parentNode,childNode,edge); + while (paddedTokens.length) { + i = paddedTokens.pop(); + formatTokenFunctions[i + i] = padToken(formatTokenFunctions[i], 2); } - } - // a contained node has no dynamic edges. - childNode.dynamicEdges = []; + formatTokenFunctions.DDDD = padToken(formatTokenFunctions.DDD, 3); - // remove circular edges from clusters - this._containCircularEdgesFromNode(parentNode,childNode); + function meridiemFixWrap(locale, hour, meridiem) { + var isPm; - // remove the childNode from the global nodes object - delete this.nodes[childNode.id]; + if (meridiem == null) { + // nothing to do + return hour; + } + if (locale.meridiemHour != null) { + return locale.meridiemHour(hour, meridiem); + } else if (locale.isPM != null) { + // Fallback + isPm = locale.isPM(meridiem); + if (isPm && hour < 12) { + hour += 12; + } + if (!isPm && hour === 12) { + hour = 0; + } + return hour; + } else { + // thie is not supposed to happen + return hour; + } + } - // update the properties of the child and parent - var massBefore = parentNode.options.mass; - childNode.clusterSession = this.clusterSession; - parentNode.options.mass += childNode.options.mass; - parentNode.clusterSize += childNode.clusterSize; - parentNode.options.fontSize = Math.min(this.constants.clustering.maxFontSize, this.constants.nodes.fontSize + this.constants.clustering.fontSizeMultiplier*parentNode.clusterSize); + /************************************ + Constructors + ************************************/ - // keep track of the clustersessions so we can open the cluster up as it has been formed. - if (parentNode.clusterSessions[parentNode.clusterSessions.length - 1] != this.clusterSession) { - parentNode.clusterSessions.push(this.clusterSession); - } + function Locale() { + } - // forced clusters only open from screen size and double tap - if (force == true) { - // parentNode.formationScale = Math.pow(1 - (1.0/11.0),this.clusterSession+3); - parentNode.formationScale = 0; - } - else { - parentNode.formationScale = this.scale; // The latest child has been added on this scale - } + // Moment prototype object + function Moment(config, skipOverflow) { + if (skipOverflow !== false) { + checkOverflow(config); + } + copyConfig(this, config); + this._d = new Date(+config._d); + // Prevent infinite loop in case updateOffset creates new moment + // objects. + if (updateInProgress === false) { + updateInProgress = true; + moment.updateOffset(this); + updateInProgress = false; + } + } - // recalculate the size of the node on the next time the node is rendered - parentNode.clearSizeCache(); + // Duration Constructor + function Duration(duration) { + var normalizedInput = normalizeObjectUnits(duration), + years = normalizedInput.year || 0, + quarters = normalizedInput.quarter || 0, + months = normalizedInput.month || 0, + weeks = normalizedInput.week || 0, + days = normalizedInput.day || 0, + hours = normalizedInput.hour || 0, + minutes = normalizedInput.minute || 0, + seconds = normalizedInput.second || 0, + milliseconds = normalizedInput.millisecond || 0; - // set the pop-out scale for the childnode - parentNode.containedNodes[childNode.id].formationScale = parentNode.formationScale; + // representation for dateAddRemove + this._milliseconds = +milliseconds + + seconds * 1e3 + // 1000 + minutes * 6e4 + // 1000 * 60 + hours * 36e5; // 1000 * 60 * 60 + // Because of dateAddRemove treats 24 hours as different from a + // day when working around DST, we need to store them separately + this._days = +days + + weeks * 7; + // It is impossible translate months into days without knowing + // which months you are are talking about, so we have to store + // it separately. + this._months = +months + + quarters * 3 + + years * 12; - // nullify the movement velocity of the child, this is to avoid hectic behaviour - childNode.clearVelocity(); + this._data = {}; - // the mass has altered, preservation of energy dictates the velocity to be updated - parentNode.updateVelocity(massBefore); + this._locale = moment.localeData(); - // restart the simulation to reorganise all nodes - this.moving = true; - }; + this._bubble(); + } + /************************************ + Helpers + ************************************/ - /** - * This function will apply the changes made to the remainingEdges during the formation of the clusters. - * This is a seperate function to allow for level-wise collapsing of the node barnesHutTree. - * It has to be called if a level is collapsed. It is called by _formClusters(). - * @private - */ - exports._updateDynamicEdges = function() { - for (var i = 0; i < this.nodeIndices.length; i++) { - var node = this.nodes[this.nodeIndices[i]]; - node.dynamicEdgesLength = node.dynamicEdges.length; - // this corrects for multiple edges pointing at the same other node - var correction = 0; - if (node.dynamicEdgesLength > 1) { - for (var j = 0; j < node.dynamicEdgesLength - 1; j++) { - var edgeToId = node.dynamicEdges[j].toId; - var edgeFromId = node.dynamicEdges[j].fromId; - for (var k = j+1; k < node.dynamicEdgesLength; k++) { - if ((node.dynamicEdges[k].toId == edgeToId && node.dynamicEdges[k].fromId == edgeFromId) || - (node.dynamicEdges[k].fromId == edgeToId && node.dynamicEdges[k].toId == edgeFromId)) { - correction += 1; - } + function extend(a, b) { + for (var i in b) { + if (hasOwnProp(b, i)) { + a[i] = b[i]; + } } - } - } - node.dynamicEdgesLength -= correction; - } - }; + if (hasOwnProp(b, 'toString')) { + a.toString = b.toString; + } - /** - * This adds an edge from the childNode to the contained edges of the parent node - * - * @param parentNode | Node object - * @param childNode | Node object - * @param edge | Edge object - * @private - */ - exports._addToContainedEdges = function(parentNode, childNode, edge) { - // create an array object if it does not yet exist for this childNode - if (!(parentNode.containedEdges.hasOwnProperty(childNode.id))) { - parentNode.containedEdges[childNode.id] = [] - } - // add this edge to the list - parentNode.containedEdges[childNode.id].push(edge); - - // remove the edge from the global edges object - delete this.edges[edge.id]; - - // remove the edge from the parent object - for (var i = 0; i < parentNode.dynamicEdges.length; i++) { - if (parentNode.dynamicEdges[i].id == edge.id) { - parentNode.dynamicEdges.splice(i,1); - break; - } - } - }; + if (hasOwnProp(b, 'valueOf')) { + a.valueOf = b.valueOf; + } - /** - * This function connects an edge that was connected to a child node to the parent node. - * It keeps track of which nodes it has been connected to with the originalId array. - * - * @param {Node} parentNode | Node object - * @param {Node} childNode | Node object - * @param {Edge} edge | Edge object - * @private - */ - exports._connectEdgeToCluster = function(parentNode, childNode, edge) { - // handle circular edges - if (edge.toId == edge.fromId) { - this._addToContainedEdges(parentNode, childNode, edge); - } - else { - if (edge.toId == childNode.id) { // edge connected to other node on the "to" side - edge.originalToId.push(childNode.id); - edge.to = parentNode; - edge.toId = parentNode.id; + return a; } - else { // edge connected to other node with the "from" side - edge.originalFromId.push(childNode.id); - edge.from = parentNode; - edge.fromId = parentNode.id; - } + function copyConfig(to, from) { + var i, prop, val; - this._addToReroutedEdges(parentNode,childNode,edge); - } - }; + if (typeof from._isAMomentObject !== 'undefined') { + to._isAMomentObject = from._isAMomentObject; + } + if (typeof from._i !== 'undefined') { + to._i = from._i; + } + if (typeof from._f !== 'undefined') { + to._f = from._f; + } + if (typeof from._l !== 'undefined') { + to._l = from._l; + } + if (typeof from._strict !== 'undefined') { + to._strict = from._strict; + } + if (typeof from._tzm !== 'undefined') { + to._tzm = from._tzm; + } + if (typeof from._isUTC !== 'undefined') { + to._isUTC = from._isUTC; + } + if (typeof from._offset !== 'undefined') { + to._offset = from._offset; + } + if (typeof from._pf !== 'undefined') { + to._pf = from._pf; + } + if (typeof from._locale !== 'undefined') { + to._locale = from._locale; + } + if (momentProperties.length > 0) { + for (i in momentProperties) { + prop = momentProperties[i]; + val = from[prop]; + if (typeof val !== 'undefined') { + to[prop] = val; + } + } + } - /** - * If a node is connected to itself, a circular edge is drawn. When clustering we want to contain - * these edges inside of the cluster. - * - * @param parentNode - * @param childNode - * @private - */ - exports._containCircularEdgesFromNode = function(parentNode, childNode) { - // manage all the edges connected to the child and parent nodes - for (var i = 0; i < parentNode.dynamicEdges.length; i++) { - var edge = parentNode.dynamicEdges[i]; - // handle circular edges - if (edge.toId == edge.fromId) { - this._addToContainedEdges(parentNode, childNode, edge); + return to; } - } - }; - - /** - * This adds an edge from the childNode to the rerouted edges of the parent node - * - * @param parentNode | Node object - * @param childNode | Node object - * @param edge | Edge object - * @private - */ - exports._addToReroutedEdges = function(parentNode, childNode, edge) { - // create an array object if it does not yet exist for this childNode - // we store the edge in the rerouted edges so we can restore it when the cluster pops open - if (!(parentNode.reroutedEdges.hasOwnProperty(childNode.id))) { - parentNode.reroutedEdges[childNode.id] = []; - } - parentNode.reroutedEdges[childNode.id].push(edge); + function absRound(number) { + if (number < 0) { + return Math.ceil(number); + } else { + return Math.floor(number); + } + } - // this edge becomes part of the dynamicEdges of the cluster node - parentNode.dynamicEdges.push(edge); - }; + // left zero fill a number + // see http://jsperf.com/left-zero-filling for performance comparison + function leftZeroFill(number, targetLength, forceSign) { + var output = '' + Math.abs(number), + sign = number >= 0; + while (output.length < targetLength) { + output = '0' + output; + } + return (sign ? (forceSign ? '+' : '') : '-') + output; + } + function positiveMomentsDifference(base, other) { + var res = {milliseconds: 0, months: 0}; - /** - * This function connects an edge that was connected to a cluster node back to the child node. - * - * @param parentNode | Node object - * @param childNode | Node object - * @private - */ - exports._connectEdgeBackToChild = function(parentNode, childNode) { - if (parentNode.reroutedEdges.hasOwnProperty(childNode.id)) { - for (var i = 0; i < parentNode.reroutedEdges[childNode.id].length; i++) { - var edge = parentNode.reroutedEdges[childNode.id][i]; - if (edge.originalFromId[edge.originalFromId.length-1] == childNode.id) { - edge.originalFromId.pop(); - edge.fromId = childNode.id; - edge.from = childNode; - } - else { - edge.originalToId.pop(); - edge.toId = childNode.id; - edge.to = childNode; - } + res.months = other.month() - base.month() + + (other.year() - base.year()) * 12; + if (base.clone().add(res.months, 'M').isAfter(other)) { + --res.months; + } - // append this edge to the list of edges connecting to the childnode - childNode.dynamicEdges.push(edge); + res.milliseconds = +other - +(base.clone().add(res.months, 'M')); - // remove the edge from the parent object - for (var j = 0; j < parentNode.dynamicEdges.length; j++) { - if (parentNode.dynamicEdges[j].id == edge.id) { - parentNode.dynamicEdges.splice(j,1); - break; - } - } + return res; } - // remove the entry from the rerouted edges - delete parentNode.reroutedEdges[childNode.id]; - } - }; + function momentsDifference(base, other) { + var res; + other = makeAs(other, base); + if (base.isBefore(other)) { + res = positiveMomentsDifference(base, other); + } else { + res = positiveMomentsDifference(other, base); + res.milliseconds = -res.milliseconds; + res.months = -res.months; + } - /** - * When loops are clustered, an edge can be both in the rerouted array and the contained array. - * This function is called last to verify that all edges in dynamicEdges are in fact connected to the - * parentNode - * - * @param parentNode | Node object - * @private - */ - exports._validateEdges = function(parentNode) { - for (var i = 0; i < parentNode.dynamicEdges.length; i++) { - var edge = parentNode.dynamicEdges[i]; - if (parentNode.id != edge.toId && parentNode.id != edge.fromId) { - parentNode.dynamicEdges.splice(i,1); + return res; } - } - }; + // TODO: remove 'name' arg after deprecation is removed + function createAdder(direction, name) { + return function (val, period) { + var dur, tmp; + //invert the arguments, but complain about it + if (period !== null && !isNaN(+period)) { + deprecateSimple(name, 'moment().' + name + '(period, number) is deprecated. Please use moment().' + name + '(number, period).'); + tmp = val; val = period; period = tmp; + } - /** - * This function released the contained edges back into the global domain and puts them back into the - * dynamic edges of both parent and child. - * - * @param {Node} parentNode | - * @param {Node} childNode | - * @private - */ - exports._releaseContainedEdges = function(parentNode, childNode) { - for (var i = 0; i < parentNode.containedEdges[childNode.id].length; i++) { - var edge = parentNode.containedEdges[childNode.id][i]; + val = typeof val === 'string' ? +val : val; + dur = moment.duration(val, period); + addOrSubtractDurationFromMoment(this, dur, direction); + return this; + }; + } - // put the edge back in the global edges object - this.edges[edge.id] = edge; + function addOrSubtractDurationFromMoment(mom, duration, isAdding, updateOffset) { + var milliseconds = duration._milliseconds, + days = duration._days, + months = duration._months; + updateOffset = updateOffset == null ? true : updateOffset; - // put the edge back in the dynamic edges of the child and parent - childNode.dynamicEdges.push(edge); - parentNode.dynamicEdges.push(edge); - } - // remove the entry from the contained edges - delete parentNode.containedEdges[childNode.id]; + if (milliseconds) { + mom._d.setTime(+mom._d + milliseconds * isAdding); + } + if (days) { + rawSetter(mom, 'Date', rawGetter(mom, 'Date') + days * isAdding); + } + if (months) { + rawMonthSetter(mom, rawGetter(mom, 'Month') + months * isAdding); + } + if (updateOffset) { + moment.updateOffset(mom, days || months); + } + } - }; + // check if is an array + function isArray(input) { + return Object.prototype.toString.call(input) === '[object Array]'; + } + function isDate(input) { + return Object.prototype.toString.call(input) === '[object Date]' || + input instanceof Date; + } + // compare two arrays, return the number of differences + function compareArrays(array1, array2, dontConvert) { + var len = Math.min(array1.length, array2.length), + lengthDiff = Math.abs(array1.length - array2.length), + diffs = 0, + i; + for (i = 0; i < len; i++) { + if ((dontConvert && array1[i] !== array2[i]) || + (!dontConvert && toInt(array1[i]) !== toInt(array2[i]))) { + diffs++; + } + } + return diffs + lengthDiff; + } + function normalizeUnits(units) { + if (units) { + var lowered = units.toLowerCase().replace(/(.)s$/, '$1'); + units = unitAliases[units] || camelFunctions[lowered] || lowered; + } + return units; + } - // ------------------- UTILITY FUNCTIONS ---------------------------- // + function normalizeObjectUnits(inputObject) { + var normalizedInput = {}, + normalizedProp, + prop; + for (prop in inputObject) { + if (hasOwnProp(inputObject, prop)) { + normalizedProp = normalizeUnits(prop); + if (normalizedProp) { + normalizedInput[normalizedProp] = inputObject[prop]; + } + } + } - /** - * This updates the node labels for all nodes (for debugging purposes) - */ - exports.updateLabels = function() { - var nodeId; - // update node labels - for (nodeId in this.nodes) { - if (this.nodes.hasOwnProperty(nodeId)) { - var node = this.nodes[nodeId]; - if (node.clusterSize > 1) { - node.label = "[".concat(String(node.clusterSize),"]"); - } + return normalizedInput; } - } - // update node labels - for (nodeId in this.nodes) { - if (this.nodes.hasOwnProperty(nodeId)) { - node = this.nodes[nodeId]; - if (node.clusterSize == 1) { - if (node.originalLabel !== undefined) { - node.label = node.originalLabel; + function makeList(field) { + var count, setter; + + if (field.indexOf('week') === 0) { + count = 7; + setter = 'day'; + } + else if (field.indexOf('month') === 0) { + count = 12; + setter = 'month'; } else { - node.label = String(node.id); + return; } - } - } - } - - // /* Debug Override */ - // for (nodeId in this.nodes) { - // if (this.nodes.hasOwnProperty(nodeId)) { - // node = this.nodes[nodeId]; - // node.label = String(node.level); - // } - // } - }; + moment[field] = function (format, index) { + var i, getter, + method = moment._locale[field], + results = []; + if (typeof format === 'number') { + index = format; + format = undefined; + } - /** - * We want to keep the cluster level distribution rather small. This means we do not want unclustered nodes - * if the rest of the nodes are already a few cluster levels in. - * To fix this we use this function. It determines the min and max cluster level and sends nodes that have not - * clustered enough to the clusterToSmallestNeighbours function. - */ - exports.normalizeClusterLevels = function() { - var maxLevel = 0; - var minLevel = 1e9; - var clusterLevel = 0; - var nodeId; + getter = function (i) { + var m = moment().utc().set(setter, i); + return method.call(moment._locale, m, format || ''); + }; - // we loop over all nodes in the list - for (nodeId in this.nodes) { - if (this.nodes.hasOwnProperty(nodeId)) { - clusterLevel = this.nodes[nodeId].clusterSessions.length; - if (maxLevel < clusterLevel) {maxLevel = clusterLevel;} - if (minLevel > clusterLevel) {minLevel = clusterLevel;} + if (index != null) { + return getter(index); + } + else { + for (i = 0; i < count; i++) { + results.push(getter(i)); + } + return results; + } + }; } - } - if (maxLevel - minLevel > this.constants.clustering.clusterLevelDifference) { - var amountOfNodes = this.nodeIndices.length; - var targetLevel = maxLevel - this.constants.clustering.clusterLevelDifference; - // we loop over all nodes in the list - for (nodeId in this.nodes) { - if (this.nodes.hasOwnProperty(nodeId)) { - if (this.nodes[nodeId].clusterSessions.length < targetLevel) { - this._clusterToSmallestNeighbour(this.nodes[nodeId]); - } - } - } - this._updateNodeIndexList(); - this._updateDynamicEdges(); - // if a cluster was formed, we increase the clusterSession - if (this.nodeIndices.length != amountOfNodes) { - this.clusterSession += 1; - } - } - }; + function toInt(argumentForCoercion) { + var coercedNumber = +argumentForCoercion, + value = 0; + if (coercedNumber !== 0 && isFinite(coercedNumber)) { + if (coercedNumber >= 0) { + value = Math.floor(coercedNumber); + } else { + value = Math.ceil(coercedNumber); + } + } + return value; + } - /** - * This function determines if the cluster we want to decluster is in the active area - * this means around the zoom center - * - * @param {Node} node - * @returns {boolean} - * @private - */ - exports._nodeInActiveArea = function(node) { - return ( - Math.abs(node.x - this.areaCenter.x) <= this.constants.clustering.activeAreaBoxSize/this.scale - && - Math.abs(node.y - this.areaCenter.y) <= this.constants.clustering.activeAreaBoxSize/this.scale - ) - }; + function daysInMonth(year, month) { + return new Date(Date.UTC(year, month + 1, 0)).getUTCDate(); + } + function weeksInYear(year, dow, doy) { + return weekOfYear(moment([year, 11, 31 + dow - doy]), dow, doy).week; + } - /** - * This is an adaptation of the original repositioning function. This is called if the system is clustered initially - * It puts large clusters away from the center and randomizes the order. - * - */ - exports.repositionNodes = function() { - for (var i = 0; i < this.nodeIndices.length; i++) { - var node = this.nodes[this.nodeIndices[i]]; - if ((node.xFixed == false || node.yFixed == false)) { - var radius = 10 * 0.1*this.nodeIndices.length * Math.min(100,node.options.mass); - var angle = 2 * Math.PI * Math.random(); - if (node.xFixed == false) {node.x = radius * Math.cos(angle);} - if (node.yFixed == false) {node.y = radius * Math.sin(angle);} - this._repositionBezierNodes(node); + function daysInYear(year) { + return isLeapYear(year) ? 366 : 365; } - } - }; + function isLeapYear(year) { + return (year % 4 === 0 && year % 100 !== 0) || year % 400 === 0; + } - /** - * We determine how many connections denote an important hub. - * We take the mean + 2*std as the important hub size. (Assuming a normal distribution of data, ~2.2%) - * - * @private - */ - exports._getHubSize = function() { - var average = 0; - var averageSquared = 0; - var hubCounter = 0; - var largestHub = 0; + function checkOverflow(m) { + var overflow; + if (m._a && m._pf.overflow === -2) { + overflow = + m._a[MONTH] < 0 || m._a[MONTH] > 11 ? MONTH : + m._a[DATE] < 1 || m._a[DATE] > daysInMonth(m._a[YEAR], m._a[MONTH]) ? DATE : + m._a[HOUR] < 0 || m._a[HOUR] > 24 || + (m._a[HOUR] === 24 && (m._a[MINUTE] !== 0 || + m._a[SECOND] !== 0 || + m._a[MILLISECOND] !== 0)) ? HOUR : + m._a[MINUTE] < 0 || m._a[MINUTE] > 59 ? MINUTE : + m._a[SECOND] < 0 || m._a[SECOND] > 59 ? SECOND : + m._a[MILLISECOND] < 0 || m._a[MILLISECOND] > 999 ? MILLISECOND : + -1; - for (var i = 0; i < this.nodeIndices.length; i++) { + if (m._pf._overflowDayOfYear && (overflow < YEAR || overflow > DATE)) { + overflow = DATE; + } - var node = this.nodes[this.nodeIndices[i]]; - if (node.dynamicEdgesLength > largestHub) { - largestHub = node.dynamicEdgesLength; + m._pf.overflow = overflow; + } } - average += node.dynamicEdgesLength; - averageSquared += Math.pow(node.dynamicEdgesLength,2); - hubCounter += 1; - } - average = average / hubCounter; - averageSquared = averageSquared / hubCounter; - - var variance = averageSquared - Math.pow(average,2); - - var standardDeviation = Math.sqrt(variance); - this.hubThreshold = Math.floor(average + 2*standardDeviation); + function isValid(m) { + if (m._isValid == null) { + m._isValid = !isNaN(m._d.getTime()) && + m._pf.overflow < 0 && + !m._pf.empty && + !m._pf.invalidMonth && + !m._pf.nullInput && + !m._pf.invalidFormat && + !m._pf.userInvalidated; - // always have at least one to cluster - if (this.hubThreshold > largestHub) { - this.hubThreshold = largestHub; - } + if (m._strict) { + m._isValid = m._isValid && + m._pf.charsLeftOver === 0 && + m._pf.unusedTokens.length === 0 && + m._pf.bigHour === undefined; + } + } + return m._isValid; + } - // console.log("average",average,"averageSQ",averageSquared,"var",variance,"std",standardDeviation); - // console.log("hubThreshold:",this.hubThreshold); - }; + function normalizeLocale(key) { + return key ? key.toLowerCase().replace('_', '-') : key; + } + // pick the locale from the array + // try ['en-au', 'en-gb'] as 'en-au', 'en-gb', 'en', as in move through the list trying each + // substring from most specific to least, but move to the next array item if it's a more specific variant than the current root + function chooseLocale(names) { + var i = 0, j, next, locale, split; - /** - * We reduce the amount of "extension nodes" or chains. These are not quickly clustered with the outliers and hubs methods - * with this amount we can cluster specifically on these chains. - * - * @param {Number} fraction | between 0 and 1, the percentage of chains to reduce - * @private - */ - exports._reduceAmountOfChains = function(fraction) { - this.hubThreshold = 2; - var reduceAmount = Math.floor(this.nodeIndices.length * fraction); - for (var nodeId in this.nodes) { - if (this.nodes.hasOwnProperty(nodeId)) { - if (this.nodes[nodeId].dynamicEdgesLength == 2 && this.nodes[nodeId].dynamicEdges.length >= 2) { - if (reduceAmount > 0) { - this._formClusterFromHub(this.nodes[nodeId],true,true,1); - reduceAmount -= 1; + while (i < names.length) { + split = normalizeLocale(names[i]).split('-'); + j = split.length; + next = normalizeLocale(names[i + 1]); + next = next ? next.split('-') : null; + while (j > 0) { + locale = loadLocale(split.slice(0, j).join('-')); + if (locale) { + return locale; + } + if (next && next.length >= j && compareArrays(split, next, true) >= j - 1) { + //the next array item is better than a shallower substring of this one + break; + } + j--; + } + i++; } - } + return null; } - } - }; - /** - * We get the amount of "extension nodes" or chains. These are not quickly clustered with the outliers and hubs methods - * with this amount we can cluster specifically on these chains. - * - * @private - */ - exports._getChainFraction = function() { - var chains = 0; - var total = 0; - for (var nodeId in this.nodes) { - if (this.nodes.hasOwnProperty(nodeId)) { - if (this.nodes[nodeId].dynamicEdgesLength == 2 && this.nodes[nodeId].dynamicEdges.length >= 2) { - chains += 1; - } - total += 1; + function loadLocale(name) { + var oldLocale = null; + if (!locales[name] && hasModule) { + try { + oldLocale = moment.locale(); + !(function webpackMissingModule() { var e = new Error("Cannot find module \"./locale\""); e.code = 'MODULE_NOT_FOUND'; throw e; }()); + // because defineLocale currently also sets the global locale, we want to undo that for lazy loaded locales + moment.locale(oldLocale); + } catch (e) { } + } + return locales[name]; } - } - return chains/total; - }; + // Return a moment from input, that is local/utc/utcOffset equivalent to + // model. + function makeAs(input, model) { + var res, diff; + if (model._isUTC) { + res = model.clone(); + diff = (moment.isMoment(input) || isDate(input) ? + +input : +moment(input)) - (+res); + // Use low-level api, because this fn is low-level api. + res._d.setTime(+res._d + diff); + moment.updateOffset(res, false); + return res; + } else { + return moment(input).local(); + } + } -/***/ }, -/* 61 */ -/***/ function(module, exports, __webpack_require__) { + /************************************ + Locale + ************************************/ - var util = __webpack_require__(1); - var Node = __webpack_require__(40); - /** - * Creation of the SectorMixin var. - * - * This contains all the functions the Network object can use to employ the sector system. - * The sector system is always used by Network, though the benefits only apply to the use of clustering. - * If clustering is not used, there is no overhead except for a duplicate object with references to nodes and edges. - */ + extend(Locale.prototype, { - /** - * This function is only called by the setData function of the Network object. - * This loads the global references into the active sector. This initializes the sector. - * - * @private - */ - exports._putDataInSector = function() { - this.sectors["active"][this._sector()].nodes = this.nodes; - this.sectors["active"][this._sector()].edges = this.edges; - this.sectors["active"][this._sector()].nodeIndices = this.nodeIndices; - }; + set : function (config) { + var prop, i; + for (i in config) { + prop = config[i]; + if (typeof prop === 'function') { + this[i] = prop; + } else { + this['_' + i] = prop; + } + } + // Lenient ordinal parsing accepts just a number in addition to + // number + (possibly) stuff coming from _ordinalParseLenient. + this._ordinalParseLenient = new RegExp(this._ordinalParse.source + '|' + /\d{1,2}/.source); + }, + _months : 'January_February_March_April_May_June_July_August_September_October_November_December'.split('_'), + months : function (m) { + return this._months[m.month()]; + }, - /** - * /** - * This function sets the global references to nodes, edges and nodeIndices back to - * those of the supplied (active) sector. If a type is defined, do the specific type - * - * @param {String} sectorId - * @param {String} [sectorType] | "active" or "frozen" - * @private - */ - exports._switchToSector = function(sectorId, sectorType) { - if (sectorType === undefined || sectorType == "active") { - this._switchToActiveSector(sectorId); - } - else { - this._switchToFrozenSector(sectorId); - } - }; + _monthsShort : 'Jan_Feb_Mar_Apr_May_Jun_Jul_Aug_Sep_Oct_Nov_Dec'.split('_'), + monthsShort : function (m) { + return this._monthsShort[m.month()]; + }, + monthsParse : function (monthName, format, strict) { + var i, mom, regex; - /** - * This function sets the global references to nodes, edges and nodeIndices back to - * those of the supplied active sector. - * - * @param sectorId - * @private - */ - exports._switchToActiveSector = function(sectorId) { - this.nodeIndices = this.sectors["active"][sectorId]["nodeIndices"]; - this.nodes = this.sectors["active"][sectorId]["nodes"]; - this.edges = this.sectors["active"][sectorId]["edges"]; - }; + if (!this._monthsParse) { + this._monthsParse = []; + this._longMonthsParse = []; + this._shortMonthsParse = []; + } + for (i = 0; i < 12; i++) { + // make the regex if we don't have it already + mom = moment.utc([2000, i]); + if (strict && !this._longMonthsParse[i]) { + this._longMonthsParse[i] = new RegExp('^' + this.months(mom, '').replace('.', '') + '$', 'i'); + this._shortMonthsParse[i] = new RegExp('^' + this.monthsShort(mom, '').replace('.', '') + '$', 'i'); + } + if (!strict && !this._monthsParse[i]) { + regex = '^' + this.months(mom, '') + '|^' + this.monthsShort(mom, ''); + this._monthsParse[i] = new RegExp(regex.replace('.', ''), 'i'); + } + // test the regex + if (strict && format === 'MMMM' && this._longMonthsParse[i].test(monthName)) { + return i; + } else if (strict && format === 'MMM' && this._shortMonthsParse[i].test(monthName)) { + return i; + } else if (!strict && this._monthsParse[i].test(monthName)) { + return i; + } + } + }, - /** - * This function sets the global references to nodes, edges and nodeIndices back to - * those of the supplied active sector. - * - * @private - */ - exports._switchToSupportSector = function() { - this.nodeIndices = this.sectors["support"]["nodeIndices"]; - this.nodes = this.sectors["support"]["nodes"]; - this.edges = this.sectors["support"]["edges"]; - }; + _weekdays : 'Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday'.split('_'), + weekdays : function (m) { + return this._weekdays[m.day()]; + }, + _weekdaysShort : 'Sun_Mon_Tue_Wed_Thu_Fri_Sat'.split('_'), + weekdaysShort : function (m) { + return this._weekdaysShort[m.day()]; + }, - /** - * This function sets the global references to nodes, edges and nodeIndices back to - * those of the supplied frozen sector. - * - * @param sectorId - * @private - */ - exports._switchToFrozenSector = function(sectorId) { - this.nodeIndices = this.sectors["frozen"][sectorId]["nodeIndices"]; - this.nodes = this.sectors["frozen"][sectorId]["nodes"]; - this.edges = this.sectors["frozen"][sectorId]["edges"]; - }; + _weekdaysMin : 'Su_Mo_Tu_We_Th_Fr_Sa'.split('_'), + weekdaysMin : function (m) { + return this._weekdaysMin[m.day()]; + }, + weekdaysParse : function (weekdayName) { + var i, mom, regex; - /** - * This function sets the global references to nodes, edges and nodeIndices back to - * those of the currently active sector. - * - * @private - */ - exports._loadLatestSector = function() { - this._switchToSector(this._sector()); - }; + if (!this._weekdaysParse) { + this._weekdaysParse = []; + } + for (i = 0; i < 7; i++) { + // make the regex if we don't have it already + if (!this._weekdaysParse[i]) { + mom = moment([2000, 1]).day(i); + regex = '^' + this.weekdays(mom, '') + '|^' + this.weekdaysShort(mom, '') + '|^' + this.weekdaysMin(mom, ''); + this._weekdaysParse[i] = new RegExp(regex.replace('.', ''), 'i'); + } + // test the regex + if (this._weekdaysParse[i].test(weekdayName)) { + return i; + } + } + }, - /** - * This function returns the currently active sector Id - * - * @returns {String} - * @private - */ - exports._sector = function() { - return this.activeSector[this.activeSector.length-1]; - }; + _longDateFormat : { + LTS : 'h:mm:ss A', + LT : 'h:mm A', + L : 'MM/DD/YYYY', + LL : 'MMMM D, YYYY', + LLL : 'MMMM D, YYYY LT', + LLLL : 'dddd, MMMM D, YYYY LT' + }, + longDateFormat : function (key) { + var output = this._longDateFormat[key]; + if (!output && this._longDateFormat[key.toUpperCase()]) { + output = this._longDateFormat[key.toUpperCase()].replace(/MMMM|MM|DD|dddd/g, function (val) { + return val.slice(1); + }); + this._longDateFormat[key] = output; + } + return output; + }, + isPM : function (input) { + // IE8 Quirks Mode & IE7 Standards Mode do not allow accessing strings like arrays + // Using charAt should be more compatible. + return ((input + '').toLowerCase().charAt(0) === 'p'); + }, - /** - * This function returns the previously active sector Id - * - * @returns {String} - * @private - */ - exports._previousSector = function() { - if (this.activeSector.length > 1) { - return this.activeSector[this.activeSector.length-2]; - } - else { - throw new TypeError('there are not enough sectors in the this.activeSector array.'); - } - }; + _meridiemParse : /[ap]\.?m?\.?/i, + meridiem : function (hours, minutes, isLower) { + if (hours > 11) { + return isLower ? 'pm' : 'PM'; + } else { + return isLower ? 'am' : 'AM'; + } + }, - /** - * We add the active sector at the end of the this.activeSector array - * This ensures it is the currently active sector returned by _sector() and it reaches the top - * of the activeSector stack. When we reverse our steps we move from the end to the beginning of this stack. - * - * @param newId - * @private - */ - exports._setActiveSector = function(newId) { - this.activeSector.push(newId); - }; + _calendar : { + sameDay : '[Today at] LT', + nextDay : '[Tomorrow at] LT', + nextWeek : 'dddd [at] LT', + lastDay : '[Yesterday at] LT', + lastWeek : '[Last] dddd [at] LT', + sameElse : 'L' + }, + calendar : function (key, mom, now) { + var output = this._calendar[key]; + return typeof output === 'function' ? output.apply(mom, [now]) : output; + }, + _relativeTime : { + future : 'in %s', + past : '%s ago', + s : 'a few seconds', + m : 'a minute', + mm : '%d minutes', + h : 'an hour', + hh : '%d hours', + d : 'a day', + dd : '%d days', + M : 'a month', + MM : '%d months', + y : 'a year', + yy : '%d years' + }, - /** - * We remove the currently active sector id from the active sector stack. This happens when - * we reactivate the previously active sector - * - * @private - */ - exports._forgetLastSector = function() { - this.activeSector.pop(); - }; + relativeTime : function (number, withoutSuffix, string, isFuture) { + var output = this._relativeTime[string]; + return (typeof output === 'function') ? + output(number, withoutSuffix, string, isFuture) : + output.replace(/%d/i, number); + }, + pastFuture : function (diff, output) { + var format = this._relativeTime[diff > 0 ? 'future' : 'past']; + return typeof format === 'function' ? format(output) : format.replace(/%s/i, output); + }, - /** - * This function creates a new active sector with the supplied newId. This newId - * is the expanding node id. - * - * @param {String} newId | Id of the new active sector - * @private - */ - exports._createNewSector = function(newId) { - // create the new sector - this.sectors["active"][newId] = {"nodes":{}, - "edges":{}, - "nodeIndices":[], - "formationScale": this.scale, - "drawingNode": undefined}; + ordinal : function (number) { + return this._ordinal.replace('%d', number); + }, + _ordinal : '%d', + _ordinalParse : /\d{1,2}/, - // create the new sector render node. This gives visual feedback that you are in a new sector. - this.sectors["active"][newId]['drawingNode'] = new Node( - {id:newId, - color: { - background: "#eaefef", - border: "495c5e" - } - },{},{},this.constants); - this.sectors["active"][newId]['drawingNode'].clusterSize = 2; - }; + preparse : function (string) { + return string; + }, + postformat : function (string) { + return string; + }, - /** - * This function removes the currently active sector. This is called when we create a new - * active sector. - * - * @param {String} sectorId | Id of the active sector that will be removed - * @private - */ - exports._deleteActiveSector = function(sectorId) { - delete this.sectors["active"][sectorId]; - }; + week : function (mom) { + return weekOfYear(mom, this._week.dow, this._week.doy).week; + }, + _week : { + dow : 0, // Sunday is the first day of the week. + doy : 6 // The week that contains Jan 1st is the first week of the year. + }, - /** - * This function removes the currently active sector. This is called when we reactivate - * the previously active sector. - * - * @param {String} sectorId | Id of the active sector that will be removed - * @private - */ - exports._deleteFrozenSector = function(sectorId) { - delete this.sectors["frozen"][sectorId]; - }; + firstDayOfWeek : function () { + return this._week.dow; + }, + firstDayOfYear : function () { + return this._week.doy; + }, - /** - * Freezing an active sector means moving it from the "active" object to the "frozen" object. - * We copy the references, then delete the active entree. - * - * @param sectorId - * @private - */ - exports._freezeSector = function(sectorId) { - // we move the set references from the active to the frozen stack. - this.sectors["frozen"][sectorId] = this.sectors["active"][sectorId]; + _invalidDate: 'Invalid date', + invalidDate: function () { + return this._invalidDate; + } + }); - // we have moved the sector data into the frozen set, we now remove it from the active set - this._deleteActiveSector(sectorId); - }; + /************************************ + Formatting + ************************************/ - /** - * This is the reverse operation of _freezeSector. Activating means moving the sector from the "frozen" - * object to the "active" object. - * - * @param sectorId - * @private - */ - exports._activateSector = function(sectorId) { - // we move the set references from the frozen to the active stack. - this.sectors["active"][sectorId] = this.sectors["frozen"][sectorId]; + function removeFormattingTokens(input) { + if (input.match(/\[[\s\S]/)) { + return input.replace(/^\[|\]$/g, ''); + } + return input.replace(/\\/g, ''); + } - // we have moved the sector data into the active set, we now remove it from the frozen stack - this._deleteFrozenSector(sectorId); - }; + function makeFormatFunction(format) { + var array = format.match(formattingTokens), i, length; + for (i = 0, length = array.length; i < length; i++) { + if (formatTokenFunctions[array[i]]) { + array[i] = formatTokenFunctions[array[i]]; + } else { + array[i] = removeFormattingTokens(array[i]); + } + } - /** - * This function merges the data from the currently active sector with a frozen sector. This is used - * in the process of reverting back to the previously active sector. - * The data that is placed in the frozen (the previously active) sector is the node that has been removed from it - * upon the creation of a new active sector. - * - * @param sectorId - * @private - */ - exports._mergeThisWithFrozen = function(sectorId) { - // copy all nodes - for (var nodeId in this.nodes) { - if (this.nodes.hasOwnProperty(nodeId)) { - this.sectors["frozen"][sectorId]["nodes"][nodeId] = this.nodes[nodeId]; + return function (mom) { + var output = ''; + for (i = 0; i < length; i++) { + output += array[i] instanceof Function ? array[i].call(mom, format) : array[i]; + } + return output; + }; } - } - // copy all edges (if not fully clustered, else there are no edges) - for (var edgeId in this.edges) { - if (this.edges.hasOwnProperty(edgeId)) { - this.sectors["frozen"][sectorId]["edges"][edgeId] = this.edges[edgeId]; + // format date using native date object + function formatMoment(m, format) { + if (!m.isValid()) { + return m.localeData().invalidDate(); + } + + format = expandFormat(format, m.localeData()); + + if (!formatFunctions[format]) { + formatFunctions[format] = makeFormatFunction(format); + } + + return formatFunctions[format](m); } - } - // merge the nodeIndices - for (var i = 0; i < this.nodeIndices.length; i++) { - this.sectors["frozen"][sectorId]["nodeIndices"].push(this.nodeIndices[i]); - } - }; + function expandFormat(format, locale) { + var i = 5; + function replaceLongDateFormatTokens(input) { + return locale.longDateFormat(input) || input; + } - /** - * This clusters the sector to one cluster. It was a single cluster before this process started so - * we revert to that state. The clusterToFit function with a maximum size of 1 node does this. - * - * @private - */ - exports._collapseThisToSingleCluster = function() { - this.clusterToFit(1,false); - }; + localFormattingTokens.lastIndex = 0; + while (i >= 0 && localFormattingTokens.test(format)) { + format = format.replace(localFormattingTokens, replaceLongDateFormatTokens); + localFormattingTokens.lastIndex = 0; + i -= 1; + } + return format; + } - /** - * We create a new active sector from the node that we want to open. - * - * @param node - * @private - */ - exports._addSector = function(node) { - // this is the currently active sector - var sector = this._sector(); - // // this should allow me to select nodes from a frozen set. - // if (this.sectors['active'][sector]["nodes"].hasOwnProperty(node.id)) { - // console.log("the node is part of the active sector"); - // } - // else { - // console.log("I dont know what the fuck happened!!"); - // } + /************************************ + Parsing + ************************************/ - // when we switch to a new sector, we remove the node that will be expanded from the current nodes list. - delete this.nodes[node.id]; - var unqiueIdentifier = util.randomUUID(); + // get the regex to find the next token + function getParseRegexForToken(token, config) { + var a, strict = config._strict; + switch (token) { + case 'Q': + return parseTokenOneDigit; + case 'DDDD': + return parseTokenThreeDigits; + case 'YYYY': + case 'GGGG': + case 'gggg': + return strict ? parseTokenFourDigits : parseTokenOneToFourDigits; + case 'Y': + case 'G': + case 'g': + return parseTokenSignedNumber; + case 'YYYYYY': + case 'YYYYY': + case 'GGGGG': + case 'ggggg': + return strict ? parseTokenSixDigits : parseTokenOneToSixDigits; + case 'S': + if (strict) { + return parseTokenOneDigit; + } + /* falls through */ + case 'SS': + if (strict) { + return parseTokenTwoDigits; + } + /* falls through */ + case 'SSS': + if (strict) { + return parseTokenThreeDigits; + } + /* falls through */ + case 'DDD': + return parseTokenOneToThreeDigits; + case 'MMM': + case 'MMMM': + case 'dd': + case 'ddd': + case 'dddd': + return parseTokenWord; + case 'a': + case 'A': + return config._locale._meridiemParse; + case 'x': + return parseTokenOffsetMs; + case 'X': + return parseTokenTimestampMs; + case 'Z': + case 'ZZ': + return parseTokenTimezone; + case 'T': + return parseTokenT; + case 'SSSS': + return parseTokenDigits; + case 'MM': + case 'DD': + case 'YY': + case 'GG': + case 'gg': + case 'HH': + case 'hh': + case 'mm': + case 'ss': + case 'ww': + case 'WW': + return strict ? parseTokenTwoDigits : parseTokenOneOrTwoDigits; + case 'M': + case 'D': + case 'd': + case 'H': + case 'h': + case 'm': + case 's': + case 'w': + case 'W': + case 'e': + case 'E': + return parseTokenOneOrTwoDigits; + case 'Do': + return strict ? config._locale._ordinalParse : config._locale._ordinalParseLenient; + default : + a = new RegExp(regexpEscape(unescapeFormat(token.replace('\\', '')), 'i')); + return a; + } + } - // we fully freeze the currently active sector - this._freezeSector(sector); + function utcOffsetFromString(string) { + string = string || ''; + var possibleTzMatches = (string.match(parseTokenTimezone) || []), + tzChunk = possibleTzMatches[possibleTzMatches.length - 1] || [], + parts = (tzChunk + '').match(parseTimezoneChunker) || ['-', 0, 0], + minutes = +(parts[1] * 60) + toInt(parts[2]); - // we create a new active sector. This sector has the Id of the node to ensure uniqueness - this._createNewSector(unqiueIdentifier); + return parts[0] === '+' ? minutes : -minutes; + } - // we add the active sector to the sectors array to be able to revert these steps later on - this._setActiveSector(unqiueIdentifier); + // function to convert string input to date + function addTimeToArrayFromToken(token, input, config) { + var a, datePartArray = config._a; - // we redirect the global references to the new sector's references. this._sector() now returns unqiueIdentifier - this._switchToSector(this._sector()); + switch (token) { + // QUARTER + case 'Q': + if (input != null) { + datePartArray[MONTH] = (toInt(input) - 1) * 3; + } + break; + // MONTH + case 'M' : // fall through to MM + case 'MM' : + if (input != null) { + datePartArray[MONTH] = toInt(input) - 1; + } + break; + case 'MMM' : // fall through to MMMM + case 'MMMM' : + a = config._locale.monthsParse(input, token, config._strict); + // if we didn't find a month name, mark the date as invalid. + if (a != null) { + datePartArray[MONTH] = a; + } else { + config._pf.invalidMonth = input; + } + break; + // DAY OF MONTH + case 'D' : // fall through to DD + case 'DD' : + if (input != null) { + datePartArray[DATE] = toInt(input); + } + break; + case 'Do' : + if (input != null) { + datePartArray[DATE] = toInt(parseInt( + input.match(/\d{1,2}/)[0], 10)); + } + break; + // DAY OF YEAR + case 'DDD' : // fall through to DDDD + case 'DDDD' : + if (input != null) { + config._dayOfYear = toInt(input); + } - // finally we add the node we removed from our previous active sector to the new active sector - this.nodes[node.id] = node; - }; + break; + // YEAR + case 'YY' : + datePartArray[YEAR] = moment.parseTwoDigitYear(input); + break; + case 'YYYY' : + case 'YYYYY' : + case 'YYYYYY' : + datePartArray[YEAR] = toInt(input); + break; + // AM / PM + case 'a' : // fall through to A + case 'A' : + config._meridiem = input; + // config._isPm = config._locale.isPM(input); + break; + // HOUR + case 'h' : // fall through to hh + case 'hh' : + config._pf.bigHour = true; + /* falls through */ + case 'H' : // fall through to HH + case 'HH' : + datePartArray[HOUR] = toInt(input); + break; + // MINUTE + case 'm' : // fall through to mm + case 'mm' : + datePartArray[MINUTE] = toInt(input); + break; + // SECOND + case 's' : // fall through to ss + case 'ss' : + datePartArray[SECOND] = toInt(input); + break; + // MILLISECOND + case 'S' : + case 'SS' : + case 'SSS' : + case 'SSSS' : + datePartArray[MILLISECOND] = toInt(('0.' + input) * 1000); + break; + // UNIX OFFSET (MILLISECONDS) + case 'x': + config._d = new Date(toInt(input)); + break; + // UNIX TIMESTAMP WITH MS + case 'X': + config._d = new Date(parseFloat(input) * 1000); + break; + // TIMEZONE + case 'Z' : // fall through to ZZ + case 'ZZ' : + config._useUTC = true; + config._tzm = utcOffsetFromString(input); + break; + // WEEKDAY - human + case 'dd': + case 'ddd': + case 'dddd': + a = config._locale.weekdaysParse(input); + // if we didn't get a weekday name, mark the date as invalid + if (a != null) { + config._w = config._w || {}; + config._w['d'] = a; + } else { + config._pf.invalidWeekday = input; + } + break; + // WEEK, WEEK DAY - numeric + case 'w': + case 'ww': + case 'W': + case 'WW': + case 'd': + case 'e': + case 'E': + token = token.substr(0, 1); + /* falls through */ + case 'gggg': + case 'GGGG': + case 'GGGGG': + token = token.substr(0, 2); + if (input) { + config._w = config._w || {}; + config._w[token] = toInt(input); + } + break; + case 'gg': + case 'GG': + config._w = config._w || {}; + config._w[token] = moment.parseTwoDigitYear(input); + } + } + function dayOfYearFromWeekInfo(config) { + var w, weekYear, week, weekday, dow, doy, temp; - /** - * We close the sector that is currently open and revert back to the one before. - * If the active sector is the "default" sector, nothing happens. - * - * @private - */ - exports._collapseSector = function() { - // the currently active sector - var sector = this._sector(); + w = config._w; + if (w.GG != null || w.W != null || w.E != null) { + dow = 1; + doy = 4; - // we cannot collapse the default sector - if (sector != "default") { - if ((this.nodeIndices.length == 1) || - (this.sectors["active"][sector]["drawingNode"].width*this.scale < this.constants.clustering.screenSizeThreshold * this.frame.canvas.clientWidth) || - (this.sectors["active"][sector]["drawingNode"].height*this.scale < this.constants.clustering.screenSizeThreshold * this.frame.canvas.clientHeight)) { - var previousSector = this._previousSector(); + // TODO: We need to take the current isoWeekYear, but that depends on + // how we interpret now (local, utc, fixed offset). So create + // a now version of current config (take local/utc/offset flags, and + // create now). + weekYear = dfl(w.GG, config._a[YEAR], weekOfYear(moment(), 1, 4).year); + week = dfl(w.W, 1); + weekday = dfl(w.E, 1); + } else { + dow = config._locale._week.dow; + doy = config._locale._week.doy; - // we collapse the sector back to a single cluster - this._collapseThisToSingleCluster(); + weekYear = dfl(w.gg, config._a[YEAR], weekOfYear(moment(), dow, doy).year); + week = dfl(w.w, 1); - // we move the remaining nodes, edges and nodeIndices to the previous sector. - // This previous sector is the one we will reactivate - this._mergeThisWithFrozen(previousSector); + if (w.d != null) { + // weekday -- low day numbers are considered next week + weekday = w.d; + if (weekday < dow) { + ++week; + } + } else if (w.e != null) { + // local weekday -- counting starts from begining of week + weekday = w.e + dow; + } else { + // default to begining of week + weekday = dow; + } + } + temp = dayOfYearFromWeeks(weekYear, week, weekday, doy, dow); - // the previously active (frozen) sector now has all the data from the currently active sector. - // we can now delete the active sector. - this._deleteActiveSector(sector); + config._a[YEAR] = temp.year; + config._dayOfYear = temp.dayOfYear; + } - // we activate the previously active (and currently frozen) sector. - this._activateSector(previousSector); + // convert an array to a date. + // the array should mirror the parameters below + // note: all values past the year are optional and will default to the lowest possible value. + // [year, month, day , hour, minute, second, millisecond] + function dateFromConfig(config) { + var i, date, input = [], currentDate, yearToUse; - // we load the references from the newly active sector into the global references - this._switchToSector(previousSector); + if (config._d) { + return; + } - // we forget the previously active sector because we reverted to the one before - this._forgetLastSector(); + currentDate = currentDateArray(config); - // finally, we update the node index list. - this._updateNodeIndexList(); + //compute day of the year from weeks and weekdays + if (config._w && config._a[DATE] == null && config._a[MONTH] == null) { + dayOfYearFromWeekInfo(config); + } - // we refresh the list with calulation nodes and calculation node indices. - this._updateCalculationNodes(); - } - } - }; + //if the day of the year is set, figure out what it is + if (config._dayOfYear) { + yearToUse = dfl(config._a[YEAR], currentDate[YEAR]); + if (config._dayOfYear > daysInYear(yearToUse)) { + config._pf._overflowDayOfYear = true; + } - /** - * This runs a function in all active sectors. This is used in _redraw() and the _initializeForceCalculation(). - * - * @param {String} runFunction | This is the NAME of a function we want to call in all active sectors - * | we dont pass the function itself because then the "this" is the window object - * | instead of the Network object - * @param {*} [argument] | Optional: arguments to pass to the runFunction - * @private - */ - exports._doInAllActiveSectors = function(runFunction,argument) { - var returnValues = []; - if (argument === undefined) { - for (var sector in this.sectors["active"]) { - if (this.sectors["active"].hasOwnProperty(sector)) { - // switch the global references to those of this sector - this._switchToActiveSector(sector); - returnValues.push( this[runFunction]() ); - } - } - } - else { - for (var sector in this.sectors["active"]) { - if (this.sectors["active"].hasOwnProperty(sector)) { - // switch the global references to those of this sector - this._switchToActiveSector(sector); - var args = Array.prototype.splice.call(arguments, 1); - if (args.length > 1) { - returnValues.push( this[runFunction](args[0],args[1]) ); + date = makeUTCDate(yearToUse, 0, config._dayOfYear); + config._a[MONTH] = date.getUTCMonth(); + config._a[DATE] = date.getUTCDate(); } - else { - returnValues.push( this[runFunction](argument) ); + + // Default to current date. + // * if no year, month, day of month are given, default to today + // * if day of month is given, default month and year + // * if month is given, default only year + // * if year is given, don't default anything + for (i = 0; i < 3 && config._a[i] == null; ++i) { + config._a[i] = input[i] = currentDate[i]; + } + + // Zero out whatever was not defaulted, including time + for (; i < 7; i++) { + config._a[i] = input[i] = (config._a[i] == null) ? (i === 2 ? 1 : 0) : config._a[i]; + } + + // Check for 24:00:00.000 + if (config._a[HOUR] === 24 && + config._a[MINUTE] === 0 && + config._a[SECOND] === 0 && + config._a[MILLISECOND] === 0) { + config._nextDay = true; + config._a[HOUR] = 0; + } + + config._d = (config._useUTC ? makeUTCDate : makeDate).apply(null, input); + // Apply timezone offset from input. The actual utcOffset can be changed + // with parseZone. + if (config._tzm != null) { + config._d.setUTCMinutes(config._d.getUTCMinutes() - config._tzm); + } + + if (config._nextDay) { + config._a[HOUR] = 24; } - } } - } - // we revert the global references back to our active sector - this._loadLatestSector(); - return returnValues; - }; + function dateFromObject(config) { + var normalizedInput; - /** - * This runs a function in all active sectors. This is used in _redraw() and the _initializeForceCalculation(). - * - * @param {String} runFunction | This is the NAME of a function we want to call in all active sectors - * | we dont pass the function itself because then the "this" is the window object - * | instead of the Network object - * @param {*} [argument] | Optional: arguments to pass to the runFunction - * @private - */ - exports._doInSupportSector = function(runFunction,argument) { - var returnValues = false; - if (argument === undefined) { - this._switchToSupportSector(); - returnValues = this[runFunction](); - } - else { - this._switchToSupportSector(); - var args = Array.prototype.splice.call(arguments, 1); - if (args.length > 1) { - returnValues = this[runFunction](args[0],args[1]); - } - else { - returnValues = this[runFunction](argument); - } - } - // we revert the global references back to our active sector - this._loadLatestSector(); - return returnValues; - }; + if (config._d) { + return; + } + normalizedInput = normalizeObjectUnits(config._i); + config._a = [ + normalizedInput.year, + normalizedInput.month, + normalizedInput.day || normalizedInput.date, + normalizedInput.hour, + normalizedInput.minute, + normalizedInput.second, + normalizedInput.millisecond + ]; - /** - * This runs a function in all frozen sectors. This is used in the _redraw(). - * - * @param {String} runFunction | This is the NAME of a function we want to call in all active sectors - * | we don't pass the function itself because then the "this" is the window object - * | instead of the Network object - * @param {*} [argument] | Optional: arguments to pass to the runFunction - * @private - */ - exports._doInAllFrozenSectors = function(runFunction,argument) { - if (argument === undefined) { - for (var sector in this.sectors["frozen"]) { - if (this.sectors["frozen"].hasOwnProperty(sector)) { - // switch the global references to those of this sector - this._switchToFrozenSector(sector); - this[runFunction](); - } + dateFromConfig(config); } - } - else { - for (var sector in this.sectors["frozen"]) { - if (this.sectors["frozen"].hasOwnProperty(sector)) { - // switch the global references to those of this sector - this._switchToFrozenSector(sector); - var args = Array.prototype.splice.call(arguments, 1); - if (args.length > 1) { - this[runFunction](args[0],args[1]); - } - else { - this[runFunction](argument); + + function currentDateArray(config) { + var now = new Date(); + if (config._useUTC) { + return [ + now.getUTCFullYear(), + now.getUTCMonth(), + now.getUTCDate() + ]; + } else { + return [now.getFullYear(), now.getMonth(), now.getDate()]; } - } } - } - this._loadLatestSector(); - }; - - /** - * This runs a function in all sectors. This is used in the _redraw(). - * - * @param {String} runFunction | This is the NAME of a function we want to call in all active sectors - * | we don't pass the function itself because then the "this" is the window object - * | instead of the Network object - * @param {*} [argument] | Optional: arguments to pass to the runFunction - * @private - */ - exports._doInAllSectors = function(runFunction,argument) { - var args = Array.prototype.splice.call(arguments, 1); - if (argument === undefined) { - this._doInAllActiveSectors(runFunction); - this._doInAllFrozenSectors(runFunction); - } - else { - if (args.length > 1) { - this._doInAllActiveSectors(runFunction,args[0],args[1]); - this._doInAllFrozenSectors(runFunction,args[0],args[1]); - } - else { - this._doInAllActiveSectors(runFunction,argument); - this._doInAllFrozenSectors(runFunction,argument); - } - } - }; + // date from string and format string + function makeDateFromStringAndFormat(config) { + if (config._f === moment.ISO_8601) { + parseISO(config); + return; + } + config._a = []; + config._pf.empty = true; - /** - * This clears the nodeIndices list. We cannot use this.nodeIndices = [] because we would break the link with the - * active sector. Thus we clear the nodeIndices in the active sector, then reconnect the this.nodeIndices to it. - * - * @private - */ - exports._clearNodeIndexList = function() { - var sector = this._sector(); - this.sectors["active"][sector]["nodeIndices"] = []; - this.nodeIndices = this.sectors["active"][sector]["nodeIndices"]; - }; + // This array is used to make a Date, either with `new Date` or `Date.UTC` + var string = '' + config._i, + i, parsedInput, tokens, token, skipped, + stringLength = string.length, + totalParsedInputLength = 0; + tokens = expandFormat(config._f, config._locale).match(formattingTokens) || []; - /** - * Draw the encompassing sector node - * - * @param ctx - * @param sectorType - * @private - */ - exports._drawSectorNodes = function(ctx,sectorType) { - var minY = 1e9, maxY = -1e9, minX = 1e9, maxX = -1e9, node; - for (var sector in this.sectors[sectorType]) { - if (this.sectors[sectorType].hasOwnProperty(sector)) { - if (this.sectors[sectorType][sector]["drawingNode"] !== undefined) { + for (i = 0; i < tokens.length; i++) { + token = tokens[i]; + parsedInput = (string.match(getParseRegexForToken(token, config)) || [])[0]; + if (parsedInput) { + skipped = string.substr(0, string.indexOf(parsedInput)); + if (skipped.length > 0) { + config._pf.unusedInput.push(skipped); + } + string = string.slice(string.indexOf(parsedInput) + parsedInput.length); + totalParsedInputLength += parsedInput.length; + } + // don't parse if it's not a known token + if (formatTokenFunctions[token]) { + if (parsedInput) { + config._pf.empty = false; + } + else { + config._pf.unusedTokens.push(token); + } + addTimeToArrayFromToken(token, parsedInput, config); + } + else if (config._strict && !parsedInput) { + config._pf.unusedTokens.push(token); + } + } - this._switchToSector(sector,sectorType); + // add remaining unparsed input length to the string + config._pf.charsLeftOver = stringLength - totalParsedInputLength; + if (string.length > 0) { + config._pf.unusedInput.push(string); + } - minY = 1e9; maxY = -1e9; minX = 1e9; maxX = -1e9; - for (var nodeId in this.nodes) { - if (this.nodes.hasOwnProperty(nodeId)) { - node = this.nodes[nodeId]; - node.resize(ctx); - if (minX > node.x - 0.5 * node.width) {minX = node.x - 0.5 * node.width;} - if (maxX < node.x + 0.5 * node.width) {maxX = node.x + 0.5 * node.width;} - if (minY > node.y - 0.5 * node.height) {minY = node.y - 0.5 * node.height;} - if (maxY < node.y + 0.5 * node.height) {maxY = node.y + 0.5 * node.height;} - } + // clear _12h flag if hour is <= 12 + if (config._pf.bigHour === true && config._a[HOUR] <= 12) { + config._pf.bigHour = undefined; } - node = this.sectors[sectorType][sector]["drawingNode"]; - node.x = 0.5 * (maxX + minX); - node.y = 0.5 * (maxY + minY); - node.width = 2 * (node.x - minX); - node.height = 2 * (node.y - minY); - node.options.radius = Math.sqrt(Math.pow(0.5*node.width,2) + Math.pow(0.5*node.height,2)); - node.setScale(this.scale); - node._drawCircle(ctx); - } + // handle meridiem + config._a[HOUR] = meridiemFixWrap(config._locale, config._a[HOUR], + config._meridiem); + dateFromConfig(config); + checkOverflow(config); } - } - }; - exports._drawAllSectorNodes = function(ctx) { - this._drawSectorNodes(ctx,"frozen"); - this._drawSectorNodes(ctx,"active"); - this._loadLatestSector(); - }; + function unescapeFormat(s) { + return s.replace(/\\(\[)|\\(\])|\[([^\]\[]*)\]|\\(.)/g, function (matched, p1, p2, p3, p4) { + return p1 || p2 || p3 || p4; + }); + } + // Code from http://stackoverflow.com/questions/3561493/is-there-a-regexp-escape-function-in-javascript + function regexpEscape(s) { + return s.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&'); + } -/***/ }, -/* 62 */ -/***/ function(module, exports, __webpack_require__) { + // date from string and array of format strings + function makeDateFromStringAndArray(config) { + var tempConfig, + bestMoment, - var Node = __webpack_require__(40); + scoreToBeat, + i, + currentScore; - /** - * This function can be called from the _doInAllSectors function - * - * @param object - * @param overlappingNodes - * @private - */ - exports._getNodesOverlappingWith = function(object, overlappingNodes) { - var nodes = this.nodes; - for (var nodeId in nodes) { - if (nodes.hasOwnProperty(nodeId)) { - if (nodes[nodeId].isOverlappingWith(object)) { - overlappingNodes.push(nodeId); - } - } - } - }; + if (config._f.length === 0) { + config._pf.invalidFormat = true; + config._d = new Date(NaN); + return; + } - /** - * retrieve all nodes overlapping with given object - * @param {Object} object An object with parameters left, top, right, bottom - * @return {Number[]} An array with id's of the overlapping nodes - * @private - */ - exports._getAllNodesOverlappingWith = function (object) { - var overlappingNodes = []; - this._doInAllActiveSectors("_getNodesOverlappingWith",object,overlappingNodes); - return overlappingNodes; - }; + for (i = 0; i < config._f.length; i++) { + currentScore = 0; + tempConfig = copyConfig({}, config); + if (config._useUTC != null) { + tempConfig._useUTC = config._useUTC; + } + tempConfig._pf = defaultParsingFlags(); + tempConfig._f = config._f[i]; + makeDateFromStringAndFormat(tempConfig); + if (!isValid(tempConfig)) { + continue; + } - /** - * Return a position object in canvasspace from a single point in screenspace - * - * @param pointer - * @returns {{left: number, top: number, right: number, bottom: number}} - * @private - */ - exports._pointerToPositionObject = function(pointer) { - var x = this._XconvertDOMtoCanvas(pointer.x); - var y = this._YconvertDOMtoCanvas(pointer.y); + // if there is any input that was not parsed add a penalty for that format + currentScore += tempConfig._pf.charsLeftOver; - return { - left: x, - top: y, - right: x, - bottom: y - }; - }; + //or tokens + currentScore += tempConfig._pf.unusedTokens.length * 10; + tempConfig._pf.score = currentScore; - /** - * Get the top node at the a specific point (like a click) - * - * @param {{x: Number, y: Number}} pointer - * @return {Node | null} node - * @private - */ - exports._getNodeAt = function (pointer) { - // we first check if this is an navigation controls element - var positionObject = this._pointerToPositionObject(pointer); - var overlappingNodes = this._getAllNodesOverlappingWith(positionObject); + if (scoreToBeat == null || currentScore < scoreToBeat) { + scoreToBeat = currentScore; + bestMoment = tempConfig; + } + } - // if there are overlapping nodes, select the last one, this is the - // one which is drawn on top of the others - if (overlappingNodes.length > 0) { - return this.nodes[overlappingNodes[overlappingNodes.length - 1]]; - } - else { - return null; - } - }; + extend(config, bestMoment || tempConfig); + } + // date from iso format + function parseISO(config) { + var i, l, + string = config._i, + match = isoRegex.exec(string); - /** - * retrieve all edges overlapping with given object, selector is around center - * @param {Object} object An object with parameters left, top, right, bottom - * @return {Number[]} An array with id's of the overlapping nodes - * @private - */ - exports._getEdgesOverlappingWith = function (object, overlappingEdges) { - var edges = this.edges; - for (var edgeId in edges) { - if (edges.hasOwnProperty(edgeId)) { - if (edges[edgeId].isOverlappingWith(object)) { - overlappingEdges.push(edgeId); - } + if (match) { + config._pf.iso = true; + for (i = 0, l = isoDates.length; i < l; i++) { + if (isoDates[i][1].exec(string)) { + // match[5] should be 'T' or undefined + config._f = isoDates[i][0] + (match[6] || ' '); + break; + } + } + for (i = 0, l = isoTimes.length; i < l; i++) { + if (isoTimes[i][1].exec(string)) { + config._f += isoTimes[i][0]; + break; + } + } + if (string.match(parseTokenTimezone)) { + config._f += 'Z'; + } + makeDateFromStringAndFormat(config); + } else { + config._isValid = false; + } } - } - }; + // date from iso format or fallback + function makeDateFromString(config) { + parseISO(config); + if (config._isValid === false) { + delete config._isValid; + moment.createFromInputFallback(config); + } + } - /** - * retrieve all nodes overlapping with given object - * @param {Object} object An object with parameters left, top, right, bottom - * @return {Number[]} An array with id's of the overlapping nodes - * @private - */ - exports._getAllEdgesOverlappingWith = function (object) { - var overlappingEdges = []; - this._doInAllActiveSectors("_getEdgesOverlappingWith",object,overlappingEdges); - return overlappingEdges; - }; + function map(arr, fn) { + var res = [], i; + for (i = 0; i < arr.length; ++i) { + res.push(fn(arr[i], i)); + } + return res; + } - /** - * Place holder. To implement change the _getNodeAt to a _getObjectAt. Have the _getObjectAt call - * _getNodeAt and _getEdgesAt, then priortize the selection to user preferences. - * - * @param pointer - * @returns {null} - * @private - */ - exports._getEdgeAt = function(pointer) { - var positionObject = this._pointerToPositionObject(pointer); - var overlappingEdges = this._getAllEdgesOverlappingWith(positionObject); + function makeDateFromInput(config) { + var input = config._i, matched; + if (input === undefined) { + config._d = new Date(); + } else if (isDate(input)) { + config._d = new Date(+input); + } else if ((matched = aspNetJsonRegex.exec(input)) !== null) { + config._d = new Date(+matched[1]); + } else if (typeof input === 'string') { + makeDateFromString(config); + } else if (isArray(input)) { + config._a = map(input.slice(0), function (obj) { + return parseInt(obj, 10); + }); + dateFromConfig(config); + } else if (typeof(input) === 'object') { + dateFromObject(config); + } else if (typeof(input) === 'number') { + // from milliseconds + config._d = new Date(input); + } else { + moment.createFromInputFallback(config); + } + } - if (overlappingEdges.length > 0) { - return this.edges[overlappingEdges[overlappingEdges.length - 1]]; - } - else { - return null; - } - }; + function makeDate(y, m, d, h, M, s, ms) { + //can't just apply() to create a date: + //http://stackoverflow.com/questions/181348/instantiating-a-javascript-object-by-calling-prototype-constructor-apply + var date = new Date(y, m, d, h, M, s, ms); + //the date constructor doesn't accept years < 1970 + if (y < 1970) { + date.setFullYear(y); + } + return date; + } - /** - * Add object to the selection array. - * - * @param obj - * @private - */ - exports._addToSelection = function(obj) { - if (obj instanceof Node) { - this.selectionObj.nodes[obj.id] = obj; - } - else { - this.selectionObj.edges[obj.id] = obj; - } - }; + function makeUTCDate(y) { + var date = new Date(Date.UTC.apply(null, arguments)); + if (y < 1970) { + date.setUTCFullYear(y); + } + return date; + } - /** - * Add object to the selection array. - * - * @param obj - * @private - */ - exports._addToHover = function(obj) { - if (obj instanceof Node) { - this.hoverObj.nodes[obj.id] = obj; - } - else { - this.hoverObj.edges[obj.id] = obj; - } - }; + function parseWeekday(input, locale) { + if (typeof input === 'string') { + if (!isNaN(input)) { + input = parseInt(input, 10); + } + else { + input = locale.weekdaysParse(input); + if (typeof input !== 'number') { + return null; + } + } + } + return input; + } + /************************************ + Relative Time + ************************************/ - /** - * Remove a single option from selection. - * - * @param {Object} obj - * @private - */ - exports._removeFromSelection = function(obj) { - if (obj instanceof Node) { - delete this.selectionObj.nodes[obj.id]; - } - else { - delete this.selectionObj.edges[obj.id]; - } - }; - /** - * Unselect all. The selectionObj is useful for this. - * - * @param {Boolean} [doNotTrigger] | ignore trigger - * @private - */ - exports._unselectAll = function(doNotTrigger) { - if (doNotTrigger === undefined) { - doNotTrigger = false; - } - for(var nodeId in this.selectionObj.nodes) { - if(this.selectionObj.nodes.hasOwnProperty(nodeId)) { - this.selectionObj.nodes[nodeId].unselect(); - } - } - for(var edgeId in this.selectionObj.edges) { - if(this.selectionObj.edges.hasOwnProperty(edgeId)) { - this.selectionObj.edges[edgeId].unselect(); + // helper function for moment.fn.from, moment.fn.fromNow, and moment.duration.fn.humanize + function substituteTimeAgo(string, number, withoutSuffix, isFuture, locale) { + return locale.relativeTime(number || 1, !!withoutSuffix, string, isFuture); } - } - this.selectionObj = {nodes:{},edges:{}}; - - if (doNotTrigger == false) { - this.emit('select', this.getSelection()); - } - }; + function relativeTime(posNegDuration, withoutSuffix, locale) { + var duration = moment.duration(posNegDuration).abs(), + seconds = round(duration.as('s')), + minutes = round(duration.as('m')), + hours = round(duration.as('h')), + days = round(duration.as('d')), + months = round(duration.as('M')), + years = round(duration.as('y')), - /** - * Unselect all clusters. The selectionObj is useful for this. - * - * @param {Boolean} [doNotTrigger] | ignore trigger - * @private - */ - exports._unselectClusters = function(doNotTrigger) { - if (doNotTrigger === undefined) { - doNotTrigger = false; - } + args = seconds < relativeTimeThresholds.s && ['s', seconds] || + minutes === 1 && ['m'] || + minutes < relativeTimeThresholds.m && ['mm', minutes] || + hours === 1 && ['h'] || + hours < relativeTimeThresholds.h && ['hh', hours] || + days === 1 && ['d'] || + days < relativeTimeThresholds.d && ['dd', days] || + months === 1 && ['M'] || + months < relativeTimeThresholds.M && ['MM', months] || + years === 1 && ['y'] || ['yy', years]; - for (var nodeId in this.selectionObj.nodes) { - if (this.selectionObj.nodes.hasOwnProperty(nodeId)) { - if (this.selectionObj.nodes[nodeId].clusterSize > 1) { - this.selectionObj.nodes[nodeId].unselect(); - this._removeFromSelection(this.selectionObj.nodes[nodeId]); - } + args[2] = withoutSuffix; + args[3] = +posNegDuration > 0; + args[4] = locale; + return substituteTimeAgo.apply({}, args); } - } - - if (doNotTrigger == false) { - this.emit('select', this.getSelection()); - } - }; - /** - * return the number of selected nodes - * - * @returns {number} - * @private - */ - exports._getSelectedNodeCount = function() { - var count = 0; - for (var nodeId in this.selectionObj.nodes) { - if (this.selectionObj.nodes.hasOwnProperty(nodeId)) { - count += 1; - } - } - return count; - }; + /************************************ + Week of Year + ************************************/ - /** - * return the selected node - * - * @returns {number} - * @private - */ - exports._getSelectedNode = function() { - for (var nodeId in this.selectionObj.nodes) { - if (this.selectionObj.nodes.hasOwnProperty(nodeId)) { - return this.selectionObj.nodes[nodeId]; - } - } - return null; - }; - /** - * return the selected edge - * - * @returns {number} - * @private - */ - exports._getSelectedEdge = function() { - for (var edgeId in this.selectionObj.edges) { - if (this.selectionObj.edges.hasOwnProperty(edgeId)) { - return this.selectionObj.edges[edgeId]; - } - } - return null; - }; + // firstDayOfWeek 0 = sun, 6 = sat + // the day of the week that starts the week + // (usually sunday or monday) + // firstDayOfWeekOfYear 0 = sun, 6 = sat + // the first week is the week that contains the first + // of this day of the week + // (eg. ISO weeks use thursday (4)) + function weekOfYear(mom, firstDayOfWeek, firstDayOfWeekOfYear) { + var end = firstDayOfWeekOfYear - firstDayOfWeek, + daysToDayOfWeek = firstDayOfWeekOfYear - mom.day(), + adjustedMoment; - /** - * return the number of selected edges - * - * @returns {number} - * @private - */ - exports._getSelectedEdgeCount = function() { - var count = 0; - for (var edgeId in this.selectionObj.edges) { - if (this.selectionObj.edges.hasOwnProperty(edgeId)) { - count += 1; - } - } - return count; - }; + if (daysToDayOfWeek > end) { + daysToDayOfWeek -= 7; + } + if (daysToDayOfWeek < end - 7) { + daysToDayOfWeek += 7; + } - /** - * return the number of selected objects. - * - * @returns {number} - * @private - */ - exports._getSelectedObjectCount = function() { - var count = 0; - for(var nodeId in this.selectionObj.nodes) { - if(this.selectionObj.nodes.hasOwnProperty(nodeId)) { - count += 1; - } - } - for(var edgeId in this.selectionObj.edges) { - if(this.selectionObj.edges.hasOwnProperty(edgeId)) { - count += 1; + adjustedMoment = moment(mom).add(daysToDayOfWeek, 'd'); + return { + week: Math.ceil(adjustedMoment.dayOfYear() / 7), + year: adjustedMoment.year() + }; } - } - return count; - }; - /** - * Check if anything is selected - * - * @returns {boolean} - * @private - */ - exports._selectionIsEmpty = function() { - for(var nodeId in this.selectionObj.nodes) { - if(this.selectionObj.nodes.hasOwnProperty(nodeId)) { - return false; - } - } - for(var edgeId in this.selectionObj.edges) { - if(this.selectionObj.edges.hasOwnProperty(edgeId)) { - return false; - } - } - return true; - }; + //http://en.wikipedia.org/wiki/ISO_week_date#Calculating_a_date_given_the_year.2C_week_number_and_weekday + function dayOfYearFromWeeks(year, week, weekday, firstDayOfWeekOfYear, firstDayOfWeek) { + var d = makeUTCDate(year, 0, 1).getUTCDay(), daysToAdd, dayOfYear; + d = d === 0 ? 7 : d; + weekday = weekday != null ? weekday : firstDayOfWeek; + daysToAdd = firstDayOfWeek - d + (d > firstDayOfWeekOfYear ? 7 : 0) - (d < firstDayOfWeek ? 7 : 0); + dayOfYear = 7 * (week - 1) + (weekday - firstDayOfWeek) + daysToAdd + 1; - /** - * check if one of the selected nodes is a cluster. - * - * @returns {boolean} - * @private - */ - exports._clusterInSelection = function() { - for(var nodeId in this.selectionObj.nodes) { - if(this.selectionObj.nodes.hasOwnProperty(nodeId)) { - if (this.selectionObj.nodes[nodeId].clusterSize > 1) { - return true; - } + return { + year: dayOfYear > 0 ? year : year - 1, + dayOfYear: dayOfYear > 0 ? dayOfYear : daysInYear(year - 1) + dayOfYear + }; } - } - return false; - }; - /** - * select the edges connected to the node that is being selected - * - * @param {Node} node - * @private - */ - exports._selectConnectedEdges = function(node) { - for (var i = 0; i < node.dynamicEdges.length; i++) { - var edge = node.dynamicEdges[i]; - edge.select(); - this._addToSelection(edge); - } - }; + /************************************ + Top Level Functions + ************************************/ - /** - * select the edges connected to the node that is being selected - * - * @param {Node} node - * @private - */ - exports._hoverConnectedEdges = function(node) { - for (var i = 0; i < node.dynamicEdges.length; i++) { - var edge = node.dynamicEdges[i]; - edge.hover = true; - this._addToHover(edge); - } - }; + function makeMoment(config) { + var input = config._i, + format = config._f, + res; + config._locale = config._locale || moment.localeData(config._l); - /** - * unselect the edges connected to the node that is being selected - * - * @param {Node} node - * @private - */ - exports._unselectConnectedEdges = function(node) { - for (var i = 0; i < node.dynamicEdges.length; i++) { - var edge = node.dynamicEdges[i]; - edge.unselect(); - this._removeFromSelection(edge); - } - }; + if (input === null || (format === undefined && input === '')) { + return moment.invalid({nullInput: true}); + } + if (typeof input === 'string') { + config._i = input = config._locale.preparse(input); + } + if (moment.isMoment(input)) { + return new Moment(input, true); + } else if (format) { + if (isArray(format)) { + makeDateFromStringAndArray(config); + } else { + makeDateFromStringAndFormat(config); + } + } else { + makeDateFromInput(config); + } + res = new Moment(config); + if (res._nextDay) { + // Adding is smart enough around DST + res.add(1, 'd'); + res._nextDay = undefined; + } - /** - * This is called when someone clicks on a node. either select or deselect it. - * If there is an existing selection and we don't want to append to it, clear the existing selection - * - * @param {Node || Edge} object - * @param {Boolean} append - * @param {Boolean} [doNotTrigger] | ignore trigger - * @private - */ - exports._selectObject = function(object, append, doNotTrigger, highlightEdges, overrideSelectable) { - if (doNotTrigger === undefined) { - doNotTrigger = false; - } - if (highlightEdges === undefined) { - highlightEdges = true; - } + return res; + } - if (this._selectionIsEmpty() == false && append == false && this.forceAppendSelection == false) { - this._unselectAll(true); - } + moment = function (input, format, locale, strict) { + var c; - // selectable allows the object to be selected. Override can be used if needed to bypass this. - if (object.selected == false && (this.constants.selectable == true || overrideSelectable)) { - object.select(); - this._addToSelection(object); - if (object instanceof Node && this.blockConnectingEdgeSelection == false && highlightEdges == true) { - this._selectConnectedEdges(object); - } - } - // do not select the object if selectable is false, only add it to selection to allow drag to work - else if (object.selected == false) { - this._addToSelection(object); - doNotTrigger = true; - } - else { - object.unselect(); - this._removeFromSelection(object); - } + if (typeof(locale) === 'boolean') { + strict = locale; + locale = undefined; + } + // object construction must be done this way. + // https://github.com/moment/moment/issues/1423 + c = {}; + c._isAMomentObject = true; + c._i = input; + c._f = format; + c._l = locale; + c._strict = strict; + c._isUTC = false; + c._pf = defaultParsingFlags(); - if (doNotTrigger == false) { - this.emit('select', this.getSelection()); - } - }; + return makeMoment(c); + }; + moment.suppressDeprecationWarnings = false; - /** - * This is called when someone clicks on a node. either select or deselect it. - * If there is an existing selection and we don't want to append to it, clear the existing selection - * - * @param {Node || Edge} object - * @private - */ - exports._blurObject = function(object) { - if (object.hover == true) { - object.hover = false; - this.emit("blurNode",{node:object.id}); - } - }; + moment.createFromInputFallback = deprecate( + 'moment construction falls back to js Date. This is ' + + 'discouraged and will be removed in upcoming major ' + + 'release. Please refer to ' + + 'https://github.com/moment/moment/issues/1407 for more info.', + function (config) { + config._d = new Date(config._i + (config._useUTC ? ' UTC' : '')); + } + ); - /** - * This is called when someone clicks on a node. either select or deselect it. - * If there is an existing selection and we don't want to append to it, clear the existing selection - * - * @param {Node || Edge} object - * @private - */ - exports._hoverObject = function(object) { - if (object.hover == false) { - object.hover = true; - this._addToHover(object); - if (object instanceof Node) { - this.emit("hoverNode",{node:object.id}); + // Pick a moment m from moments so that m[fn](other) is true for all + // other. This relies on the function fn to be transitive. + // + // moments should either be an array of moment objects or an array, whose + // first element is an array of moment objects. + function pickBy(fn, moments) { + var res, i; + if (moments.length === 1 && isArray(moments[0])) { + moments = moments[0]; + } + if (!moments.length) { + return moment(); + } + res = moments[0]; + for (i = 1; i < moments.length; ++i) { + if (moments[i][fn](res)) { + res = moments[i]; + } + } + return res; } - } - if (object instanceof Node) { - this._hoverConnectedEdges(object); - } - }; + moment.min = function () { + var args = [].slice.call(arguments, 0); - /** - * handles the selection part of the touch, only for navigation controls elements; - * Touch is triggered before tap, also before hold. Hold triggers after a while. - * This is the most responsive solution - * - * @param {Object} pointer - * @private - */ - exports._handleTouch = function(pointer) { - }; + return pickBy('isBefore', args); + }; + moment.max = function () { + var args = [].slice.call(arguments, 0); - /** - * handles the selection part of the tap; - * - * @param {Object} pointer - * @private - */ - exports._handleTap = function(pointer) { - var node = this._getNodeAt(pointer); - if (node != null) { - this._selectObject(node, false); - } - else { - var edge = this._getEdgeAt(pointer); - if (edge != null) { - this._selectObject(edge, false); - } - else { - this._unselectAll(); - } - } - var properties = this.getSelection(); - properties['pointer'] = { - DOM: {x: pointer.x, y: pointer.y}, - canvas: {x: this._XconvertDOMtoCanvas(pointer.x), y: this._YconvertDOMtoCanvas(pointer.y)} - } - this.emit("click", properties); - this._redraw(); - }; + return pickBy('isAfter', args); + }; + // creating with utc + moment.utc = function (input, format, locale, strict) { + var c; - /** - * handles the selection part of the double tap and opens a cluster if needed - * - * @param {Object} pointer - * @private - */ - exports._handleDoubleTap = function(pointer) { - var node = this._getNodeAt(pointer); - if (node != null && node !== undefined) { - // we reset the areaCenter here so the opening of the node will occur - this.areaCenter = {"x" : this._XconvertDOMtoCanvas(pointer.x), - "y" : this._YconvertDOMtoCanvas(pointer.y)}; - this.openCluster(node); - } - var properties = this.getSelection(); - properties['pointer'] = { - DOM: {x: pointer.x, y: pointer.y}, - canvas: {x: this._XconvertDOMtoCanvas(pointer.x), y: this._YconvertDOMtoCanvas(pointer.y)} - } - this.emit("doubleClick", properties); - }; + if (typeof(locale) === 'boolean') { + strict = locale; + locale = undefined; + } + // object construction must be done this way. + // https://github.com/moment/moment/issues/1423 + c = {}; + c._isAMomentObject = true; + c._useUTC = true; + c._isUTC = true; + c._l = locale; + c._i = input; + c._f = format; + c._strict = strict; + c._pf = defaultParsingFlags(); + return makeMoment(c).utc(); + }; - /** - * Handle the onHold selection part - * - * @param pointer - * @private - */ - exports._handleOnHold = function(pointer) { - var node = this._getNodeAt(pointer); - if (node != null) { - this._selectObject(node,true); - } - else { - var edge = this._getEdgeAt(pointer); - if (edge != null) { - this._selectObject(edge,true); - } - } - this._redraw(); - }; + // creating with unix timestamp (in seconds) + moment.unix = function (input) { + return moment(input * 1000); + }; + // duration + moment.duration = function (input, key) { + var duration = input, + // matching against regexp is expensive, do it on demand + match = null, + sign, + ret, + parseIso, + diffRes; - /** - * handle the onRelease event. These functions are here for the navigation controls module - * and data manipulation module. - * - * @private - */ - exports._handleOnRelease = function(pointer) { - this._manipulationReleaseOverload(pointer); - this._navigationReleaseOverload(pointer); - }; + if (moment.isDuration(input)) { + duration = { + ms: input._milliseconds, + d: input._days, + M: input._months + }; + } else if (typeof input === 'number') { + duration = {}; + if (key) { + duration[key] = input; + } else { + duration.milliseconds = input; + } + } else if (!!(match = aspNetTimeSpanJsonRegex.exec(input))) { + sign = (match[1] === '-') ? -1 : 1; + duration = { + y: 0, + d: toInt(match[DATE]) * sign, + h: toInt(match[HOUR]) * sign, + m: toInt(match[MINUTE]) * sign, + s: toInt(match[SECOND]) * sign, + ms: toInt(match[MILLISECOND]) * sign + }; + } else if (!!(match = isoDurationRegex.exec(input))) { + sign = (match[1] === '-') ? -1 : 1; + parseIso = function (inp) { + // We'd normally use ~~inp for this, but unfortunately it also + // converts floats to ints. + // inp may be undefined, so careful calling replace on it. + var res = inp && parseFloat(inp.replace(',', '.')); + // apply sign while we're at it + return (isNaN(res) ? 0 : res) * sign; + }; + duration = { + y: parseIso(match[2]), + M: parseIso(match[3]), + d: parseIso(match[4]), + h: parseIso(match[5]), + m: parseIso(match[6]), + s: parseIso(match[7]), + w: parseIso(match[8]) + }; + } else if (duration == null) {// checks for null or undefined + duration = {}; + } else if (typeof duration === 'object' && + ('from' in duration || 'to' in duration)) { + diffRes = momentsDifference(moment(duration.from), moment(duration.to)); - exports._manipulationReleaseOverload = function (pointer) {}; - exports._navigationReleaseOverload = function (pointer) {}; + duration = {}; + duration.ms = diffRes.milliseconds; + duration.M = diffRes.months; + } - /** - * - * retrieve the currently selected objects - * @return {{nodes: Array., edges: Array.}} selection - */ - exports.getSelection = function() { - var nodeIds = this.getSelectedNodes(); - var edgeIds = this.getSelectedEdges(); - return {nodes:nodeIds, edges:edgeIds}; - }; + ret = new Duration(duration); - /** - * - * retrieve the currently selected nodes - * @return {String[]} selection An array with the ids of the - * selected nodes. - */ - exports.getSelectedNodes = function() { - var idArray = []; - if (this.constants.selectable == true) { - for (var nodeId in this.selectionObj.nodes) { - if (this.selectionObj.nodes.hasOwnProperty(nodeId)) { - idArray.push(nodeId); - } - } - } - return idArray - }; + if (moment.isDuration(input) && hasOwnProp(input, '_locale')) { + ret._locale = input._locale; + } - /** - * - * retrieve the currently selected edges - * @return {Array} selection An array with the ids of the - * selected nodes. - */ - exports.getSelectedEdges = function() { - var idArray = []; - if (this.constants.selectable == true) { - for (var edgeId in this.selectionObj.edges) { - if (this.selectionObj.edges.hasOwnProperty(edgeId)) { - idArray.push(edgeId); - } - } - } - return idArray; - }; + return ret; + }; + // version number + moment.version = VERSION; - /** - * select zero or more nodes DEPRICATED - * @param {Number[] | String[]} selection An array with the ids of the - * selected nodes. - */ - exports.setSelection = function() { - console.log("setSelection is deprecated. Please use selectNodes instead.") - }; + // default format + moment.defaultFormat = isoFormat; + // constant that refers to the ISO standard + moment.ISO_8601 = function () {}; - /** - * select zero or more nodes with the option to highlight edges - * @param {Number[] | String[]} selection An array with the ids of the - * selected nodes. - * @param {boolean} [highlightEdges] - */ - exports.selectNodes = function(selection, highlightEdges) { - var i, iMax, id; + // Plugins that add properties should also add the key here (null value), + // so we can properly clone ourselves. + moment.momentProperties = momentProperties; - if (!selection || (selection.length == undefined)) - throw 'Selection must be an array with ids'; + // This function will be called whenever a moment is mutated. + // It is intended to keep the offset in sync with the timezone. + moment.updateOffset = function () {}; - // first unselect any selected node - this._unselectAll(true); + // This function allows you to set a threshold for relative time strings + moment.relativeTimeThreshold = function (threshold, limit) { + if (relativeTimeThresholds[threshold] === undefined) { + return false; + } + if (limit === undefined) { + return relativeTimeThresholds[threshold]; + } + relativeTimeThresholds[threshold] = limit; + return true; + }; - for (i = 0, iMax = selection.length; i < iMax; i++) { - id = selection[i]; + moment.lang = deprecate( + 'moment.lang is deprecated. Use moment.locale instead.', + function (key, value) { + return moment.locale(key, value); + } + ); - var node = this.nodes[id]; - if (!node) { - throw new RangeError('Node with id "' + id + '" not found'); - } - this._selectObject(node,true,true,highlightEdges,true); - } - this.redraw(); - }; + // This function will load locale and then set the global locale. If + // no arguments are passed in, it will simply return the current global + // locale key. + moment.locale = function (key, values) { + var data; + if (key) { + if (typeof(values) !== 'undefined') { + data = moment.defineLocale(key, values); + } + else { + data = moment.localeData(key); + } + if (data) { + moment.duration._locale = moment._locale = data; + } + } - /** - * select zero or more edges - * @param {Number[] | String[]} selection An array with the ids of the - * selected nodes. - */ - exports.selectEdges = function(selection) { - var i, iMax, id; + return moment._locale._abbr; + }; - if (!selection || (selection.length == undefined)) - throw 'Selection must be an array with ids'; + moment.defineLocale = function (name, values) { + if (values !== null) { + values.abbr = name; + if (!locales[name]) { + locales[name] = new Locale(); + } + locales[name].set(values); - // first unselect any selected node - this._unselectAll(true); + // backwards compat for now: also set the locale + moment.locale(name); - for (i = 0, iMax = selection.length; i < iMax; i++) { - id = selection[i]; + return locales[name]; + } else { + // useful for testing + delete locales[name]; + return null; + } + }; - var edge = this.edges[id]; - if (!edge) { - throw new RangeError('Edge with id "' + id + '" not found'); - } - this._selectObject(edge,true,true,false,true); - } - this.redraw(); - }; + moment.langData = deprecate( + 'moment.langData is deprecated. Use moment.localeData instead.', + function (key) { + return moment.localeData(key); + } + ); - /** - * Validate the selection: remove ids of nodes which no longer exist - * @private - */ - exports._updateSelection = function () { - for(var nodeId in this.selectionObj.nodes) { - if(this.selectionObj.nodes.hasOwnProperty(nodeId)) { - if (!this.nodes.hasOwnProperty(nodeId)) { - delete this.selectionObj.nodes[nodeId]; - } - } - } - for(var edgeId in this.selectionObj.edges) { - if(this.selectionObj.edges.hasOwnProperty(edgeId)) { - if (!this.edges.hasOwnProperty(edgeId)) { - delete this.selectionObj.edges[edgeId]; - } - } - } - }; + // returns locale data + moment.localeData = function (key) { + var locale; + if (key && key._locale && key._locale._abbr) { + key = key._locale._abbr; + } -/***/ }, -/* 63 */ -/***/ function(module, exports, __webpack_require__) { + if (!key) { + return moment._locale; + } - var util = __webpack_require__(1); - var Node = __webpack_require__(40); - var Edge = __webpack_require__(37); + if (!isArray(key)) { + //short-circuit everything else + locale = loadLocale(key); + if (locale) { + return locale; + } + key = [key]; + } - /** - * clears the toolbar div element of children - * - * @private - */ - exports._clearManipulatorBar = function() { - this._recursiveDOMDelete(this.manipulationDiv); - this.manipulationDOM = {}; + return chooseLocale(key); + }; - this._manipulationReleaseOverload = function () {}; - delete this.sectors['support']['nodes']['targetNode']; - delete this.sectors['support']['nodes']['targetViaNode']; - this.controlNodesActive = false; - this.freezeSimulation = false; - }; + // compare moment object + moment.isMoment = function (obj) { + return obj instanceof Moment || + (obj != null && hasOwnProp(obj, '_isAMomentObject')); + }; - /** - * Manipulation UI temporarily overloads certain functions to extend or replace them. To be able to restore - * these functions to their original functionality, we saved them in this.cachedFunctions. - * This function restores these functions to their original function. - * - * @private - */ - exports._restoreOverloadedFunctions = function() { - for (var functionName in this.cachedFunctions) { - if (this.cachedFunctions.hasOwnProperty(functionName)) { - this[functionName] = this.cachedFunctions[functionName]; - delete this.cachedFunctions[functionName]; + // for typechecking Duration objects + moment.isDuration = function (obj) { + return obj instanceof Duration; + }; + + for (i = lists.length - 1; i >= 0; --i) { + makeList(lists[i]); } - } - }; - /** - * Enable or disable edit-mode. - * - * @private - */ - exports._toggleEditMode = function() { - this.editMode = !this.editMode; - var toolbar = this.manipulationDiv; - var closeDiv = this.closeDiv; - var editModeDiv = this.editModeDiv; - if (this.editMode == true) { - toolbar.style.display="block"; - closeDiv.style.display="block"; - editModeDiv.style.display="none"; - closeDiv.onclick = this._toggleEditMode.bind(this); - } - else { - toolbar.style.display="none"; - closeDiv.style.display="none"; - editModeDiv.style.display="block"; - closeDiv.onclick = null; - } - this._createManipulatorBar() - }; + moment.normalizeUnits = function (units) { + return normalizeUnits(units); + }; - /** - * main function, creates the main toolbar. Removes functions bound to the select event. Binds all the buttons of the toolbar. - * - * @private - */ - exports._createManipulatorBar = function() { - // remove bound functions - if (this.boundFunction) { - this.off('select', this.boundFunction); - } + moment.invalid = function (flags) { + var m = moment.utc(NaN); + if (flags != null) { + extend(m._pf, flags); + } + else { + m._pf.userInvalidated = true; + } - var locale = this.constants.locales[this.constants.locale]; + return m; + }; - if (this.edgeBeingEdited !== undefined) { - this.edgeBeingEdited._disableControlNodes(); - this.edgeBeingEdited = undefined; - this.selectedControlNode = null; - this.controlNodesActive = false; - this._redraw(); - } + moment.parseZone = function () { + return moment.apply(null, arguments).parseZone(); + }; - // restore overloaded functions - this._restoreOverloadedFunctions(); + moment.parseTwoDigitYear = function (input) { + return toInt(input) + (toInt(input) > 68 ? 1900 : 2000); + }; - // resume calculation - this.freezeSimulation = false; + moment.isDate = isDate; - // reset global variables - this.blockConnectingEdgeSelection = false; - this.forceAppendSelection = false; - this.manipulationDOM = {}; + /************************************ + Moment Prototype + ************************************/ - if (this.editMode == true) { - while (this.manipulationDiv.hasChildNodes()) { - this.manipulationDiv.removeChild(this.manipulationDiv.firstChild); - } - this.manipulationDOM['addNodeSpan'] = document.createElement('span'); - this.manipulationDOM['addNodeSpan'].className = 'network-manipulationUI add'; - this.manipulationDOM['addNodeLabelSpan'] = document.createElement('span'); - this.manipulationDOM['addNodeLabelSpan'].className = 'network-manipulationLabel'; - this.manipulationDOM['addNodeLabelSpan'].innerHTML = locale['addNode']; - this.manipulationDOM['addNodeSpan'].appendChild(this.manipulationDOM['addNodeLabelSpan']); + extend(moment.fn = Moment.prototype, { - this.manipulationDOM['seperatorLineDiv1'] = document.createElement('div'); - this.manipulationDOM['seperatorLineDiv1'].className = 'network-seperatorLine'; + clone : function () { + return moment(this); + }, - this.manipulationDOM['addEdgeSpan'] = document.createElement('span'); - this.manipulationDOM['addEdgeSpan'].className = 'network-manipulationUI connect'; - this.manipulationDOM['addEdgeLabelSpan'] = document.createElement('span'); - this.manipulationDOM['addEdgeLabelSpan'].className = 'network-manipulationLabel'; - this.manipulationDOM['addEdgeLabelSpan'].innerHTML = locale['addEdge']; - this.manipulationDOM['addEdgeSpan'].appendChild(this.manipulationDOM['addEdgeLabelSpan']); + valueOf : function () { + return +this._d - ((this._offset || 0) * 60000); + }, - this.manipulationDiv.appendChild(this.manipulationDOM['addNodeSpan']); - this.manipulationDiv.appendChild(this.manipulationDOM['seperatorLineDiv1']); - this.manipulationDiv.appendChild(this.manipulationDOM['addEdgeSpan']); + unix : function () { + return Math.floor(+this / 1000); + }, - if (this._getSelectedNodeCount() == 1 && this.triggerFunctions.edit) { - this.manipulationDOM['seperatorLineDiv2'] = document.createElement('div'); - this.manipulationDOM['seperatorLineDiv2'].className = 'network-seperatorLine'; + toString : function () { + return this.clone().locale('en').format('ddd MMM DD YYYY HH:mm:ss [GMT]ZZ'); + }, - this.manipulationDOM['editNodeSpan'] = document.createElement('span'); - this.manipulationDOM['editNodeSpan'].className = 'network-manipulationUI edit'; - this.manipulationDOM['editNodeLabelSpan'] = document.createElement('span'); - this.manipulationDOM['editNodeLabelSpan'].className = 'network-manipulationLabel'; - this.manipulationDOM['editNodeLabelSpan'].innerHTML = locale['editNode']; - this.manipulationDOM['editNodeSpan'].appendChild(this.manipulationDOM['editNodeLabelSpan']); + toDate : function () { + return this._offset ? new Date(+this) : this._d; + }, - this.manipulationDiv.appendChild(this.manipulationDOM['seperatorLineDiv2']); - this.manipulationDiv.appendChild(this.manipulationDOM['editNodeSpan']); - } - else if (this._getSelectedEdgeCount() == 1 && this._getSelectedNodeCount() == 0) { - this.manipulationDOM['seperatorLineDiv3'] = document.createElement('div'); - this.manipulationDOM['seperatorLineDiv3'].className = 'network-seperatorLine'; + toISOString : function () { + var m = moment(this).utc(); + if (0 < m.year() && m.year() <= 9999) { + if ('function' === typeof Date.prototype.toISOString) { + // native implementation is ~50x faster, use it when we can + return this.toDate().toISOString(); + } else { + return formatMoment(m, 'YYYY-MM-DD[T]HH:mm:ss.SSS[Z]'); + } + } else { + return formatMoment(m, 'YYYYYY-MM-DD[T]HH:mm:ss.SSS[Z]'); + } + }, - this.manipulationDOM['editEdgeSpan'] = document.createElement('span'); - this.manipulationDOM['editEdgeSpan'].className = 'network-manipulationUI edit'; - this.manipulationDOM['editEdgeLabelSpan'] = document.createElement('span'); - this.manipulationDOM['editEdgeLabelSpan'].className = 'network-manipulationLabel'; - this.manipulationDOM['editEdgeLabelSpan'].innerHTML = locale['editEdge']; - this.manipulationDOM['editEdgeSpan'].appendChild(this.manipulationDOM['editEdgeLabelSpan']); + toArray : function () { + var m = this; + return [ + m.year(), + m.month(), + m.date(), + m.hours(), + m.minutes(), + m.seconds(), + m.milliseconds() + ]; + }, - this.manipulationDiv.appendChild(this.manipulationDOM['seperatorLineDiv3']); - this.manipulationDiv.appendChild(this.manipulationDOM['editEdgeSpan']); - } - if (this._selectionIsEmpty() == false) { - this.manipulationDOM['seperatorLineDiv4'] = document.createElement('div'); - this.manipulationDOM['seperatorLineDiv4'].className = 'network-seperatorLine'; + isValid : function () { + return isValid(this); + }, - this.manipulationDOM['deleteSpan'] = document.createElement('span'); - this.manipulationDOM['deleteSpan'].className = 'network-manipulationUI delete'; - this.manipulationDOM['deleteLabelSpan'] = document.createElement('span'); - this.manipulationDOM['deleteLabelSpan'].className = 'network-manipulationLabel'; - this.manipulationDOM['deleteLabelSpan'].innerHTML = locale['del']; - this.manipulationDOM['deleteSpan'].appendChild(this.manipulationDOM['deleteLabelSpan']); + isDSTShifted : function () { + if (this._a) { + return this.isValid() && compareArrays(this._a, (this._isUTC ? moment.utc(this._a) : moment(this._a)).toArray()) > 0; + } - this.manipulationDiv.appendChild(this.manipulationDOM['seperatorLineDiv4']); - this.manipulationDiv.appendChild(this.manipulationDOM['deleteSpan']); - } - - - // bind the icons - this.manipulationDOM['addNodeSpan'].onclick = this._createAddNodeToolbar.bind(this); - this.manipulationDOM['addEdgeSpan'].onclick = this._createAddEdgeToolbar.bind(this); - if (this._getSelectedNodeCount() == 1 && this.triggerFunctions.edit) { - this.manipulationDOM['editNodeSpan'].onclick = this._editNode.bind(this); - } - else if (this._getSelectedEdgeCount() == 1 && this._getSelectedNodeCount() == 0) { - this.manipulationDOM['editEdgeSpan'].onclick = this._createEditEdgeToolbar.bind(this); - } - if (this._selectionIsEmpty() == false) { - this.manipulationDOM['deleteSpan'].onclick = this._deleteSelected.bind(this); - } - this.closeDiv.onclick = this._toggleEditMode.bind(this); - - var me = this; - this.boundFunction = me._createManipulatorBar; - this.on('select', this.boundFunction); - } - else { - while (this.editModeDiv.hasChildNodes()) { - this.editModeDiv.removeChild(this.editModeDiv.firstChild); - } - - this.manipulationDOM['editModeSpan'] = document.createElement('span'); - this.manipulationDOM['editModeSpan'].className = 'network-manipulationUI edit editmode'; - this.manipulationDOM['editModeLabelSpan'] = document.createElement('span'); - this.manipulationDOM['editModeLabelSpan'].className = 'network-manipulationLabel'; - this.manipulationDOM['editModeLabelSpan'].innerHTML = locale['edit']; - this.manipulationDOM['editModeSpan'].appendChild(this.manipulationDOM['editModeLabelSpan']); - - this.editModeDiv.appendChild(this.manipulationDOM['editModeSpan']); - - this.manipulationDOM['editModeSpan'].onclick = this._toggleEditMode.bind(this); - } - }; - - - - /** - * Create the toolbar for adding Nodes - * - * @private - */ - exports._createAddNodeToolbar = function() { - // clear the toolbar - this._clearManipulatorBar(); - if (this.boundFunction) { - this.off('select', this.boundFunction); - } - - var locale = this.constants.locales[this.constants.locale]; - - this.manipulationDOM = {}; - this.manipulationDOM['backSpan'] = document.createElement('span'); - this.manipulationDOM['backSpan'].className = 'network-manipulationUI back'; - this.manipulationDOM['backLabelSpan'] = document.createElement('span'); - this.manipulationDOM['backLabelSpan'].className = 'network-manipulationLabel'; - this.manipulationDOM['backLabelSpan'].innerHTML = locale['back']; - this.manipulationDOM['backSpan'].appendChild(this.manipulationDOM['backLabelSpan']); - - this.manipulationDOM['seperatorLineDiv1'] = document.createElement('div'); - this.manipulationDOM['seperatorLineDiv1'].className = 'network-seperatorLine'; - - this.manipulationDOM['descriptionSpan'] = document.createElement('span'); - this.manipulationDOM['descriptionSpan'].className = 'network-manipulationUI none'; - this.manipulationDOM['descriptionLabelSpan'] = document.createElement('span'); - this.manipulationDOM['descriptionLabelSpan'].className = 'network-manipulationLabel'; - this.manipulationDOM['descriptionLabelSpan'].innerHTML = locale['addDescription']; - this.manipulationDOM['descriptionSpan'].appendChild(this.manipulationDOM['descriptionLabelSpan']); - - this.manipulationDiv.appendChild(this.manipulationDOM['backSpan']); - this.manipulationDiv.appendChild(this.manipulationDOM['seperatorLineDiv1']); - this.manipulationDiv.appendChild(this.manipulationDOM['descriptionSpan']); - - // bind the icon - this.manipulationDOM['backSpan'].onclick = this._createManipulatorBar.bind(this); - - // we use the boundFunction so we can reference it when we unbind it from the "select" event. - var me = this; - this.boundFunction = me._addNode; - this.on('select', this.boundFunction); - }; - - - /** - * create the toolbar to connect nodes - * - * @private - */ - exports._createAddEdgeToolbar = function() { - // clear the toolbar - this._clearManipulatorBar(); - this._unselectAll(true); - this.freezeSimulation = true; - - if (this.boundFunction) { - this.off('select', this.boundFunction); - } - - var locale = this.constants.locales[this.constants.locale]; - - this._unselectAll(); - this.forceAppendSelection = false; - this.blockConnectingEdgeSelection = true; - - this.manipulationDOM = {}; - this.manipulationDOM['backSpan'] = document.createElement('span'); - this.manipulationDOM['backSpan'].className = 'network-manipulationUI back'; - this.manipulationDOM['backLabelSpan'] = document.createElement('span'); - this.manipulationDOM['backLabelSpan'].className = 'network-manipulationLabel'; - this.manipulationDOM['backLabelSpan'].innerHTML = locale['back']; - this.manipulationDOM['backSpan'].appendChild(this.manipulationDOM['backLabelSpan']); - - this.manipulationDOM['seperatorLineDiv1'] = document.createElement('div'); - this.manipulationDOM['seperatorLineDiv1'].className = 'network-seperatorLine'; - - this.manipulationDOM['descriptionSpan'] = document.createElement('span'); - this.manipulationDOM['descriptionSpan'].className = 'network-manipulationUI none'; - this.manipulationDOM['descriptionLabelSpan'] = document.createElement('span'); - this.manipulationDOM['descriptionLabelSpan'].className = 'network-manipulationLabel'; - this.manipulationDOM['descriptionLabelSpan'].innerHTML = locale['edgeDescription']; - this.manipulationDOM['descriptionSpan'].appendChild(this.manipulationDOM['descriptionLabelSpan']); - - this.manipulationDiv.appendChild(this.manipulationDOM['backSpan']); - this.manipulationDiv.appendChild(this.manipulationDOM['seperatorLineDiv1']); - this.manipulationDiv.appendChild(this.manipulationDOM['descriptionSpan']); - - // bind the icon - this.manipulationDOM['backSpan'].onclick = this._createManipulatorBar.bind(this); - - // we use the boundFunction so we can reference it when we unbind it from the "select" event. - var me = this; - this.boundFunction = me._handleConnect; - this.on('select', this.boundFunction); - - // temporarily overload functions - this.cachedFunctions["_handleTouch"] = this._handleTouch; - this.cachedFunctions["_manipulationReleaseOverload"] = this._manipulationReleaseOverload; - this.cachedFunctions["_handleDragStart"] = this._handleDragStart; - this.cachedFunctions["_handleDragEnd"] = this._handleDragEnd; - this._handleTouch = this._handleConnect; - this._manipulationReleaseOverload = function () {}; - this._handleDragStart = function () {}; - this._handleDragEnd = this._finishConnect; - - // redraw to show the unselect - this._redraw(); - }; - - /** - * create the toolbar to edit edges - * - * @private - */ - exports._createEditEdgeToolbar = function() { - // clear the toolbar - this._clearManipulatorBar(); - this.controlNodesActive = true; - - if (this.boundFunction) { - this.off('select', this.boundFunction); - } - - this.edgeBeingEdited = this._getSelectedEdge(); - this.edgeBeingEdited._enableControlNodes(); - - var locale = this.constants.locales[this.constants.locale]; + return false; + }, - this.manipulationDOM = {}; - this.manipulationDOM['backSpan'] = document.createElement('span'); - this.manipulationDOM['backSpan'].className = 'network-manipulationUI back'; - this.manipulationDOM['backLabelSpan'] = document.createElement('span'); - this.manipulationDOM['backLabelSpan'].className = 'network-manipulationLabel'; - this.manipulationDOM['backLabelSpan'].innerHTML = locale['back']; - this.manipulationDOM['backSpan'].appendChild(this.manipulationDOM['backLabelSpan']); + parsingFlags : function () { + return extend({}, this._pf); + }, - this.manipulationDOM['seperatorLineDiv1'] = document.createElement('div'); - this.manipulationDOM['seperatorLineDiv1'].className = 'network-seperatorLine'; + invalidAt: function () { + return this._pf.overflow; + }, - this.manipulationDOM['descriptionSpan'] = document.createElement('span'); - this.manipulationDOM['descriptionSpan'].className = 'network-manipulationUI none'; - this.manipulationDOM['descriptionLabelSpan'] = document.createElement('span'); - this.manipulationDOM['descriptionLabelSpan'].className = 'network-manipulationLabel'; - this.manipulationDOM['descriptionLabelSpan'].innerHTML = locale['editEdgeDescription']; - this.manipulationDOM['descriptionSpan'].appendChild(this.manipulationDOM['descriptionLabelSpan']); + utc : function (keepLocalTime) { + return this.utcOffset(0, keepLocalTime); + }, - this.manipulationDiv.appendChild(this.manipulationDOM['backSpan']); - this.manipulationDiv.appendChild(this.manipulationDOM['seperatorLineDiv1']); - this.manipulationDiv.appendChild(this.manipulationDOM['descriptionSpan']); + local : function (keepLocalTime) { + if (this._isUTC) { + this.utcOffset(0, keepLocalTime); + this._isUTC = false; - // bind the icon - this.manipulationDOM['backSpan'].onclick = this._createManipulatorBar.bind(this); + if (keepLocalTime) { + this.subtract(this._dateUtcOffset(), 'm'); + } + } + return this; + }, - // temporarily overload functions - this.cachedFunctions["_handleTouch"] = this._handleTouch; - this.cachedFunctions["_manipulationReleaseOverload"] = this._manipulationReleaseOverload; - this.cachedFunctions["_handleTap"] = this._handleTap; - this.cachedFunctions["_handleDragStart"] = this._handleDragStart; - this.cachedFunctions["_handleOnDrag"] = this._handleOnDrag; - this._handleTouch = this._selectControlNode; - this._handleTap = function () {}; - this._handleOnDrag = this._controlNodeDrag; - this._handleDragStart = function () {} - this._manipulationReleaseOverload = this._releaseControlNode; + format : function (inputString) { + var output = formatMoment(this, inputString || moment.defaultFormat); + return this.localeData().postformat(output); + }, - // redraw to show the unselect - this._redraw(); - }; + add : createAdder(1, 'add'), + subtract : createAdder(-1, 'subtract'), - /** - * the function bound to the selection event. It checks if you want to connect a cluster and changes the description - * to walk the user through the process. - * - * @private - */ - exports._selectControlNode = function(pointer) { - this.edgeBeingEdited.controlNodes.from.unselect(); - this.edgeBeingEdited.controlNodes.to.unselect(); - this.selectedControlNode = this.edgeBeingEdited._getSelectedControlNode(this._XconvertDOMtoCanvas(pointer.x),this._YconvertDOMtoCanvas(pointer.y)); - if (this.selectedControlNode !== null) { - this.selectedControlNode.select(); - this.freezeSimulation = true; - } - this._redraw(); - }; + diff : function (input, units, asFloat) { + var that = makeAs(input, this), + zoneDiff = (that.utcOffset() - this.utcOffset()) * 6e4, + anchor, diff, output, daysAdjust; + units = normalizeUnits(units); - /** - * the function bound to the selection event. It checks if you want to connect a cluster and changes the description - * to walk the user through the process. - * - * @private - */ - exports._controlNodeDrag = function(event) { - var pointer = this._getPointer(event.gesture.center); - if (this.selectedControlNode !== null && this.selectedControlNode !== undefined) { - this.selectedControlNode.x = this._XconvertDOMtoCanvas(pointer.x); - this.selectedControlNode.y = this._YconvertDOMtoCanvas(pointer.y); - } - this._redraw(); - }; + if (units === 'year' || units === 'month' || units === 'quarter') { + output = monthDiff(this, that); + if (units === 'quarter') { + output = output / 3; + } else if (units === 'year') { + output = output / 12; + } + } else { + diff = this - that; + output = units === 'second' ? diff / 1e3 : // 1000 + units === 'minute' ? diff / 6e4 : // 1000 * 60 + units === 'hour' ? diff / 36e5 : // 1000 * 60 * 60 + units === 'day' ? (diff - zoneDiff) / 864e5 : // 1000 * 60 * 60 * 24, negate dst + units === 'week' ? (diff - zoneDiff) / 6048e5 : // 1000 * 60 * 60 * 24 * 7, negate dst + diff; + } + return asFloat ? output : absRound(output); + }, + from : function (time, withoutSuffix) { + return moment.duration({to: this, from: time}).locale(this.locale()).humanize(!withoutSuffix); + }, - /** - * - * @param pointer - * @private - */ - exports._releaseControlNode = function(pointer) { - var newNode = this._getNodeAt(pointer); - if (newNode !== null) { - if (this.edgeBeingEdited.controlNodes.from.selected == true) { - this.edgeBeingEdited._restoreControlNodes(); - this._editEdge(newNode.id, this.edgeBeingEdited.to.id); - this.edgeBeingEdited.controlNodes.from.unselect(); - } - if (this.edgeBeingEdited.controlNodes.to.selected == true) { - this.edgeBeingEdited._restoreControlNodes(); - this._editEdge(this.edgeBeingEdited.from.id, newNode.id); - this.edgeBeingEdited.controlNodes.to.unselect(); - } - } - else { - this.edgeBeingEdited._restoreControlNodes(); - } - this.freezeSimulation = false; - this._redraw(); - }; + fromNow : function (withoutSuffix) { + return this.from(moment(), withoutSuffix); + }, - /** - * the function bound to the selection event. It checks if you want to connect a cluster and changes the description - * to walk the user through the process. - * - * @private - */ - exports._handleConnect = function(pointer) { - if (this._getSelectedNodeCount() == 0) { - var node = this._getNodeAt(pointer); + calendar : function (time) { + // We want to compare the start of today, vs this. + // Getting start-of-today depends on whether we're locat/utc/offset + // or not. + var now = time || moment(), + sod = makeAs(now, this).startOf('day'), + diff = this.diff(sod, 'days', true), + format = diff < -6 ? 'sameElse' : + diff < -1 ? 'lastWeek' : + diff < 0 ? 'lastDay' : + diff < 1 ? 'sameDay' : + diff < 2 ? 'nextDay' : + diff < 7 ? 'nextWeek' : 'sameElse'; + return this.format(this.localeData().calendar(format, this, moment(now))); + }, - if (node != null) { - if (node.clusterSize > 1) { - alert(this.constants.locales[this.constants.locale]['createEdgeError']) - } - else { - this._selectObject(node,false); - var supportNodes = this.sectors['support']['nodes']; + isLeapYear : function () { + return isLeapYear(this.year()); + }, - // create a node the temporary line can look at - supportNodes['targetNode'] = new Node({id:'targetNode'},{},{},this.constants); - var targetNode = supportNodes['targetNode']; - targetNode.x = node.x; - targetNode.y = node.y; + isDST : function () { + return (this.utcOffset() > this.clone().month(0).utcOffset() || + this.utcOffset() > this.clone().month(5).utcOffset()); + }, - // create a temporary edge - this.edges['connectionEdge'] = new Edge({id:"connectionEdge",from:node.id,to:targetNode.id}, this, this.constants); - var connectionEdge = this.edges['connectionEdge']; - connectionEdge.from = node; - connectionEdge.connected = true; - connectionEdge.options.smoothCurves = {enabled: true, - dynamic: false, - type: "continuous", - roundness: 0.5 - }; - connectionEdge.selected = true; - connectionEdge.to = targetNode; + day : function (input) { + var day = this._isUTC ? this._d.getUTCDay() : this._d.getDay(); + if (input != null) { + input = parseWeekday(input, this.localeData()); + return this.add(input - day, 'd'); + } else { + return day; + } + }, - this.cachedFunctions["_handleOnDrag"] = this._handleOnDrag; - this._handleOnDrag = function(event) { - var pointer = this._getPointer(event.gesture.center); - var connectionEdge = this.edges['connectionEdge']; - connectionEdge.to.x = this._XconvertDOMtoCanvas(pointer.x); - connectionEdge.to.y = this._YconvertDOMtoCanvas(pointer.y); - }; + month : makeAccessor('Month', true), - this.moving = true; - this.start(); - } - } - } - }; + startOf : function (units) { + units = normalizeUnits(units); + // the following switch intentionally omits break keywords + // to utilize falling through the cases. + switch (units) { + case 'year': + this.month(0); + /* falls through */ + case 'quarter': + case 'month': + this.date(1); + /* falls through */ + case 'week': + case 'isoWeek': + case 'day': + this.hours(0); + /* falls through */ + case 'hour': + this.minutes(0); + /* falls through */ + case 'minute': + this.seconds(0); + /* falls through */ + case 'second': + this.milliseconds(0); + /* falls through */ + } - exports._finishConnect = function(event) { - if (this._getSelectedNodeCount() == 1) { - var pointer = this._getPointer(event.gesture.center); - // restore the drag function - this._handleOnDrag = this.cachedFunctions["_handleOnDrag"]; - delete this.cachedFunctions["_handleOnDrag"]; + // weeks are a special case + if (units === 'week') { + this.weekday(0); + } else if (units === 'isoWeek') { + this.isoWeekday(1); + } - // remember the edge id - var connectFromId = this.edges['connectionEdge'].fromId; + // quarters are also special + if (units === 'quarter') { + this.month(Math.floor(this.month() / 3) * 3); + } - // remove the temporary nodes and edge - delete this.edges['connectionEdge']; - delete this.sectors['support']['nodes']['targetNode']; - delete this.sectors['support']['nodes']['targetViaNode']; + return this; + }, - var node = this._getNodeAt(pointer); - if (node != null) { - if (node.clusterSize > 1) { - alert(this.constants.locales[this.constants.locale]["createEdgeError"]) - } - else { - this._createEdge(connectFromId,node.id); - this._createManipulatorBar(); - } - } - this._unselectAll(); - } - }; + endOf: function (units) { + units = normalizeUnits(units); + if (units === undefined || units === 'millisecond') { + return this; + } + return this.startOf(units).add(1, (units === 'isoWeek' ? 'week' : units)).subtract(1, 'ms'); + }, + isAfter: function (input, units) { + var inputMs; + units = normalizeUnits(typeof units !== 'undefined' ? units : 'millisecond'); + if (units === 'millisecond') { + input = moment.isMoment(input) ? input : moment(input); + return +this > +input; + } else { + inputMs = moment.isMoment(input) ? +input : +moment(input); + return inputMs < +this.clone().startOf(units); + } + }, - /** - * Adds a node on the specified location - */ - exports._addNode = function() { - if (this._selectionIsEmpty() && this.editMode == true) { - var positionObject = this._pointerToPositionObject(this.pointerPosition); - var defaultData = {id:util.randomUUID(),x:positionObject.left,y:positionObject.top,label:"new",allowedToMoveX:true,allowedToMoveY:true}; - if (this.triggerFunctions.add) { - if (this.triggerFunctions.add.length == 2) { - var me = this; - this.triggerFunctions.add(defaultData, function(finalizedData) { - me.nodesData.add(finalizedData); - me._createManipulatorBar(); - me.moving = true; - me.start(); - }); - } - else { - throw new Error('The function for add does not support two arguments (data,callback)'); - this._createManipulatorBar(); - this.moving = true; - this.start(); - } - } - else { - this.nodesData.add(defaultData); - this._createManipulatorBar(); - this.moving = true; - this.start(); - } - } - }; + isBefore: function (input, units) { + var inputMs; + units = normalizeUnits(typeof units !== 'undefined' ? units : 'millisecond'); + if (units === 'millisecond') { + input = moment.isMoment(input) ? input : moment(input); + return +this < +input; + } else { + inputMs = moment.isMoment(input) ? +input : +moment(input); + return +this.clone().endOf(units) < inputMs; + } + }, + isBetween: function (from, to, units) { + return this.isAfter(from, units) && this.isBefore(to, units); + }, - /** - * connect two nodes with a new edge. - * - * @private - */ - exports._createEdge = function(sourceNodeId,targetNodeId) { - if (this.editMode == true) { - var defaultData = {from:sourceNodeId, to:targetNodeId}; - if (this.triggerFunctions.connect) { - if (this.triggerFunctions.connect.length == 2) { - var me = this; - this.triggerFunctions.connect(defaultData, function(finalizedData) { - me.edgesData.add(finalizedData); - me.moving = true; - me.start(); - }); - } - else { - throw new Error('The function for connect does not support two arguments (data,callback)'); - this.moving = true; - this.start(); - } - } - else { - this.edgesData.add(defaultData); - this.moving = true; - this.start(); - } - } - }; + isSame: function (input, units) { + var inputMs; + units = normalizeUnits(units || 'millisecond'); + if (units === 'millisecond') { + input = moment.isMoment(input) ? input : moment(input); + return +this === +input; + } else { + inputMs = +moment(input); + return +(this.clone().startOf(units)) <= inputMs && inputMs <= +(this.clone().endOf(units)); + } + }, - /** - * connect two nodes with a new edge. - * - * @private - */ - exports._editEdge = function(sourceNodeId,targetNodeId) { - if (this.editMode == true) { - var defaultData = {id: this.edgeBeingEdited.id, from:sourceNodeId, to:targetNodeId}; - if (this.triggerFunctions.editEdge) { - if (this.triggerFunctions.editEdge.length == 2) { - var me = this; - this.triggerFunctions.editEdge(defaultData, function(finalizedData) { - me.edgesData.update(finalizedData); - me.moving = true; - me.start(); - }); - } - else { - throw new Error('The function for edit does not support two arguments (data, callback)'); - this.moving = true; - this.start(); - } - } - else { - this.edgesData.update(defaultData); - this.moving = true; - this.start(); - } - } - }; + min: deprecate( + 'moment().min is deprecated, use moment.min instead. https://github.com/moment/moment/issues/1548', + function (other) { + other = moment.apply(null, arguments); + return other < this ? this : other; + } + ), - /** - * Create the toolbar to edit the selected node. The label and the color can be changed. Other colors are derived from the chosen color. - * - * @private - */ - exports._editNode = function() { - if (this.triggerFunctions.edit && this.editMode == true) { - var node = this._getSelectedNode(); - var data = {id:node.id, - label: node.label, - group: node.options.group, - shape: node.options.shape, - color: { - background:node.options.color.background, - border:node.options.color.border, - highlight: { - background:node.options.color.highlight.background, - border:node.options.color.highlight.border - } - }}; - if (this.triggerFunctions.edit.length == 2) { - var me = this; - this.triggerFunctions.edit(data, function (finalizedData) { - me.nodesData.update(finalizedData); - me._createManipulatorBar(); - me.moving = true; - me.start(); - }); - } - else { - throw new Error('The function for edit does not support two arguments (data, callback)'); - } - } - else { - throw new Error('No edit function has been bound to this button'); - } - }; + max: deprecate( + 'moment().max is deprecated, use moment.max instead. https://github.com/moment/moment/issues/1548', + function (other) { + other = moment.apply(null, arguments); + return other > this ? this : other; + } + ), + zone : deprecate( + 'moment().zone is deprecated, use moment().utcOffset instead. ' + + 'https://github.com/moment/moment/issues/1779', + function (input, keepLocalTime) { + if (input != null) { + if (typeof input !== 'string') { + input = -input; + } + this.utcOffset(input, keepLocalTime); + return this; + } else { + return -this.utcOffset(); + } + } + ), - /** - * delete everything in the selection - * - * @private - */ - exports._deleteSelected = function() { - if (!this._selectionIsEmpty() && this.editMode == true) { - if (!this._clusterInSelection()) { - var selectedNodes = this.getSelectedNodes(); - var selectedEdges = this.getSelectedEdges(); - if (this.triggerFunctions.del) { - var me = this; - var data = {nodes: selectedNodes, edges: selectedEdges}; - if (this.triggerFunctions.del.length == 2) { - this.triggerFunctions.del(data, function (finalizedData) { - me.edgesData.remove(finalizedData.edges); - me.nodesData.remove(finalizedData.nodes); - me._unselectAll(); - me.moving = true; - me.start(); - }); - } - else { - throw new Error('The function for delete does not support two arguments (data, callback)') - } - } - else { - this.edgesData.remove(selectedEdges); - this.nodesData.remove(selectedNodes); - this._unselectAll(); - this.moving = true; - this.start(); - } - } - else { - alert(this.constants.locales[this.constants.locale]["deleteClusterError"]); - } - } - }; + // keepLocalTime = true means only change the timezone, without + // affecting the local hour. So 5:31:26 +0300 --[utcOffset(2, true)]--> + // 5:31:26 +0200 It is possible that 5:31:26 doesn't exist with offset + // +0200, so we adjust the time as needed, to be valid. + // + // Keeping the time actually adds/subtracts (one hour) + // from the actual represented time. That is why we call updateOffset + // a second time. In case it wants us to change the offset again + // _changeInProgress == true case, then we have to adjust, because + // there is no such time in the given timezone. + utcOffset : function (input, keepLocalTime) { + var offset = this._offset || 0, + localAdjust; + if (input != null) { + if (typeof input === 'string') { + input = utcOffsetFromString(input); + } + if (Math.abs(input) < 16) { + input = input * 60; + } + if (!this._isUTC && keepLocalTime) { + localAdjust = this._dateUtcOffset(); + } + this._offset = input; + this._isUTC = true; + if (localAdjust != null) { + this.add(localAdjust, 'm'); + } + if (offset !== input) { + if (!keepLocalTime || this._changeInProgress) { + addOrSubtractDurationFromMoment(this, + moment.duration(input - offset, 'm'), 1, false); + } else if (!this._changeInProgress) { + this._changeInProgress = true; + moment.updateOffset(this, true); + this._changeInProgress = null; + } + } + return this; + } else { + return this._isUTC ? offset : this._dateUtcOffset(); + } + }, -/***/ }, -/* 64 */ -/***/ function(module, exports, __webpack_require__) { + isLocal : function () { + return !this._isUTC; + }, - var util = __webpack_require__(1); - var Hammer = __webpack_require__(45); + isUtcOffset : function () { + return this._isUTC; + }, - exports._cleanNavigation = function() { - // clean hammer bindings - if (this.navigationHammers.existing.length != 0) { - for (var i = 0; i < this.navigationHammers.existing.length; i++) { - this.navigationHammers.existing[i].dispose(); - } - this.navigationHammers.existing = []; - } + isUtc : function () { + return this._isUTC && this._offset === 0; + }, - this._navigationReleaseOverload = function () {}; + zoneAbbr : function () { + return this._isUTC ? 'UTC' : ''; + }, - // clean up previous navigation items - if (this.navigationDivs && this.navigationDivs['wrapper'] && this.navigationDivs['wrapper'].parentNode) { - this.navigationDivs['wrapper'].parentNode.removeChild(this.navigationDivs['wrapper']); - } - }; + zoneName : function () { + return this._isUTC ? 'Coordinated Universal Time' : ''; + }, - /** - * 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 - */ - exports._loadNavigationElements = function() { - this._cleanNavigation(); + parseZone : function () { + if (this._tzm) { + this.utcOffset(this._tzm); + } else if (typeof this._i === 'string') { + this.utcOffset(utcOffsetFromString(this._i)); + } + return this; + }, - this.navigationDivs = {}; - var navigationDivs = ['up','down','left','right','zoomIn','zoomOut','zoomExtends']; - var navigationDivActions = ['_moveUp','_moveDown','_moveLeft','_moveRight','_zoomIn','_zoomOut','_zoomExtent']; + hasAlignedHourOffset : function (input) { + if (!input) { + input = 0; + } + else { + input = moment(input).utcOffset(); + } - this.navigationDivs['wrapper'] = document.createElement('div'); - this.frame.appendChild(this.navigationDivs['wrapper']); + return (this.utcOffset() - input) % 60 === 0; + }, - for (var i = 0; i < navigationDivs.length; i++) { - this.navigationDivs[navigationDivs[i]] = document.createElement('div'); - this.navigationDivs[navigationDivs[i]].className = 'network-navigation ' + navigationDivs[i]; - this.navigationDivs['wrapper'].appendChild(this.navigationDivs[navigationDivs[i]]); + daysInMonth : function () { + return daysInMonth(this.year(), this.month()); + }, - var hammer = Hammer(this.navigationDivs[navigationDivs[i]], {prevent_default: true}); - hammer.on('touch', this[navigationDivActions[i]].bind(this)); - this.navigationHammers._new.push(hammer); - } + dayOfYear : function (input) { + var dayOfYear = round((moment(this).startOf('day') - moment(this).startOf('year')) / 864e5) + 1; + return input == null ? dayOfYear : this.add((input - dayOfYear), 'd'); + }, - this._navigationReleaseOverload = this._stopMovement; + quarter : function (input) { + return input == null ? Math.ceil((this.month() + 1) / 3) : this.month((input - 1) * 3 + this.month() % 3); + }, - this.navigationHammers.existing = this.navigationHammers._new; - }; + weekYear : function (input) { + var year = weekOfYear(this, this.localeData()._week.dow, this.localeData()._week.doy).year; + return input == null ? year : this.add((input - year), 'y'); + }, + isoWeekYear : function (input) { + var year = weekOfYear(this, 1, 4).year; + return input == null ? year : this.add((input - year), 'y'); + }, - /** - * this stops all movement induced by the navigation buttons - * - * @private - */ - exports._zoomExtent = function(event) { - this.zoomExtent({duration:700}); - event.stopPropagation(); - }; + week : function (input) { + var week = this.localeData().week(this); + return input == null ? week : this.add((input - week) * 7, 'd'); + }, - /** - * this stops all movement induced by the navigation buttons - * - * @private - */ - exports._stopMovement = function() { - this._xStopMoving(); - this._yStopMoving(); - this._stopZoom(); - }; + isoWeek : function (input) { + var week = weekOfYear(this, 1, 4).week; + return input == null ? week : this.add((input - week) * 7, 'd'); + }, + weekday : function (input) { + var weekday = (this.day() + 7 - this.localeData()._week.dow) % 7; + return input == null ? weekday : this.add(input - weekday, 'd'); + }, - /** - * move the screen up - * By using the increments, instead of adding a fixed number to the translation, we keep fluent and - * instant movement. The onKeypress event triggers immediately, then pauses, then triggers frequently - * To avoid this behaviour, we do the translation in the start loop. - * - * @private - */ - exports._moveUp = function(event) { - this.yIncrement = this.constants.keyboard.speed.y; - this.start(); // if there is no node movement, the calculation wont be done - event.preventDefault(); - }; + isoWeekday : function (input) { + // behaves the same as moment#day except + // as a getter, returns 7 instead of 0 (1-7 range instead of 0-6) + // as a setter, sunday should belong to the previous week. + return input == null ? this.day() || 7 : this.day(this.day() % 7 ? input : input - 7); + }, + isoWeeksInYear : function () { + return weeksInYear(this.year(), 1, 4); + }, - /** - * move the screen down - * @private - */ - exports._moveDown = function(event) { - this.yIncrement = -this.constants.keyboard.speed.y; - this.start(); // if there is no node movement, the calculation wont be done - event.preventDefault(); - }; + weeksInYear : function () { + var weekInfo = this.localeData()._week; + return weeksInYear(this.year(), weekInfo.dow, weekInfo.doy); + }, + get : function (units) { + units = normalizeUnits(units); + return this[units](); + }, - /** - * move the screen left - * @private - */ - exports._moveLeft = function(event) { - this.xIncrement = this.constants.keyboard.speed.x; - this.start(); // if there is no node movement, the calculation wont be done - event.preventDefault(); - }; + set : function (units, value) { + var unit; + if (typeof units === 'object') { + for (unit in units) { + this.set(unit, units[unit]); + } + } + else { + units = normalizeUnits(units); + if (typeof this[units] === 'function') { + this[units](value); + } + } + return this; + }, + + // If passed a locale key, it will set the locale for this + // instance. Otherwise, it will return the locale configuration + // variables for this instance. + locale : function (key) { + var newLocaleData; + if (key === undefined) { + return this._locale._abbr; + } else { + newLocaleData = moment.localeData(key); + if (newLocaleData != null) { + this._locale = newLocaleData; + } + return this; + } + }, - /** - * move the screen right - * @private - */ - exports._moveRight = function(event) { - this.xIncrement = -this.constants.keyboard.speed.y; - this.start(); // if there is no node movement, the calculation wont be done - event.preventDefault(); - }; + lang : deprecate( + 'moment().lang() is deprecated. Instead, use moment().localeData() to get the language configuration. Use moment().locale() to change languages.', + function (key) { + if (key === undefined) { + return this.localeData(); + } else { + return this.locale(key); + } + } + ), + localeData : function () { + return this._locale; + }, - /** - * Zoom in, using the same method as the movement. - * @private - */ - exports._zoomIn = function(event) { - this.zoomIncrement = this.constants.keyboard.speed.zoom; - this.start(); // if there is no node movement, the calculation wont be done - event.preventDefault(); - }; + _dateUtcOffset : function () { + // On Firefox.24 Date#getTimezoneOffset returns a floating point. + // https://github.com/moment/moment/pull/1871 + return -Math.round(this._d.getTimezoneOffset() / 15) * 15; + } + }); - /** - * Zoom out - * @private - */ - exports._zoomOut = function(event) { - this.zoomIncrement = -this.constants.keyboard.speed.zoom; - this.start(); // if there is no node movement, the calculation wont be done - event.preventDefault(); - }; + function rawMonthSetter(mom, value) { + var dayOfMonth; + // TODO: Move this out of here! + if (typeof value === 'string') { + value = mom.localeData().monthsParse(value); + // TODO: Another silent failure? + if (typeof value !== 'number') { + return mom; + } + } - /** - * Stop zooming and unhighlight the zoom controls - * @private - */ - exports._stopZoom = function(event) { - this.zoomIncrement = 0; - event && event.preventDefault(); - }; + dayOfMonth = Math.min(mom.date(), + daysInMonth(mom.year(), value)); + mom._d['set' + (mom._isUTC ? 'UTC' : '') + 'Month'](value, dayOfMonth); + return mom; + } + function rawGetter(mom, unit) { + return mom._d['get' + (mom._isUTC ? 'UTC' : '') + unit](); + } - /** - * Stop moving in the Y direction and unHighlight the up and down - * @private - */ - exports._yStopMoving = function(event) { - this.yIncrement = 0; - event && event.preventDefault(); - }; + function rawSetter(mom, unit, value) { + if (unit === 'Month') { + return rawMonthSetter(mom, value); + } else { + return mom._d['set' + (mom._isUTC ? 'UTC' : '') + unit](value); + } + } + function makeAccessor(unit, keepTime) { + return function (value) { + if (value != null) { + rawSetter(this, unit, value); + moment.updateOffset(this, keepTime); + return this; + } else { + return rawGetter(this, unit); + } + }; + } - /** - * Stop moving in the X direction and unHighlight left and right. - * @private - */ - exports._xStopMoving = function(event) { - this.xIncrement = 0; - event && event.preventDefault(); - }; + moment.fn.millisecond = moment.fn.milliseconds = makeAccessor('Milliseconds', false); + moment.fn.second = moment.fn.seconds = makeAccessor('Seconds', false); + moment.fn.minute = moment.fn.minutes = makeAccessor('Minutes', false); + // Setting the hour should keep the time, because the user explicitly + // specified which hour he wants. So trying to maintain the same hour (in + // a new timezone) makes sense. Adding/subtracting hours does not follow + // this rule. + moment.fn.hour = moment.fn.hours = makeAccessor('Hours', true); + // moment.fn.month is defined separately + moment.fn.date = makeAccessor('Date', true); + moment.fn.dates = deprecate('dates accessor is deprecated. Use date instead.', makeAccessor('Date', true)); + moment.fn.year = makeAccessor('FullYear', true); + moment.fn.years = deprecate('years accessor is deprecated. Use year instead.', makeAccessor('FullYear', true)); + // add plural methods + moment.fn.days = moment.fn.day; + moment.fn.months = moment.fn.month; + moment.fn.weeks = moment.fn.week; + moment.fn.isoWeeks = moment.fn.isoWeek; + moment.fn.quarters = moment.fn.quarter; -/***/ }, -/* 65 */ -/***/ function(module, exports, __webpack_require__) { + // add aliased format methods + moment.fn.toJSON = moment.fn.toISOString; - exports._resetLevels = function() { - for (var nodeId in this.nodes) { - if (this.nodes.hasOwnProperty(nodeId)) { - var node = this.nodes[nodeId]; - if (node.preassignedLevel == false) { - node.level = -1; - node.hierarchyEnumerated = false; - } - } - } - }; + // alias isUtc for dev-friendliness + moment.fn.isUTC = moment.fn.isUtc; - /** - * This is the main function to layout the nodes in a hierarchical way. - * It checks if the node details are supplied correctly - * - * @private - */ - exports._setupHierarchicalLayout = function() { - if (this.constants.hierarchicalLayout.enabled == true && this.nodeIndices.length > 0) { - // get the size of the largest hubs and check if the user has defined a level for a node. - var hubsize = 0; - var node, nodeId; - var definedLevel = false; - var undefinedLevel = false; + /************************************ + Duration Prototype + ************************************/ - for (nodeId in this.nodes) { - if (this.nodes.hasOwnProperty(nodeId)) { - node = this.nodes[nodeId]; - if (node.level != -1) { - definedLevel = true; - } - else { - undefinedLevel = true; - } - if (hubsize < node.edges.length) { - hubsize = node.edges.length; - } - } + + function daysToYears (days) { + // 400 years have 146097 days (taking into account leap year rules) + return days * 400 / 146097; } - // if the user defined some levels but not all, alert and run without hierarchical layout - if (undefinedLevel == true && definedLevel == true) { - throw new Error("To use the hierarchical layout, nodes require either no predefined levels or levels have to be defined for all nodes."); - this.zoomExtent(undefined,true,this.constants.clustering.enabled); - if (!this.constants.clustering.enabled) { - this.start(); - } + function yearsToDays (years) { + // years * 365 + absRound(years / 4) - + // absRound(years / 100) + absRound(years / 400); + return years * 146097 / 400; } - else { - // setup the system to use hierarchical method. - this._changeConstants(); - // define levels if undefined by the users. Based on hubsize - if (undefinedLevel == true) { - if (this.constants.hierarchicalLayout.layout == "hubsize") { - this._determineLevels(hubsize); - } - else { - this._determineLevelsDirected(false); - } + extend(moment.duration.fn = Duration.prototype, { + + _bubble : function () { + var milliseconds = this._milliseconds, + days = this._days, + months = this._months, + data = this._data, + seconds, minutes, hours, years = 0; + + // The following code bubbles up values, see the tests for + // examples of what that means. + data.milliseconds = milliseconds % 1000; + + seconds = absRound(milliseconds / 1000); + data.seconds = seconds % 60; + + minutes = absRound(seconds / 60); + data.minutes = minutes % 60; + + hours = absRound(minutes / 60); + data.hours = hours % 24; + + days += absRound(hours / 24); - } - // check the distribution of the nodes per level. - var distribution = this._getDistribution(); + // Accurately convert days to years, assume start from year 0. + years = absRound(daysToYears(days)); + days -= absRound(yearsToDays(years)); - // place the nodes on the canvas. This also stablilizes the system. - this._placeNodesByHierarchy(distribution); + // 30 days to a month + // TODO (iskren): Use anchor date (like 1st Jan) to compute this. + months += absRound(days / 30); + days %= 30; - // start the simulation. - this.start(); - } - } - }; + // 12 months -> 1 year + years += absRound(months / 12); + months %= 12; + data.days = days; + data.months = months; + data.years = years; + }, - /** - * This function places the nodes on the canvas based on the hierarchial distribution. - * - * @param {Object} distribution | obtained by the function this._getDistribution() - * @private - */ - exports._placeNodesByHierarchy = function(distribution) { - var nodeId, node; + abs : function () { + this._milliseconds = Math.abs(this._milliseconds); + this._days = Math.abs(this._days); + this._months = Math.abs(this._months); - // start placing all the level 0 nodes first. Then recursively position their branches. - for (var level in distribution) { - if (distribution.hasOwnProperty(level)) { + this._data.milliseconds = Math.abs(this._data.milliseconds); + this._data.seconds = Math.abs(this._data.seconds); + this._data.minutes = Math.abs(this._data.minutes); + this._data.hours = Math.abs(this._data.hours); + this._data.months = Math.abs(this._data.months); + this._data.years = Math.abs(this._data.years); - for (nodeId in distribution[level].nodes) { - if (distribution[level].nodes.hasOwnProperty(nodeId)) { - node = distribution[level].nodes[nodeId]; - if (this.constants.hierarchicalLayout.direction == "UD" || this.constants.hierarchicalLayout.direction == "DU") { - if (node.xFixed) { - node.x = distribution[level].minPos; - node.xFixed = false; + return this; + }, - distribution[level].minPos += distribution[level].nodeSpacing; - } - } - else { - if (node.yFixed) { - node.y = distribution[level].minPos; - node.yFixed = false; + weeks : function () { + return absRound(this.days() / 7); + }, - distribution[level].minPos += distribution[level].nodeSpacing; - } - } - this._placeBranchNodes(node.edges,node.id,distribution,node.level); - } - } - } - } + valueOf : function () { + return this._milliseconds + + this._days * 864e5 + + (this._months % 12) * 2592e6 + + toInt(this._months / 12) * 31536e6; + }, - // stabilize the system after positioning. This function calls zoomExtent. - this._stabilize(); - }; + humanize : function (withSuffix) { + var output = relativeTime(this, !withSuffix, this.localeData()); + if (withSuffix) { + output = this.localeData().pastFuture(+this, output); + } - /** - * This function get the distribution of levels based on hubsize - * - * @returns {Object} - * @private - */ - exports._getDistribution = function() { - var distribution = {}; - var nodeId, node, level; + return this.localeData().postformat(output); + }, - // we fix Y because the hierarchy is vertical, we fix X so we do not give a node an x position for a second time. - // the fix of X is removed after the x value has been set. - for (nodeId in this.nodes) { - if (this.nodes.hasOwnProperty(nodeId)) { - node = this.nodes[nodeId]; - node.xFixed = true; - node.yFixed = true; - if (this.constants.hierarchicalLayout.direction == "UD" || this.constants.hierarchicalLayout.direction == "DU") { - node.y = this.constants.hierarchicalLayout.levelSeparation*node.level; - } - else { - node.x = this.constants.hierarchicalLayout.levelSeparation*node.level; - } - if (distribution[node.level] === undefined) { - distribution[node.level] = {amount: 0, nodes: {}, minPos:0, nodeSpacing:0}; - } - distribution[node.level].amount += 1; - distribution[node.level].nodes[nodeId] = node; - } - } + add : function (input, val) { + // supports only 2.0-style add(1, 's') or add(moment) + var dur = moment.duration(input, val); - // determine the largest amount of nodes of all levels - var maxCount = 0; - for (level in distribution) { - if (distribution.hasOwnProperty(level)) { - if (maxCount < distribution[level].amount) { - maxCount = distribution[level].amount; - } - } - } + this._milliseconds += dur._milliseconds; + this._days += dur._days; + this._months += dur._months; - // set the initial position and spacing of each nodes accordingly - for (level in distribution) { - if (distribution.hasOwnProperty(level)) { - distribution[level].nodeSpacing = (maxCount + 1) * this.constants.hierarchicalLayout.nodeSpacing; - distribution[level].nodeSpacing /= (distribution[level].amount + 1); - distribution[level].minPos = distribution[level].nodeSpacing - (0.5 * (distribution[level].amount + 1) * distribution[level].nodeSpacing); - } - } + this._bubble(); - return distribution; - }; + return this; + }, + subtract : function (input, val) { + var dur = moment.duration(input, val); - /** - * this function allocates nodes in levels based on the recursive branching from the largest hubs. - * - * @param hubsize - * @private - */ - exports._determineLevels = function(hubsize) { - var nodeId, node; + this._milliseconds -= dur._milliseconds; + this._days -= dur._days; + this._months -= dur._months; - // determine hubs - for (nodeId in this.nodes) { - if (this.nodes.hasOwnProperty(nodeId)) { - node = this.nodes[nodeId]; - if (node.edges.length == hubsize) { - node.level = 0; - } - } - } + this._bubble(); - // branch from hubs - for (nodeId in this.nodes) { - if (this.nodes.hasOwnProperty(nodeId)) { - node = this.nodes[nodeId]; - if (node.level == 0) { - this._setLevel(1,node.edges,node.id); - } - } - } - }; + return this; + }, + get : function (units) { + units = normalizeUnits(units); + return this[units.toLowerCase() + 's'](); + }, + as : function (units) { + var days, months; + units = normalizeUnits(units); - /** - * this function allocates nodes in levels based on the direction of the edges - * - * @param hubsize - * @private - */ - exports._determineLevelsDirected = function() { - var nodeId, node, firstNode; - var minLevel = 10000; + if (units === 'month' || units === 'year') { + days = this._days + this._milliseconds / 864e5; + months = this._months + daysToYears(days) * 12; + return units === 'month' ? months : months / 12; + } else { + // handle milliseconds separately because of floating point math errors (issue #1867) + days = this._days + Math.round(yearsToDays(this._months / 12)); + switch (units) { + case 'week': return days / 7 + this._milliseconds / 6048e5; + case 'day': return days + this._milliseconds / 864e5; + case 'hour': return days * 24 + this._milliseconds / 36e5; + case 'minute': return days * 24 * 60 + this._milliseconds / 6e4; + case 'second': return days * 24 * 60 * 60 + this._milliseconds / 1000; + // Math.floor prevents floating point math errors here + case 'millisecond': return Math.floor(days * 24 * 60 * 60 * 1000) + this._milliseconds; + default: throw new Error('Unknown unit ' + units); + } + } + }, - // set first node to source - firstNode = this.nodes[this.nodeIndices[0]]; - firstNode.level = minLevel; - this._setLevelDirected(minLevel,firstNode.edges,firstNode.id); + lang : moment.fn.lang, + locale : moment.fn.locale, - // get the minimum level - for (nodeId in this.nodes) { - if (this.nodes.hasOwnProperty(nodeId)) { - node = this.nodes[nodeId]; - minLevel = node.level < minLevel ? node.level : minLevel; - } - } + toIsoString : deprecate( + 'toIsoString() is deprecated. Please use toISOString() instead ' + + '(notice the capitals)', + function () { + return this.toISOString(); + } + ), - // subtract the minimum from the set so we have a range starting from 0 - for (nodeId in this.nodes) { - if (this.nodes.hasOwnProperty(nodeId)) { - node = this.nodes[nodeId]; - node.level -= minLevel; - } - } - }; + toISOString : function () { + // inspired by https://github.com/dordille/moment-isoduration/blob/master/moment.isoduration.js + var years = Math.abs(this.years()), + months = Math.abs(this.months()), + days = Math.abs(this.days()), + hours = Math.abs(this.hours()), + minutes = Math.abs(this.minutes()), + seconds = Math.abs(this.seconds() + this.milliseconds() / 1000); + if (!this.asSeconds()) { + // this is the same as C#'s (Noda) and python (isodate)... + // but not other JS (goog.date) + return 'P0D'; + } - /** - * Since hierarchical layout does not support: - * - smooth curves (based on the physics), - * - clustering (based on dynamic node counts) - * - * We disable both features so there will be no problems. - * - * @private - */ - exports._changeConstants = function() { - this.constants.clustering.enabled = false; - this.constants.physics.barnesHut.enabled = false; - this.constants.physics.hierarchicalRepulsion.enabled = true; - this._loadSelectedForceSolver(); - if (this.constants.smoothCurves.enabled == true) { - this.constants.smoothCurves.dynamic = false; - } - this._configureSmoothCurves(); + return (this.asSeconds() < 0 ? '-' : '') + + 'P' + + (years ? years + 'Y' : '') + + (months ? months + 'M' : '') + + (days ? days + 'D' : '') + + ((hours || minutes || seconds) ? 'T' : '') + + (hours ? hours + 'H' : '') + + (minutes ? minutes + 'M' : '') + + (seconds ? seconds + 'S' : ''); + }, - var config = this.constants.hierarchicalLayout; - config.levelSeparation = Math.abs(config.levelSeparation); - if (config.direction == "RL" || config.direction == "DU") { - config.levelSeparation *= -1; - } + localeData : function () { + return this._locale; + }, - if (config.direction == "RL" || config.direction == "LR") { - if (this.constants.smoothCurves.enabled == true) { - this.constants.smoothCurves.type = "vertical"; - } - } - else { - if (this.constants.smoothCurves.enabled == true) { - this.constants.smoothCurves.type = "horizontal"; - } - } - }; + toJSON : function () { + return this.toISOString(); + } + }); + moment.duration.fn.toString = moment.duration.fn.toISOString; - /** - * This is a recursively called function to enumerate the branches from the largest hubs and place the nodes - * on a X position that ensures there will be no overlap. - * - * @param edges - * @param parentId - * @param distribution - * @param parentLevel - * @private - */ - exports._placeBranchNodes = function(edges, parentId, distribution, parentLevel) { - for (var i = 0; i < edges.length; i++) { - var childNode = null; - if (edges[i].toId == parentId) { - childNode = edges[i].from; - } - else { - childNode = edges[i].to; + function makeDurationGetter(name) { + moment.duration.fn[name] = function () { + return this._data[name]; + }; } - // if a node is conneceted to another node on the same level (or higher (means lower level))!, this is not handled here. - var nodeMoved = false; - if (this.constants.hierarchicalLayout.direction == "UD" || this.constants.hierarchicalLayout.direction == "DU") { - if (childNode.xFixed && childNode.level > parentLevel) { - childNode.xFixed = false; - childNode.x = distribution[childNode.level].minPos; - nodeMoved = true; - } - } - else { - if (childNode.yFixed && childNode.level > parentLevel) { - childNode.yFixed = false; - childNode.y = distribution[childNode.level].minPos; - nodeMoved = true; - } + for (i in unitMillisecondFactors) { + if (hasOwnProp(unitMillisecondFactors, i)) { + makeDurationGetter(i.toLowerCase()); + } } - if (nodeMoved == true) { - distribution[childNode.level].minPos += distribution[childNode.level].nodeSpacing; - if (childNode.edges.length > 1) { - this._placeBranchNodes(childNode.edges,childNode.id,distribution,childNode.level); - } - } - } - }; + moment.duration.fn.asMilliseconds = function () { + return this.as('ms'); + }; + moment.duration.fn.asSeconds = function () { + return this.as('s'); + }; + moment.duration.fn.asMinutes = function () { + return this.as('m'); + }; + moment.duration.fn.asHours = function () { + return this.as('h'); + }; + moment.duration.fn.asDays = function () { + return this.as('d'); + }; + moment.duration.fn.asWeeks = function () { + return this.as('weeks'); + }; + moment.duration.fn.asMonths = function () { + return this.as('M'); + }; + moment.duration.fn.asYears = function () { + return this.as('y'); + }; + /************************************ + Default Locale + ************************************/ - /** - * this function is called recursively to enumerate the barnches of the largest hubs and give each node a level. - * - * @param level - * @param edges - * @param parentId - * @private - */ - exports._setLevel = function(level, edges, parentId) { - for (var i = 0; i < edges.length; i++) { - var childNode = null; - if (edges[i].toId == parentId) { - childNode = edges[i].from; - } - else { - childNode = edges[i].to; - } - if (childNode.level == -1 || childNode.level > level) { - childNode.level = level; - if (childNode.edges.length > 1) { - this._setLevel(level+1, childNode.edges, childNode.id); - } - } - } - }; + // Set default locale, other locale will inherit from English. + moment.locale('en', { + ordinalParse: /\d{1,2}(th|st|nd|rd)/, + ordinal : function (number) { + var b = number % 10, + output = (toInt(number % 100 / 10) === 1) ? 'th' : + (b === 1) ? 'st' : + (b === 2) ? 'nd' : + (b === 3) ? 'rd' : 'th'; + return number + output; + } + }); - /** - * this function is called recursively to enumerate the branched of the first node and give each node a level based on edge direction - * - * @param level - * @param edges - * @param parentId - * @private - */ - exports._setLevelDirected = function(level, edges, parentId) { - this.nodes[parentId].hierarchyEnumerated = true; - var childNode, direction; - for (var i = 0; i < edges.length; i++) { - direction = 1; - if (edges[i].toId == parentId) { - childNode = edges[i].from; - direction = -1; - } - else { - childNode = edges[i].to; - } - if (childNode.level == -1) { - childNode.level = level + direction; - } - } + /* EMBED_LOCALES */ - for (var i = 0; i < edges.length; i++) { - if (edges[i].toId == parentId) {childNode = edges[i].from;} - else {childNode = edges[i].to;} + /************************************ + Exposing Moment + ************************************/ - if (childNode.edges.length > 1 && childNode.hierarchyEnumerated === false) { - this._setLevelDirected(childNode.level, childNode.edges, childNode.id); + function makeGlobal(shouldDeprecate) { + /*global ender:false */ + if (typeof ender !== 'undefined') { + return; + } + oldGlobalMoment = globalScope.moment; + if (shouldDeprecate) { + globalScope.moment = deprecate( + 'Accessing Moment through the global scope is ' + + 'deprecated, and will be removed in an upcoming ' + + 'release.', + moment); + } else { + globalScope.moment = moment; + } } - } - }; + // CommonJS module is defined + if (hasModule) { + module.exports = moment; + } else if (true) { + !(__WEBPACK_AMD_DEFINE_RESULT__ = function (require, exports, module) { + if (module.config && module.config() && module.config().noGlobal === true) { + // release the global variable + globalScope.moment = oldGlobalMoment; + } - /** - * Unfix nodes - * - * @private - */ - exports._restoreNodes = function() { - for (var nodeId in this.nodes) { - if (this.nodes.hasOwnProperty(nodeId)) { - this.nodes[nodeId].xFixed = false; - this.nodes[nodeId].yFixed = false; + return moment; + }.call(exports, __webpack_require__, exports, module), __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__)); + makeGlobal(true); + } else { + makeGlobal(); } - } - }; - + }).call(this); + + /* WEBPACK VAR INJECTION */}.call(exports, (function() { return this; }()), __webpack_require__(71)(module))) /***/ }, /* 66 */ diff --git a/dist/vis.map b/dist/vis.map index 1a6ef774..89ce19b4 100644 --- a/dist/vis.map +++ b/dist/vis.map @@ -1 +1 @@ -{"version":3,"file":"vis.map","sources":["./dist/vis.js"],"names":["root","factory","exports","module","define","amd","this","modules","__webpack_require__","moduleId","installedModules","id","loaded","call","m","c","p","util","DOMutil","DataSet","DataView","Queue","Graph3d","graph3d","Camera","Filter","Point2d","Point3d","Slider","StepNumber","Timeline","Graph2d","timeline","DateUtil","DataStep","Range","stack","TimeStep","components","items","Item","BackgroundItem","BoxItem","PointItem","RangeItem","Component","CurrentTime","CustomTime","DataAxis","GraphGroup","Group","BackgroundGroup","ItemSet","Legend","LineGraph","TimeAxis","Network","network","Edge","Groups","Images","Node","Popup","dotparser","gephiParser","Graph","Error","moment","hammer","isNumber","object","Number","isString","String","isDate","Date","match","ASPDateRegex","exec","isNaN","parse","isDataTable","google","visualization","DataTable","randomUUID","S4","Math","floor","random","toString","extend","a","i","len","arguments","length","other","prop","hasOwnProperty","selectiveExtend","props","Array","isArray","selectiveDeepExtend","b","TypeError","constructor","Object","undefined","deepExtend","selectiveNotDeepExtend","indexOf","equalArray","convert","type","Boolean","valueOf","isMoment","toDate","getType","toISOString","value","getAbsoluteLeft","elem","getBoundingClientRect","left","window","pageXOffset","getAbsoluteTop","top","pageYOffset","addClassName","className","classes","split","push","join","removeClassName","index","splice","forEach","callback","toArray","array","updateProperty","key","addEventListener","element","action","listener","useCapture","navigator","userAgent","attachEvent","removeEventListener","detachEvent","preventDefault","event","returnValue","getTarget","target","srcElement","nodeType","parentNode","option","asBoolean","defaultValue","asNumber","asString","asSize","asElement","hexToRGB","hex","shorthandRegex","replace","r","g","result","parseInt","RGBToHex","red","green","blue","slice","parseColor","color","isValidRGB","rgb","substr","isValidHex","hsv","hexToHSV","lighterColorHSV","h","s","v","min","darkerColorHSV","darkerColorHex","HSVToHex","lighterColorHex","background","border","highlight","hover","RGBToHSV","minRGB","maxRGB","max","d","hue","saturation","cssUtil","cssText","styles","style","trim","parts","keys","map","addCssText","currentStyles","newStyles","removeCssText","removeStyles","HSVToRGB","f","q","t","isOk","test","selectiveBridgeObject","fields","referenceObject","objectTo","create","bridgeObject","mergeOptions","mergeTarget","options","enabled","binarySearchCustom","orderedItems","searchFunction","field","field2","maxIterations","iteration","low","high","middle","item","searchResult","binarySearchValue","sidePreference","prevValue","nextValue","easeInOutQuad","start","end","duration","change","easingFunctions","linear","easeInQuad","easeOutQuad","easeInCubic","easeOutCubic","easeInOutCubic","easeInQuart","easeOutQuart","easeInOutQuart","easeInQuint","easeOutQuint","easeInOutQuint","prepareElements","JSONcontainer","elementType","redundant","used","cleanupElements","removeChild","getSVGElement","svgContainer","shift","document","createElementNS","appendChild","getDOMElement","DOMContainer","insertBefore","createElement","drawPoint","x","y","group","point","drawPoints","setAttributeNS","size","drawBar","width","height","rect","data","_options","_data","_fieldId","fieldId","_type","_subscribers","add","setOptions","prototype","queue","_queue","destroy","on","subscribers","subscribe","off","filter","unsubscribe","_trigger","params","senderId","concat","subscriber","addedIds","me","_addItem","columns","_getColumnNames","row","rows","getNumberOfRows","col","cols","getValue","update","updatedIds","updatedData","addOrUpdate","_updateItem","get","ids","firstType","returnType","allowedValues","itemId","_getItem","order","_sort","_filterFields","_appendRow","getIds","getDataSet","mappedItems","filteredItem","name","sort","av","bv","remove","removedId","removedIds","_remove","clear","maxField","itemField","minField","distinct","values","fieldType","count","exists","types","raw","converted","JSON","stringify","dataTable","getNumberOfColumns","getColumnId","getColumnLabel","addRow","setValue","_ids","_onEvent","apply","setData","viewOptions","getArguments","defaultFilter","dataSet","added","updated","removed","delay","Infinity","_timeout","_extended","_flushIfNeeded","flush","methods","original","method","args","fn","context","entry","clearTimeout","setTimeout","container","SyntaxError","containerElement","margin","defaultXCenter","defaultYCenter","xLabel","yLabel","zLabel","passValueFn","xValueLabel","yValueLabel","zValueLabel","filterLabel","legendLabel","STYLE","DOT","showPerspective","showGrid","keepAspectRatio","showShadow","showGrayBottom","showTooltip","verticalRatio","animationInterval","animationPreload","camera","eye","dataPoints","colX","colY","colZ","colValue","colFilter","xMin","xStep","xMax","yMin","yStep","yMax","zMin","zStep","zMax","valueMin","valueMax","xBarWidth","yBarWidth","colorAxis","colorGrid","colorDot","colorDotBorder","getMouseX","clientX","targetTouches","getMouseY","clientY","Emitter","_setScale","scale","z","xCenter","yCenter","zCenter","setArmLocation","_convert3Dto2D","point3d","translation","_convertPointToTranslation","_convertTranslationToScreen","ax","ay","az","cx","getCameraLocation","cy","cz","sinTx","sin","getCameraRotation","cosTx","cos","sinTy","cosTy","sinTz","cosTz","dx","dy","dz","bx","by","ex","ey","ez","getArmLength","xcenter","frame","canvas","clientWidth","ycenter","_setBackgroundColor","backgroundColor","fill","stroke","strokeWidth","borderColor","borderWidth","borderStyle","BAR","BARCOLOR","BARSIZE","DOTLINE","DOTCOLOR","DOTSIZE","GRID","LINE","SURFACE","_getStyleNumber","styleName","_determineColumnIndexes","counter","column","getDistinctValues","distinctValues","getColumnRange","minMax","_dataInitialize","rawData","_onChange","dataFilter","setOnLoadCallback","redraw","withBars","defaultXBarWidth","dataX","defaultYBarWidth","dataY","xRange","defaultXMin","defaultXMax","defaultXStep","yRange","defaultYMin","defaultYMax","defaultYStep","zRange","defaultZMin","defaultZMax","defaultZStep","valueRange","defaultValueMin","defaultValueMax","_getDataPoints","obj","sortNumber","dataMatrix","xIndex","yIndex","trans","screen","bottom","pointRight","pointTop","pointCross","hasChildNodes","firstChild","position","overflow","noCanvas","fontWeight","padding","innerHTML","onmousedown","_onMouseDown","ontouchstart","_onTouchStart","onmousewheel","_onWheel","ontooltip","_onTooltip","onkeydown","setSize","_resizeCanvas","clientHeight","animationStart","slider","play","animationStop","stop","_resizeCenter","charAt","parseFloat","setCameraPosition","pos","horizontal","vertical","setArmRotation","distance","setArmLength","getCameraPosition","getArmRotation","_readData","_redrawFilter","animationAutoStart","cameraPosition","styleNumber","tooltip","showAnimationControls","_redrawSlider","_redrawClear","_redrawAxis","_redrawDataGrid","_redrawDataLine","_redrawDataBar","_redrawDataDot","_redrawInfo","_redrawLegend","ctx","getContext","clearRect","widthMin","widthMax","dotSize","right","lineWidth","font","ymin","ymax","_hsv2rgb","strokeStyle","beginPath","moveTo","lineTo","strokeRect","fillStyle","closePath","gridLineLen","step","getCurrent","next","textAlign","textBaseline","fillText","label","visible","setValues","setPlayInterval","onchange","getIndex","selectValue","setOnChangeCallback","lineStyle","getLabel","getSelectedValue","from","to","prettyStep","text","xText","yText","zText","offset","xOffset","yOffset","xMin2d","xMax2d","gridLenX","gridLenY","textMargin","armAngle","H","S","V","R","G","B","C","Hi","X","abs","cross","topSideVisible","zAvg","transBottom","dist","sortDepth","aDiff","subtract","bDiff","crossproduct","crossProduct","radius","arc","PI","j","surface","corners","xWidth","yWidth","surfaces","center","avg","transCenter","diff","leftButtonDown","_onMouseUp","which","button","touchDown","startMouseX","startMouseY","startStart","startEnd","startArmRotation","cursor","onmousemove","_onMouseMove","onmouseup","diffX","diffY","horizontalNew","verticalNew","snapAngle","snapValue","round","parameters","emit","boundingRect","mouseX","mouseY","tooltipTimeout","_hideTooltip","dataPoint","_dataPointFromXY","_showTooltip","ontouchmove","_onTouchMove","ontouchend","_onTouchEnd","delta","wheelDelta","detail","oldLength","newLength","_insideTriangle","triangle","sign","as","bs","cs","distMax","closestDataPoint","closestDist","triangle1","triangle2","distX","distY","sqrt","content","line","dot","dom","borderRadius","boxShadow","borderLeft","contentWidth","offsetWidth","contentHeight","offsetHeight","lineHeight","dotWidth","dotHeight","armLocation","armRotation","armLength","cameraLocation","cameraRotation","calculateCameraOrientation","rot","graph","onLoadCallback","loadInBackground","isLoaded","getLoadedProgress","getColumn","getValues","dataView","progress","sub","sum","prev","bar","MozBorderRadius","slide","onclick","togglePlay","onChangeCallback","playTimeout","playInterval","playLoop","setIndex","playNext","interval","clearInterval","getPlayInterval","setPlayLoop","doLoop","onChange","indexToLeft","startClientX","startSlideX","leftToIndex","_start","_end","_step","precision","_current","setRange","setStep","calculatePrettyStep","log10","log","LN10","step1","pow","step2","step5","toPrecision","getStep","groups","forthArgument","defaultOptions","autoResize","orientation","maxHeight","minHeight","_create","body","domProps","emitter","bind","hiddenDates","snap","toScreen","_toScreen","toGlobalScreen","_toGlobalScreen","toTime","_toTime","toGlobalTime","_toGlobalTime","range","timeAxis","currentTime","customTime","itemSet","itemsData","groupsData","setGroups","setItems","Core","newDataSet","initialLoad","dataRange","_getDataRange","setWindow","animate","fit","setSelection","focus","getSelection","itemData","e","getItemRange","dataset","minItem","maxStartItem","maxEndItem","linegraph","getLegend","groupId","isGroupVisible","visibility","convertHiddenOptions","repeat","dateItem","updateHiddenDates","centerContainer","totalRange","pixelTime","startDate","endDate","_d","runUntil","clone","day","dayOfYear","year","dayOffset","date","month","console","removeDuplicates","startHidden","isHidden","endHidden","rangeStart","rangeEnd","hidden","startToFront","endToFront","_applyRange","safeDates","printDates","dates","stepOverHiddenDates","timeStep","previousTime","stepInHidden","currentValue","current","newValue","switchedYear","switchedMonth","switchedDay","time","conversion","getHiddenDurationBetween","correctTimeForHidden","hiddenDuration","totalDuration","partialDuration","accumulatedHiddenDuration","getAccumulatedHiddenDuration","newTime","getHiddenDurationBefore","timeOffset","requiredDuration","previousPoint","snapAwayFromHidden","direction","correctionEnabled","minimumStep","containerHeight","customRange","alignZeros","autoScale","stepIndex","marginStart","marginEnd","deadSpace","majorSteps","minorSteps","setMinimumStep","setFirst","safeSize","minimumStepValue","orderOfMagnitude","minorStepIdx","magnitudefactor","solutionFound","stepSize","niceStart","niceEnd","roundToMinor","marginRange","rounded","hasNext","previous","decimals","exp","cnt","isMajor","now","hours","minutes","seconds","milliseconds","deltaDifference","scaleOffset","moveable","zoomable","zoomMin","zoomMax","touch","animateTimer","_onDragStart","_onDrag","_onDragEnd","_onHold","_onMouseWheel","_onTouch","_onPinch","validateDirection","getPointer","pageX","pageY","hammerUtil","byUser","_cancelAnimation","initStart","initEnd","initTime","anyChanged","dragging","done","changed","newStart","newEnd","getRange","totalHidden","previousDelta","allowDragging","gesture","deltaX","deltaY","diffRange","safeStart","safeEnd","fakeGesture","pointer","pointerDate","_pointerToDate","zoom","touches","centerDate","hiddenDurationBefore","hiddenDurationAfter","move","EPSILON","orderByStart","orderByEnd","aTime","bTime","force","iMax","axis","collidingItem","jj","collision","nostack","subgroups","newTop","subgroup","format","FORMAT","minorLabels","millisecond","second","minute","hour","weekday","majorLabels","setFormat","defaultFormat","first","setFullYear","getFullYear","setMonth","setDate","setHours","setMinutes","setSeconds","setMilliseconds","getMilliseconds","getSeconds","getMinutes","getHours","getDate","getMonth","setScale","setAutoScale","enable","stepYear","stepMonth","stepDay","stepHour","stepMinute","stepSecond","stepMillisecond","getLabelMinor","getLabelMajor","getClassName","even","today","isSame","currentWeek","currentMonth","currentYear","locale","lang","toLowerCase","parent","selected","displayed","dirty","Hammer","select","unselect","setParent","hide","show","isVisible","repositionX","repositionY","_repaintDeleteButton","anchor","editable","deleteButton","title","removeFromDataSet","stopPropagation","_updateContents","template","Element","_updateTitle","removeAttribute","_updateDataAttributes","dataAttributes","attributes","setAttribute","_updateStyle","emptyContent","baseClassName","box","getComputedStyle","onTop","itemSubgroup","subgroupIndex","foreground","align","itemSetHeight","marginLeft","maxWidth","_repaintDragLeft","_repaintDragRight","contentLeft","parentWidth","boxWidth","updateTime","dragLeft","dragLeftItem","dragRight","dragRightItem","_isResized","resized","_previousWidth","_previousHeight","showCurrentTime","locales","backgroundVertical","toUpperCase","substring","currentTimeTimer","setCurrentTime","getCurrentTime","showCustomTime","eventParams","drag","prevent_default","setCustomTime","getCustomTime","svg","linegraphOptions","showMinorLabels","showMajorLabels","icons","majorLinesOffset","minorLinesOffset","labelOffsetX","labelOffsetY","iconWidth","linegraphSVG","DOMelements","lines","labels","conversionFactor","minWidth","stepPixels","stepPixelsForced","zeroCrossing","lineOffset","master","svgElements","iconsRemoved","amountOfGroups","lineContainer","scrollTop","addGroup","graphOptions","updateGroup","removeGroup","display","_redrawGroupIcons","iconHeight","iconOffset","drawIcon","_cleanupIcons","backgroundHorizontal","activeGroups","_calculateCharSize","minorLabelHeight","minorCharHeight","majorLabelHeight","majorCharHeight","minorLineWidth","minorLineHeight","majorLineWidth","majorLineHeight","_redrawLabels","_redrawTitle","amountOfSteps","stepDifference","zeroStepDifference","valueAtZero","marginStartPos","maxLabelSize","_redrawLabel","_redrawLine","titleWidth","titleCharHeight","convertValue","invertedValue","convertedValue","characterHeight","largestWidth","majorCharWidth","minorCharWidth","textMinor","createTextNode","measureCharMinor","textMajor","measureCharMajor","textTitle","measureCharTitle","titleCharWidth","groupsUsingDefaultStyles","usingDefaultStyle","zeroPosition","Line","Bar","Points","setZeroPosition","catmullRom","parametrization","alpha","SVGcontainer","path","fillPath","fillHeight","outline","shaded","barWidth","bar1Height","bar2Height","icon","yAxisOrientation","getYRange","groupData","draw","framework","subgroupOrderer","subgroupOrder","visibleItems","byStart","byEnd","checkRangedItems","inner","marker","getLabelWidth","restack","_updateVisibleItems","markerHeight","lastMarkerHeight","_calculateHeight","offsetTop","offsetLeft","ii","resetSubgroups","labelSet","orderSubgroups","_checkIfVisible","sortArray","sortField","removeItem","startArray","endArray","oldVisibleItems","visibleItemsLookup","lowerBound","upperBound","_checkIfVisibleWithReference","initialPosByStart","_traceVisible","initialPosByEnd","initialPos","breakCondition","groupOrder","selectable","onAdd","onUpdate","onMove","onRemove","onMoving","itemOptions","itemListeners","_onAdd","_onUpdate","_onRemove","groupListeners","_onAddGroups","_onUpdateGroups","_onRemoveGroups","groupIds","selection","stackDirty","touchParams","UNGROUPED","BACKGROUND","_updateUngrouped","backgroundGroup","_onSelectItem","_onMultiSelectItem","_onAddItem","addCallback","Function","markDirty","getVisibleItems","rawVisibleItems","_deselect","_orderGroups","visibleInterval","zoomed","lastVisibleInterval","lastWidth","firstGroup","_firstGroup","firstMargin","nonFirstMargin","groupMargin","groupResized","firstGroupIndex","firstGroupId","ungrouped","_getGroupId","getLabelSet","oldItemsData","getItems","_order","getGroups","_getType","_removeItem","groupOptions","oldGroupId","oldGroup","_constructByEndArray","itemFromTarget","initialX","itemProps","newProps","initial","groupFromTarget","_updateItemProps","_moveToGroup","changes","ctrlKey","srcEvent","shiftKey","oldSelection","newSelection","xAbs","newItem","_getItemRange","_item","itemSetFromTarget","side","iconSize","iconSpacing","textArea","scrollableHeight","drawLegendIcons","paddingTop","defaultGroup","sampling","graphHeight","barChart","handleOverlap","dataAxis","legend","abortedGraphUpdate","updateSVGheight","updateSVGheightOnResize","lastStart","COUNTER","BarGraphFunctions","yAxisLeft","yAxisRight","legendLeft","legendRight","_updateAllGroupData","_updateGroup","groupsContent","ungroupedCounter","forceGraphUpdate","_updateGraph","rangePerPixelInv","preprocessedGroupData","processedGroupData","groupRanges","changeCalled","minDate","maxDate","_getRelevantData","_applySampling","_convertXcoordinates","_getYRanges","_updateYAxis","MAX_CYCLES","_convertYcoordinates","dataContainer","guess","increment","amountOfPoints","xDistance","pointsPerPixel","ceil","sampledData","barCombinedDataLeft","barCombinedDataRight","getStackedBarYRange","minVal","maxVal","yAxisLeftUsed","yAxisRightUsed","minLeft","minRight","maxLeft","maxRight","ignore","_toggleAxisVisiblity","drawIcons","axisUsed","datapoints","xValue","yValue","extractedData","svgHeight","majorTexts","minorTexts","lineTop","parentChanged","foregroundNextSibling","nextSibling","backgroundNextSibling","_repaintLabels","timeLabelsize","cur","prevLine","xPrev","xFirstMajorLabel","_repaintMinorText","_repaintMajorText","_repaintMajorLine","_repaintMinorLine","leftTime","leftText","widthText","arr","pop","childNodes","nodeValue","_determineBrowserMethod","_initializeMixinLoaders","renderRefreshRate","renderTimestep","renderTime","physicsTime","runDoubleSpeed","physicsDiscreteStepsize","initializing","triggerFunctions","edit","editEdge","connect","del","nodes","mass","radiusMin","radiusMax","shape","image","fontColor","fontSize","fontFace","fontFill","fontStrokeWidth","fontStrokeColor","level","borderWidthSelected","edges","widthSelectionMultiplier","hoverWidth","labelAlignment","arrowScaleFactor","dash","gap","altLength","inheritColor","configurePhysics","physics","barnesHut","thetaInverted","gravitationalConstant","centralGravity","springLength","springConstant","damping","repulsion","nodeDistance","hierarchicalRepulsion","clustering","initialMaxNodes","clusterThreshold","reduceToNodes","chainThreshold","clusterEdgeThreshold","sectorThreshold","screenSizeThreshold","fontSizeMultiplier","maxFontSize","forceAmplification","distanceAmplification","edgeGrowth","nodeScaling","maxNodeSizeIncrements","activeAreaBoxSize","clusterLevelDifference","navigation","keyboard","speed","dataManipulation","initiallyVisible","hierarchicalLayout","levelSeparation","nodeSpacing","layout","freezeForStabilization","smoothCurves","dynamic","roundness","maxVelocity","minVelocity","stabilize","stabilizationIterations","zoomExtentOnStabilize","dragNetwork","dragNodes","hideEdgesOnDrag","hideNodesOnDrag","constants","pixelRatio","hoverObj","controlNodesActive","navigationHammers","existing","_new","animationSpeed","animationEasingFunction","animating","easingTime","sourceScale","targetScale","sourceTranslation","targetTranslation","lockedOnNodeId","lockedOnNodeOffset","touchTime","images","setOnloadCallback","_redraw","xIncrement","yIncrement","zoomIncrement","_loadPhysicsSystem","_loadSectorSystem","_loadClusterSystem","_loadSelectionSystem","_loadHierarchySystem","_setTranslation","freezeSimulation","cachedFunctions","startedStabilization","stabilized","draggingNodes","calculationNodes","calculationNodeIndices","nodeIndices","canvasTopLeft","canvasBottomRight","pointerPosition","areaCenter","previousScale","nodesData","edgesData","nodesListeners","_addNodes","_updateNodes","_removeNodes","edgesListeners","_addEdges","_updateEdges","_removeEdges","moving","timer","_setupHierarchicalLayout","zoomExtent","startWithClustering","keycharm","MixinLoader","Activator","browserType","requiresTimeout","_getScriptPath","scripts","getElementsByTagName","src","_getRange","node","minY","maxY","minX","maxX","nodeId","boundingBox","_findCenter","animationOptions","initialZoom","disableStart","zoomLevel","numberOfNodes","factor","yDistance","xZoomLevel","yZoomLevel","animation","_updateNodeIndexList","_clearNodeIndexList","idx","_createManipulatorBar","dotData","DOTToGraph","gephi","gephiData","parseGephi","_setNodes","_setEdges","_putDataInSector","_resetLevels","_stabilize","onEdit","onEditEdge","onConnect","onDelete","editMode","newColorObj","groupname","clickToUse","activator","_createKeyBinds","_loadNavigationControls","_loadManipulationSystem","_configureSmoothCurves","_bindHammer","devicePixelRatio","webkitBackingStorePixelRatio","mozBackingStorePixelRatio","msBackingStorePixelRatio","oBackingStorePixelRatio","backingStorePixelRatio","setTransform","dispose","pinch","_onTap","_onDoubleTap","_onMouseMoveTitle","hammerFrame","_onRelease","reset","isActive","_moveUp","_yStopMoving","_moveDown","_moveLeft","_xStopMoving","_moveRight","_zoomIn","_stopZoom","_zoomOut","_deleteSelected","_cleanupPhysicsConfiguration","_recursiveDOMDelete","DOMobject","_getPointer","pinched","_getScale","_handleTouch","_handleDragStart","_getNodeAt","_getTranslation","isSelected","_selectObject","nodeIds","objectId","selectionObj","xFixed","yFixed","_handleOnDrag","releaseNode","_XconvertDOMtoCanvas","_XconvertCanvasToDOM","_YconvertDOMtoCanvas","_YconvertCanvasToDOM","_handleDragEnd","_handleTap","_handleDoubleTap","_handleOnHold","_handleOnRelease","_zoom","scaleOld","preScaleDragPointer","DOMtoCanvas","scaleFrac","tx","ty","updateClustersDefault","postScaleDragPointer","canvasToDOM","popupObj","_checkHidePopup","checkShow","_checkShowPopup","popupTimer","edgeId","_getEdgeAt","_hoverObject","_blurObject","lastPopupNode","nodeUnderCursor","overlappingNodes","isOverlappingWith","getTitle","overlappingEdges","edge","connected","popup","setPosition","setText","emitEvent","oldWidth","oldHeight","oldNodesData","_updateSelection","angle","_updateCalculationNodes","_reconnectEdges","_updateValueRange","updateLabels","changedData","setProperties","properties","oldEdgesData","oldEdge","disconnect","showInternalIds","_createBezierNodes","via","sectors","dynamicEdges","setValueRange","w","save","translate","_doInAllSectors","restore","offsetX","offsetY","_drawNodes","alwaysShow","setScaleAndPos","inArea","sMax","_drawEdges","_drawControlNodes","_freezeDefinedNodes","_physicsTick","_restoreFrozenNodes","fixedData","_isMoving","vmin","isMoving","_discreteStepNodes","nodesPresent","discreteStepLimited","discreteStep","vminCorrected","_revertPhysicsState","revertPosition","_revertPhysicsTick","_doInAllActiveSectors","_doInSupportSector","mainMovingStatus","supportMovingStatus","mainMoving","_animationStep","_handleNavigation","startTime","renderStartTime","requestAnimationFrame","mozRequestAnimationFrame","webkitRequestAnimationFrame","msRequestAnimationFrame","iterations","toggleFreeze","parentEdgeId","internalMultiplier","positionBezierNode","mixin","storePosition","storePositions","dataArray","allowedToMoveX","allowedToMoveY","getPositions","focusOnNode","nodePosition","lockedOnNode","easingFunction","animateView","locked","_transitionRedraw","viewCenter","distanceFromCenter","_classicRedraw","_lockedRedraw","active","getScale","getCenterCoordinates","getBoundingBox","networkConstants","fromId","toId","widthSelected","labelDimensions","yLine","dirtyLabel","fromBackup","toBackup","originalFromId","originalToId","widthFixed","lengthFixed","controlNodesEnabled","controlNodes","positions","connectedNode","_drawLine","_drawArrow","_drawArrowCenter","_drawDashLine","attachEdge","detachEdge","xFrom","yFrom","xTo","yTo","xObj","yObj","_getDistanceToEdge","_getColor","colorObj","_getLineWidth","_line","midpointX","midpointY","_pointOnLine","_label","resize","_circle","_pointOnCircle","networkScaleInv","_getViaCoordinates","xVia","yVia","quadraticCurveTo","lineCount","measureText","_rotateForLabelAlignment","_drawLabelRect","_drawLabelText","angleInDegrees","atan2","rotate","lineMargin","fillRect","lineJoin","strokeText","setLineDash","pattern","lineDashOffset","lineCap","dashedLine","percentage","arrow","_pointOnBezier","_findBorderPosition","distanceToBorder","distanceToNodes","difference","threshold","arrowPos","guidePos","edgeSegmentLength","toBorderDist","toBorderPoint","x1","y1","x2","y2","x3","y3","lastX","lastY","minDistance","_getDistanceToLine","px","py","something","u","nodeIdFrom","nodeIdTo","getControlNodeFromPosition","getControlNodeToPosition","_enableControlNodes","_disableControlNodes","_getSelectedControlNode","fromDistance","toDistance","_restoreControlNodes","controlnodeFromPos","fromBorderDist","fromBorderPoint","controlnodeToPos","defaultIndex","DEFAULT","imageBroken","load","url","brokenUrl","img","Image","onload","onerror","error","imagelist","grouplist","reroutedEdges","fontDrawThreshold","horizontalAlignLeft","verticalAlignTop","baseRadiusValue","radiusFixed","preassignedLevel","hierarchyEnumerated","fx","fy","vx","vy","previousState","resetCluster","dynamicEdgesLength","clusterSession","clusterSizeWidthFactor","clusterSizeHeightFactor","clusterSizeRadiusFactor","growthIndicator","networkScale","formationScale","clusterSize","containedNodes","containedEdges","clusterSessions","originalLabel","triggerFunction","groupObj","imageObj","brokenImage","_drawDatabase","_resizeDatabase","_drawBox","_resizeBox","_drawCircle","_resizeCircle","_drawEllipse","_resizeEllipse","_drawImage","_resizeImage","_drawCircularImage","_resizeCircularImage","_drawText","_resizeText","_drawDot","_resizeShape","_drawSquare","_drawTriangle","_drawTriangleDown","_drawStar","_reset","clearSizeCache","_setForce","_addForce","storeState","isFixed","velocity","getDistance","_drawImageAtPosition","globalAlpha","drawImage","_drawImageLabel","getTextSize","_swapToImageResizeWhenImageLoaded","diameter","centerX","centerY","_drawRawCircle","circle","clip","textSize","clusterLineWidth","selectionLineWidth","roundRect","database","defaultSize","ellipse","_drawShape","radiusMultiplier","baseline","labelUnderNode","inView","clearVelocity","updateVelocity","massBeforeClustering","energyBefore","styleAttr","fontFamily","WebkitBorderRadius","whiteSpace","parseDOT","parseGraph","nextPreview","isAlphaNumeric","regexAlphaNumeric","merge","o","addNode","graphs","attr","addEdge","createEdge","getToken","tokenType","TOKENTYPE","NULL","token","isComment","DELIMITER","c2","DELIMITERS","IDENTIFIER","newSyntaxError","UNKNOWN","chop","strict","parseStatements","parseStatement","subgraph","parseSubgraph","parseEdge","parseAttributeStatement","parseNodeStatement","subgraphs","parseAttributeList","message","maxLength","forEach2","array1","array2","elem1","elem2","graphData","dotNode","graphNode","convertEdge","dotEdge","graphEdge","subEdge","{","}","[","]",";","=",",","->","--","gephiJSON","allowedToMove","gEdges","gNodes","gEdge","source","gNode","leftContainer","rightContainer","shadowTop","shadowBottom","shadowTopLeft","shadowBottomLeft","shadowTopRight","shadowBottomRight","_redrawTimer","listeners","events","scrollTopMin","redrawCount","_initAutoResize","component","_stopAutoResize","what","getWindow","borderRootHeight","borderRootWidth","autoHeight","centerWidth","_updateScrollTop","visibilityTop","visibilityBottom","MAX_REDRAWS","repaint","_startAutoResize","_onResize","lastHeight","watchTimer","setInterval","initialScrollTop","oldScrollTop","_getScrollTop","newScrollTop","_setScrollTop","eventType","getTouchList","collectEventData","custom","_catmullRom","_linear","dFill","_catmullRomUniform","p0","p1","p2","p3","bp1","bp2","normalization","d1","d2","d3","A","N","M","d3powA","d2powA","d3pow2A","d2pow2A","d1pow2A","d1powA","Bargraph","barCombinedData","coreDistance","drawData","combinedData","intersections","barPoints","_getDataIntersections","heightOffset","_getSafeDrawData","nextKey","amount","resolved","prevKey","accumulated","groupLabel","_getStackedBarYRange","xpos","PhysicsMixin","ClusterMixin","SectorsMixin","SelectionMixin","ManipulationMixin","NavigationMixin","HierarchicalLayoutMixin","_loadMixin","sourceVariable","mixinFunction","_clearMixin","_loadSelectedForceSolver","_loadPhysicsConfiguration","hubThreshold","activeSector","drawingNode","blockConnectingEdgeSelection","forceAppendSelection","manipulationDiv","editModeDiv","closeDiv","_cleanNavigation","_loadNavigationElements","overlay","_onTapOverlay","windowHammer","_hasParent","deactivate","escListener","activate","unbind","back","editNode","addDescription","edgeDescription","editEdgeDescription","createEdgeError","deleteClusterError","CanvasRenderingContext2D","square","s2","ir","triangleDown","star","n","r2d","kappa","ox","oy","xe","ye","xm","ym","bezierCurveTo","wEllipse","hEllipse","ymb","yeb","xt","yt","xi","yi","xl","yl","xr","yr","dashArray","dashLength","dashCount","slope","distRemaining","dashIndex","_callbacks","once","self","removeListener","removeAllListeners","callbacks","cb","hasListeners","__WEBPACK_AMD_DEFINE_FACTORY__","__WEBPACK_AMD_DEFINE_ARRAY__","__WEBPACK_AMD_DEFINE_RESULT__","_exportFunctions","_bound","keydown","keyup","_keys","fromCharCode","code","down","handleEvent","up","keyCode","bound","bindAll","getKey","newBindings","global","dfl","hasOwnProp","defaultParsingFlags","empty","unusedTokens","unusedInput","charsLeftOver","nullInput","invalidMonth","invalidFormat","userInvalidated","iso","printMsg","msg","suppressDeprecationWarnings","warn","deprecate","firstTime","deprecateSimple","deprecations","padToken","func","leftZeroFill","ordinalizeToken","period","localeData","ordinal","monthDiff","anchor2","adjust","wholeMonthDiff","meridiemFixWrap","meridiem","isPm","meridiemHour","isPM","Locale","Moment","config","skipOverflow","checkOverflow","copyConfig","updateInProgress","updateOffset","Duration","normalizedInput","normalizeObjectUnits","years","quarters","quarter","months","weeks","week","days","_milliseconds","_days","_months","_locale","_bubble","val","_isAMomentObject","_i","_f","_l","_strict","_tzm","_isUTC","_offset","_pf","momentProperties","absRound","number","targetLength","forceSign","output","positiveMomentsDifference","base","res","isAfter","momentsDifference","makeAs","isBefore","createAdder","dur","tmp","addOrSubtractDurationFromMoment","mom","isAdding","setTime","rawSetter","rawGetter","rawMonthSetter","input","compareArrays","dontConvert","lengthDiff","diffs","toInt","normalizeUnits","units","lowered","unitAliases","camelFunctions","inputObject","normalizedProp","makeList","setter","getter","results","utc","set","argumentForCoercion","coercedNumber","isFinite","daysInMonth","UTC","getUTCDate","weeksInYear","dow","doy","weekOfYear","daysInYear","isLeapYear","_a","MONTH","DATE","YEAR","HOUR","MINUTE","SECOND","MILLISECOND","_overflowDayOfYear","isValid","_isValid","getTime","bigHour","normalizeLocale","chooseLocale","names","loadLocale","oldLocale","hasModule","model","local","removeFormattingTokens","makeFormatFunction","formattingTokens","formatTokenFunctions","formatMoment","expandFormat","formatFunctions","invalidDate","replaceLongDateFormatTokens","longDateFormat","localFormattingTokens","lastIndex","getParseRegexForToken","parseTokenOneDigit","parseTokenThreeDigits","parseTokenFourDigits","parseTokenOneToFourDigits","parseTokenSignedNumber","parseTokenSixDigits","parseTokenOneToSixDigits","parseTokenTwoDigits","parseTokenOneToThreeDigits","parseTokenWord","_meridiemParse","parseTokenOffsetMs","parseTokenTimestampMs","parseTokenTimezone","parseTokenT","parseTokenDigits","parseTokenOneOrTwoDigits","_ordinalParse","_ordinalParseLenient","RegExp","regexpEscape","unescapeFormat","utcOffsetFromString","string","possibleTzMatches","tzChunk","parseTimezoneChunker","addTimeToArrayFromToken","datePartArray","monthsParse","_dayOfYear","parseTwoDigitYear","_meridiem","_useUTC","weekdaysParse","_w","invalidWeekday","dayOfYearFromWeekInfo","weekYear","temp","GG","W","E","_week","gg","dayOfYearFromWeeks","dateFromConfig","currentDate","yearToUse","currentDateArray","makeUTCDate","getUTCMonth","_nextDay","makeDate","setUTCMinutes","getUTCMinutes","dateFromObject","getUTCFullYear","makeDateFromStringAndFormat","ISO_8601","parseISO","parsedInput","tokens","skipped","stringLength","totalParsedInputLength","matched","p4","makeDateFromStringAndArray","tempConfig","bestMoment","scoreToBeat","currentScore","NaN","score","l","isoRegex","isoDates","isoTimes","makeDateFromString","createFromInputFallback","makeDateFromInput","aspNetJsonRegex","ms","setUTCFullYear","parseWeekday","substituteTimeAgo","withoutSuffix","isFuture","relativeTime","posNegDuration","relativeTimeThresholds","firstDayOfWeek","firstDayOfWeekOfYear","adjustedMoment","daysToDayOfWeek","daysToAdd","getUTCDay","makeMoment","invalid","preparse","pickBy","moments","dayOfMonth","unit","makeAccessor","keepTime","daysToYears","yearsToDays","makeDurationGetter","makeGlobal","shouldDeprecate","ender","oldGlobalMoment","globalScope","VERSION","aspNetTimeSpanJsonRegex","isoDurationRegex","isoFormat","unitMillisecondFactors","Milliseconds","Seconds","Minutes","Hours","Days","Months","Years","D","Q","DDD","dayofyear","isoweekday","isoweek","weekyear","isoweekyear","ordinalizeTokens","paddedTokens","MMM","monthsShort","MMMM","dd","weekdaysMin","ddd","weekdaysShort","dddd","weekdays","isoWeek","YY","YYYY","YYYYY","YYYYYY","gggg","ggggg","isoWeekYear","GGGG","GGGGG","isoWeekday","SS","SSS","SSSS","Z","utcOffset","ZZ","zoneAbbr","zz","zoneName","unix","lists","DDDD","_monthsShort","monthName","regex","_monthsParse","_longMonthsParse","_shortMonthsParse","_weekdays","_weekdaysShort","_weekdaysMin","weekdayName","_weekdaysParse","_longDateFormat","LTS","LT","L","LL","LLL","LLLL","isLower","_calendar","sameDay","nextDay","nextWeek","lastDay","lastWeek","sameElse","calendar","_relativeTime","future","past","mm","hh","MM","yy","pastFuture","_ordinal","postformat","firstDayOfYear","_invalidDate","ret","parseIso","diffRes","isDuration","inp","version","relativeTimeThreshold","limit","defineLocale","_abbr","abbr","langData","flags","parseZone","isDSTShifted","parsingFlags","invalidAt","keepLocalTime","_dateUtcOffset","inputString","asFloat","that","zoneDiff","humanize","fromNow","sod","startOf","isDST","getDay","endOf","inputMs","isBetween","zone","localAdjust","_changeInProgress","isLocal","isUtcOffset","isUtc","hasAlignedHourOffset","isoWeeksInYear","weekInfo","newLocaleData","getTimezoneOffset","isoWeeks","toJSON","isUTC","withSuffix","toIsoString","asSeconds","asMilliseconds","asMinutes","asHours","asDays","asWeeks","asMonths","asYears","ordinalParse","require","noGlobal","graphToggleSmoothCurves","graph_toggleSmooth","getElementById","graphRepositionNodes","showValueOfRange","repositionNodes","graphGenerateOptions","optionsSpecific","radioButton1","radioButton2","checked","backupConstants","optionsDiv","switchConfigurations","radioButton","querySelector","tableId","table","_restoreNodes","constantsVariableName","valueId","rangeValue","_overWriteGraphConstants","RepulsionMixin","HierarchialRepulsionMixin","BarnesHutMixin","_toggleBarnesHut","barnesHutTree","_initializeForceCalculation","clusterToFit","_calculateForces","_calculateGravitationalForces","_calculateNodeForces","_calculateSpringForcesWithSupport","_calculateHierarchicalSpringForces","_calculateSpringForces","supportNodes","supportNodeId","gravity","gravityForce","_sector","edgeLength","springForce","combinedClusterSize","node1","node2","node3","_calculateSpringForce","physicsConfiguration","hierarchicalLayoutDirections","parentElement","rangeElement","radioButton3","graph_repositionNodes","graph_generateOptions","dynamicSmoothCurves","nameArray","maxNumberOfNodes","reposition","maxLevels","forceAggregateHubs","normalizeClusterLevels","increaseClusterLevel","openCluster","isMovingBeforeClustering","_nodeInActiveArea","_addSector","decreaseClusterLevel","_expandClusterNode","_updateDynamicEdges","updateClusters","zoomDirection","recursive","doNotStart","amountOfNodes","_collapseSector","_formClusters","_openClusters","_openClustersBySize","_aggregateHubs","handleChains","chainPercentage","_getChainFraction","_reduceAmountOfChains","_getHubSize","_formClustersByHub","openAll","containedNodeId","childNode","_expelChildFromParent","_unselectAll","_releaseContainedEdges","_connectEdgeBackToChild","_validateEdges","othersPresent","childNodeId","_repositionBezierNodes","_formClustersByZoom","_forceClustersByZoom","minLength","_addToCluster","_clusterToSmallestNeighbour","smallestNeighbour","smallestNeighbourNode","neighbour","onlyEqual","_formClusterFromHub","hubNode","absorptionSizeOffset","allowCluster","edgesIdarray","amountOfInitialEdges","_addToContainedEdges","_connectEdgeToCluster","_containCircularEdgesFromNode","massBefore","correction","edgeToId","edgeFromId","k","_addToReroutedEdges","maxLevel","minLevel","clusterLevel","targetLevel","average","averageSquared","hubCounter","largestHub","variance","standardDeviation","fraction","reduceAmount","chains","total","_switchToSector","sectorId","sectorType","_switchToActiveSector","_switchToFrozenSector","_switchToSupportSector","_loadLatestSector","_previousSector","_setActiveSector","newId","_forgetLastSector","_createNewSector","_deleteActiveSector","_deleteFrozenSector","_freezeSector","_activateSector","_mergeThisWithFrozen","_collapseThisToSingleCluster","sector","unqiueIdentifier","previousSector","runFunction","argument","returnValues","_doInAllFrozenSectors","_drawSectorNodes","_drawAllSectorNodes","_getNodesOverlappingWith","_getAllNodesOverlappingWith","_pointerToPositionObject","positionObject","_getEdgesOverlappingWith","_getAllEdgesOverlappingWith","_addToSelection","_addToHover","_removeFromSelection","doNotTrigger","_unselectClusters","_getSelectedNodeCount","_getSelectedNode","_getSelectedEdge","_getSelectedEdgeCount","_getSelectedObjectCount","_selectionIsEmpty","_clusterInSelection","_selectConnectedEdges","_hoverConnectedEdges","_unselectConnectedEdges","append","highlightEdges","overrideSelectable","DOM","_manipulationReleaseOverload","_navigationReleaseOverload","getSelectedNodes","edgeIds","getSelectedEdges","idArray","selectNodes","RangeError","selectEdges","_clearManipulatorBar","manipulationDOM","_restoreOverloadedFunctions","functionName","_toggleEditMode","toolbar","boundFunction","edgeBeingEdited","selectedControlNode","_createAddNodeToolbar","_createAddEdgeToolbar","_editNode","_createEditEdgeToolbar","_addNode","_handleConnect","_finishConnect","_selectControlNode","_controlNodeDrag","_releaseControlNode","newNode","_editEdge","alert","targetNode","connectionEdge","connectFromId","_createEdge","defaultData","finalizedData","sourceNodeId","targetNodeId","selectedNodes","selectedEdges","navigationDivs","navigationDivActions","_stopMovement","_zoomExtent","hubsize","definedLevel","undefinedLevel","_changeConstants","_determineLevels","_determineLevelsDirected","distribution","_getDistribution","_placeNodesByHierarchy","minPos","_placeBranchNodes","maxCount","_setLevel","firstNode","_setLevelDirected","parentId","parentLevel","nodeMoved","setup","READY","Event","determineEventTypes","Utils","each","gestures","Detection","register","onTouch","DOCUMENT","EVENT_MOVE","detect","EVENT_END","Instance","defaults","behavior","userSelect","touchAction","touchCallout","contentZooming","userDrag","tapHighlightColor","HAS_POINTEREVENTS","pointerEnabled","msPointerEnabled","HAS_TOUCHEVENTS","IS_MOBILE","NO_MOUSEEVENTS","CALCULATE_INTERVAL","EVENT_TYPES","DIRECTION_DOWN","DIRECTION_LEFT","DIRECTION_UP","DIRECTION_RIGHT","POINTER_MOUSE","POINTER_TOUCH","POINTER_PEN","EVENT_START","EVENT_RELEASE","EVENT_TOUCH","plugins","utils","dest","handler","iterator","inStr","find","inArray","hasParent","getCenter","getVelocity","deltaTime","getAngle","touch1","touch2","getDirection","getRotation","isVertical","setPrefixedCss","toggle","prefixes","toCamelCase","toggleBehavior","falseFn","onselectstart","ondragstart","str","preventMouseEvents","started","shouldDetect","hook","onTouchHandler","ev","triggerType","srcType","isPointer","isMouse","buttons","PointerEvent","matchType","updatePointer","doDetect","touchList","touchListLength","triggerChange","trigger","changedLength","changedTouches","evData","identifiers","identifier","pointerType","timeStamp","preventManipulation","stopDetect","pointers","touchlist","pointerEvent","pointerId","pt","MSPOINTER_TYPE_MOUSE","MSPOINTER_TYPE_TOUCH","MSPOINTER_TYPE_PEN","detection","stopped","startDetect","inst","eventData","startEvent","lastEvent","lastCalcEvent","futureCalcEvent","lastCalcData","extendEventData","instOptions","getCalculatedData","recalc","calcEv","calcData","velocityX","velocityY","interimAngle","interimDirection","startEv","lastEv","rotation","eventStartHandler","eventHandlers","createEvent","initEvent","dispatchEvent","state","eh","dragGesture","dragMaxTouches","triggered","dragMinDistance","startCenter","dragDistanceCorrection","dragLockToAxis","dragLockMinDistance","lastDirection","dragBlockVertical","dragBlockHorizontal","Drag","Gesture","holdGesture","holdTimeout","holdThreshold","Hold","Release","Swipe","swipeMinTouches","swipeMaxTouches","swipeVelocityX","swipeVelocityY","tapGesture","sincePrev","didDoubleTap","hasMoved","tapMaxDistance","tapMaxTime","doubleTapInterval","doubleTapDistance","tapAlways","Tap","Touch","preventMouse","transformGesture","scaleThreshold","rotationThreshold","transformMinScale","transformMinRotation","Transform","webpackContext","req","resolve","repulsingForce","a_base","minimumDistance","steepness","springFx","springFy","totalFx","totalFy","correctionFx","correctionFy","nodeCount","_formBarnesHutTree","_getForceContribution","children","NW","NE","SW","SE","parentBranch","childrenCount","centerOfMass","calcSize","MAX_VALUE","sizeDiff","minimumTreeSize","rootSize","halfRootSize","_splitBranch","_placeInTree","_updateBranchMass","totalMass","totalMassInv","biggestSize","skipMassUpdate","_placeInRegion","region","containedNode","_insertRegion","childSize","_drawTree","_drawBranch","branch","webpackPolyfill","paths"],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"vis.map","sources":["./dist/vis.js"],"names":["root","factory","exports","module","define","amd","this","modules","__webpack_require__","moduleId","installedModules","id","loaded","call","m","c","p","util","DOMutil","DataSet","DataView","Queue","Graph3d","graph3d","Camera","Filter","Point2d","Point3d","Slider","StepNumber","Timeline","Graph2d","timeline","DateUtil","DataStep","Range","stack","TimeStep","components","items","Item","BackgroundItem","BoxItem","PointItem","RangeItem","Component","CurrentTime","CustomTime","DataAxis","GraphGroup","Group","BackgroundGroup","ItemSet","Legend","LineGraph","TimeAxis","Network","network","Edge","Groups","Images","Node","Popup","dotparser","gephiParser","Graph","Error","moment","hammer","isNumber","object","Number","isString","String","isDate","Date","match","ASPDateRegex","exec","isNaN","parse","isDataTable","google","visualization","DataTable","randomUUID","S4","Math","floor","random","toString","extend","a","i","len","arguments","length","other","prop","hasOwnProperty","selectiveExtend","props","Array","isArray","selectiveDeepExtend","b","TypeError","constructor","Object","undefined","deepExtend","selectiveNotDeepExtend","indexOf","equalArray","convert","type","Boolean","valueOf","isMoment","toDate","getType","toISOString","value","getAbsoluteLeft","elem","getBoundingClientRect","left","window","pageXOffset","getAbsoluteTop","top","pageYOffset","addClassName","className","classes","split","push","join","removeClassName","index","splice","forEach","callback","toArray","array","updateProperty","key","addEventListener","element","action","listener","useCapture","navigator","userAgent","attachEvent","removeEventListener","detachEvent","preventDefault","event","returnValue","getTarget","target","srcElement","nodeType","parentNode","option","asBoolean","defaultValue","asNumber","asString","asSize","asElement","hexToRGB","hex","shorthandRegex","replace","r","g","result","parseInt","RGBToHex","red","green","blue","slice","parseColor","color","isValidRGB","rgb","substr","isValidHex","hsv","hexToHSV","lighterColorHSV","h","s","v","min","darkerColorHSV","darkerColorHex","HSVToHex","lighterColorHex","background","border","highlight","hover","RGBToHSV","minRGB","maxRGB","max","d","hue","saturation","cssUtil","cssText","styles","style","trim","parts","keys","map","addCssText","currentStyles","newStyles","removeCssText","removeStyles","HSVToRGB","f","q","t","isOk","test","selectiveBridgeObject","fields","referenceObject","objectTo","create","bridgeObject","mergeOptions","mergeTarget","options","enabled","binarySearchCustom","orderedItems","searchFunction","field","field2","maxIterations","iteration","low","high","middle","item","searchResult","binarySearchValue","sidePreference","prevValue","nextValue","easeInOutQuad","start","end","duration","change","easingFunctions","linear","easeInQuad","easeOutQuad","easeInCubic","easeOutCubic","easeInOutCubic","easeInQuart","easeOutQuart","easeInOutQuart","easeInQuint","easeOutQuint","easeInOutQuint","prepareElements","JSONcontainer","elementType","redundant","used","cleanupElements","removeChild","getSVGElement","svgContainer","shift","document","createElementNS","appendChild","getDOMElement","DOMContainer","insertBefore","createElement","drawPoint","x","y","group","point","drawPoints","setAttributeNS","size","drawBar","width","height","rect","data","_options","_data","_fieldId","fieldId","_type","_subscribers","add","setOptions","prototype","queue","_queue","destroy","on","subscribers","subscribe","off","filter","unsubscribe","_trigger","params","senderId","concat","subscriber","addedIds","me","_addItem","columns","_getColumnNames","row","rows","getNumberOfRows","col","cols","getValue","update","updatedIds","updatedData","addOrUpdate","_updateItem","get","ids","firstType","returnType","allowedValues","itemId","_getItem","order","_sort","_filterFields","_appendRow","getIds","getDataSet","mappedItems","filteredItem","name","sort","av","bv","remove","removedId","removedIds","_remove","clear","maxField","itemField","minField","distinct","values","fieldType","count","exists","types","raw","converted","JSON","stringify","dataTable","getNumberOfColumns","getColumnId","getColumnLabel","addRow","setValue","_ids","_onEvent","apply","setData","viewOptions","getArguments","defaultFilter","dataSet","added","updated","removed","delay","Infinity","_timeout","_extended","_flushIfNeeded","flush","methods","original","method","args","fn","context","entry","clearTimeout","setTimeout","container","SyntaxError","containerElement","margin","defaultXCenter","defaultYCenter","xLabel","yLabel","zLabel","passValueFn","xValueLabel","yValueLabel","zValueLabel","filterLabel","legendLabel","STYLE","DOT","showPerspective","showGrid","keepAspectRatio","showShadow","showGrayBottom","showTooltip","verticalRatio","animationInterval","animationPreload","camera","eye","dataPoints","colX","colY","colZ","colValue","colFilter","xMin","xStep","xMax","yMin","yStep","yMax","zMin","zStep","zMax","valueMin","valueMax","xBarWidth","yBarWidth","colorAxis","colorGrid","colorDot","colorDotBorder","getMouseX","clientX","targetTouches","getMouseY","clientY","Emitter","_setScale","scale","z","xCenter","yCenter","zCenter","setArmLocation","_convert3Dto2D","point3d","translation","_convertPointToTranslation","_convertTranslationToScreen","ax","ay","az","cx","getCameraLocation","cy","cz","sinTx","sin","getCameraRotation","cosTx","cos","sinTy","cosTy","sinTz","cosTz","dx","dy","dz","bx","by","ex","ey","ez","getArmLength","xcenter","frame","canvas","clientWidth","ycenter","_setBackgroundColor","backgroundColor","fill","stroke","strokeWidth","borderColor","borderWidth","borderStyle","BAR","BARCOLOR","BARSIZE","DOTLINE","DOTCOLOR","DOTSIZE","GRID","LINE","SURFACE","_getStyleNumber","styleName","_determineColumnIndexes","counter","column","getDistinctValues","distinctValues","getColumnRange","minMax","_dataInitialize","rawData","_onChange","dataFilter","setOnLoadCallback","redraw","withBars","defaultXBarWidth","dataX","defaultYBarWidth","dataY","xRange","defaultXMin","defaultXMax","defaultXStep","yRange","defaultYMin","defaultYMax","defaultYStep","zRange","defaultZMin","defaultZMax","defaultZStep","valueRange","defaultValueMin","defaultValueMax","_getDataPoints","obj","sortNumber","dataMatrix","xIndex","yIndex","trans","screen","bottom","pointRight","pointTop","pointCross","hasChildNodes","firstChild","position","overflow","noCanvas","fontWeight","padding","innerHTML","onmousedown","_onMouseDown","ontouchstart","_onTouchStart","onmousewheel","_onWheel","ontooltip","_onTooltip","onkeydown","setSize","_resizeCanvas","clientHeight","animationStart","slider","play","animationStop","stop","_resizeCenter","charAt","parseFloat","setCameraPosition","pos","horizontal","vertical","setArmRotation","distance","setArmLength","getCameraPosition","getArmRotation","_readData","_redrawFilter","animationAutoStart","cameraPosition","styleNumber","tooltip","showAnimationControls","_redrawSlider","_redrawClear","_redrawAxis","_redrawDataGrid","_redrawDataLine","_redrawDataBar","_redrawDataDot","_redrawInfo","_redrawLegend","ctx","getContext","clearRect","widthMin","widthMax","dotSize","right","lineWidth","font","ymin","ymax","_hsv2rgb","strokeStyle","beginPath","moveTo","lineTo","strokeRect","fillStyle","closePath","gridLineLen","step","getCurrent","next","textAlign","textBaseline","fillText","label","visible","setValues","setPlayInterval","onchange","getIndex","selectValue","setOnChangeCallback","lineStyle","getLabel","getSelectedValue","from","to","prettyStep","text","xText","yText","zText","offset","xOffset","yOffset","xMin2d","xMax2d","gridLenX","gridLenY","textMargin","armAngle","H","S","V","R","G","B","C","Hi","X","abs","cross","topSideVisible","zAvg","transBottom","dist","sortDepth","aDiff","subtract","bDiff","crossproduct","crossProduct","radius","arc","PI","j","surface","corners","xWidth","yWidth","surfaces","center","avg","transCenter","diff","leftButtonDown","_onMouseUp","which","button","touchDown","startMouseX","startMouseY","startStart","startEnd","startArmRotation","cursor","onmousemove","_onMouseMove","onmouseup","diffX","diffY","horizontalNew","verticalNew","snapAngle","snapValue","round","parameters","emit","boundingRect","mouseX","mouseY","tooltipTimeout","_hideTooltip","dataPoint","_dataPointFromXY","_showTooltip","ontouchmove","_onTouchMove","ontouchend","_onTouchEnd","delta","wheelDelta","detail","oldLength","newLength","_insideTriangle","triangle","sign","as","bs","cs","distMax","closestDataPoint","closestDist","triangle1","triangle2","distX","distY","sqrt","content","line","dot","dom","borderRadius","boxShadow","borderLeft","contentWidth","offsetWidth","contentHeight","offsetHeight","lineHeight","dotWidth","dotHeight","armLocation","armRotation","armLength","cameraLocation","cameraRotation","calculateCameraOrientation","rot","graph","onLoadCallback","loadInBackground","isLoaded","getLoadedProgress","getColumn","getValues","dataView","progress","sub","sum","prev","bar","MozBorderRadius","slide","onclick","togglePlay","onChangeCallback","playTimeout","playInterval","playLoop","setIndex","playNext","interval","clearInterval","getPlayInterval","setPlayLoop","doLoop","onChange","indexToLeft","startClientX","startSlideX","leftToIndex","_start","_end","_step","precision","_current","setRange","setStep","calculatePrettyStep","log10","log","LN10","step1","pow","step2","step5","toPrecision","getStep","groups","forthArgument","defaultOptions","autoResize","orientation","maxHeight","minHeight","_create","body","domProps","emitter","bind","hiddenDates","snap","toScreen","_toScreen","toGlobalScreen","_toGlobalScreen","toTime","_toTime","toGlobalTime","_toGlobalTime","range","timeAxis","currentTime","customTime","itemSet","itemsData","groupsData","setGroups","setItems","Core","newDataSet","initialLoad","dataRange","_getDataRange","setWindow","animate","fit","setSelection","focus","getSelection","itemData","e","getItemRange","dataset","minItem","maxStartItem","maxEndItem","linegraph","getLegend","groupId","isGroupVisible","visibility","convertHiddenOptions","repeat","dateItem","updateHiddenDates","centerContainer","totalRange","pixelTime","startDate","endDate","_d","runUntil","clone","day","dayOfYear","year","dayOffset","date","month","console","removeDuplicates","startHidden","isHidden","endHidden","rangeStart","rangeEnd","hidden","startToFront","endToFront","_applyRange","safeDates","printDates","dates","stepOverHiddenDates","timeStep","previousTime","stepInHidden","currentValue","current","newValue","switchedYear","switchedMonth","switchedDay","time","conversion","getHiddenDurationBetween","correctTimeForHidden","hiddenDuration","totalDuration","partialDuration","accumulatedHiddenDuration","getAccumulatedHiddenDuration","newTime","getHiddenDurationBefore","timeOffset","requiredDuration","previousPoint","snapAwayFromHidden","direction","correctionEnabled","minimumStep","containerHeight","customRange","alignZeros","autoScale","stepIndex","marginStart","marginEnd","deadSpace","majorSteps","minorSteps","setMinimumStep","setFirst","safeSize","minimumStepValue","orderOfMagnitude","minorStepIdx","magnitudefactor","solutionFound","stepSize","niceStart","niceEnd","roundToMinor","marginRange","rounded","hasNext","previous","decimals","exp","cnt","isMajor","now","hours","minutes","seconds","milliseconds","deltaDifference","scaleOffset","moveable","zoomable","zoomMin","zoomMax","touch","animateTimer","_onDragStart","_onDrag","_onDragEnd","_onHold","_onMouseWheel","_onTouch","_onPinch","validateDirection","getPointer","pageX","pageY","hammerUtil","byUser","_cancelAnimation","initStart","initEnd","initTime","anyChanged","dragging","done","changed","newStart","newEnd","getRange","totalHidden","previousDelta","allowDragging","gesture","deltaX","deltaY","diffRange","safeStart","safeEnd","fakeGesture","pointer","pointerDate","_pointerToDate","zoom","touches","centerDate","hiddenDurationBefore","hiddenDurationAfter","move","EPSILON","orderByStart","orderByEnd","aTime","bTime","force","iMax","axis","collidingItem","jj","collision","nostack","subgroups","newTop","subgroup","format","FORMAT","minorLabels","millisecond","second","minute","hour","weekday","majorLabels","setFormat","defaultFormat","first","setFullYear","getFullYear","setMonth","setDate","setHours","setMinutes","setSeconds","setMilliseconds","getMilliseconds","getSeconds","getMinutes","getHours","getDate","getMonth","setScale","setAutoScale","enable","stepYear","stepMonth","stepDay","stepHour","stepMinute","stepSecond","stepMillisecond","getLabelMinor","getLabelMajor","getClassName","even","today","isSame","currentWeek","currentMonth","currentYear","locale","lang","toLowerCase","_isResized","resized","_previousWidth","_previousHeight","showCurrentTime","locales","parent","backgroundVertical","title","toUpperCase","substring","currentTimeTimer","setCurrentTime","getCurrentTime","showCustomTime","eventParams","Hammer","drag","prevent_default","setCustomTime","getCustomTime","stopPropagation","svg","linegraphOptions","showMinorLabels","showMajorLabels","icons","majorLinesOffset","minorLinesOffset","labelOffsetX","labelOffsetY","iconWidth","linegraphSVG","DOMelements","lines","labels","conversionFactor","minWidth","stepPixels","stepPixelsForced","zeroCrossing","lineOffset","master","svgElements","iconsRemoved","amountOfGroups","lineContainer","scrollTop","addGroup","graphOptions","updateGroup","removeGroup","hide","show","display","_redrawGroupIcons","iconHeight","iconOffset","drawIcon","_cleanupIcons","backgroundHorizontal","activeGroups","_calculateCharSize","minorLabelHeight","minorCharHeight","majorLabelHeight","majorCharHeight","minorLineWidth","minorLineHeight","majorLineWidth","majorLineHeight","_redrawLabels","_redrawTitle","amountOfSteps","stepDifference","zeroStepDifference","valueAtZero","marginStartPos","maxLabelSize","_redrawLabel","_redrawLine","titleWidth","titleCharHeight","convertValue","invertedValue","convertedValue","characterHeight","largestWidth","majorCharWidth","minorCharWidth","textMinor","createTextNode","measureCharMinor","textMajor","measureCharMajor","textTitle","measureCharTitle","titleCharWidth","groupsUsingDefaultStyles","usingDefaultStyle","zeroPosition","Line","Bar","Points","setZeroPosition","catmullRom","parametrization","alpha","SVGcontainer","path","fillPath","fillHeight","outline","shaded","barWidth","bar1Height","bar2Height","icon","yAxisOrientation","getYRange","groupData","draw","framework","subgroupIndex","subgroupOrderer","subgroupOrder","visibleItems","byStart","byEnd","checkRangedItems","inner","foreground","marker","Element","getLabelWidth","restack","_updateVisibleItems","markerHeight","lastMarkerHeight","dirty","displayed","_calculateHeight","offsetTop","offsetLeft","ii","repositionY","resetSubgroups","labelSet","setParent","orderSubgroups","_checkIfVisible","sortArray","sortField","removeFromDataSet","removeItem","startArray","endArray","oldVisibleItems","visibleItemsLookup","lowerBound","upperBound","_checkIfVisibleWithReference","initialPosByStart","_traceVisible","initialPosByEnd","repositionX","initialPos","breakCondition","isVisible","align","groupOrder","selectable","editable","updateTime","onAdd","onUpdate","onMove","onRemove","onMoving","itemOptions","itemListeners","_onAdd","_onUpdate","_onRemove","groupListeners","_onAddGroups","_onUpdateGroups","_onRemoveGroups","groupIds","selection","stackDirty","touchParams","UNGROUPED","BACKGROUND","box","_updateUngrouped","backgroundGroup","_onSelectItem","_onMultiSelectItem","_onAddItem","addCallback","Function","markDirty","unselect","select","getVisibleItems","rawVisibleItems","_deselect","_orderGroups","visibleInterval","zoomed","lastVisibleInterval","lastWidth","firstGroup","_firstGroup","firstMargin","nonFirstMargin","groupMargin","groupResized","firstGroupIndex","firstGroupId","ungrouped","_getGroupId","getLabelSet","oldItemsData","getItems","_order","getGroups","_getType","_removeItem","groupOptions","oldGroupId","oldGroup","_constructByEndArray","itemFromTarget","selected","dragLeftItem","dragRightItem","initialX","itemProps","newProps","initial","groupFromTarget","_updateItemProps","_moveToGroup","changes","ctrlKey","srcEvent","shiftKey","oldSelection","newSelection","xAbs","newItem","_getItemRange","_item","itemSetFromTarget","side","iconSize","iconSpacing","textArea","scrollableHeight","drawLegendIcons","getComputedStyle","paddingTop","defaultGroup","sampling","graphHeight","barChart","handleOverlap","dataAxis","legend","abortedGraphUpdate","updateSVGheight","updateSVGheightOnResize","lastStart","COUNTER","BarGraphFunctions","yAxisLeft","yAxisRight","legendLeft","legendRight","_updateAllGroupData","_updateGroup","groupsContent","ungroupedCounter","forceGraphUpdate","_updateGraph","rangePerPixelInv","preprocessedGroupData","processedGroupData","groupRanges","changeCalled","minDate","maxDate","_getRelevantData","_applySampling","_convertXcoordinates","_getYRanges","_updateYAxis","MAX_CYCLES","_convertYcoordinates","dataContainer","guess","increment","amountOfPoints","xDistance","pointsPerPixel","ceil","sampledData","barCombinedDataLeft","barCombinedDataRight","getStackedBarYRange","minVal","maxVal","yAxisLeftUsed","yAxisRightUsed","minLeft","minRight","maxLeft","maxRight","ignore","_toggleAxisVisiblity","drawIcons","axisUsed","datapoints","xValue","yValue","extractedData","svgHeight","majorTexts","minorTexts","lineTop","parentChanged","foregroundNextSibling","nextSibling","backgroundNextSibling","_repaintLabels","timeLabelsize","cur","prevLine","xPrev","xFirstMajorLabel","_repaintMinorText","_repaintMajorText","_repaintMajorLine","_repaintMinorLine","leftTime","leftText","widthText","arr","pop","childNodes","nodeValue","_repaintDeleteButton","anchor","deleteButton","_updateContents","template","_updateTitle","removeAttribute","_updateDataAttributes","dataAttributes","attributes","setAttribute","_updateStyle","emptyContent","baseClassName","onTop","itemSubgroup","itemSetHeight","marginLeft","maxWidth","_repaintDragLeft","_repaintDragRight","contentLeft","parentWidth","boxWidth","dragLeft","dragRight","_determineBrowserMethod","_initializeMixinLoaders","renderRefreshRate","renderTimestep","renderTime","physicsTime","runDoubleSpeed","physicsDiscreteStepsize","initializing","triggerFunctions","edit","editEdge","connect","del","nodes","mass","radiusMin","radiusMax","shape","image","fontColor","fontSize","fontFace","fontFill","fontStrokeWidth","fontStrokeColor","level","borderWidthSelected","edges","widthSelectionMultiplier","hoverWidth","labelAlignment","arrowScaleFactor","dash","gap","altLength","inheritColor","configurePhysics","physics","barnesHut","thetaInverted","gravitationalConstant","centralGravity","springLength","springConstant","damping","repulsion","nodeDistance","hierarchicalRepulsion","clustering","initialMaxNodes","clusterThreshold","reduceToNodes","chainThreshold","clusterEdgeThreshold","sectorThreshold","screenSizeThreshold","fontSizeMultiplier","maxFontSize","forceAmplification","distanceAmplification","edgeGrowth","nodeScaling","maxNodeSizeIncrements","activeAreaBoxSize","clusterLevelDifference","navigation","keyboard","speed","bindToWindow","dataManipulation","initiallyVisible","hierarchicalLayout","levelSeparation","nodeSpacing","layout","freezeForStabilization","smoothCurves","dynamic","roundness","maxVelocity","minVelocity","stabilize","stabilizationIterations","zoomExtentOnStabilize","dragNetwork","dragNodes","hideEdgesOnDrag","hideNodesOnDrag","constants","pixelRatio","hoverObj","controlNodesActive","navigationHammers","existing","_new","animationSpeed","animationEasingFunction","animating","easingTime","sourceScale","targetScale","sourceTranslation","targetTranslation","lockedOnNodeId","lockedOnNodeOffset","touchTime","images","setOnloadCallback","_redraw","xIncrement","yIncrement","zoomIncrement","_loadPhysicsSystem","_loadSectorSystem","_loadClusterSystem","_loadSelectionSystem","_loadHierarchySystem","_setTranslation","freezeSimulation","cachedFunctions","startedStabilization","stabilized","draggingNodes","calculationNodes","calculationNodeIndices","nodeIndices","canvasTopLeft","canvasBottomRight","pointerPosition","areaCenter","previousScale","nodesData","edgesData","nodesListeners","_addNodes","_updateNodes","_removeNodes","edgesListeners","_addEdges","_updateEdges","_removeEdges","moving","timer","_setupHierarchicalLayout","zoomExtent","startWithClustering","keycharm","MixinLoader","Activator","browserType","requiresTimeout","_getScriptPath","scripts","getElementsByTagName","src","_getRange","node","minY","maxY","minX","maxX","nodeId","boundingBox","_findCenter","animationOptions","initialZoom","disableStart","zoomLevel","numberOfNodes","factor","yDistance","xZoomLevel","yZoomLevel","animation","_updateNodeIndexList","_clearNodeIndexList","idx","_createManipulatorBar","dotData","DOTToGraph","gephi","gephiData","parseGephi","_setNodes","_setEdges","_putDataInSector","_resetLevels","_stabilize","onEdit","onEditEdge","onConnect","onDelete","editMode","newColorObj","groupname","clickToUse","activator","_createKeyBinds","_loadNavigationControls","_loadManipulationSystem","_configureSmoothCurves","_bindHammer","tabIndex","devicePixelRatio","webkitBackingStorePixelRatio","mozBackingStorePixelRatio","msBackingStorePixelRatio","oBackingStorePixelRatio","backingStorePixelRatio","setTransform","dispose","pinch","_onTap","_onDoubleTap","_onMouseMoveTitle","hammerFrame","_onRelease","reset","isActive","_moveUp","_yStopMoving","_moveDown","_moveLeft","_xStopMoving","_moveRight","_zoomIn","_stopZoom","_zoomOut","_deleteSelected","_cleanupPhysicsConfiguration","_recursiveDOMDelete","DOMobject","_getPointer","pinched","_getScale","_handleTouch","_handleDragStart","_getNodeAt","_getTranslation","isSelected","_selectObject","nodeIds","objectId","selectionObj","xFixed","yFixed","_handleOnDrag","releaseNode","_XconvertDOMtoCanvas","_XconvertCanvasToDOM","_YconvertDOMtoCanvas","_YconvertCanvasToDOM","_handleDragEnd","_handleTap","_handleDoubleTap","_handleOnHold","_handleOnRelease","_zoom","scaleOld","preScaleDragPointer","DOMtoCanvas","scaleFrac","tx","ty","updateClustersDefault","postScaleDragPointer","canvasToDOM","popupObj","_checkHidePopup","checkShow","_checkShowPopup","popupTimer","edgeId","_getEdgeAt","_hoverObject","_blurObject","lastPopupNode","nodeUnderCursor","overlappingNodes","isOverlappingWith","getTitle","overlappingEdges","edge","connected","popup","setPosition","setText","emitEvent","oldWidth","oldHeight","oldNodesData","_updateSelection","angle","_updateCalculationNodes","_reconnectEdges","_updateValueRange","updateLabels","changedData","setProperties","properties","oldEdgesData","oldEdge","disconnect","showInternalIds","_createBezierNodes","via","sectors","dynamicEdges","setValueRange","w","save","translate","_doInAllSectors","restore","offsetX","offsetY","_drawNodes","alwaysShow","setScaleAndPos","inArea","sMax","_drawEdges","_drawControlNodes","_freezeDefinedNodes","_physicsTick","_restoreFrozenNodes","fixedData","_isMoving","vmin","isMoving","_discreteStepNodes","nodesPresent","discreteStepLimited","discreteStep","vminCorrected","_revertPhysicsState","revertPosition","_revertPhysicsTick","_doInAllActiveSectors","_doInSupportSector","mainMovingStatus","supportMovingStatus","mainMoving","_animationStep","_handleNavigation","startTime","renderStartTime","requestAnimationFrame","mozRequestAnimationFrame","webkitRequestAnimationFrame","msRequestAnimationFrame","iterations","toggleFreeze","parentEdgeId","internalMultiplier","positionBezierNode","mixin","storePosition","storePositions","dataArray","allowedToMoveX","allowedToMoveY","getPositions","focusOnNode","nodePosition","lockedOnNode","easingFunction","animateView","locked","_transitionRedraw","viewCenter","distanceFromCenter","_classicRedraw","_lockedRedraw","active","getScale","getCenterCoordinates","getBoundingBox","networkConstants","fromId","toId","widthSelected","labelDimensions","yLine","dirtyLabel","fromBackup","toBackup","originalFromId","originalToId","widthFixed","lengthFixed","controlNodesEnabled","controlNodes","positions","connectedNode","_drawLine","_drawArrow","_drawArrowCenter","_drawDashLine","attachEdge","detachEdge","xFrom","yFrom","xTo","yTo","xObj","yObj","_getDistanceToEdge","_getColor","colorObj","_getLineWidth","_line","midpointX","midpointY","_pointOnLine","_label","resize","_circle","_pointOnCircle","networkScaleInv","_getViaCoordinates","xVia","yVia","quadraticCurveTo","lineCount","measureText","_rotateForLabelAlignment","_drawLabelRect","_drawLabelText","angleInDegrees","atan2","rotate","lineMargin","fillRect","lineJoin","strokeText","setLineDash","pattern","lineDashOffset","lineCap","dashedLine","percentage","arrow","_pointOnBezier","_findBorderPosition","distanceToBorder","distanceToNodes","difference","threshold","arrowPos","guidePos","edgeSegmentLength","toBorderDist","toBorderPoint","x1","y1","x2","y2","x3","y3","lastX","lastY","minDistance","_getDistanceToLine","px","py","something","u","nodeIdFrom","nodeIdTo","getControlNodeFromPosition","getControlNodeToPosition","_enableControlNodes","_disableControlNodes","_getSelectedControlNode","fromDistance","toDistance","_restoreControlNodes","controlnodeFromPos","fromBorderDist","fromBorderPoint","controlnodeToPos","defaultIndex","DEFAULT","imageBroken","load","url","brokenUrl","img","Image","onload","onerror","error","imagelist","grouplist","reroutedEdges","fontDrawThreshold","horizontalAlignLeft","verticalAlignTop","baseRadiusValue","radiusFixed","preassignedLevel","hierarchyEnumerated","fx","fy","vx","vy","previousState","resetCluster","dynamicEdgesLength","clusterSession","clusterSizeWidthFactor","clusterSizeHeightFactor","clusterSizeRadiusFactor","growthIndicator","networkScale","formationScale","clusterSize","containedNodes","containedEdges","clusterSessions","originalLabel","triggerFunction","groupObj","imageObj","brokenImage","_drawDatabase","_resizeDatabase","_drawBox","_resizeBox","_drawCircle","_resizeCircle","_drawEllipse","_resizeEllipse","_drawImage","_resizeImage","_drawCircularImage","_resizeCircularImage","_drawText","_resizeText","_drawDot","_resizeShape","_drawSquare","_drawTriangle","_drawTriangleDown","_drawStar","_reset","clearSizeCache","_setForce","_addForce","storeState","isFixed","velocity","getDistance","_drawImageAtPosition","globalAlpha","drawImage","_drawImageLabel","getTextSize","_swapToImageResizeWhenImageLoaded","diameter","centerX","centerY","_drawRawCircle","circle","clip","textSize","clusterLineWidth","selectionLineWidth","roundRect","database","defaultSize","ellipse","_drawShape","radiusMultiplier","baseline","labelUnderNode","inView","clearVelocity","updateVelocity","massBeforeClustering","energyBefore","styleAttr","fontFamily","WebkitBorderRadius","whiteSpace","parseDOT","parseGraph","nextPreview","isAlphaNumeric","regexAlphaNumeric","merge","o","addNode","graphs","attr","addEdge","createEdge","getToken","tokenType","TOKENTYPE","NULL","token","isComment","DELIMITER","c2","DELIMITERS","IDENTIFIER","newSyntaxError","UNKNOWN","chop","strict","parseStatements","parseStatement","subgraph","parseSubgraph","parseEdge","parseAttributeStatement","parseNodeStatement","subgraphs","parseAttributeList","message","maxLength","forEach2","array1","array2","elem1","elem2","graphData","dotNode","graphNode","convertEdge","dotEdge","graphEdge","subEdge","{","}","[","]",";","=",",","->","--","gephiJSON","allowedToMove","gEdges","gNodes","gEdge","source","gNode","leftContainer","rightContainer","shadowTop","shadowBottom","shadowTopLeft","shadowBottomLeft","shadowTopRight","shadowBottomRight","_redrawTimer","listeners","events","scrollTopMin","redrawCount","_initAutoResize","component","_stopAutoResize","what","getWindow","borderRootHeight","borderRootWidth","autoHeight","centerWidth","_updateScrollTop","visibilityTop","visibilityBottom","MAX_REDRAWS","repaint","_startAutoResize","_onResize","lastHeight","watchTimer","setInterval","initialScrollTop","oldScrollTop","_getScrollTop","newScrollTop","_setScrollTop","eventType","getTouchList","collectEventData","custom","back","editNode","addDescription","edgeDescription","editEdgeDescription","createEdgeError","deleteClusterError","CanvasRenderingContext2D","square","s2","ir","triangleDown","star","n","r2d","kappa","ox","oy","xe","ye","xm","ym","bezierCurveTo","wEllipse","hEllipse","ymb","yeb","xt","yt","xi","yi","xl","yl","xr","yr","dashArray","dashLength","dashCount","slope","distRemaining","dashIndex","_catmullRom","_linear","dFill","_catmullRomUniform","p0","p1","p2","p3","bp1","bp2","normalization","d1","d2","d3","A","N","M","d3powA","d2powA","d3pow2A","d2pow2A","d1pow2A","d1powA","Bargraph","barCombinedData","coreDistance","drawData","combinedData","intersections","barPoints","_getDataIntersections","heightOffset","_getSafeDrawData","nextKey","amount","resolved","prevKey","accumulated","groupLabel","_getStackedBarYRange","xpos","PhysicsMixin","ClusterMixin","SectorsMixin","SelectionMixin","ManipulationMixin","NavigationMixin","HierarchicalLayoutMixin","_loadMixin","sourceVariable","mixinFunction","_clearMixin","_loadSelectedForceSolver","_loadPhysicsConfiguration","hubThreshold","activeSector","drawingNode","blockConnectingEdgeSelection","forceAppendSelection","manipulationDiv","editModeDiv","closeDiv","_cleanNavigation","_loadNavigationElements","overlay","_onTapOverlay","windowHammer","_hasParent","deactivate","escListener","activate","unbind","_callbacks","once","self","removeListener","removeAllListeners","callbacks","cb","hasListeners","clusterToFit","maxNumberOfNodes","reposition","maxLevels","forceAggregateHubs","normalizeClusterLevels","increaseClusterLevel","repositionNodes","openCluster","isMovingBeforeClustering","_nodeInActiveArea","_sector","_addSector","decreaseClusterLevel","_expandClusterNode","_updateDynamicEdges","updateClusters","zoomDirection","recursive","doNotStart","amountOfNodes","_collapseSector","_formClusters","_openClusters","_openClustersBySize","_aggregateHubs","handleChains","chainPercentage","_getChainFraction","_reduceAmountOfChains","_getHubSize","_formClustersByHub","openAll","containedNodeId","childNode","_expelChildFromParent","_unselectAll","_releaseContainedEdges","_connectEdgeBackToChild","_validateEdges","othersPresent","childNodeId","_repositionBezierNodes","_formClustersByZoom","_forceClustersByZoom","minLength","_addToCluster","_clusterToSmallestNeighbour","smallestNeighbour","smallestNeighbourNode","neighbour","onlyEqual","_formClusterFromHub","hubNode","absorptionSizeOffset","allowCluster","edgesIdarray","amountOfInitialEdges","_addToContainedEdges","_connectEdgeToCluster","_containCircularEdgesFromNode","massBefore","correction","edgeToId","edgeFromId","k","_addToReroutedEdges","maxLevel","minLevel","clusterLevel","targetLevel","average","averageSquared","hubCounter","largestHub","variance","standardDeviation","fraction","reduceAmount","chains","total","_switchToSector","sectorId","sectorType","_switchToActiveSector","_switchToFrozenSector","_switchToSupportSector","_loadLatestSector","_previousSector","_setActiveSector","newId","_forgetLastSector","_createNewSector","_deleteActiveSector","_deleteFrozenSector","_freezeSector","_activateSector","_mergeThisWithFrozen","_collapseThisToSingleCluster","sector","unqiueIdentifier","previousSector","runFunction","argument","returnValues","_doInAllFrozenSectors","_drawSectorNodes","_drawAllSectorNodes","_getNodesOverlappingWith","_getAllNodesOverlappingWith","_pointerToPositionObject","positionObject","_getEdgesOverlappingWith","_getAllEdgesOverlappingWith","_addToSelection","_addToHover","_removeFromSelection","doNotTrigger","_unselectClusters","_getSelectedNodeCount","_getSelectedNode","_getSelectedEdge","_getSelectedEdgeCount","_getSelectedObjectCount","_selectionIsEmpty","_clusterInSelection","_selectConnectedEdges","_hoverConnectedEdges","_unselectConnectedEdges","append","highlightEdges","overrideSelectable","DOM","_manipulationReleaseOverload","_navigationReleaseOverload","getSelectedNodes","edgeIds","getSelectedEdges","idArray","selectNodes","RangeError","selectEdges","_clearManipulatorBar","manipulationDOM","_restoreOverloadedFunctions","functionName","_toggleEditMode","toolbar","boundFunction","edgeBeingEdited","selectedControlNode","_createAddNodeToolbar","_createAddEdgeToolbar","_editNode","_createEditEdgeToolbar","_addNode","_handleConnect","_finishConnect","_selectControlNode","_controlNodeDrag","_releaseControlNode","newNode","_editEdge","alert","supportNodes","targetNode","connectionEdge","connectFromId","_createEdge","defaultData","finalizedData","sourceNodeId","targetNodeId","selectedNodes","selectedEdges","navigationDivs","navigationDivActions","_stopMovement","_zoomExtent","hubsize","definedLevel","undefinedLevel","_changeConstants","_determineLevels","_determineLevelsDirected","distribution","_getDistribution","_placeNodesByHierarchy","minPos","_placeBranchNodes","maxCount","_setLevel","firstNode","_setLevelDirected","config","parentId","parentLevel","nodeMoved","_restoreNodes","__WEBPACK_AMD_DEFINE_FACTORY__","__WEBPACK_AMD_DEFINE_ARRAY__","__WEBPACK_AMD_DEFINE_RESULT__","_exportFunctions","_bound","keydown","keyup","_keys","fromCharCode","code","down","handleEvent","up","keyCode","bound","bindAll","getKey","newBindings","setup","READY","Event","determineEventTypes","Utils","each","gestures","Detection","register","onTouch","DOCUMENT","EVENT_MOVE","detect","EVENT_END","Instance","VERSION","defaults","behavior","userSelect","touchAction","touchCallout","contentZooming","userDrag","tapHighlightColor","HAS_POINTEREVENTS","pointerEnabled","msPointerEnabled","HAS_TOUCHEVENTS","IS_MOBILE","NO_MOUSEEVENTS","CALCULATE_INTERVAL","EVENT_TYPES","DIRECTION_DOWN","DIRECTION_LEFT","DIRECTION_UP","DIRECTION_RIGHT","POINTER_MOUSE","POINTER_TOUCH","POINTER_PEN","EVENT_START","EVENT_RELEASE","EVENT_TOUCH","plugins","utils","dest","handler","iterator","inStr","find","inArray","hasParent","getCenter","getVelocity","deltaTime","getAngle","touch1","touch2","getDirection","getRotation","isVertical","setPrefixedCss","toggle","prefixes","toCamelCase","toggleBehavior","falseFn","onselectstart","ondragstart","str","preventMouseEvents","started","shouldDetect","hook","onTouchHandler","ev","triggerType","srcType","isPointer","isMouse","buttons","PointerEvent","matchType","updatePointer","doDetect","touchList","touchListLength","triggerChange","trigger","changedLength","changedTouches","evData","identifiers","identifier","pointerType","timeStamp","preventManipulation","stopDetect","pointers","touchlist","pointerEvent","pointerId","pt","MSPOINTER_TYPE_MOUSE","MSPOINTER_TYPE_TOUCH","MSPOINTER_TYPE_PEN","detection","stopped","startDetect","inst","eventData","startEvent","lastEvent","lastCalcEvent","futureCalcEvent","lastCalcData","extendEventData","instOptions","getCalculatedData","recalc","calcEv","calcData","velocityX","velocityY","interimAngle","interimDirection","startEv","lastEv","rotation","eventStartHandler","eventHandlers","createEvent","initEvent","dispatchEvent","state","eh","dragGesture","dragMaxTouches","triggered","dragMinDistance","startCenter","dragDistanceCorrection","dragLockToAxis","dragLockMinDistance","lastDirection","dragBlockVertical","dragBlockHorizontal","Drag","Gesture","holdGesture","holdTimeout","holdThreshold","Hold","Release","Swipe","swipeMinTouches","swipeMaxTouches","swipeVelocityX","swipeVelocityY","tapGesture","sincePrev","didDoubleTap","hasMoved","tapMaxDistance","tapMaxTime","doubleTapInterval","doubleTapDistance","tapAlways","Tap","Touch","preventMouse","transformGesture","scaleThreshold","rotationThreshold","transformMinScale","transformMinRotation","Transform","global","dfl","hasOwnProp","defaultParsingFlags","empty","unusedTokens","unusedInput","charsLeftOver","nullInput","invalidMonth","invalidFormat","userInvalidated","iso","printMsg","msg","suppressDeprecationWarnings","warn","deprecate","firstTime","deprecateSimple","deprecations","padToken","func","leftZeroFill","ordinalizeToken","period","localeData","ordinal","monthDiff","anchor2","adjust","wholeMonthDiff","meridiemFixWrap","meridiem","isPm","meridiemHour","isPM","Locale","Moment","skipOverflow","checkOverflow","copyConfig","updateInProgress","updateOffset","Duration","normalizedInput","normalizeObjectUnits","years","quarters","quarter","months","weeks","week","days","_milliseconds","_days","_months","_locale","_bubble","val","_isAMomentObject","_i","_f","_l","_strict","_tzm","_isUTC","_offset","_pf","momentProperties","absRound","number","targetLength","forceSign","output","positiveMomentsDifference","base","res","isAfter","momentsDifference","makeAs","isBefore","createAdder","dur","tmp","addOrSubtractDurationFromMoment","mom","isAdding","setTime","rawSetter","rawGetter","rawMonthSetter","input","compareArrays","dontConvert","lengthDiff","diffs","toInt","normalizeUnits","units","lowered","unitAliases","camelFunctions","inputObject","normalizedProp","makeList","setter","getter","results","utc","set","argumentForCoercion","coercedNumber","isFinite","daysInMonth","UTC","getUTCDate","weeksInYear","dow","doy","weekOfYear","daysInYear","isLeapYear","_a","MONTH","DATE","YEAR","HOUR","MINUTE","SECOND","MILLISECOND","_overflowDayOfYear","isValid","_isValid","getTime","bigHour","normalizeLocale","chooseLocale","names","loadLocale","oldLocale","hasModule","model","local","removeFormattingTokens","makeFormatFunction","formattingTokens","formatTokenFunctions","formatMoment","expandFormat","formatFunctions","invalidDate","replaceLongDateFormatTokens","longDateFormat","localFormattingTokens","lastIndex","getParseRegexForToken","parseTokenOneDigit","parseTokenThreeDigits","parseTokenFourDigits","parseTokenOneToFourDigits","parseTokenSignedNumber","parseTokenSixDigits","parseTokenOneToSixDigits","parseTokenTwoDigits","parseTokenOneToThreeDigits","parseTokenWord","_meridiemParse","parseTokenOffsetMs","parseTokenTimestampMs","parseTokenTimezone","parseTokenT","parseTokenDigits","parseTokenOneOrTwoDigits","_ordinalParse","_ordinalParseLenient","RegExp","regexpEscape","unescapeFormat","utcOffsetFromString","string","possibleTzMatches","tzChunk","parseTimezoneChunker","addTimeToArrayFromToken","datePartArray","monthsParse","_dayOfYear","parseTwoDigitYear","_meridiem","_useUTC","weekdaysParse","_w","invalidWeekday","dayOfYearFromWeekInfo","weekYear","temp","GG","W","E","_week","gg","dayOfYearFromWeeks","dateFromConfig","currentDate","yearToUse","currentDateArray","makeUTCDate","getUTCMonth","_nextDay","makeDate","setUTCMinutes","getUTCMinutes","dateFromObject","getUTCFullYear","makeDateFromStringAndFormat","ISO_8601","parseISO","parsedInput","tokens","skipped","stringLength","totalParsedInputLength","matched","p4","makeDateFromStringAndArray","tempConfig","bestMoment","scoreToBeat","currentScore","NaN","score","l","isoRegex","isoDates","isoTimes","makeDateFromString","createFromInputFallback","makeDateFromInput","aspNetJsonRegex","ms","setUTCFullYear","parseWeekday","substituteTimeAgo","withoutSuffix","isFuture","relativeTime","posNegDuration","relativeTimeThresholds","firstDayOfWeek","firstDayOfWeekOfYear","adjustedMoment","daysToDayOfWeek","daysToAdd","getUTCDay","makeMoment","invalid","preparse","pickBy","moments","dayOfMonth","unit","makeAccessor","keepTime","daysToYears","yearsToDays","makeDurationGetter","makeGlobal","shouldDeprecate","ender","oldGlobalMoment","globalScope","aspNetTimeSpanJsonRegex","isoDurationRegex","isoFormat","unitMillisecondFactors","Milliseconds","Seconds","Minutes","Hours","Days","Months","Years","D","Q","DDD","dayofyear","isoweekday","isoweek","weekyear","isoweekyear","ordinalizeTokens","paddedTokens","MMM","monthsShort","MMMM","dd","weekdaysMin","ddd","weekdaysShort","dddd","weekdays","isoWeek","YY","YYYY","YYYYY","YYYYYY","gggg","ggggg","isoWeekYear","GGGG","GGGGG","isoWeekday","SS","SSS","SSSS","Z","utcOffset","ZZ","zoneAbbr","zz","zoneName","unix","lists","DDDD","_monthsShort","monthName","regex","_monthsParse","_longMonthsParse","_shortMonthsParse","_weekdays","_weekdaysShort","_weekdaysMin","weekdayName","_weekdaysParse","_longDateFormat","LTS","LT","L","LL","LLL","LLLL","isLower","_calendar","sameDay","nextDay","nextWeek","lastDay","lastWeek","sameElse","calendar","_relativeTime","future","past","mm","hh","MM","yy","pastFuture","_ordinal","postformat","firstDayOfYear","_invalidDate","ret","parseIso","diffRes","isDuration","inp","version","relativeTimeThreshold","limit","defineLocale","_abbr","abbr","langData","flags","parseZone","isDSTShifted","parsingFlags","invalidAt","keepLocalTime","_dateUtcOffset","inputString","asFloat","that","zoneDiff","humanize","fromNow","sod","startOf","isDST","getDay","endOf","inputMs","isBetween","zone","localAdjust","_changeInProgress","isLocal","isUtcOffset","isUtc","hasAlignedHourOffset","isoWeeksInYear","weekInfo","newLocaleData","getTimezoneOffset","isoWeeks","toJSON","isUTC","withSuffix","toIsoString","asSeconds","asMilliseconds","asMinutes","asHours","asDays","asWeeks","asMonths","asYears","ordinalParse","require","noGlobal","graphToggleSmoothCurves","graph_toggleSmooth","getElementById","graphRepositionNodes","showValueOfRange","graphGenerateOptions","optionsSpecific","radioButton1","radioButton2","checked","backupConstants","optionsDiv","switchConfigurations","radioButton","querySelector","tableId","table","constantsVariableName","valueId","rangeValue","_overWriteGraphConstants","RepulsionMixin","HierarchialRepulsionMixin","BarnesHutMixin","_toggleBarnesHut","barnesHutTree","_initializeForceCalculation","_calculateForces","_calculateGravitationalForces","_calculateNodeForces","_calculateSpringForcesWithSupport","_calculateHierarchicalSpringForces","_calculateSpringForces","supportNodeId","gravity","gravityForce","edgeLength","springForce","combinedClusterSize","node1","node2","node3","_calculateSpringForce","physicsConfiguration","hierarchicalLayoutDirections","parentElement","rangeElement","radioButton3","graph_repositionNodes","graph_generateOptions","dynamicSmoothCurves","nameArray","webpackContext","req","resolve","repulsingForce","a_base","minimumDistance","steepness","springFx","springFy","totalFx","totalFy","correctionFx","correctionFy","nodeCount","_formBarnesHutTree","_getForceContribution","children","NW","NE","SW","SE","parentBranch","childrenCount","centerOfMass","calcSize","MAX_VALUE","sizeDiff","minimumTreeSize","rootSize","halfRootSize","_splitBranch","_placeInTree","_updateBranchMass","totalMass","totalMassInv","biggestSize","skipMassUpdate","_placeInRegion","region","containedNode","_insertRegion","childSize","_drawTree","_drawBranch","branch","webpackPolyfill","paths"],"mappings":"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"} \ No newline at end of file diff --git a/dist/vis.min.js b/dist/vis.min.js index df8b687b..32e0656e 100644 --- a/dist/vis.min.js +++ b/dist/vis.min.js @@ -5,7 +5,7 @@ * A dynamic, browser-based visualization library. * * @version 3.9.2-SNAPSHOT - * @date 2015-01-23 + * @date 2015-02-02 * * @license * Copyright (C) 2011-2014 Almende B.V, http://almende.com @@ -22,18 +22,18 @@ * * Vis.js may be distributed under either license. */ -"use strict";!function(t,e){"object"==typeof exports&&"object"==typeof module?module.exports=e():"function"==typeof define&&define.amd?define(e):"object"==typeof exports?exports.vis=e():t.vis=e()}(this,function(){return function(t){function e(s){if(i[s])return i[s].exports;var o=i[s]={exports:{},id:s,loaded:!1};return t[s].call(o.exports,o,o.exports,e),o.loaded=!0,o.exports}var i={};return e.m=t,e.c=i,e.p="",e(0)}([function(t,e,i){e.util=i(1),e.DOMutil=i(2),e.DataSet=i(3),e.DataView=i(4),e.Queue=i(5),e.Graph3d=i(6),e.graph3d={Camera:i(7),Filter:i(8),Point2d:i(9),Point3d:i(10),Slider:i(11),StepNumber:i(12)},e.Timeline=i(13),e.Graph2d=i(14),e.timeline={DateUtil:i(15),DataStep:i(16),Range:i(17),stack:i(18),TimeStep:i(19),components:{items:{Item:i(20),BackgroundItem:i(21),BoxItem:i(22),PointItem:i(23),RangeItem:i(24)},Component:i(25),CurrentTime:i(26),CustomTime:i(27),DataAxis:i(28),GraphGroup:i(29),Group:i(30),BackgroundGroup:i(31),ItemSet:i(32),Legend:i(33),LineGraph:i(34),TimeAxis:i(35)}},e.Network=i(36),e.network={Edge:i(37),Groups:i(38),Images:i(39),Node:i(40),Popup:i(41),dotparser:i(42),gephiParser:i(43)},e.Graph=function(){throw new Error("Graph is renamed to Network. Please create a graph as new vis.Network(...)")},e.moment=i(44),e.hammer=i(45)},function(t,e,i){var s=i(44);e.isNumber=function(t){return t instanceof Number||"number"==typeof t},e.isString=function(t){return t instanceof String||"string"==typeof t},e.isDate=function(t){if(t instanceof Date)return!0;if(e.isString(t)){var i=o.exec(t);if(i)return!0;if(!isNaN(Date.parse(t)))return!0}return!1},e.isDataTable=function(t){return"undefined"!=typeof google&&google.visualization&&google.visualization.DataTable&&t instanceof google.visualization.DataTable},e.randomUUID=function(){var t=function(){return Math.floor(65536*Math.random()).toString(16)};return t()+t()+"-"+t()+"-"+t()+"-"+t()+"-"+t()+t()+t()},e.extend=function(t){for(var e=1,i=arguments.length;i>e;e++){var s=arguments[e];for(var o in s)s.hasOwnProperty(o)&&(t[o]=s[o])}return t},e.selectiveExtend=function(t,e){if(!Array.isArray(t))throw new Error("Array with property names expected as first argument");for(var i=2;ii;i++)if(t[i]!=e[i])return!1;return!0},e.convert=function(t,i){var n;if(void 0===t)return void 0;if(null===t)return null;if(!i)return t;if("string"!=typeof i&&!(i instanceof String))throw new Error("Type must be a string");switch(i){case"boolean":case"Boolean":return Boolean(t);case"number":case"Number":return Number(t.valueOf());case"string":case"String":return String(t);case"Date":if(e.isNumber(t))return new Date(t);if(t instanceof Date)return new Date(t.valueOf());if(s.isMoment(t))return new Date(t.valueOf());if(e.isString(t))return n=o.exec(t),n?new Date(Number(n[1])):s(t).toDate();throw new Error("Cannot convert object of type "+e.getType(t)+" to type Date");case"Moment":if(e.isNumber(t))return s(t);if(t instanceof Date)return s(t.valueOf());if(s.isMoment(t))return s(t);if(e.isString(t))return n=o.exec(t),s(n?Number(n[1]):t);throw new Error("Cannot convert object of type "+e.getType(t)+" to type Date");case"ISODate":if(e.isNumber(t))return new Date(t);if(t instanceof Date)return t.toISOString();if(s.isMoment(t))return t.toDate().toISOString();if(e.isString(t))return n=o.exec(t),n?new Date(Number(n[1])).toISOString():new Date(t).toISOString();throw new Error("Cannot convert object of type "+e.getType(t)+" to type ISODate");case"ASPDate":if(e.isNumber(t))return"/Date("+t+")/";if(t instanceof Date)return"/Date("+t.valueOf()+")/";if(e.isString(t)){n=o.exec(t);var r;return r=n?new Date(Number(n[1])).valueOf():new Date(t).valueOf(),"/Date("+r+")/"}throw new Error("Cannot convert object of type "+e.getType(t)+" to type ASPDate");default:throw new Error('Unknown type "'+i+'"')}};var o=/^\/?Date\((\-?\d+)/i;e.getType=function(t){var e=typeof t;return"object"==e?null==t?"null":t instanceof Boolean?"Boolean":t instanceof Number?"Number":t instanceof String?"String":Array.isArray(t)?"Array":t instanceof Date?"Date":"Object":"number"==e?"Number":"boolean"==e?"Boolean":"string"==e?"String":e},e.getAbsoluteLeft=function(t){return t.getBoundingClientRect().left+window.pageXOffset},e.getAbsoluteTop=function(t){return t.getBoundingClientRect().top+window.pageYOffset},e.addClassName=function(t,e){var i=t.className.split(" ");-1==i.indexOf(e)&&(i.push(e),t.className=i.join(" "))},e.removeClassName=function(t,e){var i=t.className.split(" "),s=i.indexOf(e);-1!=s&&(i.splice(s,1),t.className=i.join(" "))},e.forEach=function(t,e){var i,s;if(Array.isArray(t))for(i=0,s=t.length;s>i;i++)e(t[i],i,t);else for(i in t)t.hasOwnProperty(i)&&e(t[i],i,t)},e.toArray=function(t){var e=[];for(var i in t)t.hasOwnProperty(i)&&e.push(t[i]);return e},e.updateProperty=function(t,e,i){return t[e]!==i?(t[e]=i,!0):!1},e.addEventListener=function(t,e,i,s){t.addEventListener?(void 0===s&&(s=!1),"mousewheel"===e&&navigator.userAgent.indexOf("Firefox")>=0&&(e="DOMMouseScroll"),t.addEventListener(e,i,s)):t.attachEvent("on"+e,i)},e.removeEventListener=function(t,e,i,s){t.removeEventListener?(void 0===s&&(s=!1),"mousewheel"===e&&navigator.userAgent.indexOf("Firefox")>=0&&(e="DOMMouseScroll"),t.removeEventListener(e,i,s)):t.detachEvent("on"+e,i)},e.preventDefault=function(t){t||(t=window.event),t.preventDefault?t.preventDefault():t.returnValue=!1},e.getTarget=function(t){t||(t=window.event);var e;return t.target?e=t.target:t.srcElement&&(e=t.srcElement),void 0!=e.nodeType&&3==e.nodeType&&(e=e.parentNode),e},e.option={},e.option.asBoolean=function(t,e){return"function"==typeof t&&(t=t()),null!=t?0!=t:e||null},e.option.asNumber=function(t,e){return"function"==typeof t&&(t=t()),null!=t?Number(t)||e||null:e||null},e.option.asString=function(t,e){return"function"==typeof t&&(t=t()),null!=t?String(t):e||null},e.option.asSize=function(t,i){return"function"==typeof t&&(t=t()),e.isString(t)?t:e.isNumber(t)?t+"px":i||null},e.option.asElement=function(t,e){return"function"==typeof t&&(t=t()),t||e||null},e.hexToRGB=function(t){var e=/^#?([a-f\d])([a-f\d])([a-f\d])$/i;t=t.replace(e,function(t,e,i,s){return e+e+i+i+s+s});var i=/^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(t);return i?{r:parseInt(i[1],16),g:parseInt(i[2],16),b:parseInt(i[3],16)}:null},e.RGBToHex=function(t,e,i){return"#"+((1<<24)+(t<<16)+(e<<8)+i).toString(16).slice(1)},e.parseColor=function(t){var i;if(e.isString(t)){if(e.isValidRGB(t)){var s=t.substr(4).substr(0,t.length-5).split(",");t=e.RGBToHex(s[0],s[1],s[2])}if(e.isValidHex(t)){var o=e.hexToHSV(t),n={h:o.h,s:.45*o.s,v:Math.min(1,1.05*o.v)},r={h:o.h,s:Math.min(1,1.25*o.v),v:.6*o.v},a=e.HSVToHex(r.h,r.h,r.v),h=e.HSVToHex(n.h,n.s,n.v);i={background:t,border:a,highlight:{background:h,border:a},hover:{background:h,border:a}}}else i={background:t,border:t,highlight:{background:t,border:t},hover:{background:t,border:t}}}else i={},i.background=t.background||"white",i.border=t.border||i.background,e.isString(t.highlight)?i.highlight={border:t.highlight,background:t.highlight}:(i.highlight={},i.highlight.background=t.highlight&&t.highlight.background||i.background,i.highlight.border=t.highlight&&t.highlight.border||i.border),e.isString(t.hover)?i.hover={border:t.hover,background:t.hover}:(i.hover={},i.hover.background=t.hover&&t.hover.background||i.background,i.hover.border=t.hover&&t.hover.border||i.border);return i},e.RGBToHSV=function(t,e,i){t/=255,e/=255,i/=255;var s=Math.min(t,Math.min(e,i)),o=Math.max(t,Math.max(e,i));if(s==o)return{h:0,s:0,v:s};var n=t==s?e-i:i==s?t-e:i-t,r=t==s?3:i==s?1:5,a=60*(r-n/(o-s))/360,h=(o-s)/o,d=o;return{h:a,s:h,v:d}};var n={split:function(t){var e={};return t.split(";").forEach(function(t){if(""!=t.trim()){var i=t.split(":"),s=i[0].trim(),o=i[1].trim();e[s]=o}}),e},join:function(t){return Object.keys(t).map(function(e){return e+": "+t[e]}).join("; ")}};e.addCssText=function(t,i){var s=n.split(t.style.cssText),o=n.split(i),r=e.extend(s,o);t.style.cssText=n.join(r)},e.removeCssText=function(t,e){var i=n.split(t.style.cssText),s=n.split(e);for(var o in s)s.hasOwnProperty(o)&&delete i[o];t.style.cssText=n.join(i)},e.HSVToRGB=function(t,e,i){var s,o,n,r=Math.floor(6*t),a=6*t-r,h=i*(1-e),d=i*(1-a*e),l=i*(1-(1-a)*e);switch(r%6){case 0:s=i,o=l,n=h;break;case 1:s=d,o=i,n=h;break;case 2:s=h,o=i,n=l;break;case 3:s=h,o=d,n=i;break;case 4:s=l,o=h,n=i;break;case 5:s=i,o=h,n=d}return{r:Math.floor(255*s),g:Math.floor(255*o),b:Math.floor(255*n)}},e.HSVToHex=function(t,i,s){var o=e.HSVToRGB(t,i,s);return e.RGBToHex(o.r,o.g,o.b)},e.hexToHSV=function(t){var i=e.hexToRGB(t);return e.RGBToHSV(i.r,i.g,i.b)},e.isValidHex=function(t){var e=/(^#[0-9A-F]{6}$)|(^#[0-9A-F]{3}$)/i.test(t);return e},e.isValidRGB=function(t){t=t.replace(" ","");var e=/rgb\((\d{1,3}),(\d{1,3}),(\d{1,3})\)/i.test(t);return e},e.selectiveBridgeObject=function(t,i){if("object"==typeof i){for(var s=Object.create(i),o=0;o=r&&o>n;){var h=Math.floor((r+a)/2),d=t[h],l=void 0===s?d[i]:d[i][s],c=e(l);if(0==c)return h;-1==c?r=h+1:a=h-1,n++}return-1},e.binarySearchValue=function(t,e,i,s){for(var o,n,r,a,h=1e4,d=0,l=0,c=t.length-1;c>=l&&h>d;){if(a=Math.floor(.5*(c+l)),o=t[Math.max(0,a-1)][i],n=t[a][i],r=t[Math.min(t.length-1,a+1)][i],n==e)return a;if(e>o&&n>e)return"before"==s?Math.max(0,a-1):a;if(e>n&&r>e)return"before"==s?a:Math.min(t.length-1,a+1);e>n?l=a+1:c=a-1,d++}return-1},e.easeInOutQuad=function(t,e,i,s){var o=i-e;return t/=s/2,1>t?o/2*t*t+e:(t--,-o/2*(t*(t-2)-1)+e)},e.easingFunctions={linear:function(t){return t},easeInQuad:function(t){return t*t},easeOutQuad:function(t){return t*(2-t)},easeInOutQuad:function(t){return.5>t?2*t*t:-1+(4-2*t)*t},easeInCubic:function(t){return t*t*t},easeOutCubic:function(t){return--t*t*t+1},easeInOutCubic:function(t){return.5>t?4*t*t*t:(t-1)*(2*t-2)*(2*t-2)+1},easeInQuart:function(t){return t*t*t*t},easeOutQuart:function(t){return 1- --t*t*t*t},easeInOutQuart:function(t){return.5>t?8*t*t*t*t:1-8*--t*t*t*t},easeInQuint:function(t){return t*t*t*t*t},easeOutQuint:function(t){return 1+--t*t*t*t*t},easeInOutQuint:function(t){return.5>t?16*t*t*t*t*t:1+16*--t*t*t*t*t}}},function(t,e){e.prepareElements=function(t){for(var e in t)t.hasOwnProperty(e)&&(t[e].redundant=t[e].used,t[e].used=[])},e.cleanupElements=function(t){for(var e in t)if(t.hasOwnProperty(e)&&t[e].redundant){for(var i=0;i0?(s=e[t].redundant[0],e[t].redundant.shift()):(s=document.createElementNS("http://www.w3.org/2000/svg",t),i.appendChild(s)):(s=document.createElementNS("http://www.w3.org/2000/svg",t),e[t]={used:[],redundant:[]},i.appendChild(s)),e[t].used.push(s),s},e.getDOMElement=function(t,e,i,s){var o;return e.hasOwnProperty(t)?e[t].redundant.length>0?(o=e[t].redundant[0],e[t].redundant.shift()):(o=document.createElement(t),void 0!==s?i.insertBefore(o,s):i.appendChild(o)):(o=document.createElement(t),e[t]={used:[],redundant:[]},void 0!==s?i.insertBefore(o,s):i.appendChild(o)),e[t].used.push(o),o},e.drawPoint=function(t,i,s,o,n){var r;return"circle"==s.options.drawPoints.style?(r=e.getSVGElement("circle",o,n),r.setAttributeNS(null,"cx",t),r.setAttributeNS(null,"cy",i),r.setAttributeNS(null,"r",.5*s.options.drawPoints.size)):(r=e.getSVGElement("rect",o,n),r.setAttributeNS(null,"x",t-.5*s.options.drawPoints.size),r.setAttributeNS(null,"y",i-.5*s.options.drawPoints.size),r.setAttributeNS(null,"width",s.options.drawPoints.size),r.setAttributeNS(null,"height",s.options.drawPoints.size)),void 0!==s.options.drawPoints.styles&&r.setAttributeNS(null,"style",s.group.options.drawPoints.styles),r.setAttributeNS(null,"class",s.className+" point"),r},e.drawBar=function(t,i,s,o,n,r,a){if(0!=o){0>o&&(o*=-1,i-=o);var h=e.getSVGElement("rect",r,a);h.setAttributeNS(null,"x",t-.5*s),h.setAttributeNS(null,"y",i),h.setAttributeNS(null,"width",s),h.setAttributeNS(null,"height",o),h.setAttributeNS(null,"class",n)}}},function(t,e,i){function s(t,e){if(!t||Array.isArray(t)||o.isDataTable(t)||(e=t,t=null),this._options=e||{},this._data={},this.length=0,this._fieldId=this._options.fieldId||"id",this._type={},this._options.type)for(var i in this._options.type)if(this._options.type.hasOwnProperty(i)){var s=this._options.type[i];this._type[i]="Date"==s||"ISODate"==s||"ASPDate"==s?"Date":s}if(this._options.convert)throw new Error('Option "convert" is deprecated. Use "type" instead.');this._subscribers={},t&&this.add(t),this.setOptions(e)}var o=i(1),n=i(5);s.prototype.setOptions=function(t){t&&void 0!==t.queue&&(t.queue===!1?this._queue&&(this._queue.destroy(),delete this._queue):(this._queue||(this._queue=n.extend(this,{replace:["add","update","remove"]})),"object"==typeof t.queue&&this._queue.setOptions(t.queue)))},s.prototype.on=function(t,e){var i=this._subscribers[t];i||(i=[],this._subscribers[t]=i),i.push({callback:e})},s.prototype.subscribe=s.prototype.on,s.prototype.off=function(t,e){var i=this._subscribers[t];i&&(this._subscribers[t]=i.filter(function(t){return t.callback!=e}))},s.prototype.unsubscribe=s.prototype.off,s.prototype._trigger=function(t,e,i){if("*"==t)throw new Error("Cannot trigger event *");var s=[];t in this._subscribers&&(s=s.concat(this._subscribers[t])),"*"in this._subscribers&&(s=s.concat(this._subscribers["*"]));for(var o=0;or;r++)i=n._addItem(t[r]),s.push(i);else if(o.isDataTable(t))for(var h=this._getColumnNames(t),d=0,l=t.getNumberOfRows();l>d;d++){for(var c={},p=0,u=h.length;u>p;p++){var m=h[p];c[m]=t.getValue(d,p)}i=n._addItem(c),s.push(i)}else{if(!(t instanceof Object))throw new Error("Unknown dataType");i=n._addItem(t),s.push(i)}return s.length&&this._trigger("add",{items:s},e),s},s.prototype.update=function(t,e){var i=[],s=[],n=[],r=this,a=r._fieldId,h=function(t){var e=t[a];r._data[e]?(e=r._updateItem(t),s.push(e),n.push(t)):(e=r._addItem(t),i.push(e))};if(Array.isArray(t))for(var d=0,l=t.length;l>d;d++)h(t[d]);else if(o.isDataTable(t))for(var c=this._getColumnNames(t),p=0,u=t.getNumberOfRows();u>p;p++){for(var m={},f=0,g=c.length;g>f;f++){var v=c[f];m[v]=t.getValue(p,f)}h(m)}else{if(!(t instanceof Object))throw new Error("Unknown dataType");h(t)}return i.length&&this._trigger("add",{items:i},e),s.length&&this._trigger("update",{items:s,data:n},e),i.concat(s)},s.prototype.get=function(){var t,e,i,s,n=this,r=o.getType(arguments[0]);"String"==r||"Number"==r?(t=arguments[0],i=arguments[1],s=arguments[2]):"Array"==r?(e=arguments[0],i=arguments[1],s=arguments[2]):(i=arguments[0],s=arguments[1]);var a;if(i&&i.returnType){var h=["DataTable","Array","Object"];if(a=-1==h.indexOf(i.returnType)?"Array":i.returnType,s&&a!=o.getType(s))throw new Error('Type of parameter "data" ('+o.getType(s)+") does not correspond with specified options.type ("+i.type+")");if("DataTable"==a&&!o.isDataTable(s))throw new Error('Parameter "data" must be a DataTable when options.type is "DataTable"')}else a=s&&"DataTable"==o.getType(s)?"DataTable":"Array";var d,l,c,p,u=i&&i.type||this._options.type,m=i&&i.filter,f=[];if(void 0!=t)d=n._getItem(t,u),m&&!m(d)&&(d=null);else if(void 0!=e)for(c=0,p=e.length;p>c;c++)d=n._getItem(e[c],u),(!m||m(d))&&f.push(d);else for(l in this._data)this._data.hasOwnProperty(l)&&(d=n._getItem(l,u),(!m||m(d))&&f.push(d));if(i&&i.order&&void 0==t&&this._sort(f,i.order),i&&i.fields){var g=i.fields;if(void 0!=t)d=this._filterFields(d,g);else for(c=0,p=f.length;p>c;c++)f[c]=this._filterFields(f[c],g)}if("DataTable"==a){var v=this._getColumnNames(s);if(void 0!=t)n._appendRow(s,v,d);else for(c=0;cc;c++)s.push(f[c]);return s}return f},s.prototype.getIds=function(t){var e,i,s,o,n,r=this._data,a=t&&t.filter,h=t&&t.order,d=t&&t.type||this._options.type,l=[];if(a)if(h){n=[];for(s in r)r.hasOwnProperty(s)&&(o=this._getItem(s,d),a(o)&&n.push(o));for(this._sort(n,h),e=0,i=n.length;i>e;e++)l[e]=n[e][this._fieldId]}else for(s in r)r.hasOwnProperty(s)&&(o=this._getItem(s,d),a(o)&&l.push(o[this._fieldId]));else if(h){n=[];for(s in r)r.hasOwnProperty(s)&&n.push(r[s]);for(this._sort(n,h),e=0,i=n.length;i>e;e++)l[e]=n[e][this._fieldId]}else for(s in r)r.hasOwnProperty(s)&&(o=r[s],l.push(o[this._fieldId]));return l},s.prototype.getDataSet=function(){return this},s.prototype.forEach=function(t,e){var i,s,o=e&&e.filter,n=e&&e.type||this._options.type,r=this._data;if(e&&e.order)for(var a=this.get(e),h=0,d=a.length;d>h;h++)i=a[h],s=i[this._fieldId],t(i,s);else for(s in r)r.hasOwnProperty(s)&&(i=this._getItem(s,n),(!o||o(i))&&t(i,s))},s.prototype.map=function(t,e){var i,s=e&&e.filter,o=e&&e.type||this._options.type,n=[],r=this._data;for(var a in r)r.hasOwnProperty(a)&&(i=this._getItem(a,o),(!s||s(i))&&n.push(t(i,a)));return e&&e.order&&this._sort(n,e.order),n},s.prototype._filterFields=function(t,e){var i={};for(var s in t)t.hasOwnProperty(s)&&-1!=e.indexOf(s)&&(i[s]=t[s]);return i},s.prototype._sort=function(t,e){if(o.isString(e)){var i=e;t.sort(function(t,e){var s=t[i],o=e[i];return s>o?1:o>s?-1:0})}else{if("function"!=typeof e)throw new TypeError("Order must be a function or a string");t.sort(e)}},s.prototype.remove=function(t,e){var i,s,o,n=[];if(Array.isArray(t))for(i=0,s=t.length;s>i;i++)o=this._remove(t[i]),null!=o&&n.push(o);else o=this._remove(t),null!=o&&n.push(o);return n.length&&this._trigger("remove",{items:n},e),n},s.prototype._remove=function(t){if(o.isNumber(t)||o.isString(t)){if(this._data[t])return delete this._data[t],this.length--,t}else if(t instanceof Object){var e=t[this._fieldId];if(e&&this._data[e])return delete this._data[e],this.length--,e}return null},s.prototype.clear=function(t){var e=Object.keys(this._data);return this._data={},this.length=0,this._trigger("remove",{items:e},t),e},s.prototype.max=function(t){var e=this._data,i=null,s=null;for(var o in e)if(e.hasOwnProperty(o)){var n=e[o],r=n[t];null!=r&&(!i||r>s)&&(i=n,s=r)}return i},s.prototype.min=function(t){var e=this._data,i=null,s=null;for(var o in e)if(e.hasOwnProperty(o)){var n=e[o],r=n[t];null!=r&&(!i||s>r)&&(i=n,s=r)}return i},s.prototype.distinct=function(t){var e,i=this._data,s=[],n=this._options.type&&this._options.type[t]||null,r=0;for(var a in i)if(i.hasOwnProperty(a)){var h=i[a],d=h[t],l=!1;for(e=0;r>e;e++)if(s[e]==d){l=!0;break}l||void 0===d||(s[r]=d,r++)}if(n)for(e=0;ei;i++)e[i]=t.getColumnId(i)||t.getColumnLabel(i);return e},s.prototype._appendRow=function(t,e,i){for(var s=t.addRow(),o=0,n=e.length;n>o;o++){var r=e[o];t.setValue(s,o,i[r])}},t.exports=s},function(t,e,i){function s(t,e){this._data=null,this._ids={},this.length=0,this._options=e||{},this._fieldId="id",this._subscribers={};var i=this;this.listener=function(){i._onEvent.apply(i,arguments)},this.setData(t)}var o=i(1),n=i(3);s.prototype.setData=function(t){var e,i,s;if(this._data){this._data.unsubscribe&&this._data.unsubscribe("*",this.listener),e=[];for(var o in this._ids)this._ids.hasOwnProperty(o)&&e.push(o);this._ids={},this.length=0,this._trigger("remove",{items:e})}if(this._data=t,this._data){for(this._fieldId=this._options.fieldId||this._data&&this._data.options&&this._data.options.fieldId||"id",e=this._data.getIds({filter:this._options&&this._options.filter}),i=0,s=e.length;s>i;i++)o=e[i],this._ids[o]=!0;this.length=e.length,this._trigger("add",{items:e}),this._data.on&&this._data.on("*",this.listener)}},s.prototype.get=function(){var t,e,i,s=this,n=o.getType(arguments[0]);"String"==n||"Number"==n||"Array"==n?(t=arguments[0],e=arguments[1],i=arguments[2]):(e=arguments[0],i=arguments[1]);var r=o.extend({},this._options,e);this._options.filter&&e&&e.filter&&(r.filter=function(t){return s._options.filter(t)&&e.filter(t)});var a=[];return void 0!=t&&a.push(t),a.push(r),a.push(i),this._data&&this._data.get.apply(this._data,a)},s.prototype.getIds=function(t){var e;if(this._data){var i,s=this._options.filter;i=t&&t.filter?s?function(e){return s(e)&&t.filter(e)}:t.filter:s,e=this._data.getIds({filter:i,order:t&&t.order})}else e=[];return e},s.prototype.getDataSet=function(){for(var t=this;t instanceof s;)t=t._data;return t||null},s.prototype._onEvent=function(t,e,i){var s,o,n,r,a=e&&e.items,h=this._data,d=[],l=[],c=[];if(a&&h){switch(t){case"add":for(s=0,o=a.length;o>s;s++)n=a[s],r=this.get(n),r&&(this._ids[n]=!0,d.push(n));break;case"update":for(s=0,o=a.length;o>s;s++)n=a[s],r=this.get(n),r?this._ids[n]?l.push(n):(this._ids[n]=!0,d.push(n)):this._ids[n]&&(delete this._ids[n],c.push(n));break;case"remove":for(s=0,o=a.length;o>s;s++)n=a[s],this._ids[n]&&(delete this._ids[n],c.push(n))}this.length+=d.length-c.length,d.length&&this._trigger("add",{items:d},i),l.length&&this._trigger("update",{items:l},i),c.length&&this._trigger("remove",{items:c},i)}},s.prototype.on=n.prototype.on,s.prototype.off=n.prototype.off,s.prototype._trigger=n.prototype._trigger,s.prototype.subscribe=s.prototype.on,s.prototype.unsubscribe=s.prototype.off,t.exports=s},function(t){function e(t){this.delay=null,this.max=1/0,this._queue=[],this._timeout=null,this._extended=null,this.setOptions(t)}e.prototype.setOptions=function(t){t&&"undefined"!=typeof t.delay&&(this.delay=t.delay),t&&"undefined"!=typeof t.max&&(this.max=t.max),this._flushIfNeeded()},e.extend=function(t,i){var s=new e(i);if(void 0!==t.flush)throw new Error("Target object already has a property flush");t.flush=function(){s.flush()};var o=[{name:"flush",original:void 0}];if(i&&i.replace)for(var n=0;nthis.max&&this.flush(),clearTimeout(this._timeout),this.queue.length>0&&"number"==typeof this.delay){var t=this;this._timeout=setTimeout(function(){t.flush()},this.delay)}},e.prototype.flush=function(){for(;this._queue.length>0;){var t=this._queue.shift();t.fn.apply(t.context||t.fn,t.args||[])}},t.exports=e},function(t,e,i){function s(t,e,i){if(!(this instanceof s))throw new SyntaxError("Constructor must be called with the new operator");this.containerElement=t,this.width="400px",this.height="400px",this.margin=10,this.defaultXCenter="55%",this.defaultYCenter="50%",this.xLabel="x",this.yLabel="y",this.zLabel="z";var o=function(t){return t};this.xValueLabel=o,this.yValueLabel=o,this.zValueLabel=o,this.filterLabel="time",this.legendLabel="value",this.style=s.STYLE.DOT,this.showPerspective=!0,this.showGrid=!0,this.keepAspectRatio=!0,this.showShadow=!1,this.showGrayBottom=!1,this.showTooltip=!1,this.verticalRatio=.5,this.animationInterval=1e3,this.animationPreload=!1,this.camera=new p,this.eye=new l(0,0,-1),this.dataTable=null,this.dataPoints=null,this.colX=void 0,this.colY=void 0,this.colZ=void 0,this.colValue=void 0,this.colFilter=void 0,this.xMin=0,this.xStep=void 0,this.xMax=1,this.yMin=0,this.yStep=void 0,this.yMax=1,this.zMin=0,this.zStep=void 0,this.zMax=1,this.valueMin=0,this.valueMax=1,this.xBarWidth=1,this.yBarWidth=1,this.colorAxis="#4D4D4D",this.colorGrid="#D3D3D3",this.colorDot="#7DC1FF",this.colorDotBorder="#3267D2",this.create(),this.setOptions(i),e&&this.setData(e)}function o(t){return"clientX"in t?t.clientX:t.targetTouches[0]&&t.targetTouches[0].clientX||0}function n(t){return"clientY"in t?t.clientY:t.targetTouches[0]&&t.targetTouches[0].clientY||0}var r=i(56),a=i(3),h=i(4),d=i(1),l=i(10),c=i(9),p=i(7),u=i(8),m=i(11),f=i(12);r(s.prototype),s.prototype._setScale=function(){this.scale=new l(1/(this.xMax-this.xMin),1/(this.yMax-this.yMin),1/(this.zMax-this.zMin)),this.keepAspectRatio&&(this.scale.x3&&(this.colFilter=3);else{if(this.style!==s.STYLE.DOTCOLOR&&this.style!==s.STYLE.DOTSIZE&&this.style!==s.STYLE.BARCOLOR&&this.style!==s.STYLE.BARSIZE)throw'Unknown style "'+this.style+'"';this.colX=0,this.colY=1,this.colZ=2,this.colValue=3,t.getNumberOfColumns()>4&&(this.colFilter=4)}},s.prototype.getNumberOfRows=function(t){return t.length},s.prototype.getNumberOfColumns=function(t){var e=0;for(var i in t[0])t[0].hasOwnProperty(i)&&e++;return e},s.prototype.getDistinctValues=function(t,e){for(var i=[],s=0;st[s][e]&&(i.min=t[s][e]),i.maxe;e++){var s=arguments[e];for(var o in s)s.hasOwnProperty(o)&&(t[o]=s[o])}return t},e.selectiveExtend=function(t,e){if(!Array.isArray(t))throw new Error("Array with property names expected as first argument");for(var i=2;ii;i++)if(t[i]!=e[i])return!1;return!0},e.convert=function(t,i){var n;if(void 0===t)return void 0;if(null===t)return null;if(!i)return t;if("string"!=typeof i&&!(i instanceof String))throw new Error("Type must be a string");switch(i){case"boolean":case"Boolean":return Boolean(t);case"number":case"Number":return Number(t.valueOf());case"string":case"String":return String(t);case"Date":if(e.isNumber(t))return new Date(t);if(t instanceof Date)return new Date(t.valueOf());if(s.isMoment(t))return new Date(t.valueOf());if(e.isString(t))return n=o.exec(t),n?new Date(Number(n[1])):s(t).toDate();throw new Error("Cannot convert object of type "+e.getType(t)+" to type Date");case"Moment":if(e.isNumber(t))return s(t);if(t instanceof Date)return s(t.valueOf());if(s.isMoment(t))return s(t);if(e.isString(t))return n=o.exec(t),s(n?Number(n[1]):t);throw new Error("Cannot convert object of type "+e.getType(t)+" to type Date");case"ISODate":if(e.isNumber(t))return new Date(t);if(t instanceof Date)return t.toISOString();if(s.isMoment(t))return t.toDate().toISOString();if(e.isString(t))return n=o.exec(t),n?new Date(Number(n[1])).toISOString():new Date(t).toISOString();throw new Error("Cannot convert object of type "+e.getType(t)+" to type ISODate");case"ASPDate":if(e.isNumber(t))return"/Date("+t+")/";if(t instanceof Date)return"/Date("+t.valueOf()+")/";if(e.isString(t)){n=o.exec(t);var r;return r=n?new Date(Number(n[1])).valueOf():new Date(t).valueOf(),"/Date("+r+")/"}throw new Error("Cannot convert object of type "+e.getType(t)+" to type ASPDate");default:throw new Error('Unknown type "'+i+'"')}};var o=/^\/?Date\((\-?\d+)/i;e.getType=function(t){var e=typeof t;return"object"==e?null==t?"null":t instanceof Boolean?"Boolean":t instanceof Number?"Number":t instanceof String?"String":Array.isArray(t)?"Array":t instanceof Date?"Date":"Object":"number"==e?"Number":"boolean"==e?"Boolean":"string"==e?"String":e},e.getAbsoluteLeft=function(t){return t.getBoundingClientRect().left+window.pageXOffset},e.getAbsoluteTop=function(t){return t.getBoundingClientRect().top+window.pageYOffset},e.addClassName=function(t,e){var i=t.className.split(" ");-1==i.indexOf(e)&&(i.push(e),t.className=i.join(" "))},e.removeClassName=function(t,e){var i=t.className.split(" "),s=i.indexOf(e);-1!=s&&(i.splice(s,1),t.className=i.join(" "))},e.forEach=function(t,e){var i,s;if(Array.isArray(t))for(i=0,s=t.length;s>i;i++)e(t[i],i,t);else for(i in t)t.hasOwnProperty(i)&&e(t[i],i,t)},e.toArray=function(t){var e=[];for(var i in t)t.hasOwnProperty(i)&&e.push(t[i]);return e},e.updateProperty=function(t,e,i){return t[e]!==i?(t[e]=i,!0):!1},e.addEventListener=function(t,e,i,s){t.addEventListener?(void 0===s&&(s=!1),"mousewheel"===e&&navigator.userAgent.indexOf("Firefox")>=0&&(e="DOMMouseScroll"),t.addEventListener(e,i,s)):t.attachEvent("on"+e,i)},e.removeEventListener=function(t,e,i,s){t.removeEventListener?(void 0===s&&(s=!1),"mousewheel"===e&&navigator.userAgent.indexOf("Firefox")>=0&&(e="DOMMouseScroll"),t.removeEventListener(e,i,s)):t.detachEvent("on"+e,i)},e.preventDefault=function(t){t||(t=window.event),t.preventDefault?t.preventDefault():t.returnValue=!1},e.getTarget=function(t){t||(t=window.event);var e;return t.target?e=t.target:t.srcElement&&(e=t.srcElement),void 0!=e.nodeType&&3==e.nodeType&&(e=e.parentNode),e},e.option={},e.option.asBoolean=function(t,e){return"function"==typeof t&&(t=t()),null!=t?0!=t:e||null},e.option.asNumber=function(t,e){return"function"==typeof t&&(t=t()),null!=t?Number(t)||e||null:e||null},e.option.asString=function(t,e){return"function"==typeof t&&(t=t()),null!=t?String(t):e||null},e.option.asSize=function(t,i){return"function"==typeof t&&(t=t()),e.isString(t)?t:e.isNumber(t)?t+"px":i||null},e.option.asElement=function(t,e){return"function"==typeof t&&(t=t()),t||e||null},e.hexToRGB=function(t){var e=/^#?([a-f\d])([a-f\d])([a-f\d])$/i;t=t.replace(e,function(t,e,i,s){return e+e+i+i+s+s});var i=/^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(t);return i?{r:parseInt(i[1],16),g:parseInt(i[2],16),b:parseInt(i[3],16)}:null},e.RGBToHex=function(t,e,i){return"#"+((1<<24)+(t<<16)+(e<<8)+i).toString(16).slice(1)},e.parseColor=function(t){var i;if(e.isString(t)){if(e.isValidRGB(t)){var s=t.substr(4).substr(0,t.length-5).split(",");t=e.RGBToHex(s[0],s[1],s[2])}if(e.isValidHex(t)){var o=e.hexToHSV(t),n={h:o.h,s:.45*o.s,v:Math.min(1,1.05*o.v)},r={h:o.h,s:Math.min(1,1.25*o.v),v:.6*o.v},a=e.HSVToHex(r.h,r.h,r.v),h=e.HSVToHex(n.h,n.s,n.v);i={background:t,border:a,highlight:{background:h,border:a},hover:{background:h,border:a}}}else i={background:t,border:t,highlight:{background:t,border:t},hover:{background:t,border:t}}}else i={},i.background=t.background||"white",i.border=t.border||i.background,e.isString(t.highlight)?i.highlight={border:t.highlight,background:t.highlight}:(i.highlight={},i.highlight.background=t.highlight&&t.highlight.background||i.background,i.highlight.border=t.highlight&&t.highlight.border||i.border),e.isString(t.hover)?i.hover={border:t.hover,background:t.hover}:(i.hover={},i.hover.background=t.hover&&t.hover.background||i.background,i.hover.border=t.hover&&t.hover.border||i.border);return i},e.RGBToHSV=function(t,e,i){t/=255,e/=255,i/=255;var s=Math.min(t,Math.min(e,i)),o=Math.max(t,Math.max(e,i));if(s==o)return{h:0,s:0,v:s};var n=t==s?e-i:i==s?t-e:i-t,r=t==s?3:i==s?1:5,a=60*(r-n/(o-s))/360,h=(o-s)/o,d=o;return{h:a,s:h,v:d}};var n={split:function(t){var e={};return t.split(";").forEach(function(t){if(""!=t.trim()){var i=t.split(":"),s=i[0].trim(),o=i[1].trim();e[s]=o}}),e},join:function(t){return Object.keys(t).map(function(e){return e+": "+t[e]}).join("; ")}};e.addCssText=function(t,i){var s=n.split(t.style.cssText),o=n.split(i),r=e.extend(s,o);t.style.cssText=n.join(r)},e.removeCssText=function(t,e){var i=n.split(t.style.cssText),s=n.split(e);for(var o in s)s.hasOwnProperty(o)&&delete i[o];t.style.cssText=n.join(i)},e.HSVToRGB=function(t,e,i){var s,o,n,r=Math.floor(6*t),a=6*t-r,h=i*(1-e),d=i*(1-a*e),l=i*(1-(1-a)*e);switch(r%6){case 0:s=i,o=l,n=h;break;case 1:s=d,o=i,n=h;break;case 2:s=h,o=i,n=l;break;case 3:s=h,o=d,n=i;break;case 4:s=l,o=h,n=i;break;case 5:s=i,o=h,n=d}return{r:Math.floor(255*s),g:Math.floor(255*o),b:Math.floor(255*n)}},e.HSVToHex=function(t,i,s){var o=e.HSVToRGB(t,i,s);return e.RGBToHex(o.r,o.g,o.b)},e.hexToHSV=function(t){var i=e.hexToRGB(t);return e.RGBToHSV(i.r,i.g,i.b)},e.isValidHex=function(t){var e=/(^#[0-9A-F]{6}$)|(^#[0-9A-F]{3}$)/i.test(t);return e},e.isValidRGB=function(t){t=t.replace(" ","");var e=/rgb\((\d{1,3}),(\d{1,3}),(\d{1,3})\)/i.test(t);return e},e.selectiveBridgeObject=function(t,i){if("object"==typeof i){for(var s=Object.create(i),o=0;o=r&&o>n;){var h=Math.floor((r+a)/2),d=t[h],l=void 0===s?d[i]:d[i][s],c=e(l);if(0==c)return h;-1==c?r=h+1:a=h-1,n++}return-1},e.binarySearchValue=function(t,e,i,s){for(var o,n,r,a,h=1e4,d=0,l=0,c=t.length-1;c>=l&&h>d;){if(a=Math.floor(.5*(c+l)),o=t[Math.max(0,a-1)][i],n=t[a][i],r=t[Math.min(t.length-1,a+1)][i],n==e)return a;if(e>o&&n>e)return"before"==s?Math.max(0,a-1):a;if(e>n&&r>e)return"before"==s?a:Math.min(t.length-1,a+1);e>n?l=a+1:c=a-1,d++}return-1},e.easeInOutQuad=function(t,e,i,s){var o=i-e;return t/=s/2,1>t?o/2*t*t+e:(t--,-o/2*(t*(t-2)-1)+e)},e.easingFunctions={linear:function(t){return t},easeInQuad:function(t){return t*t},easeOutQuad:function(t){return t*(2-t)},easeInOutQuad:function(t){return.5>t?2*t*t:-1+(4-2*t)*t},easeInCubic:function(t){return t*t*t},easeOutCubic:function(t){return--t*t*t+1},easeInOutCubic:function(t){return.5>t?4*t*t*t:(t-1)*(2*t-2)*(2*t-2)+1},easeInQuart:function(t){return t*t*t*t},easeOutQuart:function(t){return 1- --t*t*t*t},easeInOutQuart:function(t){return.5>t?8*t*t*t*t:1-8*--t*t*t*t},easeInQuint:function(t){return t*t*t*t*t},easeOutQuint:function(t){return 1+--t*t*t*t*t},easeInOutQuint:function(t){return.5>t?16*t*t*t*t*t:1+16*--t*t*t*t*t}}},function(t,e){e.prepareElements=function(t){for(var e in t)t.hasOwnProperty(e)&&(t[e].redundant=t[e].used,t[e].used=[])},e.cleanupElements=function(t){for(var e in t)if(t.hasOwnProperty(e)&&t[e].redundant){for(var i=0;i0?(s=e[t].redundant[0],e[t].redundant.shift()):(s=document.createElementNS("http://www.w3.org/2000/svg",t),i.appendChild(s)):(s=document.createElementNS("http://www.w3.org/2000/svg",t),e[t]={used:[],redundant:[]},i.appendChild(s)),e[t].used.push(s),s},e.getDOMElement=function(t,e,i,s){var o;return e.hasOwnProperty(t)?e[t].redundant.length>0?(o=e[t].redundant[0],e[t].redundant.shift()):(o=document.createElement(t),void 0!==s?i.insertBefore(o,s):i.appendChild(o)):(o=document.createElement(t),e[t]={used:[],redundant:[]},void 0!==s?i.insertBefore(o,s):i.appendChild(o)),e[t].used.push(o),o},e.drawPoint=function(t,i,s,o,n){var r;return"circle"==s.options.drawPoints.style?(r=e.getSVGElement("circle",o,n),r.setAttributeNS(null,"cx",t),r.setAttributeNS(null,"cy",i),r.setAttributeNS(null,"r",.5*s.options.drawPoints.size)):(r=e.getSVGElement("rect",o,n),r.setAttributeNS(null,"x",t-.5*s.options.drawPoints.size),r.setAttributeNS(null,"y",i-.5*s.options.drawPoints.size),r.setAttributeNS(null,"width",s.options.drawPoints.size),r.setAttributeNS(null,"height",s.options.drawPoints.size)),void 0!==s.options.drawPoints.styles&&r.setAttributeNS(null,"style",s.group.options.drawPoints.styles),r.setAttributeNS(null,"class",s.className+" point"),r},e.drawBar=function(t,i,s,o,n,r,a){if(0!=o){0>o&&(o*=-1,i-=o);var h=e.getSVGElement("rect",r,a);h.setAttributeNS(null,"x",t-.5*s),h.setAttributeNS(null,"y",i),h.setAttributeNS(null,"width",s),h.setAttributeNS(null,"height",o),h.setAttributeNS(null,"class",n)}}},function(t,e,i){function s(t,e){if(!t||Array.isArray(t)||o.isDataTable(t)||(e=t,t=null),this._options=e||{},this._data={},this.length=0,this._fieldId=this._options.fieldId||"id",this._type={},this._options.type)for(var i in this._options.type)if(this._options.type.hasOwnProperty(i)){var s=this._options.type[i];this._type[i]="Date"==s||"ISODate"==s||"ASPDate"==s?"Date":s}if(this._options.convert)throw new Error('Option "convert" is deprecated. Use "type" instead.');this._subscribers={},t&&this.add(t),this.setOptions(e)}var o=i(1),n=i(5);s.prototype.setOptions=function(t){t&&void 0!==t.queue&&(t.queue===!1?this._queue&&(this._queue.destroy(),delete this._queue):(this._queue||(this._queue=n.extend(this,{replace:["add","update","remove"]})),"object"==typeof t.queue&&this._queue.setOptions(t.queue)))},s.prototype.on=function(t,e){var i=this._subscribers[t];i||(i=[],this._subscribers[t]=i),i.push({callback:e})},s.prototype.subscribe=s.prototype.on,s.prototype.off=function(t,e){var i=this._subscribers[t];i&&(this._subscribers[t]=i.filter(function(t){return t.callback!=e}))},s.prototype.unsubscribe=s.prototype.off,s.prototype._trigger=function(t,e,i){if("*"==t)throw new Error("Cannot trigger event *");var s=[];t in this._subscribers&&(s=s.concat(this._subscribers[t])),"*"in this._subscribers&&(s=s.concat(this._subscribers["*"]));for(var o=0;or;r++)i=n._addItem(t[r]),s.push(i);else if(o.isDataTable(t))for(var h=this._getColumnNames(t),d=0,l=t.getNumberOfRows();l>d;d++){for(var c={},p=0,u=h.length;u>p;p++){var m=h[p];c[m]=t.getValue(d,p)}i=n._addItem(c),s.push(i)}else{if(!(t instanceof Object))throw new Error("Unknown dataType");i=n._addItem(t),s.push(i)}return s.length&&this._trigger("add",{items:s},e),s},s.prototype.update=function(t,e){var i=[],s=[],n=[],r=this,a=r._fieldId,h=function(t){var e=t[a];r._data[e]?(e=r._updateItem(t),s.push(e),n.push(t)):(e=r._addItem(t),i.push(e))};if(Array.isArray(t))for(var d=0,l=t.length;l>d;d++)h(t[d]);else if(o.isDataTable(t))for(var c=this._getColumnNames(t),p=0,u=t.getNumberOfRows();u>p;p++){for(var m={},f=0,g=c.length;g>f;f++){var v=c[f];m[v]=t.getValue(p,f)}h(m)}else{if(!(t instanceof Object))throw new Error("Unknown dataType");h(t)}return i.length&&this._trigger("add",{items:i},e),s.length&&this._trigger("update",{items:s,data:n},e),i.concat(s)},s.prototype.get=function(){var t,e,i,s,n=this,r=o.getType(arguments[0]);"String"==r||"Number"==r?(t=arguments[0],i=arguments[1],s=arguments[2]):"Array"==r?(e=arguments[0],i=arguments[1],s=arguments[2]):(i=arguments[0],s=arguments[1]);var a;if(i&&i.returnType){var h=["DataTable","Array","Object"];if(a=-1==h.indexOf(i.returnType)?"Array":i.returnType,s&&a!=o.getType(s))throw new Error('Type of parameter "data" ('+o.getType(s)+") does not correspond with specified options.type ("+i.type+")");if("DataTable"==a&&!o.isDataTable(s))throw new Error('Parameter "data" must be a DataTable when options.type is "DataTable"')}else a=s&&"DataTable"==o.getType(s)?"DataTable":"Array";var d,l,c,p,u=i&&i.type||this._options.type,m=i&&i.filter,f=[];if(void 0!=t)d=n._getItem(t,u),m&&!m(d)&&(d=null);else if(void 0!=e)for(c=0,p=e.length;p>c;c++)d=n._getItem(e[c],u),(!m||m(d))&&f.push(d);else for(l in this._data)this._data.hasOwnProperty(l)&&(d=n._getItem(l,u),(!m||m(d))&&f.push(d));if(i&&i.order&&void 0==t&&this._sort(f,i.order),i&&i.fields){var g=i.fields;if(void 0!=t)d=this._filterFields(d,g);else for(c=0,p=f.length;p>c;c++)f[c]=this._filterFields(f[c],g)}if("DataTable"==a){var v=this._getColumnNames(s);if(void 0!=t)n._appendRow(s,v,d);else for(c=0;cc;c++)s.push(f[c]);return s}return f},s.prototype.getIds=function(t){var e,i,s,o,n,r=this._data,a=t&&t.filter,h=t&&t.order,d=t&&t.type||this._options.type,l=[];if(a)if(h){n=[];for(s in r)r.hasOwnProperty(s)&&(o=this._getItem(s,d),a(o)&&n.push(o));for(this._sort(n,h),e=0,i=n.length;i>e;e++)l[e]=n[e][this._fieldId]}else for(s in r)r.hasOwnProperty(s)&&(o=this._getItem(s,d),a(o)&&l.push(o[this._fieldId]));else if(h){n=[];for(s in r)r.hasOwnProperty(s)&&n.push(r[s]);for(this._sort(n,h),e=0,i=n.length;i>e;e++)l[e]=n[e][this._fieldId]}else for(s in r)r.hasOwnProperty(s)&&(o=r[s],l.push(o[this._fieldId]));return l},s.prototype.getDataSet=function(){return this},s.prototype.forEach=function(t,e){var i,s,o=e&&e.filter,n=e&&e.type||this._options.type,r=this._data;if(e&&e.order)for(var a=this.get(e),h=0,d=a.length;d>h;h++)i=a[h],s=i[this._fieldId],t(i,s);else for(s in r)r.hasOwnProperty(s)&&(i=this._getItem(s,n),(!o||o(i))&&t(i,s))},s.prototype.map=function(t,e){var i,s=e&&e.filter,o=e&&e.type||this._options.type,n=[],r=this._data;for(var a in r)r.hasOwnProperty(a)&&(i=this._getItem(a,o),(!s||s(i))&&n.push(t(i,a)));return e&&e.order&&this._sort(n,e.order),n},s.prototype._filterFields=function(t,e){var i={};for(var s in t)t.hasOwnProperty(s)&&-1!=e.indexOf(s)&&(i[s]=t[s]);return i},s.prototype._sort=function(t,e){if(o.isString(e)){var i=e;t.sort(function(t,e){var s=t[i],o=e[i];return s>o?1:o>s?-1:0})}else{if("function"!=typeof e)throw new TypeError("Order must be a function or a string");t.sort(e)}},s.prototype.remove=function(t,e){var i,s,o,n=[];if(Array.isArray(t))for(i=0,s=t.length;s>i;i++)o=this._remove(t[i]),null!=o&&n.push(o);else o=this._remove(t),null!=o&&n.push(o);return n.length&&this._trigger("remove",{items:n},e),n},s.prototype._remove=function(t){if(o.isNumber(t)||o.isString(t)){if(this._data[t])return delete this._data[t],this.length--,t}else if(t instanceof Object){var e=t[this._fieldId];if(e&&this._data[e])return delete this._data[e],this.length--,e}return null},s.prototype.clear=function(t){var e=Object.keys(this._data);return this._data={},this.length=0,this._trigger("remove",{items:e},t),e},s.prototype.max=function(t){var e=this._data,i=null,s=null;for(var o in e)if(e.hasOwnProperty(o)){var n=e[o],r=n[t];null!=r&&(!i||r>s)&&(i=n,s=r)}return i},s.prototype.min=function(t){var e=this._data,i=null,s=null;for(var o in e)if(e.hasOwnProperty(o)){var n=e[o],r=n[t];null!=r&&(!i||s>r)&&(i=n,s=r)}return i},s.prototype.distinct=function(t){var e,i=this._data,s=[],n=this._options.type&&this._options.type[t]||null,r=0;for(var a in i)if(i.hasOwnProperty(a)){var h=i[a],d=h[t],l=!1;for(e=0;r>e;e++)if(s[e]==d){l=!0;break}l||void 0===d||(s[r]=d,r++)}if(n)for(e=0;ei;i++)e[i]=t.getColumnId(i)||t.getColumnLabel(i);return e},s.prototype._appendRow=function(t,e,i){for(var s=t.addRow(),o=0,n=e.length;n>o;o++){var r=e[o];t.setValue(s,o,i[r])}},t.exports=s},function(t,e,i){function s(t,e){this._data=null,this._ids={},this.length=0,this._options=e||{},this._fieldId="id",this._subscribers={};var i=this;this.listener=function(){i._onEvent.apply(i,arguments)},this.setData(t)}var o=i(1),n=i(3);s.prototype.setData=function(t){var e,i,s;if(this._data){this._data.unsubscribe&&this._data.unsubscribe("*",this.listener),e=[];for(var o in this._ids)this._ids.hasOwnProperty(o)&&e.push(o);this._ids={},this.length=0,this._trigger("remove",{items:e})}if(this._data=t,this._data){for(this._fieldId=this._options.fieldId||this._data&&this._data.options&&this._data.options.fieldId||"id",e=this._data.getIds({filter:this._options&&this._options.filter}),i=0,s=e.length;s>i;i++)o=e[i],this._ids[o]=!0;this.length=e.length,this._trigger("add",{items:e}),this._data.on&&this._data.on("*",this.listener)}},s.prototype.get=function(){var t,e,i,s=this,n=o.getType(arguments[0]);"String"==n||"Number"==n||"Array"==n?(t=arguments[0],e=arguments[1],i=arguments[2]):(e=arguments[0],i=arguments[1]);var r=o.extend({},this._options,e);this._options.filter&&e&&e.filter&&(r.filter=function(t){return s._options.filter(t)&&e.filter(t)});var a=[];return void 0!=t&&a.push(t),a.push(r),a.push(i),this._data&&this._data.get.apply(this._data,a)},s.prototype.getIds=function(t){var e;if(this._data){var i,s=this._options.filter;i=t&&t.filter?s?function(e){return s(e)&&t.filter(e)}:t.filter:s,e=this._data.getIds({filter:i,order:t&&t.order})}else e=[];return e},s.prototype.getDataSet=function(){for(var t=this;t instanceof s;)t=t._data;return t||null},s.prototype._onEvent=function(t,e,i){var s,o,n,r,a=e&&e.items,h=this._data,d=[],l=[],c=[];if(a&&h){switch(t){case"add":for(s=0,o=a.length;o>s;s++)n=a[s],r=this.get(n),r&&(this._ids[n]=!0,d.push(n));break;case"update":for(s=0,o=a.length;o>s;s++)n=a[s],r=this.get(n),r?this._ids[n]?l.push(n):(this._ids[n]=!0,d.push(n)):this._ids[n]&&(delete this._ids[n],c.push(n));break;case"remove":for(s=0,o=a.length;o>s;s++)n=a[s],this._ids[n]&&(delete this._ids[n],c.push(n))}this.length+=d.length-c.length,d.length&&this._trigger("add",{items:d},i),l.length&&this._trigger("update",{items:l},i),c.length&&this._trigger("remove",{items:c},i)}},s.prototype.on=n.prototype.on,s.prototype.off=n.prototype.off,s.prototype._trigger=n.prototype._trigger,s.prototype.subscribe=s.prototype.on,s.prototype.unsubscribe=s.prototype.off,t.exports=s},function(t){function e(t){this.delay=null,this.max=1/0,this._queue=[],this._timeout=null,this._extended=null,this.setOptions(t)}e.prototype.setOptions=function(t){t&&"undefined"!=typeof t.delay&&(this.delay=t.delay),t&&"undefined"!=typeof t.max&&(this.max=t.max),this._flushIfNeeded()},e.extend=function(t,i){var s=new e(i);if(void 0!==t.flush)throw new Error("Target object already has a property flush");t.flush=function(){s.flush()};var o=[{name:"flush",original:void 0}];if(i&&i.replace)for(var n=0;nthis.max&&this.flush(),clearTimeout(this._timeout),this.queue.length>0&&"number"==typeof this.delay){var t=this;this._timeout=setTimeout(function(){t.flush()},this.delay)}},e.prototype.flush=function(){for(;this._queue.length>0;){var t=this._queue.shift();t.fn.apply(t.context||t.fn,t.args||[])}},t.exports=e},function(t,e,i){function s(t,e,i){if(!(this instanceof s))throw new SyntaxError("Constructor must be called with the new operator");this.containerElement=t,this.width="400px",this.height="400px",this.margin=10,this.defaultXCenter="55%",this.defaultYCenter="50%",this.xLabel="x",this.yLabel="y",this.zLabel="z";var o=function(t){return t};this.xValueLabel=o,this.yValueLabel=o,this.zValueLabel=o,this.filterLabel="time",this.legendLabel="value",this.style=s.STYLE.DOT,this.showPerspective=!0,this.showGrid=!0,this.keepAspectRatio=!0,this.showShadow=!1,this.showGrayBottom=!1,this.showTooltip=!1,this.verticalRatio=.5,this.animationInterval=1e3,this.animationPreload=!1,this.camera=new p,this.eye=new l(0,0,-1),this.dataTable=null,this.dataPoints=null,this.colX=void 0,this.colY=void 0,this.colZ=void 0,this.colValue=void 0,this.colFilter=void 0,this.xMin=0,this.xStep=void 0,this.xMax=1,this.yMin=0,this.yStep=void 0,this.yMax=1,this.zMin=0,this.zStep=void 0,this.zMax=1,this.valueMin=0,this.valueMax=1,this.xBarWidth=1,this.yBarWidth=1,this.colorAxis="#4D4D4D",this.colorGrid="#D3D3D3",this.colorDot="#7DC1FF",this.colorDotBorder="#3267D2",this.create(),this.setOptions(i),e&&this.setData(e)}function o(t){return"clientX"in t?t.clientX:t.targetTouches[0]&&t.targetTouches[0].clientX||0}function n(t){return"clientY"in t?t.clientY:t.targetTouches[0]&&t.targetTouches[0].clientY||0}var r=i(56),a=i(3),h=i(4),d=i(1),l=i(10),c=i(9),p=i(7),u=i(8),m=i(11),f=i(12);r(s.prototype),s.prototype._setScale=function(){this.scale=new l(1/(this.xMax-this.xMin),1/(this.yMax-this.yMin),1/(this.zMax-this.zMin)),this.keepAspectRatio&&(this.scale.x3&&(this.colFilter=3);else{if(this.style!==s.STYLE.DOTCOLOR&&this.style!==s.STYLE.DOTSIZE&&this.style!==s.STYLE.BARCOLOR&&this.style!==s.STYLE.BARSIZE)throw'Unknown style "'+this.style+'"';this.colX=0,this.colY=1,this.colZ=2,this.colValue=3,t.getNumberOfColumns()>4&&(this.colFilter=4)}},s.prototype.getNumberOfRows=function(t){return t.length},s.prototype.getNumberOfColumns=function(t){var e=0;for(var i in t[0])t[0].hasOwnProperty(i)&&e++;return e},s.prototype.getDistinctValues=function(t,e){for(var i=[],s=0;st[s][e]&&(i.min=t[s][e]),i.maxt;t++){var m=(t-p)/(u-p),g=240*m,v=this._hsv2rgb(g,1,1);c.strokeStyle=v,c.beginPath(),c.moveTo(h,r+t),c.lineTo(a,r+t),c.stroke()}c.strokeStyle=this.colorAxis,c.strokeRect(h,r,i,n)}if(this.style===s.STYLE.DOTSIZE&&(c.strokeStyle=this.colorAxis,c.fillStyle=this.colorDot,c.beginPath(),c.moveTo(h,r),c.lineTo(a,r),c.lineTo(a-i+e,d),c.lineTo(h,d),c.closePath(),c.fill(),c.stroke()),this.style===s.STYLE.DOTCOLOR||this.style===s.STYLE.DOTSIZE){var y=5,b=new f(this.valueMin,this.valueMax,(this.valueMax-this.valueMin)/5,!0);for(b.start(),b.getCurrent()0?this.yMin:this.yMax,o=this._convert3Dto2D(new l(x,r,this.zMin)),Math.cos(2*_)>0?(g.textAlign="center",g.textBaseline="top",o.y+=b):Math.sin(2*_)<0?(g.textAlign="right",g.textBaseline="middle"):(g.textAlign="left",g.textBaseline="middle"),g.fillStyle=this.colorAxis,g.fillText(" "+this.xValueLabel(i.getCurrent())+" ",o.x,o.y),i.next()}for(g.lineWidth=1,s=void 0===this.defaultYStep,i=new f(this.yMin,this.yMax,this.yStep,s),i.start(),i.getCurrent()0?this.xMin:this.xMax,o=this._convert3Dto2D(new l(n,i.getCurrent(),this.zMin)),Math.cos(2*_)<0?(g.textAlign="center",g.textBaseline="top",o.y+=b):Math.sin(2*_)>0?(g.textAlign="right",g.textBaseline="middle"):(g.textAlign="left",g.textBaseline="middle"),g.fillStyle=this.colorAxis,g.fillText(" "+this.yValueLabel(i.getCurrent())+" ",o.x,o.y),i.next();for(g.lineWidth=1,s=void 0===this.defaultZStep,i=new f(this.zMin,this.zMax,this.zStep,s),i.start(),i.getCurrent()0?this.xMin:this.xMax,r=Math.sin(_)<0?this.yMin:this.yMax;!i.end();)t=this._convert3Dto2D(new l(n,r,i.getCurrent())),g.strokeStyle=this.colorAxis,g.beginPath(),g.moveTo(t.x,t.y),g.lineTo(t.x-b,t.y),g.stroke(),g.textAlign="right",g.textBaseline="middle",g.fillStyle=this.colorAxis,g.fillText(this.zValueLabel(i.getCurrent())+" ",t.x-5,t.y),i.next();g.lineWidth=1,t=this._convert3Dto2D(new l(n,r,this.zMin)),e=this._convert3Dto2D(new l(n,r,this.zMax)),g.strokeStyle=this.colorAxis,g.beginPath(),g.moveTo(t.x,t.y),g.lineTo(e.x,e.y),g.stroke(),g.lineWidth=1,p=this._convert3Dto2D(new l(this.xMin,this.yMin,this.zMin)),u=this._convert3Dto2D(new l(this.xMax,this.yMin,this.zMin)),g.strokeStyle=this.colorAxis,g.beginPath(),g.moveTo(p.x,p.y),g.lineTo(u.x,u.y),g.stroke(),p=this._convert3Dto2D(new l(this.xMin,this.yMax,this.zMin)),u=this._convert3Dto2D(new l(this.xMax,this.yMax,this.zMin)),g.strokeStyle=this.colorAxis,g.beginPath(),g.moveTo(p.x,p.y),g.lineTo(u.x,u.y),g.stroke(),g.lineWidth=1,t=this._convert3Dto2D(new l(this.xMin,this.yMin,this.zMin)),e=this._convert3Dto2D(new l(this.xMin,this.yMax,this.zMin)),g.strokeStyle=this.colorAxis,g.beginPath(),g.moveTo(t.x,t.y),g.lineTo(e.x,e.y),g.stroke(),t=this._convert3Dto2D(new l(this.xMax,this.yMin,this.zMin)),e=this._convert3Dto2D(new l(this.xMax,this.yMax,this.zMin)),g.strokeStyle=this.colorAxis,g.beginPath(),g.moveTo(t.x,t.y),g.lineTo(e.x,e.y),g.stroke();var w=this.xLabel;w.length>0&&(c=.1/this.scale.y,n=(this.xMin+this.xMax)/2,r=Math.cos(_)>0?this.yMin-c:this.yMax+c,o=this._convert3Dto2D(new l(n,r,this.zMin)),Math.cos(2*_)>0?(g.textAlign="center",g.textBaseline="top"):Math.sin(2*_)<0?(g.textAlign="right",g.textBaseline="middle"):(g.textAlign="left",g.textBaseline="middle"),g.fillStyle=this.colorAxis,g.fillText(w,o.x,o.y));var M=this.yLabel;M.length>0&&(d=.1/this.scale.x,n=Math.sin(_)>0?this.xMin-d:this.xMax+d,r=(this.yMin+this.yMax)/2,o=this._convert3Dto2D(new l(n,r,this.zMin)),Math.cos(2*_)<0?(g.textAlign="center",g.textBaseline="top"):Math.sin(2*_)>0?(g.textAlign="right",g.textBaseline="middle"):(g.textAlign="left",g.textBaseline="middle"),g.fillStyle=this.colorAxis,g.fillText(M,o.x,o.y));var S=this.zLabel;S.length>0&&(h=30,n=Math.cos(_)>0?this.xMin:this.xMax,r=Math.sin(_)<0?this.yMin:this.yMax,a=(this.zMin+this.zMax)/2,o=this._convert3Dto2D(new l(n,r,a)),g.textAlign="right",g.textBaseline="middle",g.fillStyle=this.colorAxis,g.fillText(S,o.x-h,o.y))},s.prototype._hsv2rgb=function(t,e,i){var s,o,n,r,a,h;switch(r=i*e,a=Math.floor(t/60),h=r*(1-Math.abs(t/60%2-1)),a){case 0:s=r,o=h,n=0;break;case 1:s=h,o=r,n=0;break;case 2:s=0,o=r,n=h;break;case 3:s=0,o=h,n=r;break;case 4:s=h,o=0,n=r;break;case 5:s=r,o=0,n=h;break;default:s=0,o=0,n=0}return"RGB("+parseInt(255*s)+","+parseInt(255*o)+","+parseInt(255*n)+")"},s.prototype._redrawDataGrid=function(){var t,e,i,o,n,r,a,h,d,c,p,u,m,f=this.frame.canvas,g=f.getContext("2d");if(!(void 0===this.dataPoints||this.dataPoints.length<=0)){for(n=0;n0}else r=!0;r?(m=(t.point.z+e.point.z+i.point.z+o.point.z)/4,c=240*(1-(m-this.zMin)*this.scale.z/this.verticalRatio),p=1,this.showShadow?(u=Math.min(1+M.x/S/2,1),a=this._hsv2rgb(c,p,u),h=a):(u=1,a=this._hsv2rgb(c,p,u),h=this.colorAxis)):(a="gray",h=this.colorAxis),d=.5,g.lineWidth=d,g.fillStyle=a,g.strokeStyle=h,g.beginPath(),g.moveTo(t.screen.x,t.screen.y),g.lineTo(e.screen.x,e.screen.y),g.lineTo(o.screen.x,o.screen.y),g.lineTo(i.screen.x,i.screen.y),g.closePath(),g.fill(),g.stroke()}}else for(n=0;np&&(p=0);var u,m,f;this.style===s.STYLE.DOTCOLOR?(u=240*(1-(d.point.value-this.valueMin)*this.scale.value),m=this._hsv2rgb(u,1,1),f=this._hsv2rgb(u,1,.8)):this.style===s.STYLE.DOTSIZE?(m=this.colorDot,f=this.colorDotBorder):(u=240*(1-(d.point.z-this.zMin)*this.scale.z/this.verticalRatio),m=this._hsv2rgb(u,1,1),f=this._hsv2rgb(u,1,.8)),i.lineWidth=1,i.strokeStyle=f,i.fillStyle=m,i.beginPath(),i.arc(d.screen.x,d.screen.y,p,0,2*Math.PI,!0),i.fill(),i.stroke()}}},s.prototype._redrawDataBar=function(){var t,e,i,o,n=this.frame.canvas,r=n.getContext("2d");if(!(void 0===this.dataPoints||this.dataPoints.length<=0)){for(t=0;t0&&(t=this.dataPoints[0],s.lineWidth=1,s.strokeStyle="blue",s.beginPath(),s.moveTo(t.screen.x,t.screen.y)),e=1;e0&&s.stroke()}},s.prototype._onMouseDown=function(t){if(t=t||window.event,this.leftButtonDown&&this._onMouseUp(t),this.leftButtonDown=t.which?1===t.which:1===t.button,this.leftButtonDown||this.touchDown){this.startMouseX=o(t),this.startMouseY=n(t),this.startStart=new Date(this.start),this.startEnd=new Date(this.end),this.startArmRotation=this.camera.getArmRotation(),this.frame.style.cursor="move";var e=this;this.onmousemove=function(t){e._onMouseMove(t)},this.onmouseup=function(t){e._onMouseUp(t)},d.addEventListener(document,"mousemove",e.onmousemove),d.addEventListener(document,"mouseup",e.onmouseup),d.preventDefault(t)}},s.prototype._onMouseMove=function(t){t=t||window.event;var e=parseFloat(o(t))-this.startMouseX,i=parseFloat(n(t))-this.startMouseY,s=this.startArmRotation.horizontal+e/200,r=this.startArmRotation.vertical+i/200,a=4,h=Math.sin(a/360*2*Math.PI);Math.abs(Math.sin(s))0?1:0>t?-1:0}var s=e[0],o=e[1],n=e[2],r=i((o.x-s.x)*(t.y-s.y)-(o.y-s.y)*(t.x-s.x)),a=i((n.x-o.x)*(t.y-o.y)-(n.y-o.y)*(t.x-o.x)),h=i((s.x-n.x)*(t.y-n.y)-(s.y-n.y)*(t.x-n.x));return!(0!=r&&0!=a&&r!=a||0!=a&&0!=h&&a!=h||0!=r&&0!=h&&r!=h)},s.prototype._dataPointFromXY=function(t,e){var i,o=100,n=null,r=null,a=null,h=new c(t,e);if(this.style===s.STYLE.BAR||this.style===s.STYLE.BARCOLOR||this.style===s.STYLE.BARSIZE)for(i=this.dataPoints.length-1;i>=0;i--){n=this.dataPoints[i];var d=n.surfaces;if(d)for(var l=d.length-1;l>=0;l--){var p=d[l],u=p.corners,m=[u[0].screen,u[1].screen,u[2].screen],f=[u[2].screen,u[3].screen,u[0].screen];if(this._insideTriangle(h,m)||this._insideTriangle(h,f))return n}}else for(i=0;ib)&&o>b&&(a=b,r=n)}}return r},s.prototype._showTooltip=function(t){var e,i,s;this.tooltip?(e=this.tooltip.dom.content,i=this.tooltip.dom.line,s=this.tooltip.dom.dot):(e=document.createElement("div"),e.style.position="absolute",e.style.padding="10px",e.style.border="1px solid #4d4d4d",e.style.color="#1a1a1a",e.style.background="rgba(255,255,255,0.7)",e.style.borderRadius="2px",e.style.boxShadow="5px 5px 10px rgba(128,128,128,0.5)",i=document.createElement("div"),i.style.position="absolute",i.style.height="40px",i.style.width="0",i.style.borderLeft="1px solid #4d4d4d",s=document.createElement("div"),s.style.position="absolute",s.style.height="0",s.style.width="0",s.style.border="5px solid #4d4d4d",s.style.borderRadius="5px",this.tooltip={dataPoint:null,dom:{content:e,line:i,dot:s}}),this._hideTooltip(),this.tooltip.dataPoint=t,e.innerHTML="function"==typeof this.showTooltip?this.showTooltip(t.point):"
x:"+t.point.x+"
y:"+t.point.y+"
z:"+t.point.z+"
",e.style.left="0",e.style.top="0",this.frame.appendChild(e),this.frame.appendChild(i),this.frame.appendChild(s);var o=e.offsetWidth,n=e.offsetHeight,r=i.offsetHeight,a=s.offsetWidth,h=s.offsetHeight,d=t.screen.x-o/2;d=Math.min(Math.max(d,10),this.frame.clientWidth-10-o),i.style.left=t.screen.x+"px",i.style.top=t.screen.y-r+"px",e.style.left=d+"px",e.style.top=t.screen.y-r-n+"px",s.style.left=t.screen.x-a/2+"px",s.style.top=t.screen.y-h/2+"px"},s.prototype._hideTooltip=function(){if(this.tooltip){this.tooltip.dataPoint=null;for(var t in this.tooltip.dom)if(this.tooltip.dom.hasOwnProperty(t)){var e=this.tooltip.dom[t];e&&e.parentNode&&e.parentNode.removeChild(e)}}},t.exports=s},function(t,e,i){function s(){this.armLocation=new o,this.armRotation={},this.armRotation.horizontal=0,this.armRotation.vertical=0,this.armLength=1.7,this.cameraLocation=new o,this.cameraRotation=new o(.5*Math.PI,0,0),this.calculateCameraOrientation()}var o=i(10);s.prototype.setArmLocation=function(t,e,i){this.armLocation.x=t,this.armLocation.y=e,this.armLocation.z=i,this.calculateCameraOrientation()},s.prototype.setArmRotation=function(t,e){void 0!==t&&(this.armRotation.horizontal=t),void 0!==e&&(this.armRotation.vertical=e,this.armRotation.vertical<0&&(this.armRotation.vertical=0),this.armRotation.vertical>.5*Math.PI&&(this.armRotation.vertical=.5*Math.PI)),(void 0!==t||void 0!==e)&&this.calculateCameraOrientation()},s.prototype.getArmRotation=function(){var t={};return t.horizontal=this.armRotation.horizontal,t.vertical=this.armRotation.vertical,t},s.prototype.setArmLength=function(t){void 0!==t&&(this.armLength=t,this.armLength<.71&&(this.armLength=.71),this.armLength>5&&(this.armLength=5),this.calculateCameraOrientation())},s.prototype.getArmLength=function(){return this.armLength},s.prototype.getCameraLocation=function(){return this.cameraLocation},s.prototype.getCameraRotation=function(){return this.cameraRotation},s.prototype.calculateCameraOrientation=function(){this.cameraLocation.x=this.armLocation.x-this.armLength*Math.sin(this.armRotation.horizontal)*Math.cos(this.armRotation.vertical),this.cameraLocation.y=this.armLocation.y-this.armLength*Math.cos(this.armRotation.horizontal)*Math.cos(this.armRotation.vertical),this.cameraLocation.z=this.armLocation.z+this.armLength*Math.sin(this.armRotation.vertical),this.cameraRotation.x=Math.PI/2-this.armRotation.vertical,this.cameraRotation.y=0,this.cameraRotation.z=-this.armRotation.horizontal},t.exports=s},function(t,e,i){function s(t,e,i){this.data=t,this.column=e,this.graph=i,this.index=void 0,this.value=void 0,this.values=i.getDistinctValues(t.get(),this.column),this.values.sort(function(t,e){return t>e?1:e>t?-1:0}),this.values.length>0&&this.selectValue(0),this.dataPoints=[],this.loaded=!1,this.onLoadCallback=void 0,i.animationPreload?(this.loaded=!1,this.loadInBackground()):this.loaded=!0}var o=i(4);s.prototype.isLoaded=function(){return this.loaded},s.prototype.getLoadedProgress=function(){for(var t=this.values.length,e=0;this.dataPoints[e];)e++;return Math.round(e/t*100)},s.prototype.getLabel=function(){return this.graph.filterLabel},s.prototype.getColumn=function(){return this.column},s.prototype.getSelectedValue=function(){return void 0===this.index?void 0:this.values[this.index]},s.prototype.getValues=function(){return this.values},s.prototype.getValue=function(t){if(t>=this.values.length)throw"Error: index out of range";return this.values[t]},s.prototype._getDataPoints=function(t){if(void 0===t&&(t=this.index),void 0===t)return[];var e;if(this.dataPoints[t])e=this.dataPoints[t];else{var i={};i.column=this.column,i.value=this.values[t];var s=new o(this.data,{filter:function(t){return t[i.column]==i.value}}).get();e=this.graph._getDataPoints(s),this.dataPoints[t]=e}return e},s.prototype.setOnLoadCallback=function(t){this.onLoadCallback=t},s.prototype.selectValue=function(t){if(t>=this.values.length)throw"Error: index out of range";this.index=t,this.value=this.values[t]},s.prototype.loadInBackground=function(t){void 0===t&&(t=0);var e=this.graph.frame;if(t0&&(t--,this.setIndex(t))},s.prototype.next=function(){var t=this.getIndex();t0?this.setIndex(0):this.index=void 0},s.prototype.setIndex=function(t){if(!(ts&&(s=0),s>this.values.length-1&&(s=this.values.length-1),s},s.prototype.indexToLeft=function(t){var e=parseFloat(this.frame.bar.style.width)-this.frame.slide.clientWidth-10,i=t/(this.values.length-1)*e,s=i+3;return s},s.prototype._onMouseMove=function(t){var e=t.clientX-this.startClientX,i=this.startSlideX+e,s=this.leftToIndex(i);this.setIndex(s),o.preventDefault()},s.prototype._onMouseUp=function(){this.frame.style.cursor="auto",o.removeEventListener(document,"mousemove",this.onmousemove),o.removeEventListener(document,"mouseup",this.onmouseup),o.preventDefault()},t.exports=s},function(t){function e(t,e,i,s){this._start=0,this._end=0,this._step=1,this.prettyStep=!0,this.precision=5,this._current=0,this.setRange(t,e,i,s)}e.prototype.setRange=function(t,e,i,s){this._start=t?t:0,this._end=e?e:0,this.setStep(i,s)},e.prototype.setStep=function(t,i){void 0===t||0>=t||(void 0!==i&&(this.prettyStep=i),this._step=this.prettyStep===!0?e.calculatePrettyStep(t):t)},e.calculatePrettyStep=function(t){var e=function(t){return Math.log(t)/Math.LN10},i=Math.pow(10,Math.round(e(t))),s=2*Math.pow(10,Math.round(e(t/2))),o=5*Math.pow(10,Math.round(e(t/5))),n=i;return Math.abs(s-t)<=Math.abs(n-t)&&(n=s),Math.abs(o-t)<=Math.abs(n-t)&&(n=o),0>=n&&(n=1),n},e.prototype.getCurrent=function(){return parseFloat(this._current.toPrecision(this.precision))},e.prototype.getStep=function(){return this._step},e.prototype.start=function(){this._current=this._start-this._start%this._step},e.prototype.next=function(){this._current+=this._step},e.prototype.end=function(){return this._current>this._end},t.exports=e},function(t,e,i){function s(t,e,i,r){if(!(this instanceof s))throw new SyntaxError("Constructor must be called with the new operator");if(!(Array.isArray(i)||i instanceof n)&&i instanceof Object){var h=r;r=i,i=h}var u=this;this.defaultOptions={start:null,end:null,autoResize:!0,orientation:"bottom",width:null,height:null,maxHeight:null,minHeight:null},this.options=o.deepExtend({},this.defaultOptions),this._create(t),this.components=[],this.body={dom:this.dom,domProps:this.props,emitter:{on:this.on.bind(this),off:this.off.bind(this),emit:this.emit.bind(this)},hiddenDates:[],util:{snap:null,toScreen:u._toScreen.bind(u),toGlobalScreen:u._toGlobalScreen.bind(u),toTime:u._toTime.bind(u),toGlobalTime:u._toGlobalTime.bind(u)}},this.range=new a(this.body),this.components.push(this.range),this.body.range=this.range,this.timeAxis=new d(this.body),this.components.push(this.timeAxis),this.body.util.snap=this.timeAxis.snap.bind(this.timeAxis),this.currentTime=new l(this.body),this.components.push(this.currentTime),this.customTime=new c(this.body),this.components.push(this.customTime),this.itemSet=new p(this.body),this.components.push(this.itemSet),this.itemsData=null,this.groupsData=null,r&&this.setOptions(r),i&&this.setGroups(i),e?this.setItems(e):this.redraw()}var o=(i(56),i(45),i(1)),n=i(3),r=i(4),a=i(17),h=i(46),d=i(35),l=i(26),c=i(27),p=i(32);s.prototype=new h,s.prototype.setItems=function(t){var e,i=null==this.itemsData;if(e=t?t instanceof n||t instanceof r?t:new n(t,{type:{start:"Date",end:"Date"}}):null,this.itemsData=e,this.itemSet&&this.itemSet.setItems(e),i)if(void 0!=this.options.start||void 0!=this.options.end){if(void 0==this.options.start||void 0==this.options.end)var s=this._getDataRange();var o=void 0!=this.options.start?this.options.start:s.start,a=void 0!=this.options.end?this.options.end:s.end;this.setWindow(o,a,{animate:!1})}else this.fit({animate:!1})},s.prototype.setGroups=function(t){var e;e=t?t instanceof n||t instanceof r?t:new n(t):null,this.groupsData=e,this.itemSet.setGroups(e)},s.prototype.setSelection=function(t,e){this.itemSet&&this.itemSet.setSelection(t),e&&e.focus&&this.focus(t,e)},s.prototype.getSelection=function(){return this.itemSet&&this.itemSet.getSelection()||[]},s.prototype.focus=function(t,e){if(this.itemsData&&void 0!=t){var i=Array.isArray(t)?t:[t],s=this.itemsData.getDataSet().get(i,{type:{start:"Date",end:"Date"}}),o=null,n=null;if(s.forEach(function(t){var e=t.start.valueOf(),i="end"in t?t.end.valueOf():t.start.valueOf();(null===o||o>e)&&(o=e),(null===n||i>n)&&(n=i)}),null!==o&&null!==n){var r=(o+n)/2,a=Math.max(this.range.end-this.range.start,1.1*(n-o)),h=e&&void 0!==e.animate?e.animate:!0;this.range.setRange(r-a/2,r+a/2,h)}}},s.prototype.getItemRange=function(){var t=this.itemsData.getDataSet(),e=null,i=null;if(t){var s=t.min("start");e=s?o.convert(s.start,"Date").valueOf():null;var n=t.max("start");n&&(i=o.convert(n.start,"Date").valueOf());var r=t.max("end");r&&(i=null==i?o.convert(r.end,"Date").valueOf():Math.max(i,o.convert(r.end,"Date").valueOf()))}return{min:null!=e?new Date(e):null,max:null!=i?new Date(i):null}},t.exports=s},function(t,e,i){function s(t,e,i,s){if(!(Array.isArray(i)||i instanceof n)&&i instanceof Object){var r=s;s=i,i=r}var h=this;this.defaultOptions={start:null,end:null,autoResize:!0,orientation:"bottom",width:null,height:null,maxHeight:null,minHeight:null},this.options=o.deepExtend({},this.defaultOptions),this._create(t),this.components=[],this.body={dom:this.dom,domProps:this.props,emitter:{on:this.on.bind(this),off:this.off.bind(this),emit:this.emit.bind(this)},hiddenDates:[],util:{snap:null,toScreen:h._toScreen.bind(h),toGlobalScreen:h._toGlobalScreen.bind(h),toTime:h._toTime.bind(h),toGlobalTime:h._toGlobalTime.bind(h)}},this.range=new a(this.body),this.components.push(this.range),this.body.range=this.range,this.timeAxis=new d(this.body),this.components.push(this.timeAxis),this.body.util.snap=this.timeAxis.snap.bind(this.timeAxis),this.currentTime=new l(this.body),this.components.push(this.currentTime),this.customTime=new c(this.body),this.components.push(this.customTime),this.linegraph=new p(this.body),this.components.push(this.linegraph),this.itemsData=null,this.groupsData=null,s&&this.setOptions(s),i&&this.setGroups(i),e?this.setItems(e):this.redraw()}var o=(i(56),i(45),i(1)),n=i(3),r=i(4),a=i(17),h=i(46),d=i(35),l=i(26),c=i(27),p=i(34);s.prototype=new h,s.prototype.setItems=function(t){var e,i=null==this.itemsData;if(e=t?t instanceof n||t instanceof r?t:new n(t,{type:{start:"Date",end:"Date"}}):null,this.itemsData=e,this.linegraph&&this.linegraph.setItems(e),i)if(void 0!=this.options.start||void 0!=this.options.end){var s=void 0!=this.options.start?this.options.start:null,o=void 0!=this.options.end?this.options.end:null;this.setWindow(s,o,{animate:!1})}else this.fit({animate:!1})},s.prototype.setGroups=function(t){var e;e=t?t instanceof n||t instanceof r?t:new n(t):null,this.groupsData=e,this.linegraph.setGroups(e)},s.prototype.getLegend=function(t,e,i){return void 0===e&&(e=15),void 0===i&&(i=15),void 0!==this.linegraph.groups[t]?this.linegraph.groups[t].getLegend(e,i):"cannot find group:"+t},s.prototype.isGroupVisible=function(t){return void 0!==this.linegraph.groups[t]?this.linegraph.groups[t].visible&&(void 0===this.linegraph.options.groups.visibility[t]||1==this.linegraph.options.groups.visibility[t]):!1},s.prototype.getItemRange=function(){var t=null,e=null;for(var i in this.linegraph.groups)if(this.linegraph.groups.hasOwnProperty(i)&&1==this.linegraph.groups[i].visible)for(var s=0;sr?r:t,e=null==e?r:r>e?r:e}return{min:null!=t?new Date(t):null,max:null!=e?new Date(e):null}},t.exports=s},function(t,e,i){var s=i(44);e.convertHiddenOptions=function(t,e){if(t.hiddenDates=[],e&&1==Array.isArray(e)){for(var i=0;i=4*a){var p=0,u=n.clone();switch(i[h].repeat){case"daily":d.day()!=l.day()&&(p=1),d.dayOfYear(o.dayOfYear()),d.year(o.year()),d.subtract(7,"days"),l.dayOfYear(o.dayOfYear()),l.year(o.year()),l.subtract(7-p,"days"),u.add(1,"weeks");break;case"weekly":var m=l.diff(d,"days"),f=d.day();d.date(o.date()),d.month(o.month()),d.year(o.year()),l=d.clone(),d.day(f),l.day(f),l.add(m,"days"),d.subtract(1,"weeks"),l.subtract(1,"weeks"),u.add(1,"weeks");break;case"monthly":d.month()!=l.month()&&(p=1),d.month(o.month()),d.year(o.year()),d.subtract(1,"months"),l.month(o.month()),l.year(o.year()),l.subtract(1,"months"),l.add(p,"months"),u.add(1,"months");break;case"yearly":d.year()!=l.year()&&(p=1),d.year(o.year()),d.subtract(1,"years"),l.year(o.year()),l.subtract(1,"years"),l.add(p,"years"),u.add(1,"years");break;default:return void console.log("Wrong repeat format, allowed are: daily, weekly, monthly, yearly. Given:",i[h].repeat)}for(;u>d;)switch(t.hiddenDates.push({start:d.valueOf(),end:l.valueOf()}),i[h].repeat){case"daily":d.add(1,"days"),l.add(1,"days");break;case"weekly":d.add(1,"weeks"),l.add(1,"weeks");break;case"monthly":d.add(1,"months"),l.add(1,"months");break;case"yearly":d.add(1,"y"),l.add(1,"y");break;default:return void console.log("Wrong repeat format, allowed are: daily, weekly, monthly, yearly. Given:",i[h].repeat)}t.hiddenDates.push({start:d.valueOf(),end:l.valueOf()})}}e.removeDuplicates(t);var g=e.isHidden(t.range.start,t.hiddenDates),v=e.isHidden(t.range.end,t.hiddenDates),y=t.range.start,b=t.range.end;1==g.hidden&&(y=1==t.range.startToFront?g.startDate-1:g.endDate+1),1==v.hidden&&(b=1==t.range.endToFront?v.startDate-1:v.endDate+1),(1==g.hidden||1==v.hidden)&&t.range._applyRange(y,b)}},e.removeDuplicates=function(t){for(var e=t.hiddenDates,i=[],s=0;s=e[s].start&&e[o].end<=e[s].end?e[o].remove=!0:e[o].start>=e[s].start&&e[o].start<=e[s].end?(e[s].end=e[o].end,e[o].remove=!0):e[o].end>=e[s].start&&e[o].end<=e[s].end&&(e[s].start=e[o].start,e[o].remove=!0));for(var s=0;s=r&&a>o){i=!0;break}}if(1==i&&o=e&&i>r&&(s+=r-n)}return s},e.correctTimeForHidden=function(t,i,o){return o=s(o).toDate().valueOf(),o-=e.getHiddenDurationBefore(t,i,o)},e.getHiddenDurationBefore=function(t,e,i){var o=0;i=s(i).toDate().valueOf();for(var n=0;n=e.start&&a=a&&(o+=a-r)}return o},e.getAccumulatedHiddenDuration=function(t,e,i){for(var s=0,o=0,n=e.start,r=0;r=e.start&&h=i)break;s+=h-a}}return s},e.snapAwayFromHidden=function(t,i,s,o){var n=e.isHidden(i,t);return 1==n.hidden?0>s?1==o?n.startDate-(n.endDate-i)-1:n.startDate-1:1==o?n.endDate+(i-n.startDate)+1:n.endDate+1:i},e.isHidden=function(t,e){for(var i=0;i=s&&o>t)return{hidden:!0,startDate:s,endDate:o}}return{hidden:!1,startDate:s,endDate:o}}},function(t){function e(t,e,i,s,o,n){this.current=0,this.autoScale=!0,this.stepIndex=0,this.step=1,this.scale=1,this.marginStart,this.marginEnd,this.deadSpace=0,this.majorSteps=[1,2,5,10],this.minorSteps=[.25,.5,1,2],this.alignZeros=n,this.setRange(t,e,i,s,o)}e.prototype.setRange=function(t,e,i,s,o){this._start=void 0===o.min?t:o.min,this._end=void 0===o.max?e:o.max,this._start==this._end&&(this._start-=.75,this._end+=1),1==this.autoScale&&this.setMinimumStep(i,s),this.setFirst(o)},e.prototype.setMinimumStep=function(t,e){var i=this._end-this._start,s=1.2*i,o=t*(s/e),n=Math.round(Math.log(s)/Math.LN10),r=-1,a=Math.pow(10,n),h=0;0>n&&(h=n);for(var d=!1,l=h;Math.abs(l)<=Math.abs(n);l++){a=Math.pow(10,l);for(var c=0;c=o){d=!0,r=c;break}}if(1==d)break}this.stepIndex=r,this.scale=a,this.step=a*this.minorSteps[r]},e.prototype.setFirst=function(t){void 0===t&&(t={});var e=void 0===t.min?this._start-2*this.scale*this.minorSteps[this.stepIndex]:t.min,i=void 0===t.max?this._end+this.scale*this.minorSteps[this.stepIndex]:t.max;this.marginEnd=void 0===t.max?this.roundToMinor(i):t.max,this.marginStart=void 0===t.min?this.roundToMinor(e):t.min,1==this.alignZeros&&(this.marginEnd-this.marginStart)%this.step!=0&&(this.marginEnd+=this.marginEnd%this.step),this.deadSpace=this.roundToMinor(i)-i+this.roundToMinor(e)-e,this.marginRange=this.marginEnd-this.marginStart,this.current=this.marginEnd},e.prototype.roundToMinor=function(t){var e=t-t%(this.scale*this.minorSteps[this.stepIndex]);return t%(this.scale*this.minorSteps[this.stepIndex])>.5*this.scale*this.minorSteps[this.stepIndex]?e+this.scale*this.minorSteps[this.stepIndex]:e},e.prototype.hasNext=function(){return this.current>=this.marginStart},e.prototype.next=function(){var t=this.current;this.current-=this.step,this.current==t&&(this.current=this._end)},e.prototype.previous=function(){this.current+=this.step,this.marginEnd+=this.step,this.marginRange=this.marginEnd-this.marginStart},e.prototype.getCurrent=function(t){var e=Math.abs(this.current)0;s--){if("0"!=i[s]){if("."==i[s]||","==i[s]){i=i.slice(0,s);break}break}i=i.slice(0,s)}}else{var o="",n=i.indexOf("e");if(-1!=n&&(o=i.slice(n),i=i.slice(0,n)),n=Math.max(i.indexOf(","),i.indexOf(".")),-1===n?(0!==t&&(i+="."),n=i.length+t):0!==t&&(n+=t+1),n>i.length)for(var r=n-i.length;r>0;r--)i+="0";else i=i.slice(0,n);i+=o}return i},e.prototype.snap=function(){},e.prototype.isMajor=function(){return this.current%(this.scale*this.majorSteps[this.stepIndex])==0},t.exports=e},function(t,e,i){function s(t,e){var i=h().hours(0).minutes(0).seconds(0).milliseconds(0);this.start=i.clone().add(-3,"days").valueOf(),this.end=i.clone().add(4,"days").valueOf(),this.body=t,this.deltaDifference=0,this.scaleOffset=0,this.startToFront=!1,this.endToFront=!0,this.defaultOptions={start:null,end:null,direction:"horizontal",moveable:!0,zoomable:!0,min:null,max:null,zoomMin:10,zoomMax:31536e10},this.options=r.extend({},this.defaultOptions),this.props={touch:{}},this.animateTimer=null,this.body.emitter.on("dragstart",this._onDragStart.bind(this)),this.body.emitter.on("drag",this._onDrag.bind(this)),this.body.emitter.on("dragend",this._onDragEnd.bind(this)),this.body.emitter.on("hold",this._onHold.bind(this)),this.body.emitter.on("mousewheel",this._onMouseWheel.bind(this)),this.body.emitter.on("DOMMouseScroll",this._onMouseWheel.bind(this)),this.body.emitter.on("touch",this._onTouch.bind(this)),this.body.emitter.on("pinch",this._onPinch.bind(this)),this.setOptions(e)}function o(t){if("horizontal"!=t&&"vertical"!=t)throw new TypeError('Unknown direction "'+t+'". Choose "horizontal" or "vertical".')}function n(t,e){return{x:t.pageX-r.getAbsoluteLeft(e),y:t.pageY-r.getAbsoluteTop(e)}}var r=i(1),a=i(47),h=i(44),d=i(25),l=i(15);s.prototype=new d,s.prototype.setOptions=function(t){if(t){var e=["direction","min","max","zoomMin","zoomMax","moveable","zoomable","activate","hiddenDates"];r.selectiveExtend(e,this.options,t),("start"in t||"end"in t)&&this.setRange(t.start,t.end)}},s.prototype.setRange=function(t,e,i,s){s!==!0&&(s=!1);var o=void 0!=t?r.convert(t,"Date").valueOf():null,n=void 0!=e?r.convert(e,"Date").valueOf():null;if(this._cancelAnimation(),i){var a=this,h=this.start,d=this.end,c="number"==typeof i?i:500,p=(new Date).valueOf(),u=!1,m=function(){if(!a.props.touch.dragging){var t=(new Date).valueOf(),e=t-p,i=e>c,g=i||null===o?o:r.easeInOutQuad(e,h,o,c),v=i||null===n?n:r.easeInOutQuad(e,d,n,c);f=a._applyRange(g,v),l.updateHiddenDates(a.body,a.options.hiddenDates),u=u||f,f&&a.body.emitter.emit("rangechange",{start:new Date(a.start),end:new Date(a.end),byUser:s}),i?u&&a.body.emitter.emit("rangechanged",{start:new Date(a.start),end:new Date(a.end),byUser:s}):a.animateTimer=setTimeout(m,20)}};return m()}var f=this._applyRange(o,n);if(l.updateHiddenDates(this.body,this.options.hiddenDates),f){var g={start:new Date(this.start),end:new Date(this.end),byUser:s};this.body.emitter.emit("rangechange",g),this.body.emitter.emit("rangechanged",g)}},s.prototype._cancelAnimation=function(){this.animateTimer&&(clearTimeout(this.animateTimer),this.animateTimer=null)},s.prototype._applyRange=function(t,e){var i,s=null!=t?r.convert(t,"Date").valueOf():this.start,o=null!=e?r.convert(e,"Date").valueOf():this.end,n=null!=this.options.max?r.convert(this.options.max,"Date").valueOf():null,a=null!=this.options.min?r.convert(this.options.min,"Date").valueOf():null;if(isNaN(s)||null===s)throw new Error('Invalid start "'+t+'"');if(isNaN(o)||null===o)throw new Error('Invalid end "'+e+'"');if(s>o&&(o=s),null!==a&&a>s&&(i=a-s,s+=i,o+=i,null!=n&&o>n&&(o=n)),null!==n&&o>n&&(i=o-n,s-=i,o-=i,null!=a&&a>s&&(s=a)),null!==this.options.zoomMin){var h=parseFloat(this.options.zoomMin);0>h&&(h=0),h>o-s&&(this.end-this.start===h?(s=this.start,o=this.end):(i=h-(o-s),s-=i/2,o+=i/2))}if(null!==this.options.zoomMax){var d=parseFloat(this.options.zoomMax);0>d&&(d=0),o-s>d&&(this.end-this.start===d?(s=this.start,o=this.end):(i=o-s-d,s+=i/2,o-=i/2))}var l=this.start!=s||this.end!=o;return s>=this.start&&s<=this.end||o>=this.start&&o<=this.end||this.start>=s&&this.start<=o||this.end>=s&&this.end<=o||this.body.emitter.emit("checkRangedItems"),this.start=s,this.end=o,l},s.prototype.getRange=function(){return{start:this.start,end:this.end}},s.prototype.conversion=function(t,e){return s.conversion(this.start,this.end,t,e)},s.conversion=function(t,e,i,s){return void 0===s&&(s=0),0!=i&&e-t!=0?{offset:t,scale:i/(e-t-s)}:{offset:0,scale:1}},s.prototype._onDragStart=function(){this.deltaDifference=0,this.previousDelta=0,this.options.moveable&&this.props.touch.allowDragging&&(this.props.touch.start=this.start,this.props.touch.end=this.end,this.props.touch.dragging=!0,this.body.dom.root&&(this.body.dom.root.style.cursor="move"))},s.prototype._onDrag=function(t){if(this.options.moveable&&this.props.touch.allowDragging){var e=this.options.direction;o(e);var i="horizontal"==e?t.gesture.deltaX:t.gesture.deltaY;i-=this.deltaDifference;var s=this.props.touch.end-this.props.touch.start,n=l.getHiddenDurationBetween(this.body.hiddenDates,this.start,this.end);s-=n;var r="horizontal"==e?this.body.domProps.center.width:this.body.domProps.center.height,a=-i/r*s,h=this.props.touch.start+a,d=this.props.touch.end+a,c=l.snapAwayFromHidden(this.body.hiddenDates,h,this.previousDelta-i,!0),p=l.snapAwayFromHidden(this.body.hiddenDates,d,this.previousDelta-i,!0);if(c!=h||p!=d)return this.deltaDifference+=i,this.props.touch.start=c,this.props.touch.end=p,void this._onDrag(t);this.previousDelta=i,this._applyRange(h,d),this.body.emitter.emit("rangechange",{start:new Date(this.start),end:new Date(this.end),byUser:!0})}},s.prototype._onDragEnd=function(){this.options.moveable&&this.props.touch.allowDragging&&(this.props.touch.dragging=!1,this.body.dom.root&&(this.body.dom.root.style.cursor="auto"),this.body.emitter.emit("rangechanged",{start:new Date(this.start),end:new Date(this.end),byUser:!0}))},s.prototype._onMouseWheel=function(t){if(this.options.zoomable&&this.options.moveable){var e=0;if(t.wheelDelta?e=t.wheelDelta/120:t.detail&&(e=-t.detail/3),e){var i;i=0>e?1-e/5:1/(1+e/5);var s=a.fakeGesture(this,t),o=n(s.center,this.body.dom.center),r=this._pointerToDate(o);this.zoom(i,r,e)}t.preventDefault()}},s.prototype._onTouch=function(){this.props.touch.start=this.start,this.props.touch.end=this.end,this.props.touch.allowDragging=!0,this.props.touch.center=null,this.scaleOffset=0,this.deltaDifference=0},s.prototype._onHold=function(){this.props.touch.allowDragging=!1},s.prototype._onPinch=function(t){if(this.options.zoomable&&this.options.moveable&&(this.props.touch.allowDragging=!1,t.gesture.touches.length>1)){this.props.touch.center||(this.props.touch.center=n(t.gesture.center,this.body.dom.center));var e=1/(t.gesture.scale+this.scaleOffset),i=this._pointerToDate(this.props.touch.center),s=l.getHiddenDurationBetween(this.body.hiddenDates,this.start,this.end),o=l.getHiddenDurationBefore(this.body.hiddenDates,this,i),r=s-o,a=i-o+(this.props.touch.start-(i-o))*e,h=i+r+(this.props.touch.end-(i+r))*e;this.startToFront=1-e>0?!1:!0,this.endToFront=e-1>0?!1:!0;var d=l.snapAwayFromHidden(this.body.hiddenDates,a,1-e,!0),c=l.snapAwayFromHidden(this.body.hiddenDates,h,e-1,!0);(d!=a||c!=h)&&(this.props.touch.start=d,this.props.touch.end=c,this.scaleOffset=1-t.gesture.scale,a=d,h=c),this.setRange(a,h,!1,!0),this.startToFront=!1,this.endToFront=!0}},s.prototype._pointerToDate=function(t){var e,i=this.options.direction;if(o(i),"horizontal"==i)return this.body.util.toTime(t.x).valueOf();var s=this.body.domProps.center.height;return e=this.conversion(s),t.y/e.scale+e.offset},s.prototype.zoom=function(t,e,i){null==e&&(e=(this.start+this.end)/2);var s=l.getHiddenDurationBetween(this.body.hiddenDates,this.start,this.end),o=l.getHiddenDurationBefore(this.body.hiddenDates,this,e),n=s-o,r=e-o+(this.start-(e-o))*t,a=e+n+(this.end-(e+n))*t;this.startToFront=i>0?!1:!0,this.endToFront=-i>0?!1:!0;var h=l.snapAwayFromHidden(this.body.hiddenDates,r,i,!0),d=l.snapAwayFromHidden(this.body.hiddenDates,a,-i,!0);(h!=r||d!=a)&&(r=h,a=d),this.setRange(r,a,!1,!0),this.startToFront=!1,this.endToFront=!0},s.prototype.move=function(t){var e=this.end-this.start,i=this.start+e*t,s=this.end+e*t;this.start=i,this.end=s},s.prototype.moveTo=function(t){var e=(this.start+this.end)/2,i=e-t,s=this.start-i,o=this.end-i;this.setRange(s,o)},t.exports=s},function(t,e){var i=.001;e.orderByStart=function(t){t.sort(function(t,e){return t.data.start-e.data.start})},e.orderByEnd=function(t){t.sort(function(t,e){var i="end"in t.data?t.data.end:t.data.start,s="end"in e.data?e.data.end:e.data.start;return i-s})},e.stack=function(t,i,s){var o,n;if(s)for(o=0,n=t.length;n>o;o++)t[o].top=null;for(o=0,n=t.length;n>o;o++){var r=t[o];if(r.stack&&null===r.top){r.top=i.axis;do{for(var a=null,h=0,d=t.length;d>h;h++){var l=t[h];if(null!==l.top&&l!==r&&l.stack&&e.collision(r,l,i.item)){a=l;break}}null!=a&&(r.top=a.top+a.height+i.item.vertical)}while(a)}}},e.nostack=function(t,e,i){var s,o,n;for(s=0,o=t.length;o>s;s++)if(void 0!==t[s].data.subgroup){n=e.axis;for(var r in i)i.hasOwnProperty(r)&&1==i[r].visible&&i[r].indexe.left&&t.top-s.vertical+ie.top}},function(t,e,i){function s(t,e,i,o){this.current=new Date,this._start=new Date,this._end=new Date,this.autoScale=!0,this.scale="day",this.step=1,this.setRange(t,e,i),this.switchedDay=!1,this.switchedMonth=!1,this.switchedYear=!1,this.hiddenDates=o,void 0===o&&(this.hiddenDates=[]),this.format=s.FORMAT}var o=i(44),n=i(15),r=i(1);s.FORMAT={minorLabels:{millisecond:"SSS",second:"s",minute:"HH:mm",hour:"HH:mm",weekday:"ddd D",day:"D",month:"MMM",year:"YYYY"},majorLabels:{millisecond:"HH:mm:ss",second:"D MMMM HH:mm",minute:"ddd D MMMM",hour:"ddd D MMMM",weekday:"MMMM YYYY",day:"MMMM YYYY",month:"YYYY",year:""}},s.prototype.setFormat=function(t){var e=r.deepExtend({},s.FORMAT);this.format=r.deepExtend(e,t)},s.prototype.setRange=function(t,e,i){if(!(t instanceof Date&&e instanceof Date))throw"No legal start or end date in method setRange";this._start=void 0!=t?new Date(t.valueOf()):new Date,this._end=void 0!=e?new Date(e.valueOf()):new Date,this.autoScale&&this.setMinimumStep(i)},s.prototype.first=function(){this.current=new Date(this._start.valueOf()),this.roundToMinor()},s.prototype.roundToMinor=function(){switch(this.scale){case"year":this.current.setFullYear(this.step*Math.floor(this.current.getFullYear()/this.step)),this.current.setMonth(0);case"month":this.current.setDate(1);case"day":case"weekday":this.current.setHours(0);case"hour":this.current.setMinutes(0);case"minute":this.current.setSeconds(0);case"second":this.current.setMilliseconds(0)}if(1!=this.step)switch(this.scale){case"millisecond":this.current.setMilliseconds(this.current.getMilliseconds()-this.current.getMilliseconds()%this.step);break;case"second":this.current.setSeconds(this.current.getSeconds()-this.current.getSeconds()%this.step);break;case"minute":this.current.setMinutes(this.current.getMinutes()-this.current.getMinutes()%this.step);break;case"hour":this.current.setHours(this.current.getHours()-this.current.getHours()%this.step);break;case"weekday":case"day":this.current.setDate(this.current.getDate()-1-(this.current.getDate()-1)%this.step+1);break;case"month":this.current.setMonth(this.current.getMonth()-this.current.getMonth()%this.step);break;case"year":this.current.setFullYear(this.current.getFullYear()-this.current.getFullYear()%this.step)}},s.prototype.hasNext=function(){return this.current.valueOf()<=this._end.valueOf()},s.prototype.next=function(){var t=this.current.valueOf();if(this.current.getMonth()<6)switch(this.scale){case"millisecond":this.current=new Date(this.current.valueOf()+this.step);break;case"second":this.current=new Date(this.current.valueOf()+1e3*this.step);break;case"minute":this.current=new Date(this.current.valueOf()+1e3*this.step*60);break;case"hour":this.current=new Date(this.current.valueOf()+1e3*this.step*60*60);var e=this.current.getHours();this.current.setHours(e-e%this.step);break;case"weekday":case"day":this.current.setDate(this.current.getDate()+this.step); -break;case"month":this.current.setMonth(this.current.getMonth()+this.step);break;case"year":this.current.setFullYear(this.current.getFullYear()+this.step)}else switch(this.scale){case"millisecond":this.current=new Date(this.current.valueOf()+this.step);break;case"second":this.current.setSeconds(this.current.getSeconds()+this.step);break;case"minute":this.current.setMinutes(this.current.getMinutes()+this.step);break;case"hour":this.current.setHours(this.current.getHours()+this.step);break;case"weekday":case"day":this.current.setDate(this.current.getDate()+this.step);break;case"month":this.current.setMonth(this.current.getMonth()+this.step);break;case"year":this.current.setFullYear(this.current.getFullYear()+this.step)}if(1!=this.step)switch(this.scale){case"millisecond":this.current.getMilliseconds()0?t.step:1,this.autoScale=!1)},s.prototype.setAutoScale=function(t){this.autoScale=t},s.prototype.setMinimumStep=function(t){if(void 0!=t){var e=31104e6,i=2592e6,s=864e5,o=36e5,n=6e4,r=1e3,a=1;1e3*e>t&&(this.scale="year",this.step=1e3),500*e>t&&(this.scale="year",this.step=500),100*e>t&&(this.scale="year",this.step=100),50*e>t&&(this.scale="year",this.step=50),10*e>t&&(this.scale="year",this.step=10),5*e>t&&(this.scale="year",this.step=5),e>t&&(this.scale="year",this.step=1),3*i>t&&(this.scale="month",this.step=3),i>t&&(this.scale="month",this.step=1),5*s>t&&(this.scale="day",this.step=5),2*s>t&&(this.scale="day",this.step=2),s>t&&(this.scale="day",this.step=1),s/2>t&&(this.scale="weekday",this.step=1),4*o>t&&(this.scale="hour",this.step=4),o>t&&(this.scale="hour",this.step=1),15*n>t&&(this.scale="minute",this.step=15),10*n>t&&(this.scale="minute",this.step=10),5*n>t&&(this.scale="minute",this.step=5),n>t&&(this.scale="minute",this.step=1),15*r>t&&(this.scale="second",this.step=15),10*r>t&&(this.scale="second",this.step=10),5*r>t&&(this.scale="second",this.step=5),r>t&&(this.scale="second",this.step=1),200*a>t&&(this.scale="millisecond",this.step=200),100*a>t&&(this.scale="millisecond",this.step=100),50*a>t&&(this.scale="millisecond",this.step=50),10*a>t&&(this.scale="millisecond",this.step=10),5*a>t&&(this.scale="millisecond",this.step=5),a>t&&(this.scale="millisecond",this.step=1)}},s.prototype.snap=function(t){var e=new Date(t.valueOf());if("year"==this.scale){var i=e.getFullYear()+Math.round(e.getMonth()/12);e.setFullYear(Math.round(i/this.step)*this.step),e.setMonth(0),e.setDate(0),e.setHours(0),e.setMinutes(0),e.setSeconds(0),e.setMilliseconds(0)}else if("month"==this.scale)e.getDate()>15?(e.setDate(1),e.setMonth(e.getMonth()+1)):e.setDate(1),e.setHours(0),e.setMinutes(0),e.setSeconds(0),e.setMilliseconds(0);else if("day"==this.scale){switch(this.step){case 5:case 2:e.setHours(24*Math.round(e.getHours()/24));break;default:e.setHours(12*Math.round(e.getHours()/12))}e.setMinutes(0),e.setSeconds(0),e.setMilliseconds(0)}else if("weekday"==this.scale){switch(this.step){case 5:case 2:e.setHours(12*Math.round(e.getHours()/12));break;default:e.setHours(6*Math.round(e.getHours()/6))}e.setMinutes(0),e.setSeconds(0),e.setMilliseconds(0)}else if("hour"==this.scale){switch(this.step){case 4:e.setMinutes(60*Math.round(e.getMinutes()/60));break;default:e.setMinutes(30*Math.round(e.getMinutes()/30))}e.setSeconds(0),e.setMilliseconds(0)}else if("minute"==this.scale){switch(this.step){case 15:case 10:e.setMinutes(5*Math.round(e.getMinutes()/5)),e.setSeconds(0);break;case 5:e.setSeconds(60*Math.round(e.getSeconds()/60));break;default:e.setSeconds(30*Math.round(e.getSeconds()/30))}e.setMilliseconds(0)}else if("second"==this.scale)switch(this.step){case 15:case 10:e.setSeconds(5*Math.round(e.getSeconds()/5)),e.setMilliseconds(0);break;case 5:e.setMilliseconds(1e3*Math.round(e.getMilliseconds()/1e3));break;default:e.setMilliseconds(500*Math.round(e.getMilliseconds()/500))}else if("millisecond"==this.scale){var s=this.step>5?this.step/2:1;e.setMilliseconds(Math.round(e.getMilliseconds()/s)*s)}return e},s.prototype.isMajor=function(){if(1==this.switchedYear)switch(this.switchedYear=!1,this.scale){case"year":case"month":case"weekday":case"day":case"hour":case"minute":case"second":case"millisecond":return!0;default:return!1}else if(1==this.switchedMonth)switch(this.switchedMonth=!1,this.scale){case"weekday":case"day":case"hour":case"minute":case"second":case"millisecond":return!0;default:return!1}else if(1==this.switchedDay)switch(this.switchedDay=!1,this.scale){case"millisecond":case"second":case"minute":case"hour":return!0;default:return!1}switch(this.scale){case"millisecond":return 0==this.current.getMilliseconds();case"second":return 0==this.current.getSeconds();case"minute":return 0==this.current.getHours()&&0==this.current.getMinutes();case"hour":return 0==this.current.getHours();case"weekday":case"day":return 1==this.current.getDate();case"month":return 0==this.current.getMonth();case"year":return!1;default:return!1}},s.prototype.getLabelMinor=function(t){void 0==t&&(t=this.current);var e=this.format.minorLabels[this.scale];return e&&e.length>0?o(t).format(e):""},s.prototype.getLabelMajor=function(t){void 0==t&&(t=this.current);var e=this.format.majorLabels[this.scale];return e&&e.length>0?o(t).format(e):""},s.prototype.getClassName=function(){function t(t){return t/h%2==0?" even":" odd"}function e(t){return t.isSame(new Date,"day")?" today":t.isSame(o().add(1,"day"),"day")?" tomorrow":t.isSame(o().add(-1,"day"),"day")?" yesterday":""}function i(t){return t.isSame(new Date,"week")?" current-week":""}function s(t){return t.isSame(new Date,"month")?" current-month":""}function n(t){return t.isSame(new Date,"year")?" current-year":""}var r=o(this.current),a=r.locale?r.locale("en"):r.lang("en"),h=this.step;switch(this.scale){case"millisecond":return t(a.milliseconds()).trim();case"second":return t(a.seconds()).trim();case"minute":return t(a.minutes()).trim();case"hour":var d=a.hours();return 4==this.step&&(d=d+"-"+(d+4)),d+"h"+e(a)+t(a.hours());case"weekday":return a.format("dddd").toLowerCase()+e(a)+i(a)+t(a.date());case"day":var l=a.date(),c=a.format("MMMM").toLowerCase();return"day"+l+" "+c+s(a)+t(l-1);case"month":return a.format("MMMM").toLowerCase()+s(a)+t(a.month());case"year":var p=a.year();return"year"+p+n(a)+t(p);default:return""}},t.exports=s},function(t,e,i){function s(t,e,i){this.id=null,this.parent=null,this.data=t,this.dom=null,this.conversion=e||{},this.options=i||{},this.selected=!1,this.displayed=!1,this.dirty=!0,this.top=null,this.left=null,this.width=null,this.height=null}var o=i(45),n=i(1);s.prototype.stack=!0,s.prototype.select=function(){this.selected=!0,this.dirty=!0,this.displayed&&this.redraw()},s.prototype.unselect=function(){this.selected=!1,this.dirty=!0,this.displayed&&this.redraw()},s.prototype.setData=function(t){this.data=t,this.dirty=!0,this.displayed&&this.redraw()},s.prototype.setParent=function(t){this.displayed?(this.hide(),this.parent=t,this.parent&&this.show()):this.parent=t},s.prototype.isVisible=function(){return!1},s.prototype.show=function(){return!1},s.prototype.hide=function(){return!1},s.prototype.redraw=function(){},s.prototype.repositionX=function(){},s.prototype.repositionY=function(){},s.prototype._repaintDeleteButton=function(t){if(this.selected&&this.options.editable.remove&&!this.dom.deleteButton){var e=this,i=document.createElement("div");i.className="delete",i.title="Delete this item",o(i,{preventDefault:!0}).on("tap",function(t){e.parent.removeFromDataSet(e),t.stopPropagation()}),t.appendChild(i),this.dom.deleteButton=i}else!this.selected&&this.dom.deleteButton&&(this.dom.deleteButton.parentNode&&this.dom.deleteButton.parentNode.removeChild(this.dom.deleteButton),this.dom.deleteButton=null)},s.prototype._updateContents=function(t){var e;if(this.options.template){var i=this.parent.itemSet.itemsData.get(this.id);e=this.options.template(i)}else e=this.data.content;if(e!==this.content){if(e instanceof Element)t.innerHTML="",t.appendChild(e);else if(void 0!=e)t.innerHTML=e;else if("background"!=this.data.type||void 0!==this.data.content)throw new Error('Property "content" missing in item '+this.id);this.content=e}},s.prototype._updateTitle=function(t){null!=this.data.title?t.title=this.data.title||"":t.removeAttribute("title")},s.prototype._updateDataAttributes=function(t){if(this.options.dataAttributes&&this.options.dataAttributes.length>0){var e=[];if(Array.isArray(this.options.dataAttributes))e=this.options.dataAttributes;else{if("all"!=this.options.dataAttributes)return;e=Object.keys(this.data)}for(var i=0;it.start},s.prototype.redraw=function(){var t=this.dom;if(t||(this.dom={},t=this.dom,t.box=document.createElement("div"),t.content=document.createElement("div"),t.content.className="content",t.box.appendChild(t.content),this.dirty=!0),!this.parent)throw new Error("Cannot redraw item: no parent attached");if(!t.box.parentNode){var e=this.parent.dom.background;if(!e)throw new Error("Cannot redraw item: parent has no background container element");e.appendChild(t.box)}if(this.displayed=!0,this.dirty){this._updateContents(this.dom.content),this._updateTitle(this.dom.content),this._updateDataAttributes(this.dom.content),this._updateStyle(this.dom.box);var i=(this.data.className?" "+this.data.className:"")+(this.selected?" selected":"");t.box.className=this.baseClassName+i,this.overflow="hidden"!==window.getComputedStyle(t.content).overflow,this.props.content.width=this.dom.content.offsetWidth,this.height=0,this.dirty=!1}},s.prototype.show=r.prototype.show,s.prototype.hide=r.prototype.hide,s.prototype.repositionX=r.prototype.repositionX,s.prototype.repositionY=function(t){var e="top"===this.options.orientation;this.dom.content.style.top=e?"":"0",this.dom.content.style.bottom=e?"0":"";var i;if(void 0!==this.data.subgroup){var s=this.data.subgroup,o=this.parent.subgroups,r=o[s].index;if(1==e){i=this.parent.subgroups[s].height+t.item.vertical,i+=0==r?t.axis-.5*t.item.vertical:0;var a=this.parent.top;for(var h in o)o.hasOwnProperty(h)&&1==o[h].visible&&o[h].indexr&&(a+=o[h].height+t.item.vertical);i=this.parent.subgroups[s].height+t.item.vertical,this.dom.box.style.top=a+"px",this.dom.box.style.bottom=""}}else this.parent instanceof n?(i=Math.max(this.parent.height,this.parent.itemSet.body.domProps.center.height,this.parent.itemSet.body.domProps.centerContainer.height),this.dom.box.style.top=e?"0":"",this.dom.box.style.bottom=e?"":"0"):(i=this.parent.height,this.dom.box.style.top=this.parent.top+"px",this.dom.box.style.bottom="");this.dom.box.style.height=i+"px"},t.exports=s},function(t,e,i){function s(t,e,i){if(this.props={dot:{width:0,height:0},line:{width:0,height:0}},t&&void 0==t.start)throw new Error('Property "start" missing in item '+t);o.call(this,t,e,i)}{var o=i(20);i(1)}s.prototype=new o(null,null,null),s.prototype.isVisible=function(t){var e=(t.end-t.start)/4;return this.data.start>t.start-e&&this.data.startt.start-e&&this.data.startt.start},s.prototype.redraw=function(){var t=this.dom;if(t||(this.dom={},t=this.dom,t.box=document.createElement("div"),t.content=document.createElement("div"),t.content.className="content",t.box.appendChild(t.content),t.box["timeline-item"]=this,this.dirty=!0),!this.parent)throw new Error("Cannot redraw item: no parent attached");if(!t.box.parentNode){var e=this.parent.dom.foreground;if(!e)throw new Error("Cannot redraw item: parent has no foreground container element");e.appendChild(t.box)}if(this.displayed=!0,this.dirty){this._updateContents(this.dom.content),this._updateTitle(this.dom.box),this._updateDataAttributes(this.dom.box),this._updateStyle(this.dom.box);var i=(this.data.className?" "+this.data.className:"")+(this.selected?" selected":"");t.box.className=this.baseClassName+i,this.overflow="hidden"!==window.getComputedStyle(t.content).overflow,this.dom.content.style.maxWidth="none",this.props.content.width=this.dom.content.offsetWidth,this.height=this.dom.box.offsetHeight,this.dom.content.style.maxWidth="",this.dirty=!1}this._repaintDeleteButton(t.box),this._repaintDragLeft(),this._repaintDragRight()},s.prototype.show=function(){this.displayed||this.redraw()},s.prototype.hide=function(){if(this.displayed){var t=this.dom.box;t.parentNode&&t.parentNode.removeChild(t),this.top=null,this.left=null,this.displayed=!1}},s.prototype.repositionX=function(){var t,e,i=this.parent.width,s=this.conversion.toScreen(this.data.start),o=this.conversion.toScreen(this.data.end);-i>s&&(s=-i),o>2*i&&(o=2*i);var n=Math.max(o-s,1);switch(this.overflow?(this.left=s,this.width=n+this.props.content.width,e=this.props.content.width):(this.left=s,this.width=n,e=Math.min(o-s-2*this.options.padding,this.props.content.width)),this.dom.box.style.left=this.left+"px",this.dom.box.style.width=n+"px",this.options.align){case"left":this.dom.content.style.left="0";break;case"right":this.dom.content.style.left=Math.max(n-e-2*this.options.padding,0)+"px";break;case"center":this.dom.content.style.left=Math.max((n-e-2*this.options.padding)/2,0)+"px";break;default:t=this.overflow?o>0?Math.max(-s,0):-e:0>s?Math.min(-s,o-s-e-2*this.options.padding):0,this.dom.content.style.left=t+"px"}},s.prototype.repositionY=function(){var t=this.options.orientation,e=this.dom.box;e.style.top="top"==t?this.top+"px":this.parent.height-this.top-this.height+"px"},s.prototype._repaintDragLeft=function(){if(this.selected&&this.options.editable.updateTime&&!this.dom.dragLeft){var t=document.createElement("div");t.className="drag-left",t.dragLeftItem=this,o(t,{preventDefault:!0}).on("drag",function(){}),this.dom.box.appendChild(t),this.dom.dragLeft=t}else!this.selected&&this.dom.dragLeft&&(this.dom.dragLeft.parentNode&&this.dom.dragLeft.parentNode.removeChild(this.dom.dragLeft),this.dom.dragLeft=null)},s.prototype._repaintDragRight=function(){if(this.selected&&this.options.editable.updateTime&&!this.dom.dragRight){var t=document.createElement("div");t.className="drag-right",t.dragRightItem=this,o(t,{preventDefault:!0}).on("drag",function(){}),this.dom.box.appendChild(t),this.dom.dragRight=t}else!this.selected&&this.dom.dragRight&&(this.dom.dragRight.parentNode&&this.dom.dragRight.parentNode.removeChild(this.dom.dragRight),this.dom.dragRight=null)},t.exports=s},function(t){function e(){this.options=null,this.props=null}e.prototype.setOptions=function(t){t&&util.extend(this.options,t)},e.prototype.redraw=function(){return!1},e.prototype.destroy=function(){},e.prototype._isResized=function(){var t=this.props._previousWidth!==this.props.width||this.props._previousHeight!==this.props.height;return this.props._previousWidth=this.props.width,this.props._previousHeight=this.props.height,t},t.exports=e},function(t,e,i){function s(t,e){this.body=t,this.defaultOptions={showCurrentTime:!0,locales:a,locale:"en"},this.options=o.extend({},this.defaultOptions),this.offset=0,this._create(),this.setOptions(e)}var o=i(1),n=i(25),r=i(44),a=i(48);s.prototype=new n,s.prototype._create=function(){var t=document.createElement("div");t.className="currenttime",t.style.position="absolute",t.style.top="0px",t.style.height="100%",this.bar=t},s.prototype.destroy=function(){this.options.showCurrentTime=!1,this.redraw(),this.body=null},s.prototype.setOptions=function(t){t&&o.selectiveExtend(["showCurrentTime","locale","locales"],this.options,t)},s.prototype.redraw=function(){if(this.options.showCurrentTime){var t=this.body.dom.backgroundVertical;this.bar.parentNode!=t&&(this.bar.parentNode&&this.bar.parentNode.removeChild(this.bar),t.appendChild(this.bar),this.start());var e=new Date((new Date).valueOf()+this.offset),i=this.body.util.toScreen(e),s=this.options.locales[this.options.locale],o=s.current+" "+s.time+": "+r(e).format("dddd, MMMM Do YYYY, H:mm:ss");o=o.charAt(0).toUpperCase()+o.substring(1),this.bar.style.left=i+"px",this.bar.title=o}else this.bar.parentNode&&this.bar.parentNode.removeChild(this.bar),this.stop();return!1},s.prototype.start=function(){function t(){e.stop();var i=e.body.range.conversion(e.body.domProps.center.width).scale,s=1/i/10;30>s&&(s=30),s>1e3&&(s=1e3),e.redraw(),e.currentTimeTimer=setTimeout(t,s)}var e=this;t()},s.prototype.stop=function(){void 0!==this.currentTimeTimer&&(clearTimeout(this.currentTimeTimer),delete this.currentTimeTimer)},s.prototype.setCurrentTime=function(t){var e=o.convert(t,"Date").valueOf(),i=(new Date).valueOf();this.offset=e-i,this.redraw()},s.prototype.getCurrentTime=function(){return new Date((new Date).valueOf()+this.offset)},t.exports=s},function(t,e,i){function s(t,e){this.body=t,this.defaultOptions={showCustomTime:!1,locales:h,locale:"en"},this.options=n.extend({},this.defaultOptions),this.customTime=new Date,this.eventParams={},this._create(),this.setOptions(e)}var o=i(45),n=i(1),r=i(25),a=i(44),h=i(48);s.prototype=new r,s.prototype.setOptions=function(t){t&&n.selectiveExtend(["showCustomTime","locale","locales"],this.options,t)},s.prototype._create=function(){var t=document.createElement("div");t.className="customtime",t.style.position="absolute",t.style.top="0px",t.style.height="100%",this.bar=t;var e=document.createElement("div");e.style.position="relative",e.style.top="0px",e.style.left="-10px",e.style.height="100%",e.style.width="20px",t.appendChild(e),this.hammer=o(t,{prevent_default:!0}),this.hammer.on("dragstart",this._onDragStart.bind(this)),this.hammer.on("drag",this._onDrag.bind(this)),this.hammer.on("dragend",this._onDragEnd.bind(this))},s.prototype.destroy=function(){this.options.showCustomTime=!1,this.redraw(),this.hammer.enable(!1),this.hammer=null,this.body=null},s.prototype.redraw=function(){if(this.options.showCustomTime){var t=this.body.dom.backgroundVertical;this.bar.parentNode!=t&&(this.bar.parentNode&&this.bar.parentNode.removeChild(this.bar),t.appendChild(this.bar));var e=this.body.util.toScreen(this.customTime),i=this.options.locales[this.options.locale],s=i.time+": "+a(this.customTime).format("dddd, MMMM Do YYYY, H:mm:ss");s=s.charAt(0).toUpperCase()+s.substring(1),this.bar.style.left=e+"px",this.bar.title=s}else this.bar.parentNode&&this.bar.parentNode.removeChild(this.bar);return!1},s.prototype.setCustomTime=function(t){this.customTime=n.convert(t,"Date"),this.redraw()},s.prototype.getCustomTime=function(){return new Date(this.customTime.valueOf())},s.prototype._onDragStart=function(t){this.eventParams.dragging=!0,this.eventParams.customTime=this.customTime,t.stopPropagation(),t.preventDefault()},s.prototype._onDrag=function(t){if(this.eventParams.dragging){var e=t.gesture.deltaX,i=this.body.util.toScreen(this.eventParams.customTime)+e,s=this.body.util.toTime(i);this.setCustomTime(s),this.body.emitter.emit("timechange",{time:new Date(this.customTime.valueOf())}),t.stopPropagation(),t.preventDefault()}},s.prototype._onDragEnd=function(t){this.eventParams.dragging&&(this.body.emitter.emit("timechanged",{time:new Date(this.customTime.valueOf())}),t.stopPropagation(),t.preventDefault())},t.exports=s},function(t,e,i){function s(t,e,i,s){this.id=o.randomUUID(),this.body=t,this.defaultOptions={orientation:"left",showMinorLabels:!0,showMajorLabels:!0,icons:!0,majorLinesOffset:7,minorLinesOffset:4,labelOffsetX:10,labelOffsetY:2,iconWidth:20,width:"40px",visible:!0,alignZeros:!0,customRange:{left:{min:void 0,max:void 0},right:{min:void 0,max:void 0}},title:{left:{text:void 0},right:{text:void 0}},format:{left:{decimals:void 0},right:{decimals:void 0}}},this.linegraphOptions=s,this.linegraphSVG=i,this.props={},this.DOMelements={lines:{},labels:{},title:{}},this.dom={},this.range={start:0,end:0},this.options=o.extend({},this.defaultOptions),this.conversionFactor=1,this.setOptions(e),this.width=Number((""+this.options.width).replace("px","")),this.minWidth=this.width,this.height=this.linegraphSVG.offsetHeight,this.hidden=!1,this.stepPixels=25,this.stepPixelsForced=25,this.zeroCrossing=-1,this.lineOffset=0,this.master=!0,this.svgElements={},this.iconsRemoved=!1,this.groups={},this.amountOfGroups=0,this._create();var n=this;this.body.emitter.on("verticalDrag",function(){n.dom.lineContainer.style.top=n.body.domProps.scrollTop+"px"})}var o=i(1),n=i(2),r=i(25),a=i(16);s.prototype=new r,s.prototype.addGroup=function(t,e){this.groups.hasOwnProperty(t)||(this.groups[t]=e),this.amountOfGroups+=1},s.prototype.updateGroup=function(t,e){this.groups[t]=e},s.prototype.removeGroup=function(t){this.groups.hasOwnProperty(t)&&(delete this.groups[t],this.amountOfGroups-=1)},s.prototype.setOptions=function(t){if(t){var e=!1;this.options.orientation!=t.orientation&&void 0!==t.orientation&&(e=!0);var i=["orientation","showMinorLabels","showMajorLabels","icons","majorLinesOffset","minorLinesOffset","labelOffsetX","labelOffsetY","iconWidth","width","visible","customRange","title","format","alignZeros"];o.selectiveExtend(i,this.options,t),this.minWidth=Number((""+this.options.width).replace("px","")),1==e&&this.dom.frame&&(this.hide(),this.show())}},s.prototype._create=function(){this.dom.frame=document.createElement("div"),this.dom.frame.style.width=this.options.width,this.dom.frame.style.height=this.height,this.dom.lineContainer=document.createElement("div"),this.dom.lineContainer.style.width="100%",this.dom.lineContainer.style.height=this.height,this.dom.lineContainer.style.position="relative",this.svg=document.createElementNS("http://www.w3.org/2000/svg","svg"),this.svg.style.position="absolute",this.svg.style.top="0px",this.svg.style.height="100%",this.svg.style.width="100%",this.svg.style.display="block",this.dom.frame.appendChild(this.svg)},s.prototype._redrawGroupIcons=function(){n.prepareElements(this.svgElements);var t,e=this.options.iconWidth,i=15,s=4,o=s+.5*i;t="left"==this.options.orientation?s:this.width-e-s;for(var r in this.groups)this.groups.hasOwnProperty(r)&&(1!=this.groups[r].visible||void 0!==this.linegraphOptions.visibility[r]&&1!=this.linegraphOptions.visibility[r]||(this.groups[r].drawIcon(t,o,this.svgElements,this.svg,e,i),o+=i+s));n.cleanupElements(this.svgElements),this.iconsRemoved=!1},s.prototype._cleanupIcons=function(){0==this.iconsRemoved&&(n.prepareElements(this.svgElements),n.cleanupElements(this.svgElements),this.iconsRemoved=!0)},s.prototype.show=function(){this.hidden=!1,this.dom.frame.parentNode||("left"==this.options.orientation?this.body.dom.left.appendChild(this.dom.frame):this.body.dom.right.appendChild(this.dom.frame)),this.dom.lineContainer.parentNode||this.body.dom.backgroundHorizontal.appendChild(this.dom.lineContainer)},s.prototype.hide=function(){this.hidden=!0,this.dom.frame.parentNode&&this.dom.frame.parentNode.removeChild(this.dom.frame),this.dom.lineContainer.parentNode&&this.dom.lineContainer.parentNode.removeChild(this.dom.lineContainer)},s.prototype.setRange=function(t,e){0==this.master&&1==this.options.alignZeros&&-1!=this.zeroCrossing&&t>0&&(t=0),this.range.start=t,this.range.end=e},s.prototype.redraw=function(){var t=!1,e=0;this.dom.lineContainer.style.top=this.body.domProps.scrollTop+"px";for(var i in this.groups)this.groups.hasOwnProperty(i)&&(1!=this.groups[i].visible||void 0!==this.linegraphOptions.visibility[i]&&1!=this.linegraphOptions.visibility[i]||e++);if(0==this.amountOfGroups||0==e)this.hide();else{this.show(),this.height=Number(this.linegraphSVG.style.height.replace("px","")),this.dom.lineContainer.style.height=this.height+"px",this.width=1==this.options.visible?Number((""+this.options.width).replace("px","")):0;var s=this.props,o=this.dom.frame;o.className="dataaxis",this._calculateCharSize();var n=this.options.orientation,r=this.options.showMinorLabels,a=this.options.showMajorLabels;s.minorLabelHeight=r?s.minorCharHeight:0,s.majorLabelHeight=a?s.majorCharHeight:0,s.minorLineWidth=this.body.dom.backgroundHorizontal.offsetWidth-this.lineOffset-this.width+2*this.options.minorLinesOffset,s.minorLineHeight=1,s.majorLineWidth=this.body.dom.backgroundHorizontal.offsetWidth-this.lineOffset-this.width+2*this.options.majorLinesOffset,s.majorLineHeight=1,"left"==n?(o.style.top="0",o.style.left="0",o.style.bottom="",o.style.width=this.width+"px",o.style.height=this.height+"px",this.props.width=this.body.domProps.left.width,this.props.height=this.body.domProps.left.height):(o.style.top="",o.style.bottom="0",o.style.left="0",o.style.width=this.width+"px",o.style.height=this.height+"px",this.props.width=this.body.domProps.right.width,this.props.height=this.body.domProps.right.height),t=this._redrawLabels(),t=this._isResized()||t,1==this.options.icons?this._redrawGroupIcons():this._cleanupIcons(),this._redrawTitle(n)}return t},s.prototype._redrawLabels=function(){var t=!1;n.prepareElements(this.DOMelements.lines),n.prepareElements(this.DOMelements.labels);var e=this.options.orientation,i=this.master?this.props.majorCharHeight||10:this.stepPixelsForced,s=new a(this.range.start,this.range.end,i,this.dom.frame.offsetHeight,this.options.customRange[this.options.orientation],0==this.master&&this.options.alignZeros);this.step=s;var o=(this.dom.frame.offsetHeight-s.deadSpace*(this.dom.frame.offsetHeight/s.marginRange))/((s.marginRange-s.deadSpace)/s.step);this.stepPixels=o;var r=this.height/o,h=0;if(0==this.master){o=this.stepPixelsForced,h=Math.round(this.dom.frame.offsetHeight/o-r); -for(var d=0;.5*h>d;d++)s.previous();if(r=this.height/o,-1!=this.zeroCrossing&&1==this.options.alignZeros){var l=s.marginEnd/s.step-this.zeroCrossing;if(l>0)for(var d=0;l>d;d++)s.next();else if(0>l)for(var d=0;-l>d;d++)s.previous()}}else r+=.25;this.valueAtZero=s.marginEnd;var c,p=0,u=1;void 0!==this.options.format[e]&&(c=this.options.format[e].decimals),this.maxLabelSize=0;for(var m=0;u=0&&this._redrawLabel(m-2,s.getCurrent(c),e,"yAxis major",this.props.majorCharHeight),this._redrawLine(m,e,"grid horizontal major",this.options.majorLinesOffset,this.props.majorLineWidth)):this._redrawLine(m,e,"grid horizontal minor",this.options.minorLinesOffset,this.props.minorLineWidth),1==this.master&&0==s.current&&(this.zeroCrossing=u),u++}this.conversionFactor=0==this.master?m/(this.valueAtZero-s.current):this.dom.frame.offsetHeight/s.marginRange;var g=0;void 0!==this.options.title[e]&&void 0!==this.options.title[e].text&&(g=this.props.titleCharHeight);var v=1==this.options.icons?Math.max(this.options.iconWidth,g)+this.options.labelOffsetX+15:g+this.options.labelOffsetX+15;return this.maxLabelSize>this.width-v&&1==this.options.visible?(this.width=this.maxLabelSize+v,this.options.width=this.width+"px",n.cleanupElements(this.DOMelements.lines),n.cleanupElements(this.DOMelements.labels),this.redraw(),t=!0):this.maxLabelSizethis.minWidth?(this.width=Math.max(this.minWidth,this.maxLabelSize+v),this.options.width=this.width+"px",n.cleanupElements(this.DOMelements.lines),n.cleanupElements(this.DOMelements.labels),this.redraw(),t=!0):(n.cleanupElements(this.DOMelements.lines),n.cleanupElements(this.DOMelements.labels),t=!1),t},s.prototype.convertValue=function(t){var e=this.valueAtZero-t,i=e*this.conversionFactor;return i},s.prototype._redrawLabel=function(t,e,i,s,o){var r=n.getDOMElement("div",this.DOMelements.labels,this.dom.frame);r.className=s,r.innerHTML=e,"left"==i?(r.style.left="-"+this.options.labelOffsetX+"px",r.style.textAlign="right"):(r.style.right="-"+this.options.labelOffsetX+"px",r.style.textAlign="left"),r.style.top=t-.5*o+this.options.labelOffsetY+"px",e+="";var a=Math.max(this.props.majorCharWidth,this.props.minorCharWidth);this.maxLabelSized;d++){var c=this.visibleItems[d];c.repositionY(e)}return s},s.prototype._calculateHeight=function(t){var e,i=this.visibleItems;this.resetSubgroups();var s=this;if(i.length){var n=i[0].top,r=i[0].top+i[0].height;if(o.forEach(i,function(t){n=Math.min(n,t.top),r=Math.max(r,t.top+t.height),void 0!==t.data.subgroup&&(s.subgroups[t.data.subgroup].height=Math.max(s.subgroups[t.data.subgroup].height,t.height),s.subgroups[t.data.subgroup].visible=!0)}),n>t.axis){var a=n-t.axis;r-=a,o.forEach(i,function(t){t.top-=a})}e=r+t.item.vertical/2}else e=t.axis+t.item.vertical;return e=Math.max(e,this.props.label.height)},s.prototype.show=function(){this.dom.label.parentNode||this.itemSet.dom.labelSet.appendChild(this.dom.label),this.dom.foreground.parentNode||this.itemSet.dom.foreground.appendChild(this.dom.foreground),this.dom.background.parentNode||this.itemSet.dom.background.appendChild(this.dom.background),this.dom.axis.parentNode||this.itemSet.dom.axis.appendChild(this.dom.axis)},s.prototype.hide=function(){var t=this.dom.label;t.parentNode&&t.parentNode.removeChild(t);var e=this.dom.foreground;e.parentNode&&e.parentNode.removeChild(e);var i=this.dom.background;i.parentNode&&i.parentNode.removeChild(i);var s=this.dom.axis;s.parentNode&&s.parentNode.removeChild(s)},s.prototype.add=function(t){if(this.items[t.id]=t,t.setParent(this),void 0!==t.data.subgroup&&(void 0===this.subgroups[t.data.subgroup]&&(this.subgroups[t.data.subgroup]={height:0,visible:!1,index:this.subgroupIndex,items:[]},this.subgroupIndex++),this.subgroups[t.data.subgroup].items.push(t)),this.orderSubgroups(),-1==this.visibleItems.indexOf(t)){var e=this.itemSet.body.range;this._checkIfVisible(t,this.visibleItems,e)}},s.prototype.orderSubgroups=function(){if(void 0!==this.subgroupOrderer){var t=[];if("string"==typeof this.subgroupOrderer){for(var e in this.subgroups)t.push({subgroup:e,sortField:this.subgroups[e].items[0].data[this.subgroupOrderer]});t.sort(function(t,e){return t.sortField-e.sortField})}else if("function"==typeof this.subgroupOrderer){for(var e in this.subgroups)t.push(this.subgroups[e].items[0].data);t.sort(this.subgroupOrderer)}if(t.length>0)for(var i=0;it?-1:l>=t?0:1};if(e.length>0)for(n=0;nl}),1==this.checkRangedItems)for(this.checkRangedItems=!1,n=0;nl})}for(n=0;n=0&&(n=e[r],!o(n));r--)void 0===s[n.id]&&(s[n.id]=!0,i.push(n));for(r=t+1;rs;s++){var n=this.visibleItems[s];n.repositionY(e)}return i},s.prototype.show=function(){this.dom.background.parentNode||this.itemSet.dom.background.appendChild(this.dom.background)},t.exports=s},function(t,e,i){function s(t,e){this.body=t,this.defaultOptions={type:null,orientation:"bottom",align:"auto",stack:!0,groupOrder:null,selectable:!0,editable:{updateTime:!1,updateGroup:!1,add:!1,remove:!1},onAdd:function(t,e){e(t)},onUpdate:function(t,e){e(t)},onMove:function(t,e){e(t)},onRemove:function(t,e){e(t)},onMoving:function(t,e){e(t)},margin:{item:{horizontal:10,vertical:10},axis:20},padding:5},this.options=n.extend({},this.defaultOptions),this.itemOptions={type:{start:"Date",end:"Date"}},this.conversion={toScreen:t.util.toScreen,toTime:t.util.toTime},this.dom={},this.props={},this.hammer=null;var i=this;this.itemsData=null,this.groupsData=null,this.itemListeners={add:function(t,e){i._onAdd(e.items)},update:function(t,e){i._onUpdate(e.items)},remove:function(t,e){i._onRemove(e.items)}},this.groupListeners={add:function(t,e){i._onAddGroups(e.items)},update:function(t,e){i._onUpdateGroups(e.items)},remove:function(t,e){i._onRemoveGroups(e.items)}},this.items={},this.groups={},this.groupIds=[],this.selection=[],this.stackDirty=!0,this.touchParams={},this._create(),this.setOptions(e)}var o=i(45),n=i(1),r=i(3),a=i(4),h=i(25),d=i(30),l=i(31),c=i(22),p=i(23),u=i(24),m=i(21),f="__ungrouped__",g="__background__";s.prototype=new h,s.types={background:m,box:c,range:u,point:p},s.prototype._create=function(){var t=document.createElement("div");t.className="itemset",t["timeline-itemset"]=this,this.dom.frame=t;var e=document.createElement("div");e.className="background",t.appendChild(e),this.dom.background=e;var i=document.createElement("div");i.className="foreground",t.appendChild(i),this.dom.foreground=i;var s=document.createElement("div");s.className="axis",this.dom.axis=s;var n=document.createElement("div");n.className="labelset",this.dom.labelSet=n,this._updateUngrouped();var r=new l(g,null,this);r.show(),this.groups[g]=r,this.hammer=o(this.body.dom.centerContainer,{preventDefault:!0}),this.hammer.on("touch",this._onTouch.bind(this)),this.hammer.on("dragstart",this._onDragStart.bind(this)),this.hammer.on("drag",this._onDrag.bind(this)),this.hammer.on("dragend",this._onDragEnd.bind(this)),this.hammer.on("tap",this._onSelectItem.bind(this)),this.hammer.on("hold",this._onMultiSelectItem.bind(this)),this.hammer.on("doubletap",this._onAddItem.bind(this)),this.show()},s.prototype.setOptions=function(t){if(t){var e=["type","align","orientation","padding","stack","selectable","groupOrder","dataAttributes","template","hide"];n.selectiveExtend(e,this.options,t),"margin"in t&&("number"==typeof t.margin?(this.options.margin.axis=t.margin,this.options.margin.item.horizontal=t.margin,this.options.margin.item.vertical=t.margin):"object"==typeof t.margin&&(n.selectiveExtend(["axis"],this.options.margin,t.margin),"item"in t.margin&&("number"==typeof t.margin.item?(this.options.margin.item.horizontal=t.margin.item,this.options.margin.item.vertical=t.margin.item):"object"==typeof t.margin.item&&n.selectiveExtend(["horizontal","vertical"],this.options.margin.item,t.margin.item)))),"editable"in t&&("boolean"==typeof t.editable?(this.options.editable.updateTime=t.editable,this.options.editable.updateGroup=t.editable,this.options.editable.add=t.editable,this.options.editable.remove=t.editable):"object"==typeof t.editable&&n.selectiveExtend(["updateTime","updateGroup","add","remove"],this.options.editable,t.editable));var i=function(e){var i=t[e];if(i){if(!(i instanceof Function))throw new Error("option "+e+" must be a function "+e+"(item, callback)");this.options[e]=i}}.bind(this);["onAdd","onUpdate","onRemove","onMove","onMoving"].forEach(i),this.markDirty()}},s.prototype.markDirty=function(){this.groupIds=[],this.stackDirty=!0},s.prototype.destroy=function(){this.hide(),this.setItems(null),this.setGroups(null),this.hammer=null,this.body=null,this.conversion=null},s.prototype.hide=function(){this.dom.frame.parentNode&&this.dom.frame.parentNode.removeChild(this.dom.frame),this.dom.axis.parentNode&&this.dom.axis.parentNode.removeChild(this.dom.axis),this.dom.labelSet.parentNode&&this.dom.labelSet.parentNode.removeChild(this.dom.labelSet)},s.prototype.show=function(){this.dom.frame.parentNode||this.body.dom.center.appendChild(this.dom.frame),this.dom.axis.parentNode||this.body.dom.backgroundVertical.appendChild(this.dom.axis),this.dom.labelSet.parentNode||this.body.dom.left.appendChild(this.dom.labelSet)},s.prototype.setSelection=function(t){var e,i,s,o;for(void 0==t&&(t=[]),Array.isArray(t)||(t=[t]),e=0,i=this.selection.length;i>e;e++)s=this.selection[e],o=this.items[s],o&&o.unselect();for(this.selection=[],e=0,i=t.length;i>e;e++)s=t[e],o=this.items[s],o&&(this.selection.push(s),o.select())},s.prototype.getSelection=function(){return this.selection.concat([])},s.prototype.getVisibleItems=function(){var t=this.body.range.getRange(),e=this.body.util.toScreen(t.start),i=this.body.util.toScreen(t.end),s=[];for(var o in this.groups)if(this.groups.hasOwnProperty(o))for(var n=this.groups[o],r=n.visibleItems,a=0;ae&&s.push(h.id)}return s},s.prototype._deselect=function(t){for(var e=this.selection,i=0,s=e.length;s>i;i++)if(e[i]==t){e.splice(i,1);break}},s.prototype.redraw=function(){var t=this.options.margin,e=this.body.range,i=n.option.asSize,s=this.options,o=s.orientation,r=!1,a=this.dom.frame,h=s.editable.updateTime||s.editable.updateGroup;this.props.top=this.body.domProps.top.height+this.body.domProps.border.top,this.props.left=this.body.domProps.left.width+this.body.domProps.border.left,a.className="itemset"+(h?" editable":""),r=this._orderGroups()||r;var d=e.end-e.start,l=d!=this.lastVisibleInterval||this.props.width!=this.props.lastWidth;l&&(this.stackDirty=!0),this.lastVisibleInterval=d,this.props.lastWidth=this.props.width;var c=this.stackDirty,p=this._firstGroup(),u={item:t.item,axis:t.axis},m={item:t.item,axis:t.item.vertical/2},f=0,v=t.axis+t.item.vertical;return this.groups[g].redraw(e,m,c),n.forEach(this.groups,function(t){var i=t==p?u:m,s=t.redraw(e,i,c);r=s||r,f+=t.height}),f=Math.max(f,v),this.stackDirty=!1,a.style.height=i(f),this.props.width=a.offsetWidth,this.props.height=f,this.dom.axis.style.top=i("top"==o?this.body.domProps.top.height+this.body.domProps.border.top:this.body.domProps.top.height+this.body.domProps.centerContainer.height),this.dom.axis.style.left="0",r=this._isResized()||r},s.prototype._firstGroup=function(){var t="top"==this.options.orientation?0:this.groupIds.length-1,e=this.groupIds[t],i=this.groups[e]||this.groups[f];return i||null},s.prototype._updateUngrouped=function(){{var t,e,i=this.groups[f];this.groups[g]}if(this.groupsData){if(i){i.hide(),delete this.groups[f];for(e in this.items)if(this.items.hasOwnProperty(e)){t=this.items[e],t.parent&&t.parent.remove(t);var s=this._getGroupId(t.data),o=this.groups[s];o&&o.add(t)||t.hide()}}}else if(!i){var n=null,r=null;i=new d(n,r,this),this.groups[f]=i;for(e in this.items)this.items.hasOwnProperty(e)&&(t=this.items[e],i.add(t));i.show()}},s.prototype.getLabelSet=function(){return this.dom.labelSet},s.prototype.setItems=function(t){var e,i=this,s=this.itemsData;if(t){if(!(t instanceof r||t instanceof a))throw new TypeError("Data must be an instance of DataSet or DataView");this.itemsData=t}else this.itemsData=null;if(s&&(n.forEach(this.itemListeners,function(t,e){s.off(e,t)}),e=s.getIds(),this._onRemove(e)),this.itemsData){var o=this.id;n.forEach(this.itemListeners,function(t,e){i.itemsData.on(e,t,o)}),e=this.itemsData.getIds(),this._onAdd(e),this._updateUngrouped()}},s.prototype.getItems=function(){return this.itemsData},s.prototype.setGroups=function(t){var e,i=this;if(this.groupsData&&(n.forEach(this.groupListeners,function(t,e){i.groupsData.unsubscribe(e,t)}),e=this.groupsData.getIds(),this.groupsData=null,this._onRemoveGroups(e)),t){if(!(t instanceof r||t instanceof a))throw new TypeError("Data must be an instance of DataSet or DataView");this.groupsData=t}else this.groupsData=null;if(this.groupsData){var s=this.id;n.forEach(this.groupListeners,function(t,e){i.groupsData.on(e,t,s)}),e=this.groupsData.getIds(),this._onAddGroups(e)}this._updateUngrouped(),this._order(),this.body.emitter.emit("change",{queue:!0})},s.prototype.getGroups=function(){return this.groupsData},s.prototype.removeItem=function(t){var e=this.itemsData.get(t),i=this.itemsData.getDataSet();e&&this.options.onRemove(e,function(e){e&&i.remove(t)})},s.prototype._getType=function(t){return t.type||this.options.type||(t.end?"range":"box")},s.prototype._getGroupId=function(t){var e=this._getType(t);return"background"==e&&void 0==t.group?g:this.groupsData?t.group:f},s.prototype._onUpdate=function(t){var e=this;t.forEach(function(t){var i=e.itemsData.get(t,e.itemOptions),o=e.items[t],n=e._getType(i),r=s.types[n];if(o&&(r&&o instanceof r?e._updateItem(o,i):(e._removeItem(o),o=null)),!o){if(!r)throw new TypeError("rangeoverflow"==n?'Item type "rangeoverflow" is deprecated. Use css styling instead: .vis.timeline .item.range .content {overflow: visible;}':'Unknown item type "'+n+'"');o=new r(i,e.conversion,e.options),o.id=t,e._addItem(o)}}),this._order(),this.stackDirty=!0,this.body.emitter.emit("change",{queue:!0})},s.prototype._onAdd=s.prototype._onUpdate,s.prototype._onRemove=function(t){var e=0,i=this;t.forEach(function(t){var s=i.items[t];s&&(e++,i._removeItem(s))}),e&&(this._order(),this.stackDirty=!0,this.body.emitter.emit("change",{queue:!0}))},s.prototype._order=function(){n.forEach(this.groups,function(t){t.order()})},s.prototype._onUpdateGroups=function(t){this._onAddGroups(t)},s.prototype._onAddGroups=function(t){var e=this;t.forEach(function(t){var i=e.groupsData.get(t),s=e.groups[t];if(s)s.setData(i);else{if(t==f||t==g)throw new Error("Illegal group id. "+t+" is a reserved id.");var o=Object.create(e.options);n.extend(o,{height:null}),s=new d(t,i,e),e.groups[t]=s;for(var r in e.items)if(e.items.hasOwnProperty(r)){var a=e.items[r];a.data.group==t&&s.add(a)}s.order(),s.show()}}),this.body.emitter.emit("change",{queue:!0})},s.prototype._onRemoveGroups=function(t){var e=this.groups;t.forEach(function(t){var i=e[t];i&&(i.hide(),delete e[t])}),this.markDirty(),this.body.emitter.emit("change",{queue:!0})},s.prototype._orderGroups=function(){if(this.groupsData){var t=this.groupsData.getIds({order:this.options.groupOrder}),e=!n.equalArray(t,this.groupIds);if(e){var i=this.groups;t.forEach(function(t){i[t].hide()}),t.forEach(function(t){i[t].show()}),this.groupIds=t}return e}return!1},s.prototype._addItem=function(t){this.items[t.id]=t;var e=this._getGroupId(t.data),i=this.groups[e];i&&i.add(t)},s.prototype._updateItem=function(t,e){var i=t.data.group;if(t.setData(e),i!=t.data.group){var s=this.groups[i];s&&s.remove(t);var o=this._getGroupId(t.data),n=this.groups[o];n&&n.add(t)}},s.prototype._removeItem=function(t){t.hide(),delete this.items[t.id];var e=this.selection.indexOf(t.id);-1!=e&&this.selection.splice(e,1),t.parent&&t.parent.remove(t)},s.prototype._constructByEndArray=function(t){for(var e=[],i=0;i0||o.length>0)&&this.body.emitter.emit("select",{items:a})}},s.prototype._onAddItem=function(t){if(this.options.selectable&&this.options.editable.add){var e=this,i=this.body.util.snap||null,o=s.itemFromTarget(t);if(o){var r=e.itemsData.get(o.id);this.options.onUpdate(r,function(t){t&&e.itemsData.getDataSet().update(t)})}else{var a=n.getAbsoluteLeft(this.dom.frame),h=t.gesture.center.pageX-a,d=this.body.util.toTime(h),l={start:i?i(d):d,content:"new item"};if("range"===this.options.type){var c=this.body.util.toTime(h+this.props.width/5);l.end=i?i(c):c}l[this.itemsData._fieldId]=n.randomUUID();var p=s.groupFromTarget(t);p&&(l.group=p.groupId),this.options.onAdd(l,function(t){t&&e.itemsData.getDataSet().add(t)})}}},s.prototype._onMultiSelectItem=function(t){if(this.options.selectable){var e,i=s.itemFromTarget(t);if(i){e=this.getSelection();var o=t.gesture.touches[0]&&t.gesture.touches[0].shiftKey||!1;if(o){e.push(i.id);var n=s._getItemRange(this.itemsData.get(e,this.itemOptions));e=[];for(var r in this.items)if(this.items.hasOwnProperty(r)){var a=this.items[r],h=a.data.start,d=void 0!==a.data.end?a.data.end:h;h>=n.min&&d<=n.max&&e.push(a.id)}}else{var l=e.indexOf(i.id);-1==l?e.push(i.id):e.splice(l,1)}this.setSelection(e),this.body.emitter.emit("select",{items:this.getSelection()})}}},s._getItemRange=function(t){var e=null,i=null;return t.forEach(function(t){(null==i||t.starte)&&(e=t.end):(null==e||t.start>e)&&(e=t.start)}),{min:i,max:e}},s.itemFromTarget=function(t){for(var e=t.target;e;){if(e.hasOwnProperty("timeline-item"))return e["timeline-item"];e=e.parentNode}return null},s.groupFromTarget=function(t){for(var e=t.target;e;){if(e.hasOwnProperty("timeline-group"))return e["timeline-group"];e=e.parentNode}return null},s.itemSetFromTarget=function(t){for(var e=t.target;e;){if(e.hasOwnProperty("timeline-itemset"))return e["timeline-itemset"];e=e.parentNode}return null},t.exports=s},function(t,e,i){function s(t,e,i,s){this.body=t,this.defaultOptions={enabled:!0,icons:!0,iconSize:20,iconSpacing:6,left:{visible:!0,position:"top-left"},right:{visible:!0,position:"top-left"}},this.side=i,this.options=o.extend({},this.defaultOptions),this.linegraphOptions=s,this.svgElements={},this.dom={},this.groups={},this.amountOfGroups=0,this._create(),this.setOptions(e)}var o=i(1),n=i(2),r=i(25);s.prototype=new r,s.prototype.clear=function(){this.groups={},this.amountOfGroups=0},s.prototype.addGroup=function(t,e){this.groups.hasOwnProperty(t)||(this.groups[t]=e),this.amountOfGroups+=1 -},s.prototype.updateGroup=function(t,e){this.groups[t]=e},s.prototype.removeGroup=function(t){this.groups.hasOwnProperty(t)&&(delete this.groups[t],this.amountOfGroups-=1)},s.prototype._create=function(){this.dom.frame=document.createElement("div"),this.dom.frame.className="legend",this.dom.frame.style.position="absolute",this.dom.frame.style.top="10px",this.dom.frame.style.display="block",this.dom.textArea=document.createElement("div"),this.dom.textArea.className="legendText",this.dom.textArea.style.position="relative",this.dom.textArea.style.top="0px",this.svg=document.createElementNS("http://www.w3.org/2000/svg","svg"),this.svg.style.position="absolute",this.svg.style.top="0px",this.svg.style.width=this.options.iconSize+5+"px",this.svg.style.height="100%",this.dom.frame.appendChild(this.svg),this.dom.frame.appendChild(this.dom.textArea)},s.prototype.hide=function(){this.dom.frame.parentNode&&this.dom.frame.parentNode.removeChild(this.dom.frame)},s.prototype.show=function(){this.dom.frame.parentNode||this.body.dom.center.appendChild(this.dom.frame)},s.prototype.setOptions=function(t){var e=["enabled","orientation","icons","left","right"];o.selectiveDeepExtend(e,this.options,t)},s.prototype.redraw=function(){var t=0;for(var e in this.groups)this.groups.hasOwnProperty(e)&&(1!=this.groups[e].visible||void 0!==this.linegraphOptions.visibility[e]&&1!=this.linegraphOptions.visibility[e]||t++);if(0==this.options[this.side].visible||0==this.amountOfGroups||0==this.options.enabled||0==t)this.hide();else{if(this.show(),"top-left"==this.options[this.side].position||"bottom-left"==this.options[this.side].position?(this.dom.frame.style.left="4px",this.dom.frame.style.textAlign="left",this.dom.textArea.style.textAlign="left",this.dom.textArea.style.left=this.options.iconSize+15+"px",this.dom.textArea.style.right="",this.svg.style.left="0px",this.svg.style.right=""):(this.dom.frame.style.right="4px",this.dom.frame.style.textAlign="right",this.dom.textArea.style.textAlign="right",this.dom.textArea.style.right=this.options.iconSize+15+"px",this.dom.textArea.style.left="",this.svg.style.right="0px",this.svg.style.left=""),"top-left"==this.options[this.side].position||"top-right"==this.options[this.side].position)this.dom.frame.style.top=4-Number(this.body.dom.center.style.top.replace("px",""))+"px",this.dom.frame.style.bottom="";else{var i=this.body.domProps.center.height-this.body.domProps.centerContainer.height;this.dom.frame.style.bottom=4+i+Number(this.body.dom.center.style.top.replace("px",""))+"px",this.dom.frame.style.top=""}0==this.options.icons?(this.dom.frame.style.width=this.dom.textArea.offsetWidth+10+"px",this.dom.textArea.style.right="",this.dom.textArea.style.left="",this.svg.style.width="0px"):(this.dom.frame.style.width=this.options.iconSize+15+this.dom.textArea.offsetWidth+10+"px",this.drawLegendIcons());var s="";for(var e in this.groups)this.groups.hasOwnProperty(e)&&(1!=this.groups[e].visible||void 0!==this.linegraphOptions.visibility[e]&&1!=this.linegraphOptions.visibility[e]||(s+=this.groups[e].content+"
"));this.dom.textArea.innerHTML=s,this.dom.textArea.style.lineHeight=.75*this.options.iconSize+this.options.iconSpacing+"px"}},s.prototype.drawLegendIcons=function(){if(this.dom.frame.parentNode){n.prepareElements(this.svgElements);var t=window.getComputedStyle(this.dom.frame).paddingTop,e=Number(t.replace("px","")),i=e,s=this.options.iconSize,o=.75*this.options.iconSize,r=e+.5*o+3;this.svg.style.width=s+5+e+"px";for(var a in this.groups)this.groups.hasOwnProperty(a)&&(1!=this.groups[a].visible||void 0!==this.linegraphOptions.visibility[a]&&1!=this.linegraphOptions.visibility[a]||(this.groups[a].drawIcon(i,r,this.svgElements,this.svg,s,o),r+=o+this.options.iconSpacing));n.cleanupElements(this.svgElements)}},t.exports=s},function(t,e,i){function s(t,e){this.id=o.randomUUID(),this.body=t,this.defaultOptions={yAxisOrientation:"left",defaultGroup:"default",sort:!0,sampling:!0,graphHeight:"400px",shaded:{enabled:!1,orientation:"bottom"},style:"line",barChart:{width:50,handleOverlap:"overlap",align:"center"},catmullRom:{enabled:!0,parametrization:"centripetal",alpha:.5},drawPoints:{enabled:!0,size:6,style:"square"},dataAxis:{showMinorLabels:!0,showMajorLabels:!0,icons:!1,width:"40px",visible:!0,alignZeros:!0,customRange:{left:{min:void 0,max:void 0},right:{min:void 0,max:void 0}}},legend:{enabled:!1,icons:!0,left:{visible:!0,position:"top-left"},right:{visible:!0,position:"top-right"}},groups:{visibility:{}}},this.options=o.extend({},this.defaultOptions),this.dom={},this.props={},this.hammer=null,this.groups={},this.abortedGraphUpdate=!1,this.updateSVGheight=!1,this.updateSVGheightOnResize=!1;var i=this;this.itemsData=null,this.groupsData=null,this.itemListeners={add:function(t,e){i._onAdd(e.items)},update:function(t,e){i._onUpdate(e.items)},remove:function(t,e){i._onRemove(e.items)}},this.groupListeners={add:function(t,e){i._onAddGroups(e.items)},update:function(t,e){i._onUpdateGroups(e.items)},remove:function(t,e){i._onRemoveGroups(e.items)}},this.items={},this.selection=[],this.lastStart=this.body.range.start,this.touchParams={},this.svgElements={},this.setOptions(e),this.groupsUsingDefaultStyles=[0],this.COUNTER=0,this.body.emitter.on("rangechanged",function(){i.lastStart=i.body.range.start,i.svg.style.left=o.option.asSize(-i.props.width),i.redraw.call(i,!0)}),this._create(),this.framework={svg:this.svg,svgElements:this.svgElements,options:this.options,groups:this.groups},this.body.emitter.emit("change")}var o=i(1),n=i(2),r=i(3),a=i(4),h=i(25),d=i(28),l=i(29),c=i(33),p=i(50),u="__ungrouped__";s.prototype=new h,s.prototype._create=function(){var t=document.createElement("div");t.className="LineGraph",this.dom.frame=t,this.svg=document.createElementNS("http://www.w3.org/2000/svg","svg"),this.svg.style.position="relative",this.svg.style.height=(""+this.options.graphHeight).replace("px","")+"px",this.svg.style.display="block",t.appendChild(this.svg),this.options.dataAxis.orientation="left",this.yAxisLeft=new d(this.body,this.options.dataAxis,this.svg,this.options.groups),this.options.dataAxis.orientation="right",this.yAxisRight=new d(this.body,this.options.dataAxis,this.svg,this.options.groups),delete this.options.dataAxis.orientation,this.legendLeft=new c(this.body,this.options.legend,"left",this.options.groups),this.legendRight=new c(this.body,this.options.legend,"right",this.options.groups),this.show()},s.prototype.setOptions=function(t){if(t){var e=["sampling","defaultGroup","height","graphHeight","yAxisOrientation","style","barChart","dataAxis","sort","groups"];void 0===t.graphHeight&&void 0!==t.height&&void 0!==this.body.domProps.centerContainer.height?(this.updateSVGheight=!0,this.updateSVGheightOnResize=!0):void 0!==this.body.domProps.centerContainer.height&&void 0!==t.graphHeight&&parseInt((t.graphHeight+"").replace("px",""))0){var d=this.body.util.toGlobalTime(-this.body.domProps.root.width),l=this.body.util.toGlobalTime(2*this.body.domProps.root.width),c={};for(this._getRelevantData(a,c,d,l),this._applySampling(a,c),e=0;eu&&console.log("WARNING: there may be an infinite loop in the _updateGraph emitter cycle."),this.COUNTER=0,this.abortedGraphUpdate=!1,e=0;e0)for(r=0;rs){d.push(h);break}d.push(h)}}else for(a=0;ai&&h.x0)for(var s=0;s0){var n=1,r=o.length,a=this.body.util.toGlobalScreen(o[o.length-1].x)-this.body.util.toGlobalScreen(o[0].x),h=r/a;n=Math.min(Math.ceil(.2*r),Math.max(1,Math.round(h)));for(var d=[],l=0;r>l;l+=n)d.push(o[l]);e[t[s]]=d}}},s.prototype._getYRanges=function(t,e,i){var s,o,n,r,a=[],h=[];if(t.length>0){for(n=0;n0&&(o=this.groups[t[n]],"stack"==r.barChart.handleOverlap&&"bar"==r.style?"left"==r.yAxisOrientation?a=a.concat(o.getYRange(s)):h=h.concat(o.getYRange(s)):i[t[n]]=o.getYRange(s,t[n]));p.getStackedBarYRange(a,i,t,"__barchartLeft","left"),p.getStackedBarYRange(h,i,t,"__barchartRight","right")}},s.prototype._updateYAxis=function(t,e){var i,s,o=!1,n=!1,r=!1,a=1e9,h=1e9,d=-1e9,l=-1e9;if(t.length>0){for(var c=0;ci?i:a,d=s>d?s:d):(r=!0,h=h>i?i:h,l=s>l?s:l));1==n&&this.yAxisLeft.setRange(a,d),1==r&&this.yAxisRight.setRange(h,l)}return o=this._toggleAxisVisiblity(n,this.yAxisLeft)||o,o=this._toggleAxisVisiblity(r,this.yAxisRight)||o,1==r&&1==n?(this.yAxisLeft.drawIcons=!0,this.yAxisRight.drawIcons=!0):(this.yAxisLeft.drawIcons=!1,this.yAxisRight.drawIcons=!1),this.yAxisRight.master=!n,0==this.yAxisRight.master?(this.yAxisLeft.lineOffset=1==r?this.yAxisRight.width:0,o=this.yAxisLeft.redraw()||o,this.yAxisRight.stepPixelsForced=this.yAxisLeft.stepPixels,this.yAxisRight.zeroCrossing=this.yAxisLeft.zeroCrossing,o=this.yAxisRight.redraw()||o):o=this.yAxisRight.redraw()||o,-1!=t.indexOf("__barchartLeft")&&t.splice(t.indexOf("__barchartLeft"),1),-1!=t.indexOf("__barchartRight")&&t.splice(t.indexOf("__barchartRight"),1),o},s.prototype._toggleAxisVisiblity=function(t,e){var i=!1;return 0==t?e.dom.frame.parentNode&&0==e.hidden&&(e.hide(),i=!0):e.dom.frame.parentNode||1!=e.hidden||(e.show(),i=!0),i},s.prototype._convertXcoordinates=function(t){for(var e,i,s=[],o=this.body.util.toScreen,n=0;ny;)y++,l=h.getCurrent(),c=h.isMajor(),u=h.getClassName(),f=m,m=this.body.util.toScreen(l),g=m-f,p&&(p.style.width=g+"px"),this.options.showMinorLabels&&this._repaintMinorText(m,h.getLabelMinor(),t,u),c&&this.options.showMajorLabels?(m>0&&(void 0==v&&(v=m),this._repaintMajorText(m,h.getLabelMajor(),t,u)),p=this._repaintMajorLine(m,t,u)):p=this._repaintMinorLine(m,t,u),h.next();if(this.options.showMajorLabels){var b=this.body.util.toTime(0),_=h.getLabelMajor(b),x=_.length*(this.props.majorCharWidth||10)+10;(void 0==v||v>x)&&this._repaintMajorText(0,_,t,u)}o.forEach(this.dom.redundant,function(t){for(;t.length;){var e=t.pop();e&&e.parentNode&&e.parentNode.removeChild(e)}})},s.prototype._repaintMinorText=function(t,e,i,s){var o=this.dom.redundant.minorTexts.shift();if(!o){var n=document.createTextNode("");o=document.createElement("div"),o.appendChild(n),this.dom.foreground.appendChild(o)}this.dom.minorTexts.push(o),o.childNodes[0].nodeValue=e,o.style.top="top"==i?this.props.majorLabelHeight+"px":"0",o.style.left=t+"px",o.className="text minor "+s},s.prototype._repaintMajorText=function(t,e,i,s){var o=this.dom.redundant.majorTexts.shift();if(!o){var n=document.createTextNode(e);o=document.createElement("div"),o.appendChild(n),this.dom.foreground.appendChild(o)}this.dom.majorTexts.push(o),o.childNodes[0].nodeValue=e,o.className="text major "+s,o.style.top="top"==i?"0":this.props.minorLabelHeight+"px",o.style.left=t+"px"},s.prototype._repaintMinorLine=function(t,e,i){var s=this.dom.redundant.lines.shift();s||(s=document.createElement("div"),this.dom.background.appendChild(s)),this.dom.lines.push(s);var o=this.props;return s.style.top="top"==e?o.majorLabelHeight+"px":this.body.domProps.top.height+"px",s.style.height=o.minorLineHeight+"px",s.style.left=t-o.minorLineWidth/2+"px",s.className="grid vertical minor "+i,s},s.prototype._repaintMajorLine=function(t,e,i){var s=this.dom.redundant.lines.shift();s||(s=document.createElement("div"),this.dom.background.appendChild(s)),this.dom.lines.push(s);var o=this.props;return s.style.top="top"==e?"0":this.body.domProps.top.height+"px",s.style.left=t-o.majorLineWidth/2+"px",s.style.height=o.majorLineHeight+"px",s.className="grid vertical major "+i,s},s.prototype._calculateCharSize=function(){this.dom.measureCharMinor||(this.dom.measureCharMinor=document.createElement("DIV"),this.dom.measureCharMinor.className="text minor measure",this.dom.measureCharMinor.style.position="absolute",this.dom.measureCharMinor.appendChild(document.createTextNode("0")),this.dom.foreground.appendChild(this.dom.measureCharMinor)),this.props.minorCharHeight=this.dom.measureCharMinor.clientHeight,this.props.minorCharWidth=this.dom.measureCharMinor.clientWidth,this.dom.measureCharMajor||(this.dom.measureCharMajor=document.createElement("DIV"),this.dom.measureCharMajor.className="text major measure",this.dom.measureCharMajor.style.position="absolute",this.dom.measureCharMajor.appendChild(document.createTextNode("0")),this.dom.foreground.appendChild(this.dom.measureCharMajor)),this.props.majorCharHeight=this.dom.measureCharMajor.clientHeight,this.props.majorCharWidth=this.dom.measureCharMajor.clientWidth},s.prototype.snap=function(t){return this.step.snap(t)},t.exports=s},function(t,e,i){function s(t,e,i){if(!(this instanceof s))throw new SyntaxError("Constructor must be called with the new operator");this._determineBrowserMethod(),this._initializeMixinLoaders(),this.containerElement=t,this.renderRefreshRate=60,this.renderTimestep=1e3/this.renderRefreshRate,this.renderTime=0,this.physicsTime=0,this.runDoubleSpeed=!1,this.physicsDiscreteStepsize=.5,this.initializing=!0,this.triggerFunctions={add:null,edit:null,editEdge:null,connect:null,del:null},this.defaultOptions={nodes:{mass:1,radiusMin:10,radiusMax:30,radius:10,shape:"ellipse",image:void 0,widthMin:16,widthMax:64,fontColor:"black",fontSize:14,fontFace:"verdana",fontFill:void 0,fontStrokeWidth:0,fontStrokeColor:"white",level:-1,color:{border:"#2B7CE9",background:"#97C2FC",highlight:{border:"#2B7CE9",background:"#D2E5FF"},hover:{border:"#2B7CE9",background:"#D2E5FF"}},group:void 0,borderWidth:1,borderWidthSelected:void 0},edges:{widthMin:1,widthMax:15,width:1,widthSelectionMultiplier:2,hoverWidth:1.5,style:"line",color:{color:"#848484",highlight:"#848484",hover:"#848484"},fontColor:"#343434",fontSize:14,fontFace:"arial",fontFill:"white",fontStrokeWidth:0,fontStrokeColor:"white",labelAlignment:"horizontal",arrowScaleFactor:1,dash:{length:10,gap:5,altLength:void 0},inheritColor:"from"},configurePhysics:!1,physics:{barnesHut:{enabled:!0,thetaInverted:2,gravitationalConstant:-2e3,centralGravity:.3,springLength:95,springConstant:.04,damping:.09},repulsion:{centralGravity:0,springLength:200,springConstant:.05,nodeDistance:100,damping:.09},hierarchicalRepulsion:{enabled:!1,centralGravity:0,springLength:100,springConstant:.01,nodeDistance:150,damping:.09},damping:null,centralGravity:null,springLength:null,springConstant:null},clustering:{enabled:!1,initialMaxNodes:100,clusterThreshold:500,reduceToNodes:300,chainThreshold:.4,clusterEdgeThreshold:20,sectorThreshold:100,screenSizeThreshold:.2,fontSizeMultiplier:4,maxFontSize:1e3,forceAmplification:.1,distanceAmplification:.1,edgeGrowth:20,nodeScaling:{width:1,height:1,radius:1},maxNodeSizeIncrements:600,activeAreaBoxSize:80,clusterLevelDifference:2},navigation:{enabled:!1},keyboard:{enabled:!1,speed:{x:10,y:10,zoom:.02}},dataManipulation:{enabled:!1,initiallyVisible:!1},hierarchicalLayout:{enabled:!1,levelSeparation:150,nodeSpacing:100,direction:"UD",layout:"hubsize"},freezeForStabilization:!1,smoothCurves:{enabled:!0,dynamic:!0,type:"continuous",roundness:.5},maxVelocity:30,minVelocity:.1,stabilize:!0,stabilizationIterations:1e3,zoomExtentOnStabilize:!0,locale:"en",locales:_,tooltip:{delay:300,fontColor:"black",fontSize:14,fontFace:"verdana",color:{border:"#666",background:"#FFFFC6"}},dragNetwork:!0,dragNodes:!0,zoomable:!0,hover:!1,hideEdgesOnDrag:!1,hideNodesOnDrag:!1,width:"100%",height:"100%",selectable:!0},this.constants=a.extend({},this.defaultOptions),this.pixelRatio=1,this.hoverObj={nodes:{},edges:{}},this.controlNodesActive=!1,this.navigationHammers={existing:[],_new:[]},this.animationSpeed=1/this.renderRefreshRate,this.animationEasingFunction="easeInOutQuint",this.animating=!1,this.easingTime=0,this.sourceScale=0,this.targetScale=0,this.sourceTranslation=0,this.targetTranslation=0,this.lockedOnNodeId=null,this.lockedOnNodeOffset=null,this.touchTime=0;var o=this;this.groups=new u,this.images=new m,this.images.setOnloadCallback(function(){o._redraw()}),this.xIncrement=0,this.yIncrement=0,this.zoomIncrement=0,this._loadPhysicsSystem(),this._create(),this._loadSectorSystem(),this._loadClusterSystem(),this._loadSelectionSystem(),this._loadHierarchySystem(),this._setTranslation(this.frame.clientWidth/2,this.frame.clientHeight/2),this._setScale(1),this.setOptions(i),this.freezeSimulation=!1,this.cachedFunctions={},this.startedStabilization=!1,this.stabilized=!1,this.stabilizationIterations=null,this.draggingNodes=!1,this.calculationNodes={},this.calculationNodeIndices=[],this.nodeIndices=[],this.nodes={},this.edges={},this.canvasTopLeft={x:0,y:0},this.canvasBottomRight={x:0,y:0},this.pointerPosition={x:0,y:0},this.areaCenter={},this.scale=1,this.previousScale=this.scale,this.nodesData=null,this.edgesData=null,this.nodesListeners={add:function(t,e){o._addNodes(e.items),o.start()},update:function(t,e){o._updateNodes(e.items,e.data),o.start()},remove:function(t,e){o._removeNodes(e.items),o.start()}},this.edgesListeners={add:function(t,e){o._addEdges(e.items),o.start()},update:function(t,e){o._updateEdges(e.items),o.start()},remove:function(t,e){o._removeEdges(e.items),o.start()}},this.moving=!0,this.timer=void 0,this.setData(e,this.constants.clustering.enabled||this.constants.hierarchicalLayout.enabled),this.initializing=!1,1==this.constants.hierarchicalLayout.enabled?this._setupHierarchicalLayout():0==this.constants.stabilize&&this.zoomExtent(void 0,!0,this.constants.clustering.enabled),this.constants.clustering.enabled&&this.startWithClustering()}var o=i(56),n=i(45),r=i(57),a=i(1),h=i(47),d=i(3),l=i(4),c=i(42),p=i(43),u=i(38),m=i(39),f=i(40),g=i(37),v=i(41),y=i(52),b=i(53),_=i(54);i(55),o(s.prototype),s.prototype._determineBrowserMethod=function(){var t=navigator.userAgent.toLowerCase();this.requiresTimeout=!1,-1!=t.indexOf("msie 9.0")?this.requiresTimeout=!0:-1!=t.indexOf("safari")&&t.indexOf("chrome")<=-1&&(this.requiresTimeout=!0)},s.prototype._getScriptPath=function(){for(var t=document.getElementsByTagName("script"),e=0;et.boundingBox.left&&(s=t.boundingBox.left),ot.boundingBox.bottom&&(e=t.boundingBox.top),i=this.constants.clustering.initialMaxNodes?49.07548/(n+142.05338)+91444e-8:12.662/(n+7.4147)+.0964822:1==this.constants.clustering.enabled&&n>=this.constants.clustering.initialMaxNodes?77.5271985/(n+187.266146)+476710517e-13:30.5062972/(n+19.93597763)+.08413486;var r=Math.min(this.frame.canvas.clientWidth/600,this.frame.canvas.clientHeight/600);s*=r}else{var a=1.1*Math.abs(o.maxX-o.minX),h=1.1*Math.abs(o.maxY-o.minY),d=this.frame.canvas.clientWidth/a,l=this.frame.canvas.clientHeight/h;s=l>=d?d:l}s>1&&(s=1);var c=this._findCenter(o);if(0==i){var p={position:c,scale:s,animation:t};this.moveTo(p),this.moving=!0,this.start()}else c.x*=s,c.y*=s,c.x-=.5*this.frame.canvas.clientWidth,c.y-=.5*this.frame.canvas.clientHeight,this._setScale(s),this._setTranslation(-c.x,-c.y)},s.prototype._updateNodeIndexList=function(){this._clearNodeIndexList();for(var t in this.nodes)this.nodes.hasOwnProperty(t)&&this.nodeIndices.push(t)},s.prototype.setData=function(t,e){if(void 0===e&&(e=!1),this.initializing=!0,t&&t.dot&&(t.nodes||t.edges))throw new SyntaxError('Data must contain either parameter "dot" or parameter pair "nodes" and "edges", but not both.');if(1==this.constants.dataManipulation.enabled&&this._createManipulatorBar(),this.setOptions(t&&t.options),t&&t.dot){if(t&&t.dot){var i=c.DOTToGraph(t.dot);return void this.setData(i)}}else if(t&&t.gephi){if(t&&t.gephi){var s=p.parseGephi(t.gephi);return void this.setData(s)}}else this._setNodes(t&&t.nodes),this._setEdges(t&&t.edges);this._putDataInSector(),0==e&&(1==this.constants.hierarchicalLayout.enabled?(this._resetLevels(),this._setupHierarchicalLayout()):this.constants.stabilize&&this._stabilize(),this.start()),this.initializing=!1},s.prototype.setOptions=function(t){if(t){var e,i=["nodes","edges","smoothCurves","hierarchicalLayout","clustering","navigation","keyboard","dataManipulation","onAdd","onEdit","onEditEdge","onConnect","onDelete","clickToUse"];if(a.selectiveNotDeepExtend(i,this.constants,t),a.selectiveNotDeepExtend(["color"],this.constants.nodes,t.nodes),a.selectiveNotDeepExtend(["color","length"],this.constants.edges,t.edges),t.physics&&(a.mergeOptions(this.constants.physics,t.physics,"barnesHut"),a.mergeOptions(this.constants.physics,t.physics,"repulsion"),t.physics.hierarchicalRepulsion)){this.constants.hierarchicalLayout.enabled=!0,this.constants.physics.hierarchicalRepulsion.enabled=!0,this.constants.physics.barnesHut.enabled=!1; -for(e in t.physics.hierarchicalRepulsion)t.physics.hierarchicalRepulsion.hasOwnProperty(e)&&(this.constants.physics.hierarchicalRepulsion[e]=t.physics.hierarchicalRepulsion[e])}if(t.onAdd&&(this.triggerFunctions.add=t.onAdd),t.onEdit&&(this.triggerFunctions.edit=t.onEdit),t.onEditEdge&&(this.triggerFunctions.editEdge=t.onEditEdge),t.onConnect&&(this.triggerFunctions.connect=t.onConnect),t.onDelete&&(this.triggerFunctions.del=t.onDelete),a.mergeOptions(this.constants,t,"smoothCurves"),a.mergeOptions(this.constants,t,"hierarchicalLayout"),a.mergeOptions(this.constants,t,"clustering"),a.mergeOptions(this.constants,t,"navigation"),a.mergeOptions(this.constants,t,"keyboard"),a.mergeOptions(this.constants,t,"dataManipulation"),t.dataManipulation&&(this.editMode=this.constants.dataManipulation.initiallyVisible),t.edges&&(void 0!==t.edges.color&&(a.isString(t.edges.color)?(this.constants.edges.color={},this.constants.edges.color.color=t.edges.color,this.constants.edges.color.highlight=t.edges.color,this.constants.edges.color.hover=t.edges.color):(void 0!==t.edges.color.color&&(this.constants.edges.color.color=t.edges.color.color),void 0!==t.edges.color.highlight&&(this.constants.edges.color.highlight=t.edges.color.highlight),void 0!==t.edges.color.hover&&(this.constants.edges.color.hover=t.edges.color.hover)),this.constants.edges.inheritColor=!1),t.edges.fontColor||void 0!==t.edges.color&&(a.isString(t.edges.color)?this.constants.edges.fontColor=t.edges.color:void 0!==t.edges.color.color&&(this.constants.edges.fontColor=t.edges.color.color))),t.nodes&&t.nodes.color){var s=a.parseColor(t.nodes.color);this.constants.nodes.color.background=s.background,this.constants.nodes.color.border=s.border,this.constants.nodes.color.highlight.background=s.highlight.background,this.constants.nodes.color.highlight.border=s.highlight.border,this.constants.nodes.color.hover.background=s.hover.background,this.constants.nodes.color.hover.border=s.hover.border}if(t.groups)for(var o in t.groups)if(t.groups.hasOwnProperty(o)){var n=t.groups[o];this.groups.add(o,n)}if(t.tooltip){for(e in t.tooltip)t.tooltip.hasOwnProperty(e)&&(this.constants.tooltip[e]=t.tooltip[e]);t.tooltip.color&&(this.constants.tooltip.color=a.parseColor(t.tooltip.color))}if("clickToUse"in t&&(t.clickToUse?this.activator||(this.activator=new b(this.frame),this.activator.on("change",this._createKeyBinds.bind(this))):this.activator&&(this.activator.destroy(),delete this.activator)),t.labels)throw new Error('Option "labels" is deprecated. Use options "locale" and "locales" instead.');this._loadPhysicsSystem(),this._loadNavigationControls(),this._loadManipulationSystem(),this._configureSmoothCurves(),this._bindHammer(),this._createKeyBinds(),this.setSize(this.constants.width,this.constants.height),this.moving=!0,this.start()}},s.prototype._create=function(){for(;this.containerElement.hasChildNodes();)this.containerElement.removeChild(this.containerElement.firstChild);if(this.frame=document.createElement("div"),this.frame.className="vis network-frame",this.frame.style.position="relative",this.frame.style.overflow="hidden",this.frame.canvas=document.createElement("canvas"),this.frame.canvas.style.position="relative",this.frame.appendChild(this.frame.canvas),this.frame.canvas.getContext){var t=this.frame.canvas.getContext("2d");this.pixelRatio=(window.devicePixelRatio||1)/(t.webkitBackingStorePixelRatio||t.mozBackingStorePixelRatio||t.msBackingStorePixelRatio||t.oBackingStorePixelRatio||t.backingStorePixelRatio||1),this.frame.canvas.getContext("2d").setTransform(this.pixelRatio,0,0,this.pixelRatio,0,0)}else{var e=document.createElement("DIV");e.style.color="red",e.style.fontWeight="bold",e.style.padding="10px",e.innerHTML="Error: your browser does not support HTML canvas",this.frame.canvas.appendChild(e)}this._bindHammer()},s.prototype._bindHammer=function(){var t=this;void 0!==this.hammer&&this.hammer.dispose(),this.drag={},this.pinch={},this.hammer=n(this.frame.canvas,{prevent_default:!0}),this.hammer.on("tap",t._onTap.bind(t)),this.hammer.on("doubletap",t._onDoubleTap.bind(t)),this.hammer.on("hold",t._onHold.bind(t)),this.hammer.on("touch",t._onTouch.bind(t)),this.hammer.on("dragstart",t._onDragStart.bind(t)),this.hammer.on("drag",t._onDrag.bind(t)),this.hammer.on("dragend",t._onDragEnd.bind(t)),1==this.constants.zoomable&&(this.hammer.on("mousewheel",t._onMouseWheel.bind(t)),this.hammer.on("DOMMouseScroll",t._onMouseWheel.bind(t)),this.hammer.on("pinch",t._onPinch.bind(t))),this.hammer.on("mousemove",t._onMouseMoveTitle.bind(t)),this.hammerFrame=n(this.frame,{prevent_default:!0}),this.hammerFrame.on("release",t._onRelease.bind(t)),this.containerElement.appendChild(this.frame)},s.prototype._createKeyBinds=function(){var t=this;void 0!==this.keycharm&&this.keycharm.destroy(),this.keycharm=r(),this.keycharm.reset(),this.constants.keyboard.enabled&&this.isActive()&&(this.keycharm.bind("up",this._moveUp.bind(t),"keydown"),this.keycharm.bind("up",this._yStopMoving.bind(t),"keyup"),this.keycharm.bind("down",this._moveDown.bind(t),"keydown"),this.keycharm.bind("down",this._yStopMoving.bind(t),"keyup"),this.keycharm.bind("left",this._moveLeft.bind(t),"keydown"),this.keycharm.bind("left",this._xStopMoving.bind(t),"keyup"),this.keycharm.bind("right",this._moveRight.bind(t),"keydown"),this.keycharm.bind("right",this._xStopMoving.bind(t),"keyup"),this.keycharm.bind("=",this._zoomIn.bind(t),"keydown"),this.keycharm.bind("=",this._stopZoom.bind(t),"keyup"),this.keycharm.bind("num+",this._zoomIn.bind(t),"keydown"),this.keycharm.bind("num+",this._stopZoom.bind(t),"keyup"),this.keycharm.bind("num-",this._zoomOut.bind(t),"keydown"),this.keycharm.bind("num-",this._stopZoom.bind(t),"keyup"),this.keycharm.bind("-",this._zoomOut.bind(t),"keydown"),this.keycharm.bind("-",this._stopZoom.bind(t),"keyup"),this.keycharm.bind("[",this._zoomIn.bind(t),"keydown"),this.keycharm.bind("[",this._stopZoom.bind(t),"keyup"),this.keycharm.bind("]",this._zoomOut.bind(t),"keydown"),this.keycharm.bind("]",this._stopZoom.bind(t),"keyup"),this.keycharm.bind("pageup",this._zoomIn.bind(t),"keydown"),this.keycharm.bind("pageup",this._stopZoom.bind(t),"keyup"),this.keycharm.bind("pagedown",this._zoomOut.bind(t),"keydown"),this.keycharm.bind("pagedown",this._stopZoom.bind(t),"keyup")),1==this.constants.dataManipulation.enabled&&(this.keycharm.bind("esc",this._createManipulatorBar.bind(t)),this.keycharm.bind("delete",this._deleteSelected.bind(t)))},s.prototype.destroy=function(){this.start=function(){},this.redraw=function(){},this.timer=!1,this._cleanupPhysicsConfiguration(),this.keycharm.reset(),this.hammer.dispose(),this.off(),this._recursiveDOMDelete(this.containerElement)},s.prototype._recursiveDOMDelete=function(t){for(;1==t.hasChildNodes();)this._recursiveDOMDelete(t.firstChild),t.removeChild(t.firstChild)},s.prototype._getPointer=function(t){return{x:t.pageX-a.getAbsoluteLeft(this.frame.canvas),y:t.pageY-a.getAbsoluteTop(this.frame.canvas)}},s.prototype._onTouch=function(t){(new Date).valueOf()-this.touchTime>100&&(this.drag.pointer=this._getPointer(t.gesture.center),this.drag.pinched=!1,this.pinch.scale=this._getScale(),this.touchTime=(new Date).valueOf(),this._handleTouch(this.drag.pointer))},s.prototype._onDragStart=function(t){this._handleDragStart(t)},s.prototype._handleDragStart=function(t){void 0===this.drag.pointer&&this._onTouch(t);var e=this._getNodeAt(this.drag.pointer);if(this.drag.dragging=!0,this.drag.selection=[],this.drag.translation=this._getTranslation(),this.drag.nodeId=null,this.draggingNodes=!1,null!=e&&1==this.constants.dragNodes){this.draggingNodes=!0,this.drag.nodeId=e.id,e.isSelected()||this._selectObject(e,!1),this.emit("dragStart",{nodeIds:this.getSelection().nodes});for(var i in this.selectionObj.nodes)if(this.selectionObj.nodes.hasOwnProperty(i)){var s=this.selectionObj.nodes[i],o={id:s.id,node:s,x:s.x,y:s.y,xFixed:s.xFixed,yFixed:s.yFixed};s.xFixed=!0,s.yFixed=!0,this.drag.selection.push(o)}}},s.prototype._onDrag=function(t){this._handleOnDrag(t)},s.prototype._handleOnDrag=function(t){if(!this.drag.pinched){this.releaseNode();var e=this._getPointer(t.gesture.center),i=this,s=this.drag,o=s.selection;if(o&&o.length&&1==this.constants.dragNodes){var n=e.x-s.pointer.x,r=e.y-s.pointer.y;o.forEach(function(t){var e=t.node;t.xFixed||(e.x=i._XconvertDOMtoCanvas(i._XconvertCanvasToDOM(t.x)+n)),t.yFixed||(e.y=i._YconvertDOMtoCanvas(i._YconvertCanvasToDOM(t.y)+r))}),this.moving||(this.moving=!0,this.start())}else if(1==this.constants.dragNetwork){if(void 0===this.drag.pointer)return void this._handleDragStart(t);var a=e.x-this.drag.pointer.x,h=e.y-this.drag.pointer.y;this._setTranslation(this.drag.translation.x+a,this.drag.translation.y+h),this._redraw()}}},s.prototype._onDragEnd=function(t){this._handleDragEnd(t)},s.prototype._handleDragEnd=function(){this.drag.dragging=!1;var t=this.drag.selection;t&&t.length?(t.forEach(function(t){t.node.xFixed=t.xFixed,t.node.yFixed=t.yFixed}),this.moving=!0,this.start()):this._redraw(),0==this.draggingNodes?this.emit("dragEnd",{nodeIds:[]}):this.emit("dragEnd",{nodeIds:this.getSelection().nodes})},s.prototype._onTap=function(t){var e=this._getPointer(t.gesture.center);this.pointerPosition=e,this._handleTap(e)},s.prototype._onDoubleTap=function(t){var e=this._getPointer(t.gesture.center);this._handleDoubleTap(e)},s.prototype._onHold=function(t){var e=this._getPointer(t.gesture.center);this.pointerPosition=e,this._handleOnHold(e)},s.prototype._onRelease=function(t){var e=this._getPointer(t.gesture.center);this._handleOnRelease(e)},s.prototype._onPinch=function(t){var e=this._getPointer(t.gesture.center);this.drag.pinched=!0,"scale"in this.pinch||(this.pinch.scale=1);var i=this.pinch.scale*t.gesture.scale;this._zoom(i,e)},s.prototype._zoom=function(t,e){if(1==this.constants.zoomable){var i=this._getScale();1e-5>t&&(t=1e-5),t>10&&(t=10);var s=null;void 0!==this.drag&&1==this.drag.dragging&&(s=this.DOMtoCanvas(this.drag.pointer));var o=this._getTranslation(),n=t/i,r=(1-n)*e.x+o.x*n,a=(1-n)*e.y+o.y*n;if(this.areaCenter={x:this._XconvertDOMtoCanvas(e.x),y:this._YconvertDOMtoCanvas(e.y)},this._setScale(t),this._setTranslation(r,a),this.updateClustersDefault(),null!=s){var h=this.canvasToDOM(s);this.drag.pointer.x=h.x,this.drag.pointer.y=h.y}return this._redraw(),t>i?this.emit("zoom",{direction:"+"}):this.emit("zoom",{direction:"-"}),t}},s.prototype._onMouseWheel=function(t){var e=0;if(t.wheelDelta?e=t.wheelDelta/120:t.detail&&(e=-t.detail/3),e){var i=this._getScale(),s=e/10;0>e&&(s/=1-s),i*=1+s;var o=h.fakeGesture(this,t),n=this._getPointer(o.center);this._zoom(i,n)}t.preventDefault()},s.prototype._onMouseMoveTitle=function(t){var e=h.fakeGesture(this,t),i=this._getPointer(e.center);this.popupObj&&this._checkHidePopup(i);var s=this,o=function(){s._checkShowPopup(i)};if(this.popupTimer&&clearInterval(this.popupTimer),this.drag.dragging||(this.popupTimer=setTimeout(o,this.constants.tooltip.delay)),1==this.constants.hover){for(var n in this.hoverObj.edges)this.hoverObj.edges.hasOwnProperty(n)&&(this.hoverObj.edges[n].hover=!1,delete this.hoverObj.edges[n]);var r=this._getNodeAt(i);null==r&&(r=this._getEdgeAt(i)),null!=r&&this._hoverObject(r);for(var a in this.hoverObj.nodes)this.hoverObj.nodes.hasOwnProperty(a)&&(r instanceof f&&r.id!=a||r instanceof g||null==r)&&(this._blurObject(this.hoverObj.nodes[a]),delete this.hoverObj.nodes[a]);this.redraw()}},s.prototype._checkShowPopup=function(t){var e,i={left:this._XconvertDOMtoCanvas(t.x),top:this._YconvertDOMtoCanvas(t.y),right:this._XconvertDOMtoCanvas(t.x),bottom:this._YconvertDOMtoCanvas(t.y)},s=this.popupObj,o=!1;if(void 0==this.popupObj){var n=this.nodes,r=[];for(e in n)if(n.hasOwnProperty(e)){var a=n[e];a.isOverlappingWith(i)&&void 0!==a.getTitle()&&r.push(e)}r.length>0&&(this.popupObj=this.nodes[r[r.length-1]],o=!0)}if(void 0===this.popupObj&&0==o){var h=this.edges,d=[];for(e in h)if(h.hasOwnProperty(e)){var l=h[e];l.connected&&void 0!==l.getTitle()&&l.isOverlappingWith(i)&&d.push(e)}d.length>0&&(this.popupObj=this.edges[d[d.length-1]])}if(this.popupObj){if(this.popupObj!=s){var c=this;c.popup||(c.popup=new v(c.frame,c.constants.tooltip)),c.popup.setPosition(t.x-3,t.y-3),c.popup.setText(c.popupObj.getTitle()),c.popup.show()}}else this.popup&&this.popup.hide()},s.prototype._checkHidePopup=function(t){this.popupObj&&this._getNodeAt(t)||(this.popupObj=void 0,this.popup&&this.popup.hide())},s.prototype.setSize=function(t,e){var i=!1,s=this.frame.canvas.width,o=this.frame.canvas.height;t!=this.constants.width||e!=this.constants.height||this.frame.style.width!=t||this.frame.style.height!=e?(this.frame.style.width=t,this.frame.style.height=e,this.frame.canvas.style.width="100%",this.frame.canvas.style.height="100%",this.frame.canvas.width=this.frame.canvas.clientWidth*this.pixelRatio,this.frame.canvas.height=this.frame.canvas.clientHeight*this.pixelRatio,this.constants.width=t,this.constants.height=e,i=!0):(this.frame.canvas.width!=this.frame.canvas.clientWidth*this.pixelRatio&&(this.frame.canvas.width=this.frame.canvas.clientWidth*this.pixelRatio,i=!0),this.frame.canvas.height!=this.frame.canvas.clientHeight*this.pixelRatio&&(this.frame.canvas.height=this.frame.canvas.clientHeight*this.pixelRatio,i=!0)),1==i&&this.emit("resize",{width:this.frame.canvas.width*this.pixelRatio,height:this.frame.canvas.height*this.pixelRatio,oldWidth:s*this.pixelRatio,oldHeight:o*this.pixelRatio})},s.prototype._setNodes=function(t){var e=this.nodesData;if(t instanceof d||t instanceof l)this.nodesData=t;else if(Array.isArray(t))this.nodesData=new d,this.nodesData.add(t);else{if(t)throw new TypeError("Array or DataSet expected");this.nodesData=new d}if(e&&a.forEach(this.nodesListeners,function(t,i){e.off(i,t)}),this.nodes={},this.nodesData){var i=this;a.forEach(this.nodesListeners,function(t,e){i.nodesData.on(e,t)});var s=this.nodesData.getIds();this._addNodes(s)}this._updateSelection()},s.prototype._addNodes=function(t){for(var e,i=0,s=t.length;s>i;i++){e=t[i];var o=this.nodesData.get(e),n=new f(o,this.images,this.groups,this.constants);if(this.nodes[e]=n,!(0!=n.xFixed&&0!=n.yFixed||null!==n.x&&null!==n.y)){var r=1*t.length+10,a=2*Math.PI*Math.random();0==n.xFixed&&(n.x=r*Math.cos(a)),0==n.yFixed&&(n.y=r*Math.sin(a))}this.moving=!0}this._updateNodeIndexList(),1==this.constants.hierarchicalLayout.enabled&&0==this.initializing&&(this._resetLevels(),this._setupHierarchicalLayout()),this._updateCalculationNodes(),this._reconnectEdges(),this._updateValueRange(this.nodes),this.updateLabels()},s.prototype._updateNodes=function(t,e){for(var i=this.nodes,s=0,o=t.length;o>s;s++){var n=t[s],r=i[n],a=e[s];r?r.setProperties(a,this.constants):(r=new f(properties,this.images,this.groups,this.constants),i[n]=r)}this.moving=!0,1==this.constants.hierarchicalLayout.enabled&&0==this.initializing&&(this._resetLevels(),this._setupHierarchicalLayout()),this._updateNodeIndexList(),this._updateValueRange(i)},s.prototype._removeNodes=function(t){for(var e=this.nodes,i=0,s=t.length;s>i;i++){var o=t[i];delete e[o]}this._updateNodeIndexList(),1==this.constants.hierarchicalLayout.enabled&&0==this.initializing&&(this._resetLevels(),this._setupHierarchicalLayout()),this._updateCalculationNodes(),this._reconnectEdges(),this._updateSelection(),this._updateValueRange(e)},s.prototype._setEdges=function(t){var e=this.edgesData;if(t instanceof d||t instanceof l)this.edgesData=t;else if(Array.isArray(t))this.edgesData=new d,this.edgesData.add(t);else{if(t)throw new TypeError("Array or DataSet expected");this.edgesData=new d}if(e&&a.forEach(this.edgesListeners,function(t,i){e.off(i,t)}),this.edges={},this.edgesData){var i=this;a.forEach(this.edgesListeners,function(t,e){i.edgesData.on(e,t)});var s=this.edgesData.getIds();this._addEdges(s)}this._reconnectEdges()},s.prototype._addEdges=function(t){for(var e=this.edges,i=this.edgesData,s=0,o=t.length;o>s;s++){var n=t[s],r=e[n];r&&r.disconnect();var a=i.get(n,{showInternalIds:!0});e[n]=new g(a,this,this.constants)}this.moving=!0,this._updateValueRange(e),this._createBezierNodes(),this._updateCalculationNodes(),1==this.constants.hierarchicalLayout.enabled&&0==this.initializing&&(this._resetLevels(),this._setupHierarchicalLayout())},s.prototype._updateEdges=function(t){for(var e=this.edges,i=this.edgesData,s=0,o=t.length;o>s;s++){var n=t[s],r=i.get(n),a=e[n];a?(a.disconnect(),a.setProperties(r,this.constants),a.connect()):(a=new g(r,this,this.constants),this.edges[n]=a)}this._createBezierNodes(),1==this.constants.hierarchicalLayout.enabled&&0==this.initializing&&(this._resetLevels(),this._setupHierarchicalLayout()),this.moving=!0,this._updateValueRange(e)},s.prototype._removeEdges=function(t){for(var e=this.edges,i=0,s=t.length;s>i;i++){var o=t[i],n=e[o];n&&(null!=n.via&&delete this.sectors.support.nodes[n.via.id],n.disconnect(),delete e[o])}this.moving=!0,this._updateValueRange(e),1==this.constants.hierarchicalLayout.enabled&&0==this.initializing&&(this._resetLevels(),this._setupHierarchicalLayout()),this._updateCalculationNodes()},s.prototype._reconnectEdges=function(){var t,e=this.nodes,i=this.edges;for(t in e)e.hasOwnProperty(t)&&(e[t].edges=[],e[t].dynamicEdges=[]);for(t in i)if(i.hasOwnProperty(t)){var s=i[t];s.from=null,s.to=null,s.connect()}},s.prototype._updateValueRange=function(t){var e,i=void 0,s=void 0;for(e in t)if(t.hasOwnProperty(e)){var o=t[e].getValue();void 0!==o&&(i=void 0===i?o:Math.min(o,i),s=void 0===s?o:Math.max(o,s))}if(void 0!==i&&void 0!==s)for(e in t)t.hasOwnProperty(e)&&t[e].setValueRange(i,s)},s.prototype.redraw=function(){this.setSize(this.constants.width,this.constants.height),this._redraw()},s.prototype._redraw=function(t){var e=this.frame.canvas.getContext("2d");e.setTransform(this.pixelRatio,0,0,this.pixelRatio,0,0);var i=this.frame.canvas.width*this.pixelRatio,s=this.frame.canvas.height*this.pixelRatio;e.clearRect(0,0,i,s),e.save(),e.translate(this.translation.x,this.translation.y),e.scale(this.scale,this.scale),this.canvasTopLeft={x:this._XconvertDOMtoCanvas(0),y:this._YconvertDOMtoCanvas(0)},this.canvasBottomRight={x:this._XconvertDOMtoCanvas(this.frame.canvas.clientWidth*this.pixelRatio),y:this._YconvertDOMtoCanvas(this.frame.canvas.clientHeight*this.pixelRatio)},1!=t&&(this._doInAllSectors("_drawAllSectorNodes",e),(0==this.drag.dragging||void 0===this.drag.dragging||0==this.constants.hideEdgesOnDrag)&&this._doInAllSectors("_drawEdges",e)),(0==this.drag.dragging||void 0===this.drag.dragging||0==this.constants.hideNodesOnDrag)&&this._doInAllSectors("_drawNodes",e,!1),1!=t&&1==this.controlNodesActive&&this._doInAllSectors("_drawControlNodes",e),e.restore(),1==t&&e.clearRect(0,0,i,s)},s.prototype._setTranslation=function(t,e){void 0===this.translation&&(this.translation={x:0,y:0}),void 0!==t&&(this.translation.x=t),void 0!==e&&(this.translation.y=e),this.emit("viewChanged")},s.prototype._getTranslation=function(){return{x:this.translation.x,y:this.translation.y}},s.prototype._setScale=function(t){this.scale=t},s.prototype._getScale=function(){return this.scale},s.prototype._XconvertDOMtoCanvas=function(t){return(t-this.translation.x)/this.scale},s.prototype._XconvertCanvasToDOM=function(t){return t*this.scale+this.translation.x},s.prototype._YconvertDOMtoCanvas=function(t){return(t-this.translation.y)/this.scale},s.prototype._YconvertCanvasToDOM=function(t){return t*this.scale+this.translation.y},s.prototype.canvasToDOM=function(t){return{x:this._XconvertCanvasToDOM(t.x),y:this._YconvertCanvasToDOM(t.y)}},s.prototype.DOMtoCanvas=function(t){return{x:this._XconvertDOMtoCanvas(t.x),y:this._YconvertDOMtoCanvas(t.y)}},s.prototype._drawNodes=function(t,e){void 0===e&&(e=!1);var i=this.nodes,s=[];for(var o in i)i.hasOwnProperty(o)&&(i[o].setScaleAndPos(this.scale,this.canvasTopLeft,this.canvasBottomRight),i[o].isSelected()?s.push(o):(i[o].inArea()||e)&&i[o].draw(t));for(var n=0,r=s.length;r>n;n++)(i[s[n]].inArea()||e)&&i[s[n]].draw(t)},s.prototype._drawEdges=function(t){var e=this.edges;for(var i in e)if(e.hasOwnProperty(i)){var s=e[i];s.setScale(this.scale),s.connected&&e[i].draw(t)}},s.prototype._drawControlNodes=function(t){var e=this.edges;for(var i in e)e.hasOwnProperty(i)&&e[i]._drawControlNodes(t)},s.prototype._stabilize=function(){1==this.constants.freezeForStabilization&&this._freezeDefinedNodes();for(var t=0;this.moving&&t0)for(t in i)i.hasOwnProperty(t)&&(i[t].discreteStepLimited(e,this.constants.maxVelocity),s=!0);else for(t in i)i.hasOwnProperty(t)&&(i[t].discreteStep(e),s=!0);if(1==s){var o=this.constants.minVelocity/Math.max(this.scale,.05);return o>.5*this.constants.maxVelocity?!0:this._isMoving(o)}return!1},s.prototype._revertPhysicsState=function(){var t=this.nodes;for(var e in t)t.hasOwnProperty(e)&&t[e].revertPosition()},s.prototype._revertPhysicsTick=function(){this._doInAllActiveSectors("_revertPhysicsState"),1==this.constants.smoothCurves.enabled&&1==this.constants.smoothCurves.dynamic&&this._doInSupportSector("_revertPhysicsState")},s.prototype._physicsTick=function(){if(!this.freezeSimulation&&1==this.moving){var t=!1,e=!1;this._doInAllActiveSectors("_initializeForceCalculation");var i=this._doInAllActiveSectors("_discreteStepNodes");1==this.constants.smoothCurves.enabled&&1==this.constants.smoothCurves.dynamic&&(e=this._doInSupportSector("_discreteStepNodes"));for(var s=0;s2*e||1==this.runDoubleSpeed)&&1==this.moving&&(this._physicsTick(),0!=this.renderTime&&(this.runDoubleSpeed=!0))}var i=Date.now();this._redraw(),this.renderTime=Date.now()-i,this.start()},"undefined"!=typeof window&&(window.requestAnimationFrame=window.requestAnimationFrame||window.mozRequestAnimationFrame||window.webkitRequestAnimationFrame||window.msRequestAnimationFrame),s.prototype.start=function(){if(1==this.moving||0!=this.xIncrement||0!=this.yIncrement||0!=this.zoomIncrement||1==this.animating)this.timer||(this.timer=1==this.requiresTimeout?window.setTimeout(this._animationStep.bind(this),this.renderTimestep):window.requestAnimationFrame(this._animationStep.bind(this)));else if(this._redraw(),this.stabilizationIterations>1){var t=this,e={iterations:t.stabilizationIterations};this.stabilizationIterations=0,this.startedStabilization=!1,setTimeout(function(){t.emit("stabilized",e)},0)}else this.stabilizationIterations=0},s.prototype._handleNavigation=function(){if(0!=this.xIncrement||0!=this.yIncrement){var t=this._getTranslation();this._setTranslation(t.x+this.xIncrement,t.y+this.yIncrement)}if(0!=this.zoomIncrement){var e={x:this.frame.canvas.clientWidth/2,y:this.frame.canvas.clientHeight/2};this._zoom(this.scale*(1+this.zoomIncrement),e)}},s.prototype.toggleFreeze=function(){0==this.freezeSimulation?this.freezeSimulation=!0:(this.freezeSimulation=!1,this.start())},s.prototype._configureSmoothCurves=function(t){if(void 0===t&&(t=!0),1==this.constants.smoothCurves.enabled&&1==this.constants.smoothCurves.dynamic){this._createBezierNodes();for(var e in this.sectors.support.nodes)this.sectors.support.nodes.hasOwnProperty(e)&&void 0===this.edges[this.sectors.support.nodes[e].parentEdgeId]&&delete this.sectors.support.nodes[e]}else{this.sectors.support.nodes={};for(var i in this.edges)this.edges.hasOwnProperty(i)&&(this.edges[i].via=null)}this._updateCalculationNodes(),t||(this.moving=!0,this.start())},s.prototype._createBezierNodes=function(){if(1==this.constants.smoothCurves.enabled&&1==this.constants.smoothCurves.dynamic)for(var t in this.edges)if(this.edges.hasOwnProperty(t)){var e=this.edges[t];if(null==e.via){var i="edgeId:".concat(e.id);this.sectors.support.nodes[i]=new f({id:i,mass:1,shape:"circle",image:"",internalMultiplier:1},{},{},this.constants),e.via=this.sectors.support.nodes[i],e.via.parentEdgeId=e.id,e.positionBezierNode()}}},s.prototype._initializeMixinLoaders=function(){for(var t in y)y.hasOwnProperty(t)&&(s.prototype[t]=y[t])},s.prototype.storePosition=function(){console.log("storePosition is depricated: use .storePositions() from now on."),this.storePositions()},s.prototype.storePositions=function(){var t=[];for(var e in this.nodes)if(this.nodes.hasOwnProperty(e)){var i=this.nodes[e],s=!this.nodes.xFixed,o=!this.nodes.yFixed;(this.nodesData._data[e].x!=Math.round(i.x)||this.nodesData._data[e].y!=Math.round(i.y))&&t.push({id:e,x:Math.round(i.x),y:Math.round(i.y),allowedToMoveX:s,allowedToMoveY:o})}this.nodesData.update(t)},s.prototype.getPositions=function(t){var e={};if(void 0!==t){if(1==Array.isArray(t)){for(var i=0;i=1&&(this.animating=!1,this.easingTime=0,this._redraw=null!=this.lockedOnNodeId?this._lockedRedraw:this._classicRedraw,this.emit("animationFinished"))},s.prototype._classicRedraw=function(){},s.prototype.isActive=function(){return!this.activator||this.activator.active},s.prototype.setScale=function(){return this._setScale()},s.prototype.getScale=function(){return this._getScale()},s.prototype.getCenterCoordinates=function(){return this.DOMtoCanvas({x:.5*this.frame.canvas.clientWidth,y:.5*this.frame.canvas.clientHeight})},s.prototype.getBoundingBox=function(t){return void 0!==this.nodes[t]?this.nodes[t].boundingBox:void 0},t.exports=s},function(t,e,i){function s(t,e,i){if(!e)throw"No network provided";var s=["edges","physics"],n=o.selectiveBridgeObject(s,i);this.options=n.edges,this.physics=n.physics,this.options.smoothCurves=i.smoothCurves,this.network=e,this.id=void 0,this.fromId=void 0,this.toId=void 0,this.title=void 0,this.widthSelected=this.options.width*this.options.widthSelectionMultiplier,this.value=void 0,this.selected=!1,this.hover=!1,this.labelDimensions={top:0,left:0,width:0,height:0,yLine:0},this.dirtyLabel=!0,this.from=null,this.to=null,this.via=null,this.fromBackup=null,this.toBackup=null,this.originalFromId=[],this.originalToId=[],this.connected=!1,this.widthFixed=!1,this.lengthFixed=!1,this.setProperties(t),this.controlNodesEnabled=!1,this.controlNodes={from:null,to:null,positions:{}},this.connectedNode=null}var o=i(1),n=i(40);s.prototype.setProperties=function(t){if(t){var e=["style","fontSize","fontFace","fontColor","fontFill","fontStrokeWidth","fontStrokeColor","width","widthSelectionMultiplier","hoverWidth","arrowScaleFactor","dash","inheritColor","labelAlignment"];switch(o.selectiveDeepExtend(e,this.options,t),void 0!==t.from&&(this.fromId=t.from),void 0!==t.to&&(this.toId=t.to),void 0!==t.id&&(this.id=t.id),void 0!==t.label&&(this.label=t.label,this.dirtyLabel=!0),void 0!==t.title&&(this.title=t.title),void 0!==t.value&&(this.value=t.value),void 0!==t.length&&(this.physics.springLength=t.length),void 0!==t.color&&(this.options.inheritColor=!1,o.isString(t.color)?(this.options.color.color=t.color,this.options.color.highlight=t.color):(void 0!==t.color.color&&(this.options.color.color=t.color.color),void 0!==t.color.highlight&&(this.options.color.highlight=t.color.highlight),void 0!==t.color.hover&&(this.options.color.hover=t.color.hover))),this.connect(),this.widthFixed=this.widthFixed||void 0!==t.width,this.lengthFixed=this.lengthFixed||void 0!==t.length,this.widthSelected=this.options.width*this.options.widthSelectionMultiplier,this.options.style){case"line":this.draw=this._drawLine;break;case"arrow":this.draw=this._drawArrow;break;case"arrow-center":this.draw=this._drawArrowCenter;break;case"dash-line":this.draw=this._drawDashLine;break;default:this.draw=this._drawLine}}},s.prototype.connect=function(){this.disconnect(),this.from=this.network.nodes[this.fromId]||null,this.to=this.network.nodes[this.toId]||null,this.connected=this.from&&this.to,this.connected?(this.from.attachEdge(this),this.to.attachEdge(this)):(this.from&&this.from.detachEdge(this),this.to&&this.to.detachEdge(this)) -},s.prototype.disconnect=function(){this.from&&(this.from.detachEdge(this),this.from=null),this.to&&(this.to.detachEdge(this),this.to=null),this.connected=!1},s.prototype.getTitle=function(){return"function"==typeof this.title?this.title():this.title},s.prototype.getValue=function(){return this.value},s.prototype.setValueRange=function(t,e){if(!this.widthFixed&&void 0!==this.value){var i=(this.options.widthMax-this.options.widthMin)/(e-t);this.options.width=(this.value-t)*i+this.options.widthMin,this.widthSelected=this.options.width*this.options.widthSelectionMultiplier}},s.prototype.draw=function(){throw"Method draw not initialized in edge"},s.prototype.isOverlappingWith=function(t){if(this.connected){var e=10,i=this.from.x,s=this.from.y,o=this.to.x,n=this.to.y,r=t.left,a=t.top,h=this._getDistanceToEdge(i,s,o,n,r,a);return e>h}return!1},s.prototype._getColor=function(){var t=this.options.color;return"to"==this.options.inheritColor?t={highlight:this.to.options.color.highlight.border,hover:this.to.options.color.hover.border,color:this.to.options.color.border}:("from"==this.options.inheritColor||1==this.options.inheritColor)&&(t={highlight:this.from.options.color.highlight.border,hover:this.from.options.color.hover.border,color:this.from.options.color.border}),1==this.selected?t.highlight:1==this.hover?t.hover:t.color},s.prototype._drawLine=function(t){if(t.strokeStyle=this._getColor(),t.lineWidth=this._getLineWidth(),this.from!=this.to){var e,i=this._line(t);if(this.label){if(1==this.options.smoothCurves.enabled&&null!=i){var s=.5*(.5*(this.from.x+i.x)+.5*(this.to.x+i.x)),o=.5*(.5*(this.from.y+i.y)+.5*(this.to.y+i.y));e={x:s,y:o}}else e=this._pointOnLine(.5);this._label(t,this.label,e.x,e.y)}}else{var n,r,a=this.physics.springLength/4,h=this.from;h.width||h.resize(t),h.width>h.height?(n=h.x+h.width/2,r=h.y-a):(n=h.x+a,r=h.y-h.height/2),this._circle(t,n,r,a),e=this._pointOnCircle(n,r,a,.5),this._label(t,this.label,e.x,e.y)}},s.prototype._getLineWidth=function(){return 1==this.selected?Math.max(Math.min(this.widthSelected,this.options.widthMax),.3*this.networkScaleInv):1==this.hover?Math.max(Math.min(this.options.hoverWidth,this.options.widthMax),.3*this.networkScaleInv):Math.max(this.options.width,.3*this.networkScaleInv)},s.prototype._getViaCoordinates=function(){if(1==this.options.smoothCurves.dynamic&&1==this.options.smoothCurves.enabled)return this.via;if(0==this.options.smoothCurves.enabled)return{x:0,y:0};var t=null,e=null,i=this.options.smoothCurves.roundness,s=this.options.smoothCurves.type,o=Math.abs(this.from.x-this.to.x),n=Math.abs(this.from.y-this.to.y);return"discrete"==s||"diagonalCross"==s?Math.abs(this.from.x-this.to.x)this.to.y?this.from.xthis.to.x&&(t=this.from.x-i*n,e=this.from.y-i*n):this.from.ythis.to.x&&(t=this.from.x-i*n,e=this.from.y+i*n)),"discrete"==s&&(t=i*n>o?this.from.x:t)):Math.abs(this.from.x-this.to.x)>Math.abs(this.from.y-this.to.y)&&(this.from.y>this.to.y?this.from.xthis.to.x&&(t=this.from.x-i*o,e=this.from.y-i*o):this.from.ythis.to.x&&(t=this.from.x-i*o,e=this.from.y+i*o)),"discrete"==s&&(e=i*o>n?this.from.y:e)):"straightCross"==s?Math.abs(this.from.x-this.to.x)Math.abs(this.from.y-this.to.y)&&(t=this.from.xthis.to.y?this.from.xthis.to.x&&(t=this.from.x-i*n,e=this.from.y-i*n,t=this.to.x>t?this.to.x:t):this.from.ythis.to.x&&(t=this.from.x-i*n,e=this.from.y+i*n,t=this.to.x>t?this.to.x:t)):Math.abs(this.from.x-this.to.x)>Math.abs(this.from.y-this.to.y)&&(this.from.y>this.to.y?this.from.xe?this.to.y:e):this.from.x>this.to.x&&(t=this.from.x-i*o,e=this.from.y-i*o,e=this.to.y>e?this.to.y:e):this.from.ythis.to.x&&(t=this.from.x-i*o,e=this.from.y+i*o,e=this.to.yd;d++){var l=t.measureText(n[d]).width;h=l>h?l:h}var c=this.options.fontSize*r,p=i-h/2,u=s-c/2;this.labelDimensions={top:u,left:p,width:h,height:c,yLine:o}}var o=this.labelDimensions.yLine;t.save(),"horizontal"!=this.options.labelAlignment&&(t.translate(i,o),this._rotateForLabelAlignment(t),i=0,o=0),this._drawLabelRect(t),this._drawLabelText(t,i,o,n,r,a),t.restore()}},s.prototype._rotateForLabelAlignment=function(t){var e=this.from.y-this.to.y,i=this.from.x-this.to.x,s=Math.atan2(e,i);(-1>s&&0>i||s>0&&0>i)&&(s+=Math.PI),t.rotate(s)},s.prototype._drawLabelRect=function(t){if(void 0!==this.options.fontFill&&null!==this.options.fontFill&&"none"!==this.options.fontFill){t.fillStyle=this.options.fontFill;var e=2;"line-center"==this.options.labelAlignment?t.fillRect(.5*-this.labelDimensions.width,.5*-this.labelDimensions.height,this.labelDimensions.width,this.labelDimensions.height):"line-above"==this.options.labelAlignment?t.fillRect(.5*-this.labelDimensions.width,-(this.labelDimensions.height+e),this.labelDimensions.width,this.labelDimensions.height):"line-below"==this.options.labelAlignment?t.fillRect(.5*-this.labelDimensions.width,e,this.labelDimensions.width,this.labelDimensions.height):t.fillRect(this.labelDimensions.left,this.labelDimensions.top,this.labelDimensions.width,this.labelDimensions.height)}},s.prototype._drawLabelText=function(t,e,i,s,o,n){if(t.fillStyle=this.options.fontColor||"black",t.textAlign="center","horizontal"!=this.options.labelAlignment){var r=2;"line-above"==this.options.labelAlignment?(t.textBaseline="alphabetic",i-=2*r):"line-below"==this.options.labelAlignment?(t.textBaseline="hanging",i+=2*r):t.textBaseline="middle"}else t.textBaseline="middle";this.options.fontStrokeWidth>0&&(t.lineWidth=this.options.fontStrokeWidth,t.strokeStyle=this.options.fontStrokeColor,t.lineJoin="round");for(var a=0;o>a;a++)this.options.fontStrokeWidth>0&&t.strokeText(s[a],e,i),t.fillText(s[a],e,i),i+=n},s.prototype._drawDashLine=function(t){t.strokeStyle=this._getColor(),t.lineWidth=this._getLineWidth();var e=null;if(void 0!==t.setLineDash){t.save();var i=[0];i=void 0!==this.options.dash.length&&void 0!==this.options.dash.gap?[this.options.dash.length,this.options.dash.gap]:[5,5],t.setLineDash(i),t.lineDashOffset=0,e=this._line(t),t.setLineDash([0]),t.lineDashOffset=0,t.restore()}else t.beginPath(),t.lineCap="round",void 0!==this.options.dash.altLength?t.dashedLine(this.from.x,this.from.y,this.to.x,this.to.y,[this.options.dash.length,this.options.dash.gap,this.options.dash.altLength,this.options.dash.gap]):void 0!==this.options.dash.length&&void 0!==this.options.dash.gap?t.dashedLine(this.from.x,this.from.y,this.to.x,this.to.y,[this.options.dash.length,this.options.dash.gap]):(t.moveTo(this.from.x,this.from.y),t.lineTo(this.to.x,this.to.y)),t.stroke();if(this.label){var s;if(1==this.options.smoothCurves.enabled&&null!=e){var o=.5*(.5*(this.from.x+e.x)+.5*(this.to.x+e.x)),n=.5*(.5*(this.from.y+e.y)+.5*(this.to.y+e.y));s={x:o,y:n}}else s=this._pointOnLine(.5);this._label(t,this.label,s.x,s.y)}},s.prototype._pointOnLine=function(t){return{x:(1-t)*this.from.x+t*this.to.x,y:(1-t)*this.from.y+t*this.to.y}},s.prototype._pointOnCircle=function(t,e,i,s){var o=2*(s-3/8)*Math.PI;return{x:t+i*Math.cos(o),y:e-i*Math.sin(o)}},s.prototype._drawArrowCenter=function(t){var e;if(t.strokeStyle=this._getColor(),t.fillStyle=t.strokeStyle,t.lineWidth=this._getLineWidth(),this.from!=this.to){var i=this._line(t),s=Math.atan2(this.to.y-this.from.y,this.to.x-this.from.x),o=(10+5*this.options.width)*this.options.arrowScaleFactor;if(1==this.options.smoothCurves.enabled&&null!=i){var n=.5*(.5*(this.from.x+i.x)+.5*(this.to.x+i.x)),r=.5*(.5*(this.from.y+i.y)+.5*(this.to.y+i.y));e={x:n,y:r}}else e=this._pointOnLine(.5);t.arrow(e.x,e.y,s,o),t.fill(),t.stroke(),this.label&&this._label(t,this.label,e.x,e.y)}else{var a,h,d=.25*Math.max(100,this.physics.springLength),l=this.from;l.width||l.resize(t),l.width>l.height?(a=l.x+.5*l.width,h=l.y-d):(a=l.x+d,h=l.y-.5*l.height),this._circle(t,a,h,d);var s=.2*Math.PI,o=(10+5*this.options.width)*this.options.arrowScaleFactor;e=this._pointOnCircle(a,h,d,.5),t.arrow(e.x,e.y,s,o),t.fill(),t.stroke(),this.label&&(e=this._pointOnCircle(a,h,d,.5),this._label(t,this.label,e.x,e.y))}},s.prototype._pointOnBezier=function(t){var e=this._getViaCoordinates(),i=Math.pow(1-t,2)*this.from.x+2*t*(1-t)*e.x+Math.pow(t,2)*this.to.x,s=Math.pow(1-t,2)*this.from.y+2*t*(1-t)*e.y+Math.pow(t,2)*this.to.y;return{x:i,y:s}},s.prototype._findBorderPosition=function(t,e){var i,s,o,n,r,a=10,h=0,d=0,l=1,c=.2,p=this.to;for(1==t&&(p=this.from);l>=d&&a>h;){var u=.5*(d+l);if(i=this._pointOnBezier(u),s=Math.atan2(p.y-i.y,p.x-i.x),o=p.distanceToBorder(e,s),n=Math.sqrt(Math.pow(i.x-p.x,2)+Math.pow(i.y-p.y,2)),r=o-n,Math.abs(r)r?0==t?d=u:l=u:0==t?l=u:d=u,h++}return i.t=u,i},s.prototype._drawArrow=function(t){t.strokeStyle=this._getColor(),t.fillStyle=t.strokeStyle,t.lineWidth=this._getLineWidth();var e,i,s;if(this.from!=this.to){if(this._line(t),1==this.options.smoothCurves.enabled){var o=this._getViaCoordinates();s=this._findBorderPosition(!1,t);var n=this._pointOnBezier(Math.max(0,s.t-.1));e=Math.atan2(s.y-n.y,s.x-n.x)}else{e=Math.atan2(this.to.y-this.from.y,this.to.x-this.from.x);var r=this.to.x-this.from.x,a=this.to.y-this.from.y,h=Math.sqrt(r*r+a*a),d=this.to.distanceToBorder(t,e),l=(h-d)/h;s={},s.x=(1-l)*this.from.x+l*this.to.x,s.y=(1-l)*this.from.y+l*this.to.y}if(i=(10+5*this.options.width)*this.options.arrowScaleFactor,t.arrow(s.x,s.y,e,i),t.fill(),t.stroke(),this.label){var c;c=1==this.options.smoothCurves.enabled&&null!=o?this._pointOnBezier(.5):this._pointOnLine(.5),this._label(t,this.label,c.x,c.y)}}else{var p,u,m,f=this.from,g=.25*Math.max(100,this.physics.springLength);f.width||f.resize(t),f.width>f.height?(p=f.x+.5*f.width,u=f.y-g,m={x:p,y:f.y,angle:.9*Math.PI}):(p=f.x+g,u=f.y-.5*f.height,m={x:f.x,y:u,angle:.6*Math.PI}),t.beginPath(),t.arc(p,u,g,0,2*Math.PI,!1),t.stroke();var i=(10+5*this.options.width)*this.options.arrowScaleFactor;t.arrow(m.x,m.y,m.angle,i),t.fill(),t.stroke(),this.label&&(c=this._pointOnCircle(p,u,g,.5),this._label(t,this.label,c.x,c.y))}},s.prototype._getDistanceToEdge=function(t,e,i,s,o,n){var r=0;if(this.from!=this.to)if(1==this.options.smoothCurves.enabled){var a,h;if(1==this.options.smoothCurves.enabled&&1==this.options.smoothCurves.dynamic)a=this.via.x,h=this.via.y;else{var d=this._getViaCoordinates();a=d.x,h=d.y}var l,c,p,u,m,f,g,v=1e9;for(c=0;10>c;c++)p=.1*c,u=Math.pow(1-p,2)*t+2*p*(1-p)*a+Math.pow(p,2)*i,m=Math.pow(1-p,2)*e+2*p*(1-p)*h+Math.pow(p,2)*s,c>0&&(l=this._getDistanceToLine(f,g,u,m,o,n),v=v>l?l:v),f=u,g=m;r=v}else r=this._getDistanceToLine(t,e,i,s,o,n);else{var u,m,y,b,_=.25*this.physics.springLength,x=this.from;x.width>x.height?(u=x.x+.5*x.width,m=x.y-_):(u=x.x+_,m=x.y-.5*x.height),y=u-o,b=m-n,r=Math.abs(Math.sqrt(y*y+b*b)-_)}return this.labelDimensions.lefto&&this.labelDimensions.topn?0:r},s.prototype._getDistanceToLine=function(t,e,i,s,o,n){var r=i-t,a=s-e,h=r*r+a*a,d=((o-t)*r+(n-e)*a)/h;d>1?d=1:0>d&&(d=0);var l=t+d*r,c=e+d*a,p=l-o,u=c-n;return Math.sqrt(p*p+u*u)},s.prototype.setScale=function(t){this.networkScaleInv=1/t},s.prototype.select=function(){this.selected=!0},s.prototype.unselect=function(){this.selected=!1},s.prototype.positionBezierNode=function(){null!==this.via&&null!==this.from&&null!==this.to?(this.via.x=.5*(this.from.x+this.to.x),this.via.y=.5*(this.from.y+this.to.y)):null!==this.via&&(this.via.x=0,this.via.y=0)},s.prototype._drawControlNodes=function(t){if(1==this.controlNodesEnabled){if(null===this.controlNodes.from&&null===this.controlNodes.to){var e="edgeIdFrom:".concat(this.id),i="edgeIdTo:".concat(this.id),s={nodes:{group:"",radius:7,borderWidth:2,borderWidthSelected:2},physics:{damping:0},clustering:{maxNodeSizeIncrements:0,nodeScaling:{width:0,height:0,radius:0}}};this.controlNodes.from=new n({id:e,shape:"dot",color:{background:"#ff0000",border:"#3c3c3c",highlight:{background:"#07f968"}}},{},{},s),this.controlNodes.to=new n({id:i,shape:"dot",color:{background:"#ff0000",border:"#3c3c3c",highlight:{background:"#07f968"}}},{},{},s)}this.controlNodes.positions={},0==this.controlNodes.from.selected&&(this.controlNodes.positions.from=this.getControlNodeFromPosition(t),this.controlNodes.from.x=this.controlNodes.positions.from.x,this.controlNodes.from.y=this.controlNodes.positions.from.y),0==this.controlNodes.to.selected&&(this.controlNodes.positions.to=this.getControlNodeToPosition(t),this.controlNodes.to.x=this.controlNodes.positions.to.x,this.controlNodes.to.y=this.controlNodes.positions.to.y),this.controlNodes.from.draw(t),this.controlNodes.to.draw(t)}else this.controlNodes={from:null,to:null,positions:{}}},s.prototype._enableControlNodes=function(){this.fromBackup=this.from,this.toBackup=this.to,this.controlNodesEnabled=!0},s.prototype._disableControlNodes=function(){this.fromId=this.from.id,this.toId=this.to.id,this.fromId!=this.fromBackup.id?this.fromBackup.detachEdge(this):this.toId!=this.toBackup.id&&this.toBackup.detachEdge(this),this.fromBackup=null,this.toBackup=null,this.controlNodesEnabled=!1},s.prototype._getSelectedControlNode=function(t,e){var i=this.controlNodes.positions,s=Math.sqrt(Math.pow(t-i.from.x,2)+Math.pow(e-i.from.y,2)),o=Math.sqrt(Math.pow(t-i.to.x,2)+Math.pow(e-i.to.y,2));return 15>s?(this.connectedNode=this.from,this.from=this.controlNodes.from,this.controlNodes.from):15>o?(this.connectedNode=this.to,this.to=this.controlNodes.to,this.controlNodes.to):null},s.prototype._restoreControlNodes=function(){1==this.controlNodes.from.selected?(this.from=this.connectedNode,this.connectedNode=null,this.controlNodes.from.unselect()):1==this.controlNodes.to.selected&&(this.to=this.connectedNode,this.connectedNode=null,this.controlNodes.to.unselect())},s.prototype.getControlNodeFromPosition=function(t){var e;if(1==this.options.smoothCurves.enabled)e=this._findBorderPosition(!0,t);else{var i=Math.atan2(this.to.y-this.from.y,this.to.x-this.from.x),s=this.to.x-this.from.x,o=this.to.y-this.from.y,n=Math.sqrt(s*s+o*o),r=this.from.distanceToBorder(t,i+Math.PI),a=(n-r)/n;e={},e.x=a*this.from.x+(1-a)*this.to.x,e.y=a*this.from.y+(1-a)*this.to.y}return e},s.prototype.getControlNodeToPosition=function(t){var e;if(1==this.options.smoothCurves.enabled)e=this._findBorderPosition(!1,t);else{var i=Math.atan2(this.to.y-this.from.y,this.to.x-this.from.x),s=this.to.x-this.from.x,o=this.to.y-this.from.y,n=Math.sqrt(s*s+o*o),r=this.to.distanceToBorder(t,i),a=(n-r)/n;e={},e.x=(1-a)*this.from.x+a*this.to.x,e.y=(1-a)*this.from.y+a*this.to.y}return e},t.exports=s},function(t,e,i){function s(){this.clear(),this.defaultIndex=0}i(1);s.DEFAULT=[{border:"#2B7CE9",background:"#97C2FC",highlight:{border:"#2B7CE9",background:"#D2E5FF"},hover:{border:"#2B7CE9",background:"#D2E5FF"}},{border:"#FFA500",background:"#FFFF00",highlight:{border:"#FFA500",background:"#FFFFA3"},hover:{border:"#FFA500",background:"#FFFFA3"}},{border:"#FA0A10",background:"#FB7E81",highlight:{border:"#FA0A10",background:"#FFAFB1"},hover:{border:"#FA0A10",background:"#FFAFB1"}},{border:"#41A906",background:"#7BE141",highlight:{border:"#41A906",background:"#A1EC76"},hover:{border:"#41A906",background:"#A1EC76"}},{border:"#E129F0",background:"#EB7DF4",highlight:{border:"#E129F0",background:"#F0B3F5"},hover:{border:"#E129F0",background:"#F0B3F5"}},{border:"#7C29F0",background:"#AD85E4",highlight:{border:"#7C29F0",background:"#D3BDF0"},hover:{border:"#7C29F0",background:"#D3BDF0"}},{border:"#C37F00",background:"#FFA807",highlight:{border:"#C37F00",background:"#FFCA66"},hover:{border:"#C37F00",background:"#FFCA66"}},{border:"#4220FB",background:"#6E6EFD",highlight:{border:"#4220FB",background:"#9B9BFD"},hover:{border:"#4220FB",background:"#9B9BFD"}},{border:"#FD5A77",background:"#FFC0CB",highlight:{border:"#FD5A77",background:"#FFD1D9"},hover:{border:"#FD5A77",background:"#FFD1D9"}},{border:"#4AD63A",background:"#C2FABC",highlight:{border:"#4AD63A",background:"#E6FFE3"},hover:{border:"#4AD63A",background:"#E6FFE3"}}],s.prototype.clear=function(){this.groups={},this.groups.length=function(){var t=0;for(var e in this)this.hasOwnProperty(e)&&t++;return t}},s.prototype.get=function(t){var e=this.groups[t];if(void 0==e){var i=this.defaultIndex%s.DEFAULT.length;this.defaultIndex++,e={},e.color=s.DEFAULT[i],this.groups[t]=e}return e},s.prototype.add=function(t,e){return this.groups[t]=e,e},t.exports=s},function(t){function e(){this.images={},this.imageBroken={},this.callback=void 0}e.prototype.setOnloadCallback=function(t){this.callback=t},e.prototype.load=function(t,e){var i=this.images[t];if(void 0===i){var s=this;i=new Image,i.onload=function(){0==this.width&&(document.body.appendChild(this),this.width=this.offsetWidth,this.height=this.offsetHeight,document.body.removeChild(this)),s.callback&&(s.images[t]=i,s.callback(this))},i.onerror=function(){void 0===e?(console.error("Could not load image:",t),delete this.src,s.callback&&s.callback(this)):s.imageBroken[t]===!0?(console.error("Could not load brokenImage:",e),delete this.src,s.callback&&s.callback(this)):(this.src=e,s.imageBroken[t]=!0)},i.src=t}return i},t.exports=e},function(t,e,i){function s(t,e,i,s){var n=o.selectiveBridgeObject(["nodes"],s);this.options=n.nodes,this.selected=!1,this.hover=!1,this.edges=[],this.dynamicEdges=[],this.reroutedEdges={},this.fontDrawThreshold=3,this.id=void 0,this.allowedToMoveX=!1,this.allowedToMoveY=!1,this.xFixed=!1,this.yFixed=!1,this.horizontalAlignLeft=!0,this.verticalAlignTop=!0,this.baseRadiusValue=s.nodes.radius,this.radiusFixed=!1,this.level=-1,this.preassignedLevel=!1,this.hierarchyEnumerated=!1,this.labelDimensions={top:0,left:0,width:0,height:0,yLine:0},this.boundingBox={top:0,left:0,right:0,bottom:0},this.imagelist=e,this.grouplist=i,this.fx=0,this.fy=0,this.vx=0,this.vy=0,this.x=null,this.y=null,this.previousState={vx:0,vy:0,x:0,y:0},this.damping=s.physics.damping,this.fixedData={x:null,y:null},this.setProperties(t,n),this.resetCluster(),this.dynamicEdgesLength=0,this.clusterSession=0,this.clusterSizeWidthFactor=s.clustering.nodeScaling.width,this.clusterSizeHeightFactor=s.clustering.nodeScaling.height,this.clusterSizeRadiusFactor=s.clustering.nodeScaling.radius,this.maxNodeSizeIncrements=s.clustering.maxNodeSizeIncrements,this.growthIndicator=0,this.networkScaleInv=1,this.networkScale=1,this.canvasTopLeft={x:-300,y:-300},this.canvasBottomRight={x:300,y:300},this.parentEdgeId=null}var o=i(1);s.prototype.revertPosition=function(){this.x=this.previousState.x,this.y=this.previousState.y,this.vx=this.previousState.vx,this.vy=this.previousState.vy},s.prototype.resetCluster=function(){this.formationScale=void 0,this.clusterSize=1,this.containedNodes={},this.containedEdges={},this.clusterSessions=[]},s.prototype.attachEdge=function(t){-1==this.edges.indexOf(t)&&this.edges.push(t),-1==this.dynamicEdges.indexOf(t)&&this.dynamicEdges.push(t),this.dynamicEdgesLength=this.dynamicEdges.length},s.prototype.detachEdge=function(t){var e=this.edges.indexOf(t);-1!=e&&this.edges.splice(e,1),e=this.dynamicEdges.indexOf(t),-1!=e&&this.dynamicEdges.splice(e,1),this.dynamicEdgesLength=this.dynamicEdges.length},s.prototype.setProperties=function(t,e){if(t){var i=["borderWidth","borderWidthSelected","shape","image","brokenImage","radius","fontColor","fontSize","fontFace","fontFill","fontStrokeWidth","fontStrokeColor","group","mass"];if(o.selectiveDeepExtend(i,this.options,t),void 0!==t.id&&(this.id=t.id),void 0!==t.label&&(this.label=t.label,this.originalLabel=t.label),void 0!==t.title&&(this.title=t.title),void 0!==t.x&&(this.x=t.x),void 0!==t.y&&(this.y=t.y),void 0!==t.value&&(this.value=t.value),void 0!==t.level&&(this.level=t.level,this.preassignedLevel=!0),void 0!==t.horizontalAlignLeft&&(this.horizontalAlignLeft=t.horizontalAlignLeft),void 0!==t.verticalAlignTop&&(this.verticalAlignTop=t.verticalAlignTop),void 0!==t.triggerFunction&&(this.triggerFunction=t.triggerFunction),void 0===this.id)throw"Node must have an id";if("number"==typeof t.group||"string"==typeof t.group&&""!=t.group){var s=this.grouplist.get(t.group);o.deepExtend(this.options,s),this.options.color=o.parseColor(this.options.color)}if(void 0!==t.radius&&(this.baseRadiusValue=this.options.radius),void 0!==t.color&&(this.options.color=o.parseColor(t.color)),void 0!==this.options.image&&""!=this.options.image){if(!this.imagelist)throw"No imagelist provided";this.imageObj=this.imagelist.load(this.options.image,this.options.brokenImage)}switch(void 0!==t.allowedToMoveX?(this.xFixed=!t.allowedToMoveX,this.allowedToMoveX=t.allowedToMoveX):void 0!==t.x&&0==this.allowedToMoveX&&(this.xFixed=!0),void 0!==t.allowedToMoveY?(this.yFixed=!t.allowedToMoveY,this.allowedToMoveY=t.allowedToMoveY):void 0!==t.y&&0==this.allowedToMoveY&&(this.yFixed=!0),this.radiusFixed=this.radiusFixed||void 0!==t.radius,("image"===this.options.shape||"circularImage"===this.options.shape)&&(this.options.radiusMin=e.nodes.widthMin,this.options.radiusMax=e.nodes.widthMax),this.options.shape){case"database":this.draw=this._drawDatabase,this.resize=this._resizeDatabase;break;case"box":this.draw=this._drawBox,this.resize=this._resizeBox;break;case"circle":this.draw=this._drawCircle,this.resize=this._resizeCircle;break;case"ellipse":this.draw=this._drawEllipse,this.resize=this._resizeEllipse;break;case"image":this.draw=this._drawImage,this.resize=this._resizeImage;break;case"circularImage":this.draw=this._drawCircularImage,this.resize=this._resizeCircularImage;break;case"text":this.draw=this._drawText,this.resize=this._resizeText;break;case"dot":this.draw=this._drawDot,this.resize=this._resizeShape;break;case"square":this.draw=this._drawSquare,this.resize=this._resizeShape;break;case"triangle":this.draw=this._drawTriangle,this.resize=this._resizeShape;break;case"triangleDown":this.draw=this._drawTriangleDown,this.resize=this._resizeShape;break;case"star":this.draw=this._drawStar,this.resize=this._resizeShape;break;default:this.draw=this._drawEllipse,this.resize=this._resizeEllipse}this._reset()}},s.prototype.select=function(){this.selected=!0,this._reset()},s.prototype.unselect=function(){this.selected=!1,this._reset()},s.prototype.clearSizeCache=function(){this._reset()},s.prototype._reset=function(){this.width=void 0,this.height=void 0},s.prototype.getTitle=function(){return"function"==typeof this.title?this.title():this.title},s.prototype.distanceToBorder=function(t,e){var i=1;switch(this.width||this.resize(t),this.options.shape){case"circle":case"dot":return this.options.radius+i;case"ellipse":var s=this.width/2,o=this.height/2,n=Math.sin(e)*s,r=Math.cos(e)*o;return s*o/Math.sqrt(n*n+r*r);case"box":case"image":case"text":default:return this.width?Math.min(Math.abs(this.width/2/Math.cos(e)),Math.abs(this.height/2/Math.sin(e)))+i:0}},s.prototype._setForce=function(t,e){this.fx=t,this.fy=e},s.prototype._addForce=function(t,e){this.fx+=t,this.fy+=e},s.prototype.storeState=function(){this.previousState.x=this.x,this.previousState.y=this.y,this.previousState.vx=this.vx,this.previousState.vy=this.vy},s.prototype.discreteStep=function(t){if(this.storeState(),this.xFixed)this.fx=0,this.vx=0;else{var e=this.damping*this.vx,i=(this.fx-e)/this.options.mass;this.vx+=i*t,this.x+=this.vx*t}if(this.yFixed)this.fy=0,this.vy=0;else{var s=this.damping*this.vy,o=(this.fy-s)/this.options.mass;this.vy+=o*t,this.y+=this.vy*t}},s.prototype.discreteStepLimited=function(t,e){if(this.storeState(),this.xFixed)this.fx=0,this.vx=0;else{var i=this.damping*this.vx,s=(this.fx-i)/this.options.mass;this.vx+=s*t,this.vx=Math.abs(this.vx)>e?this.vx>0?e:-e:this.vx,this.x+=this.vx*t}if(this.yFixed)this.fy=0,this.vy=0;else{var o=this.damping*this.vy,n=(this.fy-o)/this.options.mass;this.vy+=n*t,this.vy=Math.abs(this.vy)>e?this.vy>0?e:-e:this.vy,this.y+=this.vy*t}},s.prototype.isFixed=function(){return this.xFixed&&this.yFixed},s.prototype.isMoving=function(t){var e=Math.sqrt(Math.pow(this.vx,2)+Math.pow(this.vy,2));return e>t},s.prototype.isSelected=function(){return this.selected},s.prototype.getValue=function(){return this.value},s.prototype.getDistance=function(t,e){var i=this.x-t,s=this.y-e;return Math.sqrt(i*i+s*s)},s.prototype.setValueRange=function(t,e){if(!this.radiusFixed&&void 0!==this.value)if(e==t)this.options.radius=(this.options.radiusMin+this.options.radiusMax)/2;else{var i=(this.options.radiusMax-this.options.radiusMin)/(e-t);this.options.radius=(this.value-t)*i+this.options.radiusMin}this.baseRadiusValue=this.options.radius},s.prototype.draw=function(){throw"Draw method not initialized for node"},s.prototype.resize=function(){throw"Resize method not initialized for node"},s.prototype.isOverlappingWith=function(t){return this.leftt.left&&this.topt.top},s.prototype._resizeImage=function(){if(!this.width||!this.height){var t,e;if(this.value){this.options.radius=this.baseRadiusValue;var i=this.imageObj.height/this.imageObj.width;void 0!==i?(t=this.options.radius||this.imageObj.width,e=this.options.radius*i||this.imageObj.height):(t=0,e=0)}else t=this.imageObj.width,e=this.imageObj.height;this.width=t,this.height=e,this.growthIndicator=0,this.width>0&&this.height>0&&(this.width+=Math.min(this.clusterSize-1,this.maxNodeSizeIncrements)*this.clusterSizeWidthFactor,this.height+=Math.min(this.clusterSize-1,this.maxNodeSizeIncrements)*this.clusterSizeHeightFactor,this.options.radius+=Math.min(this.clusterSize-1,this.maxNodeSizeIncrements)*this.clusterSizeRadiusFactor,this.growthIndicator=this.width-t)}},s.prototype._drawImageAtPosition=function(t){if(0!=this.imageObj.width){if(this.clusterSize>1){var e=this.clusterSize>1?10:0;e*=this.networkScaleInv,e=Math.min(.2*this.width,e),t.globalAlpha=.5,t.drawImage(this.imageObj,this.left-e,this.top-e,this.width+2*e,this.height+2*e)}t.globalAlpha=1,t.drawImage(this.imageObj,this.left,this.top,this.width,this.height)}},s.prototype._drawImageLabel=function(t){var e,i=0;if(this.height){i=this.height/2;var s=this.getTextSize(t);s.lineCount>=1&&(i+=s.height/2,i+=3)}e=this.y+i,this._label(t,this.label,this.x,e,void 0)},s.prototype._drawImage=function(t){this._resizeImage(t),this.left=this.x-this.width/2,this.top=this.y-this.height/2,this._drawImageAtPosition(t),this.boundingBox.top=this.top,this.boundingBox.left=this.left,this.boundingBox.right=this.left+this.width,this.boundingBox.bottom=this.top+this.height,this._drawImageLabel(t),this.boundingBox.left=Math.min(this.boundingBox.left,this.labelDimensions.left),this.boundingBox.right=Math.max(this.boundingBox.right,this.labelDimensions.left+this.labelDimensions.width),this.boundingBox.bottom=Math.max(this.boundingBox.bottom,this.boundingBox.bottom+this.labelDimensions.height)},s.prototype._resizeCircularImage=function(t){if(this.imageObj.src&&this.imageObj.width&&this.imageObj.height)this._swapToImageResizeWhenImageLoaded&&(this.width=0,this.height=0,delete this._swapToImageResizeWhenImageLoaded),this._resizeImage(t);else if(!this.width){var e=2*this.options.radius;this.width=e,this.height=e,this.options.radius+=.5*Math.min(this.clusterSize-1,this.maxNodeSizeIncrements)*this.clusterSizeRadiusFactor,this.growthIndicator=this.options.radius-.5*e,this._swapToImageResizeWhenImageLoaded=!0}},s.prototype._drawCircularImage=function(t){this._resizeCircularImage(t),this.left=this.x-this.width/2,this.top=this.y-this.height/2;var e=this.left+this.width/2,i=this.top+this.height/2,s=Math.abs(this.height/2);this._drawRawCircle(t,e,i,s),t.save(),t.circle(this.x,this.y,s),t.stroke(),t.clip(),this._drawImageAtPosition(t),t.restore(),this.boundingBox.top=this.y-this.options.radius,this.boundingBox.left=this.x-this.options.radius,this.boundingBox.right=this.x+this.options.radius,this.boundingBox.bottom=this.y+this.options.radius,this._drawImageLabel(t),this.boundingBox.left=Math.min(this.boundingBox.left,this.labelDimensions.left),this.boundingBox.right=Math.max(this.boundingBox.right,this.labelDimensions.left+this.labelDimensions.width),this.boundingBox.bottom=Math.max(this.boundingBox.bottom,this.boundingBox.bottom+this.labelDimensions.height)},s.prototype._resizeBox=function(t){if(!this.width){var e=5,i=this.getTextSize(t);this.width=i.width+2*e,this.height=i.height+2*e,this.width+=.5*Math.min(this.clusterSize-1,this.maxNodeSizeIncrements)*this.clusterSizeWidthFactor,this.height+=.5*Math.min(this.clusterSize-1,this.maxNodeSizeIncrements)*this.clusterSizeHeightFactor,this.growthIndicator=this.width-(i.width+2*e)}},s.prototype._drawBox=function(t){this._resizeBox(t),this.left=this.x-this.width/2,this.top=this.y-this.height/2;var e=2.5,i=this.options.borderWidth,s=this.options.borderWidthSelected||2*this.options.borderWidth;t.strokeStyle=this.selected?this.options.color.highlight.border:this.hover?this.options.color.hover.border:this.options.color.border,this.clusterSize>1&&(t.lineWidth=(this.selected?s:i)+(this.clusterSize>1?e:0),t.lineWidth*=this.networkScaleInv,t.lineWidth=Math.min(this.width,t.lineWidth),t.roundRect(this.left-2*t.lineWidth,this.top-2*t.lineWidth,this.width+4*t.lineWidth,this.height+4*t.lineWidth,this.options.radius),t.stroke()),t.lineWidth=(this.selected?s:i)+(this.clusterSize>1?e:0),t.lineWidth*=this.networkScaleInv,t.lineWidth=Math.min(this.width,t.lineWidth),t.fillStyle=this.selected?this.options.color.highlight.background:this.hover?this.options.color.hover.background:this.options.color.background,t.roundRect(this.left,this.top,this.width,this.height,this.options.radius),t.fill(),t.stroke(),this.boundingBox.top=this.top,this.boundingBox.left=this.left,this.boundingBox.right=this.left+this.width,this.boundingBox.bottom=this.top+this.height,this._label(t,this.label,this.x,this.y)},s.prototype._resizeDatabase=function(t){if(!this.width){var e=5,i=this.getTextSize(t),s=i.width+2*e;this.width=s,this.height=s,this.width+=Math.min(this.clusterSize-1,this.maxNodeSizeIncrements)*this.clusterSizeWidthFactor,this.height+=Math.min(this.clusterSize-1,this.maxNodeSizeIncrements)*this.clusterSizeHeightFactor,this.options.radius+=Math.min(this.clusterSize-1,this.maxNodeSizeIncrements)*this.clusterSizeRadiusFactor,this.growthIndicator=this.width-s}},s.prototype._drawDatabase=function(t){this._resizeDatabase(t),this.left=this.x-this.width/2,this.top=this.y-this.height/2; -var e=2.5,i=this.options.borderWidth,s=this.options.borderWidthSelected||2*this.options.borderWidth;t.strokeStyle=this.selected?this.options.color.highlight.border:this.hover?this.options.color.hover.border:this.options.color.border,this.clusterSize>1&&(t.lineWidth=(this.selected?s:i)+(this.clusterSize>1?e:0),t.lineWidth*=this.networkScaleInv,t.lineWidth=Math.min(this.width,t.lineWidth),t.database(this.x-this.width/2-2*t.lineWidth,this.y-.5*this.height-2*t.lineWidth,this.width+4*t.lineWidth,this.height+4*t.lineWidth),t.stroke()),t.lineWidth=(this.selected?s:i)+(this.clusterSize>1?e:0),t.lineWidth*=this.networkScaleInv,t.lineWidth=Math.min(this.width,t.lineWidth),t.fillStyle=this.selected?this.options.color.highlight.background:this.hover?this.options.color.hover.background:this.options.color.background,t.database(this.x-this.width/2,this.y-.5*this.height,this.width,this.height),t.fill(),t.stroke(),this.boundingBox.top=this.top,this.boundingBox.left=this.left,this.boundingBox.right=this.left+this.width,this.boundingBox.bottom=this.top+this.height,this._label(t,this.label,this.x,this.y)},s.prototype._resizeCircle=function(t){if(!this.width){var e=5,i=this.getTextSize(t),s=Math.max(i.width,i.height)+2*e;this.options.radius=s/2,this.width=s,this.height=s,this.options.radius+=.5*Math.min(this.clusterSize-1,this.maxNodeSizeIncrements)*this.clusterSizeRadiusFactor,this.growthIndicator=this.options.radius-.5*s}},s.prototype._drawRawCircle=function(t,e,i,s){var o=2.5,n=this.options.borderWidth,r=this.options.borderWidthSelected||2*this.options.borderWidth;t.strokeStyle=this.selected?this.options.color.highlight.border:this.hover?this.options.color.hover.border:this.options.color.border,this.clusterSize>1&&(t.lineWidth=(this.selected?r:n)+(this.clusterSize>1?o:0),t.lineWidth*=this.networkScaleInv,t.lineWidth=Math.min(this.width,t.lineWidth),t.circle(e,i,s+2*t.lineWidth),t.stroke()),t.lineWidth=(this.selected?r:n)+(this.clusterSize>1?o:0),t.lineWidth*=this.networkScaleInv,t.lineWidth=Math.min(this.width,t.lineWidth),t.fillStyle=this.selected?this.options.color.highlight.background:this.hover?this.options.color.hover.background:this.options.color.background,t.circle(this.x,this.y,s),t.fill(),t.stroke()},s.prototype._drawCircle=function(t){this._resizeCircle(t),this.left=this.x-this.width/2,this.top=this.y-this.height/2,this._drawRawCircle(t,this.x,this.y,this.options.radius),this.boundingBox.top=this.y-this.options.radius,this.boundingBox.left=this.x-this.options.radius,this.boundingBox.right=this.x+this.options.radius,this.boundingBox.bottom=this.y+this.options.radius,this._label(t,this.label,this.x,this.y)},s.prototype._resizeEllipse=function(t){if(!this.width){var e=this.getTextSize(t);this.width=1.5*e.width,this.height=2*e.height,this.width1&&(t.lineWidth=(this.selected?s:i)+(this.clusterSize>1?e:0),t.lineWidth*=this.networkScaleInv,t.lineWidth=Math.min(this.width,t.lineWidth),t.ellipse(this.left-2*t.lineWidth,this.top-2*t.lineWidth,this.width+4*t.lineWidth,this.height+4*t.lineWidth),t.stroke()),t.lineWidth=(this.selected?s:i)+(this.clusterSize>1?e:0),t.lineWidth*=this.networkScaleInv,t.lineWidth=Math.min(this.width,t.lineWidth),t.fillStyle=this.selected?this.options.color.highlight.background:this.hover?this.options.color.hover.background:this.options.color.background,t.ellipse(this.left,this.top,this.width,this.height),t.fill(),t.stroke(),this.boundingBox.top=this.top,this.boundingBox.left=this.left,this.boundingBox.right=this.left+this.width,this.boundingBox.bottom=this.top+this.height,this._label(t,this.label,this.x,this.y)},s.prototype._drawDot=function(t){this._drawShape(t,"circle")},s.prototype._drawTriangle=function(t){this._drawShape(t,"triangle")},s.prototype._drawTriangleDown=function(t){this._drawShape(t,"triangleDown")},s.prototype._drawSquare=function(t){this._drawShape(t,"square")},s.prototype._drawStar=function(t){this._drawShape(t,"star")},s.prototype._resizeShape=function(){if(!this.width){this.options.radius=this.baseRadiusValue;var t=2*this.options.radius;this.width=t,this.height=t,this.width+=Math.min(this.clusterSize-1,this.maxNodeSizeIncrements)*this.clusterSizeWidthFactor,this.height+=Math.min(this.clusterSize-1,this.maxNodeSizeIncrements)*this.clusterSizeHeightFactor,this.options.radius+=.5*Math.min(this.clusterSize-1,this.maxNodeSizeIncrements)*this.clusterSizeRadiusFactor,this.growthIndicator=this.width-t}},s.prototype._drawShape=function(t,e){this._resizeShape(t),this.left=this.x-this.width/2,this.top=this.y-this.height/2;var i=2.5,s=this.options.borderWidth,o=this.options.borderWidthSelected||2*this.options.borderWidth,n=2;switch(e){case"dot":n=2;break;case"square":n=2;break;case"triangle":n=3;break;case"triangleDown":n=3;break;case"star":n=4}t.strokeStyle=this.selected?this.options.color.highlight.border:this.hover?this.options.color.hover.border:this.options.color.border,this.clusterSize>1&&(t.lineWidth=(this.selected?o:s)+(this.clusterSize>1?i:0),t.lineWidth*=this.networkScaleInv,t.lineWidth=Math.min(this.width,t.lineWidth),t[e](this.x,this.y,this.options.radius+n*t.lineWidth),t.stroke()),t.lineWidth=(this.selected?o:s)+(this.clusterSize>1?i:0),t.lineWidth*=this.networkScaleInv,t.lineWidth=Math.min(this.width,t.lineWidth),t.fillStyle=this.selected?this.options.color.highlight.background:this.hover?this.options.color.hover.background:this.options.color.background,t[e](this.x,this.y,this.options.radius),t.fill(),t.stroke(),this.boundingBox.top=this.y-this.options.radius,this.boundingBox.left=this.x-this.options.radius,this.boundingBox.right=this.x+this.options.radius,this.boundingBox.bottom=this.y+this.options.radius,this.label&&(this._label(t,this.label,this.x,this.y+this.height/2,void 0,"hanging",!0),this.boundingBox.left=Math.min(this.boundingBox.left,this.labelDimensions.left),this.boundingBox.right=Math.max(this.boundingBox.right,this.labelDimensions.left+this.labelDimensions.width),this.boundingBox.bottom=Math.max(this.boundingBox.bottom,this.boundingBox.bottom+this.labelDimensions.height))},s.prototype._resizeText=function(t){if(!this.width){var e=5,i=this.getTextSize(t);this.width=i.width+2*e,this.height=i.height+2*e,this.width+=Math.min(this.clusterSize-1,this.maxNodeSizeIncrements)*this.clusterSizeWidthFactor,this.height+=Math.min(this.clusterSize-1,this.maxNodeSizeIncrements)*this.clusterSizeHeightFactor,this.options.radius+=Math.min(this.clusterSize-1,this.maxNodeSizeIncrements)*this.clusterSizeRadiusFactor,this.growthIndicator=this.width-(i.width+2*e)}},s.prototype._drawText=function(t){this._resizeText(t),this.left=this.x-this.width/2,this.top=this.y-this.height/2,this._label(t,this.label,this.x,this.y),this.boundingBox.top=this.top,this.boundingBox.left=this.left,this.boundingBox.right=this.left+this.width,this.boundingBox.bottom=this.top+this.height},s.prototype._label=function(t,e,i,s,o,n,r){if(e&&Number(this.options.fontSize)*this.networkScale>this.fontDrawThreshold){t.font=(this.selected?"bold ":"")+this.options.fontSize+"px "+this.options.fontFace;var a=e.split("\n"),h=a.length,d=Number(this.options.fontSize),l=s+(1-h)/2*d;1==r&&(l=s+(1-h)/(2*d));for(var c=t.measureText(a[0]).width,p=1;h>p;p++){var u=t.measureText(a[p]).width;c=u>c?u:c}var m=this.options.fontSize*h,f=i-c/2,g=s-m/2;"hanging"==n&&(g+=.5*d,g+=4,l+=4),this.labelDimensions={top:g,left:f,width:c,height:m,yLine:l},void 0!==this.options.fontFill&&null!==this.options.fontFill&&"none"!==this.options.fontFill&&(t.fillStyle=this.options.fontFill,t.fillRect(f,g,c,m)),t.fillStyle=this.options.fontColor||"black",t.textAlign=o||"center",t.textBaseline=n||"middle",this.options.fontStrokeWidth>0&&(t.lineWidth=this.options.fontStrokeWidth,t.strokeStyle=this.options.fontStrokeColor,t.lineJoin="round");for(var p=0;h>p;p++)this.options.fontStrokeWidth&&t.strokeText(a[p],i,l),t.fillText(a[p],i,l),l+=d}},s.prototype.getTextSize=function(t){if(void 0!==this.label){t.font=(this.selected?"bold ":"")+this.options.fontSize+"px "+this.options.fontFace;for(var e=this.label.split("\n"),i=(Number(this.options.fontSize)+4)*e.length,s=0,o=0,n=e.length;n>o;o++)s=Math.max(s,t.measureText(e[o]).width);return{width:s,height:i,lineCount:e.length}}return{width:0,height:0,lineCount:0}},s.prototype.inArea=function(){return void 0!==this.width?this.x+this.width*this.networkScaleInv>=this.canvasTopLeft.x&&this.x-this.width*this.networkScaleInv=this.canvasTopLeft.y&&this.y-this.height*this.networkScaleInv=this.canvasTopLeft.x&&this.x=this.canvasTopLeft.y&&this.ys&&(n=s-e-this.padding),no&&(r=o-i-this.padding),ri;i++)if(e.id===r.nodes[i].id){o=r.nodes[i];break}for(o||(o={id:e.id},t.node&&(o.attr=a(o.attr,t.node))),i=n.length-1;i>=0;i--){var h=n[i];h.nodes||(h.nodes=[]),-1==h.nodes.indexOf(o)&&h.nodes.push(o)}e.attr&&(o.attr=a(o.attr,e.attr))}function l(t,e){if(t.edges||(t.edges=[]),t.edges.push(e),t.edge){var i=a({},t.edge);e.attr=a(i,e.attr)}}function c(t,e,i,s,o){var n={from:e,to:i,type:s};return t.edge&&(n.attr=a({},t.edge)),n.attr=a(n.attr||{},o),n}function p(){for(N=D.NULL,k="";" "==E||" "==E||"\n"==E||"\r"==E;)o();do{var t=!1;if("#"==E){for(var e=O-1;" "==T.charAt(e)||" "==T.charAt(e);)e--;if("\n"==T.charAt(e)||""==T.charAt(e)){for(;""!=E&&"\n"!=E;)o();t=!0}}if("/"==E&&"/"==n()){for(;""!=E&&"\n"!=E;)o();t=!0}if("/"==E&&"*"==n()){for(;""!=E;){if("*"==E&&"/"==n()){o(),o();break}o()}t=!0}for(;" "==E||" "==E||"\n"==E||"\r"==E;)o()}while(t);if(""==E)return void(N=D.DELIMITER);var i=E+n();if(C[i])return N=D.DELIMITER,k=i,o(),void o();if(C[E])return N=D.DELIMITER,k=E,void o();if(r(E)||"-"==E){for(k+=E,o();r(E);)k+=E,o();return"false"==k?k=!1:"true"==k?k=!0:isNaN(Number(k))||(k=Number(k)),void(N=D.IDENTIFIER)}if('"'==E){for(o();""!=E&&('"'!=E||'"'==E&&'"'==n());)k+=E,'"'==E&&o(),o();if('"'!=E)throw x('End of string " expected');return o(),void(N=D.IDENTIFIER)}for(N=D.UNKNOWN;""!=E;)k+=E,o();throw new SyntaxError('Syntax error in part "'+w(k,30)+'"')}function u(){var t={};if(s(),p(),"strict"==k&&(t.strict=!0,p()),("graph"==k||"digraph"==k)&&(t.type=k,p()),N==D.IDENTIFIER&&(t.id=k,p()),"{"!=k)throw x("Angle bracket { expected");if(p(),m(t),"}"!=k)throw x("Angle bracket } expected");if(p(),""!==k)throw x("End of file expected");return p(),delete t.node,delete t.edge,delete t.graph,t}function m(t){for(;""!==k&&"}"!=k;)f(t),";"==k&&p()}function f(t){var e=g(t);if(e)return void b(t,e);var i=v(t);if(!i){if(N!=D.IDENTIFIER)throw x("Identifier expected");var s=k;if(p(),"="==k){if(p(),N!=D.IDENTIFIER)throw x("Identifier expected");t[s]=k,p()}else y(t,s)}}function g(t){var e=null;if("subgraph"==k&&(e={},e.type="subgraph",p(),N==D.IDENTIFIER&&(e.id=k,p())),"{"==k){if(p(),e||(e={}),e.parent=t,e.node=t.node,e.edge=t.edge,e.graph=t.graph,m(e),"}"!=k)throw x("Angle bracket } expected");p(),delete e.node,delete e.edge,delete e.graph,delete e.parent,t.subgraphs||(t.subgraphs=[]),t.subgraphs.push(e)}return e}function v(t){return"node"==k?(p(),t.node=_(),"node"):"edge"==k?(p(),t.edge=_(),"edge"):"graph"==k?(p(),t.graph=_(),"graph"):null}function y(t,e){var i={id:e},s=_();s&&(i.attr=s),d(t,i),b(t,e)}function b(t,e){for(;"->"==k||"--"==k;){var i,s=k;p();var o=g(t);if(o)i=o;else{if(N!=D.IDENTIFIER)throw x("Identifier or subgraph expected");i=k,d(t,{id:i}),p()}var n=_(),r=c(t,e,i,s,n);l(t,r),e=i}}function _(){for(var t=null;"["==k;){for(p(),t={};""!==k&&"]"!=k;){if(N!=D.IDENTIFIER)throw x("Attribute name expected");var e=k;if(p(),"="!=k)throw x("Equal sign = expected");if(p(),N!=D.IDENTIFIER)throw x("Attribute value expected");var i=k;h(t,e,i),p(),","==k&&p()}if("]"!=k)throw x("Bracket ] expected");p()}return t}function x(t){return new SyntaxError(t+', got "'+w(k,30)+'" (char '+O+")")}function w(t,e){return t.length<=e?t:t.substr(0,27)+"..."}function M(t,e,i){Array.isArray(t)?t.forEach(function(t){Array.isArray(e)?e.forEach(function(e){i(t,e)}):i(t,e)}):Array.isArray(e)?e.forEach(function(e){i(t,e)}):i(t,e)}function S(t){var e=i(t),s={nodes:[],edges:[],options:{}};if(e.nodes&&e.nodes.forEach(function(t){var e={id:t.id,label:String(t.label||t.id)};a(e,t.attr),e.image&&(e.shape="image"),s.nodes.push(e)}),e.edges){var o=function(t){var e={from:t.from,to:t.to};return a(e,t.attr),e.style="->"==t.type?"arrow":"line",e};e.edges.forEach(function(t){var e,i;e=t.from instanceof Object?t.from.nodes:{id:t.from},i=t.to instanceof Object?t.to.nodes:{id:t.to},t.from instanceof Object&&t.from.edges&&t.from.edges.forEach(function(t){var e=o(t);s.edges.push(e)}),M(e,i,function(e,i){var n=c(s,e.id,i.id,t.type,t.attr),r=o(n);s.edges.push(r)}),t.to instanceof Object&&t.to.edges&&t.to.edges.forEach(function(t){var e=o(t);s.edges.push(e)})})}return e.attr&&(s.options=e.attr),s}var D={NULL:0,DELIMITER:1,IDENTIFIER:2,UNKNOWN:3},C={"{":!0,"}":!0,"[":!0,"]":!0,";":!0,"=":!0,",":!0,"->":!0,"--":!0},T="",O=0,E="",k="",N=D.NULL,L=/[a-zA-Z_0-9.:#]/;e.parseDOT=i,e.DOTToGraph=S},function(t,e){function i(t,e){var i=[],s=[];this.options={edges:{inheritColor:!0},nodes:{allowedToMove:!1,parseColor:!1}},void 0!==e&&(this.options.nodes.allowedToMove=e.allowedToMove|!1,this.options.nodes.parseColor=e.parseColor|!1,this.options.edges.inheritColor=e.inheritColor|!0);for(var o=t.edges,n=t.nodes,r=0;r=s&&(s=864e5),e=new Date(e.valueOf()-.05*s),i=new Date(i.valueOf()+.05*s)}return{start:e,end:i}},s.prototype.setWindow=function(t,e,i){var s=i&&void 0!==i.animate?i.animate:!0;if(1==arguments.length){var o=arguments[0];this.range.setRange(o.start,o.end,s)}else this.range.setRange(t,e,s)},s.prototype.moveTo=function(t,e){var i=this.range.end-this.range.start,s=r.convert(t,"Date").valueOf(),o=s-i/2,n=s+i/2,a=e&&void 0!==e.animate?e.animate:!0;this.range.setRange(o,n,a)},s.prototype.getWindow=function(){var t=this.range.getRange();return{start:new Date(t.start),end:new Date(t.end)}},s.prototype.redraw=function(){var t=!1,e=this.options,i=this.props,s=this.dom;if(s){h.updateHiddenDates(this.body,this.options.hiddenDates),"top"==e.orientation?(r.addClassName(s.root,"top"),r.removeClassName(s.root,"bottom")):(r.removeClassName(s.root,"top"),r.addClassName(s.root,"bottom")),s.root.style.maxHeight=r.option.asSize(e.maxHeight,""),s.root.style.minHeight=r.option.asSize(e.minHeight,""),s.root.style.width=r.option.asSize(e.width,""),i.border.left=(s.centerContainer.offsetWidth-s.centerContainer.clientWidth)/2,i.border.right=i.border.left,i.border.top=(s.centerContainer.offsetHeight-s.centerContainer.clientHeight)/2,i.border.bottom=i.border.top;var o=s.root.offsetHeight-s.root.clientHeight,n=s.root.offsetWidth-s.root.clientWidth;0===s.centerContainer.clientHeight&&(i.border.left=i.border.top,i.border.right=i.border.left),0===s.root.clientHeight&&(n=o),i.center.height=s.center.offsetHeight,i.left.height=s.left.offsetHeight,i.right.height=s.right.offsetHeight,i.top.height=s.top.clientHeight||-i.border.top,i.bottom.height=s.bottom.clientHeight||-i.border.bottom;var a=Math.max(i.left.height,i.center.height,i.right.height),d=i.top.height+a+i.bottom.height+o+i.border.top+i.border.bottom;s.root.style.height=r.option.asSize(e.height,d+"px"),i.root.height=s.root.offsetHeight,i.background.height=i.root.height-o;var l=i.root.height-i.top.height-i.bottom.height-o;i.centerContainer.height=l,i.leftContainer.height=l,i.rightContainer.height=i.leftContainer.height,i.root.width=s.root.offsetWidth,i.background.width=i.root.width-n,i.left.width=s.leftContainer.clientWidth||-i.border.left,i.leftContainer.width=i.left.width,i.right.width=s.rightContainer.clientWidth||-i.border.right,i.rightContainer.width=i.right.width;var c=i.root.width-i.left.width-i.right.width-n;i.center.width=c,i.centerContainer.width=c,i.top.width=c,i.bottom.width=c,s.background.style.height=i.background.height+"px",s.backgroundVertical.style.height=i.background.height+"px",s.backgroundHorizontal.style.height=i.centerContainer.height+"px",s.centerContainer.style.height=i.centerContainer.height+"px",s.leftContainer.style.height=i.leftContainer.height+"px",s.rightContainer.style.height=i.rightContainer.height+"px",s.background.style.width=i.background.width+"px",s.backgroundVertical.style.width=i.centerContainer.width+"px",s.backgroundHorizontal.style.width=i.background.width+"px",s.centerContainer.style.width=i.center.width+"px",s.top.style.width=i.top.width+"px",s.bottom.style.width=i.bottom.width+"px",s.background.style.left="0",s.background.style.top="0",s.backgroundVertical.style.left=i.left.width+i.border.left+"px",s.backgroundVertical.style.top="0",s.backgroundHorizontal.style.left="0",s.backgroundHorizontal.style.top=i.top.height+"px",s.centerContainer.style.left=i.left.width+"px",s.centerContainer.style.top=i.top.height+"px",s.leftContainer.style.left="0",s.leftContainer.style.top=i.top.height+"px",s.rightContainer.style.left=i.left.width+i.center.width+"px",s.rightContainer.style.top=i.top.height+"px",s.top.style.left=i.left.width+"px",s.top.style.top="0",s.bottom.style.left=i.left.width+"px",s.bottom.style.top=i.top.height+i.centerContainer.height+"px",this._updateScrollTop();var p=this.props.scrollTop;"bottom"==e.orientation&&(p+=Math.max(this.props.centerContainer.height-this.props.center.height-this.props.border.top-this.props.border.bottom,0)),s.center.style.left="0",s.center.style.top=p+"px",s.left.style.left="0",s.left.style.top=p+"px",s.right.style.left="0",s.right.style.top=p+"px";var u=0==this.props.scrollTop?"hidden":"",m=this.props.scrollTop==this.props.scrollTopMin?"hidden":"";if(s.shadowTop.style.visibility=u,s.shadowBottom.style.visibility=m,s.shadowTopLeft.style.visibility=u,s.shadowBottomLeft.style.visibility=m,s.shadowTopRight.style.visibility=u,s.shadowBottomRight.style.visibility=m,this.components.forEach(function(e){t=e.redraw()||t}),t){var f=3;this.redrawCount0&&(this.props.scrollTop=0),this.props.scrollTopt[s].y?t[s].y:e,i=i0){var r,a,h=Number(i.svg.style.height.replace("px",""));if(r=o.getSVGElement("path",i.svgElements,i.svg),r.setAttributeNS(null,"class",e.className),void 0!==e.style&&r.setAttributeNS(null,"style",e.style),a=1==e.options.catmullRom.enabled?s._catmullRom(t,e):s._linear(t),1==e.options.shaded.enabled){var d,l=o.getSVGElement("path",i.svgElements,i.svg);d="top"==e.options.shaded.orientation?"M"+t[0].x+",0 "+a+"L"+t[t.length-1].x+",0":"M"+t[0].x+","+h+" "+a+"L"+t[t.length-1].x+","+h,l.setAttributeNS(null,"class",e.className+" fill"),void 0!==e.options.shaded.style&&l.setAttributeNS(null,"style",e.options.shaded.style),l.setAttributeNS(null,"d",d)}r.setAttributeNS(null,"d","M"+a),1==e.options.drawPoints.enabled&&n.draw(t,e,i)}},s._catmullRomUniform=function(t){for(var e,i,s,o,n,r,a=Math.round(t[0].x)+","+Math.round(t[0].y)+" ",h=1/6,d=t.length,l=0;d-1>l;l++)e=0==l?t[0]:t[l-1],i=t[l],s=t[l+1],o=d>l+2?t[l+2]:s,n={x:(-e.x+6*i.x+s.x)*h,y:(-e.y+6*i.y+s.y)*h},r={x:(i.x+6*s.x-o.x)*h,y:(i.y+6*s.y-o.y)*h},a+="C"+n.x+","+n.y+" "+r.x+","+r.y+" "+s.x+","+s.y+" "; -return a},s._catmullRom=function(t,e){var i=e.options.catmullRom.alpha;if(0==i||void 0===i)return this._catmullRomUniform(t);for(var s,o,n,r,a,h,d,l,c,p,u,m,f,g,v,y,b,_,x,w=Math.round(t[0].x)+","+Math.round(t[0].y)+" ",M=t.length,S=0;M-1>S;S++)s=0==S?t[0]:t[S-1],o=t[S],n=t[S+1],r=M>S+2?t[S+2]:n,d=Math.sqrt(Math.pow(s.x-o.x,2)+Math.pow(s.y-o.y,2)),l=Math.sqrt(Math.pow(o.x-n.x,2)+Math.pow(o.y-n.y,2)),c=Math.sqrt(Math.pow(n.x-r.x,2)+Math.pow(n.y-r.y,2)),g=Math.pow(c,i),y=Math.pow(c,2*i),v=Math.pow(l,i),b=Math.pow(l,2*i),x=Math.pow(d,i),_=Math.pow(d,2*i),p=2*_+3*x*v+b,u=2*y+3*g*v+b,m=3*x*(x+v),m>0&&(m=1/m),f=3*g*(g+v),f>0&&(f=1/f),a={x:(-b*s.x+p*o.x+_*n.x)*m,y:(-b*s.y+p*o.y+_*n.y)*m},h={x:(y*o.x+u*n.x-b*r.x)*f,y:(y*o.y+u*n.y-b*r.y)*f},0==a.x&&0==a.y&&(a=o),0==h.x&&0==h.y&&(h=n),w+="C"+a.x+","+a.y+" "+h.x+","+h.y+" "+n.x+","+n.y+" ";return w},s._linear=function(t){for(var e="",i=0;it[s].y?t[s].y:e,i=i0&&(n=Math.min(n,Math.abs(c[d-1].x-r))),a=s._getSafeDrawData(n,h,m);else{var g=d+(p[r].amount-p[r].resolved),v=d-(p[r].resolved+1);g0&&(n=Math.min(n,Math.abs(c[v].x-r))),a=s._getSafeDrawData(n,h,m),p[r].resolved+=1,"stack"==h.options.barChart.handleOverlap?(f=p[r].accumulated,p[r].accumulated+=h.zeroPosition-c[d].y):"sideBySide"==h.options.barChart.handleOverlap&&(a.width=a.width/p[r].amount,a.offset+=p[r].resolved*a.width-.5*a.width*(p[r].amount+1),"left"==h.options.barChart.align?a.offset-=.5*a.width:"right"==h.options.barChart.align&&(a.offset+=.5*a.width))}o.drawBar(c[d].x+a.offset,c[d].y-f,a.width,h.zeroPosition-c[d].y,h.className+" bar",i.svgElements,i.svg),1==h.options.drawPoints.enabled&&o.drawPoint(c[d].x+a.offset,c[d].y,h,i.svgElements,i.svg)}},s._getDataIntersections=function(t,e){for(var i,s=0;s0&&(i=Math.min(i,Math.abs(e[s-1].x-e[s].x))),0==i&&(void 0===t[e[s].x]&&(t[e[s].x]={amount:0,resolved:0,accumulated:0}),t[e[s].x].amount+=1)},s._getSafeDrawData=function(t,e,i){var s,o;return t0?(s=i>t?i:t,o=0,"left"==e.options.barChart.align?o-=.5*t:"right"==e.options.barChart.align&&(o+=.5*t)):(s=e.options.barChart.width,o=0,"left"==e.options.barChart.align?o-=.5*e.options.barChart.width:"right"==e.options.barChart.align&&(o+=.5*e.options.barChart.width)),{width:s,offset:o}},s.getStackedBarYRange=function(t,e,i,o,n){if(t.length>0){t.sort(function(t,e){return t.x==e.x?t.groupId-e.groupId:t.x-e.x});var r={};s._getDataIntersections(r,t),e[o]=s._getStackedBarYRange(r,t),e[o].yAxisOrientation=n,i.push(o)}},s._getStackedBarYRange=function(t,e){for(var i,s=e[0].y,o=e[0].y,n=0;ne[n].y?e[n].y:s,o=ot[r].accumulated?t[r].accumulated:s,o=ot[s].y?t[s].y:e,i=is;s++){var o=s%2===0?1.3*i:.5*i;this.lineTo(t+o*Math.sin(2*s*Math.PI/10),e-o*Math.cos(2*s*Math.PI/10))}this.closePath()},CanvasRenderingContext2D.prototype.roundRect=function(t,e,i,s,o){var n=Math.PI/180;0>i-2*o&&(o=i/2),0>s-2*o&&(o=s/2),this.beginPath(),this.moveTo(t+o,e),this.lineTo(t+i-o,e),this.arc(t+i-o,e+o,o,270*n,360*n,!1),this.lineTo(t+i,e+s-o),this.arc(t+i-o,e+s-o,o,0,90*n,!1),this.lineTo(t+o,e+s),this.arc(t+o,e+s-o,o,90*n,180*n,!1),this.lineTo(t,e+o),this.arc(t+o,e+o,o,180*n,270*n,!1)},CanvasRenderingContext2D.prototype.ellipse=function(t,e,i,s){var o=.5522848,n=i/2*o,r=s/2*o,a=t+i,h=e+s,d=t+i/2,l=e+s/2;this.beginPath(),this.moveTo(t,l),this.bezierCurveTo(t,l-r,d-n,e,d,e),this.bezierCurveTo(d+n,e,a,l-r,a,l),this.bezierCurveTo(a,l+r,d+n,h,d,h),this.bezierCurveTo(d-n,h,t,l+r,t,l)},CanvasRenderingContext2D.prototype.database=function(t,e,i,s){var o=1/3,n=i,r=s*o,a=.5522848,h=n/2*a,d=r/2*a,l=t+n,c=e+r,p=t+n/2,u=e+r/2,m=e+(s-r/2),f=e+s;this.beginPath(),this.moveTo(l,u),this.bezierCurveTo(l,u+d,p+h,c,p,c),this.bezierCurveTo(p-h,c,t,u+d,t,u),this.bezierCurveTo(t,u-d,p-h,e,p,e),this.bezierCurveTo(p+h,e,l,u-d,l,u),this.lineTo(l,m),this.bezierCurveTo(l,m+d,p+h,f,p,f),this.bezierCurveTo(p-h,f,t,m+d,t,m),this.lineTo(t,u)},CanvasRenderingContext2D.prototype.arrow=function(t,e,i,s){var o=t-s*Math.cos(i),n=e-s*Math.sin(i),r=t-.9*s*Math.cos(i),a=e-.9*s*Math.sin(i),h=o+s/3*Math.cos(i+.5*Math.PI),d=n+s/3*Math.sin(i+.5*Math.PI),l=o+s/3*Math.cos(i-.5*Math.PI),c=n+s/3*Math.sin(i-.5*Math.PI);this.beginPath(),this.moveTo(t,e),this.lineTo(h,d),this.lineTo(r,a),this.lineTo(l,c),this.closePath()},CanvasRenderingContext2D.prototype.dashedLine=function(t,e,i,s,o){o||(o=[10,5]),0==p&&(p=.001);var n=o.length;this.moveTo(t,e);for(var r=i-t,a=s-e,h=a/r,d=Math.sqrt(r*r+a*a),l=0,c=!0;d>=.1;){var p=o[l++%n];p>d&&(p=d);var u=Math.sqrt(p*p/(1+h*h));0>r&&(u=-u),t+=u,e+=h*u,this[c?"lineTo":"moveTo"](t,e),d-=p,c=!c}})},function(t){function e(t){return t?i(t):void 0}function i(t){for(var i in e.prototype)t[i]=e.prototype[i];return t}t.exports=e,e.prototype.on=e.prototype.addEventListener=function(t,e){return this._callbacks=this._callbacks||{},(this._callbacks[t]=this._callbacks[t]||[]).push(e),this},e.prototype.once=function(t,e){function i(){s.off(t,i),e.apply(this,arguments)}var s=this;return this._callbacks=this._callbacks||{},i.fn=e,this.on(t,i),this},e.prototype.off=e.prototype.removeListener=e.prototype.removeAllListeners=e.prototype.removeEventListener=function(t,e){if(this._callbacks=this._callbacks||{},0==arguments.length)return this._callbacks={},this;var i=this._callbacks[t];if(!i)return this;if(1==arguments.length)return delete this._callbacks[t],this;for(var s,o=0;os;++s)i[s].apply(this,e)}return this},e.prototype.listeners=function(t){return this._callbacks=this._callbacks||{},this._callbacks[t]||[]},e.prototype.hasListeners=function(t){return!!this.listeners(t).length}},function(t,e){var i,s,o;!function(n,r){s=[],i=r,o="function"==typeof i?i.apply(e,s):i,!(void 0!==o&&(t.exports=o))}(this,function(){function t(t){var e,i=t&&t.preventDefault||!1,s=t&&t.container||window,o={},n={keydown:{},keyup:{}},r={};for(e=97;122>=e;e++)r[String.fromCharCode(e)]={code:65+(e-97),shift:!1};for(e=65;90>=e;e++)r[String.fromCharCode(e)]={code:e,shift:!0};for(e=0;9>=e;e++)r[""+e]={code:48+e,shift:!1};for(e=1;12>=e;e++)r["F"+e]={code:111+e,shift:!1};for(e=0;9>=e;e++)r["num"+e]={code:96+e,shift:!1};r["num*"]={code:106,shift:!1},r["num+"]={code:107,shift:!1},r["num-"]={code:109,shift:!1},r["num/"]={code:111,shift:!1},r["num."]={code:110,shift:!1},r.left={code:37,shift:!1},r.up={code:38,shift:!1},r.right={code:39,shift:!1},r.down={code:40,shift:!1},r.space={code:32,shift:!1},r.enter={code:13,shift:!1},r.shift={code:16,shift:void 0},r.esc={code:27,shift:!1},r.backspace={code:8,shift:!1},r.tab={code:9,shift:!1},r.ctrl={code:17,shift:!1},r.alt={code:18,shift:!1},r["delete"]={code:46,shift:!1},r.pageup={code:33,shift:!1},r.pagedown={code:34,shift:!1},r["="]={code:187,shift:!1},r["-"]={code:189,shift:!1},r["]"]={code:221,shift:!1},r["["]={code:219,shift:!1};var a=function(t){d(t,"keydown")},h=function(t){d(t,"keyup")},d=function(t,e){if(void 0!==n[e][t.keyCode]){for(var s=n[e][t.keyCode],o=0;oe-n?(i=t.clone().add(o-1,"months"),s=(e-n)/(n-i)):(i=t.clone().add(o+1,"months"),s=(e-n)/(i-n)),-(o+s)}function f(t,e,i){var s;return null==i?e:null!=t.meridiemHour?t.meridiemHour(e,i):null!=t.isPM?(s=t.isPM(i),s&&12>e&&(e+=12),s||12!==e||(e=0),e):e}function g(){}function v(t,e){e!==!1&&F(t),_(this,t),this._d=new Date(+t._d),Di===!1&&(Di=!0,Ce.updateOffset(this),Di=!1)}function y(t){var e=N(t),i=e.year||0,s=e.quarter||0,o=e.month||0,n=e.week||0,r=e.day||0,a=e.hour||0,h=e.minute||0,d=e.second||0,l=e.millisecond||0;this._milliseconds=+l+1e3*d+6e4*h+36e5*a,this._days=+r+7*n,this._months=+o+3*s+12*i,this._data={},this._locale=Ce.localeData(),this._bubble()}function b(t,e){for(var i in e)a(e,i)&&(t[i]=e[i]);return a(e,"toString")&&(t.toString=e.toString),a(e,"valueOf")&&(t.valueOf=e.valueOf),t}function _(t,e){var i,s,o;if("undefined"!=typeof e._isAMomentObject&&(t._isAMomentObject=e._isAMomentObject),"undefined"!=typeof e._i&&(t._i=e._i),"undefined"!=typeof e._f&&(t._f=e._f),"undefined"!=typeof e._l&&(t._l=e._l),"undefined"!=typeof e._strict&&(t._strict=e._strict),"undefined"!=typeof e._tzm&&(t._tzm=e._tzm),"undefined"!=typeof e._isUTC&&(t._isUTC=e._isUTC),"undefined"!=typeof e._offset&&(t._offset=e._offset),"undefined"!=typeof e._pf&&(t._pf=e._pf),"undefined"!=typeof e._locale&&(t._locale=e._locale),Ye.length>0)for(i in Ye)s=Ye[i],o=e[s],"undefined"!=typeof o&&(t[s]=o);return t}function x(t){return 0>t?Math.ceil(t):Math.floor(t)}function w(t,e,i){for(var s=""+Math.abs(t),o=t>=0;s.lengths;s++)(i&&t[s]!==e[s]||!i&&I(t[s])!==I(e[s]))&&r++;return r+n}function k(t){if(t){var e=t.toLowerCase().replace(/(.)s$/,"$1");t=gi[t]||vi[e]||e}return t}function N(t){var e,i,s={};for(i in t)a(t,i)&&(e=k(i),e&&(s[e]=t[i]));return s}function L(t){var e,i;if(0===t.indexOf("week"))e=7,i="day";else{if(0!==t.indexOf("month"))return;e=12,i="month"}Ce[t]=function(s,o){var r,a,h=Ce._locale[t],d=[];if("number"==typeof s&&(o=s,s=n),a=function(t){var e=Ce().utc().set(i,t);return h.call(Ce._locale,e,s||"")},null!=o)return a(o);for(r=0;e>r;r++)d.push(a(r));return d}}function I(t){var e=+t,i=0;return 0!==e&&isFinite(e)&&(i=e>=0?Math.floor(e):Math.ceil(e)),i}function z(t,e){return new Date(Date.UTC(t,e+1,0)).getUTCDate()}function P(t,e,i){return me(Ce([t,11,31+e-i]),e,i).week}function A(t){return R(t)?366:365}function R(t){return t%4===0&&t%100!==0||t%400===0}function F(t){var e;t._a&&-2===t._pf.overflow&&(e=t._a[ze]<0||t._a[ze]>11?ze:t._a[Pe]<1||t._a[Pe]>z(t._a[Ie],t._a[ze])?Pe:t._a[Ae]<0||t._a[Ae]>24||24===t._a[Ae]&&(0!==t._a[Re]||0!==t._a[Fe]||0!==t._a[He])?Ae:t._a[Re]<0||t._a[Re]>59?Re:t._a[Fe]<0||t._a[Fe]>59?Fe:t._a[He]<0||t._a[He]>999?He:-1,t._pf._overflowDayOfYear&&(Ie>e||e>Pe)&&(e=Pe),t._pf.overflow=e)}function H(t){return null==t._isValid&&(t._isValid=!isNaN(t._d.getTime())&&t._pf.overflow<0&&!t._pf.empty&&!t._pf.invalidMonth&&!t._pf.nullInput&&!t._pf.invalidFormat&&!t._pf.userInvalidated,t._strict&&(t._isValid=t._isValid&&0===t._pf.charsLeftOver&&0===t._pf.unusedTokens.length&&t._pf.bigHour===n)),t._isValid}function B(t){return t?t.toLowerCase().replace("_","-"):t}function Y(t){for(var e,i,s,o,n=0;n0;){if(s=W(o.slice(0,e).join("-")))return s;if(i&&i.length>=e&&E(o,i,!0)>=e-1)break;e--}n++}return null}function W(t){var e=null;if(!Be[t]&&We)try{e=Ce.locale(),!function(){var t=new Error('Cannot find module "./locale"');throw t.code="MODULE_NOT_FOUND",t}(),Ce.locale(e)}catch(i){}return Be[t]}function G(t,e){var i,s;return e._isUTC?(i=e.clone(),s=(Ce.isMoment(t)||O(t)?+t:+Ce(t))-+i,i._d.setTime(+i._d+s),Ce.updateOffset(i,!1),i):Ce(t).local()}function j(t){return t.match(/\[[\s\S]/)?t.replace(/^\[|\]$/g,""):t.replace(/\\/g,"")}function V(t){var e,i,s=t.match(Ue);for(e=0,i=s.length;i>e;e++)s[e]=wi[s[e]]?wi[s[e]]:j(s[e]);return function(o){var n="";for(e=0;i>e;e++)n+=s[e]instanceof Function?s[e].call(o,t):s[e];return n}}function U(t,e){return t.isValid()?(e=X(e,t.localeData()),yi[e]||(yi[e]=V(e)),yi[e](t)):t.localeData().invalidDate()}function X(t,e){function i(t){return e.longDateFormat(t)||t}var s=5;for(Xe.lastIndex=0;s>=0&&Xe.test(t);)t=t.replace(Xe,i),Xe.lastIndex=0,s-=1;return t}function q(t,e){var i,s=e._strict;switch(t){case"Q":return oi;case"DDDD":return ri;case"YYYY":case"GGGG":case"gggg":return s?ai:Qe;case"Y":case"G":case"g":return di;case"YYYYYY":case"YYYYY":case"GGGGG":case"ggggg":return s?hi:Ke;case"S":if(s)return oi;case"SS":if(s)return ni;case"SSS":if(s)return ri;case"DDD":return Ze;case"MMM":case"MMMM":case"dd":case"ddd":case"dddd":return Je;case"a":case"A":return e._locale._meridiemParse;case"x":return ii;case"X":return si;case"Z":case"ZZ":return ti;case"T":return ei;case"SSSS":return $e;case"MM":case"DD":case"YY":case"GG":case"gg":case"HH":case"hh":case"mm":case"ss":case"ww":case"WW":return s?ni:qe;case"M":case"D":case"d":case"H":case"h":case"m":case"s":case"w":case"W":case"e":case"E":return qe;case"Do":return s?e._locale._ordinalParse:e._locale._ordinalParseLenient;default:return i=new RegExp(se(ie(t.replace("\\","")),"i"))}}function Z(t){t=t||"";var e=t.match(ti)||[],i=e[e.length-1]||[],s=(i+"").match(mi)||["-",0,0],o=+(60*s[1])+I(s[2]);return"+"===s[0]?o:-o}function Q(t,e,i){var s,o=i._a;switch(t){case"Q":null!=e&&(o[ze]=3*(I(e)-1));break;case"M":case"MM":null!=e&&(o[ze]=I(e)-1);break;case"MMM":case"MMMM":s=i._locale.monthsParse(e,t,i._strict),null!=s?o[ze]=s:i._pf.invalidMonth=e;break;case"D":case"DD":null!=e&&(o[Pe]=I(e));break;case"Do":null!=e&&(o[Pe]=I(parseInt(e.match(/\d{1,2}/)[0],10)));break;case"DDD":case"DDDD":null!=e&&(i._dayOfYear=I(e));break;case"YY":o[Ie]=Ce.parseTwoDigitYear(e);break;case"YYYY":case"YYYYY":case"YYYYYY":o[Ie]=I(e);break;case"a":case"A":i._meridiem=e;break;case"h":case"hh":i._pf.bigHour=!0;case"H":case"HH":o[Ae]=I(e);break;case"m":case"mm":o[Re]=I(e);break;case"s":case"ss":o[Fe]=I(e);break;case"S":case"SS":case"SSS":case"SSSS":o[He]=I(1e3*("0."+e));break;case"x":i._d=new Date(I(e));break;case"X":i._d=new Date(1e3*parseFloat(e));break;case"Z":case"ZZ":i._useUTC=!0,i._tzm=Z(e);break;case"dd":case"ddd":case"dddd":s=i._locale.weekdaysParse(e),null!=s?(i._w=i._w||{},i._w.d=s):i._pf.invalidWeekday=e;break;case"w":case"ww":case"W":case"WW":case"d":case"e":case"E":t=t.substr(0,1);case"gggg":case"GGGG":case"GGGGG":t=t.substr(0,2),e&&(i._w=i._w||{},i._w[t]=I(e));break;case"gg":case"GG":i._w=i._w||{},i._w[t]=Ce.parseTwoDigitYear(e)}}function K(t){var e,i,s,o,n,a,h;e=t._w,null!=e.GG||null!=e.W||null!=e.E?(n=1,a=4,i=r(e.GG,t._a[Ie],me(Ce(),1,4).year),s=r(e.W,1),o=r(e.E,1)):(n=t._locale._week.dow,a=t._locale._week.doy,i=r(e.gg,t._a[Ie],me(Ce(),n,a).year),s=r(e.w,1),null!=e.d?(o=e.d,n>o&&++s):o=null!=e.e?e.e+n:n),h=fe(i,s,o,a,n),t._a[Ie]=h.year,t._dayOfYear=h.dayOfYear}function $(t){var e,i,s,o,n=[];if(!t._d){for(s=te(t),t._w&&null==t._a[Pe]&&null==t._a[ze]&&K(t),t._dayOfYear&&(o=r(t._a[Ie],s[Ie]),t._dayOfYear>A(o)&&(t._pf._overflowDayOfYear=!0),i=le(o,0,t._dayOfYear),t._a[ze]=i.getUTCMonth(),t._a[Pe]=i.getUTCDate()),e=0;3>e&&null==t._a[e];++e)t._a[e]=n[e]=s[e];for(;7>e;e++)t._a[e]=n[e]=null==t._a[e]?2===e?1:0:t._a[e];24===t._a[Ae]&&0===t._a[Re]&&0===t._a[Fe]&&0===t._a[He]&&(t._nextDay=!0,t._a[Ae]=0),t._d=(t._useUTC?le:de).apply(null,n),null!=t._tzm&&t._d.setUTCMinutes(t._d.getUTCMinutes()-t._tzm),t._nextDay&&(t._a[Ae]=24)}}function J(t){var e;t._d||(e=N(t._i),t._a=[e.year,e.month,e.day||e.date,e.hour,e.minute,e.second,e.millisecond],$(t))}function te(t){var e=new Date;return t._useUTC?[e.getUTCFullYear(),e.getUTCMonth(),e.getUTCDate()]:[e.getFullYear(),e.getMonth(),e.getDate()]}function ee(t){if(t._f===Ce.ISO_8601)return void ne(t);t._a=[],t._pf.empty=!0;var e,i,s,o,r,a=""+t._i,h=a.length,d=0;for(s=X(t._f,t._locale).match(Ue)||[],e=0;e0&&t._pf.unusedInput.push(r),a=a.slice(a.indexOf(i)+i.length),d+=i.length),wi[o]?(i?t._pf.empty=!1:t._pf.unusedTokens.push(o),Q(o,i,t)):t._strict&&!i&&t._pf.unusedTokens.push(o);t._pf.charsLeftOver=h-d,a.length>0&&t._pf.unusedInput.push(a),t._pf.bigHour===!0&&t._a[Ae]<=12&&(t._pf.bigHour=n),t._a[Ae]=f(t._locale,t._a[Ae],t._meridiem),$(t),F(t)}function ie(t){return t.replace(/\\(\[)|\\(\])|\[([^\]\[]*)\]|\\(.)/g,function(t,e,i,s,o){return e||i||s||o})}function se(t){return t.replace(/[-\/\\^$*+?.()|[\]{}]/g,"\\$&")}function oe(t){var e,i,s,o,n;if(0===t._f.length)return t._pf.invalidFormat=!0,void(t._d=new Date(0/0));for(o=0;on)&&(s=n,i=e));b(t,i||e)}function ne(t){var e,i,s=t._i,o=li.exec(s);if(o){for(t._pf.iso=!0,e=0,i=pi.length;i>e;e++)if(pi[e][1].exec(s)){t._f=pi[e][0]+(o[6]||" ");break}for(e=0,i=ui.length;i>e;e++)if(ui[e][1].exec(s)){t._f+=ui[e][0];break}s.match(ti)&&(t._f+="Z"),ee(t)}else t._isValid=!1}function re(t){ne(t),t._isValid===!1&&(delete t._isValid,Ce.createFromInputFallback(t))}function ae(t,e){var i,s=[];for(i=0;it&&a.setFullYear(t),a}function le(t){var e=new Date(Date.UTC.apply(null,arguments));return 1970>t&&e.setUTCFullYear(t),e}function ce(t,e){if("string"==typeof t)if(isNaN(t)){if(t=e.weekdaysParse(t),"number"!=typeof t)return null}else t=parseInt(t,10);return t}function pe(t,e,i,s,o){return o.relativeTime(e||1,!!i,t,s)}function ue(t,e,i){var s=Ce.duration(t).abs(),o=Ne(s.as("s")),n=Ne(s.as("m")),r=Ne(s.as("h")),a=Ne(s.as("d")),h=Ne(s.as("M")),d=Ne(s.as("y")),l=o0,l[4]=i,pe.apply({},l)}function me(t,e,i){var s,o=i-e,n=i-t.day();return n>o&&(n-=7),o-7>n&&(n+=7),s=Ce(t).add(n,"d"),{week:Math.ceil(s.dayOfYear()/7),year:s.year()}}function fe(t,e,i,s,o){var n,r,a=le(t,0,1).getUTCDay();return a=0===a?7:a,i=null!=i?i:o,n=o-a+(a>s?7:0)-(o>a?7:0),r=7*(e-1)+(i-o)+n+1,{year:r>0?t:t-1,dayOfYear:r>0?r:A(t-1)+r}}function ge(t){var e,i=t._i,s=t._f;return t._locale=t._locale||Ce.localeData(t._l),null===i||s===n&&""===i?Ce.invalid({nullInput:!0}):("string"==typeof i&&(t._i=i=t._locale.preparse(i)),Ce.isMoment(i)?new v(i,!0):(s?T(s)?oe(t):ee(t):he(t),e=new v(t),e._nextDay&&(e.add(1,"d"),e._nextDay=n),e))}function ve(t,e){var i,s;if(1===e.length&&T(e[0])&&(e=e[0]),!e.length)return Ce();for(i=e[0],s=1;s=0?"+":"-";return e+w(Math.abs(t),6)},gg:function(){return w(this.weekYear()%100,2)},gggg:function(){return w(this.weekYear(),4)},ggggg:function(){return w(this.weekYear(),5)},GG:function(){return w(this.isoWeekYear()%100,2)},GGGG:function(){return w(this.isoWeekYear(),4)},GGGGG:function(){return w(this.isoWeekYear(),5)},e:function(){return this.weekday()},E:function(){return this.isoWeekday()},a:function(){return this.localeData().meridiem(this.hours(),this.minutes(),!0)},A:function(){return this.localeData().meridiem(this.hours(),this.minutes(),!1)},H:function(){return this.hours()},h:function(){return this.hours()%12||12},m:function(){return this.minutes()},s:function(){return this.seconds()},S:function(){return I(this.milliseconds()/100)},SS:function(){return w(I(this.milliseconds()/10),2)},SSS:function(){return w(this.milliseconds(),3)},SSSS:function(){return w(this.milliseconds(),3)},Z:function(){var t=this.utcOffset(),e="+";return 0>t&&(t=-t,e="-"),e+w(I(t/60),2)+":"+w(I(t)%60,2)},ZZ:function(){var t=this.utcOffset(),e="+";return 0>t&&(t=-t,e="-"),e+w(I(t/60),2)+w(I(t)%60,2)},z:function(){return this.zoneAbbr()},zz:function(){return this.zoneName()},x:function(){return this.valueOf()},X:function(){return this.unix()},Q:function(){return this.quarter()}},Mi={},Si=["months","monthsShort","weekdays","weekdaysShort","weekdaysMin"],Di=!1;_i.length;)Oe=_i.pop(),wi[Oe+"o"]=u(wi[Oe],Oe);for(;xi.length;)Oe=xi.pop(),wi[Oe+Oe]=p(wi[Oe],2);wi.DDDD=p(wi.DDD,3),b(g.prototype,{set:function(t){var e,i;for(i in t)e=t[i],"function"==typeof e?this[i]=e:this["_"+i]=e;this._ordinalParseLenient=new RegExp(this._ordinalParse.source+"|"+/\d{1,2}/.source)},_months:"January_February_March_April_May_June_July_August_September_October_November_December".split("_"),months:function(t){return this._months[t.month()]},_monthsShort:"Jan_Feb_Mar_Apr_May_Jun_Jul_Aug_Sep_Oct_Nov_Dec".split("_"),monthsShort:function(t){return this._monthsShort[t.month()]},monthsParse:function(t,e,i){var s,o,n;for(this._monthsParse||(this._monthsParse=[],this._longMonthsParse=[],this._shortMonthsParse=[]),s=0;12>s;s++){if(o=Ce.utc([2e3,s]),i&&!this._longMonthsParse[s]&&(this._longMonthsParse[s]=new RegExp("^"+this.months(o,"").replace(".","")+"$","i"),this._shortMonthsParse[s]=new RegExp("^"+this.monthsShort(o,"").replace(".","")+"$","i")),i||this._monthsParse[s]||(n="^"+this.months(o,"")+"|^"+this.monthsShort(o,""),this._monthsParse[s]=new RegExp(n.replace(".",""),"i")),i&&"MMMM"===e&&this._longMonthsParse[s].test(t))return s;if(i&&"MMM"===e&&this._shortMonthsParse[s].test(t))return s;if(!i&&this._monthsParse[s].test(t))return s}},_weekdays:"Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday".split("_"),weekdays:function(t){return this._weekdays[t.day()]},_weekdaysShort:"Sun_Mon_Tue_Wed_Thu_Fri_Sat".split("_"),weekdaysShort:function(t){return this._weekdaysShort[t.day()]},_weekdaysMin:"Su_Mo_Tu_We_Th_Fr_Sa".split("_"),weekdaysMin:function(t){return this._weekdaysMin[t.day()]},weekdaysParse:function(t){var e,i,s;for(this._weekdaysParse||(this._weekdaysParse=[]),e=0;7>e;e++)if(this._weekdaysParse[e]||(i=Ce([2e3,1]).day(e),s="^"+this.weekdays(i,"")+"|^"+this.weekdaysShort(i,"")+"|^"+this.weekdaysMin(i,""),this._weekdaysParse[e]=new RegExp(s.replace(".",""),"i")),this._weekdaysParse[e].test(t))return e},_longDateFormat:{LTS:"h:mm:ss A",LT:"h:mm A",L:"MM/DD/YYYY",LL:"MMMM D, YYYY",LLL:"MMMM D, YYYY LT",LLLL:"dddd, MMMM D, YYYY LT"},longDateFormat:function(t){var e=this._longDateFormat[t];return!e&&this._longDateFormat[t.toUpperCase()]&&(e=this._longDateFormat[t.toUpperCase()].replace(/MMMM|MM|DD|dddd/g,function(t){return t.slice(1)}),this._longDateFormat[t]=e),e},isPM:function(t){return"p"===(t+"").toLowerCase().charAt(0)},_meridiemParse:/[ap]\.?m?\.?/i,meridiem:function(t,e,i){return t>11?i?"pm":"PM":i?"am":"AM"},_calendar:{sameDay:"[Today at] LT",nextDay:"[Tomorrow at] LT",nextWeek:"dddd [at] LT",lastDay:"[Yesterday at] LT",lastWeek:"[Last] dddd [at] LT",sameElse:"L"},calendar:function(t,e,i){var s=this._calendar[t];return"function"==typeof s?s.apply(e,[i]):s},_relativeTime:{future:"in %s",past:"%s ago",s:"a few seconds",m:"a minute",mm:"%d minutes",h:"an hour",hh:"%d hours",d:"a day",dd:"%d days",M:"a month",MM:"%d months",y:"a year",yy:"%d years"},relativeTime:function(t,e,i,s){var o=this._relativeTime[i];return"function"==typeof o?o(t,e,i,s):o.replace(/%d/i,t)},pastFuture:function(t,e){var i=this._relativeTime[t>0?"future":"past"];return"function"==typeof i?i(e):i.replace(/%s/i,e)},ordinal:function(t){return this._ordinal.replace("%d",t)},_ordinal:"%d",_ordinalParse:/\d{1,2}/,preparse:function(t){return t},postformat:function(t){return t},week:function(t){return me(t,this._week.dow,this._week.doy).week},_week:{dow:0,doy:6},firstDayOfWeek:function(){return this._week.dow},firstDayOfYear:function(){return this._week.doy},_invalidDate:"Invalid date",invalidDate:function(){return this._invalidDate}}),Ce=function(t,e,i,s){var o;return"boolean"==typeof i&&(s=i,i=n),o={},o._isAMomentObject=!0,o._i=t,o._f=e,o._l=i,o._strict=s,o._isUTC=!1,o._pf=h(),ge(o)},Ce.suppressDeprecationWarnings=!1,Ce.createFromInputFallback=l("moment construction falls back to js Date. This is discouraged and will be removed in upcoming major release. Please refer to https://github.com/moment/moment/issues/1407 for more info.",function(t){t._d=new Date(t._i+(t._useUTC?" UTC":""))}),Ce.min=function(){var t=[].slice.call(arguments,0);return ve("isBefore",t)},Ce.max=function(){var t=[].slice.call(arguments,0);return ve("isAfter",t)},Ce.utc=function(t,e,i,s){var o;return"boolean"==typeof i&&(s=i,i=n),o={},o._isAMomentObject=!0,o._useUTC=!0,o._isUTC=!0,o._l=i,o._i=t,o._f=e,o._strict=s,o._pf=h(),ge(o).utc()},Ce.unix=function(t){return Ce(1e3*t)},Ce.duration=function(t,e){var i,s,o,n,r=t,h=null;return Ce.isDuration(t)?r={ms:t._milliseconds,d:t._days,M:t._months}:"number"==typeof t?(r={},e?r[e]=t:r.milliseconds=t):(h=je.exec(t))?(i="-"===h[1]?-1:1,r={y:0,d:I(h[Pe])*i,h:I(h[Ae])*i,m:I(h[Re])*i,s:I(h[Fe])*i,ms:I(h[He])*i}):(h=Ve.exec(t))?(i="-"===h[1]?-1:1,o=function(t){var e=t&&parseFloat(t.replace(",","."));return(isNaN(e)?0:e)*i},r={y:o(h[2]),M:o(h[3]),d:o(h[4]),h:o(h[5]),m:o(h[6]),s:o(h[7]),w:o(h[8])}):null==r?r={}:"object"==typeof r&&("from"in r||"to"in r)&&(n=S(Ce(r.from),Ce(r.to)),r={},r.ms=n.milliseconds,r.M=n.months),s=new y(r),Ce.isDuration(t)&&a(t,"_locale")&&(s._locale=t._locale),s},Ce.version=Ee,Ce.defaultFormat=ci,Ce.ISO_8601=function(){},Ce.momentProperties=Ye,Ce.updateOffset=function(){},Ce.relativeTimeThreshold=function(t,e){return bi[t]===n?!1:e===n?bi[t]:(bi[t]=e,!0)},Ce.lang=l("moment.lang is deprecated. Use moment.locale instead.",function(t,e){return Ce.locale(t,e)}),Ce.locale=function(t,e){var i;return t&&(i="undefined"!=typeof e?Ce.defineLocale(t,e):Ce.localeData(t),i&&(Ce.duration._locale=Ce._locale=i)),Ce._locale._abbr},Ce.defineLocale=function(t,e){return null!==e?(e.abbr=t,Be[t]||(Be[t]=new g),Be[t].set(e),Ce.locale(t),Be[t]):(delete Be[t],null)},Ce.langData=l("moment.langData is deprecated. Use moment.localeData instead.",function(t){return Ce.localeData(t)}),Ce.localeData=function(t){var e;if(t&&t._locale&&t._locale._abbr&&(t=t._locale._abbr),!t)return Ce._locale;if(!T(t)){if(e=W(t))return e;t=[t]}return Y(t)},Ce.isMoment=function(t){return t instanceof v||null!=t&&a(t,"_isAMomentObject")},Ce.isDuration=function(t){return t instanceof y};for(Oe=Si.length-1;Oe>=0;--Oe)L(Si[Oe]);Ce.normalizeUnits=function(t){return k(t)},Ce.invalid=function(t){var e=Ce.utc(0/0);return null!=t?b(e._pf,t):e._pf.userInvalidated=!0,e},Ce.parseZone=function(){return Ce.apply(null,arguments).parseZone()},Ce.parseTwoDigitYear=function(t){return I(t)+(I(t)>68?1900:2e3)},Ce.isDate=O,b(Ce.fn=v.prototype,{clone:function(){return Ce(this)},valueOf:function(){return+this._d-6e4*(this._offset||0)},unix:function(){return Math.floor(+this/1e3)},toString:function(){return this.clone().locale("en").format("ddd MMM DD YYYY HH:mm:ss [GMT]ZZ")},toDate:function(){return this._offset?new Date(+this):this._d},toISOString:function(){var t=Ce(this).utc();return 00:!1},parsingFlags:function(){return b({},this._pf)},invalidAt:function(){return this._pf.overflow},utc:function(t){return this.utcOffset(0,t)},local:function(t){return this._isUTC&&(this.utcOffset(0,t),this._isUTC=!1,t&&this.subtract(this._dateUtcOffset(),"m")),this},format:function(t){var e=U(this,t||Ce.defaultFormat);return this.localeData().postformat(e)},add:D(1,"add"),subtract:D(-1,"subtract"),diff:function(t,e,i){var s,o,n=G(t,this),r=6e4*(n.utcOffset()-this.utcOffset());return e=k(e),"year"===e||"month"===e||"quarter"===e?(o=m(this,n),"quarter"===e?o/=3:"year"===e&&(o/=12)):(s=this-n,o="second"===e?s/1e3:"minute"===e?s/6e4:"hour"===e?s/36e5:"day"===e?(s-r)/864e5:"week"===e?(s-r)/6048e5:s),i?o:x(o)},from:function(t,e){return Ce.duration({to:this,from:t}).locale(this.locale()).humanize(!e)},fromNow:function(t){return this.from(Ce(),t)},calendar:function(t){var e=t||Ce(),i=G(e,this).startOf("day"),s=this.diff(i,"days",!0),o=-6>s?"sameElse":-1>s?"lastWeek":0>s?"lastDay":1>s?"sameDay":2>s?"nextDay":7>s?"nextWeek":"sameElse";return this.format(this.localeData().calendar(o,this,Ce(e)))},isLeapYear:function(){return R(this.year())},isDST:function(){return this.utcOffset()>this.clone().month(0).utcOffset()||this.utcOffset()>this.clone().month(5).utcOffset()},day:function(t){var e=this._isUTC?this._d.getUTCDay():this._d.getDay();return null!=t?(t=ce(t,this.localeData()),this.add(t-e,"d")):e},month:xe("Month",!0),startOf:function(t){switch(t=k(t)){case"year":this.month(0);case"quarter":case"month":this.date(1);case"week":case"isoWeek":case"day":this.hours(0);case"hour":this.minutes(0);case"minute":this.seconds(0);case"second":this.milliseconds(0)}return"week"===t?this.weekday(0):"isoWeek"===t&&this.isoWeekday(1),"quarter"===t&&this.month(3*Math.floor(this.month()/3)),this},endOf:function(t){return t=k(t),t===n||"millisecond"===t?this:this.startOf(t).add(1,"isoWeek"===t?"week":t).subtract(1,"ms")},isAfter:function(t,e){var i;return e=k("undefined"!=typeof e?e:"millisecond"),"millisecond"===e?(t=Ce.isMoment(t)?t:Ce(t),+this>+t):(i=Ce.isMoment(t)?+t:+Ce(t),i<+this.clone().startOf(e))},isBefore:function(t,e){var i;return e=k("undefined"!=typeof e?e:"millisecond"),"millisecond"===e?(t=Ce.isMoment(t)?t:Ce(t),+t>+this):(i=Ce.isMoment(t)?+t:+Ce(t),+this.clone().endOf(e)t?this:t}),max:l("moment().max is deprecated, use moment.max instead. https://github.com/moment/moment/issues/1548",function(t){return t=Ce.apply(null,arguments),t>this?this:t}),zone:l("moment().zone is deprecated, use moment().utcOffset instead. https://github.com/moment/moment/issues/1779",function(t,e){return null!=t?("string"!=typeof t&&(t=-t),this.utcOffset(t,e),this):-this.utcOffset()}),utcOffset:function(t,e){var i,s=this._offset||0;return null!=t?("string"==typeof t&&(t=Z(t)),Math.abs(t)<16&&(t=60*t),!this._isUTC&&e&&(i=this._dateUtcOffset()),this._offset=t,this._isUTC=!0,null!=i&&this.add(i,"m"),s!==t&&(!e||this._changeInProgress?C(this,Ce.duration(t-s,"m"),1,!1):this._changeInProgress||(this._changeInProgress=!0,Ce.updateOffset(this,!0),this._changeInProgress=null)),this):this._isUTC?s:this._dateUtcOffset()},isLocal:function(){return!this._isUTC},isUtcOffset:function(){return this._isUTC},isUtc:function(){return this._isUTC&&0===this._offset},zoneAbbr:function(){return this._isUTC?"UTC":""},zoneName:function(){return this._isUTC?"Coordinated Universal Time":""},parseZone:function(){return this._tzm?this.utcOffset(this._tzm):"string"==typeof this._i&&this.utcOffset(Z(this._i)),this},hasAlignedHourOffset:function(t){return t=t?Ce(t).utcOffset():0,(this.utcOffset()-t)%60===0},daysInMonth:function(){return z(this.year(),this.month())},dayOfYear:function(t){var e=Ne((Ce(this).startOf("day")-Ce(this).startOf("year"))/864e5)+1;return null==t?e:this.add(t-e,"d")},quarter:function(t){return null==t?Math.ceil((this.month()+1)/3):this.month(3*(t-1)+this.month()%3)},weekYear:function(t){var e=me(this,this.localeData()._week.dow,this.localeData()._week.doy).year;return null==t?e:this.add(t-e,"y")},isoWeekYear:function(t){var e=me(this,1,4).year;return null==t?e:this.add(t-e,"y")},week:function(t){var e=this.localeData().week(this);return null==t?e:this.add(7*(t-e),"d")},isoWeek:function(t){var e=me(this,1,4).week;return null==t?e:this.add(7*(t-e),"d")},weekday:function(t){var e=(this.day()+7-this.localeData()._week.dow)%7;return null==t?e:this.add(t-e,"d")},isoWeekday:function(t){return null==t?this.day()||7:this.day(this.day()%7?t:t-7)},isoWeeksInYear:function(){return P(this.year(),1,4)},weeksInYear:function(){var t=this.localeData()._week;return P(this.year(),t.dow,t.doy)},get:function(t){return t=k(t),this[t]()},set:function(t,e){var i;if("object"==typeof t)for(i in t)this.set(i,t[i]);else t=k(t),"function"==typeof this[t]&&this[t](e);return this},locale:function(t){var e;return t===n?this._locale._abbr:(e=Ce.localeData(t),null!=e&&(this._locale=e),this)},lang:l("moment().lang() is deprecated. Instead, use moment().localeData() to get the language configuration. Use moment().locale() to change languages.",function(t){return t===n?this.localeData():this.locale(t)}),localeData:function(){return this._locale},_dateUtcOffset:function(){return 15*-Math.round(this._d.getTimezoneOffset()/15)}}),Ce.fn.millisecond=Ce.fn.milliseconds=xe("Milliseconds",!1),Ce.fn.second=Ce.fn.seconds=xe("Seconds",!1),Ce.fn.minute=Ce.fn.minutes=xe("Minutes",!1),Ce.fn.hour=Ce.fn.hours=xe("Hours",!0),Ce.fn.date=xe("Date",!0),Ce.fn.dates=l("dates accessor is deprecated. Use date instead.",xe("Date",!0)),Ce.fn.year=xe("FullYear",!0),Ce.fn.years=l("years accessor is deprecated. Use year instead.",xe("FullYear",!0)),Ce.fn.days=Ce.fn.day,Ce.fn.months=Ce.fn.month,Ce.fn.weeks=Ce.fn.week,Ce.fn.isoWeeks=Ce.fn.isoWeek,Ce.fn.quarters=Ce.fn.quarter,Ce.fn.toJSON=Ce.fn.toISOString,Ce.fn.isUTC=Ce.fn.isUtc,b(Ce.duration.fn=y.prototype,{_bubble:function(){var t,e,i,s=this._milliseconds,o=this._days,n=this._months,r=this._data,a=0;r.milliseconds=s%1e3,t=x(s/1e3),r.seconds=t%60,e=x(t/60),r.minutes=e%60,i=x(e/60),r.hours=i%24,o+=x(i/24),a=x(we(o)),o-=x(Me(a)),n+=x(o/30),o%=30,a+=x(n/12),n%=12,r.days=o,r.months=n,r.years=a},abs:function(){return this._milliseconds=Math.abs(this._milliseconds),this._days=Math.abs(this._days),this._months=Math.abs(this._months),this._data.milliseconds=Math.abs(this._data.milliseconds),this._data.seconds=Math.abs(this._data.seconds),this._data.minutes=Math.abs(this._data.minutes),this._data.hours=Math.abs(this._data.hours),this._data.months=Math.abs(this._data.months),this._data.years=Math.abs(this._data.years),this},weeks:function(){return x(this.days()/7)},valueOf:function(){return this._milliseconds+864e5*this._days+this._months%12*2592e6+31536e6*I(this._months/12)},humanize:function(t){var e=ue(this,!t,this.localeData());return t&&(e=this.localeData().pastFuture(+this,e)),this.localeData().postformat(e)},add:function(t,e){var i=Ce.duration(t,e);return this._milliseconds+=i._milliseconds,this._days+=i._days,this._months+=i._months,this._bubble(),this},subtract:function(t,e){var i=Ce.duration(t,e);return this._milliseconds-=i._milliseconds,this._days-=i._days,this._months-=i._months,this._bubble(),this},get:function(t){return t=k(t),this[t.toLowerCase()+"s"]()},as:function(t){var e,i;if(t=k(t),"month"===t||"year"===t)return e=this._days+this._milliseconds/864e5,i=this._months+12*we(e),"month"===t?i:i/12;switch(e=this._days+Math.round(Me(this._months/12)),t){case"week":return e/7+this._milliseconds/6048e5;case"day":return e+this._milliseconds/864e5;case"hour":return 24*e+this._milliseconds/36e5;case"minute":return 24*e*60+this._milliseconds/6e4;case"second":return 24*e*60*60+this._milliseconds/1e3;case"millisecond":return Math.floor(24*e*60*60*1e3)+this._milliseconds;default:throw new Error("Unknown unit "+t)}},lang:Ce.fn.lang,locale:Ce.fn.locale,toIsoString:l("toIsoString() is deprecated. Please use toISOString() instead (notice the capitals)",function(){return this.toISOString()}),toISOString:function(){var t=Math.abs(this.years()),e=Math.abs(this.months()),i=Math.abs(this.days()),s=Math.abs(this.hours()),o=Math.abs(this.minutes()),n=Math.abs(this.seconds()+this.milliseconds()/1e3);return this.asSeconds()?(this.asSeconds()<0?"-":"")+"P"+(t?t+"Y":"")+(e?e+"M":"")+(i?i+"D":"")+(s||o||n?"T":"")+(s?s+"H":"")+(o?o+"M":"")+(n?n+"S":""):"P0D"},localeData:function(){return this._locale},toJSON:function(){return this.toISOString()}}),Ce.duration.fn.toString=Ce.duration.fn.toISOString;for(Oe in fi)a(fi,Oe)&&Se(Oe.toLowerCase());Ce.duration.fn.asMilliseconds=function(){return this.as("ms")},Ce.duration.fn.asSeconds=function(){return this.as("s")},Ce.duration.fn.asMinutes=function(){return this.as("m")},Ce.duration.fn.asHours=function(){return this.as("h")},Ce.duration.fn.asDays=function(){return this.as("d")},Ce.duration.fn.asWeeks=function(){return this.as("weeks")},Ce.duration.fn.asMonths=function(){return this.as("M")},Ce.duration.fn.asYears=function(){return this.as("y")},Ce.locale("en",{ordinalParse:/\d{1,2}(th|st|nd|rd)/,ordinal:function(t){var e=t%10,i=1===I(t%100/10)?"th":1===e?"st":2===e?"nd":3===e?"rd":"th";return t+i}}),We?o.exports=Ce:(s=function(t,e,i){return i.config&&i.config()&&i.config().noGlobal===!0&&(ke.moment=Te),Ce}.call(e,i,e,o),!(s!==n&&(o.exports=s)),De(!0))}).call(this)}).call(e,function(){return this}(),i(71)(t))},function(t,e,i){function s(){this.constants.smoothCurves.enabled=!this.constants.smoothCurves.enabled;var t=document.getElementById("graph_toggleSmooth");t.style.background=1==this.constants.smoothCurves.enabled?"#A4FF56":"#FF8532",this._configureSmoothCurves(!1)}function o(){for(var t in this.calculationNodes)this.calculationNodes.hasOwnProperty(t)&&(this.calculationNodes[t].vx=0,this.calculationNodes[t].vy=0,this.calculationNodes[t].fx=0,this.calculationNodes[t].fy=0);1==this.constants.hierarchicalLayout.enabled?(this._setupHierarchicalLayout(),a.call(this,"graph_H_nd",1,"physics_hierarchicalRepulsion_nodeDistance"),a.call(this,"graph_H_cg",1,"physics_centralGravity"),a.call(this,"graph_H_sc",1,"physics_springConstant"),a.call(this,"graph_H_sl",1,"physics_springLength"),a.call(this,"graph_H_damp",1,"physics_damping")):this.repositionNodes(),this.moving=!0,this.start()}function n(){var t="No options are required, default values used.",e=[],i=document.getElementById("graph_physicsMethod1"),s=document.getElementById("graph_physicsMethod2");if(1==i.checked){if(this.constants.physics.barnesHut.gravitationalConstant!=this.backupConstants.physics.barnesHut.gravitationalConstant&&e.push("gravitationalConstant: "+this.constants.physics.barnesHut.gravitationalConstant),this.constants.physics.centralGravity!=this.backupConstants.physics.barnesHut.centralGravity&&e.push("centralGravity: "+this.constants.physics.centralGravity),this.constants.physics.springLength!=this.backupConstants.physics.barnesHut.springLength&&e.push("springLength: "+this.constants.physics.springLength),this.constants.physics.springConstant!=this.backupConstants.physics.barnesHut.springConstant&&e.push("springConstant: "+this.constants.physics.springConstant),this.constants.physics.damping!=this.backupConstants.physics.barnesHut.damping&&e.push("damping: "+this.constants.physics.damping),0!=e.length){t="var options = {",t+="physics: {barnesHut: {";for(var o=0;othis.constants.clustering.clusterThreshold&&1==this.constants.clustering.enabled&&this.clusterToFit(this.constants.clustering.reduceToNodes,!1),this._calculateForces())},e._calculateForces=function(){this._calculateGravitationalForces(),this._calculateNodeForces(),this.constants.physics.springConstant>0&&(1==this.constants.smoothCurves.enabled&&1==this.constants.smoothCurves.dynamic?this._calculateSpringForcesWithSupport():1==this.constants.physics.hierarchicalRepulsion.enabled?this._calculateHierarchicalSpringForces():this._calculateSpringForces())},e._updateCalculationNodes=function(){if(1==this.constants.smoothCurves.enabled&&1==this.constants.smoothCurves.dynamic){this.calculationNodes={},this.calculationNodeIndices=[];for(var t in this.nodes)this.nodes.hasOwnProperty(t)&&(this.calculationNodes[t]=this.nodes[t]);var e=this.sectors.support.nodes;for(var i in e)e.hasOwnProperty(i)&&(this.edges.hasOwnProperty(e[i].parentEdgeId)?this.calculationNodes[i]=e[i]:e[i]._setForce(0,0));for(var s in this.calculationNodes)this.calculationNodes.hasOwnProperty(s)&&this.calculationNodeIndices.push(s)}else this.calculationNodes=this.nodes,this.calculationNodeIndices=this.nodeIndices},e._calculateGravitationalForces=function(){var t,e,i,s,o,n=this.calculationNodes,r=this.constants.physics.centralGravity,a=0;for(o=0;oSimulation Mode:Barnes HutRepulsionHierarchical
Options:
',this.containerElement.parentElement.insertBefore(this.physicsConfiguration,this.containerElement),this.optionsDiv=document.createElement("div"),this.optionsDiv.style.fontSize="14px",this.optionsDiv.style.fontFamily="verdana",this.containerElement.parentElement.insertBefore(this.optionsDiv,this.containerElement); -var e;e=document.getElementById("graph_BH_gc"),e.onchange=a.bind(this,"graph_BH_gc",-1,"physics_barnesHut_gravitationalConstant"),e=document.getElementById("graph_BH_cg"),e.onchange=a.bind(this,"graph_BH_cg",1,"physics_centralGravity"),e=document.getElementById("graph_BH_sc"),e.onchange=a.bind(this,"graph_BH_sc",1,"physics_springConstant"),e=document.getElementById("graph_BH_sl"),e.onchange=a.bind(this,"graph_BH_sl",1,"physics_springLength"),e=document.getElementById("graph_BH_damp"),e.onchange=a.bind(this,"graph_BH_damp",1,"physics_damping"),e=document.getElementById("graph_R_nd"),e.onchange=a.bind(this,"graph_R_nd",1,"physics_repulsion_nodeDistance"),e=document.getElementById("graph_R_cg"),e.onchange=a.bind(this,"graph_R_cg",1,"physics_centralGravity"),e=document.getElementById("graph_R_sc"),e.onchange=a.bind(this,"graph_R_sc",1,"physics_springConstant"),e=document.getElementById("graph_R_sl"),e.onchange=a.bind(this,"graph_R_sl",1,"physics_springLength"),e=document.getElementById("graph_R_damp"),e.onchange=a.bind(this,"graph_R_damp",1,"physics_damping"),e=document.getElementById("graph_H_nd"),e.onchange=a.bind(this,"graph_H_nd",1,"physics_hierarchicalRepulsion_nodeDistance"),e=document.getElementById("graph_H_cg"),e.onchange=a.bind(this,"graph_H_cg",1,"physics_centralGravity"),e=document.getElementById("graph_H_sc"),e.onchange=a.bind(this,"graph_H_sc",1,"physics_springConstant"),e=document.getElementById("graph_H_sl"),e.onchange=a.bind(this,"graph_H_sl",1,"physics_springLength"),e=document.getElementById("graph_H_damp"),e.onchange=a.bind(this,"graph_H_damp",1,"physics_damping"),e=document.getElementById("graph_H_direction"),e.onchange=a.bind(this,"graph_H_direction",t,"hierarchicalLayout_direction"),e=document.getElementById("graph_H_levsep"),e.onchange=a.bind(this,"graph_H_levsep",1,"hierarchicalLayout_levelSeparation"),e=document.getElementById("graph_H_nspac"),e.onchange=a.bind(this,"graph_H_nspac",1,"hierarchicalLayout_nodeSpacing");var i=document.getElementById("graph_physicsMethod1"),d=document.getElementById("graph_physicsMethod2"),l=document.getElementById("graph_physicsMethod3");d.checked=!0,this.constants.physics.barnesHut.enabled&&(i.checked=!0),this.constants.hierarchicalLayout.enabled&&(l.checked=!0);var c=document.getElementById("graph_toggleSmooth"),p=document.getElementById("graph_repositionNodes"),u=document.getElementById("graph_generateOptions");c.onclick=s.bind(this),p.onclick=o.bind(this),u.onclick=n.bind(this),c.style.background=1==this.constants.smoothCurves&&0==this.constants.dynamicSmoothCurves?"#A4FF56":"#FF8532",r.apply(this),i.onchange=r.bind(this),d.onchange=r.bind(this),l.onchange=r.bind(this)}},e._overWriteGraphConstants=function(t,e){var i=t.split("_");1==i.length?this.constants[i[0]]=e:2==i.length?this.constants[i[0]][i[1]]=e:3==i.length&&(this.constants[i[0]][i[1]][i[2]]=e)}},function(t,e){e.startWithClustering=function(){this.clusterToFit(this.constants.clustering.initialMaxNodes,!0),this.updateLabels(),this.stabilize&&this._stabilize(),this.start()},e.clusterToFit=function(t,e){for(var i=this.nodeIndices.length,s=50,o=0;i>t&&s>o;)o%3==0?(this.forceAggregateHubs(!0),this.normalizeClusterLevels()):this.increaseClusterLevel(),i=this.nodeIndices.length,o+=1;o>0&&1==e&&this.repositionNodes(),this._updateCalculationNodes()},e.openCluster=function(t){var e=this.moving;if(t.clusterSize>this.constants.clustering.sectorThreshold&&this._nodeInActiveArea(t)&&("default"!=this._sector()||1!=this.nodeIndices.length)){this._addSector(t);for(var i=0;this.nodeIndices.lengthi;)this.decreaseClusterLevel(),i+=1}else this._expandClusterNode(t,!1,!0),this._updateNodeIndexList(),this._updateDynamicEdges(),this._updateCalculationNodes(),this.updateLabels();this.moving!=e&&this.start()},e.updateClustersDefault=function(){1==this.constants.clustering.enabled&&this.updateClusters(0,!1,!1)},e.increaseClusterLevel=function(){this.updateClusters(-1,!1,!0)},e.decreaseClusterLevel=function(){this.updateClusters(1,!1,!0)},e.updateClusters=function(t,e,i,s){var o=this.moving,n=this.nodeIndices.length;this.previousScale>this.scale&&0==t&&this._collapseSector(),this.previousScale>this.scale||-1==t?this._formClusters(i):(this.previousScalethis.scale||-1==t)&&(this._aggregateHubs(i),this._updateNodeIndexList()),(this.previousScale>this.scale||-1==t)&&(this.handleChains(),this._updateNodeIndexList()),this.previousScale=this.scale,this._updateDynamicEdges(),this.updateLabels(),this.nodeIndices.lengththis.constants.clustering.chainThreshold&&this._reduceAmountOfChains(1-this.constants.clustering.chainThreshold/t)},e._aggregateHubs=function(t){this._getHubSize(),this._formClustersByHub(t,!1)},e.forceAggregateHubs=function(t){var e=this.moving,i=this.nodeIndices.length;this._aggregateHubs(!0),this._updateNodeIndexList(),this._updateDynamicEdges(),this.updateLabels(),this.nodeIndices.length!=i&&(this.clusterSession+=1),(0==t||void 0===t)&&this.moving!=e&&this.start()},e._openClustersBySize=function(){for(var t in this.nodes)if(this.nodes.hasOwnProperty(t)){var e=this.nodes[t];1==e.inView()&&(e.width*this.scale>this.constants.clustering.screenSizeThreshold*this.frame.canvas.clientWidth||e.height*this.scale>this.constants.clustering.screenSizeThreshold*this.frame.canvas.clientHeight)&&this.openCluster(e)}},e._openClusters=function(t,e){for(var i=0;i1&&(t.clusterSizei)){var r=n.from,a=n.to;n.to.options.mass>n.from.options.mass&&(r=n.to,a=n.from),1==a.dynamicEdgesLength?this._addToCluster(r,a,!1):1==r.dynamicEdgesLength&&this._addToCluster(a,r,!1)}}},e._forceClustersByZoom=function(){for(var t in this.nodes)if(this.nodes.hasOwnProperty(t)){var e=this.nodes[t];if(1==e.dynamicEdgesLength&&0!=e.dynamicEdges.length){var i=e.dynamicEdges[0],s=i.toId==e.id?this.nodes[i.fromId]:this.nodes[i.toId];e.id!=s.id&&(s.options.mass>e.options.mass?this._addToCluster(s,e,!0):this._addToCluster(e,s,!0))}}},e._clusterToSmallestNeighbour=function(t){for(var e=-1,i=null,s=0;so.clusterSessions.length&&(e=o.clusterSessions.length,i=o)}null!=o&&void 0!==this.nodes[o.id]&&this._addToCluster(o,t,!0)},e._formClustersByHub=function(t,e){for(var i in this.nodes)this.nodes.hasOwnProperty(i)&&this._formClusterFromHub(this.nodes[i],t,e)},e._formClusterFromHub=function(t,e,i,s){if(void 0===s&&(s=0),t.dynamicEdgesLength>=this.hubThreshold&&0==i||t.dynamicEdgesLength==this.hubThreshold&&1==i){for(var o,n,r,a=this.constants.clustering.clusterEdgeThreshold/this.scale,h=!1,d=[],l=t.dynamicEdges.length,c=0;l>c;c++)d.push(t.dynamicEdges[c].id);if(0==e)for(h=!1,c=0;l>c;c++){var p=this.edges[d[c]];if(void 0!==p&&p.connected&&p.toId!=p.fromId&&(o=p.to.x-p.from.x,n=p.to.y-p.from.y,r=Math.sqrt(o*o+n*n),a>r)){h=!0;break}}if(!e&&h||e)for(c=0;l>c;c++)if(p=this.edges[d[c]],void 0!==p){var u=this.nodes[p.fromId==t.id?p.toId:p.fromId];u.dynamicEdges.length<=this.hubThreshold+s&&u.id!=t.id&&this._addToCluster(t,u,e)}}},e._addToCluster=function(t,e,i){t.containedNodes[e.id]=e;for(var s=0;s1)for(var s=0;s1&&(e.label="[".concat(String(e.clusterSize),"]"))}for(t in this.nodes)this.nodes.hasOwnProperty(t)&&(e=this.nodes[t],1==e.clusterSize&&(e.label=void 0!==e.originalLabel?e.originalLabel:String(e.id)))},e.normalizeClusterLevels=function(){var t,e=0,i=1e9,s=0;for(t in this.nodes)this.nodes.hasOwnProperty(t)&&(s=this.nodes[t].clusterSessions.length,s>e&&(e=s),i>s&&(i=s));if(e-i>this.constants.clustering.clusterLevelDifference){var o=this.nodeIndices.length,n=e-this.constants.clustering.clusterLevelDifference;for(t in this.nodes)this.nodes.hasOwnProperty(t)&&this.nodes[t].clusterSessions.lengths&&(s=n.dynamicEdgesLength),t+=n.dynamicEdgesLength,e+=Math.pow(n.dynamicEdgesLength,2),i+=1}t/=i,e/=i;var r=e-Math.pow(t,2),a=Math.sqrt(r);this.hubThreshold=Math.floor(t+2*a),this.hubThreshold>s&&(this.hubThreshold=s)},e._reduceAmountOfChains=function(t){this.hubThreshold=2;var e=Math.floor(this.nodeIndices.length*t);for(var i in this.nodes)this.nodes.hasOwnProperty(i)&&2==this.nodes[i].dynamicEdgesLength&&this.nodes[i].dynamicEdges.length>=2&&e>0&&(this._formClusterFromHub(this.nodes[i],!0,!0,1),e-=1)},e._getChainFraction=function(){var t=0,e=0;for(var i in this.nodes)this.nodes.hasOwnProperty(i)&&(2==this.nodes[i].dynamicEdgesLength&&this.nodes[i].dynamicEdges.length>=2&&(t+=1),e+=1);return t/e}},function(t,e,i){var s=i(1),o=i(40);e._putDataInSector=function(){this.sectors.active[this._sector()].nodes=this.nodes,this.sectors.active[this._sector()].edges=this.edges,this.sectors.active[this._sector()].nodeIndices=this.nodeIndices},e._switchToSector=function(t,e){void 0===e||"active"==e?this._switchToActiveSector(t):this._switchToFrozenSector(t)},e._switchToActiveSector=function(t){this.nodeIndices=this.sectors.active[t].nodeIndices,this.nodes=this.sectors.active[t].nodes,this.edges=this.sectors.active[t].edges},e._switchToSupportSector=function(){this.nodeIndices=this.sectors.support.nodeIndices,this.nodes=this.sectors.support.nodes,this.edges=this.sectors.support.edges},e._switchToFrozenSector=function(t){this.nodeIndices=this.sectors.frozen[t].nodeIndices,this.nodes=this.sectors.frozen[t].nodes,this.edges=this.sectors.frozen[t].edges},e._loadLatestSector=function(){this._switchToSector(this._sector())},e._sector=function(){return this.activeSector[this.activeSector.length-1]},e._previousSector=function(){if(this.activeSector.length>1)return this.activeSector[this.activeSector.length-2];throw new TypeError("there are not enough sectors in the this.activeSector array.")},e._setActiveSector=function(t){this.activeSector.push(t)},e._forgetLastSector=function(){this.activeSector.pop()},e._createNewSector=function(t){this.sectors.active[t]={nodes:{},edges:{},nodeIndices:[],formationScale:this.scale,drawingNode:void 0},this.sectors.active[t].drawingNode=new o({id:t,color:{background:"#eaefef",border:"495c5e"}},{},{},this.constants),this.sectors.active[t].drawingNode.clusterSize=2},e._deleteActiveSector=function(t){delete this.sectors.active[t]},e._deleteFrozenSector=function(t){delete this.sectors.frozen[t]},e._freezeSector=function(t){this.sectors.frozen[t]=this.sectors.active[t],this._deleteActiveSector(t)},e._activateSector=function(t){this.sectors.active[t]=this.sectors.frozen[t],this._deleteFrozenSector(t)},e._mergeThisWithFrozen=function(t){for(var e in this.nodes)this.nodes.hasOwnProperty(e)&&(this.sectors.frozen[t].nodes[e]=this.nodes[e]);for(var i in this.edges)this.edges.hasOwnProperty(i)&&(this.sectors.frozen[t].edges[i]=this.edges[i]);for(var s=0;s1?this[t](o[0],o[1]):this[t](e))}return this._loadLatestSector(),i},e._doInSupportSector=function(t,e){var i=!1;if(void 0===e)this._switchToSupportSector(),i=this[t]();else{this._switchToSupportSector();var s=Array.prototype.splice.call(arguments,1);i=s.length>1?this[t](s[0],s[1]):this[t](e)}return this._loadLatestSector(),i},e._doInAllFrozenSectors=function(t,e){if(void 0===e)for(var i in this.sectors.frozen)this.sectors.frozen.hasOwnProperty(i)&&(this._switchToFrozenSector(i),this[t]());else for(var i in this.sectors.frozen)if(this.sectors.frozen.hasOwnProperty(i)){this._switchToFrozenSector(i);var s=Array.prototype.splice.call(arguments,1);s.length>1?this[t](s[0],s[1]):this[t](e)}this._loadLatestSector()},e._doInAllSectors=function(t,e){var i=Array.prototype.splice.call(arguments,1);void 0===e?(this._doInAllActiveSectors(t),this._doInAllFrozenSectors(t)):i.length>1?(this._doInAllActiveSectors(t,i[0],i[1]),this._doInAllFrozenSectors(t,i[0],i[1])):(this._doInAllActiveSectors(t,e),this._doInAllFrozenSectors(t,e))},e._clearNodeIndexList=function(){var t=this._sector();this.sectors.active[t].nodeIndices=[],this.nodeIndices=this.sectors.active[t].nodeIndices},e._drawSectorNodes=function(t,e){var i,s=1e9,o=-1e9,n=1e9,r=-1e9;for(var a in this.sectors[e])if(this.sectors[e].hasOwnProperty(a)&&void 0!==this.sectors[e][a].drawingNode){this._switchToSector(a,e),s=1e9,o=-1e9,n=1e9,r=-1e9;for(var h in this.nodes)this.nodes.hasOwnProperty(h)&&(i=this.nodes[h],i.resize(t),n>i.x-.5*i.width&&(n=i.x-.5*i.width),ri.y-.5*i.height&&(s=i.y-.5*i.height),o0?this.nodes[i[i.length-1]]:null},e._getEdgesOverlappingWith=function(t,e){var i=this.edges;for(var s in i)i.hasOwnProperty(s)&&i[s].isOverlappingWith(t)&&e.push(s)},e._getAllEdgesOverlappingWith=function(t){var e=[];return this._doInAllActiveSectors("_getEdgesOverlappingWith",t,e),e},e._getEdgeAt=function(t){var e=this._pointerToPositionObject(t),i=this._getAllEdgesOverlappingWith(e);return i.length>0?this.edges[i[i.length-1]]:null},e._addToSelection=function(t){t instanceof s?this.selectionObj.nodes[t.id]=t:this.selectionObj.edges[t.id]=t},e._addToHover=function(t){t instanceof s?this.hoverObj.nodes[t.id]=t:this.hoverObj.edges[t.id]=t},e._removeFromSelection=function(t){t instanceof s?delete this.selectionObj.nodes[t.id]:delete this.selectionObj.edges[t.id]},e._unselectAll=function(t){void 0===t&&(t=!1);for(var e in this.selectionObj.nodes)this.selectionObj.nodes.hasOwnProperty(e)&&this.selectionObj.nodes[e].unselect();for(var i in this.selectionObj.edges)this.selectionObj.edges.hasOwnProperty(i)&&this.selectionObj.edges[i].unselect();this.selectionObj={nodes:{},edges:{}},0==t&&this.emit("select",this.getSelection())},e._unselectClusters=function(t){void 0===t&&(t=!1);for(var e in this.selectionObj.nodes)this.selectionObj.nodes.hasOwnProperty(e)&&this.selectionObj.nodes[e].clusterSize>1&&(this.selectionObj.nodes[e].unselect(),this._removeFromSelection(this.selectionObj.nodes[e]));0==t&&this.emit("select",this.getSelection())},e._getSelectedNodeCount=function(){var t=0;for(var e in this.selectionObj.nodes)this.selectionObj.nodes.hasOwnProperty(e)&&(t+=1);return t},e._getSelectedNode=function(){for(var t in this.selectionObj.nodes)if(this.selectionObj.nodes.hasOwnProperty(t))return this.selectionObj.nodes[t];return null},e._getSelectedEdge=function(){for(var t in this.selectionObj.edges)if(this.selectionObj.edges.hasOwnProperty(t))return this.selectionObj.edges[t];return null},e._getSelectedEdgeCount=function(){var t=0;for(var e in this.selectionObj.edges)this.selectionObj.edges.hasOwnProperty(e)&&(t+=1);return t},e._getSelectedObjectCount=function(){var t=0;for(var e in this.selectionObj.nodes)this.selectionObj.nodes.hasOwnProperty(e)&&(t+=1);for(var i in this.selectionObj.edges)this.selectionObj.edges.hasOwnProperty(i)&&(t+=1);return t},e._selectionIsEmpty=function(){for(var t in this.selectionObj.nodes)if(this.selectionObj.nodes.hasOwnProperty(t))return!1;for(var e in this.selectionObj.edges)if(this.selectionObj.edges.hasOwnProperty(e))return!1;return!0},e._clusterInSelection=function(){for(var t in this.selectionObj.nodes)if(this.selectionObj.nodes.hasOwnProperty(t)&&this.selectionObj.nodes[t].clusterSize>1)return!0;return!1},e._selectConnectedEdges=function(t){for(var e=0;ei;i++){o=t[i];var n=this.nodes[o];if(!n)throw new RangeError('Node with id "'+o+'" not found');this._selectObject(n,!0,!0,e,!0)}this.redraw()},e.selectEdges=function(t){var e,i,s;if(!t||void 0==t.length)throw"Selection must be an array with ids";for(this._unselectAll(!0),e=0,i=t.length;i>e;e++){s=t[e];var o=this.edges[s];if(!o)throw new RangeError('Edge with id "'+s+'" not found');this._selectObject(o,!0,!0,!1,!0)}this.redraw()},e._updateSelection=function(){for(var t in this.selectionObj.nodes)this.selectionObj.nodes.hasOwnProperty(t)&&(this.nodes.hasOwnProperty(t)||delete this.selectionObj.nodes[t]);for(var e in this.selectionObj.edges)this.selectionObj.edges.hasOwnProperty(e)&&(this.edges.hasOwnProperty(e)||delete this.selectionObj.edges[e])}},function(t,e,i){var s=i(1),o=i(40),n=i(37);e._clearManipulatorBar=function(){this._recursiveDOMDelete(this.manipulationDiv),this.manipulationDOM={},this._manipulationReleaseOverload=function(){},delete this.sectors.support.nodes.targetNode,delete this.sectors.support.nodes.targetViaNode,this.controlNodesActive=!1,this.freezeSimulation=!1},e._restoreOverloadedFunctions=function(){for(var t in this.cachedFunctions)this.cachedFunctions.hasOwnProperty(t)&&(this[t]=this.cachedFunctions[t],delete this.cachedFunctions[t])},e._toggleEditMode=function(){this.editMode=!this.editMode;var t=this.manipulationDiv,e=this.closeDiv,i=this.editModeDiv;1==this.editMode?(t.style.display="block",e.style.display="block",i.style.display="none",e.onclick=this._toggleEditMode.bind(this)):(t.style.display="none",e.style.display="none",i.style.display="block",e.onclick=null),this._createManipulatorBar()},e._createManipulatorBar=function(){this.boundFunction&&this.off("select",this.boundFunction);var t=this.constants.locales[this.constants.locale];if(void 0!==this.edgeBeingEdited&&(this.edgeBeingEdited._disableControlNodes(),this.edgeBeingEdited=void 0,this.selectedControlNode=null,this.controlNodesActive=!1,this._redraw()),this._restoreOverloadedFunctions(),this.freezeSimulation=!1,this.blockConnectingEdgeSelection=!1,this.forceAppendSelection=!1,this.manipulationDOM={},1==this.editMode){for(;this.manipulationDiv.hasChildNodes();)this.manipulationDiv.removeChild(this.manipulationDiv.firstChild);this.manipulationDOM.addNodeSpan=document.createElement("span"),this.manipulationDOM.addNodeSpan.className="network-manipulationUI add",this.manipulationDOM.addNodeLabelSpan=document.createElement("span"),this.manipulationDOM.addNodeLabelSpan.className="network-manipulationLabel",this.manipulationDOM.addNodeLabelSpan.innerHTML=t.addNode,this.manipulationDOM.addNodeSpan.appendChild(this.manipulationDOM.addNodeLabelSpan),this.manipulationDOM.seperatorLineDiv1=document.createElement("div"),this.manipulationDOM.seperatorLineDiv1.className="network-seperatorLine",this.manipulationDOM.addEdgeSpan=document.createElement("span"),this.manipulationDOM.addEdgeSpan.className="network-manipulationUI connect",this.manipulationDOM.addEdgeLabelSpan=document.createElement("span"),this.manipulationDOM.addEdgeLabelSpan.className="network-manipulationLabel",this.manipulationDOM.addEdgeLabelSpan.innerHTML=t.addEdge,this.manipulationDOM.addEdgeSpan.appendChild(this.manipulationDOM.addEdgeLabelSpan),this.manipulationDiv.appendChild(this.manipulationDOM.addNodeSpan),this.manipulationDiv.appendChild(this.manipulationDOM.seperatorLineDiv1),this.manipulationDiv.appendChild(this.manipulationDOM.addEdgeSpan),1==this._getSelectedNodeCount()&&this.triggerFunctions.edit?(this.manipulationDOM.seperatorLineDiv2=document.createElement("div"),this.manipulationDOM.seperatorLineDiv2.className="network-seperatorLine",this.manipulationDOM.editNodeSpan=document.createElement("span"),this.manipulationDOM.editNodeSpan.className="network-manipulationUI edit",this.manipulationDOM.editNodeLabelSpan=document.createElement("span"),this.manipulationDOM.editNodeLabelSpan.className="network-manipulationLabel",this.manipulationDOM.editNodeLabelSpan.innerHTML=t.editNode,this.manipulationDOM.editNodeSpan.appendChild(this.manipulationDOM.editNodeLabelSpan),this.manipulationDiv.appendChild(this.manipulationDOM.seperatorLineDiv2),this.manipulationDiv.appendChild(this.manipulationDOM.editNodeSpan)):1==this._getSelectedEdgeCount()&&0==this._getSelectedNodeCount()&&(this.manipulationDOM.seperatorLineDiv3=document.createElement("div"),this.manipulationDOM.seperatorLineDiv3.className="network-seperatorLine",this.manipulationDOM.editEdgeSpan=document.createElement("span"),this.manipulationDOM.editEdgeSpan.className="network-manipulationUI edit",this.manipulationDOM.editEdgeLabelSpan=document.createElement("span"),this.manipulationDOM.editEdgeLabelSpan.className="network-manipulationLabel",this.manipulationDOM.editEdgeLabelSpan.innerHTML=t.editEdge,this.manipulationDOM.editEdgeSpan.appendChild(this.manipulationDOM.editEdgeLabelSpan),this.manipulationDiv.appendChild(this.manipulationDOM.seperatorLineDiv3),this.manipulationDiv.appendChild(this.manipulationDOM.editEdgeSpan)),0==this._selectionIsEmpty()&&(this.manipulationDOM.seperatorLineDiv4=document.createElement("div"),this.manipulationDOM.seperatorLineDiv4.className="network-seperatorLine",this.manipulationDOM.deleteSpan=document.createElement("span"),this.manipulationDOM.deleteSpan.className="network-manipulationUI delete",this.manipulationDOM.deleteLabelSpan=document.createElement("span"),this.manipulationDOM.deleteLabelSpan.className="network-manipulationLabel",this.manipulationDOM.deleteLabelSpan.innerHTML=t.del,this.manipulationDOM.deleteSpan.appendChild(this.manipulationDOM.deleteLabelSpan),this.manipulationDiv.appendChild(this.manipulationDOM.seperatorLineDiv4),this.manipulationDiv.appendChild(this.manipulationDOM.deleteSpan)),this.manipulationDOM.addNodeSpan.onclick=this._createAddNodeToolbar.bind(this),this.manipulationDOM.addEdgeSpan.onclick=this._createAddEdgeToolbar.bind(this),1==this._getSelectedNodeCount()&&this.triggerFunctions.edit?this.manipulationDOM.editNodeSpan.onclick=this._editNode.bind(this):1==this._getSelectedEdgeCount()&&0==this._getSelectedNodeCount()&&(this.manipulationDOM.editEdgeSpan.onclick=this._createEditEdgeToolbar.bind(this)),0==this._selectionIsEmpty()&&(this.manipulationDOM.deleteSpan.onclick=this._deleteSelected.bind(this)),this.closeDiv.onclick=this._toggleEditMode.bind(this); -var e=this;this.boundFunction=e._createManipulatorBar,this.on("select",this.boundFunction)}else{for(;this.editModeDiv.hasChildNodes();)this.editModeDiv.removeChild(this.editModeDiv.firstChild);this.manipulationDOM.editModeSpan=document.createElement("span"),this.manipulationDOM.editModeSpan.className="network-manipulationUI edit editmode",this.manipulationDOM.editModeLabelSpan=document.createElement("span"),this.manipulationDOM.editModeLabelSpan.className="network-manipulationLabel",this.manipulationDOM.editModeLabelSpan.innerHTML=t.edit,this.manipulationDOM.editModeSpan.appendChild(this.manipulationDOM.editModeLabelSpan),this.editModeDiv.appendChild(this.manipulationDOM.editModeSpan),this.manipulationDOM.editModeSpan.onclick=this._toggleEditMode.bind(this)}},e._createAddNodeToolbar=function(){this._clearManipulatorBar(),this.boundFunction&&this.off("select",this.boundFunction);var t=this.constants.locales[this.constants.locale];this.manipulationDOM={},this.manipulationDOM.backSpan=document.createElement("span"),this.manipulationDOM.backSpan.className="network-manipulationUI back",this.manipulationDOM.backLabelSpan=document.createElement("span"),this.manipulationDOM.backLabelSpan.className="network-manipulationLabel",this.manipulationDOM.backLabelSpan.innerHTML=t.back,this.manipulationDOM.backSpan.appendChild(this.manipulationDOM.backLabelSpan),this.manipulationDOM.seperatorLineDiv1=document.createElement("div"),this.manipulationDOM.seperatorLineDiv1.className="network-seperatorLine",this.manipulationDOM.descriptionSpan=document.createElement("span"),this.manipulationDOM.descriptionSpan.className="network-manipulationUI none",this.manipulationDOM.descriptionLabelSpan=document.createElement("span"),this.manipulationDOM.descriptionLabelSpan.className="network-manipulationLabel",this.manipulationDOM.descriptionLabelSpan.innerHTML=t.addDescription,this.manipulationDOM.descriptionSpan.appendChild(this.manipulationDOM.descriptionLabelSpan),this.manipulationDiv.appendChild(this.manipulationDOM.backSpan),this.manipulationDiv.appendChild(this.manipulationDOM.seperatorLineDiv1),this.manipulationDiv.appendChild(this.manipulationDOM.descriptionSpan),this.manipulationDOM.backSpan.onclick=this._createManipulatorBar.bind(this);var e=this;this.boundFunction=e._addNode,this.on("select",this.boundFunction)},e._createAddEdgeToolbar=function(){this._clearManipulatorBar(),this._unselectAll(!0),this.freezeSimulation=!0,this.boundFunction&&this.off("select",this.boundFunction);var t=this.constants.locales[this.constants.locale];this._unselectAll(),this.forceAppendSelection=!1,this.blockConnectingEdgeSelection=!0,this.manipulationDOM={},this.manipulationDOM.backSpan=document.createElement("span"),this.manipulationDOM.backSpan.className="network-manipulationUI back",this.manipulationDOM.backLabelSpan=document.createElement("span"),this.manipulationDOM.backLabelSpan.className="network-manipulationLabel",this.manipulationDOM.backLabelSpan.innerHTML=t.back,this.manipulationDOM.backSpan.appendChild(this.manipulationDOM.backLabelSpan),this.manipulationDOM.seperatorLineDiv1=document.createElement("div"),this.manipulationDOM.seperatorLineDiv1.className="network-seperatorLine",this.manipulationDOM.descriptionSpan=document.createElement("span"),this.manipulationDOM.descriptionSpan.className="network-manipulationUI none",this.manipulationDOM.descriptionLabelSpan=document.createElement("span"),this.manipulationDOM.descriptionLabelSpan.className="network-manipulationLabel",this.manipulationDOM.descriptionLabelSpan.innerHTML=t.edgeDescription,this.manipulationDOM.descriptionSpan.appendChild(this.manipulationDOM.descriptionLabelSpan),this.manipulationDiv.appendChild(this.manipulationDOM.backSpan),this.manipulationDiv.appendChild(this.manipulationDOM.seperatorLineDiv1),this.manipulationDiv.appendChild(this.manipulationDOM.descriptionSpan),this.manipulationDOM.backSpan.onclick=this._createManipulatorBar.bind(this);var e=this;this.boundFunction=e._handleConnect,this.on("select",this.boundFunction),this.cachedFunctions._handleTouch=this._handleTouch,this.cachedFunctions._manipulationReleaseOverload=this._manipulationReleaseOverload,this.cachedFunctions._handleDragStart=this._handleDragStart,this.cachedFunctions._handleDragEnd=this._handleDragEnd,this._handleTouch=this._handleConnect,this._manipulationReleaseOverload=function(){},this._handleDragStart=function(){},this._handleDragEnd=this._finishConnect,this._redraw()},e._createEditEdgeToolbar=function(){this._clearManipulatorBar(),this.controlNodesActive=!0,this.boundFunction&&this.off("select",this.boundFunction),this.edgeBeingEdited=this._getSelectedEdge(),this.edgeBeingEdited._enableControlNodes();var t=this.constants.locales[this.constants.locale];this.manipulationDOM={},this.manipulationDOM.backSpan=document.createElement("span"),this.manipulationDOM.backSpan.className="network-manipulationUI back",this.manipulationDOM.backLabelSpan=document.createElement("span"),this.manipulationDOM.backLabelSpan.className="network-manipulationLabel",this.manipulationDOM.backLabelSpan.innerHTML=t.back,this.manipulationDOM.backSpan.appendChild(this.manipulationDOM.backLabelSpan),this.manipulationDOM.seperatorLineDiv1=document.createElement("div"),this.manipulationDOM.seperatorLineDiv1.className="network-seperatorLine",this.manipulationDOM.descriptionSpan=document.createElement("span"),this.manipulationDOM.descriptionSpan.className="network-manipulationUI none",this.manipulationDOM.descriptionLabelSpan=document.createElement("span"),this.manipulationDOM.descriptionLabelSpan.className="network-manipulationLabel",this.manipulationDOM.descriptionLabelSpan.innerHTML=t.editEdgeDescription,this.manipulationDOM.descriptionSpan.appendChild(this.manipulationDOM.descriptionLabelSpan),this.manipulationDiv.appendChild(this.manipulationDOM.backSpan),this.manipulationDiv.appendChild(this.manipulationDOM.seperatorLineDiv1),this.manipulationDiv.appendChild(this.manipulationDOM.descriptionSpan),this.manipulationDOM.backSpan.onclick=this._createManipulatorBar.bind(this),this.cachedFunctions._handleTouch=this._handleTouch,this.cachedFunctions._manipulationReleaseOverload=this._manipulationReleaseOverload,this.cachedFunctions._handleTap=this._handleTap,this.cachedFunctions._handleDragStart=this._handleDragStart,this.cachedFunctions._handleOnDrag=this._handleOnDrag,this._handleTouch=this._selectControlNode,this._handleTap=function(){},this._handleOnDrag=this._controlNodeDrag,this._handleDragStart=function(){},this._manipulationReleaseOverload=this._releaseControlNode,this._redraw()},e._selectControlNode=function(t){this.edgeBeingEdited.controlNodes.from.unselect(),this.edgeBeingEdited.controlNodes.to.unselect(),this.selectedControlNode=this.edgeBeingEdited._getSelectedControlNode(this._XconvertDOMtoCanvas(t.x),this._YconvertDOMtoCanvas(t.y)),null!==this.selectedControlNode&&(this.selectedControlNode.select(),this.freezeSimulation=!0),this._redraw()},e._controlNodeDrag=function(t){var e=this._getPointer(t.gesture.center);null!==this.selectedControlNode&&void 0!==this.selectedControlNode&&(this.selectedControlNode.x=this._XconvertDOMtoCanvas(e.x),this.selectedControlNode.y=this._YconvertDOMtoCanvas(e.y)),this._redraw()},e._releaseControlNode=function(t){var e=this._getNodeAt(t);null!==e?(1==this.edgeBeingEdited.controlNodes.from.selected&&(this.edgeBeingEdited._restoreControlNodes(),this._editEdge(e.id,this.edgeBeingEdited.to.id),this.edgeBeingEdited.controlNodes.from.unselect()),1==this.edgeBeingEdited.controlNodes.to.selected&&(this.edgeBeingEdited._restoreControlNodes(),this._editEdge(this.edgeBeingEdited.from.id,e.id),this.edgeBeingEdited.controlNodes.to.unselect())):this.edgeBeingEdited._restoreControlNodes(),this.freezeSimulation=!1,this._redraw()},e._handleConnect=function(t){if(0==this._getSelectedNodeCount()){var e=this._getNodeAt(t);if(null!=e)if(e.clusterSize>1)alert(this.constants.locales[this.constants.locale].createEdgeError);else{this._selectObject(e,!1);var i=this.sectors.support.nodes;i.targetNode=new o({id:"targetNode"},{},{},this.constants);var s=i.targetNode;s.x=e.x,s.y=e.y,this.edges.connectionEdge=new n({id:"connectionEdge",from:e.id,to:s.id},this,this.constants);var r=this.edges.connectionEdge;r.from=e,r.connected=!0,r.options.smoothCurves={enabled:!0,dynamic:!1,type:"continuous",roundness:.5},r.selected=!0,r.to=s,this.cachedFunctions._handleOnDrag=this._handleOnDrag,this._handleOnDrag=function(t){var e=this._getPointer(t.gesture.center),i=this.edges.connectionEdge;i.to.x=this._XconvertDOMtoCanvas(e.x),i.to.y=this._YconvertDOMtoCanvas(e.y)},this.moving=!0,this.start()}}},e._finishConnect=function(t){if(1==this._getSelectedNodeCount()){var e=this._getPointer(t.gesture.center);this._handleOnDrag=this.cachedFunctions._handleOnDrag,delete this.cachedFunctions._handleOnDrag;var i=this.edges.connectionEdge.fromId;delete this.edges.connectionEdge,delete this.sectors.support.nodes.targetNode,delete this.sectors.support.nodes.targetViaNode;var s=this._getNodeAt(e);null!=s&&(s.clusterSize>1?alert(this.constants.locales[this.constants.locale].createEdgeError):(this._createEdge(i,s.id),this._createManipulatorBar())),this._unselectAll()}},e._addNode=function(){if(this._selectionIsEmpty()&&1==this.editMode){var t=this._pointerToPositionObject(this.pointerPosition),e={id:s.randomUUID(),x:t.left,y:t.top,label:"new",allowedToMoveX:!0,allowedToMoveY:!0};if(this.triggerFunctions.add){if(2!=this.triggerFunctions.add.length)throw new Error("The function for add does not support two arguments (data,callback)");var i=this;this.triggerFunctions.add(e,function(t){i.nodesData.add(t),i._createManipulatorBar(),i.moving=!0,i.start()})}else this.nodesData.add(e),this._createManipulatorBar(),this.moving=!0,this.start()}},e._createEdge=function(t,e){if(1==this.editMode){var i={from:t,to:e};if(this.triggerFunctions.connect){if(2!=this.triggerFunctions.connect.length)throw new Error("The function for connect does not support two arguments (data,callback)");var s=this;this.triggerFunctions.connect(i,function(t){s.edgesData.add(t),s.moving=!0,s.start()})}else this.edgesData.add(i),this.moving=!0,this.start()}},e._editEdge=function(t,e){if(1==this.editMode){var i={id:this.edgeBeingEdited.id,from:t,to:e};if(this.triggerFunctions.editEdge){if(2!=this.triggerFunctions.editEdge.length)throw new Error("The function for edit does not support two arguments (data, callback)");var s=this;this.triggerFunctions.editEdge(i,function(t){s.edgesData.update(t),s.moving=!0,s.start()})}else this.edgesData.update(i),this.moving=!0,this.start()}},e._editNode=function(){if(!this.triggerFunctions.edit||1!=this.editMode)throw new Error("No edit function has been bound to this button");var t=this._getSelectedNode(),e={id:t.id,label:t.label,group:t.options.group,shape:t.options.shape,color:{background:t.options.color.background,border:t.options.color.border,highlight:{background:t.options.color.highlight.background,border:t.options.color.highlight.border}}};if(2!=this.triggerFunctions.edit.length)throw new Error("The function for edit does not support two arguments (data, callback)");var i=this;this.triggerFunctions.edit(e,function(t){i.nodesData.update(t),i._createManipulatorBar(),i.moving=!0,i.start()})},e._deleteSelected=function(){if(!this._selectionIsEmpty()&&1==this.editMode)if(this._clusterInSelection())alert(this.constants.locales[this.constants.locale].deleteClusterError);else{var t=this.getSelectedNodes(),e=this.getSelectedEdges();if(this.triggerFunctions.del){var i=this,s={nodes:t,edges:e};if(2!=this.triggerFunctions.del.length)throw new Error("The function for delete does not support two arguments (data, callback)");this.triggerFunctions.del(s,function(t){i.edgesData.remove(t.edges),i.nodesData.remove(t.nodes),i._unselectAll(),i.moving=!0,i.start()})}else this.edgesData.remove(e),this.nodesData.remove(t),this._unselectAll(),this.moving=!0,this.start()}}},function(t,e,i){var s=(i(1),i(45));e._cleanNavigation=function(){if(0!=this.navigationHammers.existing.length){for(var t=0;t0){var t,e,i=0,s=!1,o=!1;for(e in this.nodes)this.nodes.hasOwnProperty(e)&&(t=this.nodes[e],-1!=t.level?s=!0:o=!0,is&&(n.xFixed=!1,n.x=i[n.level].minPos,r=!0):n.yFixed&&n.level>s&&(n.yFixed=!1,n.y=i[n.level].minPos,r=!0),1==r&&(i[n.level].minPos+=i[n.level].nodeSpacing,n.edges.length>1&&this._placeBranchNodes(n.edges,n.id,i,n.level))}},e._setLevel=function(t,e,i){for(var s=0;st)&&(o.level=t,o.edges.length>1&&this._setLevel(t+1,o.edges,o.id))}},e._setLevelDirected=function(t,e,i){this.nodes[i].hierarchyEnumerated=!0;for(var s,o,n=0;n1&&s.hierarchyEnumerated===!1&&this._setLevelDirected(s.level,s.edges,s.id)},e._restoreNodes=function(){for(var t in this.nodes)this.nodes.hasOwnProperty(t)&&(this.nodes[t].xFixed=!1,this.nodes[t].yFixed=!1)}},function(t,e,i){var s;!function(o,n){function r(){a.READY||(w.determineEventTypes(),x.each(a.gestures,function(t){S.register(t)}),w.onTouch(a.DOCUMENT,v,S.detect),w.onTouch(a.DOCUMENT,y,S.detect),a.READY=!0)}var a=function D(t,e){return new D.Instance(t,e||{})};a.VERSION="1.1.3",a.defaults={behavior:{userSelect:"none",touchAction:"pan-y",touchCallout:"none",contentZooming:"none",userDrag:"none",tapHighlightColor:"rgba(0,0,0,0)"}},a.DOCUMENT=document,a.HAS_POINTEREVENTS=navigator.pointerEnabled||navigator.msPointerEnabled,a.HAS_TOUCHEVENTS="ontouchstart"in o,a.IS_MOBILE=/mobile|tablet|ip(ad|hone|od)|android|silk/i.test(navigator.userAgent),a.NO_MOUSEEVENTS=a.HAS_TOUCHEVENTS&&a.IS_MOBILE||a.HAS_POINTEREVENTS,a.CALCULATE_INTERVAL=25;var h={},d=a.DIRECTION_DOWN="down",l=a.DIRECTION_LEFT="left",c=a.DIRECTION_UP="up",p=a.DIRECTION_RIGHT="right",u=a.POINTER_MOUSE="mouse",m=a.POINTER_TOUCH="touch",f=a.POINTER_PEN="pen",g=a.EVENT_START="start",v=a.EVENT_MOVE="move",y=a.EVENT_END="end",b=a.EVENT_RELEASE="release",_=a.EVENT_TOUCH="touch";a.READY=!1,a.plugins=a.plugins||{},a.gestures=a.gestures||{};var x=a.utils={extend:function(t,e,i){for(var s in e)!e.hasOwnProperty(s)||t[s]!==n&&i||(t[s]=e[s]);return t},on:function(t,e,i){t.addEventListener(e,i,!1)},off:function(t,e,i){t.removeEventListener(e,i,!1)},each:function(t,e,i){var s,o;if("forEach"in t)t.forEach(e,i);else if(t.length!==n){for(s=0,o=t.length;o>s;s++)if(e.call(i,t[s],s,t)===!1)return}else for(s in t)if(t.hasOwnProperty(s)&&e.call(i,t[s],s,t)===!1)return},inStr:function(t,e){return t.indexOf(e)>-1},inArray:function(t,e){if(t.indexOf){var i=t.indexOf(e);return-1===i?!1:i}for(var s=0,o=t.length;o>s;s++)if(t[s]===e)return s;return!1},toArray:function(t){return Array.prototype.slice.call(t,0)},hasParent:function(t,e){for(;t;){if(t==e)return!0;t=t.parentNode}return!1},getCenter:function(t){var e=[],i=[],s=[],o=[],n=Math.min,r=Math.max;return 1===t.length?{pageX:t[0].pageX,pageY:t[0].pageY,clientX:t[0].clientX,clientY:t[0].clientY}:(x.each(t,function(t){e.push(t.pageX),i.push(t.pageY),s.push(t.clientX),o.push(t.clientY)}),{pageX:(n.apply(Math,e)+r.apply(Math,e))/2,pageY:(n.apply(Math,i)+r.apply(Math,i))/2,clientX:(n.apply(Math,s)+r.apply(Math,s))/2,clientY:(n.apply(Math,o)+r.apply(Math,o))/2})},getVelocity:function(t,e,i){return{x:Math.abs(e/t)||0,y:Math.abs(i/t)||0}},getAngle:function(t,e){var i=e.clientX-t.clientX,s=e.clientY-t.clientY;return 180*Math.atan2(s,i)/Math.PI},getDirection:function(t,e){var i=Math.abs(t.clientX-e.clientX),s=Math.abs(t.clientY-e.clientY);return i>=s?t.clientX-e.clientX>0?l:p:t.clientY-e.clientY>0?c:d},getDistance:function(t,e){var i=e.clientX-t.clientX,s=e.clientY-t.clientY;return Math.sqrt(i*i+s*s)},getScale:function(t,e){return t.length>=2&&e.length>=2?this.getDistance(e[0],e[1])/this.getDistance(t[0],t[1]):1},getRotation:function(t,e){return t.length>=2&&e.length>=2?this.getAngle(e[1],e[0])-this.getAngle(t[1],t[0]):0},isVertical:function(t){return t==c||t==d},setPrefixedCss:function(t,e,i,s){var o=["","Webkit","Moz","O","ms"];e=x.toCamelCase(e);for(var n=0;n0&&this.started&&(r=v),this.started=!0;var d=this.collectEventData(i,r,o,t);return e!=y&&s.call(S,d),a&&(d.changedLength=h,d.eventType=a,s.call(S,d),d.eventType=r,delete d.changedLength),r==y&&(s.call(S,d),this.started=!1),r},determineEventTypes:function(){var t;return t=a.HAS_POINTEREVENTS?o.PointerEvent?["pointerdown","pointermove","pointerup pointercancel lostpointercapture"]:["MSPointerDown","MSPointerMove","MSPointerUp MSPointerCancel MSLostPointerCapture"]:a.NO_MOUSEEVENTS?["touchstart","touchmove","touchend touchcancel"]:["touchstart mousedown","touchmove mousemove","touchend touchcancel mouseup"],h[g]=t[0],h[v]=t[1],h[y]=t[2],h},getTouchList:function(t,e){if(a.HAS_POINTEREVENTS)return M.getTouchList();if(t.touches){if(e==v)return t.touches;var i=[],s=[].concat(x.toArray(t.touches),x.toArray(t.changedTouches)),o=[];return x.each(s,function(t){x.inArray(i,t.identifier)===!1&&o.push(t),i.push(t.identifier)}),o}return t.identifier=1,[t]},collectEventData:function(t,e,i,s){var o=m;return x.inStr(s.type,"mouse")||M.matchType(u,s)?o=u:M.matchType(f,s)&&(o=f),{center:x.getCenter(i),timeStamp:Date.now(),target:s.target,touches:i,eventType:e,pointerType:o,srcEvent:s,preventDefault:function(){var t=this.srcEvent;t.preventManipulation&&t.preventManipulation(),t.preventDefault&&t.preventDefault()},stopPropagation:function(){this.srcEvent.stopPropagation()},stopDetect:function(){return S.stopDetect()}}}},M=a.PointerEvent={pointers:{},getTouchList:function(){var t=[];return x.each(this.pointers,function(e){t.push(e)}),t},updatePointer:function(t,e){t==y||t!=y&&1!==e.buttons?delete this.pointers[e.pointerId]:(e.identifier=e.pointerId,this.pointers[e.pointerId]=e)},matchType:function(t,e){if(!e.pointerType)return!1;var i=e.pointerType,s={};return s[u]=i===(e.MSPOINTER_TYPE_MOUSE||u),s[m]=i===(e.MSPOINTER_TYPE_TOUCH||m),s[f]=i===(e.MSPOINTER_TYPE_PEN||f),s[t]},reset:function(){this.pointers={}}},S=a.detection={gestures:[],current:null,previous:null,stopped:!1,startDetect:function(t,e){this.current||(this.stopped=!1,this.current={inst:t,startEvent:x.extend({},e),lastEvent:!1,lastCalcEvent:!1,futureCalcEvent:!1,lastCalcData:{},name:""},this.detect(e))},detect:function(t){if(this.current&&!this.stopped){t=this.extendEventData(t);var e=this.current.inst,i=e.options;return x.each(this.gestures,function(s){!this.stopped&&e.enabled&&i[s.name]&&s.handler.call(s,t,e)},this),this.current&&(this.current.lastEvent=t),t.eventType==y&&this.stopDetect(),t}},stopDetect:function(){this.previous=x.extend({},this.current),this.current=null,this.stopped=!0},getCalculatedData:function(t,e,i,s,o){var n=this.current,r=!1,h=n.lastCalcEvent,d=n.lastCalcData;h&&t.timeStamp-h.timeStamp>a.CALCULATE_INTERVAL&&(e=h.center,i=t.timeStamp-h.timeStamp,s=t.center.clientX-h.center.clientX,o=t.center.clientY-h.center.clientY,r=!0),(t.eventType==_||t.eventType==b)&&(n.futureCalcEvent=t),(!n.lastCalcEvent||r)&&(d.velocity=x.getVelocity(i,s,o),d.angle=x.getAngle(e,t.center),d.direction=x.getDirection(e,t.center),n.lastCalcEvent=n.futureCalcEvent||t,n.futureCalcEvent=t),t.velocityX=d.velocity.x,t.velocityY=d.velocity.y,t.interimAngle=d.angle,t.interimDirection=d.direction},extendEventData:function(t){var e=this.current,i=e.startEvent,s=e.lastEvent||i;(t.eventType==_||t.eventType==b)&&(i.touches=[],x.each(t.touches,function(t){i.touches.push({clientX:t.clientX,clientY:t.clientY})}));var o=t.timeStamp-i.timeStamp,n=t.center.clientX-i.center.clientX,r=t.center.clientY-i.center.clientY;return this.getCalculatedData(t,s.center,o,n,r),x.extend(t,{startEvent:i,deltaTime:o,deltaX:n,deltaY:r,distance:x.getDistance(i.center,t.center),angle:x.getAngle(i.center,t.center),direction:x.getDirection(i.center,t.center),scale:x.getScale(i.touches,t.touches),rotation:x.getRotation(i.touches,t.touches)}),t},register:function(t){var e=t.defaults||{};return e[t.name]===n&&(e[t.name]=!0),x.extend(a.defaults,e,!0),t.index=t.index||1e3,this.gestures.push(t),this.gestures.sort(function(t,e){return t.indexe.index?1:0}),this.gestures}};a.Instance=function(t,e){var i=this;r(),this.element=t,this.enabled=!0,x.each(e,function(t,i){delete e[i],e[x.toCamelCase(i)]=t}),this.options=x.extend(x.extend({},a.defaults),e||{}),this.options.behavior&&x.toggleBehavior(this.element,this.options.behavior,!0),this.eventStartHandler=w.onTouch(t,g,function(t){i.enabled&&t.eventType==g?S.startDetect(i,t):t.eventType==_&&S.detect(t)}),this.eventHandlers=[]},a.Instance.prototype={on:function(t,e){var i=this;return w.on(i.element,t,e,function(t){i.eventHandlers.push({gesture:t,handler:e})}),i},off:function(t,e){var i=this;return w.off(i.element,t,e,function(t){var s=x.inArray({gesture:t,handler:e});s!==!1&&i.eventHandlers.splice(s,1)}),i},trigger:function(t,e){e||(e={});var i=a.DOCUMENT.createEvent("Event");i.initEvent(t,!0,!0),i.gesture=e;var s=this.element;return x.hasParent(e.target,s)&&(s=e.target),s.dispatchEvent(i),this},enable:function(t){return this.enabled=t,this},dispose:function(){var t,e;for(x.toggleBehavior(this.element,this.options.behavior,!1),t=-1;e=this.eventHandlers[++t];)x.off(this.element,e.gesture,e.handler);return this.eventHandlers=[],w.off(this.element,h[g],this.eventStartHandler),null}},function(t){function e(e,s){var o=S.current;if(!(s.options.dragMaxTouches>0&&e.touches.length>s.options.dragMaxTouches))switch(e.eventType){case g:i=!1;break;case v:if(e.distance0)){var r=Math.abs(s.options.dragMinDistance/e.distance);n.pageX+=e.deltaX*r,n.pageY+=e.deltaY*r,n.clientX+=e.deltaX*r,n.clientY+=e.deltaY*r,e=S.extendEventData(e)}(o.lastEvent.dragLockToAxis||s.options.dragLockToAxis&&s.options.dragLockMinDistance<=e.distance)&&(e.dragLockToAxis=!0);var a=o.lastEvent.direction;e.dragLockToAxis&&a!==e.direction&&(e.direction=x.isVertical(a)?e.deltaY<0?c:d:e.deltaX<0?l:p),i||(s.trigger(t+"start",e),i=!0),s.trigger(t,e),s.trigger(t+e.direction,e);var h=x.isVertical(e.direction);(s.options.dragBlockVertical&&h||s.options.dragBlockHorizontal&&!h)&&e.preventDefault();break;case b:i&&e.changedLength<=s.options.dragMaxTouches&&(s.trigger(t+"end",e),i=!1);break;case y:i=!1}}var i=!1;a.gestures.Drag={name:t,index:50,handler:e,defaults:{dragMinDistance:10,dragDistanceCorrection:!0,dragMaxTouches:1,dragBlockHorizontal:!1,dragBlockVertical:!1,dragLockToAxis:!1,dragLockMinDistance:25}}}("drag"),a.gestures.Gesture={name:"gesture",index:1337,handler:function(t,e){e.trigger(this.name,t)}},function(t){function e(e,s){var o=s.options,n=S.current;switch(e.eventType){case g:clearTimeout(i),n.name=t,i=setTimeout(function(){n&&n.name==t&&s.trigger(t,e)},o.holdTimeout);break;case v:e.distance>o.holdThreshold&&clearTimeout(i);break;case b:clearTimeout(i)}}var i;a.gestures.Hold={name:t,index:10,defaults:{holdTimeout:500,holdThreshold:2},handler:e}}("hold"),a.gestures.Release={name:"release",index:1/0,handler:function(t,e){t.eventType==b&&e.trigger(this.name,t)}},a.gestures.Swipe={name:"swipe",index:40,defaults:{swipeMinTouches:1,swipeMaxTouches:1,swipeVelocityX:.6,swipeVelocityY:.6},handler:function(t,e){if(t.eventType==b){var i=t.touches.length,s=e.options;if(is.swipeMaxTouches)return;(t.velocityX>s.swipeVelocityX||t.velocityY>s.swipeVelocityY)&&(e.trigger(this.name,t),e.trigger(this.name+t.direction,t))}}},function(t){function e(e,s){var o,n,r=s.options,a=S.current,h=S.previous;switch(e.eventType){case g:i=!1;break;case v:i=i||e.distance>r.tapMaxDistance;break;case y:!x.inStr(e.srcEvent.type,"cancel")&&e.deltaTimes.options.transformMinRotation&&s.trigger("rotate",e),o>s.options.transformMinScale&&(s.trigger("pinch",e),s.trigger("pinch"+(e.scale<1?"in":"out"),e));break;case b:i&&e.changedLength<2&&(s.trigger(t+"end",e),i=!1)}}var i=!1;a.gestures.Transform={name:t,index:45,defaults:{transformMinScale:.01,transformMinRotation:1},handler:e}}("transform"),s=function(){return a}.call(e,i,e,t),!(s!==n&&(t.exports=s)) -}(window)},function(t){function e(t){throw new Error("Cannot find module '"+t+"'.")}e.keys=function(){return[]},e.resolve=e,t.exports=e,e.id=67},function(t,e){e._calculateNodeForces=function(){var t,e,i,s,o,n,r,a,h,d,l,c=this.calculationNodes,p=this.calculationNodeIndices,u=-2/3,m=4/3,f=this.constants.physics.repulsion.nodeDistance,g=f;for(d=0;di&&(r=.5*g>i?1:v*i+m,r*=0==n?1:1+n*this.constants.clustering.forceAmplification,r/=Math.max(i,.01*g),s=t*r,o=e*r,a.fx-=s,a.fy-=o,h.fx+=s,h.fy+=o)}}},function(t,e){e._calculateNodeForces=function(){var t,e,i,s,o,n,r,a,h,d,l=this.calculationNodes,c=this.calculationNodeIndices,p=this.constants.physics.hierarchicalRepulsion.nodeDistance;for(h=0;hi?-Math.pow(u*i,2)+Math.pow(u*p,2):0,0==i?i=.01:n/=i,s=t*n,o=e*n,r.fx-=s,r.fy-=o,a.fx+=s,a.fy+=o}},e._calculateHierarchicalSpringForces=function(){for(var t,e,i,s,o,n,r,a,h,d=this.edges,l=this.calculationNodes,c=this.calculationNodeIndices,p=0;pn;n++)t=e[i[n]],t.options.mass>0&&(this._getForceContribution(o.root.children.NW,t),this._getForceContribution(o.root.children.NE,t),this._getForceContribution(o.root.children.SW,t),this._getForceContribution(o.root.children.SE,t))}},e._getForceContribution=function(t,e){if(t.childrenCount>0){var i,s,o;if(i=t.centerOfMass.x-e.x,s=t.centerOfMass.y-e.y,o=Math.sqrt(i*i+s*s),o*t.calcSize>this.constants.physics.barnesHut.thetaInverted){0==o&&(o=.1*Math.random(),i=o);var n=this.constants.physics.barnesHut.gravitationalConstant*t.mass*e.options.mass/(o*o*o),r=i*n,a=s*n;e.fx+=r,e.fy+=a}else if(4==t.childrenCount)this._getForceContribution(t.children.NW,e),this._getForceContribution(t.children.NE,e),this._getForceContribution(t.children.SW,e),this._getForceContribution(t.children.SE,e);else if(t.children.data.id!=e.id){0==o&&(o=.5*Math.random(),i=o);var n=this.constants.physics.barnesHut.gravitationalConstant*t.mass*e.options.mass/(o*o*o),r=i*n,a=s*n;e.fx+=r,e.fy+=a}}},e._formBarnesHutTree=function(t,e){for(var i,s=e.length,o=Number.MAX_VALUE,n=Number.MAX_VALUE,r=-Number.MAX_VALUE,a=-Number.MAX_VALUE,h=0;s>h;h++){var d=t[e[h]].x,l=t[e[h]].y;t[e[h]].options.mass>0&&(o>d&&(o=d),d>r&&(r=d),n>l&&(n=l),l>a&&(a=l))}var c=Math.abs(r-o)-Math.abs(a-n);c>0?(n-=.5*c,a+=.5*c):(o+=.5*c,r-=.5*c);var p=1e-5,u=Math.max(p,Math.abs(r-o)),m=.5*u,f=.5*(o+r),g=.5*(n+a),v={root:{centerOfMass:{x:0,y:0},mass:0,range:{minX:f-m,maxX:f+m,minY:g-m,maxY:g+m},size:u,calcSize:1/u,children:{data:null},maxWidth:0,level:0,childrenCount:4}};for(this._splitBranch(v.root),h=0;s>h;h++)i=t[e[h]],i.options.mass>0&&this._placeInTree(v.root,i);this.barnesHutTree=v},e._updateBranchMass=function(t,e){var i=t.mass+e.options.mass,s=1/i;t.centerOfMass.x=t.centerOfMass.x*t.mass+e.x*e.options.mass,t.centerOfMass.x*=s,t.centerOfMass.y=t.centerOfMass.y*t.mass+e.y*e.options.mass,t.centerOfMass.y*=s,t.mass=i;var o=Math.max(Math.max(e.height,e.radius),e.width);t.maxWidth=t.maxWidthe.x?t.children.NW.range.maxY>e.y?this._placeInRegion(t,e,"NW"):this._placeInRegion(t,e,"SW"):t.children.NW.range.maxY>e.y?this._placeInRegion(t,e,"NE"):this._placeInRegion(t,e,"SE")},e._placeInRegion=function(t,e,i){switch(t.children[i].childrenCount){case 0:t.children[i].children.data=e,t.children[i].childrenCount=1,this._updateBranchMass(t.children[i],e);break;case 1:t.children[i].children.data.x==e.x&&t.children[i].children.data.y==e.y?(e.x+=Math.random(),e.y+=Math.random()):(this._splitBranch(t.children[i]),this._placeInTree(t.children[i],e));break;case 4:this._placeInTree(t.children[i],e)}},e._splitBranch=function(t){var e=null;1==t.childrenCount&&(e=t.children.data,t.mass=0,t.centerOfMass.x=0,t.centerOfMass.y=0),t.childrenCount=4,t.children.data=null,this._insertRegion(t,"NW"),this._insertRegion(t,"NE"),this._insertRegion(t,"SW"),this._insertRegion(t,"SE"),null!=e&&this._placeInTree(t,e)},e._insertRegion=function(t,e){var i,s,o,n,r=.5*t.size;switch(e){case"NW":i=t.range.minX,s=t.range.minX+r,o=t.range.minY,n=t.range.minY+r;break;case"NE":i=t.range.minX+r,s=t.range.maxX,o=t.range.minY,n=t.range.minY+r;break;case"SW":i=t.range.minX,s=t.range.minX+r,o=t.range.minY+r,n=t.range.maxY;break;case"SE":i=t.range.minX+r,s=t.range.maxX,o=t.range.minY+r,n=t.range.maxY}t.children[e]={centerOfMass:{x:0,y:0},mass:0,range:{minX:i,maxX:s,minY:o,maxY:n},size:.5*t.size,calcSize:2*t.calcSize,children:{data:null},maxWidth:0,level:t.level+1,childrenCount:0}},e._drawTree=function(t,e){void 0!==this.barnesHutTree&&(t.lineWidth=1,this._drawBranch(this.barnesHutTree.root,t,e))},e._drawBranch=function(t,e,i){void 0===i&&(i="#FF0000"),4==t.childrenCount&&(this._drawBranch(t.children.NW,e),this._drawBranch(t.children.NE,e),this._drawBranch(t.children.SE,e),this._drawBranch(t.children.SW,e)),e.strokeStyle=i,e.beginPath(),e.moveTo(t.range.minX,t.range.minY),e.lineTo(t.range.maxX,t.range.minY),e.stroke(),e.beginPath(),e.moveTo(t.range.maxX,t.range.minY),e.lineTo(t.range.maxX,t.range.maxY),e.stroke(),e.beginPath(),e.moveTo(t.range.maxX,t.range.maxY),e.lineTo(t.range.minX,t.range.maxY),e.stroke(),e.beginPath(),e.moveTo(t.range.minX,t.range.maxY),e.lineTo(t.range.minX,t.range.minY),e.stroke()}},function(t){t.exports=function(t){return t.webpackPolyfill||(t.deprecate=function(){},t.paths=[],t.children=[],t.webpackPolyfill=1),t}}])}); +},t.exports=s},function(t){function e(t,e){this.x=void 0!==t?t:0,this.y=void 0!==e?e:0}t.exports=e},function(t){function e(t,e,i){this.x=void 0!==t?t:0,this.y=void 0!==e?e:0,this.z=void 0!==i?i:0}e.subtract=function(t,i){var s=new e;return s.x=t.x-i.x,s.y=t.y-i.y,s.z=t.z-i.z,s},e.add=function(t,i){var s=new e;return s.x=t.x+i.x,s.y=t.y+i.y,s.z=t.z+i.z,s},e.avg=function(t,i){return new e((t.x+i.x)/2,(t.y+i.y)/2,(t.z+i.z)/2)},e.crossProduct=function(t,i){var s=new e;return s.x=t.y*i.z-t.z*i.y,s.y=t.z*i.x-t.x*i.z,s.z=t.x*i.y-t.y*i.x,s},e.prototype.length=function(){return Math.sqrt(this.x*this.x+this.y*this.y+this.z*this.z)},t.exports=e},function(t,e,i){function s(t,e){if(void 0===t)throw"Error: No container element defined";if(this.container=t,this.visible=e&&void 0!=e.visible?e.visible:!0,this.visible){this.frame=document.createElement("DIV"),this.frame.style.width="100%",this.frame.style.position="relative",this.container.appendChild(this.frame),this.frame.prev=document.createElement("INPUT"),this.frame.prev.type="BUTTON",this.frame.prev.value="Prev",this.frame.appendChild(this.frame.prev),this.frame.play=document.createElement("INPUT"),this.frame.play.type="BUTTON",this.frame.play.value="Play",this.frame.appendChild(this.frame.play),this.frame.next=document.createElement("INPUT"),this.frame.next.type="BUTTON",this.frame.next.value="Next",this.frame.appendChild(this.frame.next),this.frame.bar=document.createElement("INPUT"),this.frame.bar.type="BUTTON",this.frame.bar.style.position="absolute",this.frame.bar.style.border="1px solid red",this.frame.bar.style.width="100px",this.frame.bar.style.height="6px",this.frame.bar.style.borderRadius="2px",this.frame.bar.style.MozBorderRadius="2px",this.frame.bar.style.border="1px solid #7F7F7F",this.frame.bar.style.backgroundColor="#E5E5E5",this.frame.appendChild(this.frame.bar),this.frame.slide=document.createElement("INPUT"),this.frame.slide.type="BUTTON",this.frame.slide.style.margin="0px",this.frame.slide.value=" ",this.frame.slide.style.position="relative",this.frame.slide.style.left="-100px",this.frame.appendChild(this.frame.slide);var i=this;this.frame.slide.onmousedown=function(t){i._onMouseDown(t)},this.frame.prev.onclick=function(t){i.prev(t)},this.frame.play.onclick=function(t){i.togglePlay(t)},this.frame.next.onclick=function(t){i.next(t)}}this.onChangeCallback=void 0,this.values=[],this.index=void 0,this.playTimeout=void 0,this.playInterval=1e3,this.playLoop=!0}var o=i(1);s.prototype.prev=function(){var t=this.getIndex();t>0&&(t--,this.setIndex(t))},s.prototype.next=function(){var t=this.getIndex();t0?this.setIndex(0):this.index=void 0},s.prototype.setIndex=function(t){if(!(ts&&(s=0),s>this.values.length-1&&(s=this.values.length-1),s},s.prototype.indexToLeft=function(t){var e=parseFloat(this.frame.bar.style.width)-this.frame.slide.clientWidth-10,i=t/(this.values.length-1)*e,s=i+3;return s},s.prototype._onMouseMove=function(t){var e=t.clientX-this.startClientX,i=this.startSlideX+e,s=this.leftToIndex(i);this.setIndex(s),o.preventDefault()},s.prototype._onMouseUp=function(){this.frame.style.cursor="auto",o.removeEventListener(document,"mousemove",this.onmousemove),o.removeEventListener(document,"mouseup",this.onmouseup),o.preventDefault()},t.exports=s},function(t){function e(t,e,i,s){this._start=0,this._end=0,this._step=1,this.prettyStep=!0,this.precision=5,this._current=0,this.setRange(t,e,i,s)}e.prototype.setRange=function(t,e,i,s){this._start=t?t:0,this._end=e?e:0,this.setStep(i,s)},e.prototype.setStep=function(t,i){void 0===t||0>=t||(void 0!==i&&(this.prettyStep=i),this._step=this.prettyStep===!0?e.calculatePrettyStep(t):t)},e.calculatePrettyStep=function(t){var e=function(t){return Math.log(t)/Math.LN10},i=Math.pow(10,Math.round(e(t))),s=2*Math.pow(10,Math.round(e(t/2))),o=5*Math.pow(10,Math.round(e(t/5))),n=i;return Math.abs(s-t)<=Math.abs(n-t)&&(n=s),Math.abs(o-t)<=Math.abs(n-t)&&(n=o),0>=n&&(n=1),n},e.prototype.getCurrent=function(){return parseFloat(this._current.toPrecision(this.precision))},e.prototype.getStep=function(){return this._step},e.prototype.start=function(){this._current=this._start-this._start%this._step},e.prototype.next=function(){this._current+=this._step},e.prototype.end=function(){return this._current>this._end},t.exports=e},function(t,e,i){function s(t,e,i,r){if(!(this instanceof s))throw new SyntaxError("Constructor must be called with the new operator");if(!(Array.isArray(i)||i instanceof n)&&i instanceof Object){var h=r;r=i,i=h}var u=this;this.defaultOptions={start:null,end:null,autoResize:!0,orientation:"bottom",width:null,height:null,maxHeight:null,minHeight:null},this.options=o.deepExtend({},this.defaultOptions),this._create(t),this.components=[],this.body={dom:this.dom,domProps:this.props,emitter:{on:this.on.bind(this),off:this.off.bind(this),emit:this.emit.bind(this)},hiddenDates:[],util:{snap:null,toScreen:u._toScreen.bind(u),toGlobalScreen:u._toGlobalScreen.bind(u),toTime:u._toTime.bind(u),toGlobalTime:u._toGlobalTime.bind(u)}},this.range=new a(this.body),this.components.push(this.range),this.body.range=this.range,this.timeAxis=new d(this.body),this.components.push(this.timeAxis),this.body.util.snap=this.timeAxis.snap.bind(this.timeAxis),this.currentTime=new l(this.body),this.components.push(this.currentTime),this.customTime=new c(this.body),this.components.push(this.customTime),this.itemSet=new p(this.body),this.components.push(this.itemSet),this.itemsData=null,this.groupsData=null,r&&this.setOptions(r),i&&this.setGroups(i),e?this.setItems(e):this.redraw()}var o=(i(56),i(45),i(1)),n=i(3),r=i(4),a=i(17),h=i(46),d=i(30),l=i(21),c=i(22),p=i(27);s.prototype=new h,s.prototype.setItems=function(t){var e,i=null==this.itemsData;if(e=t?t instanceof n||t instanceof r?t:new n(t,{type:{start:"Date",end:"Date"}}):null,this.itemsData=e,this.itemSet&&this.itemSet.setItems(e),i)if(void 0!=this.options.start||void 0!=this.options.end){if(void 0==this.options.start||void 0==this.options.end)var s=this._getDataRange();var o=void 0!=this.options.start?this.options.start:s.start,a=void 0!=this.options.end?this.options.end:s.end;this.setWindow(o,a,{animate:!1})}else this.fit({animate:!1})},s.prototype.setGroups=function(t){var e;e=t?t instanceof n||t instanceof r?t:new n(t):null,this.groupsData=e,this.itemSet.setGroups(e)},s.prototype.setSelection=function(t,e){this.itemSet&&this.itemSet.setSelection(t),e&&e.focus&&this.focus(t,e)},s.prototype.getSelection=function(){return this.itemSet&&this.itemSet.getSelection()||[]},s.prototype.focus=function(t,e){if(this.itemsData&&void 0!=t){var i=Array.isArray(t)?t:[t],s=this.itemsData.getDataSet().get(i,{type:{start:"Date",end:"Date"}}),o=null,n=null;if(s.forEach(function(t){var e=t.start.valueOf(),i="end"in t?t.end.valueOf():t.start.valueOf();(null===o||o>e)&&(o=e),(null===n||i>n)&&(n=i)}),null!==o&&null!==n){var r=(o+n)/2,a=Math.max(this.range.end-this.range.start,1.1*(n-o)),h=e&&void 0!==e.animate?e.animate:!0;this.range.setRange(r-a/2,r+a/2,h)}}},s.prototype.getItemRange=function(){var t=this.itemsData.getDataSet(),e=null,i=null;if(t){var s=t.min("start");e=s?o.convert(s.start,"Date").valueOf():null;var n=t.max("start");n&&(i=o.convert(n.start,"Date").valueOf());var r=t.max("end");r&&(i=null==i?o.convert(r.end,"Date").valueOf():Math.max(i,o.convert(r.end,"Date").valueOf()))}return{min:null!=e?new Date(e):null,max:null!=i?new Date(i):null}},t.exports=s},function(t,e,i){function s(t,e,i,s){if(!(Array.isArray(i)||i instanceof n)&&i instanceof Object){var r=s;s=i,i=r}var h=this;this.defaultOptions={start:null,end:null,autoResize:!0,orientation:"bottom",width:null,height:null,maxHeight:null,minHeight:null},this.options=o.deepExtend({},this.defaultOptions),this._create(t),this.components=[],this.body={dom:this.dom,domProps:this.props,emitter:{on:this.on.bind(this),off:this.off.bind(this),emit:this.emit.bind(this)},hiddenDates:[],util:{snap:null,toScreen:h._toScreen.bind(h),toGlobalScreen:h._toGlobalScreen.bind(h),toTime:h._toTime.bind(h),toGlobalTime:h._toGlobalTime.bind(h)}},this.range=new a(this.body),this.components.push(this.range),this.body.range=this.range,this.timeAxis=new d(this.body),this.components.push(this.timeAxis),this.body.util.snap=this.timeAxis.snap.bind(this.timeAxis),this.currentTime=new l(this.body),this.components.push(this.currentTime),this.customTime=new c(this.body),this.components.push(this.customTime),this.linegraph=new p(this.body),this.components.push(this.linegraph),this.itemsData=null,this.groupsData=null,s&&this.setOptions(s),i&&this.setGroups(i),e?this.setItems(e):this.redraw()}var o=(i(56),i(45),i(1)),n=i(3),r=i(4),a=i(17),h=i(46),d=i(30),l=i(21),c=i(22),p=i(29);s.prototype=new h,s.prototype.setItems=function(t){var e,i=null==this.itemsData;if(e=t?t instanceof n||t instanceof r?t:new n(t,{type:{start:"Date",end:"Date"}}):null,this.itemsData=e,this.linegraph&&this.linegraph.setItems(e),i)if(void 0!=this.options.start||void 0!=this.options.end){var s=void 0!=this.options.start?this.options.start:null,o=void 0!=this.options.end?this.options.end:null;this.setWindow(s,o,{animate:!1})}else this.fit({animate:!1})},s.prototype.setGroups=function(t){var e;e=t?t instanceof n||t instanceof r?t:new n(t):null,this.groupsData=e,this.linegraph.setGroups(e)},s.prototype.getLegend=function(t,e,i){return void 0===e&&(e=15),void 0===i&&(i=15),void 0!==this.linegraph.groups[t]?this.linegraph.groups[t].getLegend(e,i):"cannot find group:"+t},s.prototype.isGroupVisible=function(t){return void 0!==this.linegraph.groups[t]?this.linegraph.groups[t].visible&&(void 0===this.linegraph.options.groups.visibility[t]||1==this.linegraph.options.groups.visibility[t]):!1},s.prototype.getItemRange=function(){var t=null,e=null;for(var i in this.linegraph.groups)if(this.linegraph.groups.hasOwnProperty(i)&&1==this.linegraph.groups[i].visible)for(var s=0;sr?r:t,e=null==e?r:r>e?r:e}return{min:null!=t?new Date(t):null,max:null!=e?new Date(e):null}},t.exports=s},function(t,e,i){var s=i(44);e.convertHiddenOptions=function(t,e){if(t.hiddenDates=[],e&&1==Array.isArray(e)){for(var i=0;i=4*a){var p=0,u=n.clone();switch(i[h].repeat){case"daily":d.day()!=l.day()&&(p=1),d.dayOfYear(o.dayOfYear()),d.year(o.year()),d.subtract(7,"days"),l.dayOfYear(o.dayOfYear()),l.year(o.year()),l.subtract(7-p,"days"),u.add(1,"weeks");break;case"weekly":var m=l.diff(d,"days"),f=d.day();d.date(o.date()),d.month(o.month()),d.year(o.year()),l=d.clone(),d.day(f),l.day(f),l.add(m,"days"),d.subtract(1,"weeks"),l.subtract(1,"weeks"),u.add(1,"weeks");break;case"monthly":d.month()!=l.month()&&(p=1),d.month(o.month()),d.year(o.year()),d.subtract(1,"months"),l.month(o.month()),l.year(o.year()),l.subtract(1,"months"),l.add(p,"months"),u.add(1,"months");break;case"yearly":d.year()!=l.year()&&(p=1),d.year(o.year()),d.subtract(1,"years"),l.year(o.year()),l.subtract(1,"years"),l.add(p,"years"),u.add(1,"years");break;default:return void console.log("Wrong repeat format, allowed are: daily, weekly, monthly, yearly. Given:",i[h].repeat)}for(;u>d;)switch(t.hiddenDates.push({start:d.valueOf(),end:l.valueOf()}),i[h].repeat){case"daily":d.add(1,"days"),l.add(1,"days");break;case"weekly":d.add(1,"weeks"),l.add(1,"weeks");break;case"monthly":d.add(1,"months"),l.add(1,"months");break;case"yearly":d.add(1,"y"),l.add(1,"y");break;default:return void console.log("Wrong repeat format, allowed are: daily, weekly, monthly, yearly. Given:",i[h].repeat)}t.hiddenDates.push({start:d.valueOf(),end:l.valueOf()})}}e.removeDuplicates(t);var g=e.isHidden(t.range.start,t.hiddenDates),v=e.isHidden(t.range.end,t.hiddenDates),y=t.range.start,b=t.range.end;1==g.hidden&&(y=1==t.range.startToFront?g.startDate-1:g.endDate+1),1==v.hidden&&(b=1==t.range.endToFront?v.startDate-1:v.endDate+1),(1==g.hidden||1==v.hidden)&&t.range._applyRange(y,b)}},e.removeDuplicates=function(t){for(var e=t.hiddenDates,i=[],s=0;s=e[s].start&&e[o].end<=e[s].end?e[o].remove=!0:e[o].start>=e[s].start&&e[o].start<=e[s].end?(e[s].end=e[o].end,e[o].remove=!0):e[o].end>=e[s].start&&e[o].end<=e[s].end&&(e[s].start=e[o].start,e[o].remove=!0));for(var s=0;s=r&&a>o){i=!0;break}}if(1==i&&o=e&&i>r&&(s+=r-n)}return s},e.correctTimeForHidden=function(t,i,o){return o=s(o).toDate().valueOf(),o-=e.getHiddenDurationBefore(t,i,o)},e.getHiddenDurationBefore=function(t,e,i){var o=0;i=s(i).toDate().valueOf();for(var n=0;n=e.start&&a=a&&(o+=a-r)}return o},e.getAccumulatedHiddenDuration=function(t,e,i){for(var s=0,o=0,n=e.start,r=0;r=e.start&&h=i)break;s+=h-a}}return s},e.snapAwayFromHidden=function(t,i,s,o){var n=e.isHidden(i,t);return 1==n.hidden?0>s?1==o?n.startDate-(n.endDate-i)-1:n.startDate-1:1==o?n.endDate+(i-n.startDate)+1:n.endDate+1:i},e.isHidden=function(t,e){for(var i=0;i=s&&o>t)return{hidden:!0,startDate:s,endDate:o}}return{hidden:!1,startDate:s,endDate:o}}},function(t){function e(t,e,i,s,o,n){this.current=0,this.autoScale=!0,this.stepIndex=0,this.step=1,this.scale=1,this.marginStart,this.marginEnd,this.deadSpace=0,this.majorSteps=[1,2,5,10],this.minorSteps=[.25,.5,1,2],this.alignZeros=n,this.setRange(t,e,i,s,o)}e.prototype.setRange=function(t,e,i,s,o){this._start=void 0===o.min?t:o.min,this._end=void 0===o.max?e:o.max,this._start==this._end&&(this._start-=.75,this._end+=1),1==this.autoScale&&this.setMinimumStep(i,s),this.setFirst(o)},e.prototype.setMinimumStep=function(t,e){var i=this._end-this._start,s=1.2*i,o=t*(s/e),n=Math.round(Math.log(s)/Math.LN10),r=-1,a=Math.pow(10,n),h=0;0>n&&(h=n);for(var d=!1,l=h;Math.abs(l)<=Math.abs(n);l++){a=Math.pow(10,l);for(var c=0;c=o){d=!0,r=c;break}}if(1==d)break}this.stepIndex=r,this.scale=a,this.step=a*this.minorSteps[r]},e.prototype.setFirst=function(t){void 0===t&&(t={});var e=void 0===t.min?this._start-2*this.scale*this.minorSteps[this.stepIndex]:t.min,i=void 0===t.max?this._end+this.scale*this.minorSteps[this.stepIndex]:t.max;this.marginEnd=void 0===t.max?this.roundToMinor(i):t.max,this.marginStart=void 0===t.min?this.roundToMinor(e):t.min,1==this.alignZeros&&(this.marginEnd-this.marginStart)%this.step!=0&&(this.marginEnd+=this.marginEnd%this.step),this.deadSpace=this.roundToMinor(i)-i+this.roundToMinor(e)-e,this.marginRange=this.marginEnd-this.marginStart,this.current=this.marginEnd},e.prototype.roundToMinor=function(t){var e=t-t%(this.scale*this.minorSteps[this.stepIndex]);return t%(this.scale*this.minorSteps[this.stepIndex])>.5*this.scale*this.minorSteps[this.stepIndex]?e+this.scale*this.minorSteps[this.stepIndex]:e},e.prototype.hasNext=function(){return this.current>=this.marginStart},e.prototype.next=function(){var t=this.current;this.current-=this.step,this.current==t&&(this.current=this._end)},e.prototype.previous=function(){this.current+=this.step,this.marginEnd+=this.step,this.marginRange=this.marginEnd-this.marginStart},e.prototype.getCurrent=function(t){var e=Math.abs(this.current)0;s--){if("0"!=i[s]){if("."==i[s]||","==i[s]){i=i.slice(0,s);break}break}i=i.slice(0,s)}}else{var o="",n=i.indexOf("e");if(-1!=n&&(o=i.slice(n),i=i.slice(0,n)),n=Math.max(i.indexOf(","),i.indexOf(".")),-1===n?(0!==t&&(i+="."),n=i.length+t):0!==t&&(n+=t+1),n>i.length)for(var r=n-i.length;r>0;r--)i+="0";else i=i.slice(0,n);i+=o}return i},e.prototype.snap=function(){},e.prototype.isMajor=function(){return this.current%(this.scale*this.majorSteps[this.stepIndex])==0},t.exports=e},function(t,e,i){function s(t,e){var i=h().hours(0).minutes(0).seconds(0).milliseconds(0);this.start=i.clone().add(-3,"days").valueOf(),this.end=i.clone().add(4,"days").valueOf(),this.body=t,this.deltaDifference=0,this.scaleOffset=0,this.startToFront=!1,this.endToFront=!0,this.defaultOptions={start:null,end:null,direction:"horizontal",moveable:!0,zoomable:!0,min:null,max:null,zoomMin:10,zoomMax:31536e10},this.options=r.extend({},this.defaultOptions),this.props={touch:{}},this.animateTimer=null,this.body.emitter.on("dragstart",this._onDragStart.bind(this)),this.body.emitter.on("drag",this._onDrag.bind(this)),this.body.emitter.on("dragend",this._onDragEnd.bind(this)),this.body.emitter.on("hold",this._onHold.bind(this)),this.body.emitter.on("mousewheel",this._onMouseWheel.bind(this)),this.body.emitter.on("DOMMouseScroll",this._onMouseWheel.bind(this)),this.body.emitter.on("touch",this._onTouch.bind(this)),this.body.emitter.on("pinch",this._onPinch.bind(this)),this.setOptions(e)}function o(t){if("horizontal"!=t&&"vertical"!=t)throw new TypeError('Unknown direction "'+t+'". Choose "horizontal" or "vertical".')}function n(t,e){return{x:t.pageX-r.getAbsoluteLeft(e),y:t.pageY-r.getAbsoluteTop(e)}}var r=i(1),a=i(47),h=i(44),d=i(20),l=i(15);s.prototype=new d,s.prototype.setOptions=function(t){if(t){var e=["direction","min","max","zoomMin","zoomMax","moveable","zoomable","activate","hiddenDates"];r.selectiveExtend(e,this.options,t),("start"in t||"end"in t)&&this.setRange(t.start,t.end)}},s.prototype.setRange=function(t,e,i,s){s!==!0&&(s=!1);var o=void 0!=t?r.convert(t,"Date").valueOf():null,n=void 0!=e?r.convert(e,"Date").valueOf():null;if(this._cancelAnimation(),i){var a=this,h=this.start,d=this.end,c="number"==typeof i?i:500,p=(new Date).valueOf(),u=!1,m=function(){if(!a.props.touch.dragging){var t=(new Date).valueOf(),e=t-p,i=e>c,g=i||null===o?o:r.easeInOutQuad(e,h,o,c),v=i||null===n?n:r.easeInOutQuad(e,d,n,c);f=a._applyRange(g,v),l.updateHiddenDates(a.body,a.options.hiddenDates),u=u||f,f&&a.body.emitter.emit("rangechange",{start:new Date(a.start),end:new Date(a.end),byUser:s}),i?u&&a.body.emitter.emit("rangechanged",{start:new Date(a.start),end:new Date(a.end),byUser:s}):a.animateTimer=setTimeout(m,20)}};return m()}var f=this._applyRange(o,n);if(l.updateHiddenDates(this.body,this.options.hiddenDates),f){var g={start:new Date(this.start),end:new Date(this.end),byUser:s};this.body.emitter.emit("rangechange",g),this.body.emitter.emit("rangechanged",g)}},s.prototype._cancelAnimation=function(){this.animateTimer&&(clearTimeout(this.animateTimer),this.animateTimer=null)},s.prototype._applyRange=function(t,e){var i,s=null!=t?r.convert(t,"Date").valueOf():this.start,o=null!=e?r.convert(e,"Date").valueOf():this.end,n=null!=this.options.max?r.convert(this.options.max,"Date").valueOf():null,a=null!=this.options.min?r.convert(this.options.min,"Date").valueOf():null;if(isNaN(s)||null===s)throw new Error('Invalid start "'+t+'"');if(isNaN(o)||null===o)throw new Error('Invalid end "'+e+'"');if(s>o&&(o=s),null!==a&&a>s&&(i=a-s,s+=i,o+=i,null!=n&&o>n&&(o=n)),null!==n&&o>n&&(i=o-n,s-=i,o-=i,null!=a&&a>s&&(s=a)),null!==this.options.zoomMin){var h=parseFloat(this.options.zoomMin);0>h&&(h=0),h>o-s&&(this.end-this.start===h?(s=this.start,o=this.end):(i=h-(o-s),s-=i/2,o+=i/2))}if(null!==this.options.zoomMax){var d=parseFloat(this.options.zoomMax);0>d&&(d=0),o-s>d&&(this.end-this.start===d?(s=this.start,o=this.end):(i=o-s-d,s+=i/2,o-=i/2))}var l=this.start!=s||this.end!=o;return s>=this.start&&s<=this.end||o>=this.start&&o<=this.end||this.start>=s&&this.start<=o||this.end>=s&&this.end<=o||this.body.emitter.emit("checkRangedItems"),this.start=s,this.end=o,l},s.prototype.getRange=function(){return{start:this.start,end:this.end}},s.prototype.conversion=function(t,e){return s.conversion(this.start,this.end,t,e)},s.conversion=function(t,e,i,s){return void 0===s&&(s=0),0!=i&&e-t!=0?{offset:t,scale:i/(e-t-s)}:{offset:0,scale:1}},s.prototype._onDragStart=function(){this.deltaDifference=0,this.previousDelta=0,this.options.moveable&&this.props.touch.allowDragging&&(this.props.touch.start=this.start,this.props.touch.end=this.end,this.props.touch.dragging=!0,this.body.dom.root&&(this.body.dom.root.style.cursor="move"))},s.prototype._onDrag=function(t){if(this.options.moveable&&this.props.touch.allowDragging){var e=this.options.direction;o(e);var i="horizontal"==e?t.gesture.deltaX:t.gesture.deltaY;i-=this.deltaDifference;var s=this.props.touch.end-this.props.touch.start,n=l.getHiddenDurationBetween(this.body.hiddenDates,this.start,this.end);s-=n;var r="horizontal"==e?this.body.domProps.center.width:this.body.domProps.center.height,a=-i/r*s,h=this.props.touch.start+a,d=this.props.touch.end+a,c=l.snapAwayFromHidden(this.body.hiddenDates,h,this.previousDelta-i,!0),p=l.snapAwayFromHidden(this.body.hiddenDates,d,this.previousDelta-i,!0);if(c!=h||p!=d)return this.deltaDifference+=i,this.props.touch.start=c,this.props.touch.end=p,void this._onDrag(t);this.previousDelta=i,this._applyRange(h,d),this.body.emitter.emit("rangechange",{start:new Date(this.start),end:new Date(this.end),byUser:!0})}},s.prototype._onDragEnd=function(){this.options.moveable&&this.props.touch.allowDragging&&(this.props.touch.dragging=!1,this.body.dom.root&&(this.body.dom.root.style.cursor="auto"),this.body.emitter.emit("rangechanged",{start:new Date(this.start),end:new Date(this.end),byUser:!0}))},s.prototype._onMouseWheel=function(t){if(this.options.zoomable&&this.options.moveable){var e=0;if(t.wheelDelta?e=t.wheelDelta/120:t.detail&&(e=-t.detail/3),e){var i;i=0>e?1-e/5:1/(1+e/5);var s=a.fakeGesture(this,t),o=n(s.center,this.body.dom.center),r=this._pointerToDate(o);this.zoom(i,r,e)}t.preventDefault()}},s.prototype._onTouch=function(){this.props.touch.start=this.start,this.props.touch.end=this.end,this.props.touch.allowDragging=!0,this.props.touch.center=null,this.scaleOffset=0,this.deltaDifference=0},s.prototype._onHold=function(){this.props.touch.allowDragging=!1},s.prototype._onPinch=function(t){if(this.options.zoomable&&this.options.moveable&&(this.props.touch.allowDragging=!1,t.gesture.touches.length>1)){this.props.touch.center||(this.props.touch.center=n(t.gesture.center,this.body.dom.center));var e=1/(t.gesture.scale+this.scaleOffset),i=this._pointerToDate(this.props.touch.center),s=l.getHiddenDurationBetween(this.body.hiddenDates,this.start,this.end),o=l.getHiddenDurationBefore(this.body.hiddenDates,this,i),r=s-o,a=i-o+(this.props.touch.start-(i-o))*e,h=i+r+(this.props.touch.end-(i+r))*e;this.startToFront=1-e>0?!1:!0,this.endToFront=e-1>0?!1:!0;var d=l.snapAwayFromHidden(this.body.hiddenDates,a,1-e,!0),c=l.snapAwayFromHidden(this.body.hiddenDates,h,e-1,!0);(d!=a||c!=h)&&(this.props.touch.start=d,this.props.touch.end=c,this.scaleOffset=1-t.gesture.scale,a=d,h=c),this.setRange(a,h,!1,!0),this.startToFront=!1,this.endToFront=!0}},s.prototype._pointerToDate=function(t){var e,i=this.options.direction;if(o(i),"horizontal"==i)return this.body.util.toTime(t.x).valueOf();var s=this.body.domProps.center.height;return e=this.conversion(s),t.y/e.scale+e.offset},s.prototype.zoom=function(t,e,i){null==e&&(e=(this.start+this.end)/2);var s=l.getHiddenDurationBetween(this.body.hiddenDates,this.start,this.end),o=l.getHiddenDurationBefore(this.body.hiddenDates,this,e),n=s-o,r=e-o+(this.start-(e-o))*t,a=e+n+(this.end-(e+n))*t;this.startToFront=i>0?!1:!0,this.endToFront=-i>0?!1:!0;var h=l.snapAwayFromHidden(this.body.hiddenDates,r,i,!0),d=l.snapAwayFromHidden(this.body.hiddenDates,a,-i,!0);(h!=r||d!=a)&&(r=h,a=d),this.setRange(r,a,!1,!0),this.startToFront=!1,this.endToFront=!0},s.prototype.move=function(t){var e=this.end-this.start,i=this.start+e*t,s=this.end+e*t;this.start=i,this.end=s},s.prototype.moveTo=function(t){var e=(this.start+this.end)/2,i=e-t,s=this.start-i,o=this.end-i;this.setRange(s,o)},t.exports=s},function(t,e){var i=.001;e.orderByStart=function(t){t.sort(function(t,e){return t.data.start-e.data.start})},e.orderByEnd=function(t){t.sort(function(t,e){var i="end"in t.data?t.data.end:t.data.start,s="end"in e.data?e.data.end:e.data.start;return i-s})},e.stack=function(t,i,s){var o,n;if(s)for(o=0,n=t.length;n>o;o++)t[o].top=null;for(o=0,n=t.length;n>o;o++){var r=t[o];if(r.stack&&null===r.top){r.top=i.axis;do{for(var a=null,h=0,d=t.length;d>h;h++){var l=t[h];if(null!==l.top&&l!==r&&l.stack&&e.collision(r,l,i.item)){a=l;break}}null!=a&&(r.top=a.top+a.height+i.item.vertical)}while(a)}}},e.nostack=function(t,e,i){var s,o,n;for(s=0,o=t.length;o>s;s++)if(void 0!==t[s].data.subgroup){n=e.axis;for(var r in i)i.hasOwnProperty(r)&&1==i[r].visible&&i[r].indexe.left&&t.top-s.vertical+ie.top}},function(t,e,i){function s(t,e,i,o){this.current=new Date,this._start=new Date,this._end=new Date,this.autoScale=!0,this.scale="day",this.step=1,this.setRange(t,e,i),this.switchedDay=!1,this.switchedMonth=!1,this.switchedYear=!1,this.hiddenDates=o,void 0===o&&(this.hiddenDates=[]),this.format=s.FORMAT}var o=i(44),n=i(15),r=i(1);s.FORMAT={minorLabels:{millisecond:"SSS",second:"s",minute:"HH:mm",hour:"HH:mm",weekday:"ddd D",day:"D",month:"MMM",year:"YYYY"},majorLabels:{millisecond:"HH:mm:ss",second:"D MMMM HH:mm",minute:"ddd D MMMM",hour:"ddd D MMMM",weekday:"MMMM YYYY",day:"MMMM YYYY",month:"YYYY",year:""}},s.prototype.setFormat=function(t){var e=r.deepExtend({},s.FORMAT);this.format=r.deepExtend(e,t)},s.prototype.setRange=function(t,e,i){if(!(t instanceof Date&&e instanceof Date))throw"No legal start or end date in method setRange";this._start=void 0!=t?new Date(t.valueOf()):new Date,this._end=void 0!=e?new Date(e.valueOf()):new Date,this.autoScale&&this.setMinimumStep(i)},s.prototype.first=function(){this.current=new Date(this._start.valueOf()),this.roundToMinor()},s.prototype.roundToMinor=function(){switch(this.scale){case"year":this.current.setFullYear(this.step*Math.floor(this.current.getFullYear()/this.step)),this.current.setMonth(0);case"month":this.current.setDate(1);case"day":case"weekday":this.current.setHours(0);case"hour":this.current.setMinutes(0);case"minute":this.current.setSeconds(0);case"second":this.current.setMilliseconds(0)}if(1!=this.step)switch(this.scale){case"millisecond":this.current.setMilliseconds(this.current.getMilliseconds()-this.current.getMilliseconds()%this.step);break;case"second":this.current.setSeconds(this.current.getSeconds()-this.current.getSeconds()%this.step);break;case"minute":this.current.setMinutes(this.current.getMinutes()-this.current.getMinutes()%this.step);break;case"hour":this.current.setHours(this.current.getHours()-this.current.getHours()%this.step);break;case"weekday":case"day":this.current.setDate(this.current.getDate()-1-(this.current.getDate()-1)%this.step+1);break;case"month":this.current.setMonth(this.current.getMonth()-this.current.getMonth()%this.step);break;case"year":this.current.setFullYear(this.current.getFullYear()-this.current.getFullYear()%this.step)}},s.prototype.hasNext=function(){return this.current.valueOf()<=this._end.valueOf()},s.prototype.next=function(){var t=this.current.valueOf();if(this.current.getMonth()<6)switch(this.scale){case"millisecond":this.current=new Date(this.current.valueOf()+this.step);break;case"second":this.current=new Date(this.current.valueOf()+1e3*this.step);break;case"minute":this.current=new Date(this.current.valueOf()+1e3*this.step*60);break;case"hour":this.current=new Date(this.current.valueOf()+1e3*this.step*60*60);var e=this.current.getHours();this.current.setHours(e-e%this.step);break;case"weekday":case"day":this.current.setDate(this.current.getDate()+this.step); +break;case"month":this.current.setMonth(this.current.getMonth()+this.step);break;case"year":this.current.setFullYear(this.current.getFullYear()+this.step)}else switch(this.scale){case"millisecond":this.current=new Date(this.current.valueOf()+this.step);break;case"second":this.current.setSeconds(this.current.getSeconds()+this.step);break;case"minute":this.current.setMinutes(this.current.getMinutes()+this.step);break;case"hour":this.current.setHours(this.current.getHours()+this.step);break;case"weekday":case"day":this.current.setDate(this.current.getDate()+this.step);break;case"month":this.current.setMonth(this.current.getMonth()+this.step);break;case"year":this.current.setFullYear(this.current.getFullYear()+this.step)}if(1!=this.step)switch(this.scale){case"millisecond":this.current.getMilliseconds()0?t.step:1,this.autoScale=!1)},s.prototype.setAutoScale=function(t){this.autoScale=t},s.prototype.setMinimumStep=function(t){if(void 0!=t){var e=31104e6,i=2592e6,s=864e5,o=36e5,n=6e4,r=1e3,a=1;1e3*e>t&&(this.scale="year",this.step=1e3),500*e>t&&(this.scale="year",this.step=500),100*e>t&&(this.scale="year",this.step=100),50*e>t&&(this.scale="year",this.step=50),10*e>t&&(this.scale="year",this.step=10),5*e>t&&(this.scale="year",this.step=5),e>t&&(this.scale="year",this.step=1),3*i>t&&(this.scale="month",this.step=3),i>t&&(this.scale="month",this.step=1),5*s>t&&(this.scale="day",this.step=5),2*s>t&&(this.scale="day",this.step=2),s>t&&(this.scale="day",this.step=1),s/2>t&&(this.scale="weekday",this.step=1),4*o>t&&(this.scale="hour",this.step=4),o>t&&(this.scale="hour",this.step=1),15*n>t&&(this.scale="minute",this.step=15),10*n>t&&(this.scale="minute",this.step=10),5*n>t&&(this.scale="minute",this.step=5),n>t&&(this.scale="minute",this.step=1),15*r>t&&(this.scale="second",this.step=15),10*r>t&&(this.scale="second",this.step=10),5*r>t&&(this.scale="second",this.step=5),r>t&&(this.scale="second",this.step=1),200*a>t&&(this.scale="millisecond",this.step=200),100*a>t&&(this.scale="millisecond",this.step=100),50*a>t&&(this.scale="millisecond",this.step=50),10*a>t&&(this.scale="millisecond",this.step=10),5*a>t&&(this.scale="millisecond",this.step=5),a>t&&(this.scale="millisecond",this.step=1)}},s.prototype.snap=function(t){var e=new Date(t.valueOf());if("year"==this.scale){var i=e.getFullYear()+Math.round(e.getMonth()/12);e.setFullYear(Math.round(i/this.step)*this.step),e.setMonth(0),e.setDate(0),e.setHours(0),e.setMinutes(0),e.setSeconds(0),e.setMilliseconds(0)}else if("month"==this.scale)e.getDate()>15?(e.setDate(1),e.setMonth(e.getMonth()+1)):e.setDate(1),e.setHours(0),e.setMinutes(0),e.setSeconds(0),e.setMilliseconds(0);else if("day"==this.scale){switch(this.step){case 5:case 2:e.setHours(24*Math.round(e.getHours()/24));break;default:e.setHours(12*Math.round(e.getHours()/12))}e.setMinutes(0),e.setSeconds(0),e.setMilliseconds(0)}else if("weekday"==this.scale){switch(this.step){case 5:case 2:e.setHours(12*Math.round(e.getHours()/12));break;default:e.setHours(6*Math.round(e.getHours()/6))}e.setMinutes(0),e.setSeconds(0),e.setMilliseconds(0)}else if("hour"==this.scale){switch(this.step){case 4:e.setMinutes(60*Math.round(e.getMinutes()/60));break;default:e.setMinutes(30*Math.round(e.getMinutes()/30))}e.setSeconds(0),e.setMilliseconds(0)}else if("minute"==this.scale){switch(this.step){case 15:case 10:e.setMinutes(5*Math.round(e.getMinutes()/5)),e.setSeconds(0);break;case 5:e.setSeconds(60*Math.round(e.getSeconds()/60));break;default:e.setSeconds(30*Math.round(e.getSeconds()/30))}e.setMilliseconds(0)}else if("second"==this.scale)switch(this.step){case 15:case 10:e.setSeconds(5*Math.round(e.getSeconds()/5)),e.setMilliseconds(0);break;case 5:e.setMilliseconds(1e3*Math.round(e.getMilliseconds()/1e3));break;default:e.setMilliseconds(500*Math.round(e.getMilliseconds()/500))}else if("millisecond"==this.scale){var s=this.step>5?this.step/2:1;e.setMilliseconds(Math.round(e.getMilliseconds()/s)*s)}return e},s.prototype.isMajor=function(){if(1==this.switchedYear)switch(this.switchedYear=!1,this.scale){case"year":case"month":case"weekday":case"day":case"hour":case"minute":case"second":case"millisecond":return!0;default:return!1}else if(1==this.switchedMonth)switch(this.switchedMonth=!1,this.scale){case"weekday":case"day":case"hour":case"minute":case"second":case"millisecond":return!0;default:return!1}else if(1==this.switchedDay)switch(this.switchedDay=!1,this.scale){case"millisecond":case"second":case"minute":case"hour":return!0;default:return!1}switch(this.scale){case"millisecond":return 0==this.current.getMilliseconds();case"second":return 0==this.current.getSeconds();case"minute":return 0==this.current.getHours()&&0==this.current.getMinutes();case"hour":return 0==this.current.getHours();case"weekday":case"day":return 1==this.current.getDate();case"month":return 0==this.current.getMonth();case"year":return!1;default:return!1}},s.prototype.getLabelMinor=function(t){void 0==t&&(t=this.current);var e=this.format.minorLabels[this.scale];return e&&e.length>0?o(t).format(e):""},s.prototype.getLabelMajor=function(t){void 0==t&&(t=this.current);var e=this.format.majorLabels[this.scale];return e&&e.length>0?o(t).format(e):""},s.prototype.getClassName=function(){function t(t){return t/h%2==0?" even":" odd"}function e(t){return t.isSame(new Date,"day")?" today":t.isSame(o().add(1,"day"),"day")?" tomorrow":t.isSame(o().add(-1,"day"),"day")?" yesterday":""}function i(t){return t.isSame(new Date,"week")?" current-week":""}function s(t){return t.isSame(new Date,"month")?" current-month":""}function n(t){return t.isSame(new Date,"year")?" current-year":""}var r=o(this.current),a=r.locale?r.locale("en"):r.lang("en"),h=this.step;switch(this.scale){case"millisecond":return t(a.milliseconds()).trim();case"second":return t(a.seconds()).trim();case"minute":return t(a.minutes()).trim();case"hour":var d=a.hours();return 4==this.step&&(d=d+"-"+(d+4)),d+"h"+e(a)+t(a.hours());case"weekday":return a.format("dddd").toLowerCase()+e(a)+i(a)+t(a.date());case"day":var l=a.date(),c=a.format("MMMM").toLowerCase();return"day"+l+" "+c+s(a)+t(l-1);case"month":return a.format("MMMM").toLowerCase()+s(a)+t(a.month());case"year":var p=a.year();return"year"+p+n(a)+t(p);default:return""}},t.exports=s},function(t){function e(){this.options=null,this.props=null}e.prototype.setOptions=function(t){t&&util.extend(this.options,t)},e.prototype.redraw=function(){return!1},e.prototype.destroy=function(){},e.prototype._isResized=function(){var t=this.props._previousWidth!==this.props.width||this.props._previousHeight!==this.props.height;return this.props._previousWidth=this.props.width,this.props._previousHeight=this.props.height,t},t.exports=e},function(t,e,i){function s(t,e){this.body=t,this.defaultOptions={showCurrentTime:!0,locales:a,locale:"en"},this.options=o.extend({},this.defaultOptions),this.offset=0,this._create(),this.setOptions(e)}var o=i(1),n=i(20),r=i(44),a=i(48);s.prototype=new n,s.prototype._create=function(){var t=document.createElement("div");t.className="currenttime",t.style.position="absolute",t.style.top="0px",t.style.height="100%",this.bar=t},s.prototype.destroy=function(){this.options.showCurrentTime=!1,this.redraw(),this.body=null},s.prototype.setOptions=function(t){t&&o.selectiveExtend(["showCurrentTime","locale","locales"],this.options,t)},s.prototype.redraw=function(){if(this.options.showCurrentTime){var t=this.body.dom.backgroundVertical;this.bar.parentNode!=t&&(this.bar.parentNode&&this.bar.parentNode.removeChild(this.bar),t.appendChild(this.bar),this.start());var e=new Date((new Date).valueOf()+this.offset),i=this.body.util.toScreen(e),s=this.options.locales[this.options.locale],o=s.current+" "+s.time+": "+r(e).format("dddd, MMMM Do YYYY, H:mm:ss");o=o.charAt(0).toUpperCase()+o.substring(1),this.bar.style.left=i+"px",this.bar.title=o}else this.bar.parentNode&&this.bar.parentNode.removeChild(this.bar),this.stop();return!1},s.prototype.start=function(){function t(){e.stop();var i=e.body.range.conversion(e.body.domProps.center.width).scale,s=1/i/10;30>s&&(s=30),s>1e3&&(s=1e3),e.redraw(),e.currentTimeTimer=setTimeout(t,s)}var e=this;t()},s.prototype.stop=function(){void 0!==this.currentTimeTimer&&(clearTimeout(this.currentTimeTimer),delete this.currentTimeTimer)},s.prototype.setCurrentTime=function(t){var e=o.convert(t,"Date").valueOf(),i=(new Date).valueOf();this.offset=e-i,this.redraw()},s.prototype.getCurrentTime=function(){return new Date((new Date).valueOf()+this.offset)},t.exports=s},function(t,e,i){function s(t,e){this.body=t,this.defaultOptions={showCustomTime:!1,locales:h,locale:"en"},this.options=n.extend({},this.defaultOptions),this.customTime=new Date,this.eventParams={},this._create(),this.setOptions(e)}var o=i(45),n=i(1),r=i(20),a=i(44),h=i(48);s.prototype=new r,s.prototype.setOptions=function(t){t&&n.selectiveExtend(["showCustomTime","locale","locales"],this.options,t)},s.prototype._create=function(){var t=document.createElement("div");t.className="customtime",t.style.position="absolute",t.style.top="0px",t.style.height="100%",this.bar=t;var e=document.createElement("div");e.style.position="relative",e.style.top="0px",e.style.left="-10px",e.style.height="100%",e.style.width="20px",t.appendChild(e),this.hammer=o(t,{prevent_default:!0}),this.hammer.on("dragstart",this._onDragStart.bind(this)),this.hammer.on("drag",this._onDrag.bind(this)),this.hammer.on("dragend",this._onDragEnd.bind(this))},s.prototype.destroy=function(){this.options.showCustomTime=!1,this.redraw(),this.hammer.enable(!1),this.hammer=null,this.body=null},s.prototype.redraw=function(){if(this.options.showCustomTime){var t=this.body.dom.backgroundVertical;this.bar.parentNode!=t&&(this.bar.parentNode&&this.bar.parentNode.removeChild(this.bar),t.appendChild(this.bar));var e=this.body.util.toScreen(this.customTime),i=this.options.locales[this.options.locale],s=i.time+": "+a(this.customTime).format("dddd, MMMM Do YYYY, H:mm:ss");s=s.charAt(0).toUpperCase()+s.substring(1),this.bar.style.left=e+"px",this.bar.title=s}else this.bar.parentNode&&this.bar.parentNode.removeChild(this.bar);return!1},s.prototype.setCustomTime=function(t){this.customTime=n.convert(t,"Date"),this.redraw()},s.prototype.getCustomTime=function(){return new Date(this.customTime.valueOf())},s.prototype._onDragStart=function(t){this.eventParams.dragging=!0,this.eventParams.customTime=this.customTime,t.stopPropagation(),t.preventDefault()},s.prototype._onDrag=function(t){if(this.eventParams.dragging){var e=t.gesture.deltaX,i=this.body.util.toScreen(this.eventParams.customTime)+e,s=this.body.util.toTime(i);this.setCustomTime(s),this.body.emitter.emit("timechange",{time:new Date(this.customTime.valueOf())}),t.stopPropagation(),t.preventDefault()}},s.prototype._onDragEnd=function(t){this.eventParams.dragging&&(this.body.emitter.emit("timechanged",{time:new Date(this.customTime.valueOf())}),t.stopPropagation(),t.preventDefault())},t.exports=s},function(t,e,i){function s(t,e,i,s){this.id=o.randomUUID(),this.body=t,this.defaultOptions={orientation:"left",showMinorLabels:!0,showMajorLabels:!0,icons:!0,majorLinesOffset:7,minorLinesOffset:4,labelOffsetX:10,labelOffsetY:2,iconWidth:20,width:"40px",visible:!0,alignZeros:!0,customRange:{left:{min:void 0,max:void 0},right:{min:void 0,max:void 0}},title:{left:{text:void 0},right:{text:void 0}},format:{left:{decimals:void 0},right:{decimals:void 0}}},this.linegraphOptions=s,this.linegraphSVG=i,this.props={},this.DOMelements={lines:{},labels:{},title:{}},this.dom={},this.range={start:0,end:0},this.options=o.extend({},this.defaultOptions),this.conversionFactor=1,this.setOptions(e),this.width=Number((""+this.options.width).replace("px","")),this.minWidth=this.width,this.height=this.linegraphSVG.offsetHeight,this.hidden=!1,this.stepPixels=25,this.stepPixelsForced=25,this.zeroCrossing=-1,this.lineOffset=0,this.master=!0,this.svgElements={},this.iconsRemoved=!1,this.groups={},this.amountOfGroups=0,this._create();var n=this;this.body.emitter.on("verticalDrag",function(){n.dom.lineContainer.style.top=n.body.domProps.scrollTop+"px"})}var o=i(1),n=i(2),r=i(20),a=i(16);s.prototype=new r,s.prototype.addGroup=function(t,e){this.groups.hasOwnProperty(t)||(this.groups[t]=e),this.amountOfGroups+=1},s.prototype.updateGroup=function(t,e){this.groups[t]=e},s.prototype.removeGroup=function(t){this.groups.hasOwnProperty(t)&&(delete this.groups[t],this.amountOfGroups-=1)},s.prototype.setOptions=function(t){if(t){var e=!1;this.options.orientation!=t.orientation&&void 0!==t.orientation&&(e=!0);var i=["orientation","showMinorLabels","showMajorLabels","icons","majorLinesOffset","minorLinesOffset","labelOffsetX","labelOffsetY","iconWidth","width","visible","customRange","title","format","alignZeros"];o.selectiveExtend(i,this.options,t),this.minWidth=Number((""+this.options.width).replace("px","")),1==e&&this.dom.frame&&(this.hide(),this.show())}},s.prototype._create=function(){this.dom.frame=document.createElement("div"),this.dom.frame.style.width=this.options.width,this.dom.frame.style.height=this.height,this.dom.lineContainer=document.createElement("div"),this.dom.lineContainer.style.width="100%",this.dom.lineContainer.style.height=this.height,this.dom.lineContainer.style.position="relative",this.svg=document.createElementNS("http://www.w3.org/2000/svg","svg"),this.svg.style.position="absolute",this.svg.style.top="0px",this.svg.style.height="100%",this.svg.style.width="100%",this.svg.style.display="block",this.dom.frame.appendChild(this.svg)},s.prototype._redrawGroupIcons=function(){n.prepareElements(this.svgElements);var t,e=this.options.iconWidth,i=15,s=4,o=s+.5*i;t="left"==this.options.orientation?s:this.width-e-s;for(var r in this.groups)this.groups.hasOwnProperty(r)&&(1!=this.groups[r].visible||void 0!==this.linegraphOptions.visibility[r]&&1!=this.linegraphOptions.visibility[r]||(this.groups[r].drawIcon(t,o,this.svgElements,this.svg,e,i),o+=i+s));n.cleanupElements(this.svgElements),this.iconsRemoved=!1},s.prototype._cleanupIcons=function(){0==this.iconsRemoved&&(n.prepareElements(this.svgElements),n.cleanupElements(this.svgElements),this.iconsRemoved=!0)},s.prototype.show=function(){this.hidden=!1,this.dom.frame.parentNode||("left"==this.options.orientation?this.body.dom.left.appendChild(this.dom.frame):this.body.dom.right.appendChild(this.dom.frame)),this.dom.lineContainer.parentNode||this.body.dom.backgroundHorizontal.appendChild(this.dom.lineContainer)},s.prototype.hide=function(){this.hidden=!0,this.dom.frame.parentNode&&this.dom.frame.parentNode.removeChild(this.dom.frame),this.dom.lineContainer.parentNode&&this.dom.lineContainer.parentNode.removeChild(this.dom.lineContainer)},s.prototype.setRange=function(t,e){0==this.master&&1==this.options.alignZeros&&-1!=this.zeroCrossing&&t>0&&(t=0),this.range.start=t,this.range.end=e},s.prototype.redraw=function(){var t=!1,e=0;this.dom.lineContainer.style.top=this.body.domProps.scrollTop+"px";for(var i in this.groups)this.groups.hasOwnProperty(i)&&(1!=this.groups[i].visible||void 0!==this.linegraphOptions.visibility[i]&&1!=this.linegraphOptions.visibility[i]||e++);if(0==this.amountOfGroups||0==e)this.hide();else{this.show(),this.height=Number(this.linegraphSVG.style.height.replace("px","")),this.dom.lineContainer.style.height=this.height+"px",this.width=1==this.options.visible?Number((""+this.options.width).replace("px","")):0;var s=this.props,o=this.dom.frame;o.className="dataaxis",this._calculateCharSize();var n=this.options.orientation,r=this.options.showMinorLabels,a=this.options.showMajorLabels;s.minorLabelHeight=r?s.minorCharHeight:0,s.majorLabelHeight=a?s.majorCharHeight:0,s.minorLineWidth=this.body.dom.backgroundHorizontal.offsetWidth-this.lineOffset-this.width+2*this.options.minorLinesOffset,s.minorLineHeight=1,s.majorLineWidth=this.body.dom.backgroundHorizontal.offsetWidth-this.lineOffset-this.width+2*this.options.majorLinesOffset,s.majorLineHeight=1,"left"==n?(o.style.top="0",o.style.left="0",o.style.bottom="",o.style.width=this.width+"px",o.style.height=this.height+"px",this.props.width=this.body.domProps.left.width,this.props.height=this.body.domProps.left.height):(o.style.top="",o.style.bottom="0",o.style.left="0",o.style.width=this.width+"px",o.style.height=this.height+"px",this.props.width=this.body.domProps.right.width,this.props.height=this.body.domProps.right.height),t=this._redrawLabels(),t=this._isResized()||t,1==this.options.icons?this._redrawGroupIcons():this._cleanupIcons(),this._redrawTitle(n)}return t},s.prototype._redrawLabels=function(){var t=!1;n.prepareElements(this.DOMelements.lines),n.prepareElements(this.DOMelements.labels);var e=this.options.orientation,i=this.master?this.props.majorCharHeight||10:this.stepPixelsForced,s=new a(this.range.start,this.range.end,i,this.dom.frame.offsetHeight,this.options.customRange[this.options.orientation],0==this.master&&this.options.alignZeros);this.step=s;var o=(this.dom.frame.offsetHeight-s.deadSpace*(this.dom.frame.offsetHeight/s.marginRange))/((s.marginRange-s.deadSpace)/s.step);this.stepPixels=o;var r=this.height/o,h=0;if(0==this.master){o=this.stepPixelsForced,h=Math.round(this.dom.frame.offsetHeight/o-r);for(var d=0;.5*h>d;d++)s.previous();if(r=this.height/o,-1!=this.zeroCrossing&&1==this.options.alignZeros){var l=s.marginEnd/s.step-this.zeroCrossing;if(l>0)for(var d=0;l>d;d++)s.next();else if(0>l)for(var d=0;-l>d;d++)s.previous()}}else r+=.25;this.valueAtZero=s.marginEnd;var c,p=0,u=1;void 0!==this.options.format[e]&&(c=this.options.format[e].decimals),this.maxLabelSize=0;for(var m=0;u=0&&this._redrawLabel(m-2,s.getCurrent(c),e,"yAxis major",this.props.majorCharHeight),this._redrawLine(m,e,"grid horizontal major",this.options.majorLinesOffset,this.props.majorLineWidth)):this._redrawLine(m,e,"grid horizontal minor",this.options.minorLinesOffset,this.props.minorLineWidth),1==this.master&&0==s.current&&(this.zeroCrossing=u),u++}this.conversionFactor=0==this.master?m/(this.valueAtZero-s.current):this.dom.frame.offsetHeight/s.marginRange;var g=0;void 0!==this.options.title[e]&&void 0!==this.options.title[e].text&&(g=this.props.titleCharHeight);var v=1==this.options.icons?Math.max(this.options.iconWidth,g)+this.options.labelOffsetX+15:g+this.options.labelOffsetX+15;return this.maxLabelSize>this.width-v&&1==this.options.visible?(this.width=this.maxLabelSize+v,this.options.width=this.width+"px",n.cleanupElements(this.DOMelements.lines),n.cleanupElements(this.DOMelements.labels),this.redraw(),t=!0):this.maxLabelSizethis.minWidth?(this.width=Math.max(this.minWidth,this.maxLabelSize+v),this.options.width=this.width+"px",n.cleanupElements(this.DOMelements.lines),n.cleanupElements(this.DOMelements.labels),this.redraw(),t=!0):(n.cleanupElements(this.DOMelements.lines),n.cleanupElements(this.DOMelements.labels),t=!1),t},s.prototype.convertValue=function(t){var e=this.valueAtZero-t,i=e*this.conversionFactor;return i},s.prototype._redrawLabel=function(t,e,i,s,o){var r=n.getDOMElement("div",this.DOMelements.labels,this.dom.frame);r.className=s,r.innerHTML=e,"left"==i?(r.style.left="-"+this.options.labelOffsetX+"px",r.style.textAlign="right"):(r.style.right="-"+this.options.labelOffsetX+"px",r.style.textAlign="left"),r.style.top=t-.5*o+this.options.labelOffsetY+"px",e+="";var a=Math.max(this.props.majorCharWidth,this.props.minorCharWidth);this.maxLabelSized;d++){var c=this.visibleItems[d];c.repositionY(e)}return s},s.prototype._calculateHeight=function(t){var e,i=this.visibleItems;this.resetSubgroups();var s=this;if(i.length){var n=i[0].top,r=i[0].top+i[0].height;if(o.forEach(i,function(t){n=Math.min(n,t.top),r=Math.max(r,t.top+t.height),void 0!==t.data.subgroup&&(s.subgroups[t.data.subgroup].height=Math.max(s.subgroups[t.data.subgroup].height,t.height),s.subgroups[t.data.subgroup].visible=!0)}),n>t.axis){var a=n-t.axis;r-=a,o.forEach(i,function(t){t.top-=a})}e=r+t.item.vertical/2}else e=t.axis+t.item.vertical;return e=Math.max(e,this.props.label.height)},s.prototype.show=function(){this.dom.label.parentNode||this.itemSet.dom.labelSet.appendChild(this.dom.label),this.dom.foreground.parentNode||this.itemSet.dom.foreground.appendChild(this.dom.foreground),this.dom.background.parentNode||this.itemSet.dom.background.appendChild(this.dom.background),this.dom.axis.parentNode||this.itemSet.dom.axis.appendChild(this.dom.axis)},s.prototype.hide=function(){var t=this.dom.label;t.parentNode&&t.parentNode.removeChild(t);var e=this.dom.foreground;e.parentNode&&e.parentNode.removeChild(e);var i=this.dom.background;i.parentNode&&i.parentNode.removeChild(i);var s=this.dom.axis;s.parentNode&&s.parentNode.removeChild(s)},s.prototype.add=function(t){if(this.items[t.id]=t,t.setParent(this),void 0!==t.data.subgroup&&(void 0===this.subgroups[t.data.subgroup]&&(this.subgroups[t.data.subgroup]={height:0,visible:!1,index:this.subgroupIndex,items:[]},this.subgroupIndex++),this.subgroups[t.data.subgroup].items.push(t)),this.orderSubgroups(),-1==this.visibleItems.indexOf(t)){var e=this.itemSet.body.range;this._checkIfVisible(t,this.visibleItems,e)}},s.prototype.orderSubgroups=function(){if(void 0!==this.subgroupOrderer){var t=[];if("string"==typeof this.subgroupOrderer){for(var e in this.subgroups)t.push({subgroup:e,sortField:this.subgroups[e].items[0].data[this.subgroupOrderer]});t.sort(function(t,e){return t.sortField-e.sortField})}else if("function"==typeof this.subgroupOrderer){for(var e in this.subgroups)t.push(this.subgroups[e].items[0].data);t.sort(this.subgroupOrderer)}if(t.length>0)for(var i=0;it?-1:l>=t?0:1};if(e.length>0)for(n=0;nl}),1==this.checkRangedItems)for(this.checkRangedItems=!1,n=0;nl})}for(n=0;n=0&&(n=e[r],!o(n));r--)void 0===s[n.id]&&(s[n.id]=!0,i.push(n));for(r=t+1;rs;s++){var n=this.visibleItems[s];n.repositionY(e)}return i},s.prototype.show=function(){this.dom.background.parentNode||this.itemSet.dom.background.appendChild(this.dom.background)},t.exports=s},function(t,e,i){function s(t,e){this.body=t,this.defaultOptions={type:null,orientation:"bottom",align:"auto",stack:!0,groupOrder:null,selectable:!0,editable:{updateTime:!1,updateGroup:!1,add:!1,remove:!1},onAdd:function(t,e){e(t)},onUpdate:function(t,e){e(t)},onMove:function(t,e){e(t)},onRemove:function(t,e){e(t)},onMoving:function(t,e){e(t)},margin:{item:{horizontal:10,vertical:10},axis:20},padding:5},this.options=n.extend({},this.defaultOptions),this.itemOptions={type:{start:"Date",end:"Date"}},this.conversion={toScreen:t.util.toScreen,toTime:t.util.toTime},this.dom={},this.props={},this.hammer=null;var i=this;this.itemsData=null,this.groupsData=null,this.itemListeners={add:function(t,e){i._onAdd(e.items)},update:function(t,e){i._onUpdate(e.items)},remove:function(t,e){i._onRemove(e.items)}},this.groupListeners={add:function(t,e){i._onAddGroups(e.items)},update:function(t,e){i._onUpdateGroups(e.items)},remove:function(t,e){i._onRemoveGroups(e.items)}},this.items={},this.groups={},this.groupIds=[],this.selection=[],this.stackDirty=!0,this.touchParams={},this._create(),this.setOptions(e)}var o=i(45),n=i(1),r=i(3),a=i(4),h=i(20),d=i(25),l=i(26),c=i(33),p=i(34),u=i(35),m=i(32),f="__ungrouped__",g="__background__";s.prototype=new h,s.types={background:m,box:c,range:u,point:p},s.prototype._create=function(){var t=document.createElement("div");t.className="itemset",t["timeline-itemset"]=this,this.dom.frame=t;var e=document.createElement("div");e.className="background",t.appendChild(e),this.dom.background=e;var i=document.createElement("div");i.className="foreground",t.appendChild(i),this.dom.foreground=i;var s=document.createElement("div");s.className="axis",this.dom.axis=s;var n=document.createElement("div");n.className="labelset",this.dom.labelSet=n,this._updateUngrouped();var r=new l(g,null,this);r.show(),this.groups[g]=r,this.hammer=o(this.body.dom.centerContainer,{preventDefault:!0}),this.hammer.on("touch",this._onTouch.bind(this)),this.hammer.on("dragstart",this._onDragStart.bind(this)),this.hammer.on("drag",this._onDrag.bind(this)),this.hammer.on("dragend",this._onDragEnd.bind(this)),this.hammer.on("tap",this._onSelectItem.bind(this)),this.hammer.on("hold",this._onMultiSelectItem.bind(this)),this.hammer.on("doubletap",this._onAddItem.bind(this)),this.show()},s.prototype.setOptions=function(t){if(t){var e=["type","align","orientation","padding","stack","selectable","groupOrder","dataAttributes","template","hide"];n.selectiveExtend(e,this.options,t),"margin"in t&&("number"==typeof t.margin?(this.options.margin.axis=t.margin,this.options.margin.item.horizontal=t.margin,this.options.margin.item.vertical=t.margin):"object"==typeof t.margin&&(n.selectiveExtend(["axis"],this.options.margin,t.margin),"item"in t.margin&&("number"==typeof t.margin.item?(this.options.margin.item.horizontal=t.margin.item,this.options.margin.item.vertical=t.margin.item):"object"==typeof t.margin.item&&n.selectiveExtend(["horizontal","vertical"],this.options.margin.item,t.margin.item)))),"editable"in t&&("boolean"==typeof t.editable?(this.options.editable.updateTime=t.editable,this.options.editable.updateGroup=t.editable,this.options.editable.add=t.editable,this.options.editable.remove=t.editable):"object"==typeof t.editable&&n.selectiveExtend(["updateTime","updateGroup","add","remove"],this.options.editable,t.editable));var i=function(e){var i=t[e];if(i){if(!(i instanceof Function))throw new Error("option "+e+" must be a function "+e+"(item, callback)");this.options[e]=i}}.bind(this);["onAdd","onUpdate","onRemove","onMove","onMoving"].forEach(i),this.markDirty()}},s.prototype.markDirty=function(){this.groupIds=[],this.stackDirty=!0},s.prototype.destroy=function(){this.hide(),this.setItems(null),this.setGroups(null),this.hammer=null,this.body=null,this.conversion=null},s.prototype.hide=function(){this.dom.frame.parentNode&&this.dom.frame.parentNode.removeChild(this.dom.frame),this.dom.axis.parentNode&&this.dom.axis.parentNode.removeChild(this.dom.axis),this.dom.labelSet.parentNode&&this.dom.labelSet.parentNode.removeChild(this.dom.labelSet)},s.prototype.show=function(){this.dom.frame.parentNode||this.body.dom.center.appendChild(this.dom.frame),this.dom.axis.parentNode||this.body.dom.backgroundVertical.appendChild(this.dom.axis),this.dom.labelSet.parentNode||this.body.dom.left.appendChild(this.dom.labelSet)},s.prototype.setSelection=function(t){var e,i,s,o;for(void 0==t&&(t=[]),Array.isArray(t)||(t=[t]),e=0,i=this.selection.length;i>e;e++)s=this.selection[e],o=this.items[s],o&&o.unselect();for(this.selection=[],e=0,i=t.length;i>e;e++)s=t[e],o=this.items[s],o&&(this.selection.push(s),o.select())},s.prototype.getSelection=function(){return this.selection.concat([])},s.prototype.getVisibleItems=function(){var t=this.body.range.getRange(),e=this.body.util.toScreen(t.start),i=this.body.util.toScreen(t.end),s=[];for(var o in this.groups)if(this.groups.hasOwnProperty(o))for(var n=this.groups[o],r=n.visibleItems,a=0;ae&&s.push(h.id)}return s},s.prototype._deselect=function(t){for(var e=this.selection,i=0,s=e.length;s>i;i++)if(e[i]==t){e.splice(i,1);break}},s.prototype.redraw=function(){var t=this.options.margin,e=this.body.range,i=n.option.asSize,s=this.options,o=s.orientation,r=!1,a=this.dom.frame,h=s.editable.updateTime||s.editable.updateGroup;this.props.top=this.body.domProps.top.height+this.body.domProps.border.top,this.props.left=this.body.domProps.left.width+this.body.domProps.border.left,a.className="itemset"+(h?" editable":""),r=this._orderGroups()||r;var d=e.end-e.start,l=d!=this.lastVisibleInterval||this.props.width!=this.props.lastWidth;l&&(this.stackDirty=!0),this.lastVisibleInterval=d,this.props.lastWidth=this.props.width;var c=this.stackDirty,p=this._firstGroup(),u={item:t.item,axis:t.axis},m={item:t.item,axis:t.item.vertical/2},f=0,v=t.axis+t.item.vertical;return this.groups[g].redraw(e,m,c),n.forEach(this.groups,function(t){var i=t==p?u:m,s=t.redraw(e,i,c);r=s||r,f+=t.height}),f=Math.max(f,v),this.stackDirty=!1,a.style.height=i(f),this.props.width=a.offsetWidth,this.props.height=f,this.dom.axis.style.top=i("top"==o?this.body.domProps.top.height+this.body.domProps.border.top:this.body.domProps.top.height+this.body.domProps.centerContainer.height),this.dom.axis.style.left="0",r=this._isResized()||r},s.prototype._firstGroup=function(){var t="top"==this.options.orientation?0:this.groupIds.length-1,e=this.groupIds[t],i=this.groups[e]||this.groups[f];return i||null},s.prototype._updateUngrouped=function(){{var t,e,i=this.groups[f];this.groups[g]}if(this.groupsData){if(i){i.hide(),delete this.groups[f];for(e in this.items)if(this.items.hasOwnProperty(e)){t=this.items[e],t.parent&&t.parent.remove(t);var s=this._getGroupId(t.data),o=this.groups[s];o&&o.add(t)||t.hide()}}}else if(!i){var n=null,r=null;i=new d(n,r,this),this.groups[f]=i;for(e in this.items)this.items.hasOwnProperty(e)&&(t=this.items[e],i.add(t));i.show()}},s.prototype.getLabelSet=function(){return this.dom.labelSet},s.prototype.setItems=function(t){var e,i=this,s=this.itemsData;if(t){if(!(t instanceof r||t instanceof a))throw new TypeError("Data must be an instance of DataSet or DataView");this.itemsData=t}else this.itemsData=null;if(s&&(n.forEach(this.itemListeners,function(t,e){s.off(e,t)}),e=s.getIds(),this._onRemove(e)),this.itemsData){var o=this.id;n.forEach(this.itemListeners,function(t,e){i.itemsData.on(e,t,o)}),e=this.itemsData.getIds(),this._onAdd(e),this._updateUngrouped()}},s.prototype.getItems=function(){return this.itemsData},s.prototype.setGroups=function(t){var e,i=this;if(this.groupsData&&(n.forEach(this.groupListeners,function(t,e){i.groupsData.unsubscribe(e,t)}),e=this.groupsData.getIds(),this.groupsData=null,this._onRemoveGroups(e)),t){if(!(t instanceof r||t instanceof a))throw new TypeError("Data must be an instance of DataSet or DataView");this.groupsData=t}else this.groupsData=null;if(this.groupsData){var s=this.id;n.forEach(this.groupListeners,function(t,e){i.groupsData.on(e,t,s)}),e=this.groupsData.getIds(),this._onAddGroups(e)}this._updateUngrouped(),this._order(),this.body.emitter.emit("change",{queue:!0})},s.prototype.getGroups=function(){return this.groupsData},s.prototype.removeItem=function(t){var e=this.itemsData.get(t),i=this.itemsData.getDataSet();e&&this.options.onRemove(e,function(e){e&&i.remove(t)})},s.prototype._getType=function(t){return t.type||this.options.type||(t.end?"range":"box")},s.prototype._getGroupId=function(t){var e=this._getType(t);return"background"==e&&void 0==t.group?g:this.groupsData?t.group:f},s.prototype._onUpdate=function(t){var e=this;t.forEach(function(t){var i=e.itemsData.get(t,e.itemOptions),o=e.items[t],n=e._getType(i),r=s.types[n];if(o&&(r&&o instanceof r?e._updateItem(o,i):(e._removeItem(o),o=null)),!o){if(!r)throw new TypeError("rangeoverflow"==n?'Item type "rangeoverflow" is deprecated. Use css styling instead: .vis.timeline .item.range .content {overflow: visible;}':'Unknown item type "'+n+'"');o=new r(i,e.conversion,e.options),o.id=t,e._addItem(o)}}),this._order(),this.stackDirty=!0,this.body.emitter.emit("change",{queue:!0})},s.prototype._onAdd=s.prototype._onUpdate,s.prototype._onRemove=function(t){var e=0,i=this;t.forEach(function(t){var s=i.items[t];s&&(e++,i._removeItem(s))}),e&&(this._order(),this.stackDirty=!0,this.body.emitter.emit("change",{queue:!0}))},s.prototype._order=function(){n.forEach(this.groups,function(t){t.order()})},s.prototype._onUpdateGroups=function(t){this._onAddGroups(t)},s.prototype._onAddGroups=function(t){var e=this;t.forEach(function(t){var i=e.groupsData.get(t),s=e.groups[t];if(s)s.setData(i);else{if(t==f||t==g)throw new Error("Illegal group id. "+t+" is a reserved id.");var o=Object.create(e.options);n.extend(o,{height:null}),s=new d(t,i,e),e.groups[t]=s;for(var r in e.items)if(e.items.hasOwnProperty(r)){var a=e.items[r];a.data.group==t&&s.add(a)}s.order(),s.show()}}),this.body.emitter.emit("change",{queue:!0})},s.prototype._onRemoveGroups=function(t){var e=this.groups;t.forEach(function(t){var i=e[t];i&&(i.hide(),delete e[t])}),this.markDirty(),this.body.emitter.emit("change",{queue:!0})},s.prototype._orderGroups=function(){if(this.groupsData){var t=this.groupsData.getIds({order:this.options.groupOrder}),e=!n.equalArray(t,this.groupIds);if(e){var i=this.groups;t.forEach(function(t){i[t].hide()}),t.forEach(function(t){i[t].show()}),this.groupIds=t}return e}return!1},s.prototype._addItem=function(t){this.items[t.id]=t;var e=this._getGroupId(t.data),i=this.groups[e];i&&i.add(t)},s.prototype._updateItem=function(t,e){var i=t.data.group;if(t.setData(e),i!=t.data.group){var s=this.groups[i];s&&s.remove(t);var o=this._getGroupId(t.data),n=this.groups[o];n&&n.add(t)}},s.prototype._removeItem=function(t){t.hide(),delete this.items[t.id];var e=this.selection.indexOf(t.id);-1!=e&&this.selection.splice(e,1),t.parent&&t.parent.remove(t)},s.prototype._constructByEndArray=function(t){for(var e=[],i=0;i0||o.length>0)&&this.body.emitter.emit("select",{items:a})}},s.prototype._onAddItem=function(t){if(this.options.selectable&&this.options.editable.add){var e=this,i=this.body.util.snap||null,o=s.itemFromTarget(t);if(o){var r=e.itemsData.get(o.id);this.options.onUpdate(r,function(t){t&&e.itemsData.getDataSet().update(t)})}else{var a=n.getAbsoluteLeft(this.dom.frame),h=t.gesture.center.pageX-a,d=this.body.util.toTime(h),l={start:i?i(d):d,content:"new item"};if("range"===this.options.type){var c=this.body.util.toTime(h+this.props.width/5);l.end=i?i(c):c}l[this.itemsData._fieldId]=n.randomUUID();var p=s.groupFromTarget(t);p&&(l.group=p.groupId),this.options.onAdd(l,function(t){t&&e.itemsData.getDataSet().add(t)})}}},s.prototype._onMultiSelectItem=function(t){if(this.options.selectable){var e,i=s.itemFromTarget(t);if(i){e=this.getSelection();var o=t.gesture.touches[0]&&t.gesture.touches[0].shiftKey||!1;if(o){e.push(i.id);var n=s._getItemRange(this.itemsData.get(e,this.itemOptions));e=[];for(var r in this.items)if(this.items.hasOwnProperty(r)){var a=this.items[r],h=a.data.start,d=void 0!==a.data.end?a.data.end:h;h>=n.min&&d<=n.max&&e.push(a.id)}}else{var l=e.indexOf(i.id);-1==l?e.push(i.id):e.splice(l,1)}this.setSelection(e),this.body.emitter.emit("select",{items:this.getSelection()})}}},s._getItemRange=function(t){var e=null,i=null;return t.forEach(function(t){(null==i||t.starte)&&(e=t.end):(null==e||t.start>e)&&(e=t.start)}),{min:i,max:e}},s.itemFromTarget=function(t){for(var e=t.target;e;){if(e.hasOwnProperty("timeline-item"))return e["timeline-item"];e=e.parentNode}return null},s.groupFromTarget=function(t){for(var e=t.target;e;){if(e.hasOwnProperty("timeline-group"))return e["timeline-group"];e=e.parentNode}return null},s.itemSetFromTarget=function(t){for(var e=t.target;e;){if(e.hasOwnProperty("timeline-itemset"))return e["timeline-itemset"];e=e.parentNode}return null},t.exports=s},function(t,e,i){function s(t,e,i,s){this.body=t,this.defaultOptions={enabled:!0,icons:!0,iconSize:20,iconSpacing:6,left:{visible:!0,position:"top-left"},right:{visible:!0,position:"top-left"}},this.side=i,this.options=o.extend({},this.defaultOptions),this.linegraphOptions=s,this.svgElements={},this.dom={},this.groups={},this.amountOfGroups=0,this._create(),this.setOptions(e)}var o=i(1),n=i(2),r=i(20);s.prototype=new r,s.prototype.clear=function(){this.groups={},this.amountOfGroups=0},s.prototype.addGroup=function(t,e){this.groups.hasOwnProperty(t)||(this.groups[t]=e),this.amountOfGroups+=1},s.prototype.updateGroup=function(t,e){this.groups[t]=e},s.prototype.removeGroup=function(t){this.groups.hasOwnProperty(t)&&(delete this.groups[t],this.amountOfGroups-=1)},s.prototype._create=function(){this.dom.frame=document.createElement("div"),this.dom.frame.className="legend",this.dom.frame.style.position="absolute",this.dom.frame.style.top="10px",this.dom.frame.style.display="block",this.dom.textArea=document.createElement("div"),this.dom.textArea.className="legendText",this.dom.textArea.style.position="relative",this.dom.textArea.style.top="0px",this.svg=document.createElementNS("http://www.w3.org/2000/svg","svg"),this.svg.style.position="absolute",this.svg.style.top="0px",this.svg.style.width=this.options.iconSize+5+"px",this.svg.style.height="100%",this.dom.frame.appendChild(this.svg),this.dom.frame.appendChild(this.dom.textArea)},s.prototype.hide=function(){this.dom.frame.parentNode&&this.dom.frame.parentNode.removeChild(this.dom.frame)},s.prototype.show=function(){this.dom.frame.parentNode||this.body.dom.center.appendChild(this.dom.frame)},s.prototype.setOptions=function(t){var e=["enabled","orientation","icons","left","right"];o.selectiveDeepExtend(e,this.options,t)},s.prototype.redraw=function(){var t=0;for(var e in this.groups)this.groups.hasOwnProperty(e)&&(1!=this.groups[e].visible||void 0!==this.linegraphOptions.visibility[e]&&1!=this.linegraphOptions.visibility[e]||t++);if(0==this.options[this.side].visible||0==this.amountOfGroups||0==this.options.enabled||0==t)this.hide();else{if(this.show(),"top-left"==this.options[this.side].position||"bottom-left"==this.options[this.side].position?(this.dom.frame.style.left="4px",this.dom.frame.style.textAlign="left",this.dom.textArea.style.textAlign="left",this.dom.textArea.style.left=this.options.iconSize+15+"px",this.dom.textArea.style.right="",this.svg.style.left="0px",this.svg.style.right=""):(this.dom.frame.style.right="4px",this.dom.frame.style.textAlign="right",this.dom.textArea.style.textAlign="right",this.dom.textArea.style.right=this.options.iconSize+15+"px",this.dom.textArea.style.left="",this.svg.style.right="0px",this.svg.style.left=""),"top-left"==this.options[this.side].position||"top-right"==this.options[this.side].position)this.dom.frame.style.top=4-Number(this.body.dom.center.style.top.replace("px",""))+"px",this.dom.frame.style.bottom="";else{var i=this.body.domProps.center.height-this.body.domProps.centerContainer.height;this.dom.frame.style.bottom=4+i+Number(this.body.dom.center.style.top.replace("px",""))+"px",this.dom.frame.style.top=""}0==this.options.icons?(this.dom.frame.style.width=this.dom.textArea.offsetWidth+10+"px",this.dom.textArea.style.right="",this.dom.textArea.style.left="",this.svg.style.width="0px"):(this.dom.frame.style.width=this.options.iconSize+15+this.dom.textArea.offsetWidth+10+"px",this.drawLegendIcons());var s="";for(var e in this.groups)this.groups.hasOwnProperty(e)&&(1!=this.groups[e].visible||void 0!==this.linegraphOptions.visibility[e]&&1!=this.linegraphOptions.visibility[e]||(s+=this.groups[e].content+"
"));this.dom.textArea.innerHTML=s,this.dom.textArea.style.lineHeight=.75*this.options.iconSize+this.options.iconSpacing+"px"}},s.prototype.drawLegendIcons=function(){if(this.dom.frame.parentNode){n.prepareElements(this.svgElements);var t=window.getComputedStyle(this.dom.frame).paddingTop,e=Number(t.replace("px","")),i=e,s=this.options.iconSize,o=.75*this.options.iconSize,r=e+.5*o+3;this.svg.style.width=s+5+e+"px";for(var a in this.groups)this.groups.hasOwnProperty(a)&&(1!=this.groups[a].visible||void 0!==this.linegraphOptions.visibility[a]&&1!=this.linegraphOptions.visibility[a]||(this.groups[a].drawIcon(i,r,this.svgElements,this.svg,s,o),r+=o+this.options.iconSpacing));n.cleanupElements(this.svgElements)}},t.exports=s},function(t,e,i){function s(t,e){this.id=o.randomUUID(),this.body=t,this.defaultOptions={yAxisOrientation:"left",defaultGroup:"default",sort:!0,sampling:!0,graphHeight:"400px",shaded:{enabled:!1,orientation:"bottom"},style:"line",barChart:{width:50,handleOverlap:"overlap",align:"center"},catmullRom:{enabled:!0,parametrization:"centripetal",alpha:.5},drawPoints:{enabled:!0,size:6,style:"square"},dataAxis:{showMinorLabels:!0,showMajorLabels:!0,icons:!1,width:"40px",visible:!0,alignZeros:!0,customRange:{left:{min:void 0,max:void 0},right:{min:void 0,max:void 0}}},legend:{enabled:!1,icons:!0,left:{visible:!0,position:"top-left"},right:{visible:!0,position:"top-right"}},groups:{visibility:{}}},this.options=o.extend({},this.defaultOptions),this.dom={},this.props={},this.hammer=null,this.groups={},this.abortedGraphUpdate=!1,this.updateSVGheight=!1,this.updateSVGheightOnResize=!1;var i=this;this.itemsData=null,this.groupsData=null,this.itemListeners={add:function(t,e){i._onAdd(e.items)},update:function(t,e){i._onUpdate(e.items)},remove:function(t,e){i._onRemove(e.items)}},this.groupListeners={add:function(t,e){i._onAddGroups(e.items)},update:function(t,e){i._onUpdateGroups(e.items)},remove:function(t,e){i._onRemoveGroups(e.items)}},this.items={},this.selection=[],this.lastStart=this.body.range.start,this.touchParams={},this.svgElements={},this.setOptions(e),this.groupsUsingDefaultStyles=[0],this.COUNTER=0,this.body.emitter.on("rangechanged",function(){i.lastStart=i.body.range.start,i.svg.style.left=o.option.asSize(-i.props.width),i.redraw.call(i,!0)}),this._create(),this.framework={svg:this.svg,svgElements:this.svgElements,options:this.options,groups:this.groups},this.body.emitter.emit("change")}var o=i(1),n=i(2),r=i(3),a=i(4),h=i(20),d=i(23),l=i(24),c=i(28),p=i(52),u="__ungrouped__";s.prototype=new h,s.prototype._create=function(){var t=document.createElement("div");t.className="LineGraph",this.dom.frame=t,this.svg=document.createElementNS("http://www.w3.org/2000/svg","svg"),this.svg.style.position="relative",this.svg.style.height=(""+this.options.graphHeight).replace("px","")+"px",this.svg.style.display="block",t.appendChild(this.svg),this.options.dataAxis.orientation="left",this.yAxisLeft=new d(this.body,this.options.dataAxis,this.svg,this.options.groups),this.options.dataAxis.orientation="right",this.yAxisRight=new d(this.body,this.options.dataAxis,this.svg,this.options.groups),delete this.options.dataAxis.orientation,this.legendLeft=new c(this.body,this.options.legend,"left",this.options.groups),this.legendRight=new c(this.body,this.options.legend,"right",this.options.groups),this.show()},s.prototype.setOptions=function(t){if(t){var e=["sampling","defaultGroup","height","graphHeight","yAxisOrientation","style","barChart","dataAxis","sort","groups"];void 0===t.graphHeight&&void 0!==t.height&&void 0!==this.body.domProps.centerContainer.height?(this.updateSVGheight=!0,this.updateSVGheightOnResize=!0):void 0!==this.body.domProps.centerContainer.height&&void 0!==t.graphHeight&&parseInt((t.graphHeight+"").replace("px",""))0){var d=this.body.util.toGlobalTime(-this.body.domProps.root.width),l=this.body.util.toGlobalTime(2*this.body.domProps.root.width),c={};for(this._getRelevantData(a,c,d,l),this._applySampling(a,c),e=0;eu&&console.log("WARNING: there may be an infinite loop in the _updateGraph emitter cycle."),this.COUNTER=0,this.abortedGraphUpdate=!1,e=0;e0)for(r=0;rs){d.push(h);break}d.push(h)}}else for(a=0;ai&&h.x0)for(var s=0;s0){var n=1,r=o.length,a=this.body.util.toGlobalScreen(o[o.length-1].x)-this.body.util.toGlobalScreen(o[0].x),h=r/a;n=Math.min(Math.ceil(.2*r),Math.max(1,Math.round(h)));for(var d=[],l=0;r>l;l+=n)d.push(o[l]);e[t[s]]=d}}},s.prototype._getYRanges=function(t,e,i){var s,o,n,r,a=[],h=[];if(t.length>0){for(n=0;n0&&(o=this.groups[t[n]],"stack"==r.barChart.handleOverlap&&"bar"==r.style?"left"==r.yAxisOrientation?a=a.concat(o.getYRange(s)):h=h.concat(o.getYRange(s)):i[t[n]]=o.getYRange(s,t[n]));p.getStackedBarYRange(a,i,t,"__barchartLeft","left"),p.getStackedBarYRange(h,i,t,"__barchartRight","right")}},s.prototype._updateYAxis=function(t,e){var i,s,o=!1,n=!1,r=!1,a=1e9,h=1e9,d=-1e9,l=-1e9;if(t.length>0){for(var c=0;ci?i:a,d=s>d?s:d):(r=!0,h=h>i?i:h,l=s>l?s:l));1==n&&this.yAxisLeft.setRange(a,d),1==r&&this.yAxisRight.setRange(h,l)}return o=this._toggleAxisVisiblity(n,this.yAxisLeft)||o,o=this._toggleAxisVisiblity(r,this.yAxisRight)||o,1==r&&1==n?(this.yAxisLeft.drawIcons=!0,this.yAxisRight.drawIcons=!0):(this.yAxisLeft.drawIcons=!1,this.yAxisRight.drawIcons=!1),this.yAxisRight.master=!n,0==this.yAxisRight.master?(this.yAxisLeft.lineOffset=1==r?this.yAxisRight.width:0,o=this.yAxisLeft.redraw()||o,this.yAxisRight.stepPixelsForced=this.yAxisLeft.stepPixels,this.yAxisRight.zeroCrossing=this.yAxisLeft.zeroCrossing,o=this.yAxisRight.redraw()||o):o=this.yAxisRight.redraw()||o,-1!=t.indexOf("__barchartLeft")&&t.splice(t.indexOf("__barchartLeft"),1),-1!=t.indexOf("__barchartRight")&&t.splice(t.indexOf("__barchartRight"),1),o},s.prototype._toggleAxisVisiblity=function(t,e){var i=!1;return 0==t?e.dom.frame.parentNode&&0==e.hidden&&(e.hide(),i=!0):e.dom.frame.parentNode||1!=e.hidden||(e.show(),i=!0),i},s.prototype._convertXcoordinates=function(t){for(var e,i,s=[],o=this.body.util.toScreen,n=0;ny;)y++,l=h.getCurrent(),c=h.isMajor(),u=h.getClassName(),f=m,m=this.body.util.toScreen(l),g=m-f,p&&(p.style.width=g+"px"),this.options.showMinorLabels&&this._repaintMinorText(m,h.getLabelMinor(),t,u),c&&this.options.showMajorLabels?(m>0&&(void 0==v&&(v=m),this._repaintMajorText(m,h.getLabelMajor(),t,u)),p=this._repaintMajorLine(m,t,u)):p=this._repaintMinorLine(m,t,u),h.next();if(this.options.showMajorLabels){var b=this.body.util.toTime(0),_=h.getLabelMajor(b),x=_.length*(this.props.majorCharWidth||10)+10;(void 0==v||v>x)&&this._repaintMajorText(0,_,t,u)}o.forEach(this.dom.redundant,function(t){for(;t.length;){var e=t.pop();e&&e.parentNode&&e.parentNode.removeChild(e)}})},s.prototype._repaintMinorText=function(t,e,i,s){var o=this.dom.redundant.minorTexts.shift();if(!o){var n=document.createTextNode("");o=document.createElement("div"),o.appendChild(n),this.dom.foreground.appendChild(o)}this.dom.minorTexts.push(o),o.childNodes[0].nodeValue=e,o.style.top="top"==i?this.props.majorLabelHeight+"px":"0",o.style.left=t+"px",o.className="text minor "+s},s.prototype._repaintMajorText=function(t,e,i,s){var o=this.dom.redundant.majorTexts.shift();if(!o){var n=document.createTextNode(e);o=document.createElement("div"),o.appendChild(n),this.dom.foreground.appendChild(o)}this.dom.majorTexts.push(o),o.childNodes[0].nodeValue=e,o.className="text major "+s,o.style.top="top"==i?"0":this.props.minorLabelHeight+"px",o.style.left=t+"px"},s.prototype._repaintMinorLine=function(t,e,i){var s=this.dom.redundant.lines.shift();s||(s=document.createElement("div"),this.dom.background.appendChild(s)),this.dom.lines.push(s);var o=this.props;return s.style.top="top"==e?o.majorLabelHeight+"px":this.body.domProps.top.height+"px",s.style.height=o.minorLineHeight+"px",s.style.left=t-o.minorLineWidth/2+"px",s.className="grid vertical minor "+i,s},s.prototype._repaintMajorLine=function(t,e,i){var s=this.dom.redundant.lines.shift();s||(s=document.createElement("div"),this.dom.background.appendChild(s)),this.dom.lines.push(s);var o=this.props;return s.style.top="top"==e?"0":this.body.domProps.top.height+"px",s.style.left=t-o.majorLineWidth/2+"px",s.style.height=o.majorLineHeight+"px",s.className="grid vertical major "+i,s},s.prototype._calculateCharSize=function(){this.dom.measureCharMinor||(this.dom.measureCharMinor=document.createElement("DIV"),this.dom.measureCharMinor.className="text minor measure",this.dom.measureCharMinor.style.position="absolute",this.dom.measureCharMinor.appendChild(document.createTextNode("0")),this.dom.foreground.appendChild(this.dom.measureCharMinor)),this.props.minorCharHeight=this.dom.measureCharMinor.clientHeight,this.props.minorCharWidth=this.dom.measureCharMinor.clientWidth,this.dom.measureCharMajor||(this.dom.measureCharMajor=document.createElement("DIV"),this.dom.measureCharMajor.className="text major measure",this.dom.measureCharMajor.style.position="absolute",this.dom.measureCharMajor.appendChild(document.createTextNode("0")),this.dom.foreground.appendChild(this.dom.measureCharMajor)),this.props.majorCharHeight=this.dom.measureCharMajor.clientHeight,this.props.majorCharWidth=this.dom.measureCharMajor.clientWidth},s.prototype.snap=function(t){return this.step.snap(t)},t.exports=s},function(t,e,i){function s(t,e,i){this.id=null,this.parent=null,this.data=t,this.dom=null,this.conversion=e||{},this.options=i||{},this.selected=!1,this.displayed=!1,this.dirty=!0,this.top=null,this.left=null,this.width=null,this.height=null}var o=i(45),n=i(1);s.prototype.stack=!0,s.prototype.select=function(){this.selected=!0,this.dirty=!0,this.displayed&&this.redraw()},s.prototype.unselect=function(){this.selected=!1,this.dirty=!0,this.displayed&&this.redraw()},s.prototype.setData=function(t){this.data=t,this.dirty=!0,this.displayed&&this.redraw()},s.prototype.setParent=function(t){this.displayed?(this.hide(),this.parent=t,this.parent&&this.show()):this.parent=t},s.prototype.isVisible=function(){return!1},s.prototype.show=function(){return!1},s.prototype.hide=function(){return!1},s.prototype.redraw=function(){},s.prototype.repositionX=function(){},s.prototype.repositionY=function(){},s.prototype._repaintDeleteButton=function(t){if(this.selected&&this.options.editable.remove&&!this.dom.deleteButton){var e=this,i=document.createElement("div");i.className="delete",i.title="Delete this item",o(i,{preventDefault:!0}).on("tap",function(t){e.parent.removeFromDataSet(e),t.stopPropagation()}),t.appendChild(i),this.dom.deleteButton=i}else!this.selected&&this.dom.deleteButton&&(this.dom.deleteButton.parentNode&&this.dom.deleteButton.parentNode.removeChild(this.dom.deleteButton),this.dom.deleteButton=null)},s.prototype._updateContents=function(t){var e;if(this.options.template){var i=this.parent.itemSet.itemsData.get(this.id);e=this.options.template(i)}else e=this.data.content;if(e!==this.content){if(e instanceof Element)t.innerHTML="",t.appendChild(e);else if(void 0!=e)t.innerHTML=e;else if("background"!=this.data.type||void 0!==this.data.content)throw new Error('Property "content" missing in item '+this.id);this.content=e}},s.prototype._updateTitle=function(t){null!=this.data.title?t.title=this.data.title||"":t.removeAttribute("title")},s.prototype._updateDataAttributes=function(t){if(this.options.dataAttributes&&this.options.dataAttributes.length>0){var e=[];if(Array.isArray(this.options.dataAttributes))e=this.options.dataAttributes;else{if("all"!=this.options.dataAttributes)return;e=Object.keys(this.data)}for(var i=0;it.start},s.prototype.redraw=function(){var t=this.dom;if(t||(this.dom={},t=this.dom,t.box=document.createElement("div"),t.content=document.createElement("div"),t.content.className="content",t.box.appendChild(t.content),this.dirty=!0),!this.parent)throw new Error("Cannot redraw item: no parent attached");if(!t.box.parentNode){var e=this.parent.dom.background;if(!e)throw new Error("Cannot redraw item: parent has no background container element");e.appendChild(t.box)}if(this.displayed=!0,this.dirty){this._updateContents(this.dom.content),this._updateTitle(this.dom.content),this._updateDataAttributes(this.dom.content),this._updateStyle(this.dom.box);var i=(this.data.className?" "+this.data.className:"")+(this.selected?" selected":"");t.box.className=this.baseClassName+i,this.overflow="hidden"!==window.getComputedStyle(t.content).overflow,this.props.content.width=this.dom.content.offsetWidth,this.height=0,this.dirty=!1}},s.prototype.show=r.prototype.show,s.prototype.hide=r.prototype.hide,s.prototype.repositionX=r.prototype.repositionX,s.prototype.repositionY=function(t){var e="top"===this.options.orientation;this.dom.content.style.top=e?"":"0",this.dom.content.style.bottom=e?"0":"";var i;if(void 0!==this.data.subgroup){var s=this.data.subgroup,o=this.parent.subgroups,r=o[s].index;if(1==e){i=this.parent.subgroups[s].height+t.item.vertical,i+=0==r?t.axis-.5*t.item.vertical:0;var a=this.parent.top;for(var h in o)o.hasOwnProperty(h)&&1==o[h].visible&&o[h].indexr&&(a+=o[h].height+t.item.vertical);i=this.parent.subgroups[s].height+t.item.vertical,this.dom.box.style.top=a+"px",this.dom.box.style.bottom=""}}else this.parent instanceof n?(i=Math.max(this.parent.height,this.parent.itemSet.body.domProps.center.height,this.parent.itemSet.body.domProps.centerContainer.height),this.dom.box.style.top=e?"0":"",this.dom.box.style.bottom=e?"":"0"):(i=this.parent.height,this.dom.box.style.top=this.parent.top+"px",this.dom.box.style.bottom="");this.dom.box.style.height=i+"px"},t.exports=s},function(t,e,i){function s(t,e,i){if(this.props={dot:{width:0,height:0},line:{width:0,height:0}},t&&void 0==t.start)throw new Error('Property "start" missing in item '+t);o.call(this,t,e,i)}{var o=i(31);i(1)}s.prototype=new o(null,null,null),s.prototype.isVisible=function(t){var e=(t.end-t.start)/4;return this.data.start>t.start-e&&this.data.startt.start-e&&this.data.startt.start},s.prototype.redraw=function(){var t=this.dom;if(t||(this.dom={},t=this.dom,t.box=document.createElement("div"),t.content=document.createElement("div"),t.content.className="content",t.box.appendChild(t.content),t.box["timeline-item"]=this,this.dirty=!0),!this.parent)throw new Error("Cannot redraw item: no parent attached");if(!t.box.parentNode){var e=this.parent.dom.foreground;if(!e)throw new Error("Cannot redraw item: parent has no foreground container element");e.appendChild(t.box)}if(this.displayed=!0,this.dirty){this._updateContents(this.dom.content),this._updateTitle(this.dom.box),this._updateDataAttributes(this.dom.box),this._updateStyle(this.dom.box);var i=(this.data.className?" "+this.data.className:"")+(this.selected?" selected":"");t.box.className=this.baseClassName+i,this.overflow="hidden"!==window.getComputedStyle(t.content).overflow,this.dom.content.style.maxWidth="none",this.props.content.width=this.dom.content.offsetWidth,this.height=this.dom.box.offsetHeight,this.dom.content.style.maxWidth="",this.dirty=!1}this._repaintDeleteButton(t.box),this._repaintDragLeft(),this._repaintDragRight()},s.prototype.show=function(){this.displayed||this.redraw()},s.prototype.hide=function(){if(this.displayed){var t=this.dom.box;t.parentNode&&t.parentNode.removeChild(t),this.top=null,this.left=null,this.displayed=!1}},s.prototype.repositionX=function(){var t,e,i=this.parent.width,s=this.conversion.toScreen(this.data.start),o=this.conversion.toScreen(this.data.end);-i>s&&(s=-i),o>2*i&&(o=2*i);var n=Math.max(o-s,1);switch(this.overflow?(this.left=s,this.width=n+this.props.content.width,e=this.props.content.width):(this.left=s,this.width=n,e=Math.min(o-s-2*this.options.padding,this.props.content.width)),this.dom.box.style.left=this.left+"px",this.dom.box.style.width=n+"px",this.options.align){case"left":this.dom.content.style.left="0";break;case"right":this.dom.content.style.left=Math.max(n-e-2*this.options.padding,0)+"px";break;case"center":this.dom.content.style.left=Math.max((n-e-2*this.options.padding)/2,0)+"px";break;default:t=this.overflow?o>0?Math.max(-s,0):-e:0>s?Math.min(-s,o-s-e-2*this.options.padding):0,this.dom.content.style.left=t+"px"}},s.prototype.repositionY=function(){var t=this.options.orientation,e=this.dom.box;e.style.top="top"==t?this.top+"px":this.parent.height-this.top-this.height+"px"},s.prototype._repaintDragLeft=function(){if(this.selected&&this.options.editable.updateTime&&!this.dom.dragLeft){var t=document.createElement("div");t.className="drag-left",t.dragLeftItem=this,o(t,{preventDefault:!0}).on("drag",function(){}),this.dom.box.appendChild(t),this.dom.dragLeft=t}else!this.selected&&this.dom.dragLeft&&(this.dom.dragLeft.parentNode&&this.dom.dragLeft.parentNode.removeChild(this.dom.dragLeft),this.dom.dragLeft=null)},s.prototype._repaintDragRight=function(){if(this.selected&&this.options.editable.updateTime&&!this.dom.dragRight){var t=document.createElement("div");t.className="drag-right",t.dragRightItem=this,o(t,{preventDefault:!0}).on("drag",function(){}),this.dom.box.appendChild(t),this.dom.dragRight=t}else!this.selected&&this.dom.dragRight&&(this.dom.dragRight.parentNode&&this.dom.dragRight.parentNode.removeChild(this.dom.dragRight),this.dom.dragRight=null)},t.exports=s},function(t,e,i){function s(t,e,i){if(!(this instanceof s))throw new SyntaxError("Constructor must be called with the new operator");this._determineBrowserMethod(),this._initializeMixinLoaders(),this.containerElement=t,this.renderRefreshRate=60,this.renderTimestep=1e3/this.renderRefreshRate,this.renderTime=0,this.physicsTime=0,this.runDoubleSpeed=!1,this.physicsDiscreteStepsize=.5,this.initializing=!0,this.triggerFunctions={add:null,edit:null,editEdge:null,connect:null,del:null},this.defaultOptions={nodes:{mass:1,radiusMin:10,radiusMax:30,radius:10,shape:"ellipse",image:void 0,widthMin:16,widthMax:64,fontColor:"black",fontSize:14,fontFace:"verdana",fontFill:void 0,fontStrokeWidth:0,fontStrokeColor:"white",level:-1,color:{border:"#2B7CE9",background:"#97C2FC",highlight:{border:"#2B7CE9",background:"#D2E5FF"},hover:{border:"#2B7CE9",background:"#D2E5FF"}},group:void 0,borderWidth:1,borderWidthSelected:void 0},edges:{widthMin:1,widthMax:15,width:1,widthSelectionMultiplier:2,hoverWidth:1.5,style:"line",color:{color:"#848484",highlight:"#848484",hover:"#848484"},fontColor:"#343434",fontSize:14,fontFace:"arial",fontFill:"white",fontStrokeWidth:0,fontStrokeColor:"white",labelAlignment:"horizontal",arrowScaleFactor:1,dash:{length:10,gap:5,altLength:void 0},inheritColor:"from"},configurePhysics:!1,physics:{barnesHut:{enabled:!0,thetaInverted:2,gravitationalConstant:-2e3,centralGravity:.3,springLength:95,springConstant:.04,damping:.09},repulsion:{centralGravity:0,springLength:200,springConstant:.05,nodeDistance:100,damping:.09},hierarchicalRepulsion:{enabled:!1,centralGravity:0,springLength:100,springConstant:.01,nodeDistance:150,damping:.09},damping:null,centralGravity:null,springLength:null,springConstant:null},clustering:{enabled:!1,initialMaxNodes:100,clusterThreshold:500,reduceToNodes:300,chainThreshold:.4,clusterEdgeThreshold:20,sectorThreshold:100,screenSizeThreshold:.2,fontSizeMultiplier:4,maxFontSize:1e3,forceAmplification:.1,distanceAmplification:.1,edgeGrowth:20,nodeScaling:{width:1,height:1,radius:1},maxNodeSizeIncrements:600,activeAreaBoxSize:80,clusterLevelDifference:2},navigation:{enabled:!1},keyboard:{enabled:!1,speed:{x:10,y:10,zoom:.02},bindToWindow:!0},dataManipulation:{enabled:!1,initiallyVisible:!1},hierarchicalLayout:{enabled:!1,levelSeparation:150,nodeSpacing:100,direction:"UD",layout:"hubsize"},freezeForStabilization:!1,smoothCurves:{enabled:!0,dynamic:!0,type:"continuous",roundness:.5},maxVelocity:30,minVelocity:.1,stabilize:!0,stabilizationIterations:1e3,zoomExtentOnStabilize:!0,locale:"en",locales:_,tooltip:{delay:300,fontColor:"black",fontSize:14,fontFace:"verdana",color:{border:"#666",background:"#FFFFC6"}},dragNetwork:!0,dragNodes:!0,zoomable:!0,hover:!1,hideEdgesOnDrag:!1,hideNodesOnDrag:!1,width:"100%",height:"100%",selectable:!0},this.constants=a.extend({},this.defaultOptions),this.pixelRatio=1,this.hoverObj={nodes:{},edges:{}},this.controlNodesActive=!1,this.navigationHammers={existing:[],_new:[]},this.animationSpeed=1/this.renderRefreshRate,this.animationEasingFunction="easeInOutQuint",this.animating=!1,this.easingTime=0,this.sourceScale=0,this.targetScale=0,this.sourceTranslation=0,this.targetTranslation=0,this.lockedOnNodeId=null,this.lockedOnNodeOffset=null,this.touchTime=0;var o=this;this.groups=new u,this.images=new m,this.images.setOnloadCallback(function(){o._redraw()}),this.xIncrement=0,this.yIncrement=0,this.zoomIncrement=0,this._loadPhysicsSystem(),this._create(),this._loadSectorSystem(),this._loadClusterSystem(),this._loadSelectionSystem(),this._loadHierarchySystem(),this._setTranslation(this.frame.clientWidth/2,this.frame.clientHeight/2),this._setScale(1),this.setOptions(i),this.freezeSimulation=!1,this.cachedFunctions={},this.startedStabilization=!1,this.stabilized=!1,this.stabilizationIterations=null,this.draggingNodes=!1,this.calculationNodes={},this.calculationNodeIndices=[],this.nodeIndices=[],this.nodes={},this.edges={},this.canvasTopLeft={x:0,y:0},this.canvasBottomRight={x:0,y:0},this.pointerPosition={x:0,y:0},this.areaCenter={},this.scale=1,this.previousScale=this.scale,this.nodesData=null,this.edgesData=null,this.nodesListeners={add:function(t,e){o._addNodes(e.items),o.start()},update:function(t,e){o._updateNodes(e.items,e.data),o.start()},remove:function(t,e){o._removeNodes(e.items),o.start()}},this.edgesListeners={add:function(t,e){o._addEdges(e.items),o.start()},update:function(t,e){o._updateEdges(e.items),o.start()},remove:function(t,e){o._removeEdges(e.items),o.start()}},this.moving=!0,this.timer=void 0,this.setData(e,this.constants.clustering.enabled||this.constants.hierarchicalLayout.enabled),this.initializing=!1,1==this.constants.hierarchicalLayout.enabled?this._setupHierarchicalLayout():0==this.constants.stabilize&&this.zoomExtent(void 0,!0,this.constants.clustering.enabled),this.constants.clustering.enabled&&this.startWithClustering()}var o=i(56),n=i(45),r=i(63),a=i(1),h=i(47),d=i(3),l=i(4),c=i(42),p=i(43),u=i(38),m=i(39),f=i(40),g=i(37),v=i(41),y=i(54),b=i(55),_=i(49);i(50),o(s.prototype),s.prototype._determineBrowserMethod=function(){var t=navigator.userAgent.toLowerCase();this.requiresTimeout=!1,-1!=t.indexOf("msie 9.0")?this.requiresTimeout=!0:-1!=t.indexOf("safari")&&t.indexOf("chrome")<=-1&&(this.requiresTimeout=!0)},s.prototype._getScriptPath=function(){for(var t=document.getElementsByTagName("script"),e=0;et.boundingBox.left&&(s=t.boundingBox.left),ot.boundingBox.bottom&&(e=t.boundingBox.top),i=this.constants.clustering.initialMaxNodes?49.07548/(n+142.05338)+91444e-8:12.662/(n+7.4147)+.0964822:1==this.constants.clustering.enabled&&n>=this.constants.clustering.initialMaxNodes?77.5271985/(n+187.266146)+476710517e-13:30.5062972/(n+19.93597763)+.08413486;var r=Math.min(this.frame.canvas.clientWidth/600,this.frame.canvas.clientHeight/600);s*=r}else{var a=1.1*Math.abs(o.maxX-o.minX),h=1.1*Math.abs(o.maxY-o.minY),d=this.frame.canvas.clientWidth/a,l=this.frame.canvas.clientHeight/h;s=l>=d?d:l}s>1&&(s=1);var c=this._findCenter(o);if(0==i){var p={position:c,scale:s,animation:t};this.moveTo(p),this.moving=!0,this.start()}else c.x*=s,c.y*=s,c.x-=.5*this.frame.canvas.clientWidth,c.y-=.5*this.frame.canvas.clientHeight,this._setScale(s),this._setTranslation(-c.x,-c.y)},s.prototype._updateNodeIndexList=function(){this._clearNodeIndexList();for(var t in this.nodes)this.nodes.hasOwnProperty(t)&&this.nodeIndices.push(t)},s.prototype.setData=function(t,e){if(void 0===e&&(e=!1),this.initializing=!0,t&&t.dot&&(t.nodes||t.edges))throw new SyntaxError('Data must contain either parameter "dot" or parameter pair "nodes" and "edges", but not both.');if(1==this.constants.dataManipulation.enabled&&this._createManipulatorBar(),this.setOptions(t&&t.options),t&&t.dot){if(t&&t.dot){var i=c.DOTToGraph(t.dot);return void this.setData(i)}}else if(t&&t.gephi){if(t&&t.gephi){var s=p.parseGephi(t.gephi);return void this.setData(s)}}else this._setNodes(t&&t.nodes),this._setEdges(t&&t.edges);this._putDataInSector(),0==e&&(1==this.constants.hierarchicalLayout.enabled?(this._resetLevels(),this._setupHierarchicalLayout()):this.constants.stabilize&&this._stabilize(),this.start()),this.initializing=!1},s.prototype.setOptions=function(t){if(t){var e,i=["nodes","edges","smoothCurves","hierarchicalLayout","clustering","navigation","keyboard","dataManipulation","onAdd","onEdit","onEditEdge","onConnect","onDelete","clickToUse"];if(a.selectiveNotDeepExtend(i,this.constants,t),a.selectiveNotDeepExtend(["color"],this.constants.nodes,t.nodes),a.selectiveNotDeepExtend(["color","length"],this.constants.edges,t.edges),t.physics&&(a.mergeOptions(this.constants.physics,t.physics,"barnesHut"),a.mergeOptions(this.constants.physics,t.physics,"repulsion"),t.physics.hierarchicalRepulsion)){this.constants.hierarchicalLayout.enabled=!0,this.constants.physics.hierarchicalRepulsion.enabled=!0,this.constants.physics.barnesHut.enabled=!1; +for(e in t.physics.hierarchicalRepulsion)t.physics.hierarchicalRepulsion.hasOwnProperty(e)&&(this.constants.physics.hierarchicalRepulsion[e]=t.physics.hierarchicalRepulsion[e])}if(t.onAdd&&(this.triggerFunctions.add=t.onAdd),t.onEdit&&(this.triggerFunctions.edit=t.onEdit),t.onEditEdge&&(this.triggerFunctions.editEdge=t.onEditEdge),t.onConnect&&(this.triggerFunctions.connect=t.onConnect),t.onDelete&&(this.triggerFunctions.del=t.onDelete),a.mergeOptions(this.constants,t,"smoothCurves"),a.mergeOptions(this.constants,t,"hierarchicalLayout"),a.mergeOptions(this.constants,t,"clustering"),a.mergeOptions(this.constants,t,"navigation"),a.mergeOptions(this.constants,t,"keyboard"),a.mergeOptions(this.constants,t,"dataManipulation"),t.dataManipulation&&(this.editMode=this.constants.dataManipulation.initiallyVisible),t.edges&&(void 0!==t.edges.color&&(a.isString(t.edges.color)?(this.constants.edges.color={},this.constants.edges.color.color=t.edges.color,this.constants.edges.color.highlight=t.edges.color,this.constants.edges.color.hover=t.edges.color):(void 0!==t.edges.color.color&&(this.constants.edges.color.color=t.edges.color.color),void 0!==t.edges.color.highlight&&(this.constants.edges.color.highlight=t.edges.color.highlight),void 0!==t.edges.color.hover&&(this.constants.edges.color.hover=t.edges.color.hover)),this.constants.edges.inheritColor=!1),t.edges.fontColor||void 0!==t.edges.color&&(a.isString(t.edges.color)?this.constants.edges.fontColor=t.edges.color:void 0!==t.edges.color.color&&(this.constants.edges.fontColor=t.edges.color.color))),t.nodes&&t.nodes.color){var s=a.parseColor(t.nodes.color);this.constants.nodes.color.background=s.background,this.constants.nodes.color.border=s.border,this.constants.nodes.color.highlight.background=s.highlight.background,this.constants.nodes.color.highlight.border=s.highlight.border,this.constants.nodes.color.hover.background=s.hover.background,this.constants.nodes.color.hover.border=s.hover.border}if(t.groups)for(var o in t.groups)if(t.groups.hasOwnProperty(o)){var n=t.groups[o];this.groups.add(o,n)}if(t.tooltip){for(e in t.tooltip)t.tooltip.hasOwnProperty(e)&&(this.constants.tooltip[e]=t.tooltip[e]);t.tooltip.color&&(this.constants.tooltip.color=a.parseColor(t.tooltip.color))}if("clickToUse"in t&&(t.clickToUse?this.activator||(this.activator=new b(this.frame),this.activator.on("change",this._createKeyBinds.bind(this))):this.activator&&(this.activator.destroy(),delete this.activator)),t.labels)throw new Error('Option "labels" is deprecated. Use options "locale" and "locales" instead.');this._loadPhysicsSystem(),this._loadNavigationControls(),this._loadManipulationSystem(),this._configureSmoothCurves(),this._bindHammer(),this._createKeyBinds(),this.setSize(this.constants.width,this.constants.height),this.moving=!0,this.start()}},s.prototype._create=function(){for(;this.containerElement.hasChildNodes();)this.containerElement.removeChild(this.containerElement.firstChild);if(this.frame=document.createElement("div"),this.frame.className="vis network-frame",this.frame.style.position="relative",this.frame.style.overflow="hidden",this.frame.tabIndex=900,this.frame.canvas=document.createElement("canvas"),this.frame.canvas.style.position="relative",this.frame.appendChild(this.frame.canvas),this.frame.canvas.getContext){var t=this.frame.canvas.getContext("2d");this.pixelRatio=(window.devicePixelRatio||1)/(t.webkitBackingStorePixelRatio||t.mozBackingStorePixelRatio||t.msBackingStorePixelRatio||t.oBackingStorePixelRatio||t.backingStorePixelRatio||1),this.frame.canvas.getContext("2d").setTransform(this.pixelRatio,0,0,this.pixelRatio,0,0)}else{var e=document.createElement("DIV");e.style.color="red",e.style.fontWeight="bold",e.style.padding="10px",e.innerHTML="Error: your browser does not support HTML canvas",this.frame.canvas.appendChild(e)}this._bindHammer()},s.prototype._bindHammer=function(){var t=this;void 0!==this.hammer&&this.hammer.dispose(),this.drag={},this.pinch={},this.hammer=n(this.frame.canvas,{prevent_default:!0}),this.hammer.on("tap",t._onTap.bind(t)),this.hammer.on("doubletap",t._onDoubleTap.bind(t)),this.hammer.on("hold",t._onHold.bind(t)),this.hammer.on("touch",t._onTouch.bind(t)),this.hammer.on("dragstart",t._onDragStart.bind(t)),this.hammer.on("drag",t._onDrag.bind(t)),this.hammer.on("dragend",t._onDragEnd.bind(t)),1==this.constants.zoomable&&(this.hammer.on("mousewheel",t._onMouseWheel.bind(t)),this.hammer.on("DOMMouseScroll",t._onMouseWheel.bind(t)),this.hammer.on("pinch",t._onPinch.bind(t))),this.hammer.on("mousemove",t._onMouseMoveTitle.bind(t)),this.hammerFrame=n(this.frame,{prevent_default:!0}),this.hammerFrame.on("release",t._onRelease.bind(t)),this.containerElement.appendChild(this.frame)},s.prototype._createKeyBinds=function(){var t=this;void 0!==this.keycharm&&this.keycharm.destroy(),this.keycharm=r(1==this.constants.keyboard.bindToWindow?{container:window,preventDefault:!1}:{container:this.frame,preventDefault:!1}),this.keycharm.reset(),this.constants.keyboard.enabled&&this.isActive()&&(this.keycharm.bind("up",this._moveUp.bind(t),"keydown"),this.keycharm.bind("up",this._yStopMoving.bind(t),"keyup"),this.keycharm.bind("down",this._moveDown.bind(t),"keydown"),this.keycharm.bind("down",this._yStopMoving.bind(t),"keyup"),this.keycharm.bind("left",this._moveLeft.bind(t),"keydown"),this.keycharm.bind("left",this._xStopMoving.bind(t),"keyup"),this.keycharm.bind("right",this._moveRight.bind(t),"keydown"),this.keycharm.bind("right",this._xStopMoving.bind(t),"keyup"),this.keycharm.bind("=",this._zoomIn.bind(t),"keydown"),this.keycharm.bind("=",this._stopZoom.bind(t),"keyup"),this.keycharm.bind("num+",this._zoomIn.bind(t),"keydown"),this.keycharm.bind("num+",this._stopZoom.bind(t),"keyup"),this.keycharm.bind("num-",this._zoomOut.bind(t),"keydown"),this.keycharm.bind("num-",this._stopZoom.bind(t),"keyup"),this.keycharm.bind("-",this._zoomOut.bind(t),"keydown"),this.keycharm.bind("-",this._stopZoom.bind(t),"keyup"),this.keycharm.bind("[",this._zoomIn.bind(t),"keydown"),this.keycharm.bind("[",this._stopZoom.bind(t),"keyup"),this.keycharm.bind("]",this._zoomOut.bind(t),"keydown"),this.keycharm.bind("]",this._stopZoom.bind(t),"keyup"),this.keycharm.bind("pageup",this._zoomIn.bind(t),"keydown"),this.keycharm.bind("pageup",this._stopZoom.bind(t),"keyup"),this.keycharm.bind("pagedown",this._zoomOut.bind(t),"keydown"),this.keycharm.bind("pagedown",this._stopZoom.bind(t),"keyup")),1==this.constants.dataManipulation.enabled&&(this.keycharm.bind("esc",this._createManipulatorBar.bind(t)),this.keycharm.bind("delete",this._deleteSelected.bind(t)))},s.prototype.destroy=function(){this.start=function(){},this.redraw=function(){},this.timer=!1,this._cleanupPhysicsConfiguration(),this.keycharm.reset(),this.hammer.dispose(),this.off(),this._recursiveDOMDelete(this.containerElement)},s.prototype._recursiveDOMDelete=function(t){for(;1==t.hasChildNodes();)this._recursiveDOMDelete(t.firstChild),t.removeChild(t.firstChild)},s.prototype._getPointer=function(t){return{x:t.pageX-a.getAbsoluteLeft(this.frame.canvas),y:t.pageY-a.getAbsoluteTop(this.frame.canvas)}},s.prototype._onTouch=function(t){(new Date).valueOf()-this.touchTime>100&&(this.drag.pointer=this._getPointer(t.gesture.center),this.drag.pinched=!1,this.pinch.scale=this._getScale(),this.touchTime=(new Date).valueOf(),this._handleTouch(this.drag.pointer))},s.prototype._onDragStart=function(t){this._handleDragStart(t)},s.prototype._handleDragStart=function(t){void 0===this.drag.pointer&&this._onTouch(t);var e=this._getNodeAt(this.drag.pointer);if(this.drag.dragging=!0,this.drag.selection=[],this.drag.translation=this._getTranslation(),this.drag.nodeId=null,this.draggingNodes=!1,null!=e&&1==this.constants.dragNodes){this.draggingNodes=!0,this.drag.nodeId=e.id,e.isSelected()||this._selectObject(e,!1),this.emit("dragStart",{nodeIds:this.getSelection().nodes});for(var i in this.selectionObj.nodes)if(this.selectionObj.nodes.hasOwnProperty(i)){var s=this.selectionObj.nodes[i],o={id:s.id,node:s,x:s.x,y:s.y,xFixed:s.xFixed,yFixed:s.yFixed};s.xFixed=!0,s.yFixed=!0,this.drag.selection.push(o)}}},s.prototype._onDrag=function(t){this._handleOnDrag(t)},s.prototype._handleOnDrag=function(t){if(!this.drag.pinched){this.releaseNode();var e=this._getPointer(t.gesture.center),i=this,s=this.drag,o=s.selection;if(o&&o.length&&1==this.constants.dragNodes){var n=e.x-s.pointer.x,r=e.y-s.pointer.y;o.forEach(function(t){var e=t.node;t.xFixed||(e.x=i._XconvertDOMtoCanvas(i._XconvertCanvasToDOM(t.x)+n)),t.yFixed||(e.y=i._YconvertDOMtoCanvas(i._YconvertCanvasToDOM(t.y)+r))}),this.moving||(this.moving=!0,this.start())}else if(1==this.constants.dragNetwork){if(void 0===this.drag.pointer)return void this._handleDragStart(t);var a=e.x-this.drag.pointer.x,h=e.y-this.drag.pointer.y;this._setTranslation(this.drag.translation.x+a,this.drag.translation.y+h),this._redraw()}}},s.prototype._onDragEnd=function(t){this._handleDragEnd(t)},s.prototype._handleDragEnd=function(){this.drag.dragging=!1;var t=this.drag.selection;t&&t.length?(t.forEach(function(t){t.node.xFixed=t.xFixed,t.node.yFixed=t.yFixed}),this.moving=!0,this.start()):this._redraw(),0==this.draggingNodes?this.emit("dragEnd",{nodeIds:[]}):this.emit("dragEnd",{nodeIds:this.getSelection().nodes})},s.prototype._onTap=function(t){var e=this._getPointer(t.gesture.center);this.pointerPosition=e,this._handleTap(e)},s.prototype._onDoubleTap=function(t){var e=this._getPointer(t.gesture.center);this._handleDoubleTap(e)},s.prototype._onHold=function(t){var e=this._getPointer(t.gesture.center);this.pointerPosition=e,this._handleOnHold(e)},s.prototype._onRelease=function(t){var e=this._getPointer(t.gesture.center);this._handleOnRelease(e)},s.prototype._onPinch=function(t){var e=this._getPointer(t.gesture.center);this.drag.pinched=!0,"scale"in this.pinch||(this.pinch.scale=1);var i=this.pinch.scale*t.gesture.scale;this._zoom(i,e)},s.prototype._zoom=function(t,e){if(1==this.constants.zoomable){var i=this._getScale();1e-5>t&&(t=1e-5),t>10&&(t=10);var s=null;void 0!==this.drag&&1==this.drag.dragging&&(s=this.DOMtoCanvas(this.drag.pointer));var o=this._getTranslation(),n=t/i,r=(1-n)*e.x+o.x*n,a=(1-n)*e.y+o.y*n;if(this.areaCenter={x:this._XconvertDOMtoCanvas(e.x),y:this._YconvertDOMtoCanvas(e.y)},this._setScale(t),this._setTranslation(r,a),this.updateClustersDefault(),null!=s){var h=this.canvasToDOM(s);this.drag.pointer.x=h.x,this.drag.pointer.y=h.y}return this._redraw(),t>i?this.emit("zoom",{direction:"+"}):this.emit("zoom",{direction:"-"}),t}},s.prototype._onMouseWheel=function(t){var e=0;if(t.wheelDelta?e=t.wheelDelta/120:t.detail&&(e=-t.detail/3),e){var i=this._getScale(),s=e/10;0>e&&(s/=1-s),i*=1+s;var o=h.fakeGesture(this,t),n=this._getPointer(o.center);this._zoom(i,n)}t.preventDefault()},s.prototype._onMouseMoveTitle=function(t){var e=h.fakeGesture(this,t),i=this._getPointer(e.center);this.popupObj&&this._checkHidePopup(i),0==this.constants.keyboard.bindToWindow&&1==this.constants.keyboard.enabled&&this.frame.focus();var s=this,o=function(){s._checkShowPopup(i)};if(this.popupTimer&&clearInterval(this.popupTimer),this.drag.dragging||(this.popupTimer=setTimeout(o,this.constants.tooltip.delay)),1==this.constants.hover){for(var n in this.hoverObj.edges)this.hoverObj.edges.hasOwnProperty(n)&&(this.hoverObj.edges[n].hover=!1,delete this.hoverObj.edges[n]);var r=this._getNodeAt(i);null==r&&(r=this._getEdgeAt(i)),null!=r&&this._hoverObject(r);for(var a in this.hoverObj.nodes)this.hoverObj.nodes.hasOwnProperty(a)&&(r instanceof f&&r.id!=a||r instanceof g||null==r)&&(this._blurObject(this.hoverObj.nodes[a]),delete this.hoverObj.nodes[a]);this.redraw()}},s.prototype._checkShowPopup=function(t){var e,i={left:this._XconvertDOMtoCanvas(t.x),top:this._YconvertDOMtoCanvas(t.y),right:this._XconvertDOMtoCanvas(t.x),bottom:this._YconvertDOMtoCanvas(t.y)},s=this.popupObj,o=!1;if(void 0==this.popupObj){var n=this.nodes,r=[];for(e in n)if(n.hasOwnProperty(e)){var a=n[e];a.isOverlappingWith(i)&&void 0!==a.getTitle()&&r.push(e)}r.length>0&&(this.popupObj=this.nodes[r[r.length-1]],o=!0)}if(void 0===this.popupObj&&0==o){var h=this.edges,d=[];for(e in h)if(h.hasOwnProperty(e)){var l=h[e];l.connected&&void 0!==l.getTitle()&&l.isOverlappingWith(i)&&d.push(e)}d.length>0&&(this.popupObj=this.edges[d[d.length-1]])}if(this.popupObj){if(this.popupObj!=s){var c=this;c.popup||(c.popup=new v(c.frame,c.constants.tooltip)),c.popup.setPosition(t.x-3,t.y-3),c.popup.setText(c.popupObj.getTitle()),c.popup.show()}}else this.popup&&this.popup.hide()},s.prototype._checkHidePopup=function(t){this.popupObj&&this._getNodeAt(t)||(this.popupObj=void 0,this.popup&&this.popup.hide())},s.prototype.setSize=function(t,e){var i=!1,s=this.frame.canvas.width,o=this.frame.canvas.height;t!=this.constants.width||e!=this.constants.height||this.frame.style.width!=t||this.frame.style.height!=e?(this.frame.style.width=t,this.frame.style.height=e,this.frame.canvas.style.width="100%",this.frame.canvas.style.height="100%",this.frame.canvas.width=this.frame.canvas.clientWidth*this.pixelRatio,this.frame.canvas.height=this.frame.canvas.clientHeight*this.pixelRatio,this.constants.width=t,this.constants.height=e,i=!0):(this.frame.canvas.width!=this.frame.canvas.clientWidth*this.pixelRatio&&(this.frame.canvas.width=this.frame.canvas.clientWidth*this.pixelRatio,i=!0),this.frame.canvas.height!=this.frame.canvas.clientHeight*this.pixelRatio&&(this.frame.canvas.height=this.frame.canvas.clientHeight*this.pixelRatio,i=!0)),1==i&&this.emit("resize",{width:this.frame.canvas.width*this.pixelRatio,height:this.frame.canvas.height*this.pixelRatio,oldWidth:s*this.pixelRatio,oldHeight:o*this.pixelRatio})},s.prototype._setNodes=function(t){var e=this.nodesData;if(t instanceof d||t instanceof l)this.nodesData=t;else if(Array.isArray(t))this.nodesData=new d,this.nodesData.add(t);else{if(t)throw new TypeError("Array or DataSet expected");this.nodesData=new d}if(e&&a.forEach(this.nodesListeners,function(t,i){e.off(i,t)}),this.nodes={},this.nodesData){var i=this;a.forEach(this.nodesListeners,function(t,e){i.nodesData.on(e,t)});var s=this.nodesData.getIds();this._addNodes(s)}this._updateSelection()},s.prototype._addNodes=function(t){for(var e,i=0,s=t.length;s>i;i++){e=t[i];var o=this.nodesData.get(e),n=new f(o,this.images,this.groups,this.constants);if(this.nodes[e]=n,!(0!=n.xFixed&&0!=n.yFixed||null!==n.x&&null!==n.y)){var r=1*t.length+10,a=2*Math.PI*Math.random();0==n.xFixed&&(n.x=r*Math.cos(a)),0==n.yFixed&&(n.y=r*Math.sin(a))}this.moving=!0}this._updateNodeIndexList(),1==this.constants.hierarchicalLayout.enabled&&0==this.initializing&&(this._resetLevels(),this._setupHierarchicalLayout()),this._updateCalculationNodes(),this._reconnectEdges(),this._updateValueRange(this.nodes),this.updateLabels()},s.prototype._updateNodes=function(t,e){for(var i=this.nodes,s=0,o=t.length;o>s;s++){var n=t[s],r=i[n],a=e[s];r?r.setProperties(a,this.constants):(r=new f(properties,this.images,this.groups,this.constants),i[n]=r)}this.moving=!0,1==this.constants.hierarchicalLayout.enabled&&0==this.initializing&&(this._resetLevels(),this._setupHierarchicalLayout()),this._updateNodeIndexList(),this._updateValueRange(i)},s.prototype._removeNodes=function(t){for(var e=this.nodes,i=0,s=t.length;s>i;i++){var o=t[i];delete e[o]}this._updateNodeIndexList(),1==this.constants.hierarchicalLayout.enabled&&0==this.initializing&&(this._resetLevels(),this._setupHierarchicalLayout()),this._updateCalculationNodes(),this._reconnectEdges(),this._updateSelection(),this._updateValueRange(e)},s.prototype._setEdges=function(t){var e=this.edgesData;if(t instanceof d||t instanceof l)this.edgesData=t;else if(Array.isArray(t))this.edgesData=new d,this.edgesData.add(t);else{if(t)throw new TypeError("Array or DataSet expected");this.edgesData=new d}if(e&&a.forEach(this.edgesListeners,function(t,i){e.off(i,t)}),this.edges={},this.edgesData){var i=this;a.forEach(this.edgesListeners,function(t,e){i.edgesData.on(e,t)});var s=this.edgesData.getIds();this._addEdges(s)}this._reconnectEdges()},s.prototype._addEdges=function(t){for(var e=this.edges,i=this.edgesData,s=0,o=t.length;o>s;s++){var n=t[s],r=e[n];r&&r.disconnect();var a=i.get(n,{showInternalIds:!0});e[n]=new g(a,this,this.constants)}this.moving=!0,this._updateValueRange(e),this._createBezierNodes(),this._updateCalculationNodes(),1==this.constants.hierarchicalLayout.enabled&&0==this.initializing&&(this._resetLevels(),this._setupHierarchicalLayout())},s.prototype._updateEdges=function(t){for(var e=this.edges,i=this.edgesData,s=0,o=t.length;o>s;s++){var n=t[s],r=i.get(n),a=e[n];a?(a.disconnect(),a.setProperties(r,this.constants),a.connect()):(a=new g(r,this,this.constants),this.edges[n]=a)}this._createBezierNodes(),1==this.constants.hierarchicalLayout.enabled&&0==this.initializing&&(this._resetLevels(),this._setupHierarchicalLayout()),this.moving=!0,this._updateValueRange(e)},s.prototype._removeEdges=function(t){for(var e=this.edges,i=0,s=t.length;s>i;i++){var o=t[i],n=e[o];n&&(null!=n.via&&delete this.sectors.support.nodes[n.via.id],n.disconnect(),delete e[o])}this.moving=!0,this._updateValueRange(e),1==this.constants.hierarchicalLayout.enabled&&0==this.initializing&&(this._resetLevels(),this._setupHierarchicalLayout()),this._updateCalculationNodes()},s.prototype._reconnectEdges=function(){var t,e=this.nodes,i=this.edges;for(t in e)e.hasOwnProperty(t)&&(e[t].edges=[],e[t].dynamicEdges=[]);for(t in i)if(i.hasOwnProperty(t)){var s=i[t];s.from=null,s.to=null,s.connect()}},s.prototype._updateValueRange=function(t){var e,i=void 0,s=void 0;for(e in t)if(t.hasOwnProperty(e)){var o=t[e].getValue();void 0!==o&&(i=void 0===i?o:Math.min(o,i),s=void 0===s?o:Math.max(o,s))}if(void 0!==i&&void 0!==s)for(e in t)t.hasOwnProperty(e)&&t[e].setValueRange(i,s)},s.prototype.redraw=function(){this.setSize(this.constants.width,this.constants.height),this._redraw()},s.prototype._redraw=function(t){var e=this.frame.canvas.getContext("2d");e.setTransform(this.pixelRatio,0,0,this.pixelRatio,0,0);var i=this.frame.canvas.width*this.pixelRatio,s=this.frame.canvas.height*this.pixelRatio;e.clearRect(0,0,i,s),e.save(),e.translate(this.translation.x,this.translation.y),e.scale(this.scale,this.scale),this.canvasTopLeft={x:this._XconvertDOMtoCanvas(0),y:this._YconvertDOMtoCanvas(0)},this.canvasBottomRight={x:this._XconvertDOMtoCanvas(this.frame.canvas.clientWidth*this.pixelRatio),y:this._YconvertDOMtoCanvas(this.frame.canvas.clientHeight*this.pixelRatio)},1!=t&&(this._doInAllSectors("_drawAllSectorNodes",e),(0==this.drag.dragging||void 0===this.drag.dragging||0==this.constants.hideEdgesOnDrag)&&this._doInAllSectors("_drawEdges",e)),(0==this.drag.dragging||void 0===this.drag.dragging||0==this.constants.hideNodesOnDrag)&&this._doInAllSectors("_drawNodes",e,!1),1!=t&&1==this.controlNodesActive&&this._doInAllSectors("_drawControlNodes",e),e.restore(),1==t&&e.clearRect(0,0,i,s)},s.prototype._setTranslation=function(t,e){void 0===this.translation&&(this.translation={x:0,y:0}),void 0!==t&&(this.translation.x=t),void 0!==e&&(this.translation.y=e),this.emit("viewChanged")},s.prototype._getTranslation=function(){return{x:this.translation.x,y:this.translation.y}},s.prototype._setScale=function(t){this.scale=t},s.prototype._getScale=function(){return this.scale},s.prototype._XconvertDOMtoCanvas=function(t){return(t-this.translation.x)/this.scale},s.prototype._XconvertCanvasToDOM=function(t){return t*this.scale+this.translation.x},s.prototype._YconvertDOMtoCanvas=function(t){return(t-this.translation.y)/this.scale},s.prototype._YconvertCanvasToDOM=function(t){return t*this.scale+this.translation.y},s.prototype.canvasToDOM=function(t){return{x:this._XconvertCanvasToDOM(t.x),y:this._YconvertCanvasToDOM(t.y)}},s.prototype.DOMtoCanvas=function(t){return{x:this._XconvertDOMtoCanvas(t.x),y:this._YconvertDOMtoCanvas(t.y)}},s.prototype._drawNodes=function(t,e){void 0===e&&(e=!1);var i=this.nodes,s=[];for(var o in i)i.hasOwnProperty(o)&&(i[o].setScaleAndPos(this.scale,this.canvasTopLeft,this.canvasBottomRight),i[o].isSelected()?s.push(o):(i[o].inArea()||e)&&i[o].draw(t));for(var n=0,r=s.length;r>n;n++)(i[s[n]].inArea()||e)&&i[s[n]].draw(t)},s.prototype._drawEdges=function(t){var e=this.edges;for(var i in e)if(e.hasOwnProperty(i)){var s=e[i];s.setScale(this.scale),s.connected&&e[i].draw(t)}},s.prototype._drawControlNodes=function(t){var e=this.edges;for(var i in e)e.hasOwnProperty(i)&&e[i]._drawControlNodes(t)},s.prototype._stabilize=function(){1==this.constants.freezeForStabilization&&this._freezeDefinedNodes();for(var t=0;this.moving&&t0)for(t in i)i.hasOwnProperty(t)&&(i[t].discreteStepLimited(e,this.constants.maxVelocity),s=!0);else for(t in i)i.hasOwnProperty(t)&&(i[t].discreteStep(e),s=!0);if(1==s){var o=this.constants.minVelocity/Math.max(this.scale,.05);return o>.5*this.constants.maxVelocity?!0:this._isMoving(o)}return!1},s.prototype._revertPhysicsState=function(){var t=this.nodes;for(var e in t)t.hasOwnProperty(e)&&t[e].revertPosition()},s.prototype._revertPhysicsTick=function(){this._doInAllActiveSectors("_revertPhysicsState"),1==this.constants.smoothCurves.enabled&&1==this.constants.smoothCurves.dynamic&&this._doInSupportSector("_revertPhysicsState")},s.prototype._physicsTick=function(){if(!this.freezeSimulation&&1==this.moving){var t=!1,e=!1;this._doInAllActiveSectors("_initializeForceCalculation");var i=this._doInAllActiveSectors("_discreteStepNodes");1==this.constants.smoothCurves.enabled&&1==this.constants.smoothCurves.dynamic&&(e=this._doInSupportSector("_discreteStepNodes"));for(var s=0;s2*e||1==this.runDoubleSpeed)&&1==this.moving&&(this._physicsTick(),0!=this.renderTime&&(this.runDoubleSpeed=!0))}var i=Date.now();this._redraw(),this.renderTime=Date.now()-i,this.start()},"undefined"!=typeof window&&(window.requestAnimationFrame=window.requestAnimationFrame||window.mozRequestAnimationFrame||window.webkitRequestAnimationFrame||window.msRequestAnimationFrame),s.prototype.start=function(){if(1==this.moving||0!=this.xIncrement||0!=this.yIncrement||0!=this.zoomIncrement||1==this.animating)this.timer||(this.timer=1==this.requiresTimeout?window.setTimeout(this._animationStep.bind(this),this.renderTimestep):window.requestAnimationFrame(this._animationStep.bind(this)));else if(this._redraw(),this.stabilizationIterations>1){var t=this,e={iterations:t.stabilizationIterations};this.stabilizationIterations=0,this.startedStabilization=!1,setTimeout(function(){t.emit("stabilized",e)},0)}else this.stabilizationIterations=0},s.prototype._handleNavigation=function(){if(0!=this.xIncrement||0!=this.yIncrement){var t=this._getTranslation();this._setTranslation(t.x+this.xIncrement,t.y+this.yIncrement)}if(0!=this.zoomIncrement){var e={x:this.frame.canvas.clientWidth/2,y:this.frame.canvas.clientHeight/2};this._zoom(this.scale*(1+this.zoomIncrement),e)}},s.prototype.toggleFreeze=function(){0==this.freezeSimulation?this.freezeSimulation=!0:(this.freezeSimulation=!1,this.start())},s.prototype._configureSmoothCurves=function(t){if(void 0===t&&(t=!0),1==this.constants.smoothCurves.enabled&&1==this.constants.smoothCurves.dynamic){this._createBezierNodes();for(var e in this.sectors.support.nodes)this.sectors.support.nodes.hasOwnProperty(e)&&void 0===this.edges[this.sectors.support.nodes[e].parentEdgeId]&&delete this.sectors.support.nodes[e]}else{this.sectors.support.nodes={};for(var i in this.edges)this.edges.hasOwnProperty(i)&&(this.edges[i].via=null)}this._updateCalculationNodes(),t||(this.moving=!0,this.start())},s.prototype._createBezierNodes=function(){if(1==this.constants.smoothCurves.enabled&&1==this.constants.smoothCurves.dynamic)for(var t in this.edges)if(this.edges.hasOwnProperty(t)){var e=this.edges[t];if(null==e.via){var i="edgeId:".concat(e.id);this.sectors.support.nodes[i]=new f({id:i,mass:1,shape:"circle",image:"",internalMultiplier:1},{},{},this.constants),e.via=this.sectors.support.nodes[i],e.via.parentEdgeId=e.id,e.positionBezierNode()}}},s.prototype._initializeMixinLoaders=function(){for(var t in y)y.hasOwnProperty(t)&&(s.prototype[t]=y[t])},s.prototype.storePosition=function(){console.log("storePosition is depricated: use .storePositions() from now on."),this.storePositions()},s.prototype.storePositions=function(){var t=[];for(var e in this.nodes)if(this.nodes.hasOwnProperty(e)){var i=this.nodes[e],s=!this.nodes.xFixed,o=!this.nodes.yFixed;(this.nodesData._data[e].x!=Math.round(i.x)||this.nodesData._data[e].y!=Math.round(i.y))&&t.push({id:e,x:Math.round(i.x),y:Math.round(i.y),allowedToMoveX:s,allowedToMoveY:o})}this.nodesData.update(t)},s.prototype.getPositions=function(t){var e={};if(void 0!==t){if(1==Array.isArray(t)){for(var i=0;i=1&&(this.animating=!1,this.easingTime=0,this._redraw=null!=this.lockedOnNodeId?this._lockedRedraw:this._classicRedraw,this.emit("animationFinished"))},s.prototype._classicRedraw=function(){},s.prototype.isActive=function(){return!this.activator||this.activator.active},s.prototype.setScale=function(){return this._setScale()},s.prototype.getScale=function(){return this._getScale()},s.prototype.getCenterCoordinates=function(){return this.DOMtoCanvas({x:.5*this.frame.canvas.clientWidth,y:.5*this.frame.canvas.clientHeight})},s.prototype.getBoundingBox=function(t){return void 0!==this.nodes[t]?this.nodes[t].boundingBox:void 0},t.exports=s},function(t,e,i){function s(t,e,i){if(!e)throw"No network provided";var s=["edges","physics"],n=o.selectiveBridgeObject(s,i);this.options=n.edges,this.physics=n.physics,this.options.smoothCurves=i.smoothCurves,this.network=e,this.id=void 0,this.fromId=void 0,this.toId=void 0,this.title=void 0,this.widthSelected=this.options.width*this.options.widthSelectionMultiplier,this.value=void 0,this.selected=!1,this.hover=!1,this.labelDimensions={top:0,left:0,width:0,height:0,yLine:0},this.dirtyLabel=!0,this.from=null,this.to=null,this.via=null,this.fromBackup=null,this.toBackup=null,this.originalFromId=[],this.originalToId=[],this.connected=!1,this.widthFixed=!1,this.lengthFixed=!1,this.setProperties(t),this.controlNodesEnabled=!1,this.controlNodes={from:null,to:null,positions:{}},this.connectedNode=null}var o=i(1),n=i(40);s.prototype.setProperties=function(t){if(t){var e=["style","fontSize","fontFace","fontColor","fontFill","fontStrokeWidth","fontStrokeColor","width","widthSelectionMultiplier","hoverWidth","arrowScaleFactor","dash","inheritColor","labelAlignment"];switch(o.selectiveDeepExtend(e,this.options,t),void 0!==t.from&&(this.fromId=t.from),void 0!==t.to&&(this.toId=t.to),void 0!==t.id&&(this.id=t.id),void 0!==t.label&&(this.label=t.label,this.dirtyLabel=!0),void 0!==t.title&&(this.title=t.title),void 0!==t.value&&(this.value=t.value),void 0!==t.length&&(this.physics.springLength=t.length),void 0!==t.color&&(this.options.inheritColor=!1,o.isString(t.color)?(this.options.color.color=t.color,this.options.color.highlight=t.color):(void 0!==t.color.color&&(this.options.color.color=t.color.color),void 0!==t.color.highlight&&(this.options.color.highlight=t.color.highlight),void 0!==t.color.hover&&(this.options.color.hover=t.color.hover))),this.connect(),this.widthFixed=this.widthFixed||void 0!==t.width,this.lengthFixed=this.lengthFixed||void 0!==t.length,this.widthSelected=this.options.width*this.options.widthSelectionMultiplier,this.options.style){case"line":this.draw=this._drawLine;break;case"arrow":this.draw=this._drawArrow;break;case"arrow-center":this.draw=this._drawArrowCenter; +break;case"dash-line":this.draw=this._drawDashLine;break;default:this.draw=this._drawLine}}},s.prototype.connect=function(){this.disconnect(),this.from=this.network.nodes[this.fromId]||null,this.to=this.network.nodes[this.toId]||null,this.connected=this.from&&this.to,this.connected?(this.from.attachEdge(this),this.to.attachEdge(this)):(this.from&&this.from.detachEdge(this),this.to&&this.to.detachEdge(this))},s.prototype.disconnect=function(){this.from&&(this.from.detachEdge(this),this.from=null),this.to&&(this.to.detachEdge(this),this.to=null),this.connected=!1},s.prototype.getTitle=function(){return"function"==typeof this.title?this.title():this.title},s.prototype.getValue=function(){return this.value},s.prototype.setValueRange=function(t,e){if(!this.widthFixed&&void 0!==this.value){var i=(this.options.widthMax-this.options.widthMin)/(e-t);this.options.width=(this.value-t)*i+this.options.widthMin,this.widthSelected=this.options.width*this.options.widthSelectionMultiplier}},s.prototype.draw=function(){throw"Method draw not initialized in edge"},s.prototype.isOverlappingWith=function(t){if(this.connected){var e=10,i=this.from.x,s=this.from.y,o=this.to.x,n=this.to.y,r=t.left,a=t.top,h=this._getDistanceToEdge(i,s,o,n,r,a);return e>h}return!1},s.prototype._getColor=function(){var t=this.options.color;return"to"==this.options.inheritColor?t={highlight:this.to.options.color.highlight.border,hover:this.to.options.color.hover.border,color:this.to.options.color.border}:("from"==this.options.inheritColor||1==this.options.inheritColor)&&(t={highlight:this.from.options.color.highlight.border,hover:this.from.options.color.hover.border,color:this.from.options.color.border}),1==this.selected?t.highlight:1==this.hover?t.hover:t.color},s.prototype._drawLine=function(t){if(t.strokeStyle=this._getColor(),t.lineWidth=this._getLineWidth(),this.from!=this.to){var e,i=this._line(t);if(this.label){if(1==this.options.smoothCurves.enabled&&null!=i){var s=.5*(.5*(this.from.x+i.x)+.5*(this.to.x+i.x)),o=.5*(.5*(this.from.y+i.y)+.5*(this.to.y+i.y));e={x:s,y:o}}else e=this._pointOnLine(.5);this._label(t,this.label,e.x,e.y)}}else{var n,r,a=this.physics.springLength/4,h=this.from;h.width||h.resize(t),h.width>h.height?(n=h.x+h.width/2,r=h.y-a):(n=h.x+a,r=h.y-h.height/2),this._circle(t,n,r,a),e=this._pointOnCircle(n,r,a,.5),this._label(t,this.label,e.x,e.y)}},s.prototype._getLineWidth=function(){return 1==this.selected?Math.max(Math.min(this.widthSelected,this.options.widthMax),.3*this.networkScaleInv):1==this.hover?Math.max(Math.min(this.options.hoverWidth,this.options.widthMax),.3*this.networkScaleInv):Math.max(this.options.width,.3*this.networkScaleInv)},s.prototype._getViaCoordinates=function(){if(1==this.options.smoothCurves.dynamic&&1==this.options.smoothCurves.enabled)return this.via;if(0==this.options.smoothCurves.enabled)return{x:0,y:0};var t=null,e=null,i=this.options.smoothCurves.roundness,s=this.options.smoothCurves.type,o=Math.abs(this.from.x-this.to.x),n=Math.abs(this.from.y-this.to.y);return"discrete"==s||"diagonalCross"==s?Math.abs(this.from.x-this.to.x)this.to.y?this.from.xthis.to.x&&(t=this.from.x-i*n,e=this.from.y-i*n):this.from.ythis.to.x&&(t=this.from.x-i*n,e=this.from.y+i*n)),"discrete"==s&&(t=i*n>o?this.from.x:t)):Math.abs(this.from.x-this.to.x)>Math.abs(this.from.y-this.to.y)&&(this.from.y>this.to.y?this.from.xthis.to.x&&(t=this.from.x-i*o,e=this.from.y-i*o):this.from.ythis.to.x&&(t=this.from.x-i*o,e=this.from.y+i*o)),"discrete"==s&&(e=i*o>n?this.from.y:e)):"straightCross"==s?Math.abs(this.from.x-this.to.x)Math.abs(this.from.y-this.to.y)&&(t=this.from.xthis.to.y?this.from.xthis.to.x&&(t=this.from.x-i*n,e=this.from.y-i*n,t=this.to.x>t?this.to.x:t):this.from.ythis.to.x&&(t=this.from.x-i*n,e=this.from.y+i*n,t=this.to.x>t?this.to.x:t)):Math.abs(this.from.x-this.to.x)>Math.abs(this.from.y-this.to.y)&&(this.from.y>this.to.y?this.from.xe?this.to.y:e):this.from.x>this.to.x&&(t=this.from.x-i*o,e=this.from.y-i*o,e=this.to.y>e?this.to.y:e):this.from.ythis.to.x&&(t=this.from.x-i*o,e=this.from.y+i*o,e=this.to.yd;d++){var l=t.measureText(n[d]).width;h=l>h?l:h}var c=this.options.fontSize*r,p=i-h/2,u=s-c/2;this.labelDimensions={top:u,left:p,width:h,height:c,yLine:o}}var o=this.labelDimensions.yLine;t.save(),"horizontal"!=this.options.labelAlignment&&(t.translate(i,o),this._rotateForLabelAlignment(t),i=0,o=0),this._drawLabelRect(t),this._drawLabelText(t,i,o,n,r,a),t.restore()}},s.prototype._rotateForLabelAlignment=function(t){var e=this.from.y-this.to.y,i=this.from.x-this.to.x,s=Math.atan2(e,i);(-1>s&&0>i||s>0&&0>i)&&(s+=Math.PI),t.rotate(s)},s.prototype._drawLabelRect=function(t){if(void 0!==this.options.fontFill&&null!==this.options.fontFill&&"none"!==this.options.fontFill){t.fillStyle=this.options.fontFill;var e=2;"line-center"==this.options.labelAlignment?t.fillRect(.5*-this.labelDimensions.width,.5*-this.labelDimensions.height,this.labelDimensions.width,this.labelDimensions.height):"line-above"==this.options.labelAlignment?t.fillRect(.5*-this.labelDimensions.width,-(this.labelDimensions.height+e),this.labelDimensions.width,this.labelDimensions.height):"line-below"==this.options.labelAlignment?t.fillRect(.5*-this.labelDimensions.width,e,this.labelDimensions.width,this.labelDimensions.height):t.fillRect(this.labelDimensions.left,this.labelDimensions.top,this.labelDimensions.width,this.labelDimensions.height)}},s.prototype._drawLabelText=function(t,e,i,s,o,n){if(t.fillStyle=this.options.fontColor||"black",t.textAlign="center","horizontal"!=this.options.labelAlignment){var r=2;"line-above"==this.options.labelAlignment?(t.textBaseline="alphabetic",i-=2*r):"line-below"==this.options.labelAlignment?(t.textBaseline="hanging",i+=2*r):t.textBaseline="middle"}else t.textBaseline="middle";this.options.fontStrokeWidth>0&&(t.lineWidth=this.options.fontStrokeWidth,t.strokeStyle=this.options.fontStrokeColor,t.lineJoin="round");for(var a=0;o>a;a++)this.options.fontStrokeWidth>0&&t.strokeText(s[a],e,i),t.fillText(s[a],e,i),i+=n},s.prototype._drawDashLine=function(t){t.strokeStyle=this._getColor(),t.lineWidth=this._getLineWidth();var e=null;if(void 0!==t.setLineDash){t.save();var i=[0];i=void 0!==this.options.dash.length&&void 0!==this.options.dash.gap?[this.options.dash.length,this.options.dash.gap]:[5,5],t.setLineDash(i),t.lineDashOffset=0,e=this._line(t),t.setLineDash([0]),t.lineDashOffset=0,t.restore()}else t.beginPath(),t.lineCap="round",void 0!==this.options.dash.altLength?t.dashedLine(this.from.x,this.from.y,this.to.x,this.to.y,[this.options.dash.length,this.options.dash.gap,this.options.dash.altLength,this.options.dash.gap]):void 0!==this.options.dash.length&&void 0!==this.options.dash.gap?t.dashedLine(this.from.x,this.from.y,this.to.x,this.to.y,[this.options.dash.length,this.options.dash.gap]):(t.moveTo(this.from.x,this.from.y),t.lineTo(this.to.x,this.to.y)),t.stroke();if(this.label){var s;if(1==this.options.smoothCurves.enabled&&null!=e){var o=.5*(.5*(this.from.x+e.x)+.5*(this.to.x+e.x)),n=.5*(.5*(this.from.y+e.y)+.5*(this.to.y+e.y));s={x:o,y:n}}else s=this._pointOnLine(.5);this._label(t,this.label,s.x,s.y)}},s.prototype._pointOnLine=function(t){return{x:(1-t)*this.from.x+t*this.to.x,y:(1-t)*this.from.y+t*this.to.y}},s.prototype._pointOnCircle=function(t,e,i,s){var o=2*(s-3/8)*Math.PI;return{x:t+i*Math.cos(o),y:e-i*Math.sin(o)}},s.prototype._drawArrowCenter=function(t){var e;if(t.strokeStyle=this._getColor(),t.fillStyle=t.strokeStyle,t.lineWidth=this._getLineWidth(),this.from!=this.to){var i=this._line(t),s=Math.atan2(this.to.y-this.from.y,this.to.x-this.from.x),o=(10+5*this.options.width)*this.options.arrowScaleFactor;if(1==this.options.smoothCurves.enabled&&null!=i){var n=.5*(.5*(this.from.x+i.x)+.5*(this.to.x+i.x)),r=.5*(.5*(this.from.y+i.y)+.5*(this.to.y+i.y));e={x:n,y:r}}else e=this._pointOnLine(.5);t.arrow(e.x,e.y,s,o),t.fill(),t.stroke(),this.label&&this._label(t,this.label,e.x,e.y)}else{var a,h,d=.25*Math.max(100,this.physics.springLength),l=this.from;l.width||l.resize(t),l.width>l.height?(a=l.x+.5*l.width,h=l.y-d):(a=l.x+d,h=l.y-.5*l.height),this._circle(t,a,h,d);var s=.2*Math.PI,o=(10+5*this.options.width)*this.options.arrowScaleFactor;e=this._pointOnCircle(a,h,d,.5),t.arrow(e.x,e.y,s,o),t.fill(),t.stroke(),this.label&&(e=this._pointOnCircle(a,h,d,.5),this._label(t,this.label,e.x,e.y))}},s.prototype._pointOnBezier=function(t){var e=this._getViaCoordinates(),i=Math.pow(1-t,2)*this.from.x+2*t*(1-t)*e.x+Math.pow(t,2)*this.to.x,s=Math.pow(1-t,2)*this.from.y+2*t*(1-t)*e.y+Math.pow(t,2)*this.to.y;return{x:i,y:s}},s.prototype._findBorderPosition=function(t,e){var i,s,o,n,r,a=10,h=0,d=0,l=1,c=.2,p=this.to;for(1==t&&(p=this.from);l>=d&&a>h;){var u=.5*(d+l);if(i=this._pointOnBezier(u),s=Math.atan2(p.y-i.y,p.x-i.x),o=p.distanceToBorder(e,s),n=Math.sqrt(Math.pow(i.x-p.x,2)+Math.pow(i.y-p.y,2)),r=o-n,Math.abs(r)r?0==t?d=u:l=u:0==t?l=u:d=u,h++}return i.t=u,i},s.prototype._drawArrow=function(t){t.strokeStyle=this._getColor(),t.fillStyle=t.strokeStyle,t.lineWidth=this._getLineWidth();var e,i,s;if(this.from!=this.to){if(this._line(t),1==this.options.smoothCurves.enabled){var o=this._getViaCoordinates();s=this._findBorderPosition(!1,t);var n=this._pointOnBezier(Math.max(0,s.t-.1));e=Math.atan2(s.y-n.y,s.x-n.x)}else{e=Math.atan2(this.to.y-this.from.y,this.to.x-this.from.x);var r=this.to.x-this.from.x,a=this.to.y-this.from.y,h=Math.sqrt(r*r+a*a),d=this.to.distanceToBorder(t,e),l=(h-d)/h;s={},s.x=(1-l)*this.from.x+l*this.to.x,s.y=(1-l)*this.from.y+l*this.to.y}if(i=(10+5*this.options.width)*this.options.arrowScaleFactor,t.arrow(s.x,s.y,e,i),t.fill(),t.stroke(),this.label){var c;c=1==this.options.smoothCurves.enabled&&null!=o?this._pointOnBezier(.5):this._pointOnLine(.5),this._label(t,this.label,c.x,c.y)}}else{var p,u,m,f=this.from,g=.25*Math.max(100,this.physics.springLength);f.width||f.resize(t),f.width>f.height?(p=f.x+.5*f.width,u=f.y-g,m={x:p,y:f.y,angle:.9*Math.PI}):(p=f.x+g,u=f.y-.5*f.height,m={x:f.x,y:u,angle:.6*Math.PI}),t.beginPath(),t.arc(p,u,g,0,2*Math.PI,!1),t.stroke();var i=(10+5*this.options.width)*this.options.arrowScaleFactor;t.arrow(m.x,m.y,m.angle,i),t.fill(),t.stroke(),this.label&&(c=this._pointOnCircle(p,u,g,.5),this._label(t,this.label,c.x,c.y))}},s.prototype._getDistanceToEdge=function(t,e,i,s,o,n){var r=0;if(this.from!=this.to)if(1==this.options.smoothCurves.enabled){var a,h;if(1==this.options.smoothCurves.enabled&&1==this.options.smoothCurves.dynamic)a=this.via.x,h=this.via.y;else{var d=this._getViaCoordinates();a=d.x,h=d.y}var l,c,p,u,m,f,g,v=1e9;for(c=0;10>c;c++)p=.1*c,u=Math.pow(1-p,2)*t+2*p*(1-p)*a+Math.pow(p,2)*i,m=Math.pow(1-p,2)*e+2*p*(1-p)*h+Math.pow(p,2)*s,c>0&&(l=this._getDistanceToLine(f,g,u,m,o,n),v=v>l?l:v),f=u,g=m;r=v}else r=this._getDistanceToLine(t,e,i,s,o,n);else{var u,m,y,b,_=.25*this.physics.springLength,x=this.from;x.width>x.height?(u=x.x+.5*x.width,m=x.y-_):(u=x.x+_,m=x.y-.5*x.height),y=u-o,b=m-n,r=Math.abs(Math.sqrt(y*y+b*b)-_)}return this.labelDimensions.lefto&&this.labelDimensions.topn?0:r},s.prototype._getDistanceToLine=function(t,e,i,s,o,n){var r=i-t,a=s-e,h=r*r+a*a,d=((o-t)*r+(n-e)*a)/h;d>1?d=1:0>d&&(d=0);var l=t+d*r,c=e+d*a,p=l-o,u=c-n;return Math.sqrt(p*p+u*u)},s.prototype.setScale=function(t){this.networkScaleInv=1/t},s.prototype.select=function(){this.selected=!0},s.prototype.unselect=function(){this.selected=!1},s.prototype.positionBezierNode=function(){null!==this.via&&null!==this.from&&null!==this.to?(this.via.x=.5*(this.from.x+this.to.x),this.via.y=.5*(this.from.y+this.to.y)):null!==this.via&&(this.via.x=0,this.via.y=0)},s.prototype._drawControlNodes=function(t){if(1==this.controlNodesEnabled){if(null===this.controlNodes.from&&null===this.controlNodes.to){var e="edgeIdFrom:".concat(this.id),i="edgeIdTo:".concat(this.id),s={nodes:{group:"",radius:7,borderWidth:2,borderWidthSelected:2},physics:{damping:0},clustering:{maxNodeSizeIncrements:0,nodeScaling:{width:0,height:0,radius:0}}};this.controlNodes.from=new n({id:e,shape:"dot",color:{background:"#ff0000",border:"#3c3c3c",highlight:{background:"#07f968"}}},{},{},s),this.controlNodes.to=new n({id:i,shape:"dot",color:{background:"#ff0000",border:"#3c3c3c",highlight:{background:"#07f968"}}},{},{},s)}this.controlNodes.positions={},0==this.controlNodes.from.selected&&(this.controlNodes.positions.from=this.getControlNodeFromPosition(t),this.controlNodes.from.x=this.controlNodes.positions.from.x,this.controlNodes.from.y=this.controlNodes.positions.from.y),0==this.controlNodes.to.selected&&(this.controlNodes.positions.to=this.getControlNodeToPosition(t),this.controlNodes.to.x=this.controlNodes.positions.to.x,this.controlNodes.to.y=this.controlNodes.positions.to.y),this.controlNodes.from.draw(t),this.controlNodes.to.draw(t)}else this.controlNodes={from:null,to:null,positions:{}}},s.prototype._enableControlNodes=function(){this.fromBackup=this.from,this.toBackup=this.to,this.controlNodesEnabled=!0},s.prototype._disableControlNodes=function(){this.fromId=this.from.id,this.toId=this.to.id,this.fromId!=this.fromBackup.id?this.fromBackup.detachEdge(this):this.toId!=this.toBackup.id&&this.toBackup.detachEdge(this),this.fromBackup=null,this.toBackup=null,this.controlNodesEnabled=!1},s.prototype._getSelectedControlNode=function(t,e){var i=this.controlNodes.positions,s=Math.sqrt(Math.pow(t-i.from.x,2)+Math.pow(e-i.from.y,2)),o=Math.sqrt(Math.pow(t-i.to.x,2)+Math.pow(e-i.to.y,2));return 15>s?(this.connectedNode=this.from,this.from=this.controlNodes.from,this.controlNodes.from):15>o?(this.connectedNode=this.to,this.to=this.controlNodes.to,this.controlNodes.to):null},s.prototype._restoreControlNodes=function(){1==this.controlNodes.from.selected?(this.from=this.connectedNode,this.connectedNode=null,this.controlNodes.from.unselect()):1==this.controlNodes.to.selected&&(this.to=this.connectedNode,this.connectedNode=null,this.controlNodes.to.unselect())},s.prototype.getControlNodeFromPosition=function(t){var e;if(1==this.options.smoothCurves.enabled)e=this._findBorderPosition(!0,t);else{var i=Math.atan2(this.to.y-this.from.y,this.to.x-this.from.x),s=this.to.x-this.from.x,o=this.to.y-this.from.y,n=Math.sqrt(s*s+o*o),r=this.from.distanceToBorder(t,i+Math.PI),a=(n-r)/n;e={},e.x=a*this.from.x+(1-a)*this.to.x,e.y=a*this.from.y+(1-a)*this.to.y}return e},s.prototype.getControlNodeToPosition=function(t){var e;if(1==this.options.smoothCurves.enabled)e=this._findBorderPosition(!1,t);else{var i=Math.atan2(this.to.y-this.from.y,this.to.x-this.from.x),s=this.to.x-this.from.x,o=this.to.y-this.from.y,n=Math.sqrt(s*s+o*o),r=this.to.distanceToBorder(t,i),a=(n-r)/n;e={},e.x=(1-a)*this.from.x+a*this.to.x,e.y=(1-a)*this.from.y+a*this.to.y}return e},t.exports=s},function(t,e,i){function s(){this.clear(),this.defaultIndex=0}i(1);s.DEFAULT=[{border:"#2B7CE9",background:"#97C2FC",highlight:{border:"#2B7CE9",background:"#D2E5FF"},hover:{border:"#2B7CE9",background:"#D2E5FF"}},{border:"#FFA500",background:"#FFFF00",highlight:{border:"#FFA500",background:"#FFFFA3"},hover:{border:"#FFA500",background:"#FFFFA3"}},{border:"#FA0A10",background:"#FB7E81",highlight:{border:"#FA0A10",background:"#FFAFB1"},hover:{border:"#FA0A10",background:"#FFAFB1"}},{border:"#41A906",background:"#7BE141",highlight:{border:"#41A906",background:"#A1EC76"},hover:{border:"#41A906",background:"#A1EC76"}},{border:"#E129F0",background:"#EB7DF4",highlight:{border:"#E129F0",background:"#F0B3F5"},hover:{border:"#E129F0",background:"#F0B3F5"}},{border:"#7C29F0",background:"#AD85E4",highlight:{border:"#7C29F0",background:"#D3BDF0"},hover:{border:"#7C29F0",background:"#D3BDF0"}},{border:"#C37F00",background:"#FFA807",highlight:{border:"#C37F00",background:"#FFCA66"},hover:{border:"#C37F00",background:"#FFCA66"}},{border:"#4220FB",background:"#6E6EFD",highlight:{border:"#4220FB",background:"#9B9BFD"},hover:{border:"#4220FB",background:"#9B9BFD"}},{border:"#FD5A77",background:"#FFC0CB",highlight:{border:"#FD5A77",background:"#FFD1D9"},hover:{border:"#FD5A77",background:"#FFD1D9"}},{border:"#4AD63A",background:"#C2FABC",highlight:{border:"#4AD63A",background:"#E6FFE3"},hover:{border:"#4AD63A",background:"#E6FFE3"}}],s.prototype.clear=function(){this.groups={},this.groups.length=function(){var t=0;for(var e in this)this.hasOwnProperty(e)&&t++;return t}},s.prototype.get=function(t){var e=this.groups[t];if(void 0==e){var i=this.defaultIndex%s.DEFAULT.length;this.defaultIndex++,e={},e.color=s.DEFAULT[i],this.groups[t]=e}return e},s.prototype.add=function(t,e){return this.groups[t]=e,e},t.exports=s},function(t){function e(){this.images={},this.imageBroken={},this.callback=void 0}e.prototype.setOnloadCallback=function(t){this.callback=t},e.prototype.load=function(t,e){var i=this.images[t];if(void 0===i){var s=this;i=new Image,i.onload=function(){0==this.width&&(document.body.appendChild(this),this.width=this.offsetWidth,this.height=this.offsetHeight,document.body.removeChild(this)),s.callback&&(s.images[t]=i,s.callback(this))},i.onerror=function(){void 0===e?(console.error("Could not load image:",t),delete this.src,s.callback&&s.callback(this)):s.imageBroken[t]===!0?this.src==e?(console.error("Could not load brokenImage:",e),delete this.src,s.callback&&s.callback(this)):(console.error("Could not load image:",t),this.src=e):(console.error("Could not load image:",t),this.src=e,s.imageBroken[t]=!0)},i.src=t}return i},t.exports=e},function(t,e,i){function s(t,e,i,s){var n=o.selectiveBridgeObject(["nodes"],s);this.options=n.nodes,this.selected=!1,this.hover=!1,this.edges=[],this.dynamicEdges=[],this.reroutedEdges={},this.fontDrawThreshold=3,this.id=void 0,this.allowedToMoveX=!1,this.allowedToMoveY=!1,this.xFixed=!1,this.yFixed=!1,this.horizontalAlignLeft=!0,this.verticalAlignTop=!0,this.baseRadiusValue=s.nodes.radius,this.radiusFixed=!1,this.level=-1,this.preassignedLevel=!1,this.hierarchyEnumerated=!1,this.labelDimensions={top:0,left:0,width:0,height:0,yLine:0},this.boundingBox={top:0,left:0,right:0,bottom:0},this.imagelist=e,this.grouplist=i,this.fx=0,this.fy=0,this.vx=0,this.vy=0,this.x=null,this.y=null,this.previousState={vx:0,vy:0,x:0,y:0},this.damping=s.physics.damping,this.fixedData={x:null,y:null},this.setProperties(t,n),this.resetCluster(),this.dynamicEdgesLength=0,this.clusterSession=0,this.clusterSizeWidthFactor=s.clustering.nodeScaling.width,this.clusterSizeHeightFactor=s.clustering.nodeScaling.height,this.clusterSizeRadiusFactor=s.clustering.nodeScaling.radius,this.maxNodeSizeIncrements=s.clustering.maxNodeSizeIncrements,this.growthIndicator=0,this.networkScaleInv=1,this.networkScale=1,this.canvasTopLeft={x:-300,y:-300},this.canvasBottomRight={x:300,y:300},this.parentEdgeId=null}var o=i(1);s.prototype.revertPosition=function(){this.x=this.previousState.x,this.y=this.previousState.y,this.vx=this.previousState.vx,this.vy=this.previousState.vy},s.prototype.resetCluster=function(){this.formationScale=void 0,this.clusterSize=1,this.containedNodes={},this.containedEdges={},this.clusterSessions=[]},s.prototype.attachEdge=function(t){-1==this.edges.indexOf(t)&&this.edges.push(t),-1==this.dynamicEdges.indexOf(t)&&this.dynamicEdges.push(t),this.dynamicEdgesLength=this.dynamicEdges.length},s.prototype.detachEdge=function(t){var e=this.edges.indexOf(t);-1!=e&&this.edges.splice(e,1),e=this.dynamicEdges.indexOf(t),-1!=e&&this.dynamicEdges.splice(e,1),this.dynamicEdgesLength=this.dynamicEdges.length},s.prototype.setProperties=function(t,e){if(t){var i=["borderWidth","borderWidthSelected","shape","image","brokenImage","radius","fontColor","fontSize","fontFace","fontFill","fontStrokeWidth","fontStrokeColor","group","mass"];if(o.selectiveDeepExtend(i,this.options,t),void 0!==t.id&&(this.id=t.id),void 0!==t.label&&(this.label=t.label,this.originalLabel=t.label),void 0!==t.title&&(this.title=t.title),void 0!==t.x&&(this.x=t.x),void 0!==t.y&&(this.y=t.y),void 0!==t.value&&(this.value=t.value),void 0!==t.level&&(this.level=t.level,this.preassignedLevel=!0),void 0!==t.horizontalAlignLeft&&(this.horizontalAlignLeft=t.horizontalAlignLeft),void 0!==t.verticalAlignTop&&(this.verticalAlignTop=t.verticalAlignTop),void 0!==t.triggerFunction&&(this.triggerFunction=t.triggerFunction),void 0===this.id)throw"Node must have an id";if("number"==typeof t.group||"string"==typeof t.group&&""!=t.group){var s=this.grouplist.get(t.group);o.deepExtend(this.options,s),this.options.color=o.parseColor(this.options.color)}if(void 0!==t.radius&&(this.baseRadiusValue=this.options.radius),void 0!==t.color&&(this.options.color=o.parseColor(t.color)),void 0!==this.options.image&&""!=this.options.image){if(!this.imagelist)throw"No imagelist provided";this.imageObj=this.imagelist.load(this.options.image,this.options.brokenImage)}switch(void 0!==t.allowedToMoveX?(this.xFixed=!t.allowedToMoveX,this.allowedToMoveX=t.allowedToMoveX):void 0!==t.x&&0==this.allowedToMoveX&&(this.xFixed=!0),void 0!==t.allowedToMoveY?(this.yFixed=!t.allowedToMoveY,this.allowedToMoveY=t.allowedToMoveY):void 0!==t.y&&0==this.allowedToMoveY&&(this.yFixed=!0),this.radiusFixed=this.radiusFixed||void 0!==t.radius,("image"===this.options.shape||"circularImage"===this.options.shape)&&(this.options.radiusMin=e.nodes.widthMin,this.options.radiusMax=e.nodes.widthMax),this.options.shape){case"database":this.draw=this._drawDatabase,this.resize=this._resizeDatabase;break;case"box":this.draw=this._drawBox,this.resize=this._resizeBox;break;case"circle":this.draw=this._drawCircle,this.resize=this._resizeCircle;break;case"ellipse":this.draw=this._drawEllipse,this.resize=this._resizeEllipse;break;case"image":this.draw=this._drawImage,this.resize=this._resizeImage;break;case"circularImage":this.draw=this._drawCircularImage,this.resize=this._resizeCircularImage;break;case"text":this.draw=this._drawText,this.resize=this._resizeText;break;case"dot":this.draw=this._drawDot,this.resize=this._resizeShape;break;case"square":this.draw=this._drawSquare,this.resize=this._resizeShape;break;case"triangle":this.draw=this._drawTriangle,this.resize=this._resizeShape;break;case"triangleDown":this.draw=this._drawTriangleDown,this.resize=this._resizeShape;break;case"star":this.draw=this._drawStar,this.resize=this._resizeShape;break;default:this.draw=this._drawEllipse,this.resize=this._resizeEllipse}this._reset()}},s.prototype.select=function(){this.selected=!0,this._reset()},s.prototype.unselect=function(){this.selected=!1,this._reset()},s.prototype.clearSizeCache=function(){this._reset()},s.prototype._reset=function(){this.width=void 0,this.height=void 0},s.prototype.getTitle=function(){return"function"==typeof this.title?this.title():this.title},s.prototype.distanceToBorder=function(t,e){var i=1;switch(this.width||this.resize(t),this.options.shape){case"circle":case"dot":return this.options.radius+i;case"ellipse":var s=this.width/2,o=this.height/2,n=Math.sin(e)*s,r=Math.cos(e)*o;return s*o/Math.sqrt(n*n+r*r);case"box":case"image":case"text":default:return this.width?Math.min(Math.abs(this.width/2/Math.cos(e)),Math.abs(this.height/2/Math.sin(e)))+i:0}},s.prototype._setForce=function(t,e){this.fx=t,this.fy=e},s.prototype._addForce=function(t,e){this.fx+=t,this.fy+=e},s.prototype.storeState=function(){this.previousState.x=this.x,this.previousState.y=this.y,this.previousState.vx=this.vx,this.previousState.vy=this.vy},s.prototype.discreteStep=function(t){if(this.storeState(),this.xFixed)this.fx=0,this.vx=0;else{var e=this.damping*this.vx,i=(this.fx-e)/this.options.mass;this.vx+=i*t,this.x+=this.vx*t}if(this.yFixed)this.fy=0,this.vy=0;else{var s=this.damping*this.vy,o=(this.fy-s)/this.options.mass;this.vy+=o*t,this.y+=this.vy*t}},s.prototype.discreteStepLimited=function(t,e){if(this.storeState(),this.xFixed)this.fx=0,this.vx=0;else{var i=this.damping*this.vx,s=(this.fx-i)/this.options.mass;this.vx+=s*t,this.vx=Math.abs(this.vx)>e?this.vx>0?e:-e:this.vx,this.x+=this.vx*t}if(this.yFixed)this.fy=0,this.vy=0;else{var o=this.damping*this.vy,n=(this.fy-o)/this.options.mass;this.vy+=n*t,this.vy=Math.abs(this.vy)>e?this.vy>0?e:-e:this.vy,this.y+=this.vy*t}},s.prototype.isFixed=function(){return this.xFixed&&this.yFixed},s.prototype.isMoving=function(t){var e=Math.sqrt(Math.pow(this.vx,2)+Math.pow(this.vy,2));return e>t},s.prototype.isSelected=function(){return this.selected},s.prototype.getValue=function(){return this.value},s.prototype.getDistance=function(t,e){var i=this.x-t,s=this.y-e;return Math.sqrt(i*i+s*s)},s.prototype.setValueRange=function(t,e){if(!this.radiusFixed&&void 0!==this.value)if(e==t)this.options.radius=(this.options.radiusMin+this.options.radiusMax)/2;else{var i=(this.options.radiusMax-this.options.radiusMin)/(e-t);this.options.radius=(this.value-t)*i+this.options.radiusMin}this.baseRadiusValue=this.options.radius},s.prototype.draw=function(){throw"Draw method not initialized for node"},s.prototype.resize=function(){throw"Resize method not initialized for node"},s.prototype.isOverlappingWith=function(t){return this.leftt.left&&this.topt.top},s.prototype._resizeImage=function(){if(!this.width||!this.height){var t,e;if(this.value){this.options.radius=this.baseRadiusValue;var i=this.imageObj.height/this.imageObj.width;void 0!==i?(t=this.options.radius||this.imageObj.width,e=this.options.radius*i||this.imageObj.height):(t=0,e=0)}else t=this.imageObj.width,e=this.imageObj.height;this.width=t,this.height=e,this.growthIndicator=0,this.width>0&&this.height>0&&(this.width+=Math.min(this.clusterSize-1,this.maxNodeSizeIncrements)*this.clusterSizeWidthFactor,this.height+=Math.min(this.clusterSize-1,this.maxNodeSizeIncrements)*this.clusterSizeHeightFactor,this.options.radius+=Math.min(this.clusterSize-1,this.maxNodeSizeIncrements)*this.clusterSizeRadiusFactor,this.growthIndicator=this.width-t)}},s.prototype._drawImageAtPosition=function(t){if(0!=this.imageObj.width){if(this.clusterSize>1){var e=this.clusterSize>1?10:0;e*=this.networkScaleInv,e=Math.min(.2*this.width,e),t.globalAlpha=.5,t.drawImage(this.imageObj,this.left-e,this.top-e,this.width+2*e,this.height+2*e)}t.globalAlpha=1,t.drawImage(this.imageObj,this.left,this.top,this.width,this.height)}},s.prototype._drawImageLabel=function(t){var e,i=0;if(this.height){i=this.height/2;var s=this.getTextSize(t);s.lineCount>=1&&(i+=s.height/2,i+=3)}e=this.y+i,this._label(t,this.label,this.x,e,void 0)},s.prototype._drawImage=function(t){this._resizeImage(t),this.left=this.x-this.width/2,this.top=this.y-this.height/2,this._drawImageAtPosition(t),this.boundingBox.top=this.top,this.boundingBox.left=this.left,this.boundingBox.right=this.left+this.width,this.boundingBox.bottom=this.top+this.height,this._drawImageLabel(t),this.boundingBox.left=Math.min(this.boundingBox.left,this.labelDimensions.left),this.boundingBox.right=Math.max(this.boundingBox.right,this.labelDimensions.left+this.labelDimensions.width),this.boundingBox.bottom=Math.max(this.boundingBox.bottom,this.boundingBox.bottom+this.labelDimensions.height)},s.prototype._resizeCircularImage=function(t){if(this.imageObj.src&&this.imageObj.width&&this.imageObj.height)this._swapToImageResizeWhenImageLoaded&&(this.width=0,this.height=0,delete this._swapToImageResizeWhenImageLoaded),this._resizeImage(t);else if(!this.width){var e=2*this.options.radius;this.width=e,this.height=e,this.options.radius+=.5*Math.min(this.clusterSize-1,this.maxNodeSizeIncrements)*this.clusterSizeRadiusFactor,this.growthIndicator=this.options.radius-.5*e,this._swapToImageResizeWhenImageLoaded=!0}},s.prototype._drawCircularImage=function(t){this._resizeCircularImage(t),this.left=this.x-this.width/2,this.top=this.y-this.height/2;var e=this.left+this.width/2,i=this.top+this.height/2,s=Math.abs(this.height/2);this._drawRawCircle(t,e,i,s),t.save(),t.circle(this.x,this.y,s),t.stroke(),t.clip(),this._drawImageAtPosition(t),t.restore(),this.boundingBox.top=this.y-this.options.radius,this.boundingBox.left=this.x-this.options.radius,this.boundingBox.right=this.x+this.options.radius,this.boundingBox.bottom=this.y+this.options.radius,this._drawImageLabel(t),this.boundingBox.left=Math.min(this.boundingBox.left,this.labelDimensions.left),this.boundingBox.right=Math.max(this.boundingBox.right,this.labelDimensions.left+this.labelDimensions.width),this.boundingBox.bottom=Math.max(this.boundingBox.bottom,this.boundingBox.bottom+this.labelDimensions.height)},s.prototype._resizeBox=function(t){if(!this.width){var e=5,i=this.getTextSize(t);this.width=i.width+2*e,this.height=i.height+2*e,this.width+=.5*Math.min(this.clusterSize-1,this.maxNodeSizeIncrements)*this.clusterSizeWidthFactor,this.height+=.5*Math.min(this.clusterSize-1,this.maxNodeSizeIncrements)*this.clusterSizeHeightFactor,this.growthIndicator=this.width-(i.width+2*e)}},s.prototype._drawBox=function(t){this._resizeBox(t),this.left=this.x-this.width/2,this.top=this.y-this.height/2;var e=2.5,i=this.options.borderWidth,s=this.options.borderWidthSelected||2*this.options.borderWidth;t.strokeStyle=this.selected?this.options.color.highlight.border:this.hover?this.options.color.hover.border:this.options.color.border,this.clusterSize>1&&(t.lineWidth=(this.selected?s:i)+(this.clusterSize>1?e:0),t.lineWidth*=this.networkScaleInv,t.lineWidth=Math.min(this.width,t.lineWidth),t.roundRect(this.left-2*t.lineWidth,this.top-2*t.lineWidth,this.width+4*t.lineWidth,this.height+4*t.lineWidth,this.options.radius),t.stroke()),t.lineWidth=(this.selected?s:i)+(this.clusterSize>1?e:0),t.lineWidth*=this.networkScaleInv,t.lineWidth=Math.min(this.width,t.lineWidth),t.fillStyle=this.selected?this.options.color.highlight.background:this.hover?this.options.color.hover.background:this.options.color.background,t.roundRect(this.left,this.top,this.width,this.height,this.options.radius),t.fill(),t.stroke(),this.boundingBox.top=this.top,this.boundingBox.left=this.left,this.boundingBox.right=this.left+this.width,this.boundingBox.bottom=this.top+this.height,this._label(t,this.label,this.x,this.y) +},s.prototype._resizeDatabase=function(t){if(!this.width){var e=5,i=this.getTextSize(t),s=i.width+2*e;this.width=s,this.height=s,this.width+=Math.min(this.clusterSize-1,this.maxNodeSizeIncrements)*this.clusterSizeWidthFactor,this.height+=Math.min(this.clusterSize-1,this.maxNodeSizeIncrements)*this.clusterSizeHeightFactor,this.options.radius+=Math.min(this.clusterSize-1,this.maxNodeSizeIncrements)*this.clusterSizeRadiusFactor,this.growthIndicator=this.width-s}},s.prototype._drawDatabase=function(t){this._resizeDatabase(t),this.left=this.x-this.width/2,this.top=this.y-this.height/2;var e=2.5,i=this.options.borderWidth,s=this.options.borderWidthSelected||2*this.options.borderWidth;t.strokeStyle=this.selected?this.options.color.highlight.border:this.hover?this.options.color.hover.border:this.options.color.border,this.clusterSize>1&&(t.lineWidth=(this.selected?s:i)+(this.clusterSize>1?e:0),t.lineWidth*=this.networkScaleInv,t.lineWidth=Math.min(this.width,t.lineWidth),t.database(this.x-this.width/2-2*t.lineWidth,this.y-.5*this.height-2*t.lineWidth,this.width+4*t.lineWidth,this.height+4*t.lineWidth),t.stroke()),t.lineWidth=(this.selected?s:i)+(this.clusterSize>1?e:0),t.lineWidth*=this.networkScaleInv,t.lineWidth=Math.min(this.width,t.lineWidth),t.fillStyle=this.selected?this.options.color.highlight.background:this.hover?this.options.color.hover.background:this.options.color.background,t.database(this.x-this.width/2,this.y-.5*this.height,this.width,this.height),t.fill(),t.stroke(),this.boundingBox.top=this.top,this.boundingBox.left=this.left,this.boundingBox.right=this.left+this.width,this.boundingBox.bottom=this.top+this.height,this._label(t,this.label,this.x,this.y)},s.prototype._resizeCircle=function(t){if(!this.width){var e=5,i=this.getTextSize(t),s=Math.max(i.width,i.height)+2*e;this.options.radius=s/2,this.width=s,this.height=s,this.options.radius+=.5*Math.min(this.clusterSize-1,this.maxNodeSizeIncrements)*this.clusterSizeRadiusFactor,this.growthIndicator=this.options.radius-.5*s}},s.prototype._drawRawCircle=function(t,e,i,s){var o=2.5,n=this.options.borderWidth,r=this.options.borderWidthSelected||2*this.options.borderWidth;t.strokeStyle=this.selected?this.options.color.highlight.border:this.hover?this.options.color.hover.border:this.options.color.border,this.clusterSize>1&&(t.lineWidth=(this.selected?r:n)+(this.clusterSize>1?o:0),t.lineWidth*=this.networkScaleInv,t.lineWidth=Math.min(this.width,t.lineWidth),t.circle(e,i,s+2*t.lineWidth),t.stroke()),t.lineWidth=(this.selected?r:n)+(this.clusterSize>1?o:0),t.lineWidth*=this.networkScaleInv,t.lineWidth=Math.min(this.width,t.lineWidth),t.fillStyle=this.selected?this.options.color.highlight.background:this.hover?this.options.color.hover.background:this.options.color.background,t.circle(this.x,this.y,s),t.fill(),t.stroke()},s.prototype._drawCircle=function(t){this._resizeCircle(t),this.left=this.x-this.width/2,this.top=this.y-this.height/2,this._drawRawCircle(t,this.x,this.y,this.options.radius),this.boundingBox.top=this.y-this.options.radius,this.boundingBox.left=this.x-this.options.radius,this.boundingBox.right=this.x+this.options.radius,this.boundingBox.bottom=this.y+this.options.radius,this._label(t,this.label,this.x,this.y)},s.prototype._resizeEllipse=function(t){if(!this.width){var e=this.getTextSize(t);this.width=1.5*e.width,this.height=2*e.height,this.width1&&(t.lineWidth=(this.selected?s:i)+(this.clusterSize>1?e:0),t.lineWidth*=this.networkScaleInv,t.lineWidth=Math.min(this.width,t.lineWidth),t.ellipse(this.left-2*t.lineWidth,this.top-2*t.lineWidth,this.width+4*t.lineWidth,this.height+4*t.lineWidth),t.stroke()),t.lineWidth=(this.selected?s:i)+(this.clusterSize>1?e:0),t.lineWidth*=this.networkScaleInv,t.lineWidth=Math.min(this.width,t.lineWidth),t.fillStyle=this.selected?this.options.color.highlight.background:this.hover?this.options.color.hover.background:this.options.color.background,t.ellipse(this.left,this.top,this.width,this.height),t.fill(),t.stroke(),this.boundingBox.top=this.top,this.boundingBox.left=this.left,this.boundingBox.right=this.left+this.width,this.boundingBox.bottom=this.top+this.height,this._label(t,this.label,this.x,this.y)},s.prototype._drawDot=function(t){this._drawShape(t,"circle")},s.prototype._drawTriangle=function(t){this._drawShape(t,"triangle")},s.prototype._drawTriangleDown=function(t){this._drawShape(t,"triangleDown")},s.prototype._drawSquare=function(t){this._drawShape(t,"square")},s.prototype._drawStar=function(t){this._drawShape(t,"star")},s.prototype._resizeShape=function(){if(!this.width){this.options.radius=this.baseRadiusValue;var t=2*this.options.radius;this.width=t,this.height=t,this.width+=Math.min(this.clusterSize-1,this.maxNodeSizeIncrements)*this.clusterSizeWidthFactor,this.height+=Math.min(this.clusterSize-1,this.maxNodeSizeIncrements)*this.clusterSizeHeightFactor,this.options.radius+=.5*Math.min(this.clusterSize-1,this.maxNodeSizeIncrements)*this.clusterSizeRadiusFactor,this.growthIndicator=this.width-t}},s.prototype._drawShape=function(t,e){this._resizeShape(t),this.left=this.x-this.width/2,this.top=this.y-this.height/2;var i=2.5,s=this.options.borderWidth,o=this.options.borderWidthSelected||2*this.options.borderWidth,n=2;switch(e){case"dot":n=2;break;case"square":n=2;break;case"triangle":n=3;break;case"triangleDown":n=3;break;case"star":n=4}t.strokeStyle=this.selected?this.options.color.highlight.border:this.hover?this.options.color.hover.border:this.options.color.border,this.clusterSize>1&&(t.lineWidth=(this.selected?o:s)+(this.clusterSize>1?i:0),t.lineWidth*=this.networkScaleInv,t.lineWidth=Math.min(this.width,t.lineWidth),t[e](this.x,this.y,this.options.radius+n*t.lineWidth),t.stroke()),t.lineWidth=(this.selected?o:s)+(this.clusterSize>1?i:0),t.lineWidth*=this.networkScaleInv,t.lineWidth=Math.min(this.width,t.lineWidth),t.fillStyle=this.selected?this.options.color.highlight.background:this.hover?this.options.color.hover.background:this.options.color.background,t[e](this.x,this.y,this.options.radius),t.fill(),t.stroke(),this.boundingBox.top=this.y-this.options.radius,this.boundingBox.left=this.x-this.options.radius,this.boundingBox.right=this.x+this.options.radius,this.boundingBox.bottom=this.y+this.options.radius,this.label&&(this._label(t,this.label,this.x,this.y+this.height/2,void 0,"hanging",!0),this.boundingBox.left=Math.min(this.boundingBox.left,this.labelDimensions.left),this.boundingBox.right=Math.max(this.boundingBox.right,this.labelDimensions.left+this.labelDimensions.width),this.boundingBox.bottom=Math.max(this.boundingBox.bottom,this.boundingBox.bottom+this.labelDimensions.height))},s.prototype._resizeText=function(t){if(!this.width){var e=5,i=this.getTextSize(t);this.width=i.width+2*e,this.height=i.height+2*e,this.width+=Math.min(this.clusterSize-1,this.maxNodeSizeIncrements)*this.clusterSizeWidthFactor,this.height+=Math.min(this.clusterSize-1,this.maxNodeSizeIncrements)*this.clusterSizeHeightFactor,this.options.radius+=Math.min(this.clusterSize-1,this.maxNodeSizeIncrements)*this.clusterSizeRadiusFactor,this.growthIndicator=this.width-(i.width+2*e)}},s.prototype._drawText=function(t){this._resizeText(t),this.left=this.x-this.width/2,this.top=this.y-this.height/2,this._label(t,this.label,this.x,this.y),this.boundingBox.top=this.top,this.boundingBox.left=this.left,this.boundingBox.right=this.left+this.width,this.boundingBox.bottom=this.top+this.height},s.prototype._label=function(t,e,i,s,o,n,r){if(e&&Number(this.options.fontSize)*this.networkScale>this.fontDrawThreshold){t.font=(this.selected?"bold ":"")+this.options.fontSize+"px "+this.options.fontFace;var a=e.split("\n"),h=a.length,d=Number(this.options.fontSize),l=s+(1-h)/2*d;1==r&&(l=s+(1-h)/(2*d));for(var c=t.measureText(a[0]).width,p=1;h>p;p++){var u=t.measureText(a[p]).width;c=u>c?u:c}var m=this.options.fontSize*h,f=i-c/2,g=s-m/2;"hanging"==n&&(g+=.5*d,g+=4,l+=4),this.labelDimensions={top:g,left:f,width:c,height:m,yLine:l},void 0!==this.options.fontFill&&null!==this.options.fontFill&&"none"!==this.options.fontFill&&(t.fillStyle=this.options.fontFill,t.fillRect(f,g,c,m)),t.fillStyle=this.options.fontColor||"black",t.textAlign=o||"center",t.textBaseline=n||"middle",this.options.fontStrokeWidth>0&&(t.lineWidth=this.options.fontStrokeWidth,t.strokeStyle=this.options.fontStrokeColor,t.lineJoin="round");for(var p=0;h>p;p++)this.options.fontStrokeWidth&&t.strokeText(a[p],i,l),t.fillText(a[p],i,l),l+=d}},s.prototype.getTextSize=function(t){if(void 0!==this.label){t.font=(this.selected?"bold ":"")+this.options.fontSize+"px "+this.options.fontFace;for(var e=this.label.split("\n"),i=(Number(this.options.fontSize)+4)*e.length,s=0,o=0,n=e.length;n>o;o++)s=Math.max(s,t.measureText(e[o]).width);return{width:s,height:i,lineCount:e.length}}return{width:0,height:0,lineCount:0}},s.prototype.inArea=function(){return void 0!==this.width?this.x+this.width*this.networkScaleInv>=this.canvasTopLeft.x&&this.x-this.width*this.networkScaleInv=this.canvasTopLeft.y&&this.y-this.height*this.networkScaleInv=this.canvasTopLeft.x&&this.x=this.canvasTopLeft.y&&this.ys&&(n=s-e-this.padding),no&&(r=o-i-this.padding),ri;i++)if(e.id===r.nodes[i].id){o=r.nodes[i];break}for(o||(o={id:e.id},t.node&&(o.attr=a(o.attr,t.node))),i=n.length-1;i>=0;i--){var h=n[i];h.nodes||(h.nodes=[]),-1==h.nodes.indexOf(o)&&h.nodes.push(o)}e.attr&&(o.attr=a(o.attr,e.attr))}function l(t,e){if(t.edges||(t.edges=[]),t.edges.push(e),t.edge){var i=a({},t.edge);e.attr=a(i,e.attr)}}function c(t,e,i,s,o){var n={from:e,to:i,type:s};return t.edge&&(n.attr=a({},t.edge)),n.attr=a(n.attr||{},o),n}function p(){for(N=D.NULL,k="";" "==E||" "==E||"\n"==E||"\r"==E;)o();do{var t=!1;if("#"==E){for(var e=O-1;" "==T.charAt(e)||" "==T.charAt(e);)e--;if("\n"==T.charAt(e)||""==T.charAt(e)){for(;""!=E&&"\n"!=E;)o();t=!0}}if("/"==E&&"/"==n()){for(;""!=E&&"\n"!=E;)o();t=!0}if("/"==E&&"*"==n()){for(;""!=E;){if("*"==E&&"/"==n()){o(),o();break}o()}t=!0}for(;" "==E||" "==E||"\n"==E||"\r"==E;)o()}while(t);if(""==E)return void(N=D.DELIMITER);var i=E+n();if(C[i])return N=D.DELIMITER,k=i,o(),void o();if(C[E])return N=D.DELIMITER,k=E,void o();if(r(E)||"-"==E){for(k+=E,o();r(E);)k+=E,o();return"false"==k?k=!1:"true"==k?k=!0:isNaN(Number(k))||(k=Number(k)),void(N=D.IDENTIFIER)}if('"'==E){for(o();""!=E&&('"'!=E||'"'==E&&'"'==n());)k+=E,'"'==E&&o(),o();if('"'!=E)throw x('End of string " expected');return o(),void(N=D.IDENTIFIER)}for(N=D.UNKNOWN;""!=E;)k+=E,o();throw new SyntaxError('Syntax error in part "'+w(k,30)+'"')}function u(){var t={};if(s(),p(),"strict"==k&&(t.strict=!0,p()),("graph"==k||"digraph"==k)&&(t.type=k,p()),N==D.IDENTIFIER&&(t.id=k,p()),"{"!=k)throw x("Angle bracket { expected");if(p(),m(t),"}"!=k)throw x("Angle bracket } expected");if(p(),""!==k)throw x("End of file expected");return p(),delete t.node,delete t.edge,delete t.graph,t}function m(t){for(;""!==k&&"}"!=k;)f(t),";"==k&&p()}function f(t){var e=g(t);if(e)return void b(t,e);var i=v(t);if(!i){if(N!=D.IDENTIFIER)throw x("Identifier expected");var s=k;if(p(),"="==k){if(p(),N!=D.IDENTIFIER)throw x("Identifier expected");t[s]=k,p()}else y(t,s)}}function g(t){var e=null;if("subgraph"==k&&(e={},e.type="subgraph",p(),N==D.IDENTIFIER&&(e.id=k,p())),"{"==k){if(p(),e||(e={}),e.parent=t,e.node=t.node,e.edge=t.edge,e.graph=t.graph,m(e),"}"!=k)throw x("Angle bracket } expected");p(),delete e.node,delete e.edge,delete e.graph,delete e.parent,t.subgraphs||(t.subgraphs=[]),t.subgraphs.push(e)}return e}function v(t){return"node"==k?(p(),t.node=_(),"node"):"edge"==k?(p(),t.edge=_(),"edge"):"graph"==k?(p(),t.graph=_(),"graph"):null}function y(t,e){var i={id:e},s=_();s&&(i.attr=s),d(t,i),b(t,e)}function b(t,e){for(;"->"==k||"--"==k;){var i,s=k;p();var o=g(t);if(o)i=o;else{if(N!=D.IDENTIFIER)throw x("Identifier or subgraph expected");i=k,d(t,{id:i}),p()}var n=_(),r=c(t,e,i,s,n);l(t,r),e=i}}function _(){for(var t=null;"["==k;){for(p(),t={};""!==k&&"]"!=k;){if(N!=D.IDENTIFIER)throw x("Attribute name expected");var e=k;if(p(),"="!=k)throw x("Equal sign = expected");if(p(),N!=D.IDENTIFIER)throw x("Attribute value expected");var i=k;h(t,e,i),p(),","==k&&p()}if("]"!=k)throw x("Bracket ] expected");p()}return t}function x(t){return new SyntaxError(t+', got "'+w(k,30)+'" (char '+O+")")}function w(t,e){return t.length<=e?t:t.substr(0,27)+"..."}function M(t,e,i){Array.isArray(t)?t.forEach(function(t){Array.isArray(e)?e.forEach(function(e){i(t,e)}):i(t,e)}):Array.isArray(e)?e.forEach(function(e){i(t,e)}):i(t,e)}function S(t){var e=i(t),s={nodes:[],edges:[],options:{}};if(e.nodes&&e.nodes.forEach(function(t){var e={id:t.id,label:String(t.label||t.id)};a(e,t.attr),e.image&&(e.shape="image"),s.nodes.push(e)}),e.edges){var o=function(t){var e={from:t.from,to:t.to};return a(e,t.attr),e.style="->"==t.type?"arrow":"line",e};e.edges.forEach(function(t){var e,i;e=t.from instanceof Object?t.from.nodes:{id:t.from},i=t.to instanceof Object?t.to.nodes:{id:t.to},t.from instanceof Object&&t.from.edges&&t.from.edges.forEach(function(t){var e=o(t);s.edges.push(e)}),M(e,i,function(e,i){var n=c(s,e.id,i.id,t.type,t.attr),r=o(n);s.edges.push(r)}),t.to instanceof Object&&t.to.edges&&t.to.edges.forEach(function(t){var e=o(t);s.edges.push(e)})})}return e.attr&&(s.options=e.attr),s}var D={NULL:0,DELIMITER:1,IDENTIFIER:2,UNKNOWN:3},C={"{":!0,"}":!0,"[":!0,"]":!0,";":!0,"=":!0,",":!0,"->":!0,"--":!0},T="",O=0,E="",k="",N=D.NULL,L=/[a-zA-Z_0-9.:#]/;e.parseDOT=i,e.DOTToGraph=S},function(t,e){function i(t,e){var i=[],s=[];this.options={edges:{inheritColor:!0},nodes:{allowedToMove:!1,parseColor:!1}},void 0!==e&&(this.options.nodes.allowedToMove=e.allowedToMove|!1,this.options.nodes.parseColor=e.parseColor|!1,this.options.edges.inheritColor=e.inheritColor|!0);for(var o=t.edges,n=t.nodes,r=0;r=s&&(s=864e5),e=new Date(e.valueOf()-.05*s),i=new Date(i.valueOf()+.05*s)}return{start:e,end:i}},s.prototype.setWindow=function(t,e,i){var s=i&&void 0!==i.animate?i.animate:!0;if(1==arguments.length){var o=arguments[0];this.range.setRange(o.start,o.end,s)}else this.range.setRange(t,e,s)},s.prototype.moveTo=function(t,e){var i=this.range.end-this.range.start,s=r.convert(t,"Date").valueOf(),o=s-i/2,n=s+i/2,a=e&&void 0!==e.animate?e.animate:!0;this.range.setRange(o,n,a)},s.prototype.getWindow=function(){var t=this.range.getRange();return{start:new Date(t.start),end:new Date(t.end)}},s.prototype.redraw=function(){var t=!1,e=this.options,i=this.props,s=this.dom;if(s){h.updateHiddenDates(this.body,this.options.hiddenDates),"top"==e.orientation?(r.addClassName(s.root,"top"),r.removeClassName(s.root,"bottom")):(r.removeClassName(s.root,"top"),r.addClassName(s.root,"bottom")),s.root.style.maxHeight=r.option.asSize(e.maxHeight,""),s.root.style.minHeight=r.option.asSize(e.minHeight,""),s.root.style.width=r.option.asSize(e.width,""),i.border.left=(s.centerContainer.offsetWidth-s.centerContainer.clientWidth)/2,i.border.right=i.border.left,i.border.top=(s.centerContainer.offsetHeight-s.centerContainer.clientHeight)/2,i.border.bottom=i.border.top;var o=s.root.offsetHeight-s.root.clientHeight,n=s.root.offsetWidth-s.root.clientWidth;0===s.centerContainer.clientHeight&&(i.border.left=i.border.top,i.border.right=i.border.left),0===s.root.clientHeight&&(n=o),i.center.height=s.center.offsetHeight,i.left.height=s.left.offsetHeight,i.right.height=s.right.offsetHeight,i.top.height=s.top.clientHeight||-i.border.top,i.bottom.height=s.bottom.clientHeight||-i.border.bottom;var a=Math.max(i.left.height,i.center.height,i.right.height),d=i.top.height+a+i.bottom.height+o+i.border.top+i.border.bottom;s.root.style.height=r.option.asSize(e.height,d+"px"),i.root.height=s.root.offsetHeight,i.background.height=i.root.height-o;var l=i.root.height-i.top.height-i.bottom.height-o;i.centerContainer.height=l,i.leftContainer.height=l,i.rightContainer.height=i.leftContainer.height,i.root.width=s.root.offsetWidth,i.background.width=i.root.width-n,i.left.width=s.leftContainer.clientWidth||-i.border.left,i.leftContainer.width=i.left.width,i.right.width=s.rightContainer.clientWidth||-i.border.right,i.rightContainer.width=i.right.width;var c=i.root.width-i.left.width-i.right.width-n;i.center.width=c,i.centerContainer.width=c,i.top.width=c,i.bottom.width=c,s.background.style.height=i.background.height+"px",s.backgroundVertical.style.height=i.background.height+"px",s.backgroundHorizontal.style.height=i.centerContainer.height+"px",s.centerContainer.style.height=i.centerContainer.height+"px",s.leftContainer.style.height=i.leftContainer.height+"px",s.rightContainer.style.height=i.rightContainer.height+"px",s.background.style.width=i.background.width+"px",s.backgroundVertical.style.width=i.centerContainer.width+"px",s.backgroundHorizontal.style.width=i.background.width+"px",s.centerContainer.style.width=i.center.width+"px",s.top.style.width=i.top.width+"px",s.bottom.style.width=i.bottom.width+"px",s.background.style.left="0",s.background.style.top="0",s.backgroundVertical.style.left=i.left.width+i.border.left+"px",s.backgroundVertical.style.top="0",s.backgroundHorizontal.style.left="0",s.backgroundHorizontal.style.top=i.top.height+"px",s.centerContainer.style.left=i.left.width+"px",s.centerContainer.style.top=i.top.height+"px",s.leftContainer.style.left="0",s.leftContainer.style.top=i.top.height+"px",s.rightContainer.style.left=i.left.width+i.center.width+"px",s.rightContainer.style.top=i.top.height+"px",s.top.style.left=i.left.width+"px",s.top.style.top="0",s.bottom.style.left=i.left.width+"px",s.bottom.style.top=i.top.height+i.centerContainer.height+"px",this._updateScrollTop();var p=this.props.scrollTop;"bottom"==e.orientation&&(p+=Math.max(this.props.centerContainer.height-this.props.center.height-this.props.border.top-this.props.border.bottom,0)),s.center.style.left="0",s.center.style.top=p+"px",s.left.style.left="0",s.left.style.top=p+"px",s.right.style.left="0",s.right.style.top=p+"px";var u=0==this.props.scrollTop?"hidden":"",m=this.props.scrollTop==this.props.scrollTopMin?"hidden":"";if(s.shadowTop.style.visibility=u,s.shadowBottom.style.visibility=m,s.shadowTopLeft.style.visibility=u,s.shadowBottomLeft.style.visibility=m,s.shadowTopRight.style.visibility=u,s.shadowBottomRight.style.visibility=m,this.components.forEach(function(e){t=e.redraw()||t}),t){var f=3;this.redrawCount0&&(this.props.scrollTop=0),this.props.scrollTops;s++){var o=s%2===0?1.3*i:.5*i;this.lineTo(t+o*Math.sin(2*s*Math.PI/10),e-o*Math.cos(2*s*Math.PI/10))}this.closePath()},CanvasRenderingContext2D.prototype.roundRect=function(t,e,i,s,o){var n=Math.PI/180;0>i-2*o&&(o=i/2),0>s-2*o&&(o=s/2),this.beginPath(),this.moveTo(t+o,e),this.lineTo(t+i-o,e),this.arc(t+i-o,e+o,o,270*n,360*n,!1),this.lineTo(t+i,e+s-o),this.arc(t+i-o,e+s-o,o,0,90*n,!1),this.lineTo(t+o,e+s),this.arc(t+o,e+s-o,o,90*n,180*n,!1),this.lineTo(t,e+o),this.arc(t+o,e+o,o,180*n,270*n,!1)},CanvasRenderingContext2D.prototype.ellipse=function(t,e,i,s){var o=.5522848,n=i/2*o,r=s/2*o,a=t+i,h=e+s,d=t+i/2,l=e+s/2;this.beginPath(),this.moveTo(t,l),this.bezierCurveTo(t,l-r,d-n,e,d,e),this.bezierCurveTo(d+n,e,a,l-r,a,l),this.bezierCurveTo(a,l+r,d+n,h,d,h),this.bezierCurveTo(d-n,h,t,l+r,t,l)},CanvasRenderingContext2D.prototype.database=function(t,e,i,s){var o=1/3,n=i,r=s*o,a=.5522848,h=n/2*a,d=r/2*a,l=t+n,c=e+r,p=t+n/2,u=e+r/2,m=e+(s-r/2),f=e+s;this.beginPath(),this.moveTo(l,u),this.bezierCurveTo(l,u+d,p+h,c,p,c),this.bezierCurveTo(p-h,c,t,u+d,t,u),this.bezierCurveTo(t,u-d,p-h,e,p,e),this.bezierCurveTo(p+h,e,l,u-d,l,u),this.lineTo(l,m),this.bezierCurveTo(l,m+d,p+h,f,p,f),this.bezierCurveTo(p-h,f,t,m+d,t,m),this.lineTo(t,u)},CanvasRenderingContext2D.prototype.arrow=function(t,e,i,s){var o=t-s*Math.cos(i),n=e-s*Math.sin(i),r=t-.9*s*Math.cos(i),a=e-.9*s*Math.sin(i),h=o+s/3*Math.cos(i+.5*Math.PI),d=n+s/3*Math.sin(i+.5*Math.PI),l=o+s/3*Math.cos(i-.5*Math.PI),c=n+s/3*Math.sin(i-.5*Math.PI);this.beginPath(),this.moveTo(t,e),this.lineTo(h,d),this.lineTo(r,a),this.lineTo(l,c),this.closePath()},CanvasRenderingContext2D.prototype.dashedLine=function(t,e,i,s,o){o||(o=[10,5]),0==p&&(p=.001);var n=o.length;this.moveTo(t,e);for(var r=i-t,a=s-e,h=a/r,d=Math.sqrt(r*r+a*a),l=0,c=!0;d>=.1;){var p=o[l++%n];p>d&&(p=d);var u=Math.sqrt(p*p/(1+h*h));0>r&&(u=-u),t+=u,e+=h*u,this[c?"lineTo":"moveTo"](t,e),d-=p,c=!c}})},function(t,e,i){function s(t,e){this.groupId=t,this.options=e}var o=i(2),n=i(53);s.prototype.getYRange=function(t){for(var e=t[0].y,i=t[0].y,s=0;st[s].y?t[s].y:e,i=i0){var r,a,h=Number(i.svg.style.height.replace("px",""));if(r=o.getSVGElement("path",i.svgElements,i.svg),r.setAttributeNS(null,"class",e.className),void 0!==e.style&&r.setAttributeNS(null,"style",e.style),a=1==e.options.catmullRom.enabled?s._catmullRom(t,e):s._linear(t),1==e.options.shaded.enabled){var d,l=o.getSVGElement("path",i.svgElements,i.svg);d="top"==e.options.shaded.orientation?"M"+t[0].x+",0 "+a+"L"+t[t.length-1].x+",0":"M"+t[0].x+","+h+" "+a+"L"+t[t.length-1].x+","+h,l.setAttributeNS(null,"class",e.className+" fill"),void 0!==e.options.shaded.style&&l.setAttributeNS(null,"style",e.options.shaded.style),l.setAttributeNS(null,"d",d)}r.setAttributeNS(null,"d","M"+a),1==e.options.drawPoints.enabled&&n.draw(t,e,i)}},s._catmullRomUniform=function(t){for(var e,i,s,o,n,r,a=Math.round(t[0].x)+","+Math.round(t[0].y)+" ",h=1/6,d=t.length,l=0;d-1>l;l++)e=0==l?t[0]:t[l-1],i=t[l],s=t[l+1],o=d>l+2?t[l+2]:s,n={x:(-e.x+6*i.x+s.x)*h,y:(-e.y+6*i.y+s.y)*h},r={x:(i.x+6*s.x-o.x)*h,y:(i.y+6*s.y-o.y)*h},a+="C"+n.x+","+n.y+" "+r.x+","+r.y+" "+s.x+","+s.y+" ";return a},s._catmullRom=function(t,e){var i=e.options.catmullRom.alpha;if(0==i||void 0===i)return this._catmullRomUniform(t);for(var s,o,n,r,a,h,d,l,c,p,u,m,f,g,v,y,b,_,x,w=Math.round(t[0].x)+","+Math.round(t[0].y)+" ",M=t.length,S=0;M-1>S;S++)s=0==S?t[0]:t[S-1],o=t[S],n=t[S+1],r=M>S+2?t[S+2]:n,d=Math.sqrt(Math.pow(s.x-o.x,2)+Math.pow(s.y-o.y,2)),l=Math.sqrt(Math.pow(o.x-n.x,2)+Math.pow(o.y-n.y,2)),c=Math.sqrt(Math.pow(n.x-r.x,2)+Math.pow(n.y-r.y,2)),g=Math.pow(c,i),y=Math.pow(c,2*i),v=Math.pow(l,i),b=Math.pow(l,2*i),x=Math.pow(d,i),_=Math.pow(d,2*i),p=2*_+3*x*v+b,u=2*y+3*g*v+b,m=3*x*(x+v),m>0&&(m=1/m),f=3*g*(g+v),f>0&&(f=1/f),a={x:(-b*s.x+p*o.x+_*n.x)*m,y:(-b*s.y+p*o.y+_*n.y)*m},h={x:(y*o.x+u*n.x-b*r.x)*f,y:(y*o.y+u*n.y-b*r.y)*f},0==a.x&&0==a.y&&(a=o),0==h.x&&0==h.y&&(h=n),w+="C"+a.x+","+a.y+" "+h.x+","+h.y+" "+n.x+","+n.y+" ";return w},s._linear=function(t){for(var e="",i=0;it[s].y?t[s].y:e,i=i0&&(n=Math.min(n,Math.abs(c[d-1].x-r))),a=s._getSafeDrawData(n,h,m);else{var g=d+(p[r].amount-p[r].resolved),v=d-(p[r].resolved+1);g0&&(n=Math.min(n,Math.abs(c[v].x-r))),a=s._getSafeDrawData(n,h,m),p[r].resolved+=1,"stack"==h.options.barChart.handleOverlap?(f=p[r].accumulated,p[r].accumulated+=h.zeroPosition-c[d].y):"sideBySide"==h.options.barChart.handleOverlap&&(a.width=a.width/p[r].amount,a.offset+=p[r].resolved*a.width-.5*a.width*(p[r].amount+1),"left"==h.options.barChart.align?a.offset-=.5*a.width:"right"==h.options.barChart.align&&(a.offset+=.5*a.width))}o.drawBar(c[d].x+a.offset,c[d].y-f,a.width,h.zeroPosition-c[d].y,h.className+" bar",i.svgElements,i.svg),1==h.options.drawPoints.enabled&&o.drawPoint(c[d].x+a.offset,c[d].y,h,i.svgElements,i.svg)}},s._getDataIntersections=function(t,e){for(var i,s=0;s0&&(i=Math.min(i,Math.abs(e[s-1].x-e[s].x))),0==i&&(void 0===t[e[s].x]&&(t[e[s].x]={amount:0,resolved:0,accumulated:0}),t[e[s].x].amount+=1)},s._getSafeDrawData=function(t,e,i){var s,o;return t0?(s=i>t?i:t,o=0,"left"==e.options.barChart.align?o-=.5*t:"right"==e.options.barChart.align&&(o+=.5*t)):(s=e.options.barChart.width,o=0,"left"==e.options.barChart.align?o-=.5*e.options.barChart.width:"right"==e.options.barChart.align&&(o+=.5*e.options.barChart.width)),{width:s,offset:o}},s.getStackedBarYRange=function(t,e,i,o,n){if(t.length>0){t.sort(function(t,e){return t.x==e.x?t.groupId-e.groupId:t.x-e.x});var r={};s._getDataIntersections(r,t),e[o]=s._getStackedBarYRange(r,t),e[o].yAxisOrientation=n,i.push(o)}},s._getStackedBarYRange=function(t,e){for(var i,s=e[0].y,o=e[0].y,n=0;ne[n].y?e[n].y:s,o=ot[r].accumulated?t[r].accumulated:s,o=ot[s].y?t[s].y:e,i=is;++s)i[s].apply(this,e)}return this},e.prototype.listeners=function(t){return this._callbacks=this._callbacks||{},this._callbacks[t]||[]},e.prototype.hasListeners=function(t){return!!this.listeners(t).length}},function(t,e){e.startWithClustering=function(){this.clusterToFit(this.constants.clustering.initialMaxNodes,!0),this.updateLabels(),this.stabilize&&this._stabilize(),this.start()},e.clusterToFit=function(t,e){for(var i=this.nodeIndices.length,s=50,o=0;i>t&&s>o;)o%3==0?(this.forceAggregateHubs(!0),this.normalizeClusterLevels()):this.increaseClusterLevel(),i=this.nodeIndices.length,o+=1;o>0&&1==e&&this.repositionNodes(),this._updateCalculationNodes()},e.openCluster=function(t){var e=this.moving;if(t.clusterSize>this.constants.clustering.sectorThreshold&&this._nodeInActiveArea(t)&&("default"!=this._sector()||1!=this.nodeIndices.length)){this._addSector(t);for(var i=0;this.nodeIndices.lengthi;)this.decreaseClusterLevel(),i+=1}else this._expandClusterNode(t,!1,!0),this._updateNodeIndexList(),this._updateDynamicEdges(),this._updateCalculationNodes(),this.updateLabels();this.moving!=e&&this.start()},e.updateClustersDefault=function(){1==this.constants.clustering.enabled&&this.updateClusters(0,!1,!1)},e.increaseClusterLevel=function(){this.updateClusters(-1,!1,!0)},e.decreaseClusterLevel=function(){this.updateClusters(1,!1,!0)},e.updateClusters=function(t,e,i,s){var o=this.moving,n=this.nodeIndices.length;this.previousScale>this.scale&&0==t&&this._collapseSector(),this.previousScale>this.scale||-1==t?this._formClusters(i):(this.previousScalethis.scale||-1==t)&&(this._aggregateHubs(i),this._updateNodeIndexList()),(this.previousScale>this.scale||-1==t)&&(this.handleChains(),this._updateNodeIndexList()),this.previousScale=this.scale,this._updateDynamicEdges(),this.updateLabels(),this.nodeIndices.lengththis.constants.clustering.chainThreshold&&this._reduceAmountOfChains(1-this.constants.clustering.chainThreshold/t)},e._aggregateHubs=function(t){this._getHubSize(),this._formClustersByHub(t,!1)},e.forceAggregateHubs=function(t){var e=this.moving,i=this.nodeIndices.length;this._aggregateHubs(!0),this._updateNodeIndexList(),this._updateDynamicEdges(),this.updateLabels(),this.nodeIndices.length!=i&&(this.clusterSession+=1),(0==t||void 0===t)&&this.moving!=e&&this.start()},e._openClustersBySize=function(){for(var t in this.nodes)if(this.nodes.hasOwnProperty(t)){var e=this.nodes[t];1==e.inView()&&(e.width*this.scale>this.constants.clustering.screenSizeThreshold*this.frame.canvas.clientWidth||e.height*this.scale>this.constants.clustering.screenSizeThreshold*this.frame.canvas.clientHeight)&&this.openCluster(e)}},e._openClusters=function(t,e){for(var i=0;i1&&(t.clusterSizei)){var r=n.from,a=n.to;n.to.options.mass>n.from.options.mass&&(r=n.to,a=n.from),1==a.dynamicEdgesLength?this._addToCluster(r,a,!1):1==r.dynamicEdgesLength&&this._addToCluster(a,r,!1)}}},e._forceClustersByZoom=function(){for(var t in this.nodes)if(this.nodes.hasOwnProperty(t)){var e=this.nodes[t];if(1==e.dynamicEdgesLength&&0!=e.dynamicEdges.length){var i=e.dynamicEdges[0],s=i.toId==e.id?this.nodes[i.fromId]:this.nodes[i.toId];e.id!=s.id&&(s.options.mass>e.options.mass?this._addToCluster(s,e,!0):this._addToCluster(e,s,!0))}}},e._clusterToSmallestNeighbour=function(t){for(var e=-1,i=null,s=0;so.clusterSessions.length&&(e=o.clusterSessions.length,i=o)}null!=o&&void 0!==this.nodes[o.id]&&this._addToCluster(o,t,!0)},e._formClustersByHub=function(t,e){for(var i in this.nodes)this.nodes.hasOwnProperty(i)&&this._formClusterFromHub(this.nodes[i],t,e)},e._formClusterFromHub=function(t,e,i,s){if(void 0===s&&(s=0),t.dynamicEdgesLength>=this.hubThreshold&&0==i||t.dynamicEdgesLength==this.hubThreshold&&1==i){for(var o,n,r,a=this.constants.clustering.clusterEdgeThreshold/this.scale,h=!1,d=[],l=t.dynamicEdges.length,c=0;l>c;c++)d.push(t.dynamicEdges[c].id);if(0==e)for(h=!1,c=0;l>c;c++){var p=this.edges[d[c]];if(void 0!==p&&p.connected&&p.toId!=p.fromId&&(o=p.to.x-p.from.x,n=p.to.y-p.from.y,r=Math.sqrt(o*o+n*n),a>r)){h=!0;break}}if(!e&&h||e)for(c=0;l>c;c++)if(p=this.edges[d[c]],void 0!==p){var u=this.nodes[p.fromId==t.id?p.toId:p.fromId];u.dynamicEdges.length<=this.hubThreshold+s&&u.id!=t.id&&this._addToCluster(t,u,e)}}},e._addToCluster=function(t,e,i){t.containedNodes[e.id]=e;for(var s=0;s1)for(var s=0;s1&&(e.label="[".concat(String(e.clusterSize),"]"))}for(t in this.nodes)this.nodes.hasOwnProperty(t)&&(e=this.nodes[t],1==e.clusterSize&&(e.label=void 0!==e.originalLabel?e.originalLabel:String(e.id)))},e.normalizeClusterLevels=function(){var t,e=0,i=1e9,s=0;for(t in this.nodes)this.nodes.hasOwnProperty(t)&&(s=this.nodes[t].clusterSessions.length,s>e&&(e=s),i>s&&(i=s));if(e-i>this.constants.clustering.clusterLevelDifference){var o=this.nodeIndices.length,n=e-this.constants.clustering.clusterLevelDifference;for(t in this.nodes)this.nodes.hasOwnProperty(t)&&this.nodes[t].clusterSessions.lengths&&(s=n.dynamicEdgesLength),t+=n.dynamicEdgesLength,e+=Math.pow(n.dynamicEdgesLength,2),i+=1}t/=i,e/=i;var r=e-Math.pow(t,2),a=Math.sqrt(r);this.hubThreshold=Math.floor(t+2*a),this.hubThreshold>s&&(this.hubThreshold=s)},e._reduceAmountOfChains=function(t){this.hubThreshold=2;var e=Math.floor(this.nodeIndices.length*t);for(var i in this.nodes)this.nodes.hasOwnProperty(i)&&2==this.nodes[i].dynamicEdgesLength&&this.nodes[i].dynamicEdges.length>=2&&e>0&&(this._formClusterFromHub(this.nodes[i],!0,!0,1),e-=1)},e._getChainFraction=function(){var t=0,e=0;for(var i in this.nodes)this.nodes.hasOwnProperty(i)&&(2==this.nodes[i].dynamicEdgesLength&&this.nodes[i].dynamicEdges.length>=2&&(t+=1),e+=1);return t/e}},function(t,e,i){var s=i(1),o=i(40);e._putDataInSector=function(){this.sectors.active[this._sector()].nodes=this.nodes,this.sectors.active[this._sector()].edges=this.edges,this.sectors.active[this._sector()].nodeIndices=this.nodeIndices},e._switchToSector=function(t,e){void 0===e||"active"==e?this._switchToActiveSector(t):this._switchToFrozenSector(t)},e._switchToActiveSector=function(t){this.nodeIndices=this.sectors.active[t].nodeIndices,this.nodes=this.sectors.active[t].nodes,this.edges=this.sectors.active[t].edges},e._switchToSupportSector=function(){this.nodeIndices=this.sectors.support.nodeIndices,this.nodes=this.sectors.support.nodes,this.edges=this.sectors.support.edges},e._switchToFrozenSector=function(t){this.nodeIndices=this.sectors.frozen[t].nodeIndices,this.nodes=this.sectors.frozen[t].nodes,this.edges=this.sectors.frozen[t].edges},e._loadLatestSector=function(){this._switchToSector(this._sector())},e._sector=function(){return this.activeSector[this.activeSector.length-1]},e._previousSector=function(){if(this.activeSector.length>1)return this.activeSector[this.activeSector.length-2];throw new TypeError("there are not enough sectors in the this.activeSector array.")},e._setActiveSector=function(t){this.activeSector.push(t)},e._forgetLastSector=function(){this.activeSector.pop()},e._createNewSector=function(t){this.sectors.active[t]={nodes:{},edges:{},nodeIndices:[],formationScale:this.scale,drawingNode:void 0},this.sectors.active[t].drawingNode=new o({id:t,color:{background:"#eaefef",border:"495c5e"}},{},{},this.constants),this.sectors.active[t].drawingNode.clusterSize=2},e._deleteActiveSector=function(t){delete this.sectors.active[t]},e._deleteFrozenSector=function(t){delete this.sectors.frozen[t]},e._freezeSector=function(t){this.sectors.frozen[t]=this.sectors.active[t],this._deleteActiveSector(t)},e._activateSector=function(t){this.sectors.active[t]=this.sectors.frozen[t],this._deleteFrozenSector(t)},e._mergeThisWithFrozen=function(t){for(var e in this.nodes)this.nodes.hasOwnProperty(e)&&(this.sectors.frozen[t].nodes[e]=this.nodes[e]);for(var i in this.edges)this.edges.hasOwnProperty(i)&&(this.sectors.frozen[t].edges[i]=this.edges[i]);for(var s=0;s1?this[t](o[0],o[1]):this[t](e))}return this._loadLatestSector(),i},e._doInSupportSector=function(t,e){var i=!1;if(void 0===e)this._switchToSupportSector(),i=this[t]();else{this._switchToSupportSector();var s=Array.prototype.splice.call(arguments,1);i=s.length>1?this[t](s[0],s[1]):this[t](e)}return this._loadLatestSector(),i},e._doInAllFrozenSectors=function(t,e){if(void 0===e)for(var i in this.sectors.frozen)this.sectors.frozen.hasOwnProperty(i)&&(this._switchToFrozenSector(i),this[t]());else for(var i in this.sectors.frozen)if(this.sectors.frozen.hasOwnProperty(i)){this._switchToFrozenSector(i);var s=Array.prototype.splice.call(arguments,1);s.length>1?this[t](s[0],s[1]):this[t](e)}this._loadLatestSector()},e._doInAllSectors=function(t,e){var i=Array.prototype.splice.call(arguments,1);void 0===e?(this._doInAllActiveSectors(t),this._doInAllFrozenSectors(t)):i.length>1?(this._doInAllActiveSectors(t,i[0],i[1]),this._doInAllFrozenSectors(t,i[0],i[1])):(this._doInAllActiveSectors(t,e),this._doInAllFrozenSectors(t,e))},e._clearNodeIndexList=function(){var t=this._sector();this.sectors.active[t].nodeIndices=[],this.nodeIndices=this.sectors.active[t].nodeIndices},e._drawSectorNodes=function(t,e){var i,s=1e9,o=-1e9,n=1e9,r=-1e9;for(var a in this.sectors[e])if(this.sectors[e].hasOwnProperty(a)&&void 0!==this.sectors[e][a].drawingNode){this._switchToSector(a,e),s=1e9,o=-1e9,n=1e9,r=-1e9;for(var h in this.nodes)this.nodes.hasOwnProperty(h)&&(i=this.nodes[h],i.resize(t),n>i.x-.5*i.width&&(n=i.x-.5*i.width),ri.y-.5*i.height&&(s=i.y-.5*i.height),o0?this.nodes[i[i.length-1]]:null},e._getEdgesOverlappingWith=function(t,e){var i=this.edges;for(var s in i)i.hasOwnProperty(s)&&i[s].isOverlappingWith(t)&&e.push(s)},e._getAllEdgesOverlappingWith=function(t){var e=[];return this._doInAllActiveSectors("_getEdgesOverlappingWith",t,e),e},e._getEdgeAt=function(t){var e=this._pointerToPositionObject(t),i=this._getAllEdgesOverlappingWith(e);return i.length>0?this.edges[i[i.length-1]]:null +},e._addToSelection=function(t){t instanceof s?this.selectionObj.nodes[t.id]=t:this.selectionObj.edges[t.id]=t},e._addToHover=function(t){t instanceof s?this.hoverObj.nodes[t.id]=t:this.hoverObj.edges[t.id]=t},e._removeFromSelection=function(t){t instanceof s?delete this.selectionObj.nodes[t.id]:delete this.selectionObj.edges[t.id]},e._unselectAll=function(t){void 0===t&&(t=!1);for(var e in this.selectionObj.nodes)this.selectionObj.nodes.hasOwnProperty(e)&&this.selectionObj.nodes[e].unselect();for(var i in this.selectionObj.edges)this.selectionObj.edges.hasOwnProperty(i)&&this.selectionObj.edges[i].unselect();this.selectionObj={nodes:{},edges:{}},0==t&&this.emit("select",this.getSelection())},e._unselectClusters=function(t){void 0===t&&(t=!1);for(var e in this.selectionObj.nodes)this.selectionObj.nodes.hasOwnProperty(e)&&this.selectionObj.nodes[e].clusterSize>1&&(this.selectionObj.nodes[e].unselect(),this._removeFromSelection(this.selectionObj.nodes[e]));0==t&&this.emit("select",this.getSelection())},e._getSelectedNodeCount=function(){var t=0;for(var e in this.selectionObj.nodes)this.selectionObj.nodes.hasOwnProperty(e)&&(t+=1);return t},e._getSelectedNode=function(){for(var t in this.selectionObj.nodes)if(this.selectionObj.nodes.hasOwnProperty(t))return this.selectionObj.nodes[t];return null},e._getSelectedEdge=function(){for(var t in this.selectionObj.edges)if(this.selectionObj.edges.hasOwnProperty(t))return this.selectionObj.edges[t];return null},e._getSelectedEdgeCount=function(){var t=0;for(var e in this.selectionObj.edges)this.selectionObj.edges.hasOwnProperty(e)&&(t+=1);return t},e._getSelectedObjectCount=function(){var t=0;for(var e in this.selectionObj.nodes)this.selectionObj.nodes.hasOwnProperty(e)&&(t+=1);for(var i in this.selectionObj.edges)this.selectionObj.edges.hasOwnProperty(i)&&(t+=1);return t},e._selectionIsEmpty=function(){for(var t in this.selectionObj.nodes)if(this.selectionObj.nodes.hasOwnProperty(t))return!1;for(var e in this.selectionObj.edges)if(this.selectionObj.edges.hasOwnProperty(e))return!1;return!0},e._clusterInSelection=function(){for(var t in this.selectionObj.nodes)if(this.selectionObj.nodes.hasOwnProperty(t)&&this.selectionObj.nodes[t].clusterSize>1)return!0;return!1},e._selectConnectedEdges=function(t){for(var e=0;ei;i++){o=t[i];var n=this.nodes[o];if(!n)throw new RangeError('Node with id "'+o+'" not found');this._selectObject(n,!0,!0,e,!0)}this.redraw()},e.selectEdges=function(t){var e,i,s;if(!t||void 0==t.length)throw"Selection must be an array with ids";for(this._unselectAll(!0),e=0,i=t.length;i>e;e++){s=t[e];var o=this.edges[s];if(!o)throw new RangeError('Edge with id "'+s+'" not found');this._selectObject(o,!0,!0,!1,!0)}this.redraw()},e._updateSelection=function(){for(var t in this.selectionObj.nodes)this.selectionObj.nodes.hasOwnProperty(t)&&(this.nodes.hasOwnProperty(t)||delete this.selectionObj.nodes[t]);for(var e in this.selectionObj.edges)this.selectionObj.edges.hasOwnProperty(e)&&(this.edges.hasOwnProperty(e)||delete this.selectionObj.edges[e])}},function(t,e,i){var s=i(1),o=i(40),n=i(37);e._clearManipulatorBar=function(){this._recursiveDOMDelete(this.manipulationDiv),this.manipulationDOM={},this._manipulationReleaseOverload=function(){},delete this.sectors.support.nodes.targetNode,delete this.sectors.support.nodes.targetViaNode,this.controlNodesActive=!1,this.freezeSimulation=!1},e._restoreOverloadedFunctions=function(){for(var t in this.cachedFunctions)this.cachedFunctions.hasOwnProperty(t)&&(this[t]=this.cachedFunctions[t],delete this.cachedFunctions[t])},e._toggleEditMode=function(){this.editMode=!this.editMode;var t=this.manipulationDiv,e=this.closeDiv,i=this.editModeDiv;1==this.editMode?(t.style.display="block",e.style.display="block",i.style.display="none",e.onclick=this._toggleEditMode.bind(this)):(t.style.display="none",e.style.display="none",i.style.display="block",e.onclick=null),this._createManipulatorBar()},e._createManipulatorBar=function(){this.boundFunction&&this.off("select",this.boundFunction);var t=this.constants.locales[this.constants.locale];if(void 0!==this.edgeBeingEdited&&(this.edgeBeingEdited._disableControlNodes(),this.edgeBeingEdited=void 0,this.selectedControlNode=null,this.controlNodesActive=!1,this._redraw()),this._restoreOverloadedFunctions(),this.freezeSimulation=!1,this.blockConnectingEdgeSelection=!1,this.forceAppendSelection=!1,this.manipulationDOM={},1==this.editMode){for(;this.manipulationDiv.hasChildNodes();)this.manipulationDiv.removeChild(this.manipulationDiv.firstChild);this.manipulationDOM.addNodeSpan=document.createElement("span"),this.manipulationDOM.addNodeSpan.className="network-manipulationUI add",this.manipulationDOM.addNodeLabelSpan=document.createElement("span"),this.manipulationDOM.addNodeLabelSpan.className="network-manipulationLabel",this.manipulationDOM.addNodeLabelSpan.innerHTML=t.addNode,this.manipulationDOM.addNodeSpan.appendChild(this.manipulationDOM.addNodeLabelSpan),this.manipulationDOM.seperatorLineDiv1=document.createElement("div"),this.manipulationDOM.seperatorLineDiv1.className="network-seperatorLine",this.manipulationDOM.addEdgeSpan=document.createElement("span"),this.manipulationDOM.addEdgeSpan.className="network-manipulationUI connect",this.manipulationDOM.addEdgeLabelSpan=document.createElement("span"),this.manipulationDOM.addEdgeLabelSpan.className="network-manipulationLabel",this.manipulationDOM.addEdgeLabelSpan.innerHTML=t.addEdge,this.manipulationDOM.addEdgeSpan.appendChild(this.manipulationDOM.addEdgeLabelSpan),this.manipulationDiv.appendChild(this.manipulationDOM.addNodeSpan),this.manipulationDiv.appendChild(this.manipulationDOM.seperatorLineDiv1),this.manipulationDiv.appendChild(this.manipulationDOM.addEdgeSpan),1==this._getSelectedNodeCount()&&this.triggerFunctions.edit?(this.manipulationDOM.seperatorLineDiv2=document.createElement("div"),this.manipulationDOM.seperatorLineDiv2.className="network-seperatorLine",this.manipulationDOM.editNodeSpan=document.createElement("span"),this.manipulationDOM.editNodeSpan.className="network-manipulationUI edit",this.manipulationDOM.editNodeLabelSpan=document.createElement("span"),this.manipulationDOM.editNodeLabelSpan.className="network-manipulationLabel",this.manipulationDOM.editNodeLabelSpan.innerHTML=t.editNode,this.manipulationDOM.editNodeSpan.appendChild(this.manipulationDOM.editNodeLabelSpan),this.manipulationDiv.appendChild(this.manipulationDOM.seperatorLineDiv2),this.manipulationDiv.appendChild(this.manipulationDOM.editNodeSpan)):1==this._getSelectedEdgeCount()&&0==this._getSelectedNodeCount()&&(this.manipulationDOM.seperatorLineDiv3=document.createElement("div"),this.manipulationDOM.seperatorLineDiv3.className="network-seperatorLine",this.manipulationDOM.editEdgeSpan=document.createElement("span"),this.manipulationDOM.editEdgeSpan.className="network-manipulationUI edit",this.manipulationDOM.editEdgeLabelSpan=document.createElement("span"),this.manipulationDOM.editEdgeLabelSpan.className="network-manipulationLabel",this.manipulationDOM.editEdgeLabelSpan.innerHTML=t.editEdge,this.manipulationDOM.editEdgeSpan.appendChild(this.manipulationDOM.editEdgeLabelSpan),this.manipulationDiv.appendChild(this.manipulationDOM.seperatorLineDiv3),this.manipulationDiv.appendChild(this.manipulationDOM.editEdgeSpan)),0==this._selectionIsEmpty()&&(this.manipulationDOM.seperatorLineDiv4=document.createElement("div"),this.manipulationDOM.seperatorLineDiv4.className="network-seperatorLine",this.manipulationDOM.deleteSpan=document.createElement("span"),this.manipulationDOM.deleteSpan.className="network-manipulationUI delete",this.manipulationDOM.deleteLabelSpan=document.createElement("span"),this.manipulationDOM.deleteLabelSpan.className="network-manipulationLabel",this.manipulationDOM.deleteLabelSpan.innerHTML=t.del,this.manipulationDOM.deleteSpan.appendChild(this.manipulationDOM.deleteLabelSpan),this.manipulationDiv.appendChild(this.manipulationDOM.seperatorLineDiv4),this.manipulationDiv.appendChild(this.manipulationDOM.deleteSpan)),this.manipulationDOM.addNodeSpan.onclick=this._createAddNodeToolbar.bind(this),this.manipulationDOM.addEdgeSpan.onclick=this._createAddEdgeToolbar.bind(this),1==this._getSelectedNodeCount()&&this.triggerFunctions.edit?this.manipulationDOM.editNodeSpan.onclick=this._editNode.bind(this):1==this._getSelectedEdgeCount()&&0==this._getSelectedNodeCount()&&(this.manipulationDOM.editEdgeSpan.onclick=this._createEditEdgeToolbar.bind(this)),0==this._selectionIsEmpty()&&(this.manipulationDOM.deleteSpan.onclick=this._deleteSelected.bind(this)),this.closeDiv.onclick=this._toggleEditMode.bind(this);var e=this;this.boundFunction=e._createManipulatorBar,this.on("select",this.boundFunction)}else{for(;this.editModeDiv.hasChildNodes();)this.editModeDiv.removeChild(this.editModeDiv.firstChild);this.manipulationDOM.editModeSpan=document.createElement("span"),this.manipulationDOM.editModeSpan.className="network-manipulationUI edit editmode",this.manipulationDOM.editModeLabelSpan=document.createElement("span"),this.manipulationDOM.editModeLabelSpan.className="network-manipulationLabel",this.manipulationDOM.editModeLabelSpan.innerHTML=t.edit,this.manipulationDOM.editModeSpan.appendChild(this.manipulationDOM.editModeLabelSpan),this.editModeDiv.appendChild(this.manipulationDOM.editModeSpan),this.manipulationDOM.editModeSpan.onclick=this._toggleEditMode.bind(this)}},e._createAddNodeToolbar=function(){this._clearManipulatorBar(),this.boundFunction&&this.off("select",this.boundFunction);var t=this.constants.locales[this.constants.locale];this.manipulationDOM={},this.manipulationDOM.backSpan=document.createElement("span"),this.manipulationDOM.backSpan.className="network-manipulationUI back",this.manipulationDOM.backLabelSpan=document.createElement("span"),this.manipulationDOM.backLabelSpan.className="network-manipulationLabel",this.manipulationDOM.backLabelSpan.innerHTML=t.back,this.manipulationDOM.backSpan.appendChild(this.manipulationDOM.backLabelSpan),this.manipulationDOM.seperatorLineDiv1=document.createElement("div"),this.manipulationDOM.seperatorLineDiv1.className="network-seperatorLine",this.manipulationDOM.descriptionSpan=document.createElement("span"),this.manipulationDOM.descriptionSpan.className="network-manipulationUI none",this.manipulationDOM.descriptionLabelSpan=document.createElement("span"),this.manipulationDOM.descriptionLabelSpan.className="network-manipulationLabel",this.manipulationDOM.descriptionLabelSpan.innerHTML=t.addDescription,this.manipulationDOM.descriptionSpan.appendChild(this.manipulationDOM.descriptionLabelSpan),this.manipulationDiv.appendChild(this.manipulationDOM.backSpan),this.manipulationDiv.appendChild(this.manipulationDOM.seperatorLineDiv1),this.manipulationDiv.appendChild(this.manipulationDOM.descriptionSpan),this.manipulationDOM.backSpan.onclick=this._createManipulatorBar.bind(this);var e=this;this.boundFunction=e._addNode,this.on("select",this.boundFunction)},e._createAddEdgeToolbar=function(){this._clearManipulatorBar(),this._unselectAll(!0),this.freezeSimulation=!0,this.boundFunction&&this.off("select",this.boundFunction);var t=this.constants.locales[this.constants.locale];this._unselectAll(),this.forceAppendSelection=!1,this.blockConnectingEdgeSelection=!0,this.manipulationDOM={},this.manipulationDOM.backSpan=document.createElement("span"),this.manipulationDOM.backSpan.className="network-manipulationUI back",this.manipulationDOM.backLabelSpan=document.createElement("span"),this.manipulationDOM.backLabelSpan.className="network-manipulationLabel",this.manipulationDOM.backLabelSpan.innerHTML=t.back,this.manipulationDOM.backSpan.appendChild(this.manipulationDOM.backLabelSpan),this.manipulationDOM.seperatorLineDiv1=document.createElement("div"),this.manipulationDOM.seperatorLineDiv1.className="network-seperatorLine",this.manipulationDOM.descriptionSpan=document.createElement("span"),this.manipulationDOM.descriptionSpan.className="network-manipulationUI none",this.manipulationDOM.descriptionLabelSpan=document.createElement("span"),this.manipulationDOM.descriptionLabelSpan.className="network-manipulationLabel",this.manipulationDOM.descriptionLabelSpan.innerHTML=t.edgeDescription,this.manipulationDOM.descriptionSpan.appendChild(this.manipulationDOM.descriptionLabelSpan),this.manipulationDiv.appendChild(this.manipulationDOM.backSpan),this.manipulationDiv.appendChild(this.manipulationDOM.seperatorLineDiv1),this.manipulationDiv.appendChild(this.manipulationDOM.descriptionSpan),this.manipulationDOM.backSpan.onclick=this._createManipulatorBar.bind(this);var e=this;this.boundFunction=e._handleConnect,this.on("select",this.boundFunction),this.cachedFunctions._handleTouch=this._handleTouch,this.cachedFunctions._manipulationReleaseOverload=this._manipulationReleaseOverload,this.cachedFunctions._handleDragStart=this._handleDragStart,this.cachedFunctions._handleDragEnd=this._handleDragEnd,this.cachedFunctions._handleOnHold=this._handleOnHold,this._handleTouch=this._handleConnect,this._manipulationReleaseOverload=function(){},this._handleOnHold=function(){},this._handleDragStart=function(){},this._handleDragEnd=this._finishConnect,this._redraw()},e._createEditEdgeToolbar=function(){this._clearManipulatorBar(),this.controlNodesActive=!0,this.boundFunction&&this.off("select",this.boundFunction),this.edgeBeingEdited=this._getSelectedEdge(),this.edgeBeingEdited._enableControlNodes();var t=this.constants.locales[this.constants.locale];this.manipulationDOM={},this.manipulationDOM.backSpan=document.createElement("span"),this.manipulationDOM.backSpan.className="network-manipulationUI back",this.manipulationDOM.backLabelSpan=document.createElement("span"),this.manipulationDOM.backLabelSpan.className="network-manipulationLabel",this.manipulationDOM.backLabelSpan.innerHTML=t.back,this.manipulationDOM.backSpan.appendChild(this.manipulationDOM.backLabelSpan),this.manipulationDOM.seperatorLineDiv1=document.createElement("div"),this.manipulationDOM.seperatorLineDiv1.className="network-seperatorLine",this.manipulationDOM.descriptionSpan=document.createElement("span"),this.manipulationDOM.descriptionSpan.className="network-manipulationUI none",this.manipulationDOM.descriptionLabelSpan=document.createElement("span"),this.manipulationDOM.descriptionLabelSpan.className="network-manipulationLabel",this.manipulationDOM.descriptionLabelSpan.innerHTML=t.editEdgeDescription,this.manipulationDOM.descriptionSpan.appendChild(this.manipulationDOM.descriptionLabelSpan),this.manipulationDiv.appendChild(this.manipulationDOM.backSpan),this.manipulationDiv.appendChild(this.manipulationDOM.seperatorLineDiv1),this.manipulationDiv.appendChild(this.manipulationDOM.descriptionSpan),this.manipulationDOM.backSpan.onclick=this._createManipulatorBar.bind(this),this.cachedFunctions._handleTouch=this._handleTouch,this.cachedFunctions._manipulationReleaseOverload=this._manipulationReleaseOverload,this.cachedFunctions._handleTap=this._handleTap,this.cachedFunctions._handleDragStart=this._handleDragStart,this.cachedFunctions._handleOnDrag=this._handleOnDrag,this._handleTouch=this._selectControlNode,this._handleTap=function(){},this._handleOnDrag=this._controlNodeDrag,this._handleDragStart=function(){},this._manipulationReleaseOverload=this._releaseControlNode,this._redraw()},e._selectControlNode=function(t){this.edgeBeingEdited.controlNodes.from.unselect(),this.edgeBeingEdited.controlNodes.to.unselect(),this.selectedControlNode=this.edgeBeingEdited._getSelectedControlNode(this._XconvertDOMtoCanvas(t.x),this._YconvertDOMtoCanvas(t.y)),null!==this.selectedControlNode&&(this.selectedControlNode.select(),this.freezeSimulation=!0),this._redraw()},e._controlNodeDrag=function(t){var e=this._getPointer(t.gesture.center);null!==this.selectedControlNode&&void 0!==this.selectedControlNode&&(this.selectedControlNode.x=this._XconvertDOMtoCanvas(e.x),this.selectedControlNode.y=this._YconvertDOMtoCanvas(e.y)),this._redraw()},e._releaseControlNode=function(t){var e=this._getNodeAt(t);null!==e?(1==this.edgeBeingEdited.controlNodes.from.selected&&(this.edgeBeingEdited._restoreControlNodes(),this._editEdge(e.id,this.edgeBeingEdited.to.id),this.edgeBeingEdited.controlNodes.from.unselect()),1==this.edgeBeingEdited.controlNodes.to.selected&&(this.edgeBeingEdited._restoreControlNodes(),this._editEdge(this.edgeBeingEdited.from.id,e.id),this.edgeBeingEdited.controlNodes.to.unselect())):this.edgeBeingEdited._restoreControlNodes(),this.freezeSimulation=!1,this._redraw()},e._handleConnect=function(t){if(0==this._getSelectedNodeCount()){var e=this._getNodeAt(t);if(null!=e)if(e.clusterSize>1)alert(this.constants.locales[this.constants.locale].createEdgeError);else{this._selectObject(e,!1);var i=this.sectors.support.nodes;i.targetNode=new o({id:"targetNode"},{},{},this.constants);var s=i.targetNode;s.x=e.x,s.y=e.y,this.edges.connectionEdge=new n({id:"connectionEdge",from:e.id,to:s.id},this,this.constants);var r=this.edges.connectionEdge;r.from=e,r.connected=!0,r.options.smoothCurves={enabled:!0,dynamic:!1,type:"continuous",roundness:.5},r.selected=!0,r.to=s,this.cachedFunctions._handleOnDrag=this._handleOnDrag,this._handleOnDrag=function(t){var e=this._getPointer(t.gesture.center),i=this.edges.connectionEdge;i.to.x=this._XconvertDOMtoCanvas(e.x),i.to.y=this._YconvertDOMtoCanvas(e.y)},this.moving=!0,this.start()}}},e._finishConnect=function(t){if(1==this._getSelectedNodeCount()){var e=this._getPointer(t.gesture.center);this._handleOnDrag=this.cachedFunctions._handleOnDrag,delete this.cachedFunctions._handleOnDrag;var i=this.edges.connectionEdge.fromId;delete this.edges.connectionEdge,delete this.sectors.support.nodes.targetNode,delete this.sectors.support.nodes.targetViaNode;var s=this._getNodeAt(e);null!=s&&(s.clusterSize>1?alert(this.constants.locales[this.constants.locale].createEdgeError):(this._createEdge(i,s.id),this._createManipulatorBar())),this._unselectAll()}},e._addNode=function(){if(this._selectionIsEmpty()&&1==this.editMode){var t=this._pointerToPositionObject(this.pointerPosition),e={id:s.randomUUID(),x:t.left,y:t.top,label:"new",allowedToMoveX:!0,allowedToMoveY:!0};if(this.triggerFunctions.add){if(2!=this.triggerFunctions.add.length)throw new Error("The function for add does not support two arguments (data,callback)");var i=this;this.triggerFunctions.add(e,function(t){i.nodesData.add(t),i._createManipulatorBar(),i.moving=!0,i.start()})}else this.nodesData.add(e),this._createManipulatorBar(),this.moving=!0,this.start()}},e._createEdge=function(t,e){if(1==this.editMode){var i={from:t,to:e};if(this.triggerFunctions.connect){if(2!=this.triggerFunctions.connect.length)throw new Error("The function for connect does not support two arguments (data,callback)");var s=this;this.triggerFunctions.connect(i,function(t){s.edgesData.add(t),s.moving=!0,s.start()})}else this.edgesData.add(i),this.moving=!0,this.start()}},e._editEdge=function(t,e){if(1==this.editMode){var i={id:this.edgeBeingEdited.id,from:t,to:e};if(this.triggerFunctions.editEdge){if(2!=this.triggerFunctions.editEdge.length)throw new Error("The function for edit does not support two arguments (data, callback)");var s=this;this.triggerFunctions.editEdge(i,function(t){s.edgesData.update(t),s.moving=!0,s.start()})}else this.edgesData.update(i),this.moving=!0,this.start()}},e._editNode=function(){if(!this.triggerFunctions.edit||1!=this.editMode)throw new Error("No edit function has been bound to this button");var t=this._getSelectedNode(),e={id:t.id,label:t.label,group:t.options.group,shape:t.options.shape,color:{background:t.options.color.background,border:t.options.color.border,highlight:{background:t.options.color.highlight.background,border:t.options.color.highlight.border}}};if(2!=this.triggerFunctions.edit.length)throw new Error("The function for edit does not support two arguments (data, callback)");var i=this;this.triggerFunctions.edit(e,function(t){i.nodesData.update(t),i._createManipulatorBar(),i.moving=!0,i.start()})},e._deleteSelected=function(){if(!this._selectionIsEmpty()&&1==this.editMode)if(this._clusterInSelection())alert(this.constants.locales[this.constants.locale].deleteClusterError);else{var t=this.getSelectedNodes(),e=this.getSelectedEdges();if(this.triggerFunctions.del){var i=this,s={nodes:t,edges:e};if(2!=this.triggerFunctions.del.length)throw new Error("The function for delete does not support two arguments (data, callback)");this.triggerFunctions.del(s,function(t){i.edgesData.remove(t.edges),i.nodesData.remove(t.nodes),i._unselectAll(),i.moving=!0,i.start()})}else this.edgesData.remove(e),this.nodesData.remove(t),this._unselectAll(),this.moving=!0,this.start()}}},function(t,e,i){var s=(i(1),i(45));e._cleanNavigation=function(){if(0!=this.navigationHammers.existing.length){for(var t=0;t0){var t,e,i=0,s=!1,o=!1;for(e in this.nodes)this.nodes.hasOwnProperty(e)&&(t=this.nodes[e],-1!=t.level?s=!0:o=!0,is&&(n.xFixed=!1,n.x=i[n.level].minPos,r=!0):n.yFixed&&n.level>s&&(n.yFixed=!1,n.y=i[n.level].minPos,r=!0),1==r&&(i[n.level].minPos+=i[n.level].nodeSpacing,n.edges.length>1&&this._placeBranchNodes(n.edges,n.id,i,n.level))}},e._setLevel=function(t,e,i){for(var s=0;st)&&(o.level=t,o.edges.length>1&&this._setLevel(t+1,o.edges,o.id))}},e._setLevelDirected=function(t,e,i){this.nodes[i].hierarchyEnumerated=!0;for(var s,o,n=0;n1&&s.hierarchyEnumerated===!1&&this._setLevelDirected(s.level,s.edges,s.id)},e._restoreNodes=function(){for(var t in this.nodes)this.nodes.hasOwnProperty(t)&&(this.nodes[t].xFixed=!1,this.nodes[t].yFixed=!1)}},function(t,e){var i,s,o;!function(n,r){s=[],i=r,o="function"==typeof i?i.apply(e,s):i,!(void 0!==o&&(t.exports=o))}(this,function(){function t(t){var e,i=t&&t.preventDefault||!1,s=t&&t.container||window,o={},n={keydown:{},keyup:{}},r={};for(e=97;122>=e;e++)r[String.fromCharCode(e)]={code:65+(e-97),shift:!1};for(e=65;90>=e;e++)r[String.fromCharCode(e)]={code:e,shift:!0};for(e=0;9>=e;e++)r[""+e]={code:48+e,shift:!1};for(e=1;12>=e;e++)r["F"+e]={code:111+e,shift:!1};for(e=0;9>=e;e++)r["num"+e]={code:96+e,shift:!1};r["num*"]={code:106,shift:!1},r["num+"]={code:107,shift:!1},r["num-"]={code:109,shift:!1},r["num/"]={code:111,shift:!1},r["num."]={code:110,shift:!1},r.left={code:37,shift:!1},r.up={code:38,shift:!1},r.right={code:39,shift:!1},r.down={code:40,shift:!1},r.space={code:32,shift:!1},r.enter={code:13,shift:!1},r.shift={code:16,shift:void 0},r.esc={code:27,shift:!1},r.backspace={code:8,shift:!1},r.tab={code:9,shift:!1},r.ctrl={code:17,shift:!1},r.alt={code:18,shift:!1},r["delete"]={code:46,shift:!1},r.pageup={code:33,shift:!1},r.pagedown={code:34,shift:!1},r["="]={code:187,shift:!1},r["-"]={code:189,shift:!1},r["]"]={code:221,shift:!1},r["["]={code:219,shift:!1};var a=function(t){d(t,"keydown")},h=function(t){d(t,"keyup")},d=function(t,e){if(void 0!==n[e][t.keyCode]){for(var s=n[e][t.keyCode],o=0;os;s++)if(e.call(i,t[s],s,t)===!1)return}else for(s in t)if(t.hasOwnProperty(s)&&e.call(i,t[s],s,t)===!1)return},inStr:function(t,e){return t.indexOf(e)>-1},inArray:function(t,e){if(t.indexOf){var i=t.indexOf(e);return-1===i?!1:i}for(var s=0,o=t.length;o>s;s++)if(t[s]===e)return s;return!1},toArray:function(t){return Array.prototype.slice.call(t,0)},hasParent:function(t,e){for(;t;){if(t==e)return!0;t=t.parentNode}return!1},getCenter:function(t){var e=[],i=[],s=[],o=[],n=Math.min,r=Math.max;return 1===t.length?{pageX:t[0].pageX,pageY:t[0].pageY,clientX:t[0].clientX,clientY:t[0].clientY}:(x.each(t,function(t){e.push(t.pageX),i.push(t.pageY),s.push(t.clientX),o.push(t.clientY)}),{pageX:(n.apply(Math,e)+r.apply(Math,e))/2,pageY:(n.apply(Math,i)+r.apply(Math,i))/2,clientX:(n.apply(Math,s)+r.apply(Math,s))/2,clientY:(n.apply(Math,o)+r.apply(Math,o))/2})},getVelocity:function(t,e,i){return{x:Math.abs(e/t)||0,y:Math.abs(i/t)||0}},getAngle:function(t,e){var i=e.clientX-t.clientX,s=e.clientY-t.clientY;return 180*Math.atan2(s,i)/Math.PI},getDirection:function(t,e){var i=Math.abs(t.clientX-e.clientX),s=Math.abs(t.clientY-e.clientY);return i>=s?t.clientX-e.clientX>0?l:p:t.clientY-e.clientY>0?c:d},getDistance:function(t,e){var i=e.clientX-t.clientX,s=e.clientY-t.clientY;return Math.sqrt(i*i+s*s)},getScale:function(t,e){return t.length>=2&&e.length>=2?this.getDistance(e[0],e[1])/this.getDistance(t[0],t[1]):1},getRotation:function(t,e){return t.length>=2&&e.length>=2?this.getAngle(e[1],e[0])-this.getAngle(t[1],t[0]):0},isVertical:function(t){return t==c||t==d},setPrefixedCss:function(t,e,i,s){var o=["","Webkit","Moz","O","ms"];e=x.toCamelCase(e);for(var n=0;n0&&this.started&&(r=v),this.started=!0;var d=this.collectEventData(i,r,o,t);return e!=y&&s.call(S,d),a&&(d.changedLength=h,d.eventType=a,s.call(S,d),d.eventType=r,delete d.changedLength),r==y&&(s.call(S,d),this.started=!1),r},determineEventTypes:function(){var t;return t=a.HAS_POINTEREVENTS?o.PointerEvent?["pointerdown","pointermove","pointerup pointercancel lostpointercapture"]:["MSPointerDown","MSPointerMove","MSPointerUp MSPointerCancel MSLostPointerCapture"]:a.NO_MOUSEEVENTS?["touchstart","touchmove","touchend touchcancel"]:["touchstart mousedown","touchmove mousemove","touchend touchcancel mouseup"],h[g]=t[0],h[v]=t[1],h[y]=t[2],h},getTouchList:function(t,e){if(a.HAS_POINTEREVENTS)return M.getTouchList();if(t.touches){if(e==v)return t.touches;var i=[],s=[].concat(x.toArray(t.touches),x.toArray(t.changedTouches)),o=[];return x.each(s,function(t){x.inArray(i,t.identifier)===!1&&o.push(t),i.push(t.identifier)}),o}return t.identifier=1,[t]},collectEventData:function(t,e,i,s){var o=m;return x.inStr(s.type,"mouse")||M.matchType(u,s)?o=u:M.matchType(f,s)&&(o=f),{center:x.getCenter(i),timeStamp:Date.now(),target:s.target,touches:i,eventType:e,pointerType:o,srcEvent:s,preventDefault:function(){var t=this.srcEvent;t.preventManipulation&&t.preventManipulation(),t.preventDefault&&t.preventDefault()},stopPropagation:function(){this.srcEvent.stopPropagation()},stopDetect:function(){return S.stopDetect()}}}},M=a.PointerEvent={pointers:{},getTouchList:function(){var t=[];return x.each(this.pointers,function(e){t.push(e)}),t},updatePointer:function(t,e){t==y||t!=y&&1!==e.buttons?delete this.pointers[e.pointerId]:(e.identifier=e.pointerId,this.pointers[e.pointerId]=e)},matchType:function(t,e){if(!e.pointerType)return!1;var i=e.pointerType,s={};return s[u]=i===(e.MSPOINTER_TYPE_MOUSE||u),s[m]=i===(e.MSPOINTER_TYPE_TOUCH||m),s[f]=i===(e.MSPOINTER_TYPE_PEN||f),s[t]},reset:function(){this.pointers={}}},S=a.detection={gestures:[],current:null,previous:null,stopped:!1,startDetect:function(t,e){this.current||(this.stopped=!1,this.current={inst:t,startEvent:x.extend({},e),lastEvent:!1,lastCalcEvent:!1,futureCalcEvent:!1,lastCalcData:{},name:""},this.detect(e))},detect:function(t){if(this.current&&!this.stopped){t=this.extendEventData(t);var e=this.current.inst,i=e.options;return x.each(this.gestures,function(s){!this.stopped&&e.enabled&&i[s.name]&&s.handler.call(s,t,e)},this),this.current&&(this.current.lastEvent=t),t.eventType==y&&this.stopDetect(),t}},stopDetect:function(){this.previous=x.extend({},this.current),this.current=null,this.stopped=!0},getCalculatedData:function(t,e,i,s,o){var n=this.current,r=!1,h=n.lastCalcEvent,d=n.lastCalcData;h&&t.timeStamp-h.timeStamp>a.CALCULATE_INTERVAL&&(e=h.center,i=t.timeStamp-h.timeStamp,s=t.center.clientX-h.center.clientX,o=t.center.clientY-h.center.clientY,r=!0),(t.eventType==_||t.eventType==b)&&(n.futureCalcEvent=t),(!n.lastCalcEvent||r)&&(d.velocity=x.getVelocity(i,s,o),d.angle=x.getAngle(e,t.center),d.direction=x.getDirection(e,t.center),n.lastCalcEvent=n.futureCalcEvent||t,n.futureCalcEvent=t),t.velocityX=d.velocity.x,t.velocityY=d.velocity.y,t.interimAngle=d.angle,t.interimDirection=d.direction},extendEventData:function(t){var e=this.current,i=e.startEvent,s=e.lastEvent||i;(t.eventType==_||t.eventType==b)&&(i.touches=[],x.each(t.touches,function(t){i.touches.push({clientX:t.clientX,clientY:t.clientY})}));var o=t.timeStamp-i.timeStamp,n=t.center.clientX-i.center.clientX,r=t.center.clientY-i.center.clientY;return this.getCalculatedData(t,s.center,o,n,r),x.extend(t,{startEvent:i,deltaTime:o,deltaX:n,deltaY:r,distance:x.getDistance(i.center,t.center),angle:x.getAngle(i.center,t.center),direction:x.getDirection(i.center,t.center),scale:x.getScale(i.touches,t.touches),rotation:x.getRotation(i.touches,t.touches)}),t},register:function(t){var e=t.defaults||{};return e[t.name]===n&&(e[t.name]=!0),x.extend(a.defaults,e,!0),t.index=t.index||1e3,this.gestures.push(t),this.gestures.sort(function(t,e){return t.indexe.index?1:0}),this.gestures}};a.Instance=function(t,e){var i=this;r(),this.element=t,this.enabled=!0,x.each(e,function(t,i){delete e[i],e[x.toCamelCase(i)]=t}),this.options=x.extend(x.extend({},a.defaults),e||{}),this.options.behavior&&x.toggleBehavior(this.element,this.options.behavior,!0),this.eventStartHandler=w.onTouch(t,g,function(t){i.enabled&&t.eventType==g?S.startDetect(i,t):t.eventType==_&&S.detect(t)}),this.eventHandlers=[]},a.Instance.prototype={on:function(t,e){var i=this;return w.on(i.element,t,e,function(t){i.eventHandlers.push({gesture:t,handler:e})}),i},off:function(t,e){var i=this;return w.off(i.element,t,e,function(t){var s=x.inArray({gesture:t,handler:e});s!==!1&&i.eventHandlers.splice(s,1)}),i},trigger:function(t,e){e||(e={});var i=a.DOCUMENT.createEvent("Event");i.initEvent(t,!0,!0),i.gesture=e;var s=this.element;return x.hasParent(e.target,s)&&(s=e.target),s.dispatchEvent(i),this},enable:function(t){return this.enabled=t,this},dispose:function(){var t,e;for(x.toggleBehavior(this.element,this.options.behavior,!1),t=-1;e=this.eventHandlers[++t];)x.off(this.element,e.gesture,e.handler);return this.eventHandlers=[],w.off(this.element,h[g],this.eventStartHandler),null}},function(t){function e(e,s){var o=S.current;if(!(s.options.dragMaxTouches>0&&e.touches.length>s.options.dragMaxTouches))switch(e.eventType){case g:i=!1;break;case v:if(e.distance0)){var r=Math.abs(s.options.dragMinDistance/e.distance);n.pageX+=e.deltaX*r,n.pageY+=e.deltaY*r,n.clientX+=e.deltaX*r,n.clientY+=e.deltaY*r,e=S.extendEventData(e)}(o.lastEvent.dragLockToAxis||s.options.dragLockToAxis&&s.options.dragLockMinDistance<=e.distance)&&(e.dragLockToAxis=!0);var a=o.lastEvent.direction;e.dragLockToAxis&&a!==e.direction&&(e.direction=x.isVertical(a)?e.deltaY<0?c:d:e.deltaX<0?l:p),i||(s.trigger(t+"start",e),i=!0),s.trigger(t,e),s.trigger(t+e.direction,e);var h=x.isVertical(e.direction);(s.options.dragBlockVertical&&h||s.options.dragBlockHorizontal&&!h)&&e.preventDefault();break;case b:i&&e.changedLength<=s.options.dragMaxTouches&&(s.trigger(t+"end",e),i=!1);break;case y:i=!1}}var i=!1;a.gestures.Drag={name:t,index:50,handler:e,defaults:{dragMinDistance:10,dragDistanceCorrection:!0,dragMaxTouches:1,dragBlockHorizontal:!1,dragBlockVertical:!1,dragLockToAxis:!1,dragLockMinDistance:25}}}("drag"),a.gestures.Gesture={name:"gesture",index:1337,handler:function(t,e){e.trigger(this.name,t)}},function(t){function e(e,s){var o=s.options,n=S.current;switch(e.eventType){case g:clearTimeout(i),n.name=t,i=setTimeout(function(){n&&n.name==t&&s.trigger(t,e)},o.holdTimeout);break;case v:e.distance>o.holdThreshold&&clearTimeout(i);break;case b:clearTimeout(i)}}var i;a.gestures.Hold={name:t,index:10,defaults:{holdTimeout:500,holdThreshold:2},handler:e}}("hold"),a.gestures.Release={name:"release",index:1/0,handler:function(t,e){t.eventType==b&&e.trigger(this.name,t)}},a.gestures.Swipe={name:"swipe",index:40,defaults:{swipeMinTouches:1,swipeMaxTouches:1,swipeVelocityX:.6,swipeVelocityY:.6},handler:function(t,e){if(t.eventType==b){var i=t.touches.length,s=e.options;if(is.swipeMaxTouches)return;(t.velocityX>s.swipeVelocityX||t.velocityY>s.swipeVelocityY)&&(e.trigger(this.name,t),e.trigger(this.name+t.direction,t))}}},function(t){function e(e,s){var o,n,r=s.options,a=S.current,h=S.previous;switch(e.eventType){case g:i=!1;break;case v:i=i||e.distance>r.tapMaxDistance;break;case y:!x.inStr(e.srcEvent.type,"cancel")&&e.deltaTimes.options.transformMinRotation&&s.trigger("rotate",e),o>s.options.transformMinScale&&(s.trigger("pinch",e),s.trigger("pinch"+(e.scale<1?"in":"out"),e));break;case b:i&&e.changedLength<2&&(s.trigger(t+"end",e),i=!1)}}var i=!1;a.gestures.Transform={name:t,index:45,defaults:{transformMinScale:.01,transformMinRotation:1},handler:e}}("transform"),s=function(){return a}.call(e,i,e,t),!(s!==n&&(t.exports=s))}(window)},function(t,e,i){var s;(function(t,o){(function(n){function r(t,e,i){switch(arguments.length){case 2:return null!=t?t:e;case 3:return null!=t?t:null!=e?e:i;default:throw new Error("Implement me")}}function a(t,e){return Le.call(t,e)}function h(){return{empty:!1,unusedTokens:[],unusedInput:[],overflow:-2,charsLeftOver:0,nullInput:!1,invalidMonth:null,invalidFormat:!1,userInvalidated:!1,iso:!1}}function d(t){Ce.suppressDeprecationWarnings===!1&&"undefined"!=typeof console&&console.warn&&console.warn("Deprecation warning: "+t)}function l(t,e){var i=!0;return b(function(){return i&&(d(t),i=!1),e.apply(this,arguments)},e)}function c(t,e){Mi[t]||(d(e),Mi[t]=!0)}function p(t,e){return function(i){return w(t.call(this,i),e)}}function u(t,e){return function(i){return this.localeData().ordinal(t.call(this,i),e)}}function m(t,e){var i,s,o=12*(e.year()-t.year())+(e.month()-t.month()),n=t.clone().add(o,"months");return 0>e-n?(i=t.clone().add(o-1,"months"),s=(e-n)/(n-i)):(i=t.clone().add(o+1,"months"),s=(e-n)/(i-n)),-(o+s)}function f(t,e,i){var s;return null==i?e:null!=t.meridiemHour?t.meridiemHour(e,i):null!=t.isPM?(s=t.isPM(i),s&&12>e&&(e+=12),s||12!==e||(e=0),e):e}function g(){}function v(t,e){e!==!1&&F(t),_(this,t),this._d=new Date(+t._d),Di===!1&&(Di=!0,Ce.updateOffset(this),Di=!1)}function y(t){var e=N(t),i=e.year||0,s=e.quarter||0,o=e.month||0,n=e.week||0,r=e.day||0,a=e.hour||0,h=e.minute||0,d=e.second||0,l=e.millisecond||0;this._milliseconds=+l+1e3*d+6e4*h+36e5*a,this._days=+r+7*n,this._months=+o+3*s+12*i,this._data={},this._locale=Ce.localeData(),this._bubble()}function b(t,e){for(var i in e)a(e,i)&&(t[i]=e[i]);return a(e,"toString")&&(t.toString=e.toString),a(e,"valueOf")&&(t.valueOf=e.valueOf),t}function _(t,e){var i,s,o;if("undefined"!=typeof e._isAMomentObject&&(t._isAMomentObject=e._isAMomentObject),"undefined"!=typeof e._i&&(t._i=e._i),"undefined"!=typeof e._f&&(t._f=e._f),"undefined"!=typeof e._l&&(t._l=e._l),"undefined"!=typeof e._strict&&(t._strict=e._strict),"undefined"!=typeof e._tzm&&(t._tzm=e._tzm),"undefined"!=typeof e._isUTC&&(t._isUTC=e._isUTC),"undefined"!=typeof e._offset&&(t._offset=e._offset),"undefined"!=typeof e._pf&&(t._pf=e._pf),"undefined"!=typeof e._locale&&(t._locale=e._locale),Ye.length>0)for(i in Ye)s=Ye[i],o=e[s],"undefined"!=typeof o&&(t[s]=o);return t}function x(t){return 0>t?Math.ceil(t):Math.floor(t)}function w(t,e,i){for(var s=""+Math.abs(t),o=t>=0;s.lengths;s++)(i&&t[s]!==e[s]||!i&&I(t[s])!==I(e[s]))&&r++;return r+n}function k(t){if(t){var e=t.toLowerCase().replace(/(.)s$/,"$1");t=gi[t]||vi[e]||e}return t}function N(t){var e,i,s={};for(i in t)a(t,i)&&(e=k(i),e&&(s[e]=t[i]));return s}function L(t){var e,i;if(0===t.indexOf("week"))e=7,i="day";else{if(0!==t.indexOf("month"))return;e=12,i="month"}Ce[t]=function(s,o){var r,a,h=Ce._locale[t],d=[];if("number"==typeof s&&(o=s,s=n),a=function(t){var e=Ce().utc().set(i,t);return h.call(Ce._locale,e,s||"")},null!=o)return a(o);for(r=0;e>r;r++)d.push(a(r));return d}}function I(t){var e=+t,i=0;return 0!==e&&isFinite(e)&&(i=e>=0?Math.floor(e):Math.ceil(e)),i}function z(t,e){return new Date(Date.UTC(t,e+1,0)).getUTCDate()}function P(t,e,i){return me(Ce([t,11,31+e-i]),e,i).week}function A(t){return R(t)?366:365}function R(t){return t%4===0&&t%100!==0||t%400===0}function F(t){var e;t._a&&-2===t._pf.overflow&&(e=t._a[ze]<0||t._a[ze]>11?ze:t._a[Pe]<1||t._a[Pe]>z(t._a[Ie],t._a[ze])?Pe:t._a[Ae]<0||t._a[Ae]>24||24===t._a[Ae]&&(0!==t._a[Re]||0!==t._a[Fe]||0!==t._a[He])?Ae:t._a[Re]<0||t._a[Re]>59?Re:t._a[Fe]<0||t._a[Fe]>59?Fe:t._a[He]<0||t._a[He]>999?He:-1,t._pf._overflowDayOfYear&&(Ie>e||e>Pe)&&(e=Pe),t._pf.overflow=e)}function H(t){return null==t._isValid&&(t._isValid=!isNaN(t._d.getTime())&&t._pf.overflow<0&&!t._pf.empty&&!t._pf.invalidMonth&&!t._pf.nullInput&&!t._pf.invalidFormat&&!t._pf.userInvalidated,t._strict&&(t._isValid=t._isValid&&0===t._pf.charsLeftOver&&0===t._pf.unusedTokens.length&&t._pf.bigHour===n)),t._isValid}function B(t){return t?t.toLowerCase().replace("_","-"):t}function Y(t){for(var e,i,s,o,n=0;n0;){if(s=W(o.slice(0,e).join("-")))return s;if(i&&i.length>=e&&E(o,i,!0)>=e-1)break;e--}n++}return null}function W(t){var e=null;if(!Be[t]&&We)try{e=Ce.locale(),!function(){var t=new Error('Cannot find module "./locale"');throw t.code="MODULE_NOT_FOUND",t}(),Ce.locale(e)}catch(i){}return Be[t]}function G(t,e){var i,s;return e._isUTC?(i=e.clone(),s=(Ce.isMoment(t)||O(t)?+t:+Ce(t))-+i,i._d.setTime(+i._d+s),Ce.updateOffset(i,!1),i):Ce(t).local()}function j(t){return t.match(/\[[\s\S]/)?t.replace(/^\[|\]$/g,""):t.replace(/\\/g,"")}function V(t){var e,i,s=t.match(Ue);for(e=0,i=s.length;i>e;e++)s[e]=wi[s[e]]?wi[s[e]]:j(s[e]);return function(o){var n="";for(e=0;i>e;e++)n+=s[e]instanceof Function?s[e].call(o,t):s[e];return n}}function U(t,e){return t.isValid()?(e=X(e,t.localeData()),yi[e]||(yi[e]=V(e)),yi[e](t)):t.localeData().invalidDate()}function X(t,e){function i(t){return e.longDateFormat(t)||t}var s=5;for(Xe.lastIndex=0;s>=0&&Xe.test(t);)t=t.replace(Xe,i),Xe.lastIndex=0,s-=1;return t}function q(t,e){var i,s=e._strict;switch(t){case"Q":return oi;case"DDDD":return ri;case"YYYY":case"GGGG":case"gggg":return s?ai:Qe;case"Y":case"G":case"g":return di;case"YYYYYY":case"YYYYY":case"GGGGG":case"ggggg":return s?hi:Ke;case"S":if(s)return oi;case"SS":if(s)return ni;case"SSS":if(s)return ri;case"DDD":return Ze;case"MMM":case"MMMM":case"dd":case"ddd":case"dddd":return Je;case"a":case"A":return e._locale._meridiemParse;case"x":return ii;case"X":return si;case"Z":case"ZZ":return ti;case"T":return ei;case"SSSS":return $e;case"MM":case"DD":case"YY":case"GG":case"gg":case"HH":case"hh":case"mm":case"ss":case"ww":case"WW":return s?ni:qe;case"M":case"D":case"d":case"H":case"h":case"m":case"s":case"w":case"W":case"e":case"E":return qe;case"Do":return s?e._locale._ordinalParse:e._locale._ordinalParseLenient;default:return i=new RegExp(se(ie(t.replace("\\","")),"i"))}}function Z(t){t=t||"";var e=t.match(ti)||[],i=e[e.length-1]||[],s=(i+"").match(mi)||["-",0,0],o=+(60*s[1])+I(s[2]);return"+"===s[0]?o:-o}function Q(t,e,i){var s,o=i._a;switch(t){case"Q":null!=e&&(o[ze]=3*(I(e)-1));break;case"M":case"MM":null!=e&&(o[ze]=I(e)-1);break;case"MMM":case"MMMM":s=i._locale.monthsParse(e,t,i._strict),null!=s?o[ze]=s:i._pf.invalidMonth=e;break;case"D":case"DD":null!=e&&(o[Pe]=I(e));break;case"Do":null!=e&&(o[Pe]=I(parseInt(e.match(/\d{1,2}/)[0],10)));break;case"DDD":case"DDDD":null!=e&&(i._dayOfYear=I(e));break;case"YY":o[Ie]=Ce.parseTwoDigitYear(e);break;case"YYYY":case"YYYYY":case"YYYYYY":o[Ie]=I(e);break;case"a":case"A":i._meridiem=e;break;case"h":case"hh":i._pf.bigHour=!0;case"H":case"HH":o[Ae]=I(e);break;case"m":case"mm":o[Re]=I(e);break;case"s":case"ss":o[Fe]=I(e);break;case"S":case"SS":case"SSS":case"SSSS":o[He]=I(1e3*("0."+e));break;case"x":i._d=new Date(I(e));break;case"X":i._d=new Date(1e3*parseFloat(e));break;case"Z":case"ZZ":i._useUTC=!0,i._tzm=Z(e);break;case"dd":case"ddd":case"dddd":s=i._locale.weekdaysParse(e),null!=s?(i._w=i._w||{},i._w.d=s):i._pf.invalidWeekday=e;break;case"w":case"ww":case"W":case"WW":case"d":case"e":case"E":t=t.substr(0,1);case"gggg":case"GGGG":case"GGGGG":t=t.substr(0,2),e&&(i._w=i._w||{},i._w[t]=I(e));break;case"gg":case"GG":i._w=i._w||{},i._w[t]=Ce.parseTwoDigitYear(e)}}function K(t){var e,i,s,o,n,a,h;e=t._w,null!=e.GG||null!=e.W||null!=e.E?(n=1,a=4,i=r(e.GG,t._a[Ie],me(Ce(),1,4).year),s=r(e.W,1),o=r(e.E,1)):(n=t._locale._week.dow,a=t._locale._week.doy,i=r(e.gg,t._a[Ie],me(Ce(),n,a).year),s=r(e.w,1),null!=e.d?(o=e.d,n>o&&++s):o=null!=e.e?e.e+n:n),h=fe(i,s,o,a,n),t._a[Ie]=h.year,t._dayOfYear=h.dayOfYear}function $(t){var e,i,s,o,n=[];if(!t._d){for(s=te(t),t._w&&null==t._a[Pe]&&null==t._a[ze]&&K(t),t._dayOfYear&&(o=r(t._a[Ie],s[Ie]),t._dayOfYear>A(o)&&(t._pf._overflowDayOfYear=!0),i=le(o,0,t._dayOfYear),t._a[ze]=i.getUTCMonth(),t._a[Pe]=i.getUTCDate()),e=0;3>e&&null==t._a[e];++e)t._a[e]=n[e]=s[e];for(;7>e;e++)t._a[e]=n[e]=null==t._a[e]?2===e?1:0:t._a[e];24===t._a[Ae]&&0===t._a[Re]&&0===t._a[Fe]&&0===t._a[He]&&(t._nextDay=!0,t._a[Ae]=0),t._d=(t._useUTC?le:de).apply(null,n),null!=t._tzm&&t._d.setUTCMinutes(t._d.getUTCMinutes()-t._tzm),t._nextDay&&(t._a[Ae]=24)}}function J(t){var e;t._d||(e=N(t._i),t._a=[e.year,e.month,e.day||e.date,e.hour,e.minute,e.second,e.millisecond],$(t))}function te(t){var e=new Date;return t._useUTC?[e.getUTCFullYear(),e.getUTCMonth(),e.getUTCDate()]:[e.getFullYear(),e.getMonth(),e.getDate()]}function ee(t){if(t._f===Ce.ISO_8601)return void ne(t);t._a=[],t._pf.empty=!0;var e,i,s,o,r,a=""+t._i,h=a.length,d=0;for(s=X(t._f,t._locale).match(Ue)||[],e=0;e0&&t._pf.unusedInput.push(r),a=a.slice(a.indexOf(i)+i.length),d+=i.length),wi[o]?(i?t._pf.empty=!1:t._pf.unusedTokens.push(o),Q(o,i,t)):t._strict&&!i&&t._pf.unusedTokens.push(o);t._pf.charsLeftOver=h-d,a.length>0&&t._pf.unusedInput.push(a),t._pf.bigHour===!0&&t._a[Ae]<=12&&(t._pf.bigHour=n),t._a[Ae]=f(t._locale,t._a[Ae],t._meridiem),$(t),F(t)}function ie(t){return t.replace(/\\(\[)|\\(\])|\[([^\]\[]*)\]|\\(.)/g,function(t,e,i,s,o){return e||i||s||o})}function se(t){return t.replace(/[-\/\\^$*+?.()|[\]{}]/g,"\\$&")}function oe(t){var e,i,s,o,n;if(0===t._f.length)return t._pf.invalidFormat=!0,void(t._d=new Date(0/0));for(o=0;on)&&(s=n,i=e));b(t,i||e)}function ne(t){var e,i,s=t._i,o=li.exec(s);if(o){for(t._pf.iso=!0,e=0,i=pi.length;i>e;e++)if(pi[e][1].exec(s)){t._f=pi[e][0]+(o[6]||" ");break}for(e=0,i=ui.length;i>e;e++)if(ui[e][1].exec(s)){t._f+=ui[e][0];break}s.match(ti)&&(t._f+="Z"),ee(t)}else t._isValid=!1}function re(t){ne(t),t._isValid===!1&&(delete t._isValid,Ce.createFromInputFallback(t))}function ae(t,e){var i,s=[];for(i=0;it&&a.setFullYear(t),a}function le(t){var e=new Date(Date.UTC.apply(null,arguments));return 1970>t&&e.setUTCFullYear(t),e}function ce(t,e){if("string"==typeof t)if(isNaN(t)){if(t=e.weekdaysParse(t),"number"!=typeof t)return null}else t=parseInt(t,10);return t}function pe(t,e,i,s,o){return o.relativeTime(e||1,!!i,t,s)}function ue(t,e,i){var s=Ce.duration(t).abs(),o=Ne(s.as("s")),n=Ne(s.as("m")),r=Ne(s.as("h")),a=Ne(s.as("d")),h=Ne(s.as("M")),d=Ne(s.as("y")),l=o0,l[4]=i,pe.apply({},l)}function me(t,e,i){var s,o=i-e,n=i-t.day();return n>o&&(n-=7),o-7>n&&(n+=7),s=Ce(t).add(n,"d"),{week:Math.ceil(s.dayOfYear()/7),year:s.year()}}function fe(t,e,i,s,o){var n,r,a=le(t,0,1).getUTCDay();return a=0===a?7:a,i=null!=i?i:o,n=o-a+(a>s?7:0)-(o>a?7:0),r=7*(e-1)+(i-o)+n+1,{year:r>0?t:t-1,dayOfYear:r>0?r:A(t-1)+r}}function ge(t){var e,i=t._i,s=t._f;return t._locale=t._locale||Ce.localeData(t._l),null===i||s===n&&""===i?Ce.invalid({nullInput:!0}):("string"==typeof i&&(t._i=i=t._locale.preparse(i)),Ce.isMoment(i)?new v(i,!0):(s?T(s)?oe(t):ee(t):he(t),e=new v(t),e._nextDay&&(e.add(1,"d"),e._nextDay=n),e))}function ve(t,e){var i,s;if(1===e.length&&T(e[0])&&(e=e[0]),!e.length)return Ce();for(i=e[0],s=1;s=0?"+":"-";return e+w(Math.abs(t),6)},gg:function(){return w(this.weekYear()%100,2)},gggg:function(){return w(this.weekYear(),4)},ggggg:function(){return w(this.weekYear(),5)},GG:function(){return w(this.isoWeekYear()%100,2)},GGGG:function(){return w(this.isoWeekYear(),4)},GGGGG:function(){return w(this.isoWeekYear(),5)},e:function(){return this.weekday()},E:function(){return this.isoWeekday()},a:function(){return this.localeData().meridiem(this.hours(),this.minutes(),!0)},A:function(){return this.localeData().meridiem(this.hours(),this.minutes(),!1)},H:function(){return this.hours()},h:function(){return this.hours()%12||12},m:function(){return this.minutes()},s:function(){return this.seconds()},S:function(){return I(this.milliseconds()/100)},SS:function(){return w(I(this.milliseconds()/10),2)},SSS:function(){return w(this.milliseconds(),3)},SSSS:function(){return w(this.milliseconds(),3)},Z:function(){var t=this.utcOffset(),e="+";return 0>t&&(t=-t,e="-"),e+w(I(t/60),2)+":"+w(I(t)%60,2)},ZZ:function(){var t=this.utcOffset(),e="+";return 0>t&&(t=-t,e="-"),e+w(I(t/60),2)+w(I(t)%60,2)},z:function(){return this.zoneAbbr()},zz:function(){return this.zoneName()},x:function(){return this.valueOf()},X:function(){return this.unix()},Q:function(){return this.quarter()}},Mi={},Si=["months","monthsShort","weekdays","weekdaysShort","weekdaysMin"],Di=!1;_i.length;)Oe=_i.pop(),wi[Oe+"o"]=u(wi[Oe],Oe);for(;xi.length;)Oe=xi.pop(),wi[Oe+Oe]=p(wi[Oe],2); +wi.DDDD=p(wi.DDD,3),b(g.prototype,{set:function(t){var e,i;for(i in t)e=t[i],"function"==typeof e?this[i]=e:this["_"+i]=e;this._ordinalParseLenient=new RegExp(this._ordinalParse.source+"|"+/\d{1,2}/.source)},_months:"January_February_March_April_May_June_July_August_September_October_November_December".split("_"),months:function(t){return this._months[t.month()]},_monthsShort:"Jan_Feb_Mar_Apr_May_Jun_Jul_Aug_Sep_Oct_Nov_Dec".split("_"),monthsShort:function(t){return this._monthsShort[t.month()]},monthsParse:function(t,e,i){var s,o,n;for(this._monthsParse||(this._monthsParse=[],this._longMonthsParse=[],this._shortMonthsParse=[]),s=0;12>s;s++){if(o=Ce.utc([2e3,s]),i&&!this._longMonthsParse[s]&&(this._longMonthsParse[s]=new RegExp("^"+this.months(o,"").replace(".","")+"$","i"),this._shortMonthsParse[s]=new RegExp("^"+this.monthsShort(o,"").replace(".","")+"$","i")),i||this._monthsParse[s]||(n="^"+this.months(o,"")+"|^"+this.monthsShort(o,""),this._monthsParse[s]=new RegExp(n.replace(".",""),"i")),i&&"MMMM"===e&&this._longMonthsParse[s].test(t))return s;if(i&&"MMM"===e&&this._shortMonthsParse[s].test(t))return s;if(!i&&this._monthsParse[s].test(t))return s}},_weekdays:"Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday".split("_"),weekdays:function(t){return this._weekdays[t.day()]},_weekdaysShort:"Sun_Mon_Tue_Wed_Thu_Fri_Sat".split("_"),weekdaysShort:function(t){return this._weekdaysShort[t.day()]},_weekdaysMin:"Su_Mo_Tu_We_Th_Fr_Sa".split("_"),weekdaysMin:function(t){return this._weekdaysMin[t.day()]},weekdaysParse:function(t){var e,i,s;for(this._weekdaysParse||(this._weekdaysParse=[]),e=0;7>e;e++)if(this._weekdaysParse[e]||(i=Ce([2e3,1]).day(e),s="^"+this.weekdays(i,"")+"|^"+this.weekdaysShort(i,"")+"|^"+this.weekdaysMin(i,""),this._weekdaysParse[e]=new RegExp(s.replace(".",""),"i")),this._weekdaysParse[e].test(t))return e},_longDateFormat:{LTS:"h:mm:ss A",LT:"h:mm A",L:"MM/DD/YYYY",LL:"MMMM D, YYYY",LLL:"MMMM D, YYYY LT",LLLL:"dddd, MMMM D, YYYY LT"},longDateFormat:function(t){var e=this._longDateFormat[t];return!e&&this._longDateFormat[t.toUpperCase()]&&(e=this._longDateFormat[t.toUpperCase()].replace(/MMMM|MM|DD|dddd/g,function(t){return t.slice(1)}),this._longDateFormat[t]=e),e},isPM:function(t){return"p"===(t+"").toLowerCase().charAt(0)},_meridiemParse:/[ap]\.?m?\.?/i,meridiem:function(t,e,i){return t>11?i?"pm":"PM":i?"am":"AM"},_calendar:{sameDay:"[Today at] LT",nextDay:"[Tomorrow at] LT",nextWeek:"dddd [at] LT",lastDay:"[Yesterday at] LT",lastWeek:"[Last] dddd [at] LT",sameElse:"L"},calendar:function(t,e,i){var s=this._calendar[t];return"function"==typeof s?s.apply(e,[i]):s},_relativeTime:{future:"in %s",past:"%s ago",s:"a few seconds",m:"a minute",mm:"%d minutes",h:"an hour",hh:"%d hours",d:"a day",dd:"%d days",M:"a month",MM:"%d months",y:"a year",yy:"%d years"},relativeTime:function(t,e,i,s){var o=this._relativeTime[i];return"function"==typeof o?o(t,e,i,s):o.replace(/%d/i,t)},pastFuture:function(t,e){var i=this._relativeTime[t>0?"future":"past"];return"function"==typeof i?i(e):i.replace(/%s/i,e)},ordinal:function(t){return this._ordinal.replace("%d",t)},_ordinal:"%d",_ordinalParse:/\d{1,2}/,preparse:function(t){return t},postformat:function(t){return t},week:function(t){return me(t,this._week.dow,this._week.doy).week},_week:{dow:0,doy:6},firstDayOfWeek:function(){return this._week.dow},firstDayOfYear:function(){return this._week.doy},_invalidDate:"Invalid date",invalidDate:function(){return this._invalidDate}}),Ce=function(t,e,i,s){var o;return"boolean"==typeof i&&(s=i,i=n),o={},o._isAMomentObject=!0,o._i=t,o._f=e,o._l=i,o._strict=s,o._isUTC=!1,o._pf=h(),ge(o)},Ce.suppressDeprecationWarnings=!1,Ce.createFromInputFallback=l("moment construction falls back to js Date. This is discouraged and will be removed in upcoming major release. Please refer to https://github.com/moment/moment/issues/1407 for more info.",function(t){t._d=new Date(t._i+(t._useUTC?" UTC":""))}),Ce.min=function(){var t=[].slice.call(arguments,0);return ve("isBefore",t)},Ce.max=function(){var t=[].slice.call(arguments,0);return ve("isAfter",t)},Ce.utc=function(t,e,i,s){var o;return"boolean"==typeof i&&(s=i,i=n),o={},o._isAMomentObject=!0,o._useUTC=!0,o._isUTC=!0,o._l=i,o._i=t,o._f=e,o._strict=s,o._pf=h(),ge(o).utc()},Ce.unix=function(t){return Ce(1e3*t)},Ce.duration=function(t,e){var i,s,o,n,r=t,h=null;return Ce.isDuration(t)?r={ms:t._milliseconds,d:t._days,M:t._months}:"number"==typeof t?(r={},e?r[e]=t:r.milliseconds=t):(h=je.exec(t))?(i="-"===h[1]?-1:1,r={y:0,d:I(h[Pe])*i,h:I(h[Ae])*i,m:I(h[Re])*i,s:I(h[Fe])*i,ms:I(h[He])*i}):(h=Ve.exec(t))?(i="-"===h[1]?-1:1,o=function(t){var e=t&&parseFloat(t.replace(",","."));return(isNaN(e)?0:e)*i},r={y:o(h[2]),M:o(h[3]),d:o(h[4]),h:o(h[5]),m:o(h[6]),s:o(h[7]),w:o(h[8])}):null==r?r={}:"object"==typeof r&&("from"in r||"to"in r)&&(n=S(Ce(r.from),Ce(r.to)),r={},r.ms=n.milliseconds,r.M=n.months),s=new y(r),Ce.isDuration(t)&&a(t,"_locale")&&(s._locale=t._locale),s},Ce.version=Ee,Ce.defaultFormat=ci,Ce.ISO_8601=function(){},Ce.momentProperties=Ye,Ce.updateOffset=function(){},Ce.relativeTimeThreshold=function(t,e){return bi[t]===n?!1:e===n?bi[t]:(bi[t]=e,!0)},Ce.lang=l("moment.lang is deprecated. Use moment.locale instead.",function(t,e){return Ce.locale(t,e)}),Ce.locale=function(t,e){var i;return t&&(i="undefined"!=typeof e?Ce.defineLocale(t,e):Ce.localeData(t),i&&(Ce.duration._locale=Ce._locale=i)),Ce._locale._abbr},Ce.defineLocale=function(t,e){return null!==e?(e.abbr=t,Be[t]||(Be[t]=new g),Be[t].set(e),Ce.locale(t),Be[t]):(delete Be[t],null)},Ce.langData=l("moment.langData is deprecated. Use moment.localeData instead.",function(t){return Ce.localeData(t)}),Ce.localeData=function(t){var e;if(t&&t._locale&&t._locale._abbr&&(t=t._locale._abbr),!t)return Ce._locale;if(!T(t)){if(e=W(t))return e;t=[t]}return Y(t)},Ce.isMoment=function(t){return t instanceof v||null!=t&&a(t,"_isAMomentObject")},Ce.isDuration=function(t){return t instanceof y};for(Oe=Si.length-1;Oe>=0;--Oe)L(Si[Oe]);Ce.normalizeUnits=function(t){return k(t)},Ce.invalid=function(t){var e=Ce.utc(0/0);return null!=t?b(e._pf,t):e._pf.userInvalidated=!0,e},Ce.parseZone=function(){return Ce.apply(null,arguments).parseZone()},Ce.parseTwoDigitYear=function(t){return I(t)+(I(t)>68?1900:2e3)},Ce.isDate=O,b(Ce.fn=v.prototype,{clone:function(){return Ce(this)},valueOf:function(){return+this._d-6e4*(this._offset||0)},unix:function(){return Math.floor(+this/1e3)},toString:function(){return this.clone().locale("en").format("ddd MMM DD YYYY HH:mm:ss [GMT]ZZ")},toDate:function(){return this._offset?new Date(+this):this._d},toISOString:function(){var t=Ce(this).utc();return 00:!1},parsingFlags:function(){return b({},this._pf)},invalidAt:function(){return this._pf.overflow},utc:function(t){return this.utcOffset(0,t)},local:function(t){return this._isUTC&&(this.utcOffset(0,t),this._isUTC=!1,t&&this.subtract(this._dateUtcOffset(),"m")),this},format:function(t){var e=U(this,t||Ce.defaultFormat);return this.localeData().postformat(e)},add:D(1,"add"),subtract:D(-1,"subtract"),diff:function(t,e,i){var s,o,n=G(t,this),r=6e4*(n.utcOffset()-this.utcOffset());return e=k(e),"year"===e||"month"===e||"quarter"===e?(o=m(this,n),"quarter"===e?o/=3:"year"===e&&(o/=12)):(s=this-n,o="second"===e?s/1e3:"minute"===e?s/6e4:"hour"===e?s/36e5:"day"===e?(s-r)/864e5:"week"===e?(s-r)/6048e5:s),i?o:x(o)},from:function(t,e){return Ce.duration({to:this,from:t}).locale(this.locale()).humanize(!e)},fromNow:function(t){return this.from(Ce(),t)},calendar:function(t){var e=t||Ce(),i=G(e,this).startOf("day"),s=this.diff(i,"days",!0),o=-6>s?"sameElse":-1>s?"lastWeek":0>s?"lastDay":1>s?"sameDay":2>s?"nextDay":7>s?"nextWeek":"sameElse";return this.format(this.localeData().calendar(o,this,Ce(e)))},isLeapYear:function(){return R(this.year())},isDST:function(){return this.utcOffset()>this.clone().month(0).utcOffset()||this.utcOffset()>this.clone().month(5).utcOffset()},day:function(t){var e=this._isUTC?this._d.getUTCDay():this._d.getDay();return null!=t?(t=ce(t,this.localeData()),this.add(t-e,"d")):e},month:xe("Month",!0),startOf:function(t){switch(t=k(t)){case"year":this.month(0);case"quarter":case"month":this.date(1);case"week":case"isoWeek":case"day":this.hours(0);case"hour":this.minutes(0);case"minute":this.seconds(0);case"second":this.milliseconds(0)}return"week"===t?this.weekday(0):"isoWeek"===t&&this.isoWeekday(1),"quarter"===t&&this.month(3*Math.floor(this.month()/3)),this},endOf:function(t){return t=k(t),t===n||"millisecond"===t?this:this.startOf(t).add(1,"isoWeek"===t?"week":t).subtract(1,"ms")},isAfter:function(t,e){var i;return e=k("undefined"!=typeof e?e:"millisecond"),"millisecond"===e?(t=Ce.isMoment(t)?t:Ce(t),+this>+t):(i=Ce.isMoment(t)?+t:+Ce(t),i<+this.clone().startOf(e))},isBefore:function(t,e){var i;return e=k("undefined"!=typeof e?e:"millisecond"),"millisecond"===e?(t=Ce.isMoment(t)?t:Ce(t),+t>+this):(i=Ce.isMoment(t)?+t:+Ce(t),+this.clone().endOf(e)t?this:t}),max:l("moment().max is deprecated, use moment.max instead. https://github.com/moment/moment/issues/1548",function(t){return t=Ce.apply(null,arguments),t>this?this:t}),zone:l("moment().zone is deprecated, use moment().utcOffset instead. https://github.com/moment/moment/issues/1779",function(t,e){return null!=t?("string"!=typeof t&&(t=-t),this.utcOffset(t,e),this):-this.utcOffset()}),utcOffset:function(t,e){var i,s=this._offset||0;return null!=t?("string"==typeof t&&(t=Z(t)),Math.abs(t)<16&&(t=60*t),!this._isUTC&&e&&(i=this._dateUtcOffset()),this._offset=t,this._isUTC=!0,null!=i&&this.add(i,"m"),s!==t&&(!e||this._changeInProgress?C(this,Ce.duration(t-s,"m"),1,!1):this._changeInProgress||(this._changeInProgress=!0,Ce.updateOffset(this,!0),this._changeInProgress=null)),this):this._isUTC?s:this._dateUtcOffset()},isLocal:function(){return!this._isUTC},isUtcOffset:function(){return this._isUTC},isUtc:function(){return this._isUTC&&0===this._offset},zoneAbbr:function(){return this._isUTC?"UTC":""},zoneName:function(){return this._isUTC?"Coordinated Universal Time":""},parseZone:function(){return this._tzm?this.utcOffset(this._tzm):"string"==typeof this._i&&this.utcOffset(Z(this._i)),this},hasAlignedHourOffset:function(t){return t=t?Ce(t).utcOffset():0,(this.utcOffset()-t)%60===0},daysInMonth:function(){return z(this.year(),this.month())},dayOfYear:function(t){var e=Ne((Ce(this).startOf("day")-Ce(this).startOf("year"))/864e5)+1;return null==t?e:this.add(t-e,"d")},quarter:function(t){return null==t?Math.ceil((this.month()+1)/3):this.month(3*(t-1)+this.month()%3)},weekYear:function(t){var e=me(this,this.localeData()._week.dow,this.localeData()._week.doy).year;return null==t?e:this.add(t-e,"y")},isoWeekYear:function(t){var e=me(this,1,4).year;return null==t?e:this.add(t-e,"y")},week:function(t){var e=this.localeData().week(this);return null==t?e:this.add(7*(t-e),"d")},isoWeek:function(t){var e=me(this,1,4).week;return null==t?e:this.add(7*(t-e),"d")},weekday:function(t){var e=(this.day()+7-this.localeData()._week.dow)%7;return null==t?e:this.add(t-e,"d")},isoWeekday:function(t){return null==t?this.day()||7:this.day(this.day()%7?t:t-7)},isoWeeksInYear:function(){return P(this.year(),1,4)},weeksInYear:function(){var t=this.localeData()._week;return P(this.year(),t.dow,t.doy)},get:function(t){return t=k(t),this[t]()},set:function(t,e){var i;if("object"==typeof t)for(i in t)this.set(i,t[i]);else t=k(t),"function"==typeof this[t]&&this[t](e);return this},locale:function(t){var e;return t===n?this._locale._abbr:(e=Ce.localeData(t),null!=e&&(this._locale=e),this)},lang:l("moment().lang() is deprecated. Instead, use moment().localeData() to get the language configuration. Use moment().locale() to change languages.",function(t){return t===n?this.localeData():this.locale(t)}),localeData:function(){return this._locale},_dateUtcOffset:function(){return 15*-Math.round(this._d.getTimezoneOffset()/15)}}),Ce.fn.millisecond=Ce.fn.milliseconds=xe("Milliseconds",!1),Ce.fn.second=Ce.fn.seconds=xe("Seconds",!1),Ce.fn.minute=Ce.fn.minutes=xe("Minutes",!1),Ce.fn.hour=Ce.fn.hours=xe("Hours",!0),Ce.fn.date=xe("Date",!0),Ce.fn.dates=l("dates accessor is deprecated. Use date instead.",xe("Date",!0)),Ce.fn.year=xe("FullYear",!0),Ce.fn.years=l("years accessor is deprecated. Use year instead.",xe("FullYear",!0)),Ce.fn.days=Ce.fn.day,Ce.fn.months=Ce.fn.month,Ce.fn.weeks=Ce.fn.week,Ce.fn.isoWeeks=Ce.fn.isoWeek,Ce.fn.quarters=Ce.fn.quarter,Ce.fn.toJSON=Ce.fn.toISOString,Ce.fn.isUTC=Ce.fn.isUtc,b(Ce.duration.fn=y.prototype,{_bubble:function(){var t,e,i,s=this._milliseconds,o=this._days,n=this._months,r=this._data,a=0;r.milliseconds=s%1e3,t=x(s/1e3),r.seconds=t%60,e=x(t/60),r.minutes=e%60,i=x(e/60),r.hours=i%24,o+=x(i/24),a=x(we(o)),o-=x(Me(a)),n+=x(o/30),o%=30,a+=x(n/12),n%=12,r.days=o,r.months=n,r.years=a},abs:function(){return this._milliseconds=Math.abs(this._milliseconds),this._days=Math.abs(this._days),this._months=Math.abs(this._months),this._data.milliseconds=Math.abs(this._data.milliseconds),this._data.seconds=Math.abs(this._data.seconds),this._data.minutes=Math.abs(this._data.minutes),this._data.hours=Math.abs(this._data.hours),this._data.months=Math.abs(this._data.months),this._data.years=Math.abs(this._data.years),this},weeks:function(){return x(this.days()/7)},valueOf:function(){return this._milliseconds+864e5*this._days+this._months%12*2592e6+31536e6*I(this._months/12)},humanize:function(t){var e=ue(this,!t,this.localeData());return t&&(e=this.localeData().pastFuture(+this,e)),this.localeData().postformat(e)},add:function(t,e){var i=Ce.duration(t,e);return this._milliseconds+=i._milliseconds,this._days+=i._days,this._months+=i._months,this._bubble(),this},subtract:function(t,e){var i=Ce.duration(t,e);return this._milliseconds-=i._milliseconds,this._days-=i._days,this._months-=i._months,this._bubble(),this},get:function(t){return t=k(t),this[t.toLowerCase()+"s"]()},as:function(t){var e,i;if(t=k(t),"month"===t||"year"===t)return e=this._days+this._milliseconds/864e5,i=this._months+12*we(e),"month"===t?i:i/12;switch(e=this._days+Math.round(Me(this._months/12)),t){case"week":return e/7+this._milliseconds/6048e5;case"day":return e+this._milliseconds/864e5;case"hour":return 24*e+this._milliseconds/36e5;case"minute":return 24*e*60+this._milliseconds/6e4;case"second":return 24*e*60*60+this._milliseconds/1e3;case"millisecond":return Math.floor(24*e*60*60*1e3)+this._milliseconds;default:throw new Error("Unknown unit "+t)}},lang:Ce.fn.lang,locale:Ce.fn.locale,toIsoString:l("toIsoString() is deprecated. Please use toISOString() instead (notice the capitals)",function(){return this.toISOString()}),toISOString:function(){var t=Math.abs(this.years()),e=Math.abs(this.months()),i=Math.abs(this.days()),s=Math.abs(this.hours()),o=Math.abs(this.minutes()),n=Math.abs(this.seconds()+this.milliseconds()/1e3);return this.asSeconds()?(this.asSeconds()<0?"-":"")+"P"+(t?t+"Y":"")+(e?e+"M":"")+(i?i+"D":"")+(s||o||n?"T":"")+(s?s+"H":"")+(o?o+"M":"")+(n?n+"S":""):"P0D"},localeData:function(){return this._locale},toJSON:function(){return this.toISOString()}}),Ce.duration.fn.toString=Ce.duration.fn.toISOString;for(Oe in fi)a(fi,Oe)&&Se(Oe.toLowerCase());Ce.duration.fn.asMilliseconds=function(){return this.as("ms")},Ce.duration.fn.asSeconds=function(){return this.as("s")},Ce.duration.fn.asMinutes=function(){return this.as("m")},Ce.duration.fn.asHours=function(){return this.as("h")},Ce.duration.fn.asDays=function(){return this.as("d")},Ce.duration.fn.asWeeks=function(){return this.as("weeks")},Ce.duration.fn.asMonths=function(){return this.as("M")},Ce.duration.fn.asYears=function(){return this.as("y")},Ce.locale("en",{ordinalParse:/\d{1,2}(th|st|nd|rd)/,ordinal:function(t){var e=t%10,i=1===I(t%100/10)?"th":1===e?"st":2===e?"nd":3===e?"rd":"th";return t+i}}),We?o.exports=Ce:(s=function(t,e,i){return i.config&&i.config()&&i.config().noGlobal===!0&&(ke.moment=Te),Ce}.call(e,i,e,o),!(s!==n&&(o.exports=s)),De(!0))}).call(this)}).call(e,function(){return this}(),i(71)(t))},function(t,e,i){function s(){this.constants.smoothCurves.enabled=!this.constants.smoothCurves.enabled;var t=document.getElementById("graph_toggleSmooth");t.style.background=1==this.constants.smoothCurves.enabled?"#A4FF56":"#FF8532",this._configureSmoothCurves(!1)}function o(){for(var t in this.calculationNodes)this.calculationNodes.hasOwnProperty(t)&&(this.calculationNodes[t].vx=0,this.calculationNodes[t].vy=0,this.calculationNodes[t].fx=0,this.calculationNodes[t].fy=0);1==this.constants.hierarchicalLayout.enabled?(this._setupHierarchicalLayout(),a.call(this,"graph_H_nd",1,"physics_hierarchicalRepulsion_nodeDistance"),a.call(this,"graph_H_cg",1,"physics_centralGravity"),a.call(this,"graph_H_sc",1,"physics_springConstant"),a.call(this,"graph_H_sl",1,"physics_springLength"),a.call(this,"graph_H_damp",1,"physics_damping")):this.repositionNodes(),this.moving=!0,this.start()}function n(){var t="No options are required, default values used.",e=[],i=document.getElementById("graph_physicsMethod1"),s=document.getElementById("graph_physicsMethod2");if(1==i.checked){if(this.constants.physics.barnesHut.gravitationalConstant!=this.backupConstants.physics.barnesHut.gravitationalConstant&&e.push("gravitationalConstant: "+this.constants.physics.barnesHut.gravitationalConstant),this.constants.physics.centralGravity!=this.backupConstants.physics.barnesHut.centralGravity&&e.push("centralGravity: "+this.constants.physics.centralGravity),this.constants.physics.springLength!=this.backupConstants.physics.barnesHut.springLength&&e.push("springLength: "+this.constants.physics.springLength),this.constants.physics.springConstant!=this.backupConstants.physics.barnesHut.springConstant&&e.push("springConstant: "+this.constants.physics.springConstant),this.constants.physics.damping!=this.backupConstants.physics.barnesHut.damping&&e.push("damping: "+this.constants.physics.damping),0!=e.length){t="var options = {",t+="physics: {barnesHut: {";for(var o=0;othis.constants.clustering.clusterThreshold&&1==this.constants.clustering.enabled&&this.clusterToFit(this.constants.clustering.reduceToNodes,!1),this._calculateForces())},e._calculateForces=function(){this._calculateGravitationalForces(),this._calculateNodeForces(),this.constants.physics.springConstant>0&&(1==this.constants.smoothCurves.enabled&&1==this.constants.smoothCurves.dynamic?this._calculateSpringForcesWithSupport():1==this.constants.physics.hierarchicalRepulsion.enabled?this._calculateHierarchicalSpringForces():this._calculateSpringForces())},e._updateCalculationNodes=function(){if(1==this.constants.smoothCurves.enabled&&1==this.constants.smoothCurves.dynamic){this.calculationNodes={},this.calculationNodeIndices=[];for(var t in this.nodes)this.nodes.hasOwnProperty(t)&&(this.calculationNodes[t]=this.nodes[t]);var e=this.sectors.support.nodes;for(var i in e)e.hasOwnProperty(i)&&(this.edges.hasOwnProperty(e[i].parentEdgeId)?this.calculationNodes[i]=e[i]:e[i]._setForce(0,0));for(var s in this.calculationNodes)this.calculationNodes.hasOwnProperty(s)&&this.calculationNodeIndices.push(s)}else this.calculationNodes=this.nodes,this.calculationNodeIndices=this.nodeIndices},e._calculateGravitationalForces=function(){var t,e,i,s,o,n=this.calculationNodes,r=this.constants.physics.centralGravity,a=0;for(o=0;oSimulation Mode:Barnes HutRepulsionHierarchical
Options:
',this.containerElement.parentElement.insertBefore(this.physicsConfiguration,this.containerElement),this.optionsDiv=document.createElement("div"),this.optionsDiv.style.fontSize="14px",this.optionsDiv.style.fontFamily="verdana",this.containerElement.parentElement.insertBefore(this.optionsDiv,this.containerElement); +var e;e=document.getElementById("graph_BH_gc"),e.onchange=a.bind(this,"graph_BH_gc",-1,"physics_barnesHut_gravitationalConstant"),e=document.getElementById("graph_BH_cg"),e.onchange=a.bind(this,"graph_BH_cg",1,"physics_centralGravity"),e=document.getElementById("graph_BH_sc"),e.onchange=a.bind(this,"graph_BH_sc",1,"physics_springConstant"),e=document.getElementById("graph_BH_sl"),e.onchange=a.bind(this,"graph_BH_sl",1,"physics_springLength"),e=document.getElementById("graph_BH_damp"),e.onchange=a.bind(this,"graph_BH_damp",1,"physics_damping"),e=document.getElementById("graph_R_nd"),e.onchange=a.bind(this,"graph_R_nd",1,"physics_repulsion_nodeDistance"),e=document.getElementById("graph_R_cg"),e.onchange=a.bind(this,"graph_R_cg",1,"physics_centralGravity"),e=document.getElementById("graph_R_sc"),e.onchange=a.bind(this,"graph_R_sc",1,"physics_springConstant"),e=document.getElementById("graph_R_sl"),e.onchange=a.bind(this,"graph_R_sl",1,"physics_springLength"),e=document.getElementById("graph_R_damp"),e.onchange=a.bind(this,"graph_R_damp",1,"physics_damping"),e=document.getElementById("graph_H_nd"),e.onchange=a.bind(this,"graph_H_nd",1,"physics_hierarchicalRepulsion_nodeDistance"),e=document.getElementById("graph_H_cg"),e.onchange=a.bind(this,"graph_H_cg",1,"physics_centralGravity"),e=document.getElementById("graph_H_sc"),e.onchange=a.bind(this,"graph_H_sc",1,"physics_springConstant"),e=document.getElementById("graph_H_sl"),e.onchange=a.bind(this,"graph_H_sl",1,"physics_springLength"),e=document.getElementById("graph_H_damp"),e.onchange=a.bind(this,"graph_H_damp",1,"physics_damping"),e=document.getElementById("graph_H_direction"),e.onchange=a.bind(this,"graph_H_direction",t,"hierarchicalLayout_direction"),e=document.getElementById("graph_H_levsep"),e.onchange=a.bind(this,"graph_H_levsep",1,"hierarchicalLayout_levelSeparation"),e=document.getElementById("graph_H_nspac"),e.onchange=a.bind(this,"graph_H_nspac",1,"hierarchicalLayout_nodeSpacing");var i=document.getElementById("graph_physicsMethod1"),d=document.getElementById("graph_physicsMethod2"),l=document.getElementById("graph_physicsMethod3");d.checked=!0,this.constants.physics.barnesHut.enabled&&(i.checked=!0),this.constants.hierarchicalLayout.enabled&&(l.checked=!0);var c=document.getElementById("graph_toggleSmooth"),p=document.getElementById("graph_repositionNodes"),u=document.getElementById("graph_generateOptions");c.onclick=s.bind(this),p.onclick=o.bind(this),u.onclick=n.bind(this),c.style.background=1==this.constants.smoothCurves&&0==this.constants.dynamicSmoothCurves?"#A4FF56":"#FF8532",r.apply(this),i.onchange=r.bind(this),d.onchange=r.bind(this),l.onchange=r.bind(this)}},e._overWriteGraphConstants=function(t,e){var i=t.split("_");1==i.length?this.constants[i[0]]=e:2==i.length?this.constants[i[0]][i[1]]=e:3==i.length&&(this.constants[i[0]][i[1]][i[2]]=e)}},function(t){function e(t){throw new Error("Cannot find module '"+t+"'.")}e.keys=function(){return[]},e.resolve=e,t.exports=e,e.id=67},function(t,e){e._calculateNodeForces=function(){var t,e,i,s,o,n,r,a,h,d,l,c=this.calculationNodes,p=this.calculationNodeIndices,u=-2/3,m=4/3,f=this.constants.physics.repulsion.nodeDistance,g=f;for(d=0;di&&(r=.5*g>i?1:v*i+m,r*=0==n?1:1+n*this.constants.clustering.forceAmplification,r/=Math.max(i,.01*g),s=t*r,o=e*r,a.fx-=s,a.fy-=o,h.fx+=s,h.fy+=o)}}},function(t,e){e._calculateNodeForces=function(){var t,e,i,s,o,n,r,a,h,d,l=this.calculationNodes,c=this.calculationNodeIndices,p=this.constants.physics.hierarchicalRepulsion.nodeDistance;for(h=0;hi?-Math.pow(u*i,2)+Math.pow(u*p,2):0,0==i?i=.01:n/=i,s=t*n,o=e*n,r.fx-=s,r.fy-=o,a.fx+=s,a.fy+=o}},e._calculateHierarchicalSpringForces=function(){for(var t,e,i,s,o,n,r,a,h,d=this.edges,l=this.calculationNodes,c=this.calculationNodeIndices,p=0;pn;n++)t=e[i[n]],t.options.mass>0&&(this._getForceContribution(o.root.children.NW,t),this._getForceContribution(o.root.children.NE,t),this._getForceContribution(o.root.children.SW,t),this._getForceContribution(o.root.children.SE,t))}},e._getForceContribution=function(t,e){if(t.childrenCount>0){var i,s,o;if(i=t.centerOfMass.x-e.x,s=t.centerOfMass.y-e.y,o=Math.sqrt(i*i+s*s),o*t.calcSize>this.constants.physics.barnesHut.thetaInverted){0==o&&(o=.1*Math.random(),i=o);var n=this.constants.physics.barnesHut.gravitationalConstant*t.mass*e.options.mass/(o*o*o),r=i*n,a=s*n;e.fx+=r,e.fy+=a}else if(4==t.childrenCount)this._getForceContribution(t.children.NW,e),this._getForceContribution(t.children.NE,e),this._getForceContribution(t.children.SW,e),this._getForceContribution(t.children.SE,e);else if(t.children.data.id!=e.id){0==o&&(o=.5*Math.random(),i=o);var n=this.constants.physics.barnesHut.gravitationalConstant*t.mass*e.options.mass/(o*o*o),r=i*n,a=s*n;e.fx+=r,e.fy+=a}}},e._formBarnesHutTree=function(t,e){for(var i,s=e.length,o=Number.MAX_VALUE,n=Number.MAX_VALUE,r=-Number.MAX_VALUE,a=-Number.MAX_VALUE,h=0;s>h;h++){var d=t[e[h]].x,l=t[e[h]].y;t[e[h]].options.mass>0&&(o>d&&(o=d),d>r&&(r=d),n>l&&(n=l),l>a&&(a=l))}var c=Math.abs(r-o)-Math.abs(a-n);c>0?(n-=.5*c,a+=.5*c):(o+=.5*c,r-=.5*c);var p=1e-5,u=Math.max(p,Math.abs(r-o)),m=.5*u,f=.5*(o+r),g=.5*(n+a),v={root:{centerOfMass:{x:0,y:0},mass:0,range:{minX:f-m,maxX:f+m,minY:g-m,maxY:g+m},size:u,calcSize:1/u,children:{data:null},maxWidth:0,level:0,childrenCount:4}};for(this._splitBranch(v.root),h=0;s>h;h++)i=t[e[h]],i.options.mass>0&&this._placeInTree(v.root,i);this.barnesHutTree=v},e._updateBranchMass=function(t,e){var i=t.mass+e.options.mass,s=1/i;t.centerOfMass.x=t.centerOfMass.x*t.mass+e.x*e.options.mass,t.centerOfMass.x*=s,t.centerOfMass.y=t.centerOfMass.y*t.mass+e.y*e.options.mass,t.centerOfMass.y*=s,t.mass=i;var o=Math.max(Math.max(e.height,e.radius),e.width);t.maxWidth=t.maxWidthe.x?t.children.NW.range.maxY>e.y?this._placeInRegion(t,e,"NW"):this._placeInRegion(t,e,"SW"):t.children.NW.range.maxY>e.y?this._placeInRegion(t,e,"NE"):this._placeInRegion(t,e,"SE")},e._placeInRegion=function(t,e,i){switch(t.children[i].childrenCount){case 0:t.children[i].children.data=e,t.children[i].childrenCount=1,this._updateBranchMass(t.children[i],e);break;case 1:t.children[i].children.data.x==e.x&&t.children[i].children.data.y==e.y?(e.x+=Math.random(),e.y+=Math.random()):(this._splitBranch(t.children[i]),this._placeInTree(t.children[i],e));break;case 4:this._placeInTree(t.children[i],e)}},e._splitBranch=function(t){var e=null;1==t.childrenCount&&(e=t.children.data,t.mass=0,t.centerOfMass.x=0,t.centerOfMass.y=0),t.childrenCount=4,t.children.data=null,this._insertRegion(t,"NW"),this._insertRegion(t,"NE"),this._insertRegion(t,"SW"),this._insertRegion(t,"SE"),null!=e&&this._placeInTree(t,e)},e._insertRegion=function(t,e){var i,s,o,n,r=.5*t.size;switch(e){case"NW":i=t.range.minX,s=t.range.minX+r,o=t.range.minY,n=t.range.minY+r;break;case"NE":i=t.range.minX+r,s=t.range.maxX,o=t.range.minY,n=t.range.minY+r;break;case"SW":i=t.range.minX,s=t.range.minX+r,o=t.range.minY+r,n=t.range.maxY;break;case"SE":i=t.range.minX+r,s=t.range.maxX,o=t.range.minY+r,n=t.range.maxY}t.children[e]={centerOfMass:{x:0,y:0},mass:0,range:{minX:i,maxX:s,minY:o,maxY:n},size:.5*t.size,calcSize:2*t.calcSize,children:{data:null},maxWidth:0,level:t.level+1,childrenCount:0}},e._drawTree=function(t,e){void 0!==this.barnesHutTree&&(t.lineWidth=1,this._drawBranch(this.barnesHutTree.root,t,e))},e._drawBranch=function(t,e,i){void 0===i&&(i="#FF0000"),4==t.childrenCount&&(this._drawBranch(t.children.NW,e),this._drawBranch(t.children.NE,e),this._drawBranch(t.children.SE,e),this._drawBranch(t.children.SW,e)),e.strokeStyle=i,e.beginPath(),e.moveTo(t.range.minX,t.range.minY),e.lineTo(t.range.maxX,t.range.minY),e.stroke(),e.beginPath(),e.moveTo(t.range.maxX,t.range.minY),e.lineTo(t.range.maxX,t.range.maxY),e.stroke(),e.beginPath(),e.moveTo(t.range.maxX,t.range.maxY),e.lineTo(t.range.minX,t.range.maxY),e.stroke(),e.beginPath(),e.moveTo(t.range.minX,t.range.maxY),e.lineTo(t.range.minX,t.range.minY),e.stroke()}},function(t){t.exports=function(t){return t.webpackPolyfill||(t.deprecate=function(){},t.paths=[],t.children=[],t.webpackPolyfill=1),t}}])}); //# sourceMappingURL=vis.map diff --git a/lib/network/mixins/ManipulationMixin.js b/lib/network/mixins/ManipulationMixin.js index 72601b8e..cd3ed4f7 100644 --- a/lib/network/mixins/ManipulationMixin.js +++ b/lib/network/mixins/ManipulationMixin.js @@ -301,10 +301,12 @@ exports._createAddEdgeToolbar = function() { this.cachedFunctions["_manipulationReleaseOverload"] = this._manipulationReleaseOverload; this.cachedFunctions["_handleDragStart"] = this._handleDragStart; this.cachedFunctions["_handleDragEnd"] = this._handleDragEnd; + this.cachedFunctions["_handleOnHold"] = this._handleOnHold; this._handleTouch = this._handleConnect; this._manipulationReleaseOverload = function () {}; + this._handleOnHold = function () {}; this._handleDragStart = function () {}; - this._handleDragEnd = this._finishConnect; + this._handleDragEnd = this._finishConnect; // redraw to show the unselect this._redraw(); From 4cb8f2067997e093e511f6edf8a80053b6ce9640 Mon Sep 17 00:00:00 2001 From: Alex de Mulder Date: Mon, 2 Feb 2015 19:57:50 +0100 Subject: [PATCH 4/7] fixed few (but not all clustering issues) --- dist/vis.js | 54350 ++++++++++++++------------- lib/network/Network.js | 8 +- lib/network/mixins/ClusterMixin.js | 70 +- 3 files changed, 27240 insertions(+), 27188 deletions(-) diff --git a/dist/vis.js b/dist/vis.js index 0adbea51..2ffd080f 100644 --- a/dist/vis.js +++ b/dist/vis.js @@ -83,67 +83,67 @@ return /******/ (function(modules) { // webpackBootstrap // utils exports.util = __webpack_require__(1); - exports.DOMutil = __webpack_require__(2); + exports.DOMutil = __webpack_require__(6); // data - exports.DataSet = __webpack_require__(3); - exports.DataView = __webpack_require__(4); - exports.Queue = __webpack_require__(5); + exports.DataSet = __webpack_require__(7); + exports.DataView = __webpack_require__(9); + exports.Queue = __webpack_require__(8); // Graph3d - exports.Graph3d = __webpack_require__(6); + exports.Graph3d = __webpack_require__(10); exports.graph3d = { - Camera: __webpack_require__(7), - Filter: __webpack_require__(8), - Point2d: __webpack_require__(9), - Point3d: __webpack_require__(10), - Slider: __webpack_require__(11), - StepNumber: __webpack_require__(12) + Camera: __webpack_require__(14), + Filter: __webpack_require__(15), + Point2d: __webpack_require__(13), + Point3d: __webpack_require__(12), + Slider: __webpack_require__(16), + StepNumber: __webpack_require__(17) }; // Timeline - exports.Timeline = __webpack_require__(13); - exports.Graph2d = __webpack_require__(14); + exports.Timeline = __webpack_require__(18); + exports.Graph2d = __webpack_require__(42); exports.timeline = { - DateUtil: __webpack_require__(15), - DataStep: __webpack_require__(16), - Range: __webpack_require__(17), - stack: __webpack_require__(18), - TimeStep: __webpack_require__(19), + DateUtil: __webpack_require__(24), + DataStep: __webpack_require__(45), + Range: __webpack_require__(21), + stack: __webpack_require__(28), + TimeStep: __webpack_require__(38), components: { items: { - Item: __webpack_require__(31), - BackgroundItem: __webpack_require__(32), - BoxItem: __webpack_require__(33), - PointItem: __webpack_require__(34), - RangeItem: __webpack_require__(35) + Item: __webpack_require__(30), + BackgroundItem: __webpack_require__(34), + BoxItem: __webpack_require__(32), + PointItem: __webpack_require__(33), + RangeItem: __webpack_require__(29) }, - Component: __webpack_require__(20), - CurrentTime: __webpack_require__(21), - CustomTime: __webpack_require__(22), - DataAxis: __webpack_require__(23), - GraphGroup: __webpack_require__(24), - Group: __webpack_require__(25), - BackgroundGroup: __webpack_require__(26), - ItemSet: __webpack_require__(27), - Legend: __webpack_require__(28), - LineGraph: __webpack_require__(29), - TimeAxis: __webpack_require__(30) + Component: __webpack_require__(23), + CurrentTime: __webpack_require__(39), + CustomTime: __webpack_require__(41), + DataAxis: __webpack_require__(44), + GraphGroup: __webpack_require__(46), + Group: __webpack_require__(27), + BackgroundGroup: __webpack_require__(31), + ItemSet: __webpack_require__(26), + Legend: __webpack_require__(50), + LineGraph: __webpack_require__(43), + TimeAxis: __webpack_require__(37) } }; // Network - exports.Network = __webpack_require__(36); + exports.Network = __webpack_require__(51); exports.network = { - Edge: __webpack_require__(37), - Groups: __webpack_require__(38), - Images: __webpack_require__(39), - Node: __webpack_require__(40), - Popup: __webpack_require__(41), - dotparser: __webpack_require__(42), - gephiParser: __webpack_require__(43) + Edge: __webpack_require__(57), + Groups: __webpack_require__(54), + Images: __webpack_require__(55), + Node: __webpack_require__(56), + Popup: __webpack_require__(58), + dotparser: __webpack_require__(52), + gephiParser: __webpack_require__(53) }; // Deprecated since v3.0.0 @@ -152,8 +152,8 @@ return /******/ (function(modules) { // webpackBootstrap }; // bundled external libraries - exports.moment = __webpack_require__(44); - exports.hammer = __webpack_require__(45); + exports.moment = __webpack_require__(2); + exports.hammer = __webpack_require__(19); /***/ }, @@ -164,7 +164,7 @@ return /******/ (function(modules) { // webpackBootstrap // first check if moment.js is already loaded in the browser window, if so, // use this instance. Else, load via commonjs. - var moment = __webpack_require__(44); + var moment = __webpack_require__(2); /** * Test whether given object is a number @@ -1396,22018 +1396,20859 @@ return /******/ (function(modules) { // webpackBootstrap /* 2 */ /***/ function(module, exports, __webpack_require__) { - // DOM utility methods - - /** - * this prepares the JSON container for allocating SVG elements - * @param JSONcontainer - * @private - */ - exports.prepareElements = function(JSONcontainer) { - // cleanup the redundant svgElements; - for (var elementType in JSONcontainer) { - if (JSONcontainer.hasOwnProperty(elementType)) { - JSONcontainer[elementType].redundant = JSONcontainer[elementType].used; - JSONcontainer[elementType].used = []; - } - } - }; - - /** - * this cleans up all the unused SVG elements. By asking for the parentNode, we only need to supply the JSON container from - * which to remove the redundant elements. - * - * @param JSONcontainer - * @private - */ - exports.cleanupElements = function(JSONcontainer) { - // cleanup the redundant svgElements; - for (var elementType in JSONcontainer) { - if (JSONcontainer.hasOwnProperty(elementType)) { - if (JSONcontainer[elementType].redundant) { - for (var i = 0; i < JSONcontainer[elementType].redundant.length; i++) { - JSONcontainer[elementType].redundant[i].parentNode.removeChild(JSONcontainer[elementType].redundant[i]); - } - JSONcontainer[elementType].redundant = []; - } - } - } - }; - - /** - * Allocate or generate an SVG element if needed. Store a reference to it in the JSON container and draw it in the svgContainer - * the JSON container and the SVG container have to be supplied so other svg containers (like the legend) can use this. - * - * @param elementType - * @param JSONcontainer - * @param svgContainer - * @returns {*} - * @private - */ - exports.getSVGElement = function (elementType, JSONcontainer, svgContainer) { - var element; - // allocate SVG element, if it doesnt yet exist, create one. - if (JSONcontainer.hasOwnProperty(elementType)) { // this element has been created before - // check if there is an redundant element - if (JSONcontainer[elementType].redundant.length > 0) { - element = JSONcontainer[elementType].redundant[0]; - JSONcontainer[elementType].redundant.shift(); - } - else { - // create a new element and add it to the SVG - element = document.createElementNS('http://www.w3.org/2000/svg', elementType); - svgContainer.appendChild(element); - } - } - else { - // create a new element and add it to the SVG, also create a new object in the svgElements to keep track of it. - element = document.createElementNS('http://www.w3.org/2000/svg', elementType); - JSONcontainer[elementType] = {used: [], redundant: []}; - svgContainer.appendChild(element); - } - JSONcontainer[elementType].used.push(element); - return element; - }; - - - /** - * Allocate or generate an SVG element if needed. Store a reference to it in the JSON container and draw it in the svgContainer - * the JSON container and the SVG container have to be supplied so other svg containers (like the legend) can use this. - * - * @param elementType - * @param JSONcontainer - * @param DOMContainer - * @returns {*} - * @private - */ - exports.getDOMElement = function (elementType, JSONcontainer, DOMContainer, insertBefore) { - var element; - // allocate DOM element, if it doesnt yet exist, create one. - if (JSONcontainer.hasOwnProperty(elementType)) { // this element has been created before - // check if there is an redundant element - if (JSONcontainer[elementType].redundant.length > 0) { - element = JSONcontainer[elementType].redundant[0]; - JSONcontainer[elementType].redundant.shift(); - } - else { - // create a new element and add it to the SVG - element = document.createElement(elementType); - if (insertBefore !== undefined) { - DOMContainer.insertBefore(element, insertBefore); - } - else { - DOMContainer.appendChild(element); - } - } - } - else { - // create a new element and add it to the SVG, also create a new object in the svgElements to keep track of it. - element = document.createElement(elementType); - JSONcontainer[elementType] = {used: [], redundant: []}; - if (insertBefore !== undefined) { - DOMContainer.insertBefore(element, insertBefore); - } - else { - DOMContainer.appendChild(element); - } - } - JSONcontainer[elementType].used.push(element); - return element; - }; - - - - - /** - * draw a point object. this is a seperate function because it can also be called by the legend. - * The reason the JSONcontainer and the target SVG svgContainer have to be supplied is so the legend can use these functions - * as well. - * - * @param x - * @param y - * @param group - * @param JSONcontainer - * @param svgContainer - * @returns {*} - */ - exports.drawPoint = function(x, y, group, JSONcontainer, svgContainer) { - var point; - if (group.options.drawPoints.style == 'circle') { - point = exports.getSVGElement('circle',JSONcontainer,svgContainer); - point.setAttributeNS(null, "cx", x); - point.setAttributeNS(null, "cy", y); - point.setAttributeNS(null, "r", 0.5 * group.options.drawPoints.size); - } - else { - point = exports.getSVGElement('rect',JSONcontainer,svgContainer); - point.setAttributeNS(null, "x", x - 0.5*group.options.drawPoints.size); - point.setAttributeNS(null, "y", y - 0.5*group.options.drawPoints.size); - point.setAttributeNS(null, "width", group.options.drawPoints.size); - point.setAttributeNS(null, "height", group.options.drawPoints.size); - } - - if(group.options.drawPoints.styles !== undefined) { - point.setAttributeNS(null, "style", group.group.options.drawPoints.styles); - } - point.setAttributeNS(null, "class", group.className + " point"); - return point; - }; + // first check if moment.js is already loaded in the browser window, if so, + // use this instance. Else, load via commonjs. + module.exports = (typeof window !== 'undefined') && window['moment'] || __webpack_require__(3); - /** - * draw a bar SVG element centered on the X coordinate - * - * @param x - * @param y - * @param className - */ - exports.drawBar = function (x, y, width, height, className, JSONcontainer, svgContainer) { - if (height != 0) { - if (height < 0) { - height *= -1; - y -= height; - } - var rect = exports.getSVGElement('rect',JSONcontainer, svgContainer); - rect.setAttributeNS(null, "x", x - 0.5 * width); - rect.setAttributeNS(null, "y", y); - rect.setAttributeNS(null, "width", width); - rect.setAttributeNS(null, "height", height); - rect.setAttributeNS(null, "class", className); - } - }; /***/ }, /* 3 */ /***/ function(module, exports, __webpack_require__) { - var util = __webpack_require__(1); - var Queue = __webpack_require__(5); - - /** - * DataSet - * - * Usage: - * var dataSet = new DataSet({ - * fieldId: '_id', - * type: { - * // ... - * } - * }); - * - * dataSet.add(item); - * dataSet.add(data); - * dataSet.update(item); - * dataSet.update(data); - * dataSet.remove(id); - * dataSet.remove(ids); - * var data = dataSet.get(); - * var data = dataSet.get(id); - * var data = dataSet.get(ids); - * var data = dataSet.get(ids, options, data); - * dataSet.clear(); - * - * A data set can: - * - add/remove/update data - * - gives triggers upon changes in the data - * - can import/export data in various data formats - * - * @param {Array | DataTable} [data] Optional array with initial data - * @param {Object} [options] Available options: - * {String} fieldId Field name of the id in the - * items, 'id' by default. - * {Object. ['10', '00'] or '-1530' > ['-', '15', '30'] + parseTimezoneChunker = /([\+\-]|\d\d)/gi, - /** - * Add data. - * Adding an item will fail when there already is an item with the same id. - * @param {Object | Array | DataTable} data - * @param {String} [senderId] Optional sender id - * @return {Array} addedIds Array with the ids of the added items - */ - DataSet.prototype.add = function (data, senderId) { - var addedIds = [], - id, - me = this; + // getter and setter names + proxyGettersAndSetters = 'Date|Hours|Minutes|Seconds|Milliseconds'.split('|'), + unitMillisecondFactors = { + 'Milliseconds' : 1, + 'Seconds' : 1e3, + 'Minutes' : 6e4, + 'Hours' : 36e5, + 'Days' : 864e5, + 'Months' : 2592e6, + 'Years' : 31536e6 + }, - if (Array.isArray(data)) { - // Array - for (var i = 0, len = data.length; i < len; i++) { - id = me._addItem(data[i]); - addedIds.push(id); - } - } - else if (util.isDataTable(data)) { - // Google DataTable - var columns = this._getColumnNames(data); - for (var row = 0, rows = data.getNumberOfRows(); row < rows; row++) { - var item = {}; - for (var col = 0, cols = columns.length; col < cols; col++) { - var field = columns[col]; - item[field] = data.getValue(row, col); - } + unitAliases = { + ms : 'millisecond', + s : 'second', + m : 'minute', + h : 'hour', + d : 'day', + D : 'date', + w : 'week', + W : 'isoWeek', + M : 'month', + Q : 'quarter', + y : 'year', + DDD : 'dayOfYear', + e : 'weekday', + E : 'isoWeekday', + gg: 'weekYear', + GG: 'isoWeekYear' + }, - id = me._addItem(item); - addedIds.push(id); - } - } - else if (data instanceof Object) { - // Single item - id = me._addItem(data); - addedIds.push(id); - } - else { - throw new Error('Unknown dataType'); - } + camelFunctions = { + dayofyear : 'dayOfYear', + isoweekday : 'isoWeekday', + isoweek : 'isoWeek', + weekyear : 'weekYear', + isoweekyear : 'isoWeekYear' + }, - if (addedIds.length) { - this._trigger('add', {items: addedIds}, senderId); - } + // format function strings + formatFunctions = {}, - return addedIds; - }; - - /** - * Update existing items. When an item does not exist, it will be created - * @param {Object | Array | DataTable} data - * @param {String} [senderId] Optional sender id - * @return {Array} updatedIds The ids of the added or updated items - */ - DataSet.prototype.update = function (data, senderId) { - var addedIds = []; - var updatedIds = []; - var updatedData = []; - var me = this; - var fieldId = me._fieldId; - - var addOrUpdate = function (item) { - var id = item[fieldId]; - if (me._data[id]) { - // update item - id = me._updateItem(item); - updatedIds.push(id); - updatedData.push(item); - } - else { - // add new item - id = me._addItem(item); - addedIds.push(id); - } - }; - - if (Array.isArray(data)) { - // Array - for (var i = 0, len = data.length; i < len; i++) { - addOrUpdate(data[i]); - } - } - else if (util.isDataTable(data)) { - // Google DataTable - var columns = this._getColumnNames(data); - for (var row = 0, rows = data.getNumberOfRows(); row < rows; row++) { - var item = {}; - for (var col = 0, cols = columns.length; col < cols; col++) { - var field = columns[col]; - item[field] = data.getValue(row, col); - } - - addOrUpdate(item); - } - } - else if (data instanceof Object) { - // Single item - addOrUpdate(data); - } - else { - throw new Error('Unknown dataType'); - } + // default relative time thresholds + relativeTimeThresholds = { + s: 45, // seconds to minute + m: 45, // minutes to hour + h: 22, // hours to day + d: 26, // days to month + M: 11 // months to year + }, - if (addedIds.length) { - this._trigger('add', {items: addedIds}, senderId); - } - if (updatedIds.length) { - this._trigger('update', {items: updatedIds, data: updatedData}, senderId); - } + // tokens to ordinalize and pad + ordinalizeTokens = 'DDD w W M D d'.split(' '), + paddedTokens = 'M D H h m s w W'.split(' '), - return addedIds.concat(updatedIds); - }; + formatTokenFunctions = { + M : function () { + return this.month() + 1; + }, + MMM : function (format) { + return this.localeData().monthsShort(this, format); + }, + MMMM : function (format) { + return this.localeData().months(this, format); + }, + D : function () { + return this.date(); + }, + DDD : function () { + return this.dayOfYear(); + }, + d : function () { + return this.day(); + }, + dd : function (format) { + return this.localeData().weekdaysMin(this, format); + }, + ddd : function (format) { + return this.localeData().weekdaysShort(this, format); + }, + dddd : function (format) { + return this.localeData().weekdays(this, format); + }, + w : function () { + return this.week(); + }, + W : function () { + return this.isoWeek(); + }, + YY : function () { + return leftZeroFill(this.year() % 100, 2); + }, + YYYY : function () { + return leftZeroFill(this.year(), 4); + }, + YYYYY : function () { + return leftZeroFill(this.year(), 5); + }, + YYYYYY : function () { + var y = this.year(), sign = y >= 0 ? '+' : '-'; + return sign + leftZeroFill(Math.abs(y), 6); + }, + gg : function () { + return leftZeroFill(this.weekYear() % 100, 2); + }, + gggg : function () { + return leftZeroFill(this.weekYear(), 4); + }, + ggggg : function () { + return leftZeroFill(this.weekYear(), 5); + }, + GG : function () { + return leftZeroFill(this.isoWeekYear() % 100, 2); + }, + GGGG : function () { + return leftZeroFill(this.isoWeekYear(), 4); + }, + GGGGG : function () { + return leftZeroFill(this.isoWeekYear(), 5); + }, + e : function () { + return this.weekday(); + }, + E : function () { + return this.isoWeekday(); + }, + a : function () { + return this.localeData().meridiem(this.hours(), this.minutes(), true); + }, + A : function () { + return this.localeData().meridiem(this.hours(), this.minutes(), false); + }, + H : function () { + return this.hours(); + }, + h : function () { + return this.hours() % 12 || 12; + }, + m : function () { + return this.minutes(); + }, + s : function () { + return this.seconds(); + }, + S : function () { + return toInt(this.milliseconds() / 100); + }, + SS : function () { + return leftZeroFill(toInt(this.milliseconds() / 10), 2); + }, + SSS : function () { + return leftZeroFill(this.milliseconds(), 3); + }, + SSSS : function () { + return leftZeroFill(this.milliseconds(), 3); + }, + Z : function () { + var a = this.utcOffset(), + b = '+'; + if (a < 0) { + a = -a; + b = '-'; + } + return b + leftZeroFill(toInt(a / 60), 2) + ':' + leftZeroFill(toInt(a) % 60, 2); + }, + ZZ : function () { + var a = this.utcOffset(), + b = '+'; + if (a < 0) { + a = -a; + b = '-'; + } + return b + leftZeroFill(toInt(a / 60), 2) + leftZeroFill(toInt(a) % 60, 2); + }, + z : function () { + return this.zoneAbbr(); + }, + zz : function () { + return this.zoneName(); + }, + x : function () { + return this.valueOf(); + }, + X : function () { + return this.unix(); + }, + Q : function () { + return this.quarter(); + } + }, - /** - * Get a data item or multiple items. - * - * Usage: - * - * get() - * get(options: Object) - * get(options: Object, data: Array | DataTable) - * - * get(id: Number | String) - * get(id: Number | String, options: Object) - * get(id: Number | String, options: Object, data: Array | DataTable) - * - * get(ids: Number[] | String[]) - * get(ids: Number[] | String[], options: Object) - * get(ids: Number[] | String[], options: Object, data: Array | DataTable) - * - * Where: - * - * {Number | String} id The id of an item - * {Number[] | String{}} ids An array with ids of items - * {Object} options An Object with options. Available options: - * {String} [returnType] Type of data to be - * returned. Can be 'DataTable' or 'Array' (default) - * {Object.} [type] - * {String[]} [fields] field names to be returned - * {function} [filter] filter items - * {String | function} [order] Order the items by - * a field name or custom sort function. - * {Array | DataTable} [data] If provided, items will be appended to this - * array or table. Required in case of Google - * DataTable. - * - * @throws Error - */ - DataSet.prototype.get = function (args) { - var me = this; + deprecations = {}, - // parse the arguments - var id, ids, options, data; - var firstType = util.getType(arguments[0]); - if (firstType == 'String' || firstType == 'Number') { - // get(id [, options] [, data]) - id = arguments[0]; - options = arguments[1]; - data = arguments[2]; - } - else if (firstType == 'Array') { - // get(ids [, options] [, data]) - ids = arguments[0]; - options = arguments[1]; - data = arguments[2]; - } - else { - // get([, options] [, data]) - options = arguments[0]; - data = arguments[1]; - } + lists = ['months', 'monthsShort', 'weekdays', 'weekdaysShort', 'weekdaysMin'], - // determine the return type - var returnType; - if (options && options.returnType) { - var allowedValues = ["DataTable", "Array", "Object"]; - returnType = allowedValues.indexOf(options.returnType) == -1 ? "Array" : options.returnType; + updateInProgress = false; - if (data && (returnType != util.getType(data))) { - throw new Error('Type of parameter "data" (' + util.getType(data) + ') ' + - 'does not correspond with specified options.type (' + options.type + ')'); - } - if (returnType == 'DataTable' && !util.isDataTable(data)) { - throw new Error('Parameter "data" must be a DataTable ' + - 'when options.type is "DataTable"'); + // Pick the first defined of two or three arguments. dfl comes from + // default. + function dfl(a, b, c) { + switch (arguments.length) { + case 2: return a != null ? a : b; + case 3: return a != null ? a : b != null ? b : c; + default: throw new Error('Implement me'); + } } - } - else if (data) { - returnType = (util.getType(data) == 'DataTable') ? 'DataTable' : 'Array'; - } - else { - returnType = 'Array'; - } - // build options - var type = options && options.type || this._options.type; - var filter = options && options.filter; - var items = [], item, itemId, i, len; - - // convert items - if (id != undefined) { - // return a single item - item = me._getItem(id, type); - if (filter && !filter(item)) { - item = null; + function hasOwnProp(a, b) { + return hasOwnProperty.call(a, b); } - } - else if (ids != undefined) { - // return a subset of items - for (i = 0, len = ids.length; i < len; i++) { - item = me._getItem(ids[i], type); - if (!filter || filter(item)) { - items.push(item); - } + + function defaultParsingFlags() { + // We need to deep clone this object, and es5 standard is not very + // helpful. + return { + empty : false, + unusedTokens : [], + unusedInput : [], + overflow : -2, + charsLeftOver : 0, + nullInput : false, + invalidMonth : null, + invalidFormat : false, + userInvalidated : false, + iso: false + }; } - } - else { - // return all items - for (itemId in this._data) { - if (this._data.hasOwnProperty(itemId)) { - item = me._getItem(itemId, type); - if (!filter || filter(item)) { - items.push(item); + + function printMsg(msg) { + if (moment.suppressDeprecationWarnings === false && + typeof console !== 'undefined' && console.warn) { + console.warn('Deprecation warning: ' + msg); } - } } - } - - // order the results - if (options && options.order && id == undefined) { - this._sort(items, options.order); - } - // filter fields of the items - if (options && options.fields) { - var fields = options.fields; - if (id != undefined) { - item = this._filterFields(item, fields); - } - else { - for (i = 0, len = items.length; i < len; i++) { - items[i] = this._filterFields(items[i], fields); - } + function deprecate(msg, fn) { + var firstTime = true; + return extend(function () { + if (firstTime) { + printMsg(msg); + firstTime = false; + } + return fn.apply(this, arguments); + }, fn); } - } - // return the results - if (returnType == 'DataTable') { - var columns = this._getColumnNames(data); - if (id != undefined) { - // append a single item to the data table - me._appendRow(data, columns, item); - } - else { - // copy the items to the provided data table - for (i = 0; i < items.length; i++) { - me._appendRow(data, columns, items[i]); - } - } - return data; - } - else if (returnType == "Object") { - var result = {}; - for (i = 0; i < items.length; i++) { - result[items[i].id] = items[i]; + function deprecateSimple(name, msg) { + if (!deprecations[name]) { + printMsg(msg); + deprecations[name] = true; + } } - return result; - } - else { - // return an array - if (id != undefined) { - // a single item - return item; + + function padToken(func, count) { + return function (a) { + return leftZeroFill(func.call(this, a), count); + }; } - else { - // multiple items - if (data) { - // copy the items to the provided array - for (i = 0, len = items.length; i < len; i++) { - data.push(items[i]); - } - return data; - } - else { - // just return our array - return items; - } + function ordinalizeToken(func, period) { + return function (a) { + return this.localeData().ordinal(func.call(this, a), period); + }; } - } - }; - /** - * Get ids of all items or from a filtered set of items. - * @param {Object} [options] An Object with options. Available options: - * {function} [filter] filter items - * {String | function} [order] Order the items by - * a field name or custom sort function. - * @return {Array} ids - */ - DataSet.prototype.getIds = function (options) { - var data = this._data, - filter = options && options.filter, - order = options && options.order, - type = options && options.type || this._options.type, - i, - len, - id, - item, - items, - ids = []; + function monthDiff(a, b) { + // difference in months + var wholeMonthDiff = ((b.year() - a.year()) * 12) + (b.month() - a.month()), + // b is in (anchor - 1 month, anchor + 1 month) + anchor = a.clone().add(wholeMonthDiff, 'months'), + anchor2, adjust; - if (filter) { - // get filtered items - if (order) { - // create ordered list - items = []; - for (id in data) { - if (data.hasOwnProperty(id)) { - item = this._getItem(id, type); - if (filter(item)) { - items.push(item); - } + if (b - anchor < 0) { + anchor2 = a.clone().add(wholeMonthDiff - 1, 'months'); + // linear across the month + adjust = (b - anchor) / (anchor - anchor2); + } else { + anchor2 = a.clone().add(wholeMonthDiff + 1, 'months'); + // linear across the month + adjust = (b - anchor) / (anchor2 - anchor); } - } - this._sort(items, order); + return -(wholeMonthDiff + adjust); + } - for (i = 0, len = items.length; i < len; i++) { - ids[i] = items[i][this._fieldId]; - } + while (ordinalizeTokens.length) { + i = ordinalizeTokens.pop(); + formatTokenFunctions[i + 'o'] = ordinalizeToken(formatTokenFunctions[i], i); } - else { - // create unordered list - for (id in data) { - if (data.hasOwnProperty(id)) { - item = this._getItem(id, type); - if (filter(item)) { - ids.push(item[this._fieldId]); - } - } - } + while (paddedTokens.length) { + i = paddedTokens.pop(); + formatTokenFunctions[i + i] = padToken(formatTokenFunctions[i], 2); } - } - else { - // get all items - if (order) { - // create an ordered list - items = []; - for (id in data) { - if (data.hasOwnProperty(id)) { - items.push(data[id]); - } - } + formatTokenFunctions.DDDD = padToken(formatTokenFunctions.DDD, 3); - this._sort(items, order); - for (i = 0, len = items.length; i < len; i++) { - ids[i] = items[i][this._fieldId]; - } - } - else { - // create unordered list - for (id in data) { - if (data.hasOwnProperty(id)) { - item = data[id]; - ids.push(item[this._fieldId]); + function meridiemFixWrap(locale, hour, meridiem) { + var isPm; + + if (meridiem == null) { + // nothing to do + return hour; + } + if (locale.meridiemHour != null) { + return locale.meridiemHour(hour, meridiem); + } else if (locale.isPM != null) { + // Fallback + isPm = locale.isPM(meridiem); + if (isPm && hour < 12) { + hour += 12; + } + if (!isPm && hour === 12) { + hour = 0; + } + return hour; + } else { + // thie is not supposed to happen + return hour; } - } } - } - - return ids; - }; - - /** - * Returns the DataSet itself. Is overwritten for example by the DataView, - * which returns the DataSet it is connected to instead. - */ - DataSet.prototype.getDataSet = function () { - return this; - }; - - /** - * Execute a callback function for every item in the dataset. - * @param {function} callback - * @param {Object} [options] Available options: - * {Object.} [type] - * {String[]} [fields] filter fields - * {function} [filter] filter items - * {String | function} [order] Order the items by - * a field name or custom sort function. - */ - DataSet.prototype.forEach = function (callback, options) { - var filter = options && options.filter, - type = options && options.type || this._options.type, - data = this._data, - item, - id; - if (options && options.order) { - // execute forEach on ordered list - var items = this.get(options); + /************************************ + Constructors + ************************************/ - for (var i = 0, len = items.length; i < len; i++) { - item = items[i]; - id = item[this._fieldId]; - callback(item, id); + function Locale() { } - } - else { - // unordered - for (id in data) { - if (data.hasOwnProperty(id)) { - item = this._getItem(id, type); - if (!filter || filter(item)) { - callback(item, id); + + // Moment prototype object + function Moment(config, skipOverflow) { + if (skipOverflow !== false) { + checkOverflow(config); + } + copyConfig(this, config); + this._d = new Date(+config._d); + // Prevent infinite loop in case updateOffset creates new moment + // objects. + if (updateInProgress === false) { + updateInProgress = true; + moment.updateOffset(this); + updateInProgress = false; } - } } - } - }; - - /** - * Map every item in the dataset. - * @param {function} callback - * @param {Object} [options] Available options: - * {Object.} [type] - * {String[]} [fields] filter fields - * {function} [filter] filter items - * {String | function} [order] Order the items by - * a field name or custom sort function. - * @return {Object[]} mappedItems - */ - DataSet.prototype.map = function (callback, options) { - var filter = options && options.filter, - type = options && options.type || this._options.type, - mappedItems = [], - data = this._data, - item; - // convert and filter items - for (var id in data) { - if (data.hasOwnProperty(id)) { - item = this._getItem(id, type); - if (!filter || filter(item)) { - mappedItems.push(callback(item, id)); - } - } - } + // Duration Constructor + function Duration(duration) { + var normalizedInput = normalizeObjectUnits(duration), + years = normalizedInput.year || 0, + quarters = normalizedInput.quarter || 0, + months = normalizedInput.month || 0, + weeks = normalizedInput.week || 0, + days = normalizedInput.day || 0, + hours = normalizedInput.hour || 0, + minutes = normalizedInput.minute || 0, + seconds = normalizedInput.second || 0, + milliseconds = normalizedInput.millisecond || 0; - // order items - if (options && options.order) { - this._sort(mappedItems, options.order); - } + // representation for dateAddRemove + this._milliseconds = +milliseconds + + seconds * 1e3 + // 1000 + minutes * 6e4 + // 1000 * 60 + hours * 36e5; // 1000 * 60 * 60 + // Because of dateAddRemove treats 24 hours as different from a + // day when working around DST, we need to store them separately + this._days = +days + + weeks * 7; + // It is impossible translate months into days without knowing + // which months you are are talking about, so we have to store + // it separately. + this._months = +months + + quarters * 3 + + years * 12; - return mappedItems; - }; + this._data = {}; - /** - * Filter the fields of an item - * @param {Object} item - * @param {String[]} fields Field names - * @return {Object} filteredItem - * @private - */ - DataSet.prototype._filterFields = function (item, fields) { - var filteredItem = {}; + this._locale = moment.localeData(); - for (var field in item) { - if (item.hasOwnProperty(field) && (fields.indexOf(field) != -1)) { - filteredItem[field] = item[field]; + this._bubble(); } - } - return filteredItem; - }; + /************************************ + Helpers + ************************************/ - /** - * Sort the provided array with items - * @param {Object[]} items - * @param {String | function} order A field name or custom sort function. - * @private - */ - DataSet.prototype._sort = function (items, order) { - if (util.isString(order)) { - // order by provided field name - var name = order; // field name - items.sort(function (a, b) { - var av = a[name]; - var bv = b[name]; - return (av > bv) ? 1 : ((av < bv) ? -1 : 0); - }); - } - else if (typeof order === 'function') { - // order by sort function - items.sort(order); - } - // TODO: extend order by an Object {field:String, direction:String} - // where direction can be 'asc' or 'desc' - else { - throw new TypeError('Order must be a function or a string'); - } - }; - /** - * Remove an object by pointer or by id - * @param {String | Number | Object | Array} id Object or id, or an array with - * objects or ids to be removed - * @param {String} [senderId] Optional sender id - * @return {Array} removedIds - */ - DataSet.prototype.remove = function (id, senderId) { - var removedIds = [], - i, len, removedId; + function extend(a, b) { + for (var i in b) { + if (hasOwnProp(b, i)) { + a[i] = b[i]; + } + } - if (Array.isArray(id)) { - for (i = 0, len = id.length; i < len; i++) { - removedId = this._remove(id[i]); - if (removedId != null) { - removedIds.push(removedId); - } - } - } - else { - removedId = this._remove(id); - if (removedId != null) { - removedIds.push(removedId); + if (hasOwnProp(b, 'toString')) { + a.toString = b.toString; + } + + if (hasOwnProp(b, 'valueOf')) { + a.valueOf = b.valueOf; + } + + return a; } - } - if (removedIds.length) { - this._trigger('remove', {items: removedIds}, senderId); - } + function copyConfig(to, from) { + var i, prop, val; - return removedIds; - }; + if (typeof from._isAMomentObject !== 'undefined') { + to._isAMomentObject = from._isAMomentObject; + } + if (typeof from._i !== 'undefined') { + to._i = from._i; + } + if (typeof from._f !== 'undefined') { + to._f = from._f; + } + if (typeof from._l !== 'undefined') { + to._l = from._l; + } + if (typeof from._strict !== 'undefined') { + to._strict = from._strict; + } + if (typeof from._tzm !== 'undefined') { + to._tzm = from._tzm; + } + if (typeof from._isUTC !== 'undefined') { + to._isUTC = from._isUTC; + } + if (typeof from._offset !== 'undefined') { + to._offset = from._offset; + } + if (typeof from._pf !== 'undefined') { + to._pf = from._pf; + } + if (typeof from._locale !== 'undefined') { + to._locale = from._locale; + } - /** - * Remove an item by its id - * @param {Number | String | Object} id id or item - * @returns {Number | String | null} id - * @private - */ - DataSet.prototype._remove = function (id) { - if (util.isNumber(id) || util.isString(id)) { - if (this._data[id]) { - delete this._data[id]; - this.length--; - return id; + if (momentProperties.length > 0) { + for (i in momentProperties) { + prop = momentProperties[i]; + val = from[prop]; + if (typeof val !== 'undefined') { + to[prop] = val; + } + } + } + + return to; } - } - else if (id instanceof Object) { - var itemId = id[this._fieldId]; - if (itemId && this._data[itemId]) { - delete this._data[itemId]; - this.length--; - return itemId; + + function absRound(number) { + if (number < 0) { + return Math.ceil(number); + } else { + return Math.floor(number); + } } - } - return null; - }; - /** - * Clear the data - * @param {String} [senderId] Optional sender id - * @return {Array} removedIds The ids of all removed items - */ - DataSet.prototype.clear = function (senderId) { - var ids = Object.keys(this._data); + // left zero fill a number + // see http://jsperf.com/left-zero-filling for performance comparison + function leftZeroFill(number, targetLength, forceSign) { + var output = '' + Math.abs(number), + sign = number >= 0; - this._data = {}; - this.length = 0; + while (output.length < targetLength) { + output = '0' + output; + } + return (sign ? (forceSign ? '+' : '') : '-') + output; + } - this._trigger('remove', {items: ids}, senderId); + function positiveMomentsDifference(base, other) { + var res = {milliseconds: 0, months: 0}; - return ids; - }; + res.months = other.month() - base.month() + + (other.year() - base.year()) * 12; + if (base.clone().add(res.months, 'M').isAfter(other)) { + --res.months; + } - /** - * Find the item with maximum value of a specified field - * @param {String} field - * @return {Object | null} item Item containing max value, or null if no items - */ - DataSet.prototype.max = function (field) { - var data = this._data, - max = null, - maxField = null; + res.milliseconds = +other - +(base.clone().add(res.months, 'M')); - for (var id in data) { - if (data.hasOwnProperty(id)) { - var item = data[id]; - var itemField = item[field]; - if (itemField != null && (!max || itemField > maxField)) { - max = item; - maxField = itemField; - } + return res; } - } - - return max; - }; - /** - * Find the item with minimum value of a specified field - * @param {String} field - * @return {Object | null} item Item containing max value, or null if no items - */ - DataSet.prototype.min = function (field) { - var data = this._data, - min = null, - minField = null; + function momentsDifference(base, other) { + var res; + other = makeAs(other, base); + if (base.isBefore(other)) { + res = positiveMomentsDifference(base, other); + } else { + res = positiveMomentsDifference(other, base); + res.milliseconds = -res.milliseconds; + res.months = -res.months; + } - for (var id in data) { - if (data.hasOwnProperty(id)) { - var item = data[id]; - var itemField = item[field]; - if (itemField != null && (!min || itemField < minField)) { - min = item; - minField = itemField; - } + return res; } - } - return min; - }; + // TODO: remove 'name' arg after deprecation is removed + function createAdder(direction, name) { + return function (val, period) { + var dur, tmp; + //invert the arguments, but complain about it + if (period !== null && !isNaN(+period)) { + deprecateSimple(name, 'moment().' + name + '(period, number) is deprecated. Please use moment().' + name + '(number, period).'); + tmp = val; val = period; period = tmp; + } - /** - * Find all distinct values of a specified field - * @param {String} field - * @return {Array} values Array containing all distinct values. If data items - * do not contain the specified field are ignored. - * The returned array is unordered. - */ - DataSet.prototype.distinct = function (field) { - var data = this._data; - var values = []; - var fieldType = this._options.type && this._options.type[field] || null; - var count = 0; - var i; + val = typeof val === 'string' ? +val : val; + dur = moment.duration(val, period); + addOrSubtractDurationFromMoment(this, dur, direction); + return this; + }; + } - for (var prop in data) { - if (data.hasOwnProperty(prop)) { - var item = data[prop]; - var value = item[field]; - var exists = false; - for (i = 0; i < count; i++) { - if (values[i] == value) { - exists = true; - break; + function addOrSubtractDurationFromMoment(mom, duration, isAdding, updateOffset) { + var milliseconds = duration._milliseconds, + days = duration._days, + months = duration._months; + updateOffset = updateOffset == null ? true : updateOffset; + + if (milliseconds) { + mom._d.setTime(+mom._d + milliseconds * isAdding); + } + if (days) { + rawSetter(mom, 'Date', rawGetter(mom, 'Date') + days * isAdding); + } + if (months) { + rawMonthSetter(mom, rawGetter(mom, 'Month') + months * isAdding); + } + if (updateOffset) { + moment.updateOffset(mom, days || months); } - } - if (!exists && (value !== undefined)) { - values[count] = value; - count++; - } } - } - if (fieldType) { - for (i = 0; i < values.length; i++) { - values[i] = util.convert(values[i], fieldType); + // check if is an array + function isArray(input) { + return Object.prototype.toString.call(input) === '[object Array]'; } - } - return values; - }; + function isDate(input) { + return Object.prototype.toString.call(input) === '[object Date]' || + input instanceof Date; + } - /** - * Add a single item. Will fail when an item with the same id already exists. - * @param {Object} item - * @return {String} id - * @private - */ - DataSet.prototype._addItem = function (item) { - var id = item[this._fieldId]; + // compare two arrays, return the number of differences + function compareArrays(array1, array2, dontConvert) { + var len = Math.min(array1.length, array2.length), + lengthDiff = Math.abs(array1.length - array2.length), + diffs = 0, + i; + for (i = 0; i < len; i++) { + if ((dontConvert && array1[i] !== array2[i]) || + (!dontConvert && toInt(array1[i]) !== toInt(array2[i]))) { + diffs++; + } + } + return diffs + lengthDiff; + } - if (id != undefined) { - // check whether this id is already taken - if (this._data[id]) { - // item already exists - throw new Error('Cannot add item: item with id ' + id + ' already exists'); + function normalizeUnits(units) { + if (units) { + var lowered = units.toLowerCase().replace(/(.)s$/, '$1'); + units = unitAliases[units] || camelFunctions[lowered] || lowered; + } + return units; } - } - else { - // generate an id - id = util.randomUUID(); - item[this._fieldId] = id; - } - var d = {}; - for (var field in item) { - if (item.hasOwnProperty(field)) { - var fieldType = this._type[field]; // type may be undefined - d[field] = util.convert(item[field], fieldType); - } - } - this._data[id] = d; - this.length++; + function normalizeObjectUnits(inputObject) { + var normalizedInput = {}, + normalizedProp, + prop; - return id; - }; + for (prop in inputObject) { + if (hasOwnProp(inputObject, prop)) { + normalizedProp = normalizeUnits(prop); + if (normalizedProp) { + normalizedInput[normalizedProp] = inputObject[prop]; + } + } + } - /** - * Get an item. Fields can be converted to a specific type - * @param {String} id - * @param {Object.} [types] field types to convert - * @return {Object | null} item - * @private - */ - DataSet.prototype._getItem = function (id, types) { - var field, value; + return normalizedInput; + } - // get the item from the dataset - var raw = this._data[id]; - if (!raw) { - return null; - } + function makeList(field) { + var count, setter; - // convert the items field types - var converted = {}; - if (types) { - for (field in raw) { - if (raw.hasOwnProperty(field)) { - value = raw[field]; - converted[field] = util.convert(value, types[field]); - } - } - } - else { - // no field types specified, no converting needed - for (field in raw) { - if (raw.hasOwnProperty(field)) { - value = raw[field]; - converted[field] = value; - } - } - } - return converted; - }; + if (field.indexOf('week') === 0) { + count = 7; + setter = 'day'; + } + else if (field.indexOf('month') === 0) { + count = 12; + setter = 'month'; + } + else { + return; + } - /** - * Update a single item: merge with existing item. - * Will fail when the item has no id, or when there does not exist an item - * with the same id. - * @param {Object} item - * @return {String} id - * @private - */ - DataSet.prototype._updateItem = function (item) { - var id = item[this._fieldId]; - if (id == undefined) { - throw new Error('Cannot update item: item has no id (item: ' + JSON.stringify(item) + ')'); - } - var d = this._data[id]; - if (!d) { - // item doesn't exist - throw new Error('Cannot update item: no item with id ' + id + ' found'); - } + moment[field] = function (format, index) { + var i, getter, + method = moment._locale[field], + results = []; - // merge with current item - for (var field in item) { - if (item.hasOwnProperty(field)) { - var fieldType = this._type[field]; // type may be undefined - d[field] = util.convert(item[field], fieldType); - } - } + if (typeof format === 'number') { + index = format; + format = undefined; + } - return id; - }; + getter = function (i) { + var m = moment().utc().set(setter, i); + return method.call(moment._locale, m, format || ''); + }; - /** - * Get an array with the column names of a Google DataTable - * @param {DataTable} dataTable - * @return {String[]} columnNames - * @private - */ - DataSet.prototype._getColumnNames = function (dataTable) { - var columns = []; - for (var col = 0, cols = dataTable.getNumberOfColumns(); col < cols; col++) { - columns[col] = dataTable.getColumnId(col) || dataTable.getColumnLabel(col); - } - return columns; - }; + if (index != null) { + return getter(index); + } + else { + for (i = 0; i < count; i++) { + results.push(getter(i)); + } + return results; + } + }; + } - /** - * Append an item as a row to the dataTable - * @param dataTable - * @param columns - * @param item - * @private - */ - DataSet.prototype._appendRow = function (dataTable, columns, item) { - var row = dataTable.addRow(); + function toInt(argumentForCoercion) { + var coercedNumber = +argumentForCoercion, + value = 0; - for (var col = 0, cols = columns.length; col < cols; col++) { - var field = columns[col]; - dataTable.setValue(row, col, item[field]); - } - }; + if (coercedNumber !== 0 && isFinite(coercedNumber)) { + if (coercedNumber >= 0) { + value = Math.floor(coercedNumber); + } else { + value = Math.ceil(coercedNumber); + } + } - module.exports = DataSet; + return value; + } + function daysInMonth(year, month) { + return new Date(Date.UTC(year, month + 1, 0)).getUTCDate(); + } -/***/ }, -/* 4 */ -/***/ function(module, exports, __webpack_require__) { + function weeksInYear(year, dow, doy) { + return weekOfYear(moment([year, 11, 31 + dow - doy]), dow, doy).week; + } - var util = __webpack_require__(1); - var DataSet = __webpack_require__(3); + function daysInYear(year) { + return isLeapYear(year) ? 366 : 365; + } - /** - * DataView - * - * a dataview offers a filtered view on a dataset or an other dataview. - * - * @param {DataSet | DataView} data - * @param {Object} [options] Available options: see method get - * - * @constructor DataView - */ - function DataView (data, options) { - this._data = null; - this._ids = {}; // ids of the items currently in memory (just contains a boolean true) - this.length = 0; // number of items in the DataView - this._options = options || {}; - this._fieldId = 'id'; // name of the field containing id - this._subscribers = {}; // event subscribers + function isLeapYear(year) { + return (year % 4 === 0 && year % 100 !== 0) || year % 400 === 0; + } - var me = this; - this.listener = function () { - me._onEvent.apply(me, arguments); - }; + function checkOverflow(m) { + var overflow; + if (m._a && m._pf.overflow === -2) { + overflow = + m._a[MONTH] < 0 || m._a[MONTH] > 11 ? MONTH : + m._a[DATE] < 1 || m._a[DATE] > daysInMonth(m._a[YEAR], m._a[MONTH]) ? DATE : + m._a[HOUR] < 0 || m._a[HOUR] > 24 || + (m._a[HOUR] === 24 && (m._a[MINUTE] !== 0 || + m._a[SECOND] !== 0 || + m._a[MILLISECOND] !== 0)) ? HOUR : + m._a[MINUTE] < 0 || m._a[MINUTE] > 59 ? MINUTE : + m._a[SECOND] < 0 || m._a[SECOND] > 59 ? SECOND : + m._a[MILLISECOND] < 0 || m._a[MILLISECOND] > 999 ? MILLISECOND : + -1; - this.setData(data); - } + if (m._pf._overflowDayOfYear && (overflow < YEAR || overflow > DATE)) { + overflow = DATE; + } - // TODO: implement a function .config() to dynamically update things like configured filter - // and trigger changes accordingly + m._pf.overflow = overflow; + } + } - /** - * Set a data source for the view - * @param {DataSet | DataView} data - */ - DataView.prototype.setData = function (data) { - var ids, i, len; + function isValid(m) { + if (m._isValid == null) { + m._isValid = !isNaN(m._d.getTime()) && + m._pf.overflow < 0 && + !m._pf.empty && + !m._pf.invalidMonth && + !m._pf.nullInput && + !m._pf.invalidFormat && + !m._pf.userInvalidated; - if (this._data) { - // unsubscribe from current dataset - if (this._data.unsubscribe) { - this._data.unsubscribe('*', this.listener); + if (m._strict) { + m._isValid = m._isValid && + m._pf.charsLeftOver === 0 && + m._pf.unusedTokens.length === 0 && + m._pf.bigHour === undefined; + } + } + return m._isValid; } - // trigger a remove of all items in memory - ids = []; - for (var id in this._ids) { - if (this._ids.hasOwnProperty(id)) { - ids.push(id); - } + function normalizeLocale(key) { + return key ? key.toLowerCase().replace('_', '-') : key; } - this._ids = {}; - this.length = 0; - this._trigger('remove', {items: ids}); - } - this._data = data; + // pick the locale from the array + // try ['en-au', 'en-gb'] as 'en-au', 'en-gb', 'en', as in move through the list trying each + // substring from most specific to least, but move to the next array item if it's a more specific variant than the current root + function chooseLocale(names) { + var i = 0, j, next, locale, split; - if (this._data) { - // update fieldId - this._fieldId = this._options.fieldId || - (this._data && this._data.options && this._data.options.fieldId) || - 'id'; + while (i < names.length) { + split = normalizeLocale(names[i]).split('-'); + j = split.length; + next = normalizeLocale(names[i + 1]); + next = next ? next.split('-') : null; + while (j > 0) { + locale = loadLocale(split.slice(0, j).join('-')); + if (locale) { + return locale; + } + if (next && next.length >= j && compareArrays(split, next, true) >= j - 1) { + //the next array item is better than a shallower substring of this one + break; + } + j--; + } + i++; + } + return null; + } - // trigger an add of all added items - ids = this._data.getIds({filter: this._options && this._options.filter}); - for (i = 0, len = ids.length; i < len; i++) { - id = ids[i]; - this._ids[id] = true; + function loadLocale(name) { + var oldLocale = null; + if (!locales[name] && hasModule) { + try { + oldLocale = moment.locale(); + !(function webpackMissingModule() { var e = new Error("Cannot find module \"./locale\""); e.code = 'MODULE_NOT_FOUND'; throw e; }()); + // because defineLocale currently also sets the global locale, we want to undo that for lazy loaded locales + moment.locale(oldLocale); + } catch (e) { } + } + return locales[name]; } - this.length = ids.length; - this._trigger('add', {items: ids}); - // subscribe to new dataset - if (this._data.on) { - this._data.on('*', this.listener); + // Return a moment from input, that is local/utc/utcOffset equivalent to + // model. + function makeAs(input, model) { + var res, diff; + if (model._isUTC) { + res = model.clone(); + diff = (moment.isMoment(input) || isDate(input) ? + +input : +moment(input)) - (+res); + // Use low-level api, because this fn is low-level api. + res._d.setTime(+res._d + diff); + moment.updateOffset(res, false); + return res; + } else { + return moment(input).local(); + } } - } - }; - /** - * Get data from the data view - * - * Usage: - * - * get() - * get(options: Object) - * get(options: Object, data: Array | DataTable) - * - * get(id: Number) - * get(id: Number, options: Object) - * get(id: Number, options: Object, data: Array | DataTable) - * - * get(ids: Number[]) - * get(ids: Number[], options: Object) - * get(ids: Number[], options: Object, data: Array | DataTable) - * - * Where: - * - * {Number | String} id The id of an item - * {Number[] | String{}} ids An array with ids of items - * {Object} options An Object with options. Available options: - * {String} [type] Type of data to be returned. Can - * be 'DataTable' or 'Array' (default) - * {Object.} [convert] - * {String[]} [fields] field names to be returned - * {function} [filter] filter items - * {String | function} [order] Order the items by - * a field name or custom sort function. - * {Array | DataTable} [data] If provided, items will be appended to this - * array or table. Required in case of Google - * DataTable. - * @param args - */ - DataView.prototype.get = function (args) { - var me = this; + /************************************ + Locale + ************************************/ - // parse the arguments - var ids, options, data; - var firstType = util.getType(arguments[0]); - if (firstType == 'String' || firstType == 'Number' || firstType == 'Array') { - // get(id(s) [, options] [, data]) - ids = arguments[0]; // can be a single id or an array with ids - options = arguments[1]; - data = arguments[2]; - } - else { - // get([, options] [, data]) - options = arguments[0]; - data = arguments[1]; - } - // extend the options with the default options and provided options - var viewOptions = util.extend({}, this._options, options); + extend(Locale.prototype, { - // create a combined filter method when needed - if (this._options.filter && options && options.filter) { - viewOptions.filter = function (item) { - return me._options.filter(item) && options.filter(item); - } - } + set : function (config) { + var prop, i; + for (i in config) { + prop = config[i]; + if (typeof prop === 'function') { + this[i] = prop; + } else { + this['_' + i] = prop; + } + } + // Lenient ordinal parsing accepts just a number in addition to + // number + (possibly) stuff coming from _ordinalParseLenient. + this._ordinalParseLenient = new RegExp(this._ordinalParse.source + '|' + /\d{1,2}/.source); + }, - // build up the call to the linked data set - var getArguments = []; - if (ids != undefined) { - getArguments.push(ids); - } - getArguments.push(viewOptions); - getArguments.push(data); + _months : 'January_February_March_April_May_June_July_August_September_October_November_December'.split('_'), + months : function (m) { + return this._months[m.month()]; + }, - return this._data && this._data.get.apply(this._data, getArguments); - }; + _monthsShort : 'Jan_Feb_Mar_Apr_May_Jun_Jul_Aug_Sep_Oct_Nov_Dec'.split('_'), + monthsShort : function (m) { + return this._monthsShort[m.month()]; + }, - /** - * Get ids of all items or from a filtered set of items. - * @param {Object} [options] An Object with options. Available options: - * {function} [filter] filter items - * {String | function} [order] Order the items by - * a field name or custom sort function. - * @return {Array} ids - */ - DataView.prototype.getIds = function (options) { - var ids; + monthsParse : function (monthName, format, strict) { + var i, mom, regex; - if (this._data) { - var defaultFilter = this._options.filter; - var filter; + if (!this._monthsParse) { + this._monthsParse = []; + this._longMonthsParse = []; + this._shortMonthsParse = []; + } - if (options && options.filter) { - if (defaultFilter) { - filter = function (item) { - return defaultFilter(item) && options.filter(item); - } - } - else { - filter = options.filter; - } - } - else { - filter = defaultFilter; - } + for (i = 0; i < 12; i++) { + // make the regex if we don't have it already + mom = moment.utc([2000, i]); + if (strict && !this._longMonthsParse[i]) { + this._longMonthsParse[i] = new RegExp('^' + this.months(mom, '').replace('.', '') + '$', 'i'); + this._shortMonthsParse[i] = new RegExp('^' + this.monthsShort(mom, '').replace('.', '') + '$', 'i'); + } + if (!strict && !this._monthsParse[i]) { + regex = '^' + this.months(mom, '') + '|^' + this.monthsShort(mom, ''); + this._monthsParse[i] = new RegExp(regex.replace('.', ''), 'i'); + } + // test the regex + if (strict && format === 'MMMM' && this._longMonthsParse[i].test(monthName)) { + return i; + } else if (strict && format === 'MMM' && this._shortMonthsParse[i].test(monthName)) { + return i; + } else if (!strict && this._monthsParse[i].test(monthName)) { + return i; + } + } + }, - ids = this._data.getIds({ - filter: filter, - order: options && options.order - }); - } - else { - ids = []; - } + _weekdays : 'Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday'.split('_'), + weekdays : function (m) { + return this._weekdays[m.day()]; + }, - return ids; - }; + _weekdaysShort : 'Sun_Mon_Tue_Wed_Thu_Fri_Sat'.split('_'), + weekdaysShort : function (m) { + return this._weekdaysShort[m.day()]; + }, - /** - * Get the DataSet to which this DataView is connected. In case there is a chain - * of multiple DataViews, the root DataSet of this chain is returned. - * @return {DataSet} dataSet - */ - DataView.prototype.getDataSet = function () { - var dataSet = this; - while (dataSet instanceof DataView) { - dataSet = dataSet._data; - } - return dataSet || null; - }; + _weekdaysMin : 'Su_Mo_Tu_We_Th_Fr_Sa'.split('_'), + weekdaysMin : function (m) { + return this._weekdaysMin[m.day()]; + }, - /** - * Event listener. Will propagate all events from the connected data set to - * the subscribers of the DataView, but will filter the items and only trigger - * when there are changes in the filtered data set. - * @param {String} event - * @param {Object | null} params - * @param {String} senderId - * @private - */ - DataView.prototype._onEvent = function (event, params, senderId) { - var i, len, id, item, - ids = params && params.items, - data = this._data, - added = [], - updated = [], - removed = []; - - if (ids && data) { - switch (event) { - case 'add': - // filter the ids of the added items - for (i = 0, len = ids.length; i < len; i++) { - id = ids[i]; - item = this.get(id); - if (item) { - this._ids[id] = true; - added.push(id); - } - } - - break; - - case 'update': - // determine the event from the views viewpoint: an updated - // item can be added, updated, or removed from this view. - for (i = 0, len = ids.length; i < len; i++) { - id = ids[i]; - item = this.get(id); + weekdaysParse : function (weekdayName) { + var i, mom, regex; - if (item) { - if (this._ids[id]) { - updated.push(id); - } - else { - this._ids[id] = true; - added.push(id); + if (!this._weekdaysParse) { + this._weekdaysParse = []; } - } - else { - if (this._ids[id]) { - delete this._ids[id]; - removed.push(id); + + for (i = 0; i < 7; i++) { + // make the regex if we don't have it already + if (!this._weekdaysParse[i]) { + mom = moment([2000, 1]).day(i); + regex = '^' + this.weekdays(mom, '') + '|^' + this.weekdaysShort(mom, '') + '|^' + this.weekdaysMin(mom, ''); + this._weekdaysParse[i] = new RegExp(regex.replace('.', ''), 'i'); + } + // test the regex + if (this._weekdaysParse[i].test(weekdayName)) { + return i; + } } - else { - // nothing interesting for me :-( + }, + + _longDateFormat : { + LTS : 'h:mm:ss A', + LT : 'h:mm A', + L : 'MM/DD/YYYY', + LL : 'MMMM D, YYYY', + LLL : 'MMMM D, YYYY LT', + LLLL : 'dddd, MMMM D, YYYY LT' + }, + longDateFormat : function (key) { + var output = this._longDateFormat[key]; + if (!output && this._longDateFormat[key.toUpperCase()]) { + output = this._longDateFormat[key.toUpperCase()].replace(/MMMM|MM|DD|dddd/g, function (val) { + return val.slice(1); + }); + this._longDateFormat[key] = output; } - } - } + return output; + }, - break; + isPM : function (input) { + // IE8 Quirks Mode & IE7 Standards Mode do not allow accessing strings like arrays + // Using charAt should be more compatible. + return ((input + '').toLowerCase().charAt(0) === 'p'); + }, - case 'remove': - // filter the ids of the removed items - for (i = 0, len = ids.length; i < len; i++) { - id = ids[i]; - if (this._ids[id]) { - delete this._ids[id]; - removed.push(id); - } - } + _meridiemParse : /[ap]\.?m?\.?/i, + meridiem : function (hours, minutes, isLower) { + if (hours > 11) { + return isLower ? 'pm' : 'PM'; + } else { + return isLower ? 'am' : 'AM'; + } + }, - break; - } - this.length += added.length - removed.length; + _calendar : { + sameDay : '[Today at] LT', + nextDay : '[Tomorrow at] LT', + nextWeek : 'dddd [at] LT', + lastDay : '[Yesterday at] LT', + lastWeek : '[Last] dddd [at] LT', + sameElse : 'L' + }, + calendar : function (key, mom, now) { + var output = this._calendar[key]; + return typeof output === 'function' ? output.apply(mom, [now]) : output; + }, - if (added.length) { - this._trigger('add', {items: added}, senderId); - } - if (updated.length) { - this._trigger('update', {items: updated}, senderId); - } - if (removed.length) { - this._trigger('remove', {items: removed}, senderId); - } - } - }; + _relativeTime : { + future : 'in %s', + past : '%s ago', + s : 'a few seconds', + m : 'a minute', + mm : '%d minutes', + h : 'an hour', + hh : '%d hours', + d : 'a day', + dd : '%d days', + M : 'a month', + MM : '%d months', + y : 'a year', + yy : '%d years' + }, - // copy subscription functionality from DataSet - DataView.prototype.on = DataSet.prototype.on; - DataView.prototype.off = DataSet.prototype.off; - DataView.prototype._trigger = DataSet.prototype._trigger; + relativeTime : function (number, withoutSuffix, string, isFuture) { + var output = this._relativeTime[string]; + return (typeof output === 'function') ? + output(number, withoutSuffix, string, isFuture) : + output.replace(/%d/i, number); + }, - // TODO: make these functions deprecated (replaced with `on` and `off` since version 0.5) - DataView.prototype.subscribe = DataView.prototype.on; - DataView.prototype.unsubscribe = DataView.prototype.off; + pastFuture : function (diff, output) { + var format = this._relativeTime[diff > 0 ? 'future' : 'past']; + return typeof format === 'function' ? format(output) : format.replace(/%s/i, output); + }, - module.exports = DataView; + ordinal : function (number) { + return this._ordinal.replace('%d', number); + }, + _ordinal : '%d', + _ordinalParse : /\d{1,2}/, -/***/ }, -/* 5 */ -/***/ function(module, exports, __webpack_require__) { + preparse : function (string) { + return string; + }, - /** - * A queue - * @param {Object} options - * Available options: - * - delay: number When provided, the queue will be flushed - * automatically after an inactivity of this delay - * in milliseconds. - * Default value is null. - * - max: number When the queue exceeds the given maximum number - * of entries, the queue is flushed automatically. - * Default value of max is Infinity. - * @constructor - */ - function Queue(options) { - // options - this.delay = null; - this.max = Infinity; + postformat : function (string) { + return string; + }, - // properties - this._queue = []; - this._timeout = null; - this._extended = null; + week : function (mom) { + return weekOfYear(mom, this._week.dow, this._week.doy).week; + }, - this.setOptions(options); - } + _week : { + dow : 0, // Sunday is the first day of the week. + doy : 6 // The week that contains Jan 1st is the first week of the year. + }, - /** - * Update the configuration of the queue - * @param {Object} options - * Available options: - * - delay: number When provided, the queue will be flushed - * automatically after an inactivity of this delay - * in milliseconds. - * Default value is null. - * - max: number When the queue exceeds the given maximum number - * of entries, the queue is flushed automatically. - * Default value of max is Infinity. - * @param options - */ - Queue.prototype.setOptions = function (options) { - if (options && typeof options.delay !== 'undefined') { - this.delay = options.delay; - } - if (options && typeof options.max !== 'undefined') { - this.max = options.max; - } + firstDayOfWeek : function () { + return this._week.dow; + }, - this._flushIfNeeded(); - }; + firstDayOfYear : function () { + return this._week.doy; + }, - /** - * Extend an object with queuing functionality. - * The object will be extended with a function flush, and the methods provided - * in options.replace will be replaced with queued ones. - * @param {Object} object - * @param {Object} options - * Available options: - * - replace: Array. - * A list with method names of the methods - * on the object to be replaced with queued ones. - * - delay: number When provided, the queue will be flushed - * automatically after an inactivity of this delay - * in milliseconds. - * Default value is null. - * - max: number When the queue exceeds the given maximum number - * of entries, the queue is flushed automatically. - * Default value of max is Infinity. - * @return {Queue} Returns the created queue - */ - Queue.extend = function (object, options) { - var queue = new Queue(options); + _invalidDate: 'Invalid date', + invalidDate: function () { + return this._invalidDate; + } + }); - if (object.flush !== undefined) { - throw new Error('Target object already has a property flush'); - } - object.flush = function () { - queue.flush(); - }; + /************************************ + Formatting + ************************************/ - var methods = [{ - name: 'flush', - original: undefined - }]; - if (options && options.replace) { - for (var i = 0; i < options.replace.length; i++) { - var name = options.replace[i]; - methods.push({ - name: name, - original: object[name] - }); - queue.replace(object, name); + function removeFormattingTokens(input) { + if (input.match(/\[[\s\S]/)) { + return input.replace(/^\[|\]$/g, ''); + } + return input.replace(/\\/g, ''); } - } - - queue._extended = { - object: object, - methods: methods - }; - - return queue; - }; - /** - * Destroy the queue. The queue will first flush all queued actions, and in - * case it has extended an object, will restore the original object. - */ - Queue.prototype.destroy = function () { - this.flush(); - - if (this._extended) { - var object = this._extended.object; - var methods = this._extended.methods; - for (var i = 0; i < methods.length; i++) { - var method = methods[i]; - if (method.original) { - object[method.name] = method.original; - } - else { - delete object[method.name]; - } - } - this._extended = null; - } - }; + function makeFormatFunction(format) { + var array = format.match(formattingTokens), i, length; - /** - * Replace a method on an object with a queued version - * @param {Object} object Object having the method - * @param {string} method The method name - */ - Queue.prototype.replace = function(object, method) { - var me = this; - var original = object[method]; - if (!original) { - throw new Error('Method ' + method + ' undefined'); - } + for (i = 0, length = array.length; i < length; i++) { + if (formatTokenFunctions[array[i]]) { + array[i] = formatTokenFunctions[array[i]]; + } else { + array[i] = removeFormattingTokens(array[i]); + } + } - object[method] = function () { - // create an Array with the arguments - var args = []; - for (var i = 0; i < arguments.length; i++) { - args[i] = arguments[i]; + return function (mom) { + var output = ''; + for (i = 0; i < length; i++) { + output += array[i] instanceof Function ? array[i].call(mom, format) : array[i]; + } + return output; + }; } - // add this call to the queue - me.queue({ - args: args, - fn: original, - context: this - }); - }; - }; - - /** - * Queue a call - * @param {function | {fn: function, args: Array} | {fn: function, args: Array, context: Object}} entry - */ - Queue.prototype.queue = function(entry) { - if (typeof entry === 'function') { - this._queue.push({fn: entry}); - } - else { - this._queue.push(entry); - } - - this._flushIfNeeded(); - }; - - /** - * Check whether the queue needs to be flushed - * @private - */ - Queue.prototype._flushIfNeeded = function () { - // flush when the maximum is exceeded. - if (this._queue.length > this.max) { - this.flush(); - } - - // flush after a period of inactivity when a delay is configured - clearTimeout(this._timeout); - if (this.queue.length > 0 && typeof this.delay === 'number') { - var me = this; - this._timeout = setTimeout(function () { - me.flush(); - }, this.delay); - } - }; - - /** - * Flush all queued calls - */ - Queue.prototype.flush = function () { - while (this._queue.length > 0) { - var entry = this._queue.shift(); - entry.fn.apply(entry.context || entry.fn, entry.args || []); - } - }; - - module.exports = Queue; - - -/***/ }, -/* 6 */ -/***/ function(module, exports, __webpack_require__) { + // format date using native date object + function formatMoment(m, format) { + if (!m.isValid()) { + return m.localeData().invalidDate(); + } - var Emitter = __webpack_require__(56); - var DataSet = __webpack_require__(3); - var DataView = __webpack_require__(4); - var util = __webpack_require__(1); - var Point3d = __webpack_require__(10); - var Point2d = __webpack_require__(9); - var Camera = __webpack_require__(7); - var Filter = __webpack_require__(8); - var Slider = __webpack_require__(11); - var StepNumber = __webpack_require__(12); + format = expandFormat(format, m.localeData()); - /** - * @constructor Graph3d - * Graph3d displays data in 3d. - * - * Graph3d is developed in javascript as a Google Visualization Chart. - * - * @param {Element} container The DOM element in which the Graph3d will - * be created. Normally a div element. - * @param {DataSet | DataView | Array} [data] - * @param {Object} [options] - */ - function Graph3d(container, data, options) { - if (!(this instanceof Graph3d)) { - throw new SyntaxError('Constructor must be called with the new operator'); - } + if (!formatFunctions[format]) { + formatFunctions[format] = makeFormatFunction(format); + } - // create variables and set default values - this.containerElement = container; - this.width = '400px'; - this.height = '400px'; - this.margin = 10; // px - this.defaultXCenter = '55%'; - this.defaultYCenter = '50%'; + return formatFunctions[format](m); + } - this.xLabel = 'x'; - this.yLabel = 'y'; - this.zLabel = 'z'; + function expandFormat(format, locale) { + var i = 5; - var passValueFn = function(v) { return v; }; - this.xValueLabel = passValueFn; - this.yValueLabel = passValueFn; - this.zValueLabel = passValueFn; - - this.filterLabel = 'time'; - this.legendLabel = 'value'; + function replaceLongDateFormatTokens(input) { + return locale.longDateFormat(input) || input; + } - this.style = Graph3d.STYLE.DOT; - this.showPerspective = true; - this.showGrid = true; - this.keepAspectRatio = true; - this.showShadow = false; - this.showGrayBottom = false; // TODO: this does not work correctly - this.showTooltip = false; - this.verticalRatio = 0.5; // 0.1 to 1.0, where 1.0 results in a 'cube' + localFormattingTokens.lastIndex = 0; + while (i >= 0 && localFormattingTokens.test(format)) { + format = format.replace(localFormattingTokens, replaceLongDateFormatTokens); + localFormattingTokens.lastIndex = 0; + i -= 1; + } - this.animationInterval = 1000; // milliseconds - this.animationPreload = false; + return format; + } - this.camera = new Camera(); - this.eye = new Point3d(0, 0, -1); // TODO: set eye.z about 3/4 of the width of the window? - this.dataTable = null; // The original data table - this.dataPoints = null; // The table with point objects + /************************************ + Parsing + ************************************/ - // the column indexes - this.colX = undefined; - this.colY = undefined; - this.colZ = undefined; - this.colValue = undefined; - this.colFilter = undefined; - this.xMin = 0; - this.xStep = undefined; // auto by default - this.xMax = 1; - this.yMin = 0; - this.yStep = undefined; // auto by default - this.yMax = 1; - this.zMin = 0; - this.zStep = undefined; // auto by default - this.zMax = 1; - this.valueMin = 0; - this.valueMax = 1; - this.xBarWidth = 1; - this.yBarWidth = 1; - // TODO: customize axis range - - // constants - this.colorAxis = '#4D4D4D'; - this.colorGrid = '#D3D3D3'; - this.colorDot = '#7DC1FF'; - this.colorDotBorder = '#3267D2'; - - // create a frame and canvas - this.create(); + // get the regex to find the next token + function getParseRegexForToken(token, config) { + var a, strict = config._strict; + switch (token) { + case 'Q': + return parseTokenOneDigit; + case 'DDDD': + return parseTokenThreeDigits; + case 'YYYY': + case 'GGGG': + case 'gggg': + return strict ? parseTokenFourDigits : parseTokenOneToFourDigits; + case 'Y': + case 'G': + case 'g': + return parseTokenSignedNumber; + case 'YYYYYY': + case 'YYYYY': + case 'GGGGG': + case 'ggggg': + return strict ? parseTokenSixDigits : parseTokenOneToSixDigits; + case 'S': + if (strict) { + return parseTokenOneDigit; + } + /* falls through */ + case 'SS': + if (strict) { + return parseTokenTwoDigits; + } + /* falls through */ + case 'SSS': + if (strict) { + return parseTokenThreeDigits; + } + /* falls through */ + case 'DDD': + return parseTokenOneToThreeDigits; + case 'MMM': + case 'MMMM': + case 'dd': + case 'ddd': + case 'dddd': + return parseTokenWord; + case 'a': + case 'A': + return config._locale._meridiemParse; + case 'x': + return parseTokenOffsetMs; + case 'X': + return parseTokenTimestampMs; + case 'Z': + case 'ZZ': + return parseTokenTimezone; + case 'T': + return parseTokenT; + case 'SSSS': + return parseTokenDigits; + case 'MM': + case 'DD': + case 'YY': + case 'GG': + case 'gg': + case 'HH': + case 'hh': + case 'mm': + case 'ss': + case 'ww': + case 'WW': + return strict ? parseTokenTwoDigits : parseTokenOneOrTwoDigits; + case 'M': + case 'D': + case 'd': + case 'H': + case 'h': + case 'm': + case 's': + case 'w': + case 'W': + case 'e': + case 'E': + return parseTokenOneOrTwoDigits; + case 'Do': + return strict ? config._locale._ordinalParse : config._locale._ordinalParseLenient; + default : + a = new RegExp(regexpEscape(unescapeFormat(token.replace('\\', '')), 'i')); + return a; + } + } - // apply options (also when undefined) - this.setOptions(options); + function utcOffsetFromString(string) { + string = string || ''; + var possibleTzMatches = (string.match(parseTokenTimezone) || []), + tzChunk = possibleTzMatches[possibleTzMatches.length - 1] || [], + parts = (tzChunk + '').match(parseTimezoneChunker) || ['-', 0, 0], + minutes = +(parts[1] * 60) + toInt(parts[2]); - // apply data - if (data) { - this.setData(data); - } - } + return parts[0] === '+' ? minutes : -minutes; + } - // Extend Graph3d with an Emitter mixin - Emitter(Graph3d.prototype); + // function to convert string input to date + function addTimeToArrayFromToken(token, input, config) { + var a, datePartArray = config._a; - /** - * Calculate the scaling values, dependent on the range in x, y, and z direction - */ - Graph3d.prototype._setScale = function() { - this.scale = new Point3d(1 / (this.xMax - this.xMin), - 1 / (this.yMax - this.yMin), - 1 / (this.zMax - this.zMin)); + switch (token) { + // QUARTER + case 'Q': + if (input != null) { + datePartArray[MONTH] = (toInt(input) - 1) * 3; + } + break; + // MONTH + case 'M' : // fall through to MM + case 'MM' : + if (input != null) { + datePartArray[MONTH] = toInt(input) - 1; + } + break; + case 'MMM' : // fall through to MMMM + case 'MMMM' : + a = config._locale.monthsParse(input, token, config._strict); + // if we didn't find a month name, mark the date as invalid. + if (a != null) { + datePartArray[MONTH] = a; + } else { + config._pf.invalidMonth = input; + } + break; + // DAY OF MONTH + case 'D' : // fall through to DD + case 'DD' : + if (input != null) { + datePartArray[DATE] = toInt(input); + } + break; + case 'Do' : + if (input != null) { + datePartArray[DATE] = toInt(parseInt( + input.match(/\d{1,2}/)[0], 10)); + } + break; + // DAY OF YEAR + case 'DDD' : // fall through to DDDD + case 'DDDD' : + if (input != null) { + config._dayOfYear = toInt(input); + } - // keep aspect ration between x and y scale if desired - if (this.keepAspectRatio) { - if (this.scale.x < this.scale.y) { - //noinspection JSSuspiciousNameCombination - this.scale.y = this.scale.x; - } - else { - //noinspection JSSuspiciousNameCombination - this.scale.x = this.scale.y; + break; + // YEAR + case 'YY' : + datePartArray[YEAR] = moment.parseTwoDigitYear(input); + break; + case 'YYYY' : + case 'YYYYY' : + case 'YYYYYY' : + datePartArray[YEAR] = toInt(input); + break; + // AM / PM + case 'a' : // fall through to A + case 'A' : + config._meridiem = input; + // config._isPm = config._locale.isPM(input); + break; + // HOUR + case 'h' : // fall through to hh + case 'hh' : + config._pf.bigHour = true; + /* falls through */ + case 'H' : // fall through to HH + case 'HH' : + datePartArray[HOUR] = toInt(input); + break; + // MINUTE + case 'm' : // fall through to mm + case 'mm' : + datePartArray[MINUTE] = toInt(input); + break; + // SECOND + case 's' : // fall through to ss + case 'ss' : + datePartArray[SECOND] = toInt(input); + break; + // MILLISECOND + case 'S' : + case 'SS' : + case 'SSS' : + case 'SSSS' : + datePartArray[MILLISECOND] = toInt(('0.' + input) * 1000); + break; + // UNIX OFFSET (MILLISECONDS) + case 'x': + config._d = new Date(toInt(input)); + break; + // UNIX TIMESTAMP WITH MS + case 'X': + config._d = new Date(parseFloat(input) * 1000); + break; + // TIMEZONE + case 'Z' : // fall through to ZZ + case 'ZZ' : + config._useUTC = true; + config._tzm = utcOffsetFromString(input); + break; + // WEEKDAY - human + case 'dd': + case 'ddd': + case 'dddd': + a = config._locale.weekdaysParse(input); + // if we didn't get a weekday name, mark the date as invalid + if (a != null) { + config._w = config._w || {}; + config._w['d'] = a; + } else { + config._pf.invalidWeekday = input; + } + break; + // WEEK, WEEK DAY - numeric + case 'w': + case 'ww': + case 'W': + case 'WW': + case 'd': + case 'e': + case 'E': + token = token.substr(0, 1); + /* falls through */ + case 'gggg': + case 'GGGG': + case 'GGGGG': + token = token.substr(0, 2); + if (input) { + config._w = config._w || {}; + config._w[token] = toInt(input); + } + break; + case 'gg': + case 'GG': + config._w = config._w || {}; + config._w[token] = moment.parseTwoDigitYear(input); + } } - } - // scale the vertical axis - this.scale.z *= this.verticalRatio; - // TODO: can this be automated? verticalRatio? + function dayOfYearFromWeekInfo(config) { + var w, weekYear, week, weekday, dow, doy, temp; - // determine scale for (optional) value - this.scale.value = 1 / (this.valueMax - this.valueMin); + w = config._w; + if (w.GG != null || w.W != null || w.E != null) { + dow = 1; + doy = 4; - // position the camera arm - var xCenter = (this.xMax + this.xMin) / 2 * this.scale.x; - var yCenter = (this.yMax + this.yMin) / 2 * this.scale.y; - var zCenter = (this.zMax + this.zMin) / 2 * this.scale.z; - this.camera.setArmLocation(xCenter, yCenter, zCenter); - }; + // TODO: We need to take the current isoWeekYear, but that depends on + // how we interpret now (local, utc, fixed offset). So create + // a now version of current config (take local/utc/offset flags, and + // create now). + weekYear = dfl(w.GG, config._a[YEAR], weekOfYear(moment(), 1, 4).year); + week = dfl(w.W, 1); + weekday = dfl(w.E, 1); + } else { + dow = config._locale._week.dow; + doy = config._locale._week.doy; + weekYear = dfl(w.gg, config._a[YEAR], weekOfYear(moment(), dow, doy).year); + week = dfl(w.w, 1); - /** - * Convert a 3D location to a 2D location on screen - * http://en.wikipedia.org/wiki/3D_projection - * @param {Point3d} point3d A 3D point with parameters x, y, z - * @return {Point2d} point2d A 2D point with parameters x, y - */ - Graph3d.prototype._convert3Dto2D = function(point3d) { - var translation = this._convertPointToTranslation(point3d); - return this._convertTranslationToScreen(translation); - }; + if (w.d != null) { + // weekday -- low day numbers are considered next week + weekday = w.d; + if (weekday < dow) { + ++week; + } + } else if (w.e != null) { + // local weekday -- counting starts from begining of week + weekday = w.e + dow; + } else { + // default to begining of week + weekday = dow; + } + } + temp = dayOfYearFromWeeks(weekYear, week, weekday, doy, dow); - /** - * Convert a 3D location its translation seen from the camera - * http://en.wikipedia.org/wiki/3D_projection - * @param {Point3d} point3d A 3D point with parameters x, y, z - * @return {Point3d} translation A 3D point with parameters x, y, z This is - * the translation of the point, seen from the - * camera - */ - Graph3d.prototype._convertPointToTranslation = function(point3d) { - var ax = point3d.x * this.scale.x, - ay = point3d.y * this.scale.y, - az = point3d.z * this.scale.z, + config._a[YEAR] = temp.year; + config._dayOfYear = temp.dayOfYear; + } - cx = this.camera.getCameraLocation().x, - cy = this.camera.getCameraLocation().y, - cz = this.camera.getCameraLocation().z, + // convert an array to a date. + // the array should mirror the parameters below + // note: all values past the year are optional and will default to the lowest possible value. + // [year, month, day , hour, minute, second, millisecond] + function dateFromConfig(config) { + var i, date, input = [], currentDate, yearToUse; - // calculate angles - sinTx = Math.sin(this.camera.getCameraRotation().x), - cosTx = Math.cos(this.camera.getCameraRotation().x), - sinTy = Math.sin(this.camera.getCameraRotation().y), - cosTy = Math.cos(this.camera.getCameraRotation().y), - sinTz = Math.sin(this.camera.getCameraRotation().z), - cosTz = Math.cos(this.camera.getCameraRotation().z), + if (config._d) { + return; + } - // calculate translation - dx = cosTy * (sinTz * (ay - cy) + cosTz * (ax - cx)) - sinTy * (az - cz), - dy = sinTx * (cosTy * (az - cz) + sinTy * (sinTz * (ay - cy) + cosTz * (ax - cx))) + cosTx * (cosTz * (ay - cy) - sinTz * (ax-cx)), - dz = cosTx * (cosTy * (az - cz) + sinTy * (sinTz * (ay - cy) + cosTz * (ax - cx))) - sinTx * (cosTz * (ay - cy) - sinTz * (ax-cx)); + currentDate = currentDateArray(config); - return new Point3d(dx, dy, dz); - }; + //compute day of the year from weeks and weekdays + if (config._w && config._a[DATE] == null && config._a[MONTH] == null) { + dayOfYearFromWeekInfo(config); + } - /** - * Convert a translation point to a point on the screen - * @param {Point3d} translation A 3D point with parameters x, y, z This is - * the translation of the point, seen from the - * camera - * @return {Point2d} point2d A 2D point with parameters x, y - */ - Graph3d.prototype._convertTranslationToScreen = function(translation) { - var ex = this.eye.x, - ey = this.eye.y, - ez = this.eye.z, - dx = translation.x, - dy = translation.y, - dz = translation.z; + //if the day of the year is set, figure out what it is + if (config._dayOfYear) { + yearToUse = dfl(config._a[YEAR], currentDate[YEAR]); - // calculate position on screen from translation - var bx; - var by; - if (this.showPerspective) { - bx = (dx - ex) * (ez / dz); - by = (dy - ey) * (ez / dz); - } - else { - bx = dx * -(ez / this.camera.getArmLength()); - by = dy * -(ez / this.camera.getArmLength()); - } + if (config._dayOfYear > daysInYear(yearToUse)) { + config._pf._overflowDayOfYear = true; + } - // shift and scale the point to the center of the screen - // use the width of the graph to scale both horizontally and vertically. - return new Point2d( - this.xcenter + bx * this.frame.canvas.clientWidth, - this.ycenter - by * this.frame.canvas.clientWidth); - }; + date = makeUTCDate(yearToUse, 0, config._dayOfYear); + config._a[MONTH] = date.getUTCMonth(); + config._a[DATE] = date.getUTCDate(); + } - /** - * Set the background styling for the graph - * @param {string | {fill: string, stroke: string, strokeWidth: string}} backgroundColor - */ - Graph3d.prototype._setBackgroundColor = function(backgroundColor) { - var fill = 'white'; - var stroke = 'gray'; - var strokeWidth = 1; + // Default to current date. + // * if no year, month, day of month are given, default to today + // * if day of month is given, default month and year + // * if month is given, default only year + // * if year is given, don't default anything + for (i = 0; i < 3 && config._a[i] == null; ++i) { + config._a[i] = input[i] = currentDate[i]; + } - if (typeof(backgroundColor) === 'string') { - fill = backgroundColor; - stroke = 'none'; - strokeWidth = 0; - } - else if (typeof(backgroundColor) === 'object') { - if (backgroundColor.fill !== undefined) fill = backgroundColor.fill; - if (backgroundColor.stroke !== undefined) stroke = backgroundColor.stroke; - if (backgroundColor.strokeWidth !== undefined) strokeWidth = backgroundColor.strokeWidth; - } - else if (backgroundColor === undefined) { - // use use defaults - } - else { - throw 'Unsupported type of backgroundColor'; - } + // Zero out whatever was not defaulted, including time + for (; i < 7; i++) { + config._a[i] = input[i] = (config._a[i] == null) ? (i === 2 ? 1 : 0) : config._a[i]; + } - this.frame.style.backgroundColor = fill; - this.frame.style.borderColor = stroke; - this.frame.style.borderWidth = strokeWidth + 'px'; - this.frame.style.borderStyle = 'solid'; - }; + // Check for 24:00:00.000 + if (config._a[HOUR] === 24 && + config._a[MINUTE] === 0 && + config._a[SECOND] === 0 && + config._a[MILLISECOND] === 0) { + config._nextDay = true; + config._a[HOUR] = 0; + } + config._d = (config._useUTC ? makeUTCDate : makeDate).apply(null, input); + // Apply timezone offset from input. The actual utcOffset can be changed + // with parseZone. + if (config._tzm != null) { + config._d.setUTCMinutes(config._d.getUTCMinutes() - config._tzm); + } - /// enumerate the available styles - Graph3d.STYLE = { - BAR: 0, - BARCOLOR: 1, - BARSIZE: 2, - DOT : 3, - DOTLINE : 4, - DOTCOLOR: 5, - DOTSIZE: 6, - GRID : 7, - LINE: 8, - SURFACE : 9 - }; + if (config._nextDay) { + config._a[HOUR] = 24; + } + } - /** - * Retrieve the style index from given styleName - * @param {string} styleName Style name such as 'dot', 'grid', 'dot-line' - * @return {Number} styleNumber Enumeration value representing the style, or -1 - * when not found - */ - Graph3d.prototype._getStyleNumber = function(styleName) { - switch (styleName) { - case 'dot': return Graph3d.STYLE.DOT; - case 'dot-line': return Graph3d.STYLE.DOTLINE; - case 'dot-color': return Graph3d.STYLE.DOTCOLOR; - case 'dot-size': return Graph3d.STYLE.DOTSIZE; - case 'line': return Graph3d.STYLE.LINE; - case 'grid': return Graph3d.STYLE.GRID; - case 'surface': return Graph3d.STYLE.SURFACE; - case 'bar': return Graph3d.STYLE.BAR; - case 'bar-color': return Graph3d.STYLE.BARCOLOR; - case 'bar-size': return Graph3d.STYLE.BARSIZE; - } + function dateFromObject(config) { + var normalizedInput; - return -1; - }; + if (config._d) { + return; + } - /** - * Determine the indexes of the data columns, based on the given style and data - * @param {DataSet} data - * @param {Number} style - */ - Graph3d.prototype._determineColumnIndexes = function(data, style) { - if (this.style === Graph3d.STYLE.DOT || - this.style === Graph3d.STYLE.DOTLINE || - this.style === Graph3d.STYLE.LINE || - this.style === Graph3d.STYLE.GRID || - this.style === Graph3d.STYLE.SURFACE || - this.style === Graph3d.STYLE.BAR) { - // 3 columns expected, and optionally a 4th with filter values - this.colX = 0; - this.colY = 1; - this.colZ = 2; - this.colValue = undefined; + normalizedInput = normalizeObjectUnits(config._i); + config._a = [ + normalizedInput.year, + normalizedInput.month, + normalizedInput.day || normalizedInput.date, + normalizedInput.hour, + normalizedInput.minute, + normalizedInput.second, + normalizedInput.millisecond + ]; - if (data.getNumberOfColumns() > 3) { - this.colFilter = 3; + dateFromConfig(config); } - } - else if (this.style === Graph3d.STYLE.DOTCOLOR || - this.style === Graph3d.STYLE.DOTSIZE || - this.style === Graph3d.STYLE.BARCOLOR || - this.style === Graph3d.STYLE.BARSIZE) { - // 4 columns expected, and optionally a 5th with filter values - this.colX = 0; - this.colY = 1; - this.colZ = 2; - this.colValue = 3; - if (data.getNumberOfColumns() > 4) { - this.colFilter = 4; + function currentDateArray(config) { + var now = new Date(); + if (config._useUTC) { + return [ + now.getUTCFullYear(), + now.getUTCMonth(), + now.getUTCDate() + ]; + } else { + return [now.getFullYear(), now.getMonth(), now.getDate()]; + } } - } - else { - throw 'Unknown style "' + this.style + '"'; - } - }; - Graph3d.prototype.getNumberOfRows = function(data) { - return data.length; - } + // date from string and format string + function makeDateFromStringAndFormat(config) { + if (config._f === moment.ISO_8601) { + parseISO(config); + return; + } + config._a = []; + config._pf.empty = true; - Graph3d.prototype.getNumberOfColumns = function(data) { - var counter = 0; - for (var column in data[0]) { - if (data[0].hasOwnProperty(column)) { - counter++; - } - } - return counter; - } + // This array is used to make a Date, either with `new Date` or `Date.UTC` + var string = '' + config._i, + i, parsedInput, tokens, token, skipped, + stringLength = string.length, + totalParsedInputLength = 0; + tokens = expandFormat(config._f, config._locale).match(formattingTokens) || []; - Graph3d.prototype.getDistinctValues = function(data, column) { - var distinctValues = []; - for (var i = 0; i < data.length; i++) { - if (distinctValues.indexOf(data[i][column]) == -1) { - distinctValues.push(data[i][column]); - } - } - return distinctValues; - } + for (i = 0; i < tokens.length; i++) { + token = tokens[i]; + parsedInput = (string.match(getParseRegexForToken(token, config)) || [])[0]; + if (parsedInput) { + skipped = string.substr(0, string.indexOf(parsedInput)); + if (skipped.length > 0) { + config._pf.unusedInput.push(skipped); + } + string = string.slice(string.indexOf(parsedInput) + parsedInput.length); + totalParsedInputLength += parsedInput.length; + } + // don't parse if it's not a known token + if (formatTokenFunctions[token]) { + if (parsedInput) { + config._pf.empty = false; + } + else { + config._pf.unusedTokens.push(token); + } + addTimeToArrayFromToken(token, parsedInput, config); + } + else if (config._strict && !parsedInput) { + config._pf.unusedTokens.push(token); + } + } + // add remaining unparsed input length to the string + config._pf.charsLeftOver = stringLength - totalParsedInputLength; + if (string.length > 0) { + config._pf.unusedInput.push(string); + } - Graph3d.prototype.getColumnRange = function(data,column) { - var minMax = {min:data[0][column],max:data[0][column]}; - for (var i = 0; i < data.length; i++) { - if (minMax.min > data[i][column]) { minMax.min = data[i][column]; } - if (minMax.max < data[i][column]) { minMax.max = data[i][column]; } - } - return minMax; - }; + // clear _12h flag if hour is <= 12 + if (config._pf.bigHour === true && config._a[HOUR] <= 12) { + config._pf.bigHour = undefined; + } + // handle meridiem + config._a[HOUR] = meridiemFixWrap(config._locale, config._a[HOUR], + config._meridiem); + dateFromConfig(config); + checkOverflow(config); + } - /** - * Initialize the data from the data table. Calculate minimum and maximum values - * and column index values - * @param {Array | DataSet | DataView} rawData The data containing the items for the Graph. - * @param {Number} style Style Number - */ - Graph3d.prototype._dataInitialize = function (rawData, style) { - var me = this; + function unescapeFormat(s) { + return s.replace(/\\(\[)|\\(\])|\[([^\]\[]*)\]|\\(.)/g, function (matched, p1, p2, p3, p4) { + return p1 || p2 || p3 || p4; + }); + } - // unsubscribe from the dataTable - if (this.dataSet) { - this.dataSet.off('*', this._onChange); - } + // Code from http://stackoverflow.com/questions/3561493/is-there-a-regexp-escape-function-in-javascript + function regexpEscape(s) { + return s.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&'); + } - if (rawData === undefined) - return; + // date from string and array of format strings + function makeDateFromStringAndArray(config) { + var tempConfig, + bestMoment, - if (Array.isArray(rawData)) { - rawData = new DataSet(rawData); - } + scoreToBeat, + i, + currentScore; - var data; - if (rawData instanceof DataSet || rawData instanceof DataView) { - data = rawData.get(); - } - else { - throw new Error('Array, DataSet, or DataView expected'); - } + if (config._f.length === 0) { + config._pf.invalidFormat = true; + config._d = new Date(NaN); + return; + } - if (data.length == 0) - return; + for (i = 0; i < config._f.length; i++) { + currentScore = 0; + tempConfig = copyConfig({}, config); + if (config._useUTC != null) { + tempConfig._useUTC = config._useUTC; + } + tempConfig._pf = defaultParsingFlags(); + tempConfig._f = config._f[i]; + makeDateFromStringAndFormat(tempConfig); - this.dataSet = rawData; - this.dataTable = data; + if (!isValid(tempConfig)) { + continue; + } - // subscribe to changes in the dataset - this._onChange = function () { - me.setData(me.dataSet); - }; - this.dataSet.on('*', this._onChange); + // if there is any input that was not parsed add a penalty for that format + currentScore += tempConfig._pf.charsLeftOver; - // _determineColumnIndexes - // getNumberOfRows (points) - // getNumberOfColumns (x,y,z,v,t,t1,t2...) - // getDistinctValues (unique values?) - // getColumnRange + //or tokens + currentScore += tempConfig._pf.unusedTokens.length * 10; - // determine the location of x,y,z,value,filter columns - this.colX = 'x'; - this.colY = 'y'; - this.colZ = 'z'; - this.colValue = 'style'; - this.colFilter = 'filter'; + tempConfig._pf.score = currentScore; + + if (scoreToBeat == null || currentScore < scoreToBeat) { + scoreToBeat = currentScore; + bestMoment = tempConfig; + } + } + extend(config, bestMoment || tempConfig); + } + // date from iso format + function parseISO(config) { + var i, l, + string = config._i, + match = isoRegex.exec(string); - // check if a filter column is provided - if (data[0].hasOwnProperty('filter')) { - if (this.dataFilter === undefined) { - this.dataFilter = new Filter(rawData, this.colFilter, this); - this.dataFilter.setOnLoadCallback(function() {me.redraw();}); + if (match) { + config._pf.iso = true; + for (i = 0, l = isoDates.length; i < l; i++) { + if (isoDates[i][1].exec(string)) { + // match[5] should be 'T' or undefined + config._f = isoDates[i][0] + (match[6] || ' '); + break; + } + } + for (i = 0, l = isoTimes.length; i < l; i++) { + if (isoTimes[i][1].exec(string)) { + config._f += isoTimes[i][0]; + break; + } + } + if (string.match(parseTokenTimezone)) { + config._f += 'Z'; + } + makeDateFromStringAndFormat(config); + } else { + config._isValid = false; + } } - } + // date from iso format or fallback + function makeDateFromString(config) { + parseISO(config); + if (config._isValid === false) { + delete config._isValid; + moment.createFromInputFallback(config); + } + } - var withBars = this.style == Graph3d.STYLE.BAR || - this.style == Graph3d.STYLE.BARCOLOR || - this.style == Graph3d.STYLE.BARSIZE; + function map(arr, fn) { + var res = [], i; + for (i = 0; i < arr.length; ++i) { + res.push(fn(arr[i], i)); + } + return res; + } - // determine barWidth from data - if (withBars) { - if (this.defaultXBarWidth !== undefined) { - this.xBarWidth = this.defaultXBarWidth; + function makeDateFromInput(config) { + var input = config._i, matched; + if (input === undefined) { + config._d = new Date(); + } else if (isDate(input)) { + config._d = new Date(+input); + } else if ((matched = aspNetJsonRegex.exec(input)) !== null) { + config._d = new Date(+matched[1]); + } else if (typeof input === 'string') { + makeDateFromString(config); + } else if (isArray(input)) { + config._a = map(input.slice(0), function (obj) { + return parseInt(obj, 10); + }); + dateFromConfig(config); + } else if (typeof(input) === 'object') { + dateFromObject(config); + } else if (typeof(input) === 'number') { + // from milliseconds + config._d = new Date(input); + } else { + moment.createFromInputFallback(config); + } } - else { - var dataX = this.getDistinctValues(data,this.colX); - this.xBarWidth = (dataX[1] - dataX[0]) || 1; + + function makeDate(y, m, d, h, M, s, ms) { + //can't just apply() to create a date: + //http://stackoverflow.com/questions/181348/instantiating-a-javascript-object-by-calling-prototype-constructor-apply + var date = new Date(y, m, d, h, M, s, ms); + + //the date constructor doesn't accept years < 1970 + if (y < 1970) { + date.setFullYear(y); + } + return date; } - if (this.defaultYBarWidth !== undefined) { - this.yBarWidth = this.defaultYBarWidth; + function makeUTCDate(y) { + var date = new Date(Date.UTC.apply(null, arguments)); + if (y < 1970) { + date.setUTCFullYear(y); + } + return date; } - else { - var dataY = this.getDistinctValues(data,this.colY); - this.yBarWidth = (dataY[1] - dataY[0]) || 1; + + function parseWeekday(input, locale) { + if (typeof input === 'string') { + if (!isNaN(input)) { + input = parseInt(input, 10); + } + else { + input = locale.weekdaysParse(input); + if (typeof input !== 'number') { + return null; + } + } + } + return input; } - } - // calculate minimums and maximums - var xRange = this.getColumnRange(data,this.colX); - if (withBars) { - xRange.min -= this.xBarWidth / 2; - xRange.max += this.xBarWidth / 2; - } - this.xMin = (this.defaultXMin !== undefined) ? this.defaultXMin : xRange.min; - this.xMax = (this.defaultXMax !== undefined) ? this.defaultXMax : xRange.max; - if (this.xMax <= this.xMin) this.xMax = this.xMin + 1; - this.xStep = (this.defaultXStep !== undefined) ? this.defaultXStep : (this.xMax-this.xMin)/5; + /************************************ + Relative Time + ************************************/ - var yRange = this.getColumnRange(data,this.colY); - if (withBars) { - yRange.min -= this.yBarWidth / 2; - yRange.max += this.yBarWidth / 2; - } - this.yMin = (this.defaultYMin !== undefined) ? this.defaultYMin : yRange.min; - this.yMax = (this.defaultYMax !== undefined) ? this.defaultYMax : yRange.max; - if (this.yMax <= this.yMin) this.yMax = this.yMin + 1; - this.yStep = (this.defaultYStep !== undefined) ? this.defaultYStep : (this.yMax-this.yMin)/5; - var zRange = this.getColumnRange(data,this.colZ); - this.zMin = (this.defaultZMin !== undefined) ? this.defaultZMin : zRange.min; - this.zMax = (this.defaultZMax !== undefined) ? this.defaultZMax : zRange.max; - if (this.zMax <= this.zMin) this.zMax = this.zMin + 1; - this.zStep = (this.defaultZStep !== undefined) ? this.defaultZStep : (this.zMax-this.zMin)/5; + // helper function for moment.fn.from, moment.fn.fromNow, and moment.duration.fn.humanize + function substituteTimeAgo(string, number, withoutSuffix, isFuture, locale) { + return locale.relativeTime(number || 1, !!withoutSuffix, string, isFuture); + } - if (this.colValue !== undefined) { - var valueRange = this.getColumnRange(data,this.colValue); - this.valueMin = (this.defaultValueMin !== undefined) ? this.defaultValueMin : valueRange.min; - this.valueMax = (this.defaultValueMax !== undefined) ? this.defaultValueMax : valueRange.max; - if (this.valueMax <= this.valueMin) this.valueMax = this.valueMin + 1; - } + function relativeTime(posNegDuration, withoutSuffix, locale) { + var duration = moment.duration(posNegDuration).abs(), + seconds = round(duration.as('s')), + minutes = round(duration.as('m')), + hours = round(duration.as('h')), + days = round(duration.as('d')), + months = round(duration.as('M')), + years = round(duration.as('y')), - // set the scale dependent on the ranges. - this._setScale(); - }; + args = seconds < relativeTimeThresholds.s && ['s', seconds] || + minutes === 1 && ['m'] || + minutes < relativeTimeThresholds.m && ['mm', minutes] || + hours === 1 && ['h'] || + hours < relativeTimeThresholds.h && ['hh', hours] || + days === 1 && ['d'] || + days < relativeTimeThresholds.d && ['dd', days] || + months === 1 && ['M'] || + months < relativeTimeThresholds.M && ['MM', months] || + years === 1 && ['y'] || ['yy', years]; + args[2] = withoutSuffix; + args[3] = +posNegDuration > 0; + args[4] = locale; + return substituteTimeAgo.apply({}, args); + } - /** - * Filter the data based on the current filter - * @param {Array} data - * @return {Array} dataPoints Array with point objects which can be drawn on screen - */ - Graph3d.prototype._getDataPoints = function (data) { - // TODO: store the created matrix dataPoints in the filters instead of reloading each time - var x, y, i, z, obj, point; + /************************************ + Week of Year + ************************************/ - var dataPoints = []; - if (this.style === Graph3d.STYLE.GRID || - this.style === Graph3d.STYLE.SURFACE) { - // copy all values from the google data table to a matrix - // the provided values are supposed to form a grid of (x,y) positions + // firstDayOfWeek 0 = sun, 6 = sat + // the day of the week that starts the week + // (usually sunday or monday) + // firstDayOfWeekOfYear 0 = sun, 6 = sat + // the first week is the week that contains the first + // of this day of the week + // (eg. ISO weeks use thursday (4)) + function weekOfYear(mom, firstDayOfWeek, firstDayOfWeekOfYear) { + var end = firstDayOfWeekOfYear - firstDayOfWeek, + daysToDayOfWeek = firstDayOfWeekOfYear - mom.day(), + adjustedMoment; - // create two lists with all present x and y values - var dataX = []; - var dataY = []; - for (i = 0; i < this.getNumberOfRows(data); i++) { - x = data[i][this.colX] || 0; - y = data[i][this.colY] || 0; - if (dataX.indexOf(x) === -1) { - dataX.push(x); - } - if (dataY.indexOf(y) === -1) { - dataY.push(y); - } - } + if (daysToDayOfWeek > end) { + daysToDayOfWeek -= 7; + } - var sortNumber = function (a, b) { - return a - b; - }; - dataX.sort(sortNumber); - dataY.sort(sortNumber); + if (daysToDayOfWeek < end - 7) { + daysToDayOfWeek += 7; + } - // create a grid, a 2d matrix, with all values. - var dataMatrix = []; // temporary data matrix - for (i = 0; i < data.length; i++) { - x = data[i][this.colX] || 0; - y = data[i][this.colY] || 0; - z = data[i][this.colZ] || 0; + adjustedMoment = moment(mom).add(daysToDayOfWeek, 'd'); + return { + week: Math.ceil(adjustedMoment.dayOfYear() / 7), + year: adjustedMoment.year() + }; + } - var xIndex = dataX.indexOf(x); // TODO: implement Array().indexOf() for Internet Explorer - var yIndex = dataY.indexOf(y); + //http://en.wikipedia.org/wiki/ISO_week_date#Calculating_a_date_given_the_year.2C_week_number_and_weekday + function dayOfYearFromWeeks(year, week, weekday, firstDayOfWeekOfYear, firstDayOfWeek) { + var d = makeUTCDate(year, 0, 1).getUTCDay(), daysToAdd, dayOfYear; - if (dataMatrix[xIndex] === undefined) { - dataMatrix[xIndex] = []; - } + d = d === 0 ? 7 : d; + weekday = weekday != null ? weekday : firstDayOfWeek; + daysToAdd = firstDayOfWeek - d + (d > firstDayOfWeekOfYear ? 7 : 0) - (d < firstDayOfWeek ? 7 : 0); + dayOfYear = 7 * (week - 1) + (weekday - firstDayOfWeek) + daysToAdd + 1; - var point3d = new Point3d(); - point3d.x = x; - point3d.y = y; - point3d.z = z; + return { + year: dayOfYear > 0 ? year : year - 1, + dayOfYear: dayOfYear > 0 ? dayOfYear : daysInYear(year - 1) + dayOfYear + }; + } - obj = {}; - obj.point = point3d; - obj.trans = undefined; - obj.screen = undefined; - obj.bottom = new Point3d(x, y, this.zMin); + /************************************ + Top Level Functions + ************************************/ - dataMatrix[xIndex][yIndex] = obj; + function makeMoment(config) { + var input = config._i, + format = config._f, + res; - dataPoints.push(obj); - } + config._locale = config._locale || moment.localeData(config._l); - // fill in the pointers to the neighbors. - for (x = 0; x < dataMatrix.length; x++) { - for (y = 0; y < dataMatrix[x].length; y++) { - if (dataMatrix[x][y]) { - dataMatrix[x][y].pointRight = (x < dataMatrix.length-1) ? dataMatrix[x+1][y] : undefined; - dataMatrix[x][y].pointTop = (y < dataMatrix[x].length-1) ? dataMatrix[x][y+1] : undefined; - dataMatrix[x][y].pointCross = - (x < dataMatrix.length-1 && y < dataMatrix[x].length-1) ? - dataMatrix[x+1][y+1] : - undefined; + if (input === null || (format === undefined && input === '')) { + return moment.invalid({nullInput: true}); } - } - } - } - else { // 'dot', 'dot-line', etc. - // copy all values from the google data table to a list with Point3d objects - for (i = 0; i < data.length; i++) { - point = new Point3d(); - point.x = data[i][this.colX] || 0; - point.y = data[i][this.colY] || 0; - point.z = data[i][this.colZ] || 0; - if (this.colValue !== undefined) { - point.value = data[i][this.colValue] || 0; - } + if (typeof input === 'string') { + config._i = input = config._locale.preparse(input); + } - obj = {}; - obj.point = point; - obj.bottom = new Point3d(point.x, point.y, this.zMin); - obj.trans = undefined; - obj.screen = undefined; + if (moment.isMoment(input)) { + return new Moment(input, true); + } else if (format) { + if (isArray(format)) { + makeDateFromStringAndArray(config); + } else { + makeDateFromStringAndFormat(config); + } + } else { + makeDateFromInput(config); + } - dataPoints.push(obj); + res = new Moment(config); + if (res._nextDay) { + // Adding is smart enough around DST + res.add(1, 'd'); + res._nextDay = undefined; + } + + return res; } - } - return dataPoints; - }; + moment = function (input, format, locale, strict) { + var c; - /** - * Create the main frame for the Graph3d. - * This function is executed once when a Graph3d object is created. The frame - * contains a canvas, and this canvas contains all objects like the axis and - * nodes. - */ - Graph3d.prototype.create = function () { - // remove all elements from the container element. - while (this.containerElement.hasChildNodes()) { - this.containerElement.removeChild(this.containerElement.firstChild); - } + if (typeof(locale) === 'boolean') { + strict = locale; + locale = undefined; + } + // object construction must be done this way. + // https://github.com/moment/moment/issues/1423 + c = {}; + c._isAMomentObject = true; + c._i = input; + c._f = format; + c._l = locale; + c._strict = strict; + c._isUTC = false; + c._pf = defaultParsingFlags(); - this.frame = document.createElement('div'); - this.frame.style.position = 'relative'; - this.frame.style.overflow = 'hidden'; + return makeMoment(c); + }; - // create the graph canvas (HTML canvas element) - this.frame.canvas = document.createElement( 'canvas' ); - this.frame.canvas.style.position = 'relative'; - this.frame.appendChild(this.frame.canvas); - //if (!this.frame.canvas.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.frame.canvas.appendChild(noCanvas); - } + moment.suppressDeprecationWarnings = false; - this.frame.filter = document.createElement( 'div' ); - this.frame.filter.style.position = 'absolute'; - this.frame.filter.style.bottom = '0px'; - this.frame.filter.style.left = '0px'; - this.frame.filter.style.width = '100%'; - this.frame.appendChild(this.frame.filter); + moment.createFromInputFallback = deprecate( + 'moment construction falls back to js Date. This is ' + + 'discouraged and will be removed in upcoming major ' + + 'release. Please refer to ' + + 'https://github.com/moment/moment/issues/1407 for more info.', + function (config) { + config._d = new Date(config._i + (config._useUTC ? ' UTC' : '')); + } + ); - // add event listeners to handle moving and zooming the contents - var me = this; - var onmousedown = function (event) {me._onMouseDown(event);}; - var ontouchstart = function (event) {me._onTouchStart(event);}; - var onmousewheel = function (event) {me._onWheel(event);}; - var ontooltip = function (event) {me._onTooltip(event);}; - // TODO: these events are never cleaned up... can give a 'memory leakage' + // Pick a moment m from moments so that m[fn](other) is true for all + // other. This relies on the function fn to be transitive. + // + // moments should either be an array of moment objects or an array, whose + // first element is an array of moment objects. + function pickBy(fn, moments) { + var res, i; + if (moments.length === 1 && isArray(moments[0])) { + moments = moments[0]; + } + if (!moments.length) { + return moment(); + } + res = moments[0]; + for (i = 1; i < moments.length; ++i) { + if (moments[i][fn](res)) { + res = moments[i]; + } + } + return res; + } - util.addEventListener(this.frame.canvas, 'keydown', onkeydown); - util.addEventListener(this.frame.canvas, 'mousedown', onmousedown); - util.addEventListener(this.frame.canvas, 'touchstart', ontouchstart); - util.addEventListener(this.frame.canvas, 'mousewheel', onmousewheel); - util.addEventListener(this.frame.canvas, 'mousemove', ontooltip); + moment.min = function () { + var args = [].slice.call(arguments, 0); - // add the new graph to the container element - this.containerElement.appendChild(this.frame); - }; + return pickBy('isBefore', args); + }; + moment.max = function () { + var args = [].slice.call(arguments, 0); - /** - * Set a new size for the graph - * @param {string} width Width in pixels or percentage (for example '800px' - * or '50%') - * @param {string} height Height in pixels or percentage (for example '400px' - * or '30%') - */ - Graph3d.prototype.setSize = function(width, height) { - this.frame.style.width = width; - this.frame.style.height = height; + return pickBy('isAfter', args); + }; - this._resizeCanvas(); - }; + // creating with utc + moment.utc = function (input, format, locale, strict) { + var c; - /** - * Resize the canvas to the current size of the frame - */ - Graph3d.prototype._resizeCanvas = function() { - this.frame.canvas.style.width = '100%'; - this.frame.canvas.style.height = '100%'; + if (typeof(locale) === 'boolean') { + strict = locale; + locale = undefined; + } + // object construction must be done this way. + // https://github.com/moment/moment/issues/1423 + c = {}; + c._isAMomentObject = true; + c._useUTC = true; + c._isUTC = true; + c._l = locale; + c._i = input; + c._f = format; + c._strict = strict; + c._pf = defaultParsingFlags(); - this.frame.canvas.width = this.frame.canvas.clientWidth; - this.frame.canvas.height = this.frame.canvas.clientHeight; + return makeMoment(c).utc(); + }; - // adjust with for margin - this.frame.filter.style.width = (this.frame.canvas.clientWidth - 2 * 10) + 'px'; - }; + // creating with unix timestamp (in seconds) + moment.unix = function (input) { + return moment(input * 1000); + }; - /** - * Start animation - */ - Graph3d.prototype.animationStart = function() { - if (!this.frame.filter || !this.frame.filter.slider) - throw 'No animation available'; + // duration + moment.duration = function (input, key) { + var duration = input, + // matching against regexp is expensive, do it on demand + match = null, + sign, + ret, + parseIso, + diffRes; - this.frame.filter.slider.play(); - }; + if (moment.isDuration(input)) { + duration = { + ms: input._milliseconds, + d: input._days, + M: input._months + }; + } else if (typeof input === 'number') { + duration = {}; + if (key) { + duration[key] = input; + } else { + duration.milliseconds = input; + } + } else if (!!(match = aspNetTimeSpanJsonRegex.exec(input))) { + sign = (match[1] === '-') ? -1 : 1; + duration = { + y: 0, + d: toInt(match[DATE]) * sign, + h: toInt(match[HOUR]) * sign, + m: toInt(match[MINUTE]) * sign, + s: toInt(match[SECOND]) * sign, + ms: toInt(match[MILLISECOND]) * sign + }; + } else if (!!(match = isoDurationRegex.exec(input))) { + sign = (match[1] === '-') ? -1 : 1; + parseIso = function (inp) { + // We'd normally use ~~inp for this, but unfortunately it also + // converts floats to ints. + // inp may be undefined, so careful calling replace on it. + var res = inp && parseFloat(inp.replace(',', '.')); + // apply sign while we're at it + return (isNaN(res) ? 0 : res) * sign; + }; + duration = { + y: parseIso(match[2]), + M: parseIso(match[3]), + d: parseIso(match[4]), + h: parseIso(match[5]), + m: parseIso(match[6]), + s: parseIso(match[7]), + w: parseIso(match[8]) + }; + } else if (duration == null) {// checks for null or undefined + duration = {}; + } else if (typeof duration === 'object' && + ('from' in duration || 'to' in duration)) { + diffRes = momentsDifference(moment(duration.from), moment(duration.to)); + duration = {}; + duration.ms = diffRes.milliseconds; + duration.M = diffRes.months; + } - /** - * Stop animation - */ - Graph3d.prototype.animationStop = function() { - if (!this.frame.filter || !this.frame.filter.slider) return; + ret = new Duration(duration); - this.frame.filter.slider.stop(); - }; + if (moment.isDuration(input) && hasOwnProp(input, '_locale')) { + ret._locale = input._locale; + } + return ret; + }; - /** - * Resize the center position based on the current values in this.defaultXCenter - * and this.defaultYCenter (which are strings with a percentage or a value - * in pixels). The center positions are the variables this.xCenter - * and this.yCenter - */ - Graph3d.prototype._resizeCenter = function() { - // calculate the horizontal center position - if (this.defaultXCenter.charAt(this.defaultXCenter.length-1) === '%') { - this.xcenter = - parseFloat(this.defaultXCenter) / 100 * - this.frame.canvas.clientWidth; - } - else { - this.xcenter = parseFloat(this.defaultXCenter); // supposed to be in px - } + // version number + moment.version = VERSION; - // calculate the vertical center position - if (this.defaultYCenter.charAt(this.defaultYCenter.length-1) === '%') { - this.ycenter = - parseFloat(this.defaultYCenter) / 100 * - (this.frame.canvas.clientHeight - this.frame.filter.clientHeight); - } - else { - this.ycenter = parseFloat(this.defaultYCenter); // supposed to be in px - } - }; + // default format + moment.defaultFormat = isoFormat; - /** - * Set the rotation and distance of the camera - * @param {Object} pos An object with the camera position. The object - * contains three parameters: - * - horizontal {Number} - * The horizontal rotation, between 0 and 2*PI. - * Optional, can be left undefined. - * - vertical {Number} - * The vertical rotation, between 0 and 0.5*PI - * if vertical=0.5*PI, the graph is shown from the - * top. Optional, can be left undefined. - * - distance {Number} - * The (normalized) distance of the camera to the - * center of the graph, a value between 0.71 and 5.0. - * Optional, can be left undefined. - */ - Graph3d.prototype.setCameraPosition = function(pos) { - if (pos === undefined) { - return; - } + // constant that refers to the ISO standard + moment.ISO_8601 = function () {}; - if (pos.horizontal !== undefined && pos.vertical !== undefined) { - this.camera.setArmRotation(pos.horizontal, pos.vertical); - } + // Plugins that add properties should also add the key here (null value), + // so we can properly clone ourselves. + moment.momentProperties = momentProperties; - if (pos.distance !== undefined) { - this.camera.setArmLength(pos.distance); - } + // This function will be called whenever a moment is mutated. + // It is intended to keep the offset in sync with the timezone. + moment.updateOffset = function () {}; - this.redraw(); - }; + // This function allows you to set a threshold for relative time strings + moment.relativeTimeThreshold = function (threshold, limit) { + if (relativeTimeThresholds[threshold] === undefined) { + return false; + } + if (limit === undefined) { + return relativeTimeThresholds[threshold]; + } + relativeTimeThresholds[threshold] = limit; + return true; + }; + moment.lang = deprecate( + 'moment.lang is deprecated. Use moment.locale instead.', + function (key, value) { + return moment.locale(key, value); + } + ); - /** - * Retrieve the current camera rotation - * @return {object} An object with parameters horizontal, vertical, and - * distance - */ - Graph3d.prototype.getCameraPosition = function() { - var pos = this.camera.getArmRotation(); - pos.distance = this.camera.getArmLength(); - return pos; - }; + // This function will load locale and then set the global locale. If + // no arguments are passed in, it will simply return the current global + // locale key. + moment.locale = function (key, values) { + var data; + if (key) { + if (typeof(values) !== 'undefined') { + data = moment.defineLocale(key, values); + } + else { + data = moment.localeData(key); + } - /** - * Load data into the 3D Graph - */ - Graph3d.prototype._readData = function(data) { - // read the data - this._dataInitialize(data, this.style); + if (data) { + moment.duration._locale = moment._locale = data; + } + } + return moment._locale._abbr; + }; - if (this.dataFilter) { - // apply filtering - this.dataPoints = this.dataFilter._getDataPoints(); - } - else { - // no filtering. load all data - this.dataPoints = this._getDataPoints(this.dataTable); - } + moment.defineLocale = function (name, values) { + if (values !== null) { + values.abbr = name; + if (!locales[name]) { + locales[name] = new Locale(); + } + locales[name].set(values); - // draw the filter - this._redrawFilter(); - }; + // backwards compat for now: also set the locale + moment.locale(name); - /** - * Replace the dataset of the Graph3d - * @param {Array | DataSet | DataView} data - */ - Graph3d.prototype.setData = function (data) { - this._readData(data); - this.redraw(); + return locales[name]; + } else { + // useful for testing + delete locales[name]; + return null; + } + }; - // start animation when option is true - if (this.animationAutoStart && this.dataFilter) { - this.animationStart(); - } - }; + moment.langData = deprecate( + 'moment.langData is deprecated. Use moment.localeData instead.', + function (key) { + return moment.localeData(key); + } + ); - /** - * Update the options. Options will be merged with current options - * @param {Object} options - */ - Graph3d.prototype.setOptions = function (options) { - var cameraPosition = undefined; + // returns locale data + moment.localeData = function (key) { + var locale; - this.animationStop(); + if (key && key._locale && key._locale._abbr) { + key = key._locale._abbr; + } - if (options !== undefined) { - // retrieve parameter values - if (options.width !== undefined) this.width = options.width; - if (options.height !== undefined) this.height = options.height; + if (!key) { + return moment._locale; + } - if (options.xCenter !== undefined) this.defaultXCenter = options.xCenter; - if (options.yCenter !== undefined) this.defaultYCenter = options.yCenter; + if (!isArray(key)) { + //short-circuit everything else + locale = loadLocale(key); + if (locale) { + return locale; + } + key = [key]; + } - if (options.filterLabel !== undefined) this.filterLabel = options.filterLabel; - if (options.legendLabel !== undefined) this.legendLabel = options.legendLabel; - if (options.xLabel !== undefined) this.xLabel = options.xLabel; - if (options.yLabel !== undefined) this.yLabel = options.yLabel; - if (options.zLabel !== undefined) this.zLabel = options.zLabel; + return chooseLocale(key); + }; - if (options.xValueLabel !== undefined) this.xValueLabel = options.xValueLabel; - if (options.yValueLabel !== undefined) this.yValueLabel = options.yValueLabel; - if (options.zValueLabel !== undefined) this.zValueLabel = options.zValueLabel; + // compare moment object + moment.isMoment = function (obj) { + return obj instanceof Moment || + (obj != null && hasOwnProp(obj, '_isAMomentObject')); + }; - if (options.style !== undefined) { - var styleNumber = this._getStyleNumber(options.style); - if (styleNumber !== -1) { - this.style = styleNumber; - } + // for typechecking Duration objects + moment.isDuration = function (obj) { + return obj instanceof Duration; + }; + + for (i = lists.length - 1; i >= 0; --i) { + makeList(lists[i]); } - if (options.showGrid !== undefined) this.showGrid = options.showGrid; - if (options.showPerspective !== undefined) this.showPerspective = options.showPerspective; - if (options.showShadow !== undefined) this.showShadow = options.showShadow; - if (options.tooltip !== undefined) this.showTooltip = options.tooltip; - if (options.showAnimationControls !== undefined) this.showAnimationControls = options.showAnimationControls; - if (options.keepAspectRatio !== undefined) this.keepAspectRatio = options.keepAspectRatio; - if (options.verticalRatio !== undefined) this.verticalRatio = options.verticalRatio; - if (options.animationInterval !== undefined) this.animationInterval = options.animationInterval; - if (options.animationPreload !== undefined) this.animationPreload = options.animationPreload; - if (options.animationAutoStart !== undefined)this.animationAutoStart = options.animationAutoStart; + moment.normalizeUnits = function (units) { + return normalizeUnits(units); + }; - if (options.xBarWidth !== undefined) this.defaultXBarWidth = options.xBarWidth; - if (options.yBarWidth !== undefined) this.defaultYBarWidth = options.yBarWidth; + moment.invalid = function (flags) { + var m = moment.utc(NaN); + if (flags != null) { + extend(m._pf, flags); + } + else { + m._pf.userInvalidated = true; + } - if (options.xMin !== undefined) this.defaultXMin = options.xMin; - if (options.xStep !== undefined) this.defaultXStep = options.xStep; - if (options.xMax !== undefined) this.defaultXMax = options.xMax; - if (options.yMin !== undefined) this.defaultYMin = options.yMin; - if (options.yStep !== undefined) this.defaultYStep = options.yStep; - if (options.yMax !== undefined) this.defaultYMax = options.yMax; - if (options.zMin !== undefined) this.defaultZMin = options.zMin; - if (options.zStep !== undefined) this.defaultZStep = options.zStep; - if (options.zMax !== undefined) this.defaultZMax = options.zMax; - if (options.valueMin !== undefined) this.defaultValueMin = options.valueMin; - if (options.valueMax !== undefined) this.defaultValueMax = options.valueMax; + return m; + }; - if (options.cameraPosition !== undefined) cameraPosition = options.cameraPosition; + moment.parseZone = function () { + return moment.apply(null, arguments).parseZone(); + }; - if (cameraPosition !== undefined) { - this.camera.setArmRotation(cameraPosition.horizontal, cameraPosition.vertical); - this.camera.setArmLength(cameraPosition.distance); - } - else { - this.camera.setArmRotation(1.0, 0.5); - this.camera.setArmLength(1.7); - } - } + moment.parseTwoDigitYear = function (input) { + return toInt(input) + (toInt(input) > 68 ? 1900 : 2000); + }; - this._setBackgroundColor(options && options.backgroundColor); + moment.isDate = isDate; - this.setSize(this.width, this.height); + /************************************ + Moment Prototype + ************************************/ - // re-load the data - if (this.dataTable) { - this.setData(this.dataTable); - } - // start animation when option is true - if (this.animationAutoStart && this.dataFilter) { - this.animationStart(); - } - }; + extend(moment.fn = Moment.prototype, { - /** - * Redraw the Graph. - */ - Graph3d.prototype.redraw = function() { - if (this.dataPoints === undefined) { - throw 'Error: graph data not initialized'; - } + clone : function () { + return moment(this); + }, - this._resizeCanvas(); - this._resizeCenter(); - this._redrawSlider(); - this._redrawClear(); - this._redrawAxis(); + valueOf : function () { + return +this._d - ((this._offset || 0) * 60000); + }, - if (this.style === Graph3d.STYLE.GRID || - this.style === Graph3d.STYLE.SURFACE) { - this._redrawDataGrid(); - } - else if (this.style === Graph3d.STYLE.LINE) { - this._redrawDataLine(); - } - else if (this.style === Graph3d.STYLE.BAR || - this.style === Graph3d.STYLE.BARCOLOR || - this.style === Graph3d.STYLE.BARSIZE) { - this._redrawDataBar(); - } - else { - // style is DOT, DOTLINE, DOTCOLOR, DOTSIZE - this._redrawDataDot(); - } + unix : function () { + return Math.floor(+this / 1000); + }, - this._redrawInfo(); - this._redrawLegend(); - }; + toString : function () { + return this.clone().locale('en').format('ddd MMM DD YYYY HH:mm:ss [GMT]ZZ'); + }, - /** - * Clear the canvas before redrawing - */ - Graph3d.prototype._redrawClear = function() { - var canvas = this.frame.canvas; - var ctx = canvas.getContext('2d'); + toDate : function () { + return this._offset ? new Date(+this) : this._d; + }, - ctx.clearRect(0, 0, canvas.width, canvas.height); - }; + toISOString : function () { + var m = moment(this).utc(); + if (0 < m.year() && m.year() <= 9999) { + if ('function' === typeof Date.prototype.toISOString) { + // native implementation is ~50x faster, use it when we can + return this.toDate().toISOString(); + } else { + return formatMoment(m, 'YYYY-MM-DD[T]HH:mm:ss.SSS[Z]'); + } + } else { + return formatMoment(m, 'YYYYYY-MM-DD[T]HH:mm:ss.SSS[Z]'); + } + }, + toArray : function () { + var m = this; + return [ + m.year(), + m.month(), + m.date(), + m.hours(), + m.minutes(), + m.seconds(), + m.milliseconds() + ]; + }, - /** - * Redraw the legend showing the colors - */ - Graph3d.prototype._redrawLegend = function() { - var y; + isValid : function () { + return isValid(this); + }, - if (this.style === Graph3d.STYLE.DOTCOLOR || - this.style === Graph3d.STYLE.DOTSIZE) { + isDSTShifted : function () { + if (this._a) { + return this.isValid() && compareArrays(this._a, (this._isUTC ? moment.utc(this._a) : moment(this._a)).toArray()) > 0; + } - var dotSize = this.frame.clientWidth * 0.02; + return false; + }, - var widthMin, widthMax; - if (this.style === Graph3d.STYLE.DOTSIZE) { - widthMin = dotSize / 2; // px - widthMax = dotSize / 2 + dotSize * 2; // Todo: put this in one function - } - else { - widthMin = 20; // px - widthMax = 20; // px - } + parsingFlags : function () { + return extend({}, this._pf); + }, - var height = Math.max(this.frame.clientHeight * 0.25, 100); - var top = this.margin; - var right = this.frame.clientWidth - this.margin; - var left = right - widthMax; - var bottom = top + height; - } + invalidAt: function () { + return this._pf.overflow; + }, - var canvas = this.frame.canvas; - var ctx = canvas.getContext('2d'); - ctx.lineWidth = 1; - ctx.font = '14px arial'; // TODO: put in options + utc : function (keepLocalTime) { + return this.utcOffset(0, keepLocalTime); + }, - if (this.style === Graph3d.STYLE.DOTCOLOR) { - // draw the color bar - var ymin = 0; - var ymax = height; // Todo: make height customizable - for (y = ymin; y < ymax; y++) { - var f = (y - ymin) / (ymax - ymin); + local : function (keepLocalTime) { + if (this._isUTC) { + this.utcOffset(0, keepLocalTime); + this._isUTC = false; - //var width = (dotSize / 2 + (1-f) * dotSize * 2); // Todo: put this in one function - var hue = f * 240; - var color = this._hsv2rgb(hue, 1, 1); + if (keepLocalTime) { + this.subtract(this._dateUtcOffset(), 'm'); + } + } + return this; + }, - ctx.strokeStyle = color; - ctx.beginPath(); - ctx.moveTo(left, top + y); - ctx.lineTo(right, top + y); - ctx.stroke(); - } + format : function (inputString) { + var output = formatMoment(this, inputString || moment.defaultFormat); + return this.localeData().postformat(output); + }, - ctx.strokeStyle = this.colorAxis; - ctx.strokeRect(left, top, widthMax, height); - } + add : createAdder(1, 'add'), - if (this.style === Graph3d.STYLE.DOTSIZE) { - // draw border around color bar - ctx.strokeStyle = this.colorAxis; - ctx.fillStyle = this.colorDot; - ctx.beginPath(); - ctx.moveTo(left, top); - ctx.lineTo(right, top); - ctx.lineTo(right - widthMax + widthMin, bottom); - ctx.lineTo(left, bottom); - ctx.closePath(); - ctx.fill(); - ctx.stroke(); - } + subtract : createAdder(-1, 'subtract'), - if (this.style === Graph3d.STYLE.DOTCOLOR || - this.style === Graph3d.STYLE.DOTSIZE) { - // print values along the color bar - var gridLineLen = 5; // px - var step = new StepNumber(this.valueMin, this.valueMax, (this.valueMax-this.valueMin)/5, true); - step.start(); - if (step.getCurrent() < this.valueMin) { - step.next(); - } - while (!step.end()) { - y = bottom - (step.getCurrent() - this.valueMin) / (this.valueMax - this.valueMin) * height; + diff : function (input, units, asFloat) { + var that = makeAs(input, this), + zoneDiff = (that.utcOffset() - this.utcOffset()) * 6e4, + anchor, diff, output, daysAdjust; - ctx.beginPath(); - ctx.moveTo(left - gridLineLen, y); - ctx.lineTo(left, y); - ctx.stroke(); + units = normalizeUnits(units); - ctx.textAlign = 'right'; - ctx.textBaseline = 'middle'; - ctx.fillStyle = this.colorAxis; - ctx.fillText(step.getCurrent(), left - 2 * gridLineLen, y); + if (units === 'year' || units === 'month' || units === 'quarter') { + output = monthDiff(this, that); + if (units === 'quarter') { + output = output / 3; + } else if (units === 'year') { + output = output / 12; + } + } else { + diff = this - that; + output = units === 'second' ? diff / 1e3 : // 1000 + units === 'minute' ? diff / 6e4 : // 1000 * 60 + units === 'hour' ? diff / 36e5 : // 1000 * 60 * 60 + units === 'day' ? (diff - zoneDiff) / 864e5 : // 1000 * 60 * 60 * 24, negate dst + units === 'week' ? (diff - zoneDiff) / 6048e5 : // 1000 * 60 * 60 * 24 * 7, negate dst + diff; + } + return asFloat ? output : absRound(output); + }, - step.next(); - } + from : function (time, withoutSuffix) { + return moment.duration({to: this, from: time}).locale(this.locale()).humanize(!withoutSuffix); + }, - ctx.textAlign = 'right'; - ctx.textBaseline = 'top'; - var label = this.legendLabel; - ctx.fillText(label, right, bottom + this.margin); - } - }; + fromNow : function (withoutSuffix) { + return this.from(moment(), withoutSuffix); + }, - /** - * Redraw the filter - */ - Graph3d.prototype._redrawFilter = function() { - this.frame.filter.innerHTML = ''; + calendar : function (time) { + // We want to compare the start of today, vs this. + // Getting start-of-today depends on whether we're locat/utc/offset + // or not. + var now = time || moment(), + sod = makeAs(now, this).startOf('day'), + diff = this.diff(sod, 'days', true), + format = diff < -6 ? 'sameElse' : + diff < -1 ? 'lastWeek' : + diff < 0 ? 'lastDay' : + diff < 1 ? 'sameDay' : + diff < 2 ? 'nextDay' : + diff < 7 ? 'nextWeek' : 'sameElse'; + return this.format(this.localeData().calendar(format, this, moment(now))); + }, - if (this.dataFilter) { - var options = { - 'visible': this.showAnimationControls - }; - var slider = new Slider(this.frame.filter, options); - this.frame.filter.slider = slider; + isLeapYear : function () { + return isLeapYear(this.year()); + }, - // TODO: css here is not nice here... - this.frame.filter.style.padding = '10px'; - //this.frame.filter.style.backgroundColor = '#EFEFEF'; + isDST : function () { + return (this.utcOffset() > this.clone().month(0).utcOffset() || + this.utcOffset() > this.clone().month(5).utcOffset()); + }, - slider.setValues(this.dataFilter.values); - slider.setPlayInterval(this.animationInterval); + day : function (input) { + var day = this._isUTC ? this._d.getUTCDay() : this._d.getDay(); + if (input != null) { + input = parseWeekday(input, this.localeData()); + return this.add(input - day, 'd'); + } else { + return day; + } + }, - // create an event handler - var me = this; - var onchange = function () { - var index = slider.getIndex(); + month : makeAccessor('Month', true), - me.dataFilter.selectValue(index); - me.dataPoints = me.dataFilter._getDataPoints(); + startOf : function (units) { + units = normalizeUnits(units); + // the following switch intentionally omits break keywords + // to utilize falling through the cases. + switch (units) { + case 'year': + this.month(0); + /* falls through */ + case 'quarter': + case 'month': + this.date(1); + /* falls through */ + case 'week': + case 'isoWeek': + case 'day': + this.hours(0); + /* falls through */ + case 'hour': + this.minutes(0); + /* falls through */ + case 'minute': + this.seconds(0); + /* falls through */ + case 'second': + this.milliseconds(0); + /* falls through */ + } - me.redraw(); - }; - slider.setOnChangeCallback(onchange); - } - else { - this.frame.filter.slider = undefined; - } - }; + // weeks are a special case + if (units === 'week') { + this.weekday(0); + } else if (units === 'isoWeek') { + this.isoWeekday(1); + } - /** - * Redraw the slider - */ - Graph3d.prototype._redrawSlider = function() { - if ( this.frame.filter.slider !== undefined) { - this.frame.filter.slider.redraw(); - } - }; + // quarters are also special + if (units === 'quarter') { + this.month(Math.floor(this.month() / 3) * 3); + } + return this; + }, - /** - * Redraw common information - */ - Graph3d.prototype._redrawInfo = function() { - if (this.dataFilter) { - var canvas = this.frame.canvas; - var ctx = canvas.getContext('2d'); + endOf: function (units) { + units = normalizeUnits(units); + if (units === undefined || units === 'millisecond') { + return this; + } + return this.startOf(units).add(1, (units === 'isoWeek' ? 'week' : units)).subtract(1, 'ms'); + }, - ctx.font = '14px arial'; // TODO: put in options - ctx.lineStyle = 'gray'; - ctx.fillStyle = 'gray'; - ctx.textAlign = 'left'; - ctx.textBaseline = 'top'; + isAfter: function (input, units) { + var inputMs; + units = normalizeUnits(typeof units !== 'undefined' ? units : 'millisecond'); + if (units === 'millisecond') { + input = moment.isMoment(input) ? input : moment(input); + return +this > +input; + } else { + inputMs = moment.isMoment(input) ? +input : +moment(input); + return inputMs < +this.clone().startOf(units); + } + }, - var x = this.margin; - var y = this.margin; - ctx.fillText(this.dataFilter.getLabel() + ': ' + this.dataFilter.getSelectedValue(), x, y); - } - }; + isBefore: function (input, units) { + var inputMs; + units = normalizeUnits(typeof units !== 'undefined' ? units : 'millisecond'); + if (units === 'millisecond') { + input = moment.isMoment(input) ? input : moment(input); + return +this < +input; + } else { + inputMs = moment.isMoment(input) ? +input : +moment(input); + return +this.clone().endOf(units) < inputMs; + } + }, + isBetween: function (from, to, units) { + return this.isAfter(from, units) && this.isBefore(to, units); + }, - /** - * Redraw the axis - */ - Graph3d.prototype._redrawAxis = function() { - var canvas = this.frame.canvas, - ctx = canvas.getContext('2d'), - from, to, step, prettyStep, - text, xText, yText, zText, - offset, xOffset, yOffset, - xMin2d, xMax2d; + isSame: function (input, units) { + var inputMs; + units = normalizeUnits(units || 'millisecond'); + if (units === 'millisecond') { + input = moment.isMoment(input) ? input : moment(input); + return +this === +input; + } else { + inputMs = +moment(input); + return +(this.clone().startOf(units)) <= inputMs && inputMs <= +(this.clone().endOf(units)); + } + }, - // TODO: get the actual rendered style of the containerElement - //ctx.font = this.containerElement.style.font; - ctx.font = 24 / this.camera.getArmLength() + 'px arial'; + min: deprecate( + 'moment().min is deprecated, use moment.min instead. https://github.com/moment/moment/issues/1548', + function (other) { + other = moment.apply(null, arguments); + return other < this ? this : other; + } + ), - // calculate the length for the short grid lines - var gridLenX = 0.025 / this.scale.x; - var gridLenY = 0.025 / this.scale.y; - var textMargin = 5 / this.camera.getArmLength(); // px - var armAngle = this.camera.getArmRotation().horizontal; + max: deprecate( + 'moment().max is deprecated, use moment.max instead. https://github.com/moment/moment/issues/1548', + function (other) { + other = moment.apply(null, arguments); + return other > this ? this : other; + } + ), - // draw x-grid lines - ctx.lineWidth = 1; - prettyStep = (this.defaultXStep === undefined); - step = new StepNumber(this.xMin, this.xMax, this.xStep, prettyStep); - step.start(); - if (step.getCurrent() < this.xMin) { - step.next(); - } - while (!step.end()) { - var x = step.getCurrent(); + zone : deprecate( + 'moment().zone is deprecated, use moment().utcOffset instead. ' + + 'https://github.com/moment/moment/issues/1779', + function (input, keepLocalTime) { + if (input != null) { + if (typeof input !== 'string') { + input = -input; + } - if (this.showGrid) { - from = this._convert3Dto2D(new Point3d(x, this.yMin, this.zMin)); - to = this._convert3Dto2D(new Point3d(x, this.yMax, this.zMin)); - ctx.strokeStyle = this.colorGrid; - ctx.beginPath(); - ctx.moveTo(from.x, from.y); - ctx.lineTo(to.x, to.y); - ctx.stroke(); - } - else { - from = this._convert3Dto2D(new Point3d(x, this.yMin, this.zMin)); - to = this._convert3Dto2D(new Point3d(x, this.yMin+gridLenX, this.zMin)); - ctx.strokeStyle = this.colorAxis; - ctx.beginPath(); - ctx.moveTo(from.x, from.y); - ctx.lineTo(to.x, to.y); - ctx.stroke(); + this.utcOffset(input, keepLocalTime); - from = this._convert3Dto2D(new Point3d(x, this.yMax, this.zMin)); - to = this._convert3Dto2D(new Point3d(x, this.yMax-gridLenX, this.zMin)); - ctx.strokeStyle = this.colorAxis; - ctx.beginPath(); - ctx.moveTo(from.x, from.y); - ctx.lineTo(to.x, to.y); - ctx.stroke(); - } + return this; + } else { + return -this.utcOffset(); + } + } + ), - yText = (Math.cos(armAngle) > 0) ? this.yMin : this.yMax; - text = this._convert3Dto2D(new Point3d(x, yText, this.zMin)); - if (Math.cos(armAngle * 2) > 0) { - ctx.textAlign = 'center'; - ctx.textBaseline = 'top'; - text.y += textMargin; - } - else if (Math.sin(armAngle * 2) < 0){ - ctx.textAlign = 'right'; - ctx.textBaseline = 'middle'; - } - else { - ctx.textAlign = 'left'; - ctx.textBaseline = 'middle'; - } - ctx.fillStyle = this.colorAxis; - ctx.fillText(' ' + this.xValueLabel(step.getCurrent()) + ' ', text.x, text.y); + // keepLocalTime = true means only change the timezone, without + // affecting the local hour. So 5:31:26 +0300 --[utcOffset(2, true)]--> + // 5:31:26 +0200 It is possible that 5:31:26 doesn't exist with offset + // +0200, so we adjust the time as needed, to be valid. + // + // Keeping the time actually adds/subtracts (one hour) + // from the actual represented time. That is why we call updateOffset + // a second time. In case it wants us to change the offset again + // _changeInProgress == true case, then we have to adjust, because + // there is no such time in the given timezone. + utcOffset : function (input, keepLocalTime) { + var offset = this._offset || 0, + localAdjust; + if (input != null) { + if (typeof input === 'string') { + input = utcOffsetFromString(input); + } + if (Math.abs(input) < 16) { + input = input * 60; + } + if (!this._isUTC && keepLocalTime) { + localAdjust = this._dateUtcOffset(); + } + this._offset = input; + this._isUTC = true; + if (localAdjust != null) { + this.add(localAdjust, 'm'); + } + if (offset !== input) { + if (!keepLocalTime || this._changeInProgress) { + addOrSubtractDurationFromMoment(this, + moment.duration(input - offset, 'm'), 1, false); + } else if (!this._changeInProgress) { + this._changeInProgress = true; + moment.updateOffset(this, true); + this._changeInProgress = null; + } + } - step.next(); - } + return this; + } else { + return this._isUTC ? offset : this._dateUtcOffset(); + } + }, - // draw y-grid lines - ctx.lineWidth = 1; - prettyStep = (this.defaultYStep === undefined); - step = new StepNumber(this.yMin, this.yMax, this.yStep, prettyStep); - step.start(); - if (step.getCurrent() < this.yMin) { - step.next(); - } - while (!step.end()) { - if (this.showGrid) { - from = this._convert3Dto2D(new Point3d(this.xMin, step.getCurrent(), this.zMin)); - to = this._convert3Dto2D(new Point3d(this.xMax, step.getCurrent(), this.zMin)); - ctx.strokeStyle = this.colorGrid; - ctx.beginPath(); - ctx.moveTo(from.x, from.y); - ctx.lineTo(to.x, to.y); - ctx.stroke(); - } - else { - from = this._convert3Dto2D(new Point3d(this.xMin, step.getCurrent(), this.zMin)); - to = this._convert3Dto2D(new Point3d(this.xMin+gridLenY, step.getCurrent(), this.zMin)); - ctx.strokeStyle = this.colorAxis; - ctx.beginPath(); - ctx.moveTo(from.x, from.y); - ctx.lineTo(to.x, to.y); - ctx.stroke(); + isLocal : function () { + return !this._isUTC; + }, - from = this._convert3Dto2D(new Point3d(this.xMax, step.getCurrent(), this.zMin)); - to = this._convert3Dto2D(new Point3d(this.xMax-gridLenY, step.getCurrent(), this.zMin)); - ctx.strokeStyle = this.colorAxis; - ctx.beginPath(); - ctx.moveTo(from.x, from.y); - ctx.lineTo(to.x, to.y); - ctx.stroke(); - } + isUtcOffset : function () { + return this._isUTC; + }, - xText = (Math.sin(armAngle ) > 0) ? this.xMin : this.xMax; - text = this._convert3Dto2D(new Point3d(xText, step.getCurrent(), this.zMin)); - if (Math.cos(armAngle * 2) < 0) { - ctx.textAlign = 'center'; - ctx.textBaseline = 'top'; - text.y += textMargin; - } - else if (Math.sin(armAngle * 2) > 0){ - ctx.textAlign = 'right'; - ctx.textBaseline = 'middle'; - } - else { - ctx.textAlign = 'left'; - ctx.textBaseline = 'middle'; - } - ctx.fillStyle = this.colorAxis; - ctx.fillText(' ' + this.yValueLabel(step.getCurrent()) + ' ', text.x, text.y); + isUtc : function () { + return this._isUTC && this._offset === 0; + }, - step.next(); - } + zoneAbbr : function () { + return this._isUTC ? 'UTC' : ''; + }, - // draw z-grid lines and axis - ctx.lineWidth = 1; - prettyStep = (this.defaultZStep === undefined); - step = new StepNumber(this.zMin, this.zMax, this.zStep, prettyStep); - step.start(); - if (step.getCurrent() < this.zMin) { - step.next(); - } - xText = (Math.cos(armAngle ) > 0) ? this.xMin : this.xMax; - yText = (Math.sin(armAngle ) < 0) ? this.yMin : this.yMax; - while (!step.end()) { - // TODO: make z-grid lines really 3d? - from = this._convert3Dto2D(new Point3d(xText, yText, step.getCurrent())); - ctx.strokeStyle = this.colorAxis; - ctx.beginPath(); - ctx.moveTo(from.x, from.y); - ctx.lineTo(from.x - textMargin, from.y); - ctx.stroke(); + zoneName : function () { + return this._isUTC ? 'Coordinated Universal Time' : ''; + }, - ctx.textAlign = 'right'; - ctx.textBaseline = 'middle'; - ctx.fillStyle = this.colorAxis; - ctx.fillText(this.zValueLabel(step.getCurrent()) + ' ', from.x - 5, from.y); + parseZone : function () { + if (this._tzm) { + this.utcOffset(this._tzm); + } else if (typeof this._i === 'string') { + this.utcOffset(utcOffsetFromString(this._i)); + } + return this; + }, - step.next(); - } - ctx.lineWidth = 1; - from = this._convert3Dto2D(new Point3d(xText, yText, this.zMin)); - to = this._convert3Dto2D(new Point3d(xText, yText, this.zMax)); - ctx.strokeStyle = this.colorAxis; - ctx.beginPath(); - ctx.moveTo(from.x, from.y); - ctx.lineTo(to.x, to.y); - ctx.stroke(); + hasAlignedHourOffset : function (input) { + if (!input) { + input = 0; + } + else { + input = moment(input).utcOffset(); + } - // draw x-axis - ctx.lineWidth = 1; - // line at yMin - xMin2d = this._convert3Dto2D(new Point3d(this.xMin, this.yMin, this.zMin)); - xMax2d = this._convert3Dto2D(new Point3d(this.xMax, this.yMin, this.zMin)); - ctx.strokeStyle = this.colorAxis; - ctx.beginPath(); - ctx.moveTo(xMin2d.x, xMin2d.y); - ctx.lineTo(xMax2d.x, xMax2d.y); - ctx.stroke(); - // line at ymax - xMin2d = this._convert3Dto2D(new Point3d(this.xMin, this.yMax, this.zMin)); - xMax2d = this._convert3Dto2D(new Point3d(this.xMax, this.yMax, this.zMin)); - ctx.strokeStyle = this.colorAxis; - ctx.beginPath(); - ctx.moveTo(xMin2d.x, xMin2d.y); - ctx.lineTo(xMax2d.x, xMax2d.y); - ctx.stroke(); + return (this.utcOffset() - input) % 60 === 0; + }, - // draw y-axis - ctx.lineWidth = 1; - // line at xMin - from = this._convert3Dto2D(new Point3d(this.xMin, this.yMin, this.zMin)); - to = this._convert3Dto2D(new Point3d(this.xMin, this.yMax, this.zMin)); - ctx.strokeStyle = this.colorAxis; - ctx.beginPath(); - ctx.moveTo(from.x, from.y); - ctx.lineTo(to.x, to.y); - ctx.stroke(); - // line at xMax - from = this._convert3Dto2D(new Point3d(this.xMax, this.yMin, this.zMin)); - to = this._convert3Dto2D(new Point3d(this.xMax, this.yMax, this.zMin)); - ctx.strokeStyle = this.colorAxis; - ctx.beginPath(); - ctx.moveTo(from.x, from.y); - ctx.lineTo(to.x, to.y); - ctx.stroke(); + daysInMonth : function () { + return daysInMonth(this.year(), this.month()); + }, - // draw x-label - var xLabel = this.xLabel; - if (xLabel.length > 0) { - yOffset = 0.1 / this.scale.y; - xText = (this.xMin + this.xMax) / 2; - yText = (Math.cos(armAngle) > 0) ? this.yMin - yOffset: this.yMax + yOffset; - text = this._convert3Dto2D(new Point3d(xText, yText, this.zMin)); - if (Math.cos(armAngle * 2) > 0) { - ctx.textAlign = 'center'; - ctx.textBaseline = 'top'; - } - else if (Math.sin(armAngle * 2) < 0){ - ctx.textAlign = 'right'; - ctx.textBaseline = 'middle'; - } - else { - ctx.textAlign = 'left'; - ctx.textBaseline = 'middle'; - } - ctx.fillStyle = this.colorAxis; - ctx.fillText(xLabel, text.x, text.y); - } + dayOfYear : function (input) { + var dayOfYear = round((moment(this).startOf('day') - moment(this).startOf('year')) / 864e5) + 1; + return input == null ? dayOfYear : this.add((input - dayOfYear), 'd'); + }, - // draw y-label - var yLabel = this.yLabel; - if (yLabel.length > 0) { - xOffset = 0.1 / this.scale.x; - xText = (Math.sin(armAngle ) > 0) ? this.xMin - xOffset : this.xMax + xOffset; - yText = (this.yMin + this.yMax) / 2; - text = this._convert3Dto2D(new Point3d(xText, yText, this.zMin)); - if (Math.cos(armAngle * 2) < 0) { - ctx.textAlign = 'center'; - ctx.textBaseline = 'top'; - } - else if (Math.sin(armAngle * 2) > 0){ - ctx.textAlign = 'right'; - ctx.textBaseline = 'middle'; - } - else { - ctx.textAlign = 'left'; - ctx.textBaseline = 'middle'; - } - ctx.fillStyle = this.colorAxis; - ctx.fillText(yLabel, text.x, text.y); - } + quarter : function (input) { + return input == null ? Math.ceil((this.month() + 1) / 3) : this.month((input - 1) * 3 + this.month() % 3); + }, - // draw z-label - var zLabel = this.zLabel; - if (zLabel.length > 0) { - offset = 30; // pixels. // TODO: relate to the max width of the values on the z axis? - xText = (Math.cos(armAngle ) > 0) ? this.xMin : this.xMax; - yText = (Math.sin(armAngle ) < 0) ? this.yMin : this.yMax; - zText = (this.zMin + this.zMax) / 2; - text = this._convert3Dto2D(new Point3d(xText, yText, zText)); - ctx.textAlign = 'right'; - ctx.textBaseline = 'middle'; - ctx.fillStyle = this.colorAxis; - ctx.fillText(zLabel, text.x - offset, text.y); - } - }; + weekYear : function (input) { + var year = weekOfYear(this, this.localeData()._week.dow, this.localeData()._week.doy).year; + return input == null ? year : this.add((input - year), 'y'); + }, - /** - * Calculate the color based on the given value. - * @param {Number} H Hue, a value be between 0 and 360 - * @param {Number} S Saturation, a value between 0 and 1 - * @param {Number} V Value, a value between 0 and 1 - */ - Graph3d.prototype._hsv2rgb = function(H, S, V) { - var R, G, B, C, Hi, X; + isoWeekYear : function (input) { + var year = weekOfYear(this, 1, 4).year; + return input == null ? year : this.add((input - year), 'y'); + }, - C = V * S; - Hi = Math.floor(H/60); // hi = 0,1,2,3,4,5 - X = C * (1 - Math.abs(((H/60) % 2) - 1)); + week : function (input) { + var week = this.localeData().week(this); + return input == null ? week : this.add((input - week) * 7, 'd'); + }, - switch (Hi) { - case 0: R = C; G = X; B = 0; break; - case 1: R = X; G = C; B = 0; break; - case 2: R = 0; G = C; B = X; break; - case 3: R = 0; G = X; B = C; break; - case 4: R = X; G = 0; B = C; break; - case 5: R = C; G = 0; B = X; break; + isoWeek : function (input) { + var week = weekOfYear(this, 1, 4).week; + return input == null ? week : this.add((input - week) * 7, 'd'); + }, - default: R = 0; G = 0; B = 0; break; - } + weekday : function (input) { + var weekday = (this.day() + 7 - this.localeData()._week.dow) % 7; + return input == null ? weekday : this.add(input - weekday, 'd'); + }, - return 'RGB(' + parseInt(R*255) + ',' + parseInt(G*255) + ',' + parseInt(B*255) + ')'; - }; + isoWeekday : function (input) { + // behaves the same as moment#day except + // as a getter, returns 7 instead of 0 (1-7 range instead of 0-6) + // as a setter, sunday should belong to the previous week. + return input == null ? this.day() || 7 : this.day(this.day() % 7 ? input : input - 7); + }, + isoWeeksInYear : function () { + return weeksInYear(this.year(), 1, 4); + }, - /** - * Draw all datapoints as a grid - * This function can be used when the style is 'grid' - */ - Graph3d.prototype._redrawDataGrid = function() { - var canvas = this.frame.canvas, - ctx = canvas.getContext('2d'), - point, right, top, cross, - i, - topSideVisible, fillStyle, strokeStyle, lineWidth, - h, s, v, zAvg; + weeksInYear : function () { + var weekInfo = this.localeData()._week; + return weeksInYear(this.year(), weekInfo.dow, weekInfo.doy); + }, + get : function (units) { + units = normalizeUnits(units); + return this[units](); + }, - if (this.dataPoints === undefined || this.dataPoints.length <= 0) - return; // TODO: throw exception? + set : function (units, value) { + var unit; + if (typeof units === 'object') { + for (unit in units) { + this.set(unit, units[unit]); + } + } + else { + units = normalizeUnits(units); + if (typeof this[units] === 'function') { + this[units](value); + } + } + return this; + }, - // calculate the translations and screen position of all points - for (i = 0; i < this.dataPoints.length; i++) { - var trans = this._convertPointToTranslation(this.dataPoints[i].point); - var screen = this._convertTranslationToScreen(trans); + // If passed a locale key, it will set the locale for this + // instance. Otherwise, it will return the locale configuration + // variables for this instance. + locale : function (key) { + var newLocaleData; - this.dataPoints[i].trans = trans; - this.dataPoints[i].screen = screen; + if (key === undefined) { + return this._locale._abbr; + } else { + newLocaleData = moment.localeData(key); + if (newLocaleData != null) { + this._locale = newLocaleData; + } + return this; + } + }, - // calculate the translation of the point at the bottom (needed for sorting) - var transBottom = this._convertPointToTranslation(this.dataPoints[i].bottom); - this.dataPoints[i].dist = this.showPerspective ? transBottom.length() : -transBottom.z; - } + lang : deprecate( + 'moment().lang() is deprecated. Instead, use moment().localeData() to get the language configuration. Use moment().locale() to change languages.', + function (key) { + if (key === undefined) { + return this.localeData(); + } else { + return this.locale(key); + } + } + ), - // sort the points on depth of their (x,y) position (not on z) - var sortDepth = function (a, b) { - return b.dist - a.dist; - }; - this.dataPoints.sort(sortDepth); + localeData : function () { + return this._locale; + }, - if (this.style === Graph3d.STYLE.SURFACE) { - for (i = 0; i < this.dataPoints.length; i++) { - point = this.dataPoints[i]; - right = this.dataPoints[i].pointRight; - top = this.dataPoints[i].pointTop; - cross = this.dataPoints[i].pointCross; + _dateUtcOffset : function () { + // On Firefox.24 Date#getTimezoneOffset returns a floating point. + // https://github.com/moment/moment/pull/1871 + return -Math.round(this._d.getTimezoneOffset() / 15) * 15; + } - if (point !== undefined && right !== undefined && top !== undefined && cross !== undefined) { + }); - if (this.showGrayBottom || this.showShadow) { - // calculate the cross product of the two vectors from center - // to left and right, in order to know whether we are looking at the - // bottom or at the top side. We can also use the cross product - // for calculating light intensity - var aDiff = Point3d.subtract(cross.trans, point.trans); - var bDiff = Point3d.subtract(top.trans, right.trans); - var crossproduct = Point3d.crossProduct(aDiff, bDiff); - var len = crossproduct.length(); - // FIXME: there is a bug with determining the surface side (shadow or colored) + function rawMonthSetter(mom, value) { + var dayOfMonth; - topSideVisible = (crossproduct.z > 0); - } - else { - topSideVisible = true; + // TODO: Move this out of here! + if (typeof value === 'string') { + value = mom.localeData().monthsParse(value); + // TODO: Another silent failure? + if (typeof value !== 'number') { + return mom; + } } - if (topSideVisible) { - // calculate Hue from the current value. At zMin the hue is 240, at zMax the hue is 0 - zAvg = (point.point.z + right.point.z + top.point.z + cross.point.z) / 4; - h = (1 - (zAvg - this.zMin) * this.scale.z / this.verticalRatio) * 240; - s = 1; // saturation + dayOfMonth = Math.min(mom.date(), + daysInMonth(mom.year(), value)); + mom._d['set' + (mom._isUTC ? 'UTC' : '') + 'Month'](value, dayOfMonth); + return mom; + } - if (this.showShadow) { - v = Math.min(1 + (crossproduct.x / len) / 2, 1); // value. TODO: scale - fillStyle = this._hsv2rgb(h, s, v); - strokeStyle = fillStyle; - } - else { - v = 1; - fillStyle = this._hsv2rgb(h, s, v); - strokeStyle = this.colorAxis; - } - } - else { - fillStyle = 'gray'; - strokeStyle = this.colorAxis; + function rawGetter(mom, unit) { + return mom._d['get' + (mom._isUTC ? 'UTC' : '') + unit](); + } + + function rawSetter(mom, unit, value) { + if (unit === 'Month') { + return rawMonthSetter(mom, value); + } else { + return mom._d['set' + (mom._isUTC ? 'UTC' : '') + unit](value); } - lineWidth = 0.5; + } - ctx.lineWidth = lineWidth; - ctx.fillStyle = fillStyle; - ctx.strokeStyle = strokeStyle; - ctx.beginPath(); - ctx.moveTo(point.screen.x, point.screen.y); - ctx.lineTo(right.screen.x, right.screen.y); - ctx.lineTo(cross.screen.x, cross.screen.y); - ctx.lineTo(top.screen.x, top.screen.y); - ctx.closePath(); - ctx.fill(); - ctx.stroke(); - } + function makeAccessor(unit, keepTime) { + return function (value) { + if (value != null) { + rawSetter(this, unit, value); + moment.updateOffset(this, keepTime); + return this; + } else { + return rawGetter(this, unit); + } + }; } - } - else { // grid style - for (i = 0; i < this.dataPoints.length; i++) { - point = this.dataPoints[i]; - right = this.dataPoints[i].pointRight; - top = this.dataPoints[i].pointTop; - if (point !== undefined) { - if (this.showPerspective) { - lineWidth = 2 / -point.trans.z; - } - else { - lineWidth = 2 * -(this.eye.z / this.camera.getArmLength()); - } - } + moment.fn.millisecond = moment.fn.milliseconds = makeAccessor('Milliseconds', false); + moment.fn.second = moment.fn.seconds = makeAccessor('Seconds', false); + moment.fn.minute = moment.fn.minutes = makeAccessor('Minutes', false); + // Setting the hour should keep the time, because the user explicitly + // specified which hour he wants. So trying to maintain the same hour (in + // a new timezone) makes sense. Adding/subtracting hours does not follow + // this rule. + moment.fn.hour = moment.fn.hours = makeAccessor('Hours', true); + // moment.fn.month is defined separately + moment.fn.date = makeAccessor('Date', true); + moment.fn.dates = deprecate('dates accessor is deprecated. Use date instead.', makeAccessor('Date', true)); + moment.fn.year = makeAccessor('FullYear', true); + moment.fn.years = deprecate('years accessor is deprecated. Use year instead.', makeAccessor('FullYear', true)); - if (point !== undefined && right !== undefined) { - // calculate Hue from the current value. At zMin the hue is 240, at zMax the hue is 0 - zAvg = (point.point.z + right.point.z) / 2; - h = (1 - (zAvg - this.zMin) * this.scale.z / this.verticalRatio) * 240; + // add plural methods + moment.fn.days = moment.fn.day; + moment.fn.months = moment.fn.month; + moment.fn.weeks = moment.fn.week; + moment.fn.isoWeeks = moment.fn.isoWeek; + moment.fn.quarters = moment.fn.quarter; - ctx.lineWidth = lineWidth; - ctx.strokeStyle = this._hsv2rgb(h, 1, 1); - ctx.beginPath(); - ctx.moveTo(point.screen.x, point.screen.y); - ctx.lineTo(right.screen.x, right.screen.y); - ctx.stroke(); - } + // add aliased format methods + moment.fn.toJSON = moment.fn.toISOString; - if (point !== undefined && top !== undefined) { - // calculate Hue from the current value. At zMin the hue is 240, at zMax the hue is 0 - zAvg = (point.point.z + top.point.z) / 2; - h = (1 - (zAvg - this.zMin) * this.scale.z / this.verticalRatio) * 240; + // alias isUtc for dev-friendliness + moment.fn.isUTC = moment.fn.isUtc; - ctx.lineWidth = lineWidth; - ctx.strokeStyle = this._hsv2rgb(h, 1, 1); - ctx.beginPath(); - ctx.moveTo(point.screen.x, point.screen.y); - ctx.lineTo(top.screen.x, top.screen.y); - ctx.stroke(); - } - } - } - }; + /************************************ + Duration Prototype + ************************************/ - /** - * Draw all datapoints as dots. - * This function can be used when the style is 'dot' or 'dot-line' - */ - Graph3d.prototype._redrawDataDot = function() { - var canvas = this.frame.canvas; - var ctx = canvas.getContext('2d'); - var i; + function daysToYears (days) { + // 400 years have 146097 days (taking into account leap year rules) + return days * 400 / 146097; + } - if (this.dataPoints === undefined || this.dataPoints.length <= 0) - return; // TODO: throw exception? + function yearsToDays (years) { + // years * 365 + absRound(years / 4) - + // absRound(years / 100) + absRound(years / 400); + return years * 146097 / 400; + } - // calculate the translations of all points - for (i = 0; i < this.dataPoints.length; i++) { - var trans = this._convertPointToTranslation(this.dataPoints[i].point); - var screen = this._convertTranslationToScreen(trans); - this.dataPoints[i].trans = trans; - this.dataPoints[i].screen = screen; + extend(moment.duration.fn = Duration.prototype, { - // calculate the distance from the point at the bottom to the camera - var transBottom = this._convertPointToTranslation(this.dataPoints[i].bottom); - this.dataPoints[i].dist = this.showPerspective ? transBottom.length() : -transBottom.z; - } + _bubble : function () { + var milliseconds = this._milliseconds, + days = this._days, + months = this._months, + data = this._data, + seconds, minutes, hours, years = 0; - // order the translated points by depth - var sortDepth = function (a, b) { - return b.dist - a.dist; - }; - this.dataPoints.sort(sortDepth); + // The following code bubbles up values, see the tests for + // examples of what that means. + data.milliseconds = milliseconds % 1000; - // draw the datapoints as colored circles - var dotSize = this.frame.clientWidth * 0.02; // px - for (i = 0; i < this.dataPoints.length; i++) { - var point = this.dataPoints[i]; + seconds = absRound(milliseconds / 1000); + data.seconds = seconds % 60; - if (this.style === Graph3d.STYLE.DOTLINE) { - // draw a vertical line from the bottom to the graph value - //var from = this._convert3Dto2D(new Point3d(point.point.x, point.point.y, this.zMin)); - var from = this._convert3Dto2D(point.bottom); - ctx.lineWidth = 1; - ctx.strokeStyle = this.colorGrid; - ctx.beginPath(); - ctx.moveTo(from.x, from.y); - ctx.lineTo(point.screen.x, point.screen.y); - ctx.stroke(); - } + minutes = absRound(seconds / 60); + data.minutes = minutes % 60; - // calculate radius for the circle - var size; - if (this.style === Graph3d.STYLE.DOTSIZE) { - size = dotSize/2 + 2*dotSize * (point.point.value - this.valueMin) / (this.valueMax - this.valueMin); - } - else { - size = dotSize; - } + hours = absRound(minutes / 60); + data.hours = hours % 24; - var radius; - if (this.showPerspective) { - radius = size / -point.trans.z; - } - else { - radius = size * -(this.eye.z / this.camera.getArmLength()); - } - if (radius < 0) { - radius = 0; - } + days += absRound(hours / 24); - var hue, color, borderColor; - if (this.style === Graph3d.STYLE.DOTCOLOR ) { - // calculate the color based on the value - hue = (1 - (point.point.value - this.valueMin) * this.scale.value) * 240; - color = this._hsv2rgb(hue, 1, 1); - borderColor = this._hsv2rgb(hue, 1, 0.8); - } - else if (this.style === Graph3d.STYLE.DOTSIZE) { - color = this.colorDot; - borderColor = this.colorDotBorder; - } - else { - // calculate Hue from the current value. At zMin the hue is 240, at zMax the hue is 0 - hue = (1 - (point.point.z - this.zMin) * this.scale.z / this.verticalRatio) * 240; - color = this._hsv2rgb(hue, 1, 1); - borderColor = this._hsv2rgb(hue, 1, 0.8); - } + // Accurately convert days to years, assume start from year 0. + years = absRound(daysToYears(days)); + days -= absRound(yearsToDays(years)); - // draw the circle - ctx.lineWidth = 1.0; - ctx.strokeStyle = borderColor; - ctx.fillStyle = color; - ctx.beginPath(); - ctx.arc(point.screen.x, point.screen.y, radius, 0, Math.PI*2, true); - ctx.fill(); - ctx.stroke(); - } - }; + // 30 days to a month + // TODO (iskren): Use anchor date (like 1st Jan) to compute this. + months += absRound(days / 30); + days %= 30; - /** - * Draw all datapoints as bars. - * This function can be used when the style is 'bar', 'bar-color', or 'bar-size' - */ - Graph3d.prototype._redrawDataBar = function() { - var canvas = this.frame.canvas; - var ctx = canvas.getContext('2d'); - var i, j, surface, corners; + // 12 months -> 1 year + years += absRound(months / 12); + months %= 12; - if (this.dataPoints === undefined || this.dataPoints.length <= 0) - return; // TODO: throw exception? + data.days = days; + data.months = months; + data.years = years; + }, - // calculate the translations of all points - for (i = 0; i < this.dataPoints.length; i++) { - var trans = this._convertPointToTranslation(this.dataPoints[i].point); - var screen = this._convertTranslationToScreen(trans); - this.dataPoints[i].trans = trans; - this.dataPoints[i].screen = screen; + abs : function () { + this._milliseconds = Math.abs(this._milliseconds); + this._days = Math.abs(this._days); + this._months = Math.abs(this._months); - // calculate the distance from the point at the bottom to the camera - var transBottom = this._convertPointToTranslation(this.dataPoints[i].bottom); - this.dataPoints[i].dist = this.showPerspective ? transBottom.length() : -transBottom.z; - } + this._data.milliseconds = Math.abs(this._data.milliseconds); + this._data.seconds = Math.abs(this._data.seconds); + this._data.minutes = Math.abs(this._data.minutes); + this._data.hours = Math.abs(this._data.hours); + this._data.months = Math.abs(this._data.months); + this._data.years = Math.abs(this._data.years); - // order the translated points by depth - var sortDepth = function (a, b) { - return b.dist - a.dist; - }; - this.dataPoints.sort(sortDepth); + return this; + }, - // draw the datapoints as bars - var xWidth = this.xBarWidth / 2; - var yWidth = this.yBarWidth / 2; - for (i = 0; i < this.dataPoints.length; i++) { - var point = this.dataPoints[i]; + weeks : function () { + return absRound(this.days() / 7); + }, - // determine color - var hue, color, borderColor; - if (this.style === Graph3d.STYLE.BARCOLOR ) { - // calculate the color based on the value - hue = (1 - (point.point.value - this.valueMin) * this.scale.value) * 240; - color = this._hsv2rgb(hue, 1, 1); - borderColor = this._hsv2rgb(hue, 1, 0.8); - } - else if (this.style === Graph3d.STYLE.BARSIZE) { - color = this.colorDot; - borderColor = this.colorDotBorder; - } - else { - // calculate Hue from the current value. At zMin the hue is 240, at zMax the hue is 0 - hue = (1 - (point.point.z - this.zMin) * this.scale.z / this.verticalRatio) * 240; - color = this._hsv2rgb(hue, 1, 1); - borderColor = this._hsv2rgb(hue, 1, 0.8); - } + valueOf : function () { + return this._milliseconds + + this._days * 864e5 + + (this._months % 12) * 2592e6 + + toInt(this._months / 12) * 31536e6; + }, - // calculate size for the bar - if (this.style === Graph3d.STYLE.BARSIZE) { - xWidth = (this.xBarWidth / 2) * ((point.point.value - this.valueMin) / (this.valueMax - this.valueMin) * 0.8 + 0.2); - yWidth = (this.yBarWidth / 2) * ((point.point.value - this.valueMin) / (this.valueMax - this.valueMin) * 0.8 + 0.2); - } + humanize : function (withSuffix) { + var output = relativeTime(this, !withSuffix, this.localeData()); - // calculate all corner points - var me = this; - var point3d = point.point; - var top = [ - {point: new Point3d(point3d.x - xWidth, point3d.y - yWidth, point3d.z)}, - {point: new Point3d(point3d.x + xWidth, point3d.y - yWidth, point3d.z)}, - {point: new Point3d(point3d.x + xWidth, point3d.y + yWidth, point3d.z)}, - {point: new Point3d(point3d.x - xWidth, point3d.y + yWidth, point3d.z)} - ]; - var bottom = [ - {point: new Point3d(point3d.x - xWidth, point3d.y - yWidth, this.zMin)}, - {point: new Point3d(point3d.x + xWidth, point3d.y - yWidth, this.zMin)}, - {point: new Point3d(point3d.x + xWidth, point3d.y + yWidth, this.zMin)}, - {point: new Point3d(point3d.x - xWidth, point3d.y + yWidth, this.zMin)} - ]; + if (withSuffix) { + output = this.localeData().pastFuture(+this, output); + } - // calculate screen location of the points - top.forEach(function (obj) { - obj.screen = me._convert3Dto2D(obj.point); - }); - bottom.forEach(function (obj) { - obj.screen = me._convert3Dto2D(obj.point); - }); + return this.localeData().postformat(output); + }, - // create five sides, calculate both corner points and center points - var surfaces = [ - {corners: top, center: Point3d.avg(bottom[0].point, bottom[2].point)}, - {corners: [top[0], top[1], bottom[1], bottom[0]], center: Point3d.avg(bottom[1].point, bottom[0].point)}, - {corners: [top[1], top[2], bottom[2], bottom[1]], center: Point3d.avg(bottom[2].point, bottom[1].point)}, - {corners: [top[2], top[3], bottom[3], bottom[2]], center: Point3d.avg(bottom[3].point, bottom[2].point)}, - {corners: [top[3], top[0], bottom[0], bottom[3]], center: Point3d.avg(bottom[0].point, bottom[3].point)} - ]; - point.surfaces = surfaces; + add : function (input, val) { + // supports only 2.0-style add(1, 's') or add(moment) + var dur = moment.duration(input, val); - // calculate the distance of each of the surface centers to the camera - for (j = 0; j < surfaces.length; j++) { - surface = surfaces[j]; - var transCenter = this._convertPointToTranslation(surface.center); - surface.dist = this.showPerspective ? transCenter.length() : -transCenter.z; - // TODO: this dept calculation doesn't work 100% of the cases due to perspective, - // but the current solution is fast/simple and works in 99.9% of all cases - // the issue is visible in example 14, with graph.setCameraPosition({horizontal: 2.97, vertical: 0.5, distance: 0.9}) - } + this._milliseconds += dur._milliseconds; + this._days += dur._days; + this._months += dur._months; - // order the surfaces by their (translated) depth - surfaces.sort(function (a, b) { - var diff = b.dist - a.dist; - if (diff) return diff; + this._bubble(); - // if equal depth, sort the top surface last - if (a.corners === top) return 1; - if (b.corners === top) return -1; + return this; + }, - // both are equal - return 0; - }); + subtract : function (input, val) { + var dur = moment.duration(input, val); - // draw the ordered surfaces - ctx.lineWidth = 1; - ctx.strokeStyle = borderColor; - ctx.fillStyle = color; - // NOTE: we start at j=2 instead of j=0 as we don't need to draw the two surfaces at the backside - for (j = 2; j < surfaces.length; j++) { - surface = surfaces[j]; - corners = surface.corners; - ctx.beginPath(); - ctx.moveTo(corners[3].screen.x, corners[3].screen.y); - ctx.lineTo(corners[0].screen.x, corners[0].screen.y); - ctx.lineTo(corners[1].screen.x, corners[1].screen.y); - ctx.lineTo(corners[2].screen.x, corners[2].screen.y); - ctx.lineTo(corners[3].screen.x, corners[3].screen.y); - ctx.fill(); - ctx.stroke(); - } - } - }; + this._milliseconds -= dur._milliseconds; + this._days -= dur._days; + this._months -= dur._months; + this._bubble(); - /** - * Draw a line through all datapoints. - * This function can be used when the style is 'line' - */ - Graph3d.prototype._redrawDataLine = function() { - var canvas = this.frame.canvas, - ctx = canvas.getContext('2d'), - point, i; + return this; + }, - if (this.dataPoints === undefined || this.dataPoints.length <= 0) - return; // TODO: throw exception? + get : function (units) { + units = normalizeUnits(units); + return this[units.toLowerCase() + 's'](); + }, - // calculate the translations of all points - for (i = 0; i < this.dataPoints.length; i++) { - var trans = this._convertPointToTranslation(this.dataPoints[i].point); - var screen = this._convertTranslationToScreen(trans); + as : function (units) { + var days, months; + units = normalizeUnits(units); - this.dataPoints[i].trans = trans; - this.dataPoints[i].screen = screen; - } + if (units === 'month' || units === 'year') { + days = this._days + this._milliseconds / 864e5; + months = this._months + daysToYears(days) * 12; + return units === 'month' ? months : months / 12; + } else { + // handle milliseconds separately because of floating point math errors (issue #1867) + days = this._days + Math.round(yearsToDays(this._months / 12)); + switch (units) { + case 'week': return days / 7 + this._milliseconds / 6048e5; + case 'day': return days + this._milliseconds / 864e5; + case 'hour': return days * 24 + this._milliseconds / 36e5; + case 'minute': return days * 24 * 60 + this._milliseconds / 6e4; + case 'second': return days * 24 * 60 * 60 + this._milliseconds / 1000; + // Math.floor prevents floating point math errors here + case 'millisecond': return Math.floor(days * 24 * 60 * 60 * 1000) + this._milliseconds; + default: throw new Error('Unknown unit ' + units); + } + } + }, - // start the line - if (this.dataPoints.length > 0) { - point = this.dataPoints[0]; + lang : moment.fn.lang, + locale : moment.fn.locale, - ctx.lineWidth = 1; // TODO: make customizable - ctx.strokeStyle = 'blue'; // TODO: make customizable - ctx.beginPath(); - ctx.moveTo(point.screen.x, point.screen.y); - } + toIsoString : deprecate( + 'toIsoString() is deprecated. Please use toISOString() instead ' + + '(notice the capitals)', + function () { + return this.toISOString(); + } + ), - // draw the datapoints as colored circles - for (i = 1; i < this.dataPoints.length; i++) { - point = this.dataPoints[i]; - ctx.lineTo(point.screen.x, point.screen.y); - } + toISOString : function () { + // inspired by https://github.com/dordille/moment-isoduration/blob/master/moment.isoduration.js + var years = Math.abs(this.years()), + months = Math.abs(this.months()), + days = Math.abs(this.days()), + hours = Math.abs(this.hours()), + minutes = Math.abs(this.minutes()), + seconds = Math.abs(this.seconds() + this.milliseconds() / 1000); - // finish the line - if (this.dataPoints.length > 0) { - ctx.stroke(); - } - }; + if (!this.asSeconds()) { + // this is the same as C#'s (Noda) and python (isodate)... + // but not other JS (goog.date) + return 'P0D'; + } - /** - * Start a moving operation inside the provided parent element - * @param {Event} event The event that occurred (required for - * retrieving the mouse position) - */ - Graph3d.prototype._onMouseDown = function(event) { - event = event || window.event; + return (this.asSeconds() < 0 ? '-' : '') + + 'P' + + (years ? years + 'Y' : '') + + (months ? months + 'M' : '') + + (days ? days + 'D' : '') + + ((hours || minutes || seconds) ? 'T' : '') + + (hours ? hours + 'H' : '') + + (minutes ? minutes + 'M' : '') + + (seconds ? seconds + 'S' : ''); + }, - // check if mouse is still down (may be up when focus is lost for example - // in an iframe) - if (this.leftButtonDown) { - this._onMouseUp(event); - } + localeData : function () { + return this._locale; + }, - // only react on left mouse button down - this.leftButtonDown = event.which ? (event.which === 1) : (event.button === 1); - if (!this.leftButtonDown && !this.touchDown) return; + toJSON : function () { + return this.toISOString(); + } + }); - // get mouse position (different code for IE and all other browsers) - this.startMouseX = getMouseX(event); - this.startMouseY = getMouseY(event); + moment.duration.fn.toString = moment.duration.fn.toISOString; - this.startStart = new Date(this.start); - this.startEnd = new Date(this.end); - this.startArmRotation = this.camera.getArmRotation(); + function makeDurationGetter(name) { + moment.duration.fn[name] = function () { + return this._data[name]; + }; + } - this.frame.style.cursor = 'move'; + for (i in unitMillisecondFactors) { + if (hasOwnProp(unitMillisecondFactors, i)) { + makeDurationGetter(i.toLowerCase()); + } + } - // add event listeners to handle moving the contents - // we store the function onmousemove and onmouseup in the graph, so we can - // remove the eventlisteners lateron in the function mouseUp() - var me = this; - this.onmousemove = function (event) {me._onMouseMove(event);}; - this.onmouseup = function (event) {me._onMouseUp(event);}; - util.addEventListener(document, 'mousemove', me.onmousemove); - util.addEventListener(document, 'mouseup', me.onmouseup); - util.preventDefault(event); - }; + moment.duration.fn.asMilliseconds = function () { + return this.as('ms'); + }; + moment.duration.fn.asSeconds = function () { + return this.as('s'); + }; + moment.duration.fn.asMinutes = function () { + return this.as('m'); + }; + moment.duration.fn.asHours = function () { + return this.as('h'); + }; + moment.duration.fn.asDays = function () { + return this.as('d'); + }; + moment.duration.fn.asWeeks = function () { + return this.as('weeks'); + }; + moment.duration.fn.asMonths = function () { + return this.as('M'); + }; + moment.duration.fn.asYears = function () { + return this.as('y'); + }; + /************************************ + Default Locale + ************************************/ - /** - * Perform moving operating. - * This function activated from within the funcion Graph.mouseDown(). - * @param {Event} event Well, eehh, the event - */ - Graph3d.prototype._onMouseMove = function (event) { - event = event || window.event; - // calculate change in mouse position - var diffX = parseFloat(getMouseX(event)) - this.startMouseX; - var diffY = parseFloat(getMouseY(event)) - this.startMouseY; + // Set default locale, other locale will inherit from English. + moment.locale('en', { + ordinalParse: /\d{1,2}(th|st|nd|rd)/, + ordinal : function (number) { + var b = number % 10, + output = (toInt(number % 100 / 10) === 1) ? 'th' : + (b === 1) ? 'st' : + (b === 2) ? 'nd' : + (b === 3) ? 'rd' : 'th'; + return number + output; + } + }); - var horizontalNew = this.startArmRotation.horizontal + diffX / 200; - var verticalNew = this.startArmRotation.vertical + diffY / 200; + /* EMBED_LOCALES */ - var snapAngle = 4; // degrees - var snapValue = Math.sin(snapAngle / 360 * 2 * Math.PI); + /************************************ + Exposing Moment + ************************************/ - // snap horizontally to nice angles at 0pi, 0.5pi, 1pi, 1.5pi, etc... - // the -0.001 is to take care that the vertical axis is always drawn at the left front corner - if (Math.abs(Math.sin(horizontalNew)) < snapValue) { - horizontalNew = Math.round((horizontalNew / Math.PI)) * Math.PI - 0.001; - } - if (Math.abs(Math.cos(horizontalNew)) < snapValue) { - horizontalNew = (Math.round((horizontalNew/ Math.PI - 0.5)) + 0.5) * Math.PI - 0.001; - } + function makeGlobal(shouldDeprecate) { + /*global ender:false */ + if (typeof ender !== 'undefined') { + return; + } + oldGlobalMoment = globalScope.moment; + if (shouldDeprecate) { + globalScope.moment = deprecate( + 'Accessing Moment through the global scope is ' + + 'deprecated, and will be removed in an upcoming ' + + 'release.', + moment); + } else { + globalScope.moment = moment; + } + } - // snap vertically to nice angles - if (Math.abs(Math.sin(verticalNew)) < snapValue) { - verticalNew = Math.round((verticalNew / Math.PI)) * Math.PI; - } - if (Math.abs(Math.cos(verticalNew)) < snapValue) { - verticalNew = (Math.round((verticalNew/ Math.PI - 0.5)) + 0.5) * Math.PI; - } + // CommonJS module is defined + if (hasModule) { + module.exports = moment; + } else if (true) { + !(__WEBPACK_AMD_DEFINE_RESULT__ = function (require, exports, module) { + if (module.config && module.config() && module.config().noGlobal === true) { + // release the global variable + globalScope.moment = oldGlobalMoment; + } - this.camera.setArmRotation(horizontalNew, verticalNew); - this.redraw(); + return moment; + }.call(exports, __webpack_require__, exports, module), __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__)); + makeGlobal(true); + } else { + makeGlobal(); + } + }).call(this); + + /* WEBPACK VAR INJECTION */}.call(exports, (function() { return this; }()), __webpack_require__(5)(module))) - // fire a cameraPositionChange event - var parameters = this.getCameraPosition(); - this.emit('cameraPositionChange', parameters); +/***/ }, +/* 4 */ +/***/ function(module, exports, __webpack_require__) { - util.preventDefault(event); - }; + function webpackContext(req) { + throw new Error("Cannot find module '" + req + "'."); + } + webpackContext.keys = function() { return []; }; + webpackContext.resolve = webpackContext; + module.exports = webpackContext; + webpackContext.id = 4; + + +/***/ }, +/* 5 */ +/***/ 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; + } + + +/***/ }, +/* 6 */ +/***/ function(module, exports, __webpack_require__) { + // DOM utility methods /** - * Stop moving operating. - * This function activated from within the funcion Graph.mouseDown(). - * @param {event} event The event + * this prepares the JSON container for allocating SVG elements + * @param JSONcontainer + * @private */ - Graph3d.prototype._onMouseUp = function (event) { - this.frame.style.cursor = 'auto'; - this.leftButtonDown = false; - - // remove event listeners here - util.removeEventListener(document, 'mousemove', this.onmousemove); - util.removeEventListener(document, 'mouseup', this.onmouseup); - util.preventDefault(event); + exports.prepareElements = function(JSONcontainer) { + // cleanup the redundant svgElements; + for (var elementType in JSONcontainer) { + if (JSONcontainer.hasOwnProperty(elementType)) { + JSONcontainer[elementType].redundant = JSONcontainer[elementType].used; + JSONcontainer[elementType].used = []; + } + } }; /** - * After having moved the mouse, a tooltip should pop up when the mouse is resting on a data point - * @param {Event} event A mouse move event + * this cleans up all the unused SVG elements. By asking for the parentNode, we only need to supply the JSON container from + * which to remove the redundant elements. + * + * @param JSONcontainer + * @private */ - Graph3d.prototype._onTooltip = function (event) { - var delay = 300; // ms - var boundingRect = this.frame.getBoundingClientRect(); - var mouseX = getMouseX(event) - boundingRect.left; - var mouseY = getMouseY(event) - boundingRect.top; - - if (!this.showTooltip) { - return; + exports.cleanupElements = function(JSONcontainer) { + // cleanup the redundant svgElements; + for (var elementType in JSONcontainer) { + if (JSONcontainer.hasOwnProperty(elementType)) { + if (JSONcontainer[elementType].redundant) { + for (var i = 0; i < JSONcontainer[elementType].redundant.length; i++) { + JSONcontainer[elementType].redundant[i].parentNode.removeChild(JSONcontainer[elementType].redundant[i]); + } + JSONcontainer[elementType].redundant = []; + } + } } + }; - if (this.tooltipTimeout) { - clearTimeout(this.tooltipTimeout); + /** + * Allocate or generate an SVG element if needed. Store a reference to it in the JSON container and draw it in the svgContainer + * the JSON container and the SVG container have to be supplied so other svg containers (like the legend) can use this. + * + * @param elementType + * @param JSONcontainer + * @param svgContainer + * @returns {*} + * @private + */ + exports.getSVGElement = function (elementType, JSONcontainer, svgContainer) { + var element; + // allocate SVG element, if it doesnt yet exist, create one. + if (JSONcontainer.hasOwnProperty(elementType)) { // this element has been created before + // check if there is an redundant element + if (JSONcontainer[elementType].redundant.length > 0) { + element = JSONcontainer[elementType].redundant[0]; + JSONcontainer[elementType].redundant.shift(); + } + else { + // create a new element and add it to the SVG + element = document.createElementNS('http://www.w3.org/2000/svg', elementType); + svgContainer.appendChild(element); + } } - - // (delayed) display of a tooltip only if no mouse button is down - if (this.leftButtonDown) { - this._hideTooltip(); - return; + else { + // create a new element and add it to the SVG, also create a new object in the svgElements to keep track of it. + element = document.createElementNS('http://www.w3.org/2000/svg', elementType); + JSONcontainer[elementType] = {used: [], redundant: []}; + svgContainer.appendChild(element); } + JSONcontainer[elementType].used.push(element); + return element; + }; - if (this.tooltip && this.tooltip.dataPoint) { - // tooltip is currently visible - var dataPoint = this._dataPointFromXY(mouseX, mouseY); - if (dataPoint !== this.tooltip.dataPoint) { - // datapoint changed - if (dataPoint) { - this._showTooltip(dataPoint); + + /** + * Allocate or generate an SVG element if needed. Store a reference to it in the JSON container and draw it in the svgContainer + * the JSON container and the SVG container have to be supplied so other svg containers (like the legend) can use this. + * + * @param elementType + * @param JSONcontainer + * @param DOMContainer + * @returns {*} + * @private + */ + exports.getDOMElement = function (elementType, JSONcontainer, DOMContainer, insertBefore) { + var element; + // allocate DOM element, if it doesnt yet exist, create one. + if (JSONcontainer.hasOwnProperty(elementType)) { // this element has been created before + // check if there is an redundant element + if (JSONcontainer[elementType].redundant.length > 0) { + element = JSONcontainer[elementType].redundant[0]; + JSONcontainer[elementType].redundant.shift(); + } + else { + // create a new element and add it to the SVG + element = document.createElement(elementType); + if (insertBefore !== undefined) { + DOMContainer.insertBefore(element, insertBefore); } else { - this._hideTooltip(); + DOMContainer.appendChild(element); } } } else { - // tooltip is currently not visible - var me = this; - this.tooltipTimeout = setTimeout(function () { - me.tooltipTimeout = null; - - // show a tooltip if we have a data point - var dataPoint = me._dataPointFromXY(mouseX, mouseY); - if (dataPoint) { - me._showTooltip(dataPoint); - } - }, delay); + // create a new element and add it to the SVG, also create a new object in the svgElements to keep track of it. + element = document.createElement(elementType); + JSONcontainer[elementType] = {used: [], redundant: []}; + if (insertBefore !== undefined) { + DOMContainer.insertBefore(element, insertBefore); + } + else { + DOMContainer.appendChild(element); + } } + JSONcontainer[elementType].used.push(element); + return element; }; - /** - * Event handler for touchstart event on mobile devices - */ - Graph3d.prototype._onTouchStart = function(event) { - this.touchDown = true; - var me = this; - this.ontouchmove = function (event) {me._onTouchMove(event);}; - this.ontouchend = function (event) {me._onTouchEnd(event);}; - util.addEventListener(document, 'touchmove', me.ontouchmove); - util.addEventListener(document, 'touchend', me.ontouchend); - this._onMouseDown(event); - }; /** - * Event handler for touchmove event on mobile devices + * draw a point object. this is a seperate function because it can also be called by the legend. + * The reason the JSONcontainer and the target SVG svgContainer have to be supplied is so the legend can use these functions + * as well. + * + * @param x + * @param y + * @param group + * @param JSONcontainer + * @param svgContainer + * @returns {*} */ - Graph3d.prototype._onTouchMove = function(event) { - this._onMouseMove(event); + exports.drawPoint = function(x, y, group, JSONcontainer, svgContainer) { + var point; + if (group.options.drawPoints.style == 'circle') { + point = exports.getSVGElement('circle',JSONcontainer,svgContainer); + point.setAttributeNS(null, "cx", x); + point.setAttributeNS(null, "cy", y); + point.setAttributeNS(null, "r", 0.5 * group.options.drawPoints.size); + } + else { + point = exports.getSVGElement('rect',JSONcontainer,svgContainer); + point.setAttributeNS(null, "x", x - 0.5*group.options.drawPoints.size); + point.setAttributeNS(null, "y", y - 0.5*group.options.drawPoints.size); + point.setAttributeNS(null, "width", group.options.drawPoints.size); + point.setAttributeNS(null, "height", group.options.drawPoints.size); + } + + if(group.options.drawPoints.styles !== undefined) { + point.setAttributeNS(null, "style", group.group.options.drawPoints.styles); + } + point.setAttributeNS(null, "class", group.className + " point"); + return point; }; /** - * Event handler for touchend event on mobile devices + * draw a bar SVG element centered on the X coordinate + * + * @param x + * @param y + * @param className */ - Graph3d.prototype._onTouchEnd = function(event) { - this.touchDown = false; - - util.removeEventListener(document, 'touchmove', this.ontouchmove); - util.removeEventListener(document, 'touchend', this.ontouchend); - - this._onMouseUp(event); + exports.drawBar = function (x, y, width, height, className, JSONcontainer, svgContainer) { + if (height != 0) { + if (height < 0) { + height *= -1; + y -= height; + } + var rect = exports.getSVGElement('rect',JSONcontainer, svgContainer); + rect.setAttributeNS(null, "x", x - 0.5 * width); + rect.setAttributeNS(null, "y", y); + rect.setAttributeNS(null, "width", width); + rect.setAttributeNS(null, "height", height); + rect.setAttributeNS(null, "class", className); + } }; +/***/ }, +/* 7 */ +/***/ function(module, exports, __webpack_require__) { + + var util = __webpack_require__(1); + var Queue = __webpack_require__(8); /** - * Event handler for mouse wheel event, used to zoom the graph - * Code from http://adomas.org/javascript-mouse-wheel/ - * @param {event} event The event + * DataSet + * + * Usage: + * var dataSet = new DataSet({ + * fieldId: '_id', + * type: { + * // ... + * } + * }); + * + * dataSet.add(item); + * dataSet.add(data); + * dataSet.update(item); + * dataSet.update(data); + * dataSet.remove(id); + * dataSet.remove(ids); + * var data = dataSet.get(); + * var data = dataSet.get(id); + * var data = dataSet.get(ids); + * var data = dataSet.get(ids, options, data); + * dataSet.clear(); + * + * A data set can: + * - add/remove/update data + * - gives triggers upon changes in the data + * - can import/export data in various data formats + * + * @param {Array | DataTable} [data] Optional array with initial data + * @param {Object} [options] Available options: + * {String} fieldId Field name of the id in the + * items, 'id' by default. + * {Object. 0 ? 1 : x < 0 ? -1 : 0; + // add initial data when provided + if (data) { + this.add(data); } - var as = sign((b.x - a.x) * (point.y - a.y) - (b.y - a.y) * (point.x - a.x)); - var bs = sign((c.x - b.x) * (point.y - b.y) - (c.y - b.y) * (point.x - b.x)); - var cs = sign((a.x - c.x) * (point.y - c.y) - (a.y - c.y) * (point.x - c.x)); - - // each of the three signs must be either equal to each other or zero - return (as == 0 || bs == 0 || as == bs) && - (bs == 0 || cs == 0 || bs == cs) && - (as == 0 || cs == 0 || as == cs); - }; + this.setOptions(options); + } /** - * Find a data point close to given screen position (x, y) - * @param {Number} x - * @param {Number} y - * @return {Object | null} The closest data point or null if not close to any data point - * @private - */ - Graph3d.prototype._dataPointFromXY = function (x, y) { - var i, - distMax = 100, // px - dataPoint = null, - closestDataPoint = null, - closestDist = null, - center = new Point2d(x, y); - - if (this.style === Graph3d.STYLE.BAR || - this.style === Graph3d.STYLE.BARCOLOR || - this.style === Graph3d.STYLE.BARSIZE) { - // the data points are ordered from far away to closest - for (i = this.dataPoints.length - 1; i >= 0; i--) { - dataPoint = this.dataPoints[i]; - var surfaces = dataPoint.surfaces; - if (surfaces) { - for (var s = surfaces.length - 1; s >= 0; s--) { - // split each surface in two triangles, and see if the center point is inside one of these - var surface = surfaces[s]; - var corners = surface.corners; - var triangle1 = [corners[0].screen, corners[1].screen, corners[2].screen]; - var triangle2 = [corners[2].screen, corners[3].screen, corners[0].screen]; - if (this._insideTriangle(center, triangle1) || - this._insideTriangle(center, triangle2)) { - // return immediately at the first hit - return dataPoint; - } - } + * @param {Object} [options] Available options: + * {Object} queue Queue changes to the DataSet, + * flush them all at once. + * Queue options: + * - {number} delay Delay in ms, null by default + * - {number} max Maximum number of entries in the queue, Infinity by default + * @param options + */ + DataSet.prototype.setOptions = function(options) { + if (options && options.queue !== undefined) { + if (options.queue === false) { + // delete queue if loaded + if (this._queue) { + this._queue.destroy(); + delete this._queue; } } - } - else { - // find the closest data point, using distance to the center of the point on 2d screen - for (i = 0; i < this.dataPoints.length; i++) { - dataPoint = this.dataPoints[i]; - var point = dataPoint.screen; - if (point) { - var distX = Math.abs(x - point.x); - var distY = Math.abs(y - point.y); - var dist = Math.sqrt(distX * distX + distY * distY); + else { + // create queue and update its options + if (!this._queue) { + this._queue = Queue.extend(this, { + replace: ['add', 'update', 'remove'] + }); + } - if ((closestDist === null || dist < closestDist) && dist < distMax) { - closestDist = dist; - closestDataPoint = dataPoint; - } + if (typeof options.queue === 'object') { + this._queue.setOptions(options.queue); } } } - - - return closestDataPoint; }; /** - * Display a tooltip for given data point - * @param {Object} dataPoint - * @private + * Subscribe to an event, add an event listener + * @param {String} event Event name. Available events: 'put', 'update', + * 'remove' + * @param {function} callback Callback method. Called with three parameters: + * {String} event + * {Object | null} params + * {String | Number} senderId */ - Graph3d.prototype._showTooltip = function (dataPoint) { - var content, line, dot; - - if (!this.tooltip) { - content = document.createElement('div'); - content.style.position = 'absolute'; - content.style.padding = '10px'; - content.style.border = '1px solid #4d4d4d'; - content.style.color = '#1a1a1a'; - content.style.background = 'rgba(255,255,255,0.7)'; - content.style.borderRadius = '2px'; - content.style.boxShadow = '5px 5px 10px rgba(128,128,128,0.5)'; - - line = document.createElement('div'); - line.style.position = 'absolute'; - line.style.height = '40px'; - line.style.width = '0'; - line.style.borderLeft = '1px solid #4d4d4d'; - - dot = document.createElement('div'); - dot.style.position = 'absolute'; - dot.style.height = '0'; - dot.style.width = '0'; - dot.style.border = '5px solid #4d4d4d'; - dot.style.borderRadius = '5px'; - - this.tooltip = { - dataPoint: null, - dom: { - content: content, - line: line, - dot: dot - } - }; - } - else { - content = this.tooltip.dom.content; - line = this.tooltip.dom.line; - dot = this.tooltip.dom.dot; - } - - this._hideTooltip(); - - this.tooltip.dataPoint = dataPoint; - if (typeof this.showTooltip === 'function') { - content.innerHTML = this.showTooltip(dataPoint.point); - } - else { - content.innerHTML = '' + - '' + - '' + - '' + - '
x:' + dataPoint.point.x + '
y:' + dataPoint.point.y + '
z:' + dataPoint.point.z + '
'; + DataSet.prototype.on = function(event, callback) { + var subscribers = this._subscribers[event]; + if (!subscribers) { + subscribers = []; + this._subscribers[event] = subscribers; } - content.style.left = '0'; - content.style.top = '0'; - this.frame.appendChild(content); - this.frame.appendChild(line); - this.frame.appendChild(dot); - - // calculate sizes - var contentWidth = content.offsetWidth; - var contentHeight = content.offsetHeight; - var lineHeight = line.offsetHeight; - var dotWidth = dot.offsetWidth; - var dotHeight = dot.offsetHeight; - - var left = dataPoint.screen.x - contentWidth / 2; - left = Math.min(Math.max(left, 10), this.frame.clientWidth - 10 - contentWidth); - - line.style.left = dataPoint.screen.x + 'px'; - line.style.top = (dataPoint.screen.y - lineHeight) + 'px'; - content.style.left = left + 'px'; - content.style.top = (dataPoint.screen.y - lineHeight - contentHeight) + 'px'; - dot.style.left = (dataPoint.screen.x - dotWidth / 2) + 'px'; - dot.style.top = (dataPoint.screen.y - dotHeight / 2) + 'px'; + subscribers.push({ + callback: callback + }); }; + // TODO: make this function deprecated (replaced with `on` since version 0.5) + DataSet.prototype.subscribe = DataSet.prototype.on; + /** - * Hide the tooltip when displayed - * @private + * Unsubscribe from an event, remove an event listener + * @param {String} event + * @param {function} callback */ - Graph3d.prototype._hideTooltip = function () { - if (this.tooltip) { - this.tooltip.dataPoint = null; - - for (var prop in this.tooltip.dom) { - if (this.tooltip.dom.hasOwnProperty(prop)) { - var elem = this.tooltip.dom[prop]; - if (elem && elem.parentNode) { - elem.parentNode.removeChild(elem); - } - } - } + DataSet.prototype.off = function(event, callback) { + var subscribers = this._subscribers[event]; + if (subscribers) { + this._subscribers[event] = subscribers.filter(function (listener) { + return (listener.callback != callback); + }); } }; - /**--------------------------------------------------------------------------**/ - - - /** - * Get the horizontal mouse position from a mouse event - * @param {Event} event - * @return {Number} mouse x - */ - function getMouseX (event) { - if ('clientX' in event) return event.clientX; - return event.targetTouches[0] && event.targetTouches[0].clientX || 0; - } - - /** - * Get the vertical mouse position from a mouse event - * @param {Event} event - * @return {Number} mouse y - */ - function getMouseY (event) { - if ('clientY' in event) return event.clientY; - return event.targetTouches[0] && event.targetTouches[0].clientY || 0; - } - - module.exports = Graph3d; - - -/***/ }, -/* 7 */ -/***/ function(module, exports, __webpack_require__) { - - var Point3d = __webpack_require__(10); - - /** - * @class Camera - * The camera is mounted on a (virtual) camera arm. The camera arm can rotate - * The camera is always looking in the direction of the origin of the arm. - * This way, the camera always rotates around one fixed point, the location - * of the camera arm. - * - * Documentation: - * http://en.wikipedia.org/wiki/3D_projection - */ - function Camera() { - this.armLocation = new Point3d(); - this.armRotation = {}; - this.armRotation.horizontal = 0; - this.armRotation.vertical = 0; - this.armLength = 1.7; - - this.cameraLocation = new Point3d(); - this.cameraRotation = new Point3d(0.5*Math.PI, 0, 0); - - this.calculateCameraOrientation(); - } - - /** - * Set the location (origin) of the arm - * @param {Number} x Normalized value of x - * @param {Number} y Normalized value of y - * @param {Number} z Normalized value of z - */ - Camera.prototype.setArmLocation = function(x, y, z) { - this.armLocation.x = x; - this.armLocation.y = y; - this.armLocation.z = z; - - this.calculateCameraOrientation(); - }; + // TODO: make this function deprecated (replaced with `on` since version 0.5) + DataSet.prototype.unsubscribe = DataSet.prototype.off; /** - * Set the rotation of the camera arm - * @param {Number} horizontal The horizontal rotation, between 0 and 2*PI. - * Optional, can be left undefined. - * @param {Number} vertical The vertical rotation, between 0 and 0.5*PI - * if vertical=0.5*PI, the graph is shown from the - * top. Optional, can be left undefined. + * Trigger an event + * @param {String} event + * @param {Object | null} params + * @param {String} [senderId] Optional id of the sender. + * @private */ - Camera.prototype.setArmRotation = function(horizontal, vertical) { - if (horizontal !== undefined) { - this.armRotation.horizontal = horizontal; + DataSet.prototype._trigger = function (event, params, senderId) { + if (event == '*') { + throw new Error('Cannot trigger event *'); } - if (vertical !== undefined) { - this.armRotation.vertical = vertical; - if (this.armRotation.vertical < 0) this.armRotation.vertical = 0; - if (this.armRotation.vertical > 0.5*Math.PI) this.armRotation.vertical = 0.5*Math.PI; + var subscribers = []; + if (event in this._subscribers) { + subscribers = subscribers.concat(this._subscribers[event]); } - - if (horizontal !== undefined || vertical !== undefined) { - this.calculateCameraOrientation(); + if ('*' in this._subscribers) { + subscribers = subscribers.concat(this._subscribers['*']); } - }; - - /** - * Retrieve the current arm rotation - * @return {object} An object with parameters horizontal and vertical - */ - Camera.prototype.getArmRotation = function() { - var rot = {}; - rot.horizontal = this.armRotation.horizontal; - rot.vertical = this.armRotation.vertical; - - return rot; - }; - - /** - * Set the (normalized) length of the camera arm. - * @param {Number} length A length between 0.71 and 5.0 - */ - Camera.prototype.setArmLength = function(length) { - if (length === undefined) - return; - - this.armLength = length; - - // Radius must be larger than the corner of the graph, - // which has a distance of sqrt(0.5^2+0.5^2) = 0.71 from the center of the - // graph - if (this.armLength < 0.71) this.armLength = 0.71; - if (this.armLength > 5.0) this.armLength = 5.0; - this.calculateCameraOrientation(); + for (var i = 0; i < subscribers.length; i++) { + var subscriber = subscribers[i]; + if (subscriber.callback) { + subscriber.callback(event, params, senderId || null); + } + } }; /** - * Retrieve the arm length - * @return {Number} length + * Add data. + * Adding an item will fail when there already is an item with the same id. + * @param {Object | Array | DataTable} data + * @param {String} [senderId] Optional sender id + * @return {Array} addedIds Array with the ids of the added items */ - Camera.prototype.getArmLength = function() { - return this.armLength; - }; + DataSet.prototype.add = function (data, senderId) { + var addedIds = [], + id, + me = this; - /** - * Retrieve the camera location - * @return {Point3d} cameraLocation - */ - Camera.prototype.getCameraLocation = function() { - return this.cameraLocation; - }; + if (Array.isArray(data)) { + // Array + for (var i = 0, len = data.length; i < len; i++) { + id = me._addItem(data[i]); + addedIds.push(id); + } + } + else if (util.isDataTable(data)) { + // Google DataTable + var columns = this._getColumnNames(data); + for (var row = 0, rows = data.getNumberOfRows(); row < rows; row++) { + var item = {}; + for (var col = 0, cols = columns.length; col < cols; col++) { + var field = columns[col]; + item[field] = data.getValue(row, col); + } - /** - * Retrieve the camera rotation - * @return {Point3d} cameraRotation - */ - Camera.prototype.getCameraRotation = function() { - return this.cameraRotation; - }; + id = me._addItem(item); + addedIds.push(id); + } + } + else if (data instanceof Object) { + // Single item + id = me._addItem(data); + addedIds.push(id); + } + else { + throw new Error('Unknown dataType'); + } - /** - * Calculate the location and rotation of the camera based on the - * position and orientation of the camera arm - */ - Camera.prototype.calculateCameraOrientation = function() { - // calculate location of the camera - this.cameraLocation.x = this.armLocation.x - this.armLength * Math.sin(this.armRotation.horizontal) * Math.cos(this.armRotation.vertical); - this.cameraLocation.y = this.armLocation.y - this.armLength * Math.cos(this.armRotation.horizontal) * Math.cos(this.armRotation.vertical); - this.cameraLocation.z = this.armLocation.z + this.armLength * Math.sin(this.armRotation.vertical); + if (addedIds.length) { + this._trigger('add', {items: addedIds}, senderId); + } - // calculate rotation of the camera - this.cameraRotation.x = Math.PI/2 - this.armRotation.vertical; - this.cameraRotation.y = 0; - this.cameraRotation.z = -this.armRotation.horizontal; + return addedIds; }; - module.exports = Camera; - -/***/ }, -/* 8 */ -/***/ function(module, exports, __webpack_require__) { - - var DataView = __webpack_require__(4); - /** - * @class Filter - * - * @param {DataSet} data The google data table - * @param {Number} column The index of the column to be filtered - * @param {Graph} graph The graph + * Update existing items. When an item does not exist, it will be created + * @param {Object | Array | DataTable} data + * @param {String} [senderId] Optional sender id + * @return {Array} updatedIds The ids of the added or updated items */ - function Filter (data, column, graph) { - this.data = data; - this.column = column; - this.graph = graph; // the parent graph - - this.index = undefined; - this.value = undefined; - - // read all distinct values and select the first one - this.values = graph.getDistinctValues(data.get(), this.column); + DataSet.prototype.update = function (data, senderId) { + var addedIds = []; + var updatedIds = []; + var updatedData = []; + var me = this; + var fieldId = me._fieldId; - // sort both numeric and string values correctly - this.values.sort(function (a, b) { - return a > b ? 1 : a < b ? -1 : 0; - }); + var addOrUpdate = function (item) { + var id = item[fieldId]; + if (me._data[id]) { + // update item + id = me._updateItem(item); + updatedIds.push(id); + updatedData.push(item); + } + else { + // add new item + id = me._addItem(item); + addedIds.push(id); + } + }; - if (this.values.length > 0) { - this.selectValue(0); + if (Array.isArray(data)) { + // Array + for (var i = 0, len = data.length; i < len; i++) { + addOrUpdate(data[i]); + } } + else if (util.isDataTable(data)) { + // Google DataTable + var columns = this._getColumnNames(data); + for (var row = 0, rows = data.getNumberOfRows(); row < rows; row++) { + var item = {}; + for (var col = 0, cols = columns.length; col < cols; col++) { + var field = columns[col]; + item[field] = data.getValue(row, col); + } - // create an array with the filtered datapoints. this will be loaded afterwards - this.dataPoints = []; - - this.loaded = false; - this.onLoadCallback = undefined; - - if (graph.animationPreload) { - this.loaded = false; - this.loadInBackground(); + addOrUpdate(item); + } + } + else if (data instanceof Object) { + // Single item + addOrUpdate(data); } else { - this.loaded = true; + throw new Error('Unknown dataType'); } - }; - - - /** - * Return the label - * @return {string} label - */ - Filter.prototype.isLoaded = function() { - return this.loaded; - }; - - - /** - * Return the loaded progress - * @return {Number} percentage between 0 and 100 - */ - Filter.prototype.getLoadedProgress = function() { - var len = this.values.length; - var i = 0; - while (this.dataPoints[i]) { - i++; + if (addedIds.length) { + this._trigger('add', {items: addedIds}, senderId); + } + if (updatedIds.length) { + this._trigger('update', {items: updatedIds, data: updatedData}, senderId); } - return Math.round(i / len * 100); + return addedIds.concat(updatedIds); }; - /** - * Return the label - * @return {string} label - */ - Filter.prototype.getLabel = function() { - return this.graph.filterLabel; - }; - - - /** - * Return the columnIndex of the filter - * @return {Number} columnIndex + * Get a data item or multiple items. + * + * Usage: + * + * get() + * get(options: Object) + * get(options: Object, data: Array | DataTable) + * + * get(id: Number | String) + * get(id: Number | String, options: Object) + * get(id: Number | String, options: Object, data: Array | DataTable) + * + * get(ids: Number[] | String[]) + * get(ids: Number[] | String[], options: Object) + * get(ids: Number[] | String[], options: Object, data: Array | DataTable) + * + * Where: + * + * {Number | String} id The id of an item + * {Number[] | String{}} ids An array with ids of items + * {Object} options An Object with options. Available options: + * {String} [returnType] Type of data to be + * returned. Can be 'DataTable' or 'Array' (default) + * {Object.} [type] + * {String[]} [fields] field names to be returned + * {function} [filter] filter items + * {String | function} [order] Order the items by + * a field name or custom sort function. + * {Array | DataTable} [data] If provided, items will be appended to this + * array or table. Required in case of Google + * DataTable. + * + * @throws Error */ - Filter.prototype.getColumn = function() { - return this.column; - }; + DataSet.prototype.get = function (args) { + var me = this; - /** - * Return the currently selected value. Returns undefined if there is no selection - * @return {*} value - */ - Filter.prototype.getSelectedValue = function() { - if (this.index === undefined) - return undefined; + // parse the arguments + var id, ids, options, data; + var firstType = util.getType(arguments[0]); + if (firstType == 'String' || firstType == 'Number') { + // get(id [, options] [, data]) + id = arguments[0]; + options = arguments[1]; + data = arguments[2]; + } + else if (firstType == 'Array') { + // get(ids [, options] [, data]) + ids = arguments[0]; + options = arguments[1]; + data = arguments[2]; + } + else { + // get([, options] [, data]) + options = arguments[0]; + data = arguments[1]; + } - return this.values[this.index]; - }; + // determine the return type + var returnType; + if (options && options.returnType) { + var allowedValues = ["DataTable", "Array", "Object"]; + returnType = allowedValues.indexOf(options.returnType) == -1 ? "Array" : options.returnType; - /** - * Retrieve all values of the filter - * @return {Array} values - */ - Filter.prototype.getValues = function() { - return this.values; - }; + if (data && (returnType != util.getType(data))) { + throw new Error('Type of parameter "data" (' + util.getType(data) + ') ' + + 'does not correspond with specified options.type (' + options.type + ')'); + } + if (returnType == 'DataTable' && !util.isDataTable(data)) { + throw new Error('Parameter "data" must be a DataTable ' + + 'when options.type is "DataTable"'); + } + } + else if (data) { + returnType = (util.getType(data) == 'DataTable') ? 'DataTable' : 'Array'; + } + else { + returnType = 'Array'; + } - /** - * Retrieve one value of the filter - * @param {Number} index - * @return {*} value - */ - Filter.prototype.getValue = function(index) { - if (index >= this.values.length) - throw 'Error: index out of range'; + // build options + var type = options && options.type || this._options.type; + var filter = options && options.filter; + var items = [], item, itemId, i, len; - return this.values[index]; - }; + // convert items + if (id != undefined) { + // return a single item + item = me._getItem(id, type); + if (filter && !filter(item)) { + item = null; + } + } + else if (ids != undefined) { + // return a subset of items + for (i = 0, len = ids.length; i < len; i++) { + item = me._getItem(ids[i], type); + if (!filter || filter(item)) { + items.push(item); + } + } + } + else { + // return all items + for (itemId in this._data) { + if (this._data.hasOwnProperty(itemId)) { + item = me._getItem(itemId, type); + if (!filter || filter(item)) { + items.push(item); + } + } + } + } + + // order the results + if (options && options.order && id == undefined) { + this._sort(items, options.order); + } + + // filter fields of the items + if (options && options.fields) { + var fields = options.fields; + if (id != undefined) { + item = this._filterFields(item, fields); + } + else { + for (i = 0, len = items.length; i < len; i++) { + items[i] = this._filterFields(items[i], fields); + } + } + } + // return the results + if (returnType == 'DataTable') { + var columns = this._getColumnNames(data); + if (id != undefined) { + // append a single item to the data table + me._appendRow(data, columns, item); + } + else { + // copy the items to the provided data table + for (i = 0; i < items.length; i++) { + me._appendRow(data, columns, items[i]); + } + } + return data; + } + else if (returnType == "Object") { + var result = {}; + for (i = 0; i < items.length; i++) { + result[items[i].id] = items[i]; + } + return result; + } + else { + // return an array + if (id != undefined) { + // a single item + return item; + } + else { + // multiple items + if (data) { + // copy the items to the provided array + for (i = 0, len = items.length; i < len; i++) { + data.push(items[i]); + } + return data; + } + else { + // just return our array + return items; + } + } + } + }; /** - * Retrieve the (filtered) dataPoints for the currently selected filter index - * @param {Number} [index] (optional) - * @return {Array} dataPoints + * Get ids of all items or from a filtered set of items. + * @param {Object} [options] An Object with options. Available options: + * {function} [filter] filter items + * {String | function} [order] Order the items by + * a field name or custom sort function. + * @return {Array} ids */ - Filter.prototype._getDataPoints = function(index) { - if (index === undefined) - index = this.index; + DataSet.prototype.getIds = function (options) { + var data = this._data, + filter = options && options.filter, + order = options && options.order, + type = options && options.type || this._options.type, + i, + len, + id, + item, + items, + ids = []; - if (index === undefined) - return []; + if (filter) { + // get filtered items + if (order) { + // create ordered list + items = []; + for (id in data) { + if (data.hasOwnProperty(id)) { + item = this._getItem(id, type); + if (filter(item)) { + items.push(item); + } + } + } - var dataPoints; - if (this.dataPoints[index]) { - dataPoints = this.dataPoints[index]; + this._sort(items, order); + + for (i = 0, len = items.length; i < len; i++) { + ids[i] = items[i][this._fieldId]; + } + } + else { + // create unordered list + for (id in data) { + if (data.hasOwnProperty(id)) { + item = this._getItem(id, type); + if (filter(item)) { + ids.push(item[this._fieldId]); + } + } + } + } } else { - var f = {}; - f.column = this.column; - f.value = this.values[index]; + // get all items + if (order) { + // create an ordered list + items = []; + for (id in data) { + if (data.hasOwnProperty(id)) { + items.push(data[id]); + } + } - var dataView = new DataView(this.data,{filter: function (item) {return (item[f.column] == f.value);}}).get(); - dataPoints = this.graph._getDataPoints(dataView); + this._sort(items, order); - this.dataPoints[index] = dataPoints; + for (i = 0, len = items.length; i < len; i++) { + ids[i] = items[i][this._fieldId]; + } + } + else { + // create unordered list + for (id in data) { + if (data.hasOwnProperty(id)) { + item = data[id]; + ids.push(item[this._fieldId]); + } + } + } } - return dataPoints; - }; - - - - /** - * Set a callback function when the filter is fully loaded. - */ - Filter.prototype.setOnLoadCallback = function(callback) { - this.onLoadCallback = callback; + return ids; }; - /** - * Add a value to the list with available values for this filter - * No double entries will be created. - * @param {Number} index + * Returns the DataSet itself. Is overwritten for example by the DataView, + * which returns the DataSet it is connected to instead. */ - Filter.prototype.selectValue = function(index) { - if (index >= this.values.length) - throw 'Error: index out of range'; - - this.index = index; - this.value = this.values[index]; + DataSet.prototype.getDataSet = function () { + return this; }; /** - * Load all filtered rows in the background one by one - * Start this method without providing an index! + * Execute a callback function for every item in the dataset. + * @param {function} callback + * @param {Object} [options] Available options: + * {Object.} [type] + * {String[]} [fields] filter fields + * {function} [filter] filter items + * {String | function} [order] Order the items by + * a field name or custom sort function. */ - Filter.prototype.loadInBackground = function(index) { - if (index === undefined) - index = 0; - - var frame = this.graph.frame; + DataSet.prototype.forEach = function (callback, options) { + var filter = options && options.filter, + type = options && options.type || this._options.type, + data = this._data, + item, + id; - if (index < this.values.length) { - var dataPointsTemp = this._getDataPoints(index); - //this.graph.redrawInfo(); // TODO: not neat + if (options && options.order) { + // execute forEach on ordered list + var items = this.get(options); - // create a progress box - if (frame.progress === undefined) { - frame.progress = document.createElement('DIV'); - frame.progress.style.position = 'absolute'; - frame.progress.style.color = 'gray'; - frame.appendChild(frame.progress); + for (var i = 0, len = items.length; i < len; i++) { + item = items[i]; + id = item[this._fieldId]; + callback(item, id); } - var progress = this.getLoadedProgress(); - frame.progress.innerHTML = 'Loading animation... ' + progress + '%'; - // TODO: this is no nice solution... - frame.progress.style.bottom = 60 + 'px'; // TODO: use height of slider - frame.progress.style.left = 10 + 'px'; - - var me = this; - setTimeout(function() {me.loadInBackground(index+1);}, 10); - this.loaded = false; } else { - this.loaded = true; - - // remove the progress box - if (frame.progress !== undefined) { - frame.removeChild(frame.progress); - frame.progress = undefined; + // unordered + for (id in data) { + if (data.hasOwnProperty(id)) { + item = this._getItem(id, type); + if (!filter || filter(item)) { + callback(item, id); + } + } } - - if (this.onLoadCallback) - this.onLoadCallback(); } }; - module.exports = Filter; - - -/***/ }, -/* 9 */ -/***/ function(module, exports, __webpack_require__) { - /** - * @prototype Point2d - * @param {Number} [x] - * @param {Number} [y] + * Map every item in the dataset. + * @param {function} callback + * @param {Object} [options] Available options: + * {Object.} [type] + * {String[]} [fields] filter fields + * {function} [filter] filter items + * {String | function} [order] Order the items by + * a field name or custom sort function. + * @return {Object[]} mappedItems */ - function Point2d (x, y) { - this.x = x !== undefined ? x : 0; - this.y = y !== undefined ? y : 0; - } - - module.exports = Point2d; + DataSet.prototype.map = function (callback, options) { + var filter = options && options.filter, + type = options && options.type || this._options.type, + mappedItems = [], + data = this._data, + item; + // convert and filter items + for (var id in data) { + if (data.hasOwnProperty(id)) { + item = this._getItem(id, type); + if (!filter || filter(item)) { + mappedItems.push(callback(item, id)); + } + } + } -/***/ }, -/* 10 */ -/***/ function(module, exports, __webpack_require__) { + // order items + if (options && options.order) { + this._sort(mappedItems, options.order); + } - /** - * @prototype Point3d - * @param {Number} [x] - * @param {Number} [y] - * @param {Number} [z] - */ - function Point3d(x, y, z) { - this.x = x !== undefined ? x : 0; - this.y = y !== undefined ? y : 0; - this.z = z !== undefined ? z : 0; + return mappedItems; }; /** - * Subtract the two provided points, returns a-b - * @param {Point3d} a - * @param {Point3d} b - * @return {Point3d} a-b + * Filter the fields of an item + * @param {Object} item + * @param {String[]} fields Field names + * @return {Object} filteredItem + * @private */ - Point3d.subtract = function(a, b) { - var sub = new Point3d(); - sub.x = a.x - b.x; - sub.y = a.y - b.y; - sub.z = a.z - b.z; - return sub; + DataSet.prototype._filterFields = function (item, fields) { + var filteredItem = {}; + + for (var field in item) { + if (item.hasOwnProperty(field) && (fields.indexOf(field) != -1)) { + filteredItem[field] = item[field]; + } + } + + return filteredItem; }; /** - * Add the two provided points, returns a+b - * @param {Point3d} a - * @param {Point3d} b - * @return {Point3d} a+b + * Sort the provided array with items + * @param {Object[]} items + * @param {String | function} order A field name or custom sort function. + * @private */ - Point3d.add = function(a, b) { - var sum = new Point3d(); - sum.x = a.x + b.x; - sum.y = a.y + b.y; - sum.z = a.z + b.z; - return sum; + DataSet.prototype._sort = function (items, order) { + if (util.isString(order)) { + // order by provided field name + var name = order; // field name + items.sort(function (a, b) { + var av = a[name]; + var bv = b[name]; + return (av > bv) ? 1 : ((av < bv) ? -1 : 0); + }); + } + else if (typeof order === 'function') { + // order by sort function + items.sort(order); + } + // TODO: extend order by an Object {field:String, direction:String} + // where direction can be 'asc' or 'desc' + else { + throw new TypeError('Order must be a function or a string'); + } }; /** - * Calculate the average of two 3d points - * @param {Point3d} a - * @param {Point3d} b - * @return {Point3d} The average, (a+b)/2 + * Remove an object by pointer or by id + * @param {String | Number | Object | Array} id Object or id, or an array with + * objects or ids to be removed + * @param {String} [senderId] Optional sender id + * @return {Array} removedIds */ - Point3d.avg = function(a, b) { - return new Point3d( - (a.x + b.x) / 2, - (a.y + b.y) / 2, - (a.z + b.z) / 2 - ); - }; + DataSet.prototype.remove = function (id, senderId) { + var removedIds = [], + i, len, removedId; - /** - * Calculate the cross product of the two provided points, returns axb - * Documentation: http://en.wikipedia.org/wiki/Cross_product - * @param {Point3d} a - * @param {Point3d} b - * @return {Point3d} cross product axb - */ - Point3d.crossProduct = function(a, b) { - var crossproduct = new Point3d(); + if (Array.isArray(id)) { + for (i = 0, len = id.length; i < len; i++) { + removedId = this._remove(id[i]); + if (removedId != null) { + removedIds.push(removedId); + } + } + } + else { + removedId = this._remove(id); + if (removedId != null) { + removedIds.push(removedId); + } + } - crossproduct.x = a.y * b.z - a.z * b.y; - crossproduct.y = a.z * b.x - a.x * b.z; - crossproduct.z = a.x * b.y - a.y * b.x; + if (removedIds.length) { + this._trigger('remove', {items: removedIds}, senderId); + } - return crossproduct; + return removedIds; }; - /** - * Rtrieve the length of the vector (or the distance from this point to the origin - * @return {Number} length + * Remove an item by its id + * @param {Number | String | Object} id id or item + * @returns {Number | String | null} id + * @private */ - Point3d.prototype.length = function() { - return Math.sqrt( - this.x * this.x + - this.y * this.y + - this.z * this.z - ); + DataSet.prototype._remove = function (id) { + if (util.isNumber(id) || util.isString(id)) { + if (this._data[id]) { + delete this._data[id]; + this.length--; + return id; + } + } + else if (id instanceof Object) { + var itemId = id[this._fieldId]; + if (itemId && this._data[itemId]) { + delete this._data[itemId]; + this.length--; + return itemId; + } + } + return null; }; - module.exports = Point3d; - - -/***/ }, -/* 11 */ -/***/ function(module, exports, __webpack_require__) { - - var util = __webpack_require__(1); - /** - * @constructor Slider - * - * An html slider control with start/stop/prev/next buttons - * @param {Element} container The element where the slider will be created - * @param {Object} options Available options: - * {boolean} visible If true (default) the - * slider is visible. + * Clear the data + * @param {String} [senderId] Optional sender id + * @return {Array} removedIds The ids of all removed items */ - function Slider(container, options) { - if (container === undefined) { - throw 'Error: No container element defined'; - } - this.container = container; - this.visible = (options && options.visible != undefined) ? options.visible : true; - - if (this.visible) { - this.frame = document.createElement('DIV'); - //this.frame.style.backgroundColor = '#E5E5E5'; - this.frame.style.width = '100%'; - this.frame.style.position = 'relative'; - this.container.appendChild(this.frame); - - this.frame.prev = document.createElement('INPUT'); - this.frame.prev.type = 'BUTTON'; - this.frame.prev.value = 'Prev'; - this.frame.appendChild(this.frame.prev); - - this.frame.play = document.createElement('INPUT'); - this.frame.play.type = 'BUTTON'; - this.frame.play.value = 'Play'; - this.frame.appendChild(this.frame.play); - - this.frame.next = document.createElement('INPUT'); - this.frame.next.type = 'BUTTON'; - this.frame.next.value = 'Next'; - this.frame.appendChild(this.frame.next); - - this.frame.bar = document.createElement('INPUT'); - this.frame.bar.type = 'BUTTON'; - this.frame.bar.style.position = 'absolute'; - this.frame.bar.style.border = '1px solid red'; - this.frame.bar.style.width = '100px'; - this.frame.bar.style.height = '6px'; - this.frame.bar.style.borderRadius = '2px'; - this.frame.bar.style.MozBorderRadius = '2px'; - this.frame.bar.style.border = '1px solid #7F7F7F'; - this.frame.bar.style.backgroundColor = '#E5E5E5'; - this.frame.appendChild(this.frame.bar); - - this.frame.slide = document.createElement('INPUT'); - this.frame.slide.type = 'BUTTON'; - this.frame.slide.style.margin = '0px'; - this.frame.slide.value = ' '; - this.frame.slide.style.position = 'relative'; - this.frame.slide.style.left = '-100px'; - this.frame.appendChild(this.frame.slide); - - // create events - var me = this; - this.frame.slide.onmousedown = function (event) {me._onMouseDown(event);}; - this.frame.prev.onclick = function (event) {me.prev(event);}; - this.frame.play.onclick = function (event) {me.togglePlay(event);}; - this.frame.next.onclick = function (event) {me.next(event);}; - } - - this.onChangeCallback = undefined; + DataSet.prototype.clear = function (senderId) { + var ids = Object.keys(this._data); - this.values = []; - this.index = undefined; + this._data = {}; + this.length = 0; - this.playTimeout = undefined; - this.playInterval = 1000; // milliseconds - this.playLoop = true; - } + this._trigger('remove', {items: ids}, senderId); - /** - * Select the previous index - */ - Slider.prototype.prev = function() { - var index = this.getIndex(); - if (index > 0) { - index--; - this.setIndex(index); - } + return ids; }; /** - * Select the next index + * Find the item with maximum value of a specified field + * @param {String} field + * @return {Object | null} item Item containing max value, or null if no items */ - Slider.prototype.next = function() { - var index = this.getIndex(); - if (index < this.values.length - 1) { - index++; - this.setIndex(index); + DataSet.prototype.max = function (field) { + var data = this._data, + max = null, + maxField = null; + + for (var id in data) { + if (data.hasOwnProperty(id)) { + var item = data[id]; + var itemField = item[field]; + if (itemField != null && (!max || itemField > maxField)) { + max = item; + maxField = itemField; + } + } } + + return max; }; /** - * Select the next index + * Find the item with minimum value of a specified field + * @param {String} field + * @return {Object | null} item Item containing max value, or null if no items */ - Slider.prototype.playNext = function() { - var start = new Date(); + DataSet.prototype.min = function (field) { + var data = this._data, + min = null, + minField = null; - var index = this.getIndex(); - if (index < this.values.length - 1) { - index++; - this.setIndex(index); - } - else if (this.playLoop) { - // jump to the start - index = 0; - this.setIndex(index); + for (var id in data) { + if (data.hasOwnProperty(id)) { + var item = data[id]; + var itemField = item[field]; + if (itemField != null && (!min || itemField < minField)) { + min = item; + minField = itemField; + } + } } - var end = new Date(); - var diff = (end - start); - - // calculate how much time it to to set the index and to execute the callback - // function. - var interval = Math.max(this.playInterval - diff, 0); - // document.title = diff // TODO: cleanup - - var me = this; - this.playTimeout = setTimeout(function() {me.playNext();}, interval); + return min; }; /** - * Toggle start or stop playing + * Find all distinct values of a specified field + * @param {String} field + * @return {Array} values Array containing all distinct values. If data items + * do not contain the specified field are ignored. + * The returned array is unordered. */ - Slider.prototype.togglePlay = function() { - if (this.playTimeout === undefined) { - this.play(); - } else { - this.stop(); + DataSet.prototype.distinct = function (field) { + var data = this._data; + var values = []; + var fieldType = this._options.type && this._options.type[field] || null; + var count = 0; + var i; + + for (var prop in data) { + if (data.hasOwnProperty(prop)) { + var item = data[prop]; + var value = item[field]; + var exists = false; + for (i = 0; i < count; i++) { + if (values[i] == value) { + exists = true; + break; + } + } + if (!exists && (value !== undefined)) { + values[count] = value; + count++; + } + } + } + + if (fieldType) { + for (i = 0; i < values.length; i++) { + values[i] = util.convert(values[i], fieldType); + } } + + return values; }; /** - * Start playing + * Add a single item. Will fail when an item with the same id already exists. + * @param {Object} item + * @return {String} id + * @private */ - Slider.prototype.play = function() { - // Test whether already playing - if (this.playTimeout) return; + DataSet.prototype._addItem = function (item) { + var id = item[this._fieldId]; - this.playNext(); + if (id != undefined) { + // check whether this id is already taken + if (this._data[id]) { + // item already exists + throw new Error('Cannot add item: item with id ' + id + ' already exists'); + } + } + else { + // generate an id + id = util.randomUUID(); + item[this._fieldId] = id; + } - if (this.frame) { - this.frame.play.value = 'Stop'; + var d = {}; + for (var field in item) { + if (item.hasOwnProperty(field)) { + var fieldType = this._type[field]; // type may be undefined + d[field] = util.convert(item[field], fieldType); + } } + this._data[id] = d; + this.length++; + + return id; }; /** - * Stop playing + * Get an item. Fields can be converted to a specific type + * @param {String} id + * @param {Object.} [types] field types to convert + * @return {Object | null} item + * @private */ - Slider.prototype.stop = function() { - clearInterval(this.playTimeout); - this.playTimeout = undefined; + DataSet.prototype._getItem = function (id, types) { + var field, value; - if (this.frame) { - this.frame.play.value = 'Play'; + // get the item from the dataset + var raw = this._data[id]; + if (!raw) { + return null; } - }; - /** - * Set a callback function which will be triggered when the value of the - * slider bar has changed. - */ - Slider.prototype.setOnChangeCallback = function(callback) { - this.onChangeCallback = callback; + // convert the items field types + var converted = {}; + if (types) { + for (field in raw) { + if (raw.hasOwnProperty(field)) { + value = raw[field]; + converted[field] = util.convert(value, types[field]); + } + } + } + else { + // no field types specified, no converting needed + for (field in raw) { + if (raw.hasOwnProperty(field)) { + value = raw[field]; + converted[field] = value; + } + } + } + return converted; }; /** - * Set the interval for playing the list - * @param {Number} interval The interval in milliseconds + * Update a single item: merge with existing item. + * Will fail when the item has no id, or when there does not exist an item + * with the same id. + * @param {Object} item + * @return {String} id + * @private */ - Slider.prototype.setPlayInterval = function(interval) { - this.playInterval = interval; - }; + DataSet.prototype._updateItem = function (item) { + var id = item[this._fieldId]; + if (id == undefined) { + throw new Error('Cannot update item: item has no id (item: ' + JSON.stringify(item) + ')'); + } + var d = this._data[id]; + if (!d) { + // item doesn't exist + throw new Error('Cannot update item: no item with id ' + id + ' found'); + } - /** - * Retrieve the current play interval - * @return {Number} interval The interval in milliseconds - */ - Slider.prototype.getPlayInterval = function(interval) { - return this.playInterval; - }; + // merge with current item + for (var field in item) { + if (item.hasOwnProperty(field)) { + var fieldType = this._type[field]; // type may be undefined + d[field] = util.convert(item[field], fieldType); + } + } - /** - * Set looping on or off - * @pararm {boolean} doLoop If true, the slider will jump to the start when - * the end is passed, and will jump to the end - * when the start is passed. - */ - Slider.prototype.setPlayLoop = function(doLoop) { - this.playLoop = doLoop; + return id; }; - /** - * Execute the onchange callback function + * Get an array with the column names of a Google DataTable + * @param {DataTable} dataTable + * @return {String[]} columnNames + * @private */ - Slider.prototype.onChange = function() { - if (this.onChangeCallback !== undefined) { - this.onChangeCallback(); + DataSet.prototype._getColumnNames = function (dataTable) { + var columns = []; + for (var col = 0, cols = dataTable.getNumberOfColumns(); col < cols; col++) { + columns[col] = dataTable.getColumnId(col) || dataTable.getColumnLabel(col); } + return columns; }; /** - * redraw the slider on the correct place + * Append an item as a row to the dataTable + * @param dataTable + * @param columns + * @param item + * @private */ - Slider.prototype.redraw = function() { - if (this.frame) { - // resize the bar - this.frame.bar.style.top = (this.frame.clientHeight/2 - - this.frame.bar.offsetHeight/2) + 'px'; - this.frame.bar.style.width = (this.frame.clientWidth - - this.frame.prev.clientWidth - - this.frame.play.clientWidth - - this.frame.next.clientWidth - 30) + 'px'; + DataSet.prototype._appendRow = function (dataTable, columns, item) { + var row = dataTable.addRow(); - // position the slider button - var left = this.indexToLeft(this.index); - this.frame.slide.style.left = (left) + 'px'; + for (var col = 0, cols = columns.length; col < cols; col++) { + var field = columns[col]; + dataTable.setValue(row, col, item[field]); } }; + module.exports = DataSet; + + +/***/ }, +/* 8 */ +/***/ function(module, exports, __webpack_require__) { /** - * Set the list with values for the slider - * @param {Array} values A javascript array with values (any type) + * A queue + * @param {Object} options + * Available options: + * - delay: number When provided, the queue will be flushed + * automatically after an inactivity of this delay + * in milliseconds. + * Default value is null. + * - max: number When the queue exceeds the given maximum number + * of entries, the queue is flushed automatically. + * Default value of max is Infinity. + * @constructor */ - Slider.prototype.setValues = function(values) { - this.values = values; + function Queue(options) { + // options + this.delay = null; + this.max = Infinity; - if (this.values.length > 0) - this.setIndex(0); - else - this.index = undefined; - }; + // properties + this._queue = []; + this._timeout = null; + this._extended = null; + + this.setOptions(options); + } /** - * Select a value by its index - * @param {Number} index + * Update the configuration of the queue + * @param {Object} options + * Available options: + * - delay: number When provided, the queue will be flushed + * automatically after an inactivity of this delay + * in milliseconds. + * Default value is null. + * - max: number When the queue exceeds the given maximum number + * of entries, the queue is flushed automatically. + * Default value of max is Infinity. + * @param options */ - Slider.prototype.setIndex = function(index) { - if (index < this.values.length) { - this.index = index; - - this.redraw(); - this.onChange(); + Queue.prototype.setOptions = function (options) { + if (options && typeof options.delay !== 'undefined') { + this.delay = options.delay; } - else { - throw 'Error: index out of range'; + if (options && typeof options.max !== 'undefined') { + this.max = options.max; } - }; - /** - * retrieve the index of the currently selected vaue - * @return {Number} index - */ - Slider.prototype.getIndex = function() { - return this.index; + this._flushIfNeeded(); }; - /** - * retrieve the currently selected value - * @return {*} value - */ - Slider.prototype.get = function() { - return this.values[this.index]; - }; - - - Slider.prototype._onMouseDown = function(event) { - // only react on left mouse button down - var leftButtonDown = event.which ? (event.which === 1) : (event.button === 1); - if (!leftButtonDown) return; - - this.startClientX = event.clientX; - this.startSlideX = parseFloat(this.frame.slide.style.left); - - this.frame.style.cursor = 'move'; + * Extend an object with queuing functionality. + * The object will be extended with a function flush, and the methods provided + * in options.replace will be replaced with queued ones. + * @param {Object} object + * @param {Object} options + * Available options: + * - replace: Array. + * A list with method names of the methods + * on the object to be replaced with queued ones. + * - delay: number When provided, the queue will be flushed + * automatically after an inactivity of this delay + * in milliseconds. + * Default value is null. + * - max: number When the queue exceeds the given maximum number + * of entries, the queue is flushed automatically. + * Default value of max is Infinity. + * @return {Queue} Returns the created queue + */ + Queue.extend = function (object, options) { + var queue = new Queue(options); - // add event listeners to handle moving the contents - // we store the function onmousemove and onmouseup in the graph, so we can - // remove the eventlisteners lateron in the function mouseUp() - var me = this; - this.onmousemove = function (event) {me._onMouseMove(event);}; - this.onmouseup = function (event) {me._onMouseUp(event);}; - util.addEventListener(document, 'mousemove', this.onmousemove); - util.addEventListener(document, 'mouseup', this.onmouseup); - util.preventDefault(event); - }; + if (object.flush !== undefined) { + throw new Error('Target object already has a property flush'); + } + object.flush = function () { + queue.flush(); + }; + var methods = [{ + name: 'flush', + original: undefined + }]; - Slider.prototype.leftToIndex = function (left) { - var width = parseFloat(this.frame.bar.style.width) - - this.frame.slide.clientWidth - 10; - var x = left - 3; + if (options && options.replace) { + for (var i = 0; i < options.replace.length; i++) { + var name = options.replace[i]; + methods.push({ + name: name, + original: object[name] + }); + queue.replace(object, name); + } + } - var index = Math.round(x / width * (this.values.length-1)); - if (index < 0) index = 0; - if (index > this.values.length-1) index = this.values.length-1; + queue._extended = { + object: object, + methods: methods + }; - return index; + return queue; }; - Slider.prototype.indexToLeft = function (index) { - var width = parseFloat(this.frame.bar.style.width) - - this.frame.slide.clientWidth - 10; - - var x = index / (this.values.length-1) * width; - var left = x + 3; + /** + * Destroy the queue. The queue will first flush all queued actions, and in + * case it has extended an object, will restore the original object. + */ + Queue.prototype.destroy = function () { + this.flush(); - return left; + if (this._extended) { + var object = this._extended.object; + var methods = this._extended.methods; + for (var i = 0; i < methods.length; i++) { + var method = methods[i]; + if (method.original) { + object[method.name] = method.original; + } + else { + delete object[method.name]; + } + } + this._extended = null; + } }; + /** + * Replace a method on an object with a queued version + * @param {Object} object Object having the method + * @param {string} method The method name + */ + Queue.prototype.replace = function(object, method) { + var me = this; + var original = object[method]; + if (!original) { + throw new Error('Method ' + method + ' undefined'); + } + object[method] = function () { + // create an Array with the arguments + var args = []; + for (var i = 0; i < arguments.length; i++) { + args[i] = arguments[i]; + } - Slider.prototype._onMouseMove = function (event) { - var diff = event.clientX - this.startClientX; - var x = this.startSlideX + diff; - - var index = this.leftToIndex(x); + // add this call to the queue + me.queue({ + args: args, + fn: original, + context: this + }); + }; + }; - this.setIndex(index); + /** + * Queue a call + * @param {function | {fn: function, args: Array} | {fn: function, args: Array, context: Object}} entry + */ + Queue.prototype.queue = function(entry) { + if (typeof entry === 'function') { + this._queue.push({fn: entry}); + } + else { + this._queue.push(entry); + } - util.preventDefault(); + this._flushIfNeeded(); }; + /** + * Check whether the queue needs to be flushed + * @private + */ + Queue.prototype._flushIfNeeded = function () { + // flush when the maximum is exceeded. + if (this._queue.length > this.max) { + this.flush(); + } - Slider.prototype._onMouseUp = function (event) { - this.frame.style.cursor = 'auto'; - - // remove event listeners - util.removeEventListener(document, 'mousemove', this.onmousemove); - util.removeEventListener(document, 'mouseup', this.onmouseup); + // flush after a period of inactivity when a delay is configured + clearTimeout(this._timeout); + if (this.queue.length > 0 && typeof this.delay === 'number') { + var me = this; + this._timeout = setTimeout(function () { + me.flush(); + }, this.delay); + } + }; - util.preventDefault(); + /** + * Flush all queued calls + */ + Queue.prototype.flush = function () { + while (this._queue.length > 0) { + var entry = this._queue.shift(); + entry.fn.apply(entry.context || entry.fn, entry.args || []); + } }; - module.exports = Slider; + module.exports = Queue; /***/ }, -/* 12 */ +/* 9 */ /***/ function(module, exports, __webpack_require__) { + var util = __webpack_require__(1); + var DataSet = __webpack_require__(7); + /** - * @prototype StepNumber - * The class StepNumber is an iterator for Numbers. You provide a start and end - * value, and a best step size. StepNumber itself rounds to fixed values and - * a finds the step that best fits the provided step. - * - * If prettyStep is true, the step size is chosen as close as possible to the - * provided step, but being a round value like 1, 2, 5, 10, 20, 50, .... + * DataView * - * Example usage: - * var step = new StepNumber(0, 10, 2.5, true); - * step.start(); - * while (!step.end()) { - * alert(step.getCurrent()); - * step.next(); - * } + * a dataview offers a filtered view on a dataset or an other dataview. * - * Version: 1.0 + * @param {DataSet | DataView} data + * @param {Object} [options] Available options: see method get * - * @param {Number} start The start value - * @param {Number} end The end value - * @param {Number} step Optional. Step size. Must be a positive value. - * @param {boolean} prettyStep Optional. If true, the step size is rounded - * To a pretty step size (like 1, 2, 5, 10, 20, 50, ...) + * @constructor DataView */ - function StepNumber(start, end, step, prettyStep) { - // set default values - this._start = 0; - this._end = 0; - this._step = 1; - this.prettyStep = true; - this.precision = 5; + function DataView (data, options) { + this._data = null; + this._ids = {}; // ids of the items currently in memory (just contains a boolean true) + this.length = 0; // number of items in the DataView + this._options = options || {}; + this._fieldId = 'id'; // name of the field containing id + this._subscribers = {}; // event subscribers - this._current = 0; - this.setRange(start, end, step, prettyStep); - }; + var me = this; + this.listener = function () { + me._onEvent.apply(me, arguments); + }; - /** - * Set a new range: start, end and step. - * - * @param {Number} start The start value - * @param {Number} end The end value - * @param {Number} step Optional. Step size. Must be a positive value. - * @param {boolean} prettyStep Optional. If true, the step size is rounded - * To a pretty step size (like 1, 2, 5, 10, 20, 50, ...) - */ - StepNumber.prototype.setRange = function(start, end, step, prettyStep) { - this._start = start ? start : 0; - this._end = end ? end : 0; + this.setData(data); + } - this.setStep(step, prettyStep); - }; + // TODO: implement a function .config() to dynamically update things like configured filter + // and trigger changes accordingly /** - * Set a new step size - * @param {Number} step New step size. Must be a positive value - * @param {boolean} prettyStep Optional. If true, the provided step is rounded - * to a pretty step size (like 1, 2, 5, 10, 20, 50, ...) + * Set a data source for the view + * @param {DataSet | DataView} data */ - StepNumber.prototype.setStep = function(step, prettyStep) { - if (step === undefined || step <= 0) - return; + DataView.prototype.setData = function (data) { + var ids, i, len; - if (prettyStep !== undefined) - this.prettyStep = prettyStep; + if (this._data) { + // unsubscribe from current dataset + if (this._data.unsubscribe) { + this._data.unsubscribe('*', this.listener); + } - if (this.prettyStep === true) - this._step = StepNumber.calculatePrettyStep(step); - else - this._step = step; - }; + // trigger a remove of all items in memory + ids = []; + for (var id in this._ids) { + if (this._ids.hasOwnProperty(id)) { + ids.push(id); + } + } + this._ids = {}; + this.length = 0; + this._trigger('remove', {items: ids}); + } - /** - * Calculate a nice step size, closest to the desired step size. - * Returns a value in one of the ranges 1*10^n, 2*10^n, or 5*10^n, where n is an - * integer Number. For example 1, 2, 5, 10, 20, 50, etc... - * @param {Number} step Desired step size - * @return {Number} Nice step size - */ - StepNumber.calculatePrettyStep = function (step) { - var log10 = function (x) {return Math.log(x) / Math.LN10;}; + this._data = data; - // try three steps (multiple of 1, 2, or 5 - var step1 = Math.pow(10, Math.round(log10(step))), - step2 = 2 * Math.pow(10, Math.round(log10(step / 2))), - step5 = 5 * Math.pow(10, Math.round(log10(step / 5))); + if (this._data) { + // update fieldId + this._fieldId = this._options.fieldId || + (this._data && this._data.options && this._data.options.fieldId) || + 'id'; - // choose the best step (closest to minimum step) - var prettyStep = step1; - if (Math.abs(step2 - step) <= Math.abs(prettyStep - step)) prettyStep = step2; - if (Math.abs(step5 - step) <= Math.abs(prettyStep - step)) prettyStep = step5; + // trigger an add of all added items + ids = this._data.getIds({filter: this._options && this._options.filter}); + for (i = 0, len = ids.length; i < len; i++) { + id = ids[i]; + this._ids[id] = true; + } + this.length = ids.length; + this._trigger('add', {items: ids}); - // for safety - if (prettyStep <= 0) { - prettyStep = 1; + // subscribe to new dataset + if (this._data.on) { + this._data.on('*', this.listener); + } } - - return prettyStep; }; /** - * returns the current value of the step - * @return {Number} current value + * Get data from the data view + * + * Usage: + * + * get() + * get(options: Object) + * get(options: Object, data: Array | DataTable) + * + * get(id: Number) + * get(id: Number, options: Object) + * get(id: Number, options: Object, data: Array | DataTable) + * + * get(ids: Number[]) + * get(ids: Number[], options: Object) + * get(ids: Number[], options: Object, data: Array | DataTable) + * + * Where: + * + * {Number | String} id The id of an item + * {Number[] | String{}} ids An array with ids of items + * {Object} options An Object with options. Available options: + * {String} [type] Type of data to be returned. Can + * be 'DataTable' or 'Array' (default) + * {Object.} [convert] + * {String[]} [fields] field names to be returned + * {function} [filter] filter items + * {String | function} [order] Order the items by + * a field name or custom sort function. + * {Array | DataTable} [data] If provided, items will be appended to this + * array or table. Required in case of Google + * DataTable. + * @param args */ - StepNumber.prototype.getCurrent = function () { - return parseFloat(this._current.toPrecision(this.precision)); - }; + DataView.prototype.get = function (args) { + var me = this; - /** - * returns the current step size - * @return {Number} current step size - */ - StepNumber.prototype.getStep = function () { - return this._step; - }; + // parse the arguments + var ids, options, data; + var firstType = util.getType(arguments[0]); + if (firstType == 'String' || firstType == 'Number' || firstType == 'Array') { + // get(id(s) [, options] [, data]) + ids = arguments[0]; // can be a single id or an array with ids + options = arguments[1]; + data = arguments[2]; + } + else { + // get([, options] [, data]) + options = arguments[0]; + data = arguments[1]; + } - /** - * Set the current value to the largest value smaller than start, which - * is a multiple of the step size - */ - StepNumber.prototype.start = function() { - this._current = this._start - this._start % this._step; - }; + // extend the options with the default options and provided options + var viewOptions = util.extend({}, this._options, options); - /** - * Do a step, add the step size to the current value - */ - StepNumber.prototype.next = function () { - this._current += this._step; + // create a combined filter method when needed + if (this._options.filter && options && options.filter) { + viewOptions.filter = function (item) { + return me._options.filter(item) && options.filter(item); + } + } + + // build up the call to the linked data set + var getArguments = []; + if (ids != undefined) { + getArguments.push(ids); + } + getArguments.push(viewOptions); + getArguments.push(data); + + return this._data && this._data.get.apply(this._data, getArguments); }; /** - * Returns true whether the end is reached - * @return {boolean} True if the current value has passed the end value. + * Get ids of all items or from a filtered set of items. + * @param {Object} [options] An Object with options. Available options: + * {function} [filter] filter items + * {String | function} [order] Order the items by + * a field name or custom sort function. + * @return {Array} ids */ - StepNumber.prototype.end = function () { - return (this._current > this._end); - }; + DataView.prototype.getIds = function (options) { + var ids; - module.exports = StepNumber; + if (this._data) { + var defaultFilter = this._options.filter; + var filter; + if (options && options.filter) { + if (defaultFilter) { + filter = function (item) { + return defaultFilter(item) && options.filter(item); + } + } + else { + filter = options.filter; + } + } + else { + filter = defaultFilter; + } -/***/ }, -/* 13 */ -/***/ function(module, exports, __webpack_require__) { + ids = this._data.getIds({ + filter: filter, + order: options && options.order + }); + } + else { + ids = []; + } - var Emitter = __webpack_require__(56); - var Hammer = __webpack_require__(45); - var util = __webpack_require__(1); - var DataSet = __webpack_require__(3); - var DataView = __webpack_require__(4); - var Range = __webpack_require__(17); - var Core = __webpack_require__(46); - var TimeAxis = __webpack_require__(30); - var CurrentTime = __webpack_require__(21); - var CustomTime = __webpack_require__(22); - var ItemSet = __webpack_require__(27); + return ids; + }; /** - * Create a timeline visualization - * @param {HTMLElement} container - * @param {vis.DataSet | Array | google.visualization.DataTable} [items] - * @param {vis.DataSet | Array | google.visualization.DataTable} [groups] - * @param {Object} [options] See Timeline.setOptions for the available options. - * @constructor - * @extends Core + * Get the DataSet to which this DataView is connected. In case there is a chain + * of multiple DataViews, the root DataSet of this chain is returned. + * @return {DataSet} dataSet */ - function Timeline (container, items, groups, options) { - if (!(this instanceof Timeline)) { - throw new SyntaxError('Constructor must be called with the new operator'); + DataView.prototype.getDataSet = function () { + var dataSet = this; + while (dataSet instanceof DataView) { + dataSet = dataSet._data; } + return dataSet || null; + }; - // if the third element is options, the forth is groups (optionally); - if (!(Array.isArray(groups) || groups instanceof DataSet) && groups instanceof Object) { - var forthArgument = options; - options = groups; - groups = forthArgument; - } + /** + * Event listener. Will propagate all events from the connected data set to + * the subscribers of the DataView, but will filter the items and only trigger + * when there are changes in the filtered data set. + * @param {String} event + * @param {Object | null} params + * @param {String} senderId + * @private + */ + DataView.prototype._onEvent = function (event, params, senderId) { + var i, len, id, item, + ids = params && params.items, + data = this._data, + added = [], + updated = [], + removed = []; - var me = this; - this.defaultOptions = { - start: null, - end: null, + if (ids && data) { + switch (event) { + case 'add': + // filter the ids of the added items + for (i = 0, len = ids.length; i < len; i++) { + id = ids[i]; + item = this.get(id); + if (item) { + this._ids[id] = true; + added.push(id); + } + } - autoResize: true, + break; - orientation: 'bottom', - width: null, - height: null, - maxHeight: null, - minHeight: null - }; - this.options = util.deepExtend({}, this.defaultOptions); + case 'update': + // determine the event from the views viewpoint: an updated + // item can be added, updated, or removed from this view. + for (i = 0, len = ids.length; i < len; i++) { + id = ids[i]; + item = this.get(id); - // Create the DOM, props, and emitter - this._create(container); + if (item) { + if (this._ids[id]) { + updated.push(id); + } + else { + this._ids[id] = true; + added.push(id); + } + } + else { + if (this._ids[id]) { + delete this._ids[id]; + removed.push(id); + } + else { + // nothing interesting for me :-( + } + } + } - // all components listed here will be repainted automatically - this.components = []; + break; - this.body = { - dom: this.dom, - domProps: this.props, - emitter: { - on: this.on.bind(this), - off: this.off.bind(this), - emit: this.emit.bind(this) - }, - hiddenDates: [], - util: { - snap: null, // will be specified after TimeAxis is created - toScreen: me._toScreen.bind(me), - toGlobalScreen: me._toGlobalScreen.bind(me), // this refers to the root.width - toTime: me._toTime.bind(me), - toGlobalTime : me._toGlobalTime.bind(me) + case 'remove': + // filter the ids of the removed items + for (i = 0, len = ids.length; i < len; i++) { + id = ids[i]; + if (this._ids[id]) { + delete this._ids[id]; + removed.push(id); + } + } + + break; } - }; - // range - this.range = new Range(this.body); - this.components.push(this.range); - this.body.range = this.range; + this.length += added.length - removed.length; - // time axis - this.timeAxis = new TimeAxis(this.body); - this.components.push(this.timeAxis); - this.body.util.snap = this.timeAxis.snap.bind(this.timeAxis); + if (added.length) { + this._trigger('add', {items: added}, senderId); + } + if (updated.length) { + this._trigger('update', {items: updated}, senderId); + } + if (removed.length) { + this._trigger('remove', {items: removed}, senderId); + } + } + }; - // current time bar - this.currentTime = new CurrentTime(this.body); - this.components.push(this.currentTime); + // copy subscription functionality from DataSet + DataView.prototype.on = DataSet.prototype.on; + DataView.prototype.off = DataSet.prototype.off; + DataView.prototype._trigger = DataSet.prototype._trigger; - // custom time bar - // Note: time bar will be attached in this.setOptions when selected - this.customTime = new CustomTime(this.body); - this.components.push(this.customTime); + // TODO: make these functions deprecated (replaced with `on` and `off` since version 0.5) + DataView.prototype.subscribe = DataView.prototype.on; + DataView.prototype.unsubscribe = DataView.prototype.off; - // item set - this.itemSet = new ItemSet(this.body); - this.components.push(this.itemSet); + module.exports = DataView; - this.itemsData = null; // DataSet - this.groupsData = null; // DataSet +/***/ }, +/* 10 */ +/***/ function(module, exports, __webpack_require__) { - // apply options - if (options) { - this.setOptions(options); - } + var Emitter = __webpack_require__(11); + var DataSet = __webpack_require__(7); + var DataView = __webpack_require__(9); + var util = __webpack_require__(1); + var Point3d = __webpack_require__(12); + var Point2d = __webpack_require__(13); + var Camera = __webpack_require__(14); + var Filter = __webpack_require__(15); + var Slider = __webpack_require__(16); + var StepNumber = __webpack_require__(17); - // IMPORTANT: THIS HAPPENS BEFORE SET ITEMS! - if (groups) { - this.setGroups(groups); + /** + * @constructor Graph3d + * Graph3d displays data in 3d. + * + * Graph3d is developed in javascript as a Google Visualization Chart. + * + * @param {Element} container The DOM element in which the Graph3d will + * be created. Normally a div element. + * @param {DataSet | DataView | Array} [data] + * @param {Object} [options] + */ + function Graph3d(container, data, options) { + if (!(this instanceof Graph3d)) { + throw new SyntaxError('Constructor must be called with the new operator'); } - // create itemset - if (items) { - this.setItems(items); - } - else { - this.redraw(); - } - } + // create variables and set default values + this.containerElement = container; + this.width = '400px'; + this.height = '400px'; + this.margin = 10; // px + this.defaultXCenter = '55%'; + this.defaultYCenter = '50%'; - // Extend the functionality from Core - Timeline.prototype = new Core(); + this.xLabel = 'x'; + this.yLabel = 'y'; + this.zLabel = 'z'; - /** - * Set items - * @param {vis.DataSet | Array | google.visualization.DataTable | null} items - */ - Timeline.prototype.setItems = function(items) { - var initialLoad = (this.itemsData == null); + var passValueFn = function(v) { return v; }; + this.xValueLabel = passValueFn; + this.yValueLabel = passValueFn; + this.zValueLabel = passValueFn; + + this.filterLabel = 'time'; + this.legendLabel = 'value'; - // convert to type DataSet when needed - var newDataSet; - if (!items) { - newDataSet = null; - } - else if (items instanceof DataSet || items instanceof DataView) { - newDataSet = items; - } - else { - // turn an array into a dataset - newDataSet = new DataSet(items, { - type: { - start: 'Date', - end: 'Date' - } - }); - } + this.style = Graph3d.STYLE.DOT; + this.showPerspective = true; + this.showGrid = true; + this.keepAspectRatio = true; + this.showShadow = false; + this.showGrayBottom = false; // TODO: this does not work correctly + this.showTooltip = false; + this.verticalRatio = 0.5; // 0.1 to 1.0, where 1.0 results in a 'cube' - // set items - this.itemsData = newDataSet; - this.itemSet && this.itemSet.setItems(newDataSet); + this.animationInterval = 1000; // milliseconds + this.animationPreload = false; - if (initialLoad) { - if (this.options.start != undefined || this.options.end != undefined) { - if (this.options.start == undefined || this.options.end == undefined) { - var dataRange = this._getDataRange(); - } + this.camera = new Camera(); + this.eye = new Point3d(0, 0, -1); // TODO: set eye.z about 3/4 of the width of the window? - var start = this.options.start != undefined ? this.options.start : dataRange.start; - var end = this.options.end != undefined ? this.options.end : dataRange.end; + this.dataTable = null; // The original data table + this.dataPoints = null; // The table with point objects - this.setWindow(start, end, {animate: false}); - } - else { - this.fit({animate: false}); - } - } - }; + // the column indexes + this.colX = undefined; + this.colY = undefined; + this.colZ = undefined; + this.colValue = undefined; + this.colFilter = undefined; - /** - * Set groups - * @param {vis.DataSet | Array | google.visualization.DataTable} groups - */ - Timeline.prototype.setGroups = function(groups) { - // convert to type DataSet when needed - var newDataSet; - if (!groups) { - newDataSet = null; - } - else if (groups instanceof DataSet || groups instanceof DataView) { - newDataSet = groups; - } - else { - // turn an array into a dataset - newDataSet = new DataSet(groups); + this.xMin = 0; + this.xStep = undefined; // auto by default + this.xMax = 1; + this.yMin = 0; + this.yStep = undefined; // auto by default + this.yMax = 1; + this.zMin = 0; + this.zStep = undefined; // auto by default + this.zMax = 1; + this.valueMin = 0; + this.valueMax = 1; + this.xBarWidth = 1; + this.yBarWidth = 1; + // TODO: customize axis range + + // constants + this.colorAxis = '#4D4D4D'; + this.colorGrid = '#D3D3D3'; + this.colorDot = '#7DC1FF'; + this.colorDotBorder = '#3267D2'; + + // create a frame and canvas + this.create(); + + // apply options (also when undefined) + this.setOptions(options); + + // apply data + if (data) { + this.setData(data); } + } - this.groupsData = newDataSet; - this.itemSet.setGroups(newDataSet); - }; + // Extend Graph3d with an Emitter mixin + Emitter(Graph3d.prototype); /** - * Set selected items by their id. Replaces the current selection - * Unknown id's are silently ignored. - * @param {string[] | string} [ids] An array with zero or more id's of the items to be - * selected. If ids is an empty array, all items will be - * unselected. - * @param {Object} [options] Available options: - * `focus: boolean` - * If true, focus will be set to the selected item(s) - * `animate: boolean | number` - * If true (default), the range is animated - * smoothly to the new window. - * If a number, the number is taken as duration - * for the animation. Default duration is 500 ms. - * Only applicable when option focus is true. + * Calculate the scaling values, dependent on the range in x, y, and z direction */ - Timeline.prototype.setSelection = function(ids, options) { - this.itemSet && this.itemSet.setSelection(ids); + Graph3d.prototype._setScale = function() { + this.scale = new Point3d(1 / (this.xMax - this.xMin), + 1 / (this.yMax - this.yMin), + 1 / (this.zMax - this.zMin)); - if (options && options.focus) { - this.focus(ids, options); + // keep aspect ration between x and y scale if desired + if (this.keepAspectRatio) { + if (this.scale.x < this.scale.y) { + //noinspection JSSuspiciousNameCombination + this.scale.y = this.scale.x; + } + else { + //noinspection JSSuspiciousNameCombination + this.scale.x = this.scale.y; + } } + + // scale the vertical axis + this.scale.z *= this.verticalRatio; + // TODO: can this be automated? verticalRatio? + + // determine scale for (optional) value + this.scale.value = 1 / (this.valueMax - this.valueMin); + + // position the camera arm + var xCenter = (this.xMax + this.xMin) / 2 * this.scale.x; + var yCenter = (this.yMax + this.yMin) / 2 * this.scale.y; + var zCenter = (this.zMax + this.zMin) / 2 * this.scale.z; + this.camera.setArmLocation(xCenter, yCenter, zCenter); }; + /** - * Get the selected items by their id - * @return {Array} ids The ids of the selected items + * Convert a 3D location to a 2D location on screen + * http://en.wikipedia.org/wiki/3D_projection + * @param {Point3d} point3d A 3D point with parameters x, y, z + * @return {Point2d} point2d A 2D point with parameters x, y */ - Timeline.prototype.getSelection = function() { - return this.itemSet && this.itemSet.getSelection() || []; + Graph3d.prototype._convert3Dto2D = function(point3d) { + var translation = this._convertPointToTranslation(point3d); + return this._convertTranslationToScreen(translation); }; /** - * Adjust the visible window such that the selected item (or multiple items) - * are centered on screen. - * @param {String | String[]} id An item id or array with item ids - * @param {Object} [options] Available options: - * `animate: boolean | number` - * If true (default), the range is animated - * smoothly to the new window. - * If a number, the number is taken as duration - * for the animation. Default duration is 500 ms. - * Only applicable when option focus is true + * Convert a 3D location its translation seen from the camera + * http://en.wikipedia.org/wiki/3D_projection + * @param {Point3d} point3d A 3D point with parameters x, y, z + * @return {Point3d} translation A 3D point with parameters x, y, z This is + * the translation of the point, seen from the + * camera */ - Timeline.prototype.focus = function(id, options) { - if (!this.itemsData || id == undefined) return; - - var ids = Array.isArray(id) ? id : [id]; - - // get the specified item(s) - var itemsData = this.itemsData.getDataSet().get(ids, { - type: { - start: 'Date', - end: 'Date' - } - }); - - // calculate minimum start and maximum end of specified items - var start = null; - var end = null; - itemsData.forEach(function (itemData) { - var s = itemData.start.valueOf(); - var e = 'end' in itemData ? itemData.end.valueOf() : itemData.start.valueOf(); + Graph3d.prototype._convertPointToTranslation = function(point3d) { + var ax = point3d.x * this.scale.x, + ay = point3d.y * this.scale.y, + az = point3d.z * this.scale.z, - if (start === null || s < start) { - start = s; - } + cx = this.camera.getCameraLocation().x, + cy = this.camera.getCameraLocation().y, + cz = this.camera.getCameraLocation().z, - if (end === null || e > end) { - end = e; - } - }); + // calculate angles + sinTx = Math.sin(this.camera.getCameraRotation().x), + cosTx = Math.cos(this.camera.getCameraRotation().x), + sinTy = Math.sin(this.camera.getCameraRotation().y), + cosTy = Math.cos(this.camera.getCameraRotation().y), + sinTz = Math.sin(this.camera.getCameraRotation().z), + cosTz = Math.cos(this.camera.getCameraRotation().z), - if (start !== null && end !== null) { - // calculate the new middle and interval for the window - var middle = (start + end) / 2; - var interval = Math.max((this.range.end - this.range.start), (end - start) * 1.1); + // calculate translation + dx = cosTy * (sinTz * (ay - cy) + cosTz * (ax - cx)) - sinTy * (az - cz), + dy = sinTx * (cosTy * (az - cz) + sinTy * (sinTz * (ay - cy) + cosTz * (ax - cx))) + cosTx * (cosTz * (ay - cy) - sinTz * (ax-cx)), + dz = cosTx * (cosTy * (az - cz) + sinTy * (sinTz * (ay - cy) + cosTz * (ax - cx))) - sinTx * (cosTz * (ay - cy) - sinTz * (ax-cx)); - var animate = (options && options.animate !== undefined) ? options.animate : true; - this.range.setRange(middle - interval / 2, middle + interval / 2, animate); - } + return new Point3d(dx, dy, dz); }; /** - * Get the data range of the item set. - * @returns {{min: Date, max: Date}} range A range with a start and end Date. - * When no minimum is found, min==null - * When no maximum is found, max==null + * Convert a translation point to a point on the screen + * @param {Point3d} translation A 3D point with parameters x, y, z This is + * the translation of the point, seen from the + * camera + * @return {Point2d} point2d A 2D point with parameters x, y */ - Timeline.prototype.getItemRange = function() { - // calculate min from start filed - var dataset = this.itemsData.getDataSet(), - min = null, - max = null; - - if (dataset) { - // calculate the minimum value of the field 'start' - var minItem = dataset.min('start'); - min = minItem ? util.convert(minItem.start, 'Date').valueOf() : null; - // Note: we convert first to Date and then to number because else - // a conversion from ISODate to Number will fail + Graph3d.prototype._convertTranslationToScreen = function(translation) { + var ex = this.eye.x, + ey = this.eye.y, + ez = this.eye.z, + dx = translation.x, + dy = translation.y, + dz = translation.z; - // calculate maximum value of fields 'start' and 'end' - var maxStartItem = dataset.max('start'); - if (maxStartItem) { - max = util.convert(maxStartItem.start, 'Date').valueOf(); - } - var maxEndItem = dataset.max('end'); - if (maxEndItem) { - if (max == null) { - max = util.convert(maxEndItem.end, 'Date').valueOf(); - } - else { - max = Math.max(max, util.convert(maxEndItem.end, 'Date').valueOf()); - } - } + // calculate position on screen from translation + var bx; + var by; + if (this.showPerspective) { + bx = (dx - ex) * (ez / dz); + by = (dy - ey) * (ez / dz); + } + else { + bx = dx * -(ez / this.camera.getArmLength()); + by = dy * -(ez / this.camera.getArmLength()); } - return { - min: (min != null) ? new Date(min) : null, - max: (max != null) ? new Date(max) : null - }; + // shift and scale the point to the center of the screen + // use the width of the graph to scale both horizontally and vertically. + return new Point2d( + this.xcenter + bx * this.frame.canvas.clientWidth, + this.ycenter - by * this.frame.canvas.clientWidth); }; + /** + * Set the background styling for the graph + * @param {string | {fill: string, stroke: string, strokeWidth: string}} backgroundColor + */ + Graph3d.prototype._setBackgroundColor = function(backgroundColor) { + var fill = 'white'; + var stroke = 'gray'; + var strokeWidth = 1; - module.exports = Timeline; + if (typeof(backgroundColor) === 'string') { + fill = backgroundColor; + stroke = 'none'; + strokeWidth = 0; + } + else if (typeof(backgroundColor) === 'object') { + if (backgroundColor.fill !== undefined) fill = backgroundColor.fill; + if (backgroundColor.stroke !== undefined) stroke = backgroundColor.stroke; + if (backgroundColor.strokeWidth !== undefined) strokeWidth = backgroundColor.strokeWidth; + } + else if (backgroundColor === undefined) { + // use use defaults + } + else { + throw 'Unsupported type of backgroundColor'; + } + this.frame.style.backgroundColor = fill; + this.frame.style.borderColor = stroke; + this.frame.style.borderWidth = strokeWidth + 'px'; + this.frame.style.borderStyle = 'solid'; + }; -/***/ }, -/* 14 */ -/***/ function(module, exports, __webpack_require__) { - var Emitter = __webpack_require__(56); - var Hammer = __webpack_require__(45); - var util = __webpack_require__(1); - var DataSet = __webpack_require__(3); - var DataView = __webpack_require__(4); - var Range = __webpack_require__(17); - var Core = __webpack_require__(46); - var TimeAxis = __webpack_require__(30); - var CurrentTime = __webpack_require__(21); - var CustomTime = __webpack_require__(22); - var LineGraph = __webpack_require__(29); + /// enumerate the available styles + Graph3d.STYLE = { + BAR: 0, + BARCOLOR: 1, + BARSIZE: 2, + DOT : 3, + DOTLINE : 4, + DOTCOLOR: 5, + DOTSIZE: 6, + GRID : 7, + LINE: 8, + SURFACE : 9 + }; /** - * Create a timeline visualization - * @param {HTMLElement} container - * @param {vis.DataSet | Array | google.visualization.DataTable} [items] - * @param {Object} [options] See Graph2d.setOptions for the available options. - * @constructor - * @extends Core + * Retrieve the style index from given styleName + * @param {string} styleName Style name such as 'dot', 'grid', 'dot-line' + * @return {Number} styleNumber Enumeration value representing the style, or -1 + * when not found */ - function Graph2d (container, items, groups, options) { - // if the third element is options, the forth is groups (optionally); - if (!(Array.isArray(groups) || groups instanceof DataSet) && groups instanceof Object) { - var forthArgument = options; - options = groups; - groups = forthArgument; + Graph3d.prototype._getStyleNumber = function(styleName) { + switch (styleName) { + case 'dot': return Graph3d.STYLE.DOT; + case 'dot-line': return Graph3d.STYLE.DOTLINE; + case 'dot-color': return Graph3d.STYLE.DOTCOLOR; + case 'dot-size': return Graph3d.STYLE.DOTSIZE; + case 'line': return Graph3d.STYLE.LINE; + case 'grid': return Graph3d.STYLE.GRID; + case 'surface': return Graph3d.STYLE.SURFACE; + case 'bar': return Graph3d.STYLE.BAR; + case 'bar-color': return Graph3d.STYLE.BARCOLOR; + case 'bar-size': return Graph3d.STYLE.BARSIZE; } - var me = this; - this.defaultOptions = { - start: null, - end: null, + return -1; + }; - autoResize: true, + /** + * Determine the indexes of the data columns, based on the given style and data + * @param {DataSet} data + * @param {Number} style + */ + Graph3d.prototype._determineColumnIndexes = function(data, style) { + if (this.style === Graph3d.STYLE.DOT || + this.style === Graph3d.STYLE.DOTLINE || + this.style === Graph3d.STYLE.LINE || + this.style === Graph3d.STYLE.GRID || + this.style === Graph3d.STYLE.SURFACE || + this.style === Graph3d.STYLE.BAR) { + // 3 columns expected, and optionally a 4th with filter values + this.colX = 0; + this.colY = 1; + this.colZ = 2; + this.colValue = undefined; - orientation: 'bottom', - width: null, - height: null, - maxHeight: null, - minHeight: null - }; - this.options = util.deepExtend({}, this.defaultOptions); + if (data.getNumberOfColumns() > 3) { + this.colFilter = 3; + } + } + else if (this.style === Graph3d.STYLE.DOTCOLOR || + this.style === Graph3d.STYLE.DOTSIZE || + this.style === Graph3d.STYLE.BARCOLOR || + this.style === Graph3d.STYLE.BARSIZE) { + // 4 columns expected, and optionally a 5th with filter values + this.colX = 0; + this.colY = 1; + this.colZ = 2; + this.colValue = 3; - // Create the DOM, props, and emitter - this._create(container); + if (data.getNumberOfColumns() > 4) { + this.colFilter = 4; + } + } + else { + throw 'Unknown style "' + this.style + '"'; + } + }; - // all components listed here will be repainted automatically - this.components = []; + Graph3d.prototype.getNumberOfRows = function(data) { + return data.length; + } - this.body = { - dom: this.dom, - domProps: this.props, - emitter: { - on: this.on.bind(this), - off: this.off.bind(this), - emit: this.emit.bind(this) - }, - hiddenDates: [], - util: { - snap: null, // will be specified after TimeAxis is created - toScreen: me._toScreen.bind(me), - toGlobalScreen: me._toGlobalScreen.bind(me), // this refers to the root.width - toTime: me._toTime.bind(me), - toGlobalTime : me._toGlobalTime.bind(me) - } - }; - // range - this.range = new Range(this.body); - this.components.push(this.range); - this.body.range = this.range; + Graph3d.prototype.getNumberOfColumns = function(data) { + var counter = 0; + for (var column in data[0]) { + if (data[0].hasOwnProperty(column)) { + counter++; + } + } + return counter; + } - // time axis - this.timeAxis = new TimeAxis(this.body); - this.components.push(this.timeAxis); - this.body.util.snap = this.timeAxis.snap.bind(this.timeAxis); - // current time bar - this.currentTime = new CurrentTime(this.body); - this.components.push(this.currentTime); + Graph3d.prototype.getDistinctValues = function(data, column) { + var distinctValues = []; + for (var i = 0; i < data.length; i++) { + if (distinctValues.indexOf(data[i][column]) == -1) { + distinctValues.push(data[i][column]); + } + } + return distinctValues; + } - // custom time bar - // Note: time bar will be attached in this.setOptions when selected - this.customTime = new CustomTime(this.body); - this.components.push(this.customTime); - // item set - this.linegraph = new LineGraph(this.body); - this.components.push(this.linegraph); + Graph3d.prototype.getColumnRange = function(data,column) { + var minMax = {min:data[0][column],max:data[0][column]}; + for (var i = 0; i < data.length; i++) { + if (minMax.min > data[i][column]) { minMax.min = data[i][column]; } + if (minMax.max < data[i][column]) { minMax.max = data[i][column]; } + } + return minMax; + }; - this.itemsData = null; // DataSet - this.groupsData = null; // DataSet + /** + * Initialize the data from the data table. Calculate minimum and maximum values + * and column index values + * @param {Array | DataSet | DataView} rawData The data containing the items for the Graph. + * @param {Number} style Style Number + */ + Graph3d.prototype._dataInitialize = function (rawData, style) { + var me = this; - // apply options - if (options) { - this.setOptions(options); + // unsubscribe from the dataTable + if (this.dataSet) { + this.dataSet.off('*', this._onChange); } - // IMPORTANT: THIS HAPPENS BEFORE SET ITEMS! - if (groups) { - this.setGroups(groups); + if (rawData === undefined) + return; + + if (Array.isArray(rawData)) { + rawData = new DataSet(rawData); } - // create itemset - if (items) { - this.setItems(items); + var data; + if (rawData instanceof DataSet || rawData instanceof DataView) { + data = rawData.get(); } else { - this.redraw(); + throw new Error('Array, DataSet, or DataView expected'); } - } - // Extend the functionality from Core - Graph2d.prototype = new Core(); + if (data.length == 0) + return; - /** - * Set items - * @param {vis.DataSet | Array | google.visualization.DataTable | null} items - */ - Graph2d.prototype.setItems = function(items) { - var initialLoad = (this.itemsData == null); + this.dataSet = rawData; + this.dataTable = data; - // convert to type DataSet when needed - var newDataSet; - if (!items) { - newDataSet = null; - } - else if (items instanceof DataSet || items instanceof DataView) { - newDataSet = items; - } - else { - // turn an array into a dataset - newDataSet = new DataSet(items, { - type: { - start: 'Date', - end: 'Date' - } - }); + // subscribe to changes in the dataset + this._onChange = function () { + me.setData(me.dataSet); + }; + this.dataSet.on('*', this._onChange); + + // _determineColumnIndexes + // getNumberOfRows (points) + // getNumberOfColumns (x,y,z,v,t,t1,t2...) + // getDistinctValues (unique values?) + // getColumnRange + + // determine the location of x,y,z,value,filter columns + this.colX = 'x'; + this.colY = 'y'; + this.colZ = 'z'; + this.colValue = 'style'; + this.colFilter = 'filter'; + + + + // check if a filter column is provided + if (data[0].hasOwnProperty('filter')) { + if (this.dataFilter === undefined) { + this.dataFilter = new Filter(rawData, this.colFilter, this); + this.dataFilter.setOnLoadCallback(function() {me.redraw();}); + } } - // set items - this.itemsData = newDataSet; - this.linegraph && this.linegraph.setItems(newDataSet); - if (initialLoad) { - if (this.options.start != undefined || this.options.end != undefined) { - var start = this.options.start != undefined ? this.options.start : null; - var end = this.options.end != undefined ? this.options.end : null; + var withBars = this.style == Graph3d.STYLE.BAR || + this.style == Graph3d.STYLE.BARCOLOR || + this.style == Graph3d.STYLE.BARSIZE; - this.setWindow(start, end, {animate: false}); + // determine barWidth from data + if (withBars) { + if (this.defaultXBarWidth !== undefined) { + this.xBarWidth = this.defaultXBarWidth; } else { - this.fit({animate: false}); + var dataX = this.getDistinctValues(data,this.colX); + this.xBarWidth = (dataX[1] - dataX[0]) || 1; + } + + if (this.defaultYBarWidth !== undefined) { + this.yBarWidth = this.defaultYBarWidth; + } + else { + var dataY = this.getDistinctValues(data,this.colY); + this.yBarWidth = (dataY[1] - dataY[0]) || 1; } } - }; - /** - * Set groups - * @param {vis.DataSet | Array | google.visualization.DataTable} groups - */ - Graph2d.prototype.setGroups = function(groups) { - // convert to type DataSet when needed - var newDataSet; - if (!groups) { - newDataSet = null; + // calculate minimums and maximums + var xRange = this.getColumnRange(data,this.colX); + if (withBars) { + xRange.min -= this.xBarWidth / 2; + xRange.max += this.xBarWidth / 2; } - else if (groups instanceof DataSet || groups instanceof DataView) { - newDataSet = groups; + this.xMin = (this.defaultXMin !== undefined) ? this.defaultXMin : xRange.min; + this.xMax = (this.defaultXMax !== undefined) ? this.defaultXMax : xRange.max; + if (this.xMax <= this.xMin) this.xMax = this.xMin + 1; + this.xStep = (this.defaultXStep !== undefined) ? this.defaultXStep : (this.xMax-this.xMin)/5; + + var yRange = this.getColumnRange(data,this.colY); + if (withBars) { + yRange.min -= this.yBarWidth / 2; + yRange.max += this.yBarWidth / 2; } - else { - // turn an array into a dataset - newDataSet = new DataSet(groups); + this.yMin = (this.defaultYMin !== undefined) ? this.defaultYMin : yRange.min; + this.yMax = (this.defaultYMax !== undefined) ? this.defaultYMax : yRange.max; + if (this.yMax <= this.yMin) this.yMax = this.yMin + 1; + this.yStep = (this.defaultYStep !== undefined) ? this.defaultYStep : (this.yMax-this.yMin)/5; + + var zRange = this.getColumnRange(data,this.colZ); + this.zMin = (this.defaultZMin !== undefined) ? this.defaultZMin : zRange.min; + this.zMax = (this.defaultZMax !== undefined) ? this.defaultZMax : zRange.max; + if (this.zMax <= this.zMin) this.zMax = this.zMin + 1; + this.zStep = (this.defaultZStep !== undefined) ? this.defaultZStep : (this.zMax-this.zMin)/5; + + if (this.colValue !== undefined) { + var valueRange = this.getColumnRange(data,this.colValue); + this.valueMin = (this.defaultValueMin !== undefined) ? this.defaultValueMin : valueRange.min; + this.valueMax = (this.defaultValueMax !== undefined) ? this.defaultValueMax : valueRange.max; + if (this.valueMax <= this.valueMin) this.valueMax = this.valueMin + 1; } - this.groupsData = newDataSet; - this.linegraph.setGroups(newDataSet); + // set the scale dependent on the ranges. + this._setScale(); }; - /** - * Returns an object containing an SVG element with the icon of the group (size determined by iconWidth and iconHeight), the label of the group (content) and the yAxisOrientation of the group (left or right). - * @param groupId - * @param width - * @param height - */ - Graph2d.prototype.getLegend = function(groupId, width, height) { - if (width === undefined) {width = 15;} - if (height === undefined) {height = 15;} - if (this.linegraph.groups[groupId] !== undefined) { - return this.linegraph.groups[groupId].getLegend(width,height); - } - else { - return "cannot find group:" + groupId; - } - } + /** - * This checks if the visible option of the supplied group (by ID) is true or false. - * @param groupId - * @returns {*} + * Filter the data based on the current filter + * @param {Array} data + * @return {Array} dataPoints Array with point objects which can be drawn on screen */ - Graph2d.prototype.isGroupVisible = function(groupId) { - if (this.linegraph.groups[groupId] !== undefined) { - return (this.linegraph.groups[groupId].visible && (this.linegraph.options.groups.visibility[groupId] === undefined || this.linegraph.options.groups.visibility[groupId] == true)); - } - else { - return false; - } - } + Graph3d.prototype._getDataPoints = function (data) { + // TODO: store the created matrix dataPoints in the filters instead of reloading each time + var x, y, i, z, obj, point; + var dataPoints = []; - /** - * Get the data range of the item set. - * @returns {{min: Date, max: Date}} range A range with a start and end Date. - * When no minimum is found, min==null - * When no maximum is found, max==null - */ - Graph2d.prototype.getItemRange = function() { - var min = null; - var max = null; + if (this.style === Graph3d.STYLE.GRID || + this.style === Graph3d.STYLE.SURFACE) { + // copy all values from the google data table to a matrix + // the provided values are supposed to form a grid of (x,y) positions - // calculate min from start filed - for (var groupId in this.linegraph.groups) { - if (this.linegraph.groups.hasOwnProperty(groupId)) { - if (this.linegraph.groups[groupId].visible == true) { - for (var i = 0; i < this.linegraph.groups[groupId].itemsData.length; i++) { - var item = this.linegraph.groups[groupId].itemsData[i]; - var value = util.convert(item.x, 'Date').valueOf(); - min = min == null ? value : min > value ? value : min; - max = max == null ? value : max < value ? value : max; - } + // create two lists with all present x and y values + var dataX = []; + var dataY = []; + for (i = 0; i < this.getNumberOfRows(data); i++) { + x = data[i][this.colX] || 0; + y = data[i][this.colY] || 0; + + if (dataX.indexOf(x) === -1) { + dataX.push(x); + } + if (dataY.indexOf(y) === -1) { + dataY.push(y); } } - } - return { - min: (min != null) ? new Date(min) : null, - max: (max != null) ? new Date(max) : null - }; - }; + var sortNumber = function (a, b) { + return a - b; + }; + dataX.sort(sortNumber); + dataY.sort(sortNumber); + // create a grid, a 2d matrix, with all values. + var dataMatrix = []; // temporary data matrix + for (i = 0; i < data.length; i++) { + x = data[i][this.colX] || 0; + y = data[i][this.colY] || 0; + z = data[i][this.colZ] || 0; + var xIndex = dataX.indexOf(x); // TODO: implement Array().indexOf() for Internet Explorer + var yIndex = dataY.indexOf(y); - module.exports = Graph2d; + if (dataMatrix[xIndex] === undefined) { + dataMatrix[xIndex] = []; + } + var point3d = new Point3d(); + point3d.x = x; + point3d.y = y; + point3d.z = z; -/***/ }, -/* 15 */ -/***/ function(module, exports, __webpack_require__) { + obj = {}; + obj.point = point3d; + obj.trans = undefined; + obj.screen = undefined; + obj.bottom = new Point3d(x, y, this.zMin); - /** - * Created by Alex on 10/3/2014. - */ - var moment = __webpack_require__(44); + dataMatrix[xIndex][yIndex] = obj; + dataPoints.push(obj); + } - /** - * used in Core to convert the options into a volatile variable - * - * @param Core - */ - exports.convertHiddenOptions = function(body, hiddenDates) { - body.hiddenDates = []; - if (hiddenDates) { - if (Array.isArray(hiddenDates) == true) { - for (var i = 0; i < hiddenDates.length; i++) { - if (hiddenDates[i].repeat === undefined) { - var dateItem = {}; - dateItem.start = moment(hiddenDates[i].start).toDate().valueOf(); - dateItem.end = moment(hiddenDates[i].end).toDate().valueOf(); - body.hiddenDates.push(dateItem); + // fill in the pointers to the neighbors. + for (x = 0; x < dataMatrix.length; x++) { + for (y = 0; y < dataMatrix[x].length; y++) { + if (dataMatrix[x][y]) { + dataMatrix[x][y].pointRight = (x < dataMatrix.length-1) ? dataMatrix[x+1][y] : undefined; + dataMatrix[x][y].pointTop = (y < dataMatrix[x].length-1) ? dataMatrix[x][y+1] : undefined; + dataMatrix[x][y].pointCross = + (x < dataMatrix.length-1 && y < dataMatrix[x].length-1) ? + dataMatrix[x+1][y+1] : + undefined; } } - body.hiddenDates.sort(function (a, b) { - return a.start - b.start; - }); // sort by start time } } - }; + else { // 'dot', 'dot-line', etc. + // copy all values from the google data table to a list with Point3d objects + for (i = 0; i < data.length; i++) { + point = new Point3d(); + point.x = data[i][this.colX] || 0; + point.y = data[i][this.colY] || 0; + point.z = data[i][this.colZ] || 0; + if (this.colValue !== undefined) { + point.value = data[i][this.colValue] || 0; + } - /** - * create new entrees for the repeating hidden dates - * @param body - * @param hiddenDates - */ - exports.updateHiddenDates = function (body, hiddenDates) { - if (hiddenDates && body.domProps.centerContainer.width !== undefined) { - exports.convertHiddenOptions(body, hiddenDates); + obj = {}; + obj.point = point; + obj.bottom = new Point3d(point.x, point.y, this.zMin); + obj.trans = undefined; + obj.screen = undefined; - var start = moment(body.range.start); - var end = moment(body.range.end); + dataPoints.push(obj); + } + } - var totalRange = (body.range.end - body.range.start); - var pixelTime = totalRange / body.domProps.centerContainer.width; + return dataPoints; + }; - for (var i = 0; i < hiddenDates.length; i++) { - if (hiddenDates[i].repeat !== undefined) { - var startDate = moment(hiddenDates[i].start); - var endDate = moment(hiddenDates[i].end); + /** + * Create the main frame for the Graph3d. + * This function is executed once when a Graph3d object is created. The frame + * contains a canvas, and this canvas contains all objects like the axis and + * nodes. + */ + Graph3d.prototype.create = function () { + // remove all elements from the container element. + while (this.containerElement.hasChildNodes()) { + this.containerElement.removeChild(this.containerElement.firstChild); + } - if (startDate._d == "Invalid Date") { - throw new Error("Supplied start date is not valid: " + hiddenDates[i].start); - } - if (endDate._d == "Invalid Date") { - throw new Error("Supplied end date is not valid: " + hiddenDates[i].end); - } + this.frame = document.createElement('div'); + this.frame.style.position = 'relative'; + this.frame.style.overflow = 'hidden'; - var duration = endDate - startDate; - if (duration >= 4 * pixelTime) { + // create the graph canvas (HTML canvas element) + this.frame.canvas = document.createElement( 'canvas' ); + this.frame.canvas.style.position = 'relative'; + this.frame.appendChild(this.frame.canvas); + //if (!this.frame.canvas.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.frame.canvas.appendChild(noCanvas); + } - var offset = 0; - var runUntil = end.clone(); - switch (hiddenDates[i].repeat) { - case "daily": // case of time - if (startDate.day() != endDate.day()) { - offset = 1; - } - startDate.dayOfYear(start.dayOfYear()); - startDate.year(start.year()); - startDate.subtract(7,'days'); - - endDate.dayOfYear(start.dayOfYear()); - endDate.year(start.year()); - endDate.subtract(7 - offset,'days'); - - runUntil.add(1, 'weeks'); - break; - case "weekly": - var dayOffset = endDate.diff(startDate,'days') - var day = startDate.day(); + this.frame.filter = document.createElement( 'div' ); + this.frame.filter.style.position = 'absolute'; + this.frame.filter.style.bottom = '0px'; + this.frame.filter.style.left = '0px'; + this.frame.filter.style.width = '100%'; + this.frame.appendChild(this.frame.filter); - // set the start date to the range.start - startDate.date(start.date()); - startDate.month(start.month()); - startDate.year(start.year()); - endDate = startDate.clone(); + // add event listeners to handle moving and zooming the contents + var me = this; + var onmousedown = function (event) {me._onMouseDown(event);}; + var ontouchstart = function (event) {me._onTouchStart(event);}; + var onmousewheel = function (event) {me._onWheel(event);}; + var ontooltip = function (event) {me._onTooltip(event);}; + // TODO: these events are never cleaned up... can give a 'memory leakage' - // force - startDate.day(day); - endDate.day(day); - endDate.add(dayOffset,'days'); + util.addEventListener(this.frame.canvas, 'keydown', onkeydown); + util.addEventListener(this.frame.canvas, 'mousedown', onmousedown); + util.addEventListener(this.frame.canvas, 'touchstart', ontouchstart); + util.addEventListener(this.frame.canvas, 'mousewheel', onmousewheel); + util.addEventListener(this.frame.canvas, 'mousemove', ontooltip); - startDate.subtract(1,'weeks'); - endDate.subtract(1,'weeks'); + // add the new graph to the container element + this.containerElement.appendChild(this.frame); + }; - runUntil.add(1, 'weeks'); - break - case "monthly": - if (startDate.month() != endDate.month()) { - offset = 1; - } - startDate.month(start.month()); - startDate.year(start.year()); - startDate.subtract(1,'months'); - endDate.month(start.month()); - endDate.year(start.year()); - endDate.subtract(1,'months'); - endDate.add(offset,'months'); + /** + * Set a new size for the graph + * @param {string} width Width in pixels or percentage (for example '800px' + * or '50%') + * @param {string} height Height in pixels or percentage (for example '400px' + * or '30%') + */ + Graph3d.prototype.setSize = function(width, height) { + this.frame.style.width = width; + this.frame.style.height = height; - runUntil.add(1, 'months'); - break; - case "yearly": - if (startDate.year() != endDate.year()) { - offset = 1; - } - startDate.year(start.year()); - startDate.subtract(1,'years'); - endDate.year(start.year()); - endDate.subtract(1,'years'); - endDate.add(offset,'years'); + this._resizeCanvas(); + }; - runUntil.add(1, 'years'); - break; - default: - console.log("Wrong repeat format, allowed are: daily, weekly, monthly, yearly. Given:", hiddenDates[i].repeat); - return; - } - while (startDate < runUntil) { - body.hiddenDates.push({start: startDate.valueOf(), end: endDate.valueOf()}); - switch (hiddenDates[i].repeat) { - case "daily": - startDate.add(1, 'days'); - endDate.add(1, 'days'); - break; - case "weekly": - startDate.add(1, 'weeks'); - endDate.add(1, 'weeks'); - break - case "monthly": - startDate.add(1, 'months'); - endDate.add(1, 'months'); - break; - case "yearly": - startDate.add(1, 'y'); - endDate.add(1, 'y'); - break; - default: - console.log("Wrong repeat format, allowed are: daily, weekly, monthly, yearly. Given:", hiddenDates[i].repeat); - return; - } - } - body.hiddenDates.push({start: startDate.valueOf(), end: endDate.valueOf()}); - } - } - } - // remove duplicates, merge where possible - exports.removeDuplicates(body); - // ensure the new positions are not on hidden dates - var startHidden = exports.isHidden(body.range.start, body.hiddenDates); - var endHidden = exports.isHidden(body.range.end,body.hiddenDates); - var rangeStart = body.range.start; - var rangeEnd = body.range.end; - if (startHidden.hidden == true) {rangeStart = body.range.startToFront == true ? startHidden.startDate - 1 : startHidden.endDate + 1;} - if (endHidden.hidden == true) {rangeEnd = body.range.endToFront == true ? endHidden.startDate - 1 : endHidden.endDate + 1;} - if (startHidden.hidden == true || endHidden.hidden == true) { - body.range._applyRange(rangeStart, rangeEnd); - } - } + /** + * Resize the canvas to the current size of the frame + */ + Graph3d.prototype._resizeCanvas = function() { + this.frame.canvas.style.width = '100%'; + this.frame.canvas.style.height = '100%'; - } + this.frame.canvas.width = this.frame.canvas.clientWidth; + this.frame.canvas.height = this.frame.canvas.clientHeight; + // adjust with for margin + this.frame.filter.style.width = (this.frame.canvas.clientWidth - 2 * 10) + 'px'; + }; /** - * remove duplicates from the hidden dates list. Duplicates are evil. They mess everything up. - * Scales with N^2 - * @param body + * Start animation */ - exports.removeDuplicates = function(body) { - var hiddenDates = body.hiddenDates; - var safeDates = []; - for (var i = 0; i < hiddenDates.length; i++) { - for (var j = 0; j < hiddenDates.length; j++) { - if (i != j && hiddenDates[j].remove != true && hiddenDates[i].remove != true) { - // j inside i - if (hiddenDates[j].start >= hiddenDates[i].start && hiddenDates[j].end <= hiddenDates[i].end) { - hiddenDates[j].remove = true; - } - // j start inside i - else if (hiddenDates[j].start >= hiddenDates[i].start && hiddenDates[j].start <= hiddenDates[i].end) { - hiddenDates[i].end = hiddenDates[j].end; - hiddenDates[j].remove = true; - } - // j end inside i - else if (hiddenDates[j].end >= hiddenDates[i].start && hiddenDates[j].end <= hiddenDates[i].end) { - hiddenDates[i].start = hiddenDates[j].start; - hiddenDates[j].remove = true; - } - } - } - } - - for (var i = 0; i < hiddenDates.length; i++) { - if (hiddenDates[i].remove !== true) { - safeDates.push(hiddenDates[i]); - } - } + Graph3d.prototype.animationStart = function() { + if (!this.frame.filter || !this.frame.filter.slider) + throw 'No animation available'; - body.hiddenDates = safeDates; - body.hiddenDates.sort(function (a, b) { - return a.start - b.start; - }); // sort by start time - } + this.frame.filter.slider.play(); + }; - exports.printDates = function(dates) { - for (var i =0; i < dates.length; i++) { - console.log(i, new Date(dates[i].start),new Date(dates[i].end), dates[i].start, dates[i].end, dates[i].remove); - } - } /** - * Used in TimeStep to avoid the hidden times. - * @param timeStep - * @param previousTime + * Stop animation */ - exports.stepOverHiddenDates = function(timeStep, previousTime) { - var stepInHidden = false; - var currentValue = timeStep.current.valueOf(); - for (var i = 0; i < timeStep.hiddenDates.length; i++) { - var startDate = timeStep.hiddenDates[i].start; - var endDate = timeStep.hiddenDates[i].end; - if (currentValue >= startDate && currentValue < endDate) { - stepInHidden = true; - break; - } - } - - if (stepInHidden == true && currentValue < timeStep._end.valueOf() && currentValue != previousTime) { - var prevValue = moment(previousTime); - var newValue = moment(endDate); - //check if the next step should be major - if (prevValue.year() != newValue.year()) {timeStep.switchedYear = true;} - else if (prevValue.month() != newValue.month()) {timeStep.switchedMonth = true;} - else if (prevValue.dayOfYear() != newValue.dayOfYear()) {timeStep.switchedDay = true;} + Graph3d.prototype.animationStop = function() { + if (!this.frame.filter || !this.frame.filter.slider) return; - timeStep.current = newValue.toDate(); - } + this.frame.filter.slider.stop(); }; - ///** - // * Used in TimeStep to avoid the hidden times. - // * @param timeStep - // * @param previousTime - // */ - //exports.checkFirstStep = function(timeStep) { - // var stepInHidden = false; - // var currentValue = timeStep.current.valueOf(); - // for (var i = 0; i < timeStep.hiddenDates.length; i++) { - // var startDate = timeStep.hiddenDates[i].start; - // var endDate = timeStep.hiddenDates[i].end; - // if (currentValue >= startDate && currentValue < endDate) { - // stepInHidden = true; - // break; - // } - // } - // - // if (stepInHidden == true && currentValue <= timeStep._end.valueOf()) { - // var newValue = moment(endDate); - // timeStep.current = newValue.toDate(); - // } - //}; - /** - * replaces the Core toScreen methods - * @param Core - * @param time - * @param width - * @returns {number} + * Resize the center position based on the current values in this.defaultXCenter + * and this.defaultYCenter (which are strings with a percentage or a value + * in pixels). The center positions are the variables this.xCenter + * and this.yCenter */ - exports.toScreen = function(Core, time, width) { - if (Core.body.hiddenDates.length == 0) { - var conversion = Core.range.conversion(width); - return (time.valueOf() - conversion.offset) * conversion.scale; + Graph3d.prototype._resizeCenter = function() { + // calculate the horizontal center position + if (this.defaultXCenter.charAt(this.defaultXCenter.length-1) === '%') { + this.xcenter = + parseFloat(this.defaultXCenter) / 100 * + this.frame.canvas.clientWidth; } else { - var hidden = exports.isHidden(time, Core.body.hiddenDates) - if (hidden.hidden == true) { - time = hidden.startDate; - } - - var duration = exports.getHiddenDurationBetween(Core.body.hiddenDates, Core.range.start, Core.range.end); - time = exports.correctTimeForHidden(Core.body.hiddenDates, Core.range, time); + this.xcenter = parseFloat(this.defaultXCenter); // supposed to be in px + } - var conversion = Core.range.conversion(width, duration); - return (time.valueOf() - conversion.offset) * conversion.scale; + // calculate the vertical center position + if (this.defaultYCenter.charAt(this.defaultYCenter.length-1) === '%') { + this.ycenter = + parseFloat(this.defaultYCenter) / 100 * + (this.frame.canvas.clientHeight - this.frame.filter.clientHeight); + } + else { + this.ycenter = parseFloat(this.defaultYCenter); // supposed to be in px } }; - /** - * Replaces the core toTime methods - * @param body - * @param range - * @param x - * @param width - * @returns {Date} + * Set the rotation and distance of the camera + * @param {Object} pos An object with the camera position. The object + * contains three parameters: + * - horizontal {Number} + * The horizontal rotation, between 0 and 2*PI. + * Optional, can be left undefined. + * - vertical {Number} + * The vertical rotation, between 0 and 0.5*PI + * if vertical=0.5*PI, the graph is shown from the + * top. Optional, can be left undefined. + * - distance {Number} + * The (normalized) distance of the camera to the + * center of the graph, a value between 0.71 and 5.0. + * Optional, can be left undefined. */ - exports.toTime = function(Core, x, width) { - if (Core.body.hiddenDates.length == 0) { - var conversion = Core.range.conversion(width); - return new Date(x / conversion.scale + conversion.offset); + Graph3d.prototype.setCameraPosition = function(pos) { + if (pos === undefined) { + return; } - else { - var hiddenDuration = exports.getHiddenDurationBetween(Core.body.hiddenDates, Core.range.start, Core.range.end); - var totalDuration = Core.range.end - Core.range.start - hiddenDuration; - var partialDuration = totalDuration * x / width; - var accumulatedHiddenDuration = exports.getAccumulatedHiddenDuration(Core.body.hiddenDates, Core.range, partialDuration); - var newTime = new Date(accumulatedHiddenDuration + partialDuration + Core.range.start); - return newTime; + if (pos.horizontal !== undefined && pos.vertical !== undefined) { + this.camera.setArmRotation(pos.horizontal, pos.vertical); + } + + if (pos.distance !== undefined) { + this.camera.setArmLength(pos.distance); } + + this.redraw(); }; /** - * Support function - * - * @param hiddenDates - * @param range - * @returns {number} + * Retrieve the current camera rotation + * @return {object} An object with parameters horizontal, vertical, and + * distance */ - exports.getHiddenDurationBetween = function(hiddenDates, start, end) { - var duration = 0; - for (var i = 0; i < hiddenDates.length; i++) { - var startDate = hiddenDates[i].start; - var endDate = hiddenDates[i].end; - // if time after the cutout, and the - if (startDate >= start && endDate < end) { - duration += endDate - startDate; - } - } - return duration; + Graph3d.prototype.getCameraPosition = function() { + var pos = this.camera.getArmRotation(); + pos.distance = this.camera.getArmLength(); + return pos; }; - /** - * Support function - * @param hiddenDates - * @param range - * @param time - * @returns {{duration: number, time: *, offset: number}} + * Load data into the 3D Graph */ - exports.correctTimeForHidden = function(hiddenDates, range, time) { - time = moment(time).toDate().valueOf(); - time -= exports.getHiddenDurationBefore(hiddenDates,range,time); - return time; + Graph3d.prototype._readData = function(data) { + // read the data + this._dataInitialize(data, this.style); + + + if (this.dataFilter) { + // apply filtering + this.dataPoints = this.dataFilter._getDataPoints(); + } + else { + // no filtering. load all data + this.dataPoints = this._getDataPoints(this.dataTable); + } + + // draw the filter + this._redrawFilter(); }; - exports.getHiddenDurationBefore = function(hiddenDates, range, time) { - var timeOffset = 0; - time = moment(time).toDate().valueOf(); + /** + * Replace the dataset of the Graph3d + * @param {Array | DataSet | DataView} data + */ + Graph3d.prototype.setData = function (data) { + this._readData(data); + this.redraw(); - for (var i = 0; i < hiddenDates.length; i++) { - var startDate = hiddenDates[i].start; - var endDate = hiddenDates[i].end; - // if time after the cutout, and the - if (startDate >= range.start && endDate < range.end) { - if (time >= endDate) { - timeOffset += (endDate - startDate); - } - } + // start animation when option is true + if (this.animationAutoStart && this.dataFilter) { + this.animationStart(); } - return timeOffset; - } + }; /** - * sum the duration from start to finish, including the hidden duration, - * until the required amount has been reached, return the accumulated hidden duration - * @param hiddenDates - * @param range - * @param time - * @returns {{duration: number, time: *, offset: number}} + * Update the options. Options will be merged with current options + * @param {Object} options */ - exports.getAccumulatedHiddenDuration = function(hiddenDates, range, requiredDuration) { - var hiddenDuration = 0; - var duration = 0; - var previousPoint = range.start; - //exports.printDates(hiddenDates) - for (var i = 0; i < hiddenDates.length; i++) { - var startDate = hiddenDates[i].start; - var endDate = hiddenDates[i].end; - // if time after the cutout, and the - if (startDate >= range.start && endDate < range.end) { - duration += startDate - previousPoint; - previousPoint = endDate; - if (duration >= requiredDuration) { - break; - } - else { - hiddenDuration += endDate - startDate; + Graph3d.prototype.setOptions = function (options) { + var cameraPosition = undefined; + + this.animationStop(); + + if (options !== undefined) { + // retrieve parameter values + if (options.width !== undefined) this.width = options.width; + if (options.height !== undefined) this.height = options.height; + + if (options.xCenter !== undefined) this.defaultXCenter = options.xCenter; + if (options.yCenter !== undefined) this.defaultYCenter = options.yCenter; + + if (options.filterLabel !== undefined) this.filterLabel = options.filterLabel; + if (options.legendLabel !== undefined) this.legendLabel = options.legendLabel; + if (options.xLabel !== undefined) this.xLabel = options.xLabel; + if (options.yLabel !== undefined) this.yLabel = options.yLabel; + if (options.zLabel !== undefined) this.zLabel = options.zLabel; + + if (options.xValueLabel !== undefined) this.xValueLabel = options.xValueLabel; + if (options.yValueLabel !== undefined) this.yValueLabel = options.yValueLabel; + if (options.zValueLabel !== undefined) this.zValueLabel = options.zValueLabel; + + if (options.style !== undefined) { + var styleNumber = this._getStyleNumber(options.style); + if (styleNumber !== -1) { + this.style = styleNumber; } } - } + if (options.showGrid !== undefined) this.showGrid = options.showGrid; + if (options.showPerspective !== undefined) this.showPerspective = options.showPerspective; + if (options.showShadow !== undefined) this.showShadow = options.showShadow; + if (options.tooltip !== undefined) this.showTooltip = options.tooltip; + if (options.showAnimationControls !== undefined) this.showAnimationControls = options.showAnimationControls; + if (options.keepAspectRatio !== undefined) this.keepAspectRatio = options.keepAspectRatio; + if (options.verticalRatio !== undefined) this.verticalRatio = options.verticalRatio; - return hiddenDuration; - }; + if (options.animationInterval !== undefined) this.animationInterval = options.animationInterval; + if (options.animationPreload !== undefined) this.animationPreload = options.animationPreload; + if (options.animationAutoStart !== undefined)this.animationAutoStart = options.animationAutoStart; + if (options.xBarWidth !== undefined) this.defaultXBarWidth = options.xBarWidth; + if (options.yBarWidth !== undefined) this.defaultYBarWidth = options.yBarWidth; + if (options.xMin !== undefined) this.defaultXMin = options.xMin; + if (options.xStep !== undefined) this.defaultXStep = options.xStep; + if (options.xMax !== undefined) this.defaultXMax = options.xMax; + if (options.yMin !== undefined) this.defaultYMin = options.yMin; + if (options.yStep !== undefined) this.defaultYStep = options.yStep; + if (options.yMax !== undefined) this.defaultYMax = options.yMax; + if (options.zMin !== undefined) this.defaultZMin = options.zMin; + if (options.zStep !== undefined) this.defaultZStep = options.zStep; + if (options.zMax !== undefined) this.defaultZMax = options.zMax; + if (options.valueMin !== undefined) this.defaultValueMin = options.valueMin; + if (options.valueMax !== undefined) this.defaultValueMax = options.valueMax; - /** - * used to step over to either side of a hidden block. Correction is disabled on tablets, might be set to true - * @param hiddenDates - * @param time - * @param direction - * @param correctionEnabled - * @returns {*} - */ - exports.snapAwayFromHidden = function(hiddenDates, time, direction, correctionEnabled) { - var isHidden = exports.isHidden(time, hiddenDates); - if (isHidden.hidden == true) { - if (direction < 0) { - if (correctionEnabled == true) { - return isHidden.startDate - (isHidden.endDate - time) - 1; - } - else { - return isHidden.startDate - 1; - } + if (options.cameraPosition !== undefined) cameraPosition = options.cameraPosition; + + if (cameraPosition !== undefined) { + this.camera.setArmRotation(cameraPosition.horizontal, cameraPosition.vertical); + this.camera.setArmLength(cameraPosition.distance); } else { - if (correctionEnabled == true) { - return isHidden.endDate + (time - isHidden.startDate) + 1; - } - else { - return isHidden.endDate + 1; - } + this.camera.setArmRotation(1.0, 0.5); + this.camera.setArmLength(1.7); } } - else { - return time; - } - } + this._setBackgroundColor(options && options.backgroundColor); + this.setSize(this.width, this.height); - /** - * Check if a time is hidden - * - * @param time - * @param hiddenDates - * @returns {{hidden: boolean, startDate: Window.start, endDate: *}} - */ - exports.isHidden = function(time, hiddenDates) { - for (var i = 0; i < hiddenDates.length; i++) { - var startDate = hiddenDates[i].start; - var endDate = hiddenDates[i].end; - - if (time >= startDate && time < endDate) { // if the start is entering a hidden zone - return {hidden: true, startDate: startDate, endDate: endDate}; - break; - } + // re-load the data + if (this.dataTable) { + this.setData(this.dataTable); } - return {hidden: false, startDate: startDate, endDate: endDate}; - } -/***/ }, -/* 16 */ -/***/ function(module, exports, __webpack_require__) { + // start animation when option is true + if (this.animationAutoStart && this.dataFilter) { + this.animationStart(); + } + }; /** - * @constructor DataStep - * The class DataStep is an iterator for data for the lineGraph. You provide a start data point and an - * end data point. The class itself determines the best scale (step size) based on the - * provided start Date, end Date, and minimumStep. - * - * If minimumStep is provided, the step size is chosen as close as possible - * to the minimumStep but larger than minimumStep. If minimumStep is not - * provided, the scale is set to 1 DAY. - * The minimumStep should correspond with the onscreen size of about 6 characters - * - * Alternatively, you can set a scale by hand. - * After creation, you can initialize the class by executing first(). Then you - * can iterate from the start date to the end date via next(). You can check if - * the end date is reached with the function hasNext(). After each step, you can - * retrieve the current date via getCurrent(). - * The DataStep has scales ranging from milliseconds, seconds, minutes, hours, - * days, to years. - * - * Version: 1.2 - * - * @param {Date} [start] The start date, for example new Date(2010, 9, 21) - * or new Date(2010, 9, 21, 23, 45, 00) - * @param {Date} [end] The end date - * @param {Number} [minimumStep] Optional. Minimum step size in milliseconds + * Redraw the Graph. */ - function DataStep(start, end, minimumStep, containerHeight, customRange, alignZeros) { - // variables - this.current = 0; - - this.autoScale = true; - this.stepIndex = 0; - this.step = 1; - this.scale = 1; + Graph3d.prototype.redraw = function() { + if (this.dataPoints === undefined) { + throw 'Error: graph data not initialized'; + } - this.marginStart; - this.marginEnd; - this.deadSpace = 0; + this._resizeCanvas(); + this._resizeCenter(); + this._redrawSlider(); + this._redrawClear(); + this._redrawAxis(); - this.majorSteps = [1, 2, 5, 10]; - this.minorSteps = [0.25, 0.5, 1, 2]; + if (this.style === Graph3d.STYLE.GRID || + this.style === Graph3d.STYLE.SURFACE) { + this._redrawDataGrid(); + } + else if (this.style === Graph3d.STYLE.LINE) { + this._redrawDataLine(); + } + else if (this.style === Graph3d.STYLE.BAR || + this.style === Graph3d.STYLE.BARCOLOR || + this.style === Graph3d.STYLE.BARSIZE) { + this._redrawDataBar(); + } + else { + // style is DOT, DOTLINE, DOTCOLOR, DOTSIZE + this._redrawDataDot(); + } - this.alignZeros = alignZeros; + this._redrawInfo(); + this._redrawLegend(); + }; - this.setRange(start, end, minimumStep, containerHeight, customRange); - } + /** + * Clear the canvas before redrawing + */ + Graph3d.prototype._redrawClear = function() { + var canvas = this.frame.canvas; + var ctx = canvas.getContext('2d'); + ctx.clearRect(0, 0, canvas.width, canvas.height); + }; /** - * Set a new range - * If minimumStep is provided, the step size is chosen as close as possible - * to the minimumStep but larger than minimumStep. If minimumStep is not - * provided, the scale is set to 1 DAY. - * The minimumStep should correspond with the onscreen size of about 6 characters - * @param {Number} [start] The start date and time. - * @param {Number} [end] The end date and time. - * @param {Number} [minimumStep] Optional. Minimum step size in milliseconds + * Redraw the legend showing the colors */ - DataStep.prototype.setRange = function(start, end, minimumStep, containerHeight, customRange) { - this._start = customRange.min === undefined ? start : customRange.min; - this._end = customRange.max === undefined ? end : customRange.max; + Graph3d.prototype._redrawLegend = function() { + var y; - if (this._start == this._end) { - this._start -= 0.75; - this._end += 1; - } + if (this.style === Graph3d.STYLE.DOTCOLOR || + this.style === Graph3d.STYLE.DOTSIZE) { - if (this.autoScale == true) { - this.setMinimumStep(minimumStep, containerHeight); + var dotSize = this.frame.clientWidth * 0.02; + + var widthMin, widthMax; + if (this.style === Graph3d.STYLE.DOTSIZE) { + widthMin = dotSize / 2; // px + widthMax = dotSize / 2 + dotSize * 2; // Todo: put this in one function + } + else { + widthMin = 20; // px + widthMax = 20; // px + } + + var height = Math.max(this.frame.clientHeight * 0.25, 100); + var top = this.margin; + var right = this.frame.clientWidth - this.margin; + var left = right - widthMax; + var bottom = top + height; } - this.setFirst(customRange); - }; + var canvas = this.frame.canvas; + var ctx = canvas.getContext('2d'); + ctx.lineWidth = 1; + ctx.font = '14px arial'; // TODO: put in options - /** - * Automatically determine the scale that bests fits the provided minimum step - * @param {Number} [minimumStep] The minimum step size in milliseconds - */ - DataStep.prototype.setMinimumStep = function(minimumStep, containerHeight) { - // round to floor - var size = this._end - this._start; - var safeSize = size * 1.2; - var minimumStepValue = minimumStep * (safeSize / containerHeight); - var orderOfMagnitude = Math.round(Math.log(safeSize)/Math.LN10); + if (this.style === Graph3d.STYLE.DOTCOLOR) { + // draw the color bar + var ymin = 0; + var ymax = height; // Todo: make height customizable + for (y = ymin; y < ymax; y++) { + var f = (y - ymin) / (ymax - ymin); - var minorStepIdx = -1; - var magnitudefactor = Math.pow(10,orderOfMagnitude); + //var width = (dotSize / 2 + (1-f) * dotSize * 2); // Todo: put this in one function + var hue = f * 240; + var color = this._hsv2rgb(hue, 1, 1); - var start = 0; - if (orderOfMagnitude < 0) { - start = orderOfMagnitude; + ctx.strokeStyle = color; + ctx.beginPath(); + ctx.moveTo(left, top + y); + ctx.lineTo(right, top + y); + ctx.stroke(); + } + + ctx.strokeStyle = this.colorAxis; + ctx.strokeRect(left, top, widthMax, height); } - var solutionFound = false; - for (var i = start; Math.abs(i) <= Math.abs(orderOfMagnitude); i++) { - magnitudefactor = Math.pow(10,i); - for (var j = 0; j < this.minorSteps.length; j++) { - var stepSize = magnitudefactor * this.minorSteps[j]; - if (stepSize >= minimumStepValue) { - solutionFound = true; - minorStepIdx = j; - break; - } + if (this.style === Graph3d.STYLE.DOTSIZE) { + // draw border around color bar + ctx.strokeStyle = this.colorAxis; + ctx.fillStyle = this.colorDot; + ctx.beginPath(); + ctx.moveTo(left, top); + ctx.lineTo(right, top); + ctx.lineTo(right - widthMax + widthMin, bottom); + ctx.lineTo(left, bottom); + ctx.closePath(); + ctx.fill(); + ctx.stroke(); + } + + if (this.style === Graph3d.STYLE.DOTCOLOR || + this.style === Graph3d.STYLE.DOTSIZE) { + // print values along the color bar + var gridLineLen = 5; // px + var step = new StepNumber(this.valueMin, this.valueMax, (this.valueMax-this.valueMin)/5, true); + step.start(); + if (step.getCurrent() < this.valueMin) { + step.next(); } - if (solutionFound == true) { - break; + while (!step.end()) { + y = bottom - (step.getCurrent() - this.valueMin) / (this.valueMax - this.valueMin) * height; + + ctx.beginPath(); + ctx.moveTo(left - gridLineLen, y); + ctx.lineTo(left, y); + ctx.stroke(); + + ctx.textAlign = 'right'; + ctx.textBaseline = 'middle'; + ctx.fillStyle = this.colorAxis; + ctx.fillText(step.getCurrent(), left - 2 * gridLineLen, y); + + step.next(); } + + ctx.textAlign = 'right'; + ctx.textBaseline = 'top'; + var label = this.legendLabel; + ctx.fillText(label, right, bottom + this.margin); } - this.stepIndex = minorStepIdx; - this.scale = magnitudefactor; - this.step = magnitudefactor * this.minorSteps[minorStepIdx]; }; - - /** - * Round the current date to the first minor date value - * This must be executed once when the current date is set to start Date + * Redraw the filter */ - DataStep.prototype.setFirst = function(customRange) { - if (customRange === undefined) { - customRange = {}; - } - - var niceStart = customRange.min === undefined ? this._start - (this.scale * 2 * this.minorSteps[this.stepIndex]) : customRange.min; - var niceEnd = customRange.max === undefined ? this._end + (this.scale * this.minorSteps[this.stepIndex]) : customRange.max; + Graph3d.prototype._redrawFilter = function() { + this.frame.filter.innerHTML = ''; - this.marginEnd = customRange.max === undefined ? this.roundToMinor(niceEnd) : customRange.max; - this.marginStart = customRange.min === undefined ? this.roundToMinor(niceStart) : customRange.min; + if (this.dataFilter) { + var options = { + 'visible': this.showAnimationControls + }; + var slider = new Slider(this.frame.filter, options); + this.frame.filter.slider = slider; - // if we need to align the zero's we need to make sure that there is a zero to use. - if (this.alignZeros == true && (this.marginEnd - this.marginStart) % this.step != 0) { - this.marginEnd += this.marginEnd % this.step; - } + // TODO: css here is not nice here... + this.frame.filter.style.padding = '10px'; + //this.frame.filter.style.backgroundColor = '#EFEFEF'; - this.deadSpace = this.roundToMinor(niceEnd) - niceEnd + this.roundToMinor(niceStart) - niceStart; - this.marginRange = this.marginEnd - this.marginStart; + slider.setValues(this.dataFilter.values); + slider.setPlayInterval(this.animationInterval); + // create an event handler + var me = this; + var onchange = function () { + var index = slider.getIndex(); - this.current = this.marginEnd; - }; + me.dataFilter.selectValue(index); + me.dataPoints = me.dataFilter._getDataPoints(); - DataStep.prototype.roundToMinor = function(value) { - var rounded = value - (value % (this.scale * this.minorSteps[this.stepIndex])); - if (value % (this.scale * this.minorSteps[this.stepIndex]) > 0.5 * (this.scale * this.minorSteps[this.stepIndex])) { - return rounded + (this.scale * this.minorSteps[this.stepIndex]); + me.redraw(); + }; + slider.setOnChangeCallback(onchange); } else { - return rounded; + this.frame.filter.slider = undefined; } - } - + }; /** - * Check if the there is a next step - * @return {boolean} true if the current date has not passed the end date + * Redraw the slider */ - DataStep.prototype.hasNext = function () { - return (this.current >= this.marginStart); + Graph3d.prototype._redrawSlider = function() { + if ( this.frame.filter.slider !== undefined) { + this.frame.filter.slider.redraw(); + } }; + /** - * Do the next step + * Redraw common information */ - DataStep.prototype.next = function() { - var prev = this.current; - this.current -= this.step; + Graph3d.prototype._redrawInfo = function() { + if (this.dataFilter) { + var canvas = this.frame.canvas; + var ctx = canvas.getContext('2d'); - // safety mechanism: if current time is still unchanged, move to the end - if (this.current == prev) { - this.current = this._end; + ctx.font = '14px arial'; // TODO: put in options + ctx.lineStyle = 'gray'; + ctx.fillStyle = 'gray'; + ctx.textAlign = 'left'; + ctx.textBaseline = 'top'; + + var x = this.margin; + var y = this.margin; + ctx.fillText(this.dataFilter.getLabel() + ': ' + this.dataFilter.getSelectedValue(), x, y); } }; + /** - * Do the next step + * Redraw the axis */ - DataStep.prototype.previous = function() { - this.current += this.step; - this.marginEnd += this.step; - this.marginRange = this.marginEnd - this.marginStart; - }; + Graph3d.prototype._redrawAxis = function() { + var canvas = this.frame.canvas, + ctx = canvas.getContext('2d'), + from, to, step, prettyStep, + text, xText, yText, zText, + offset, xOffset, yOffset, + xMin2d, xMax2d; + // TODO: get the actual rendered style of the containerElement + //ctx.font = this.containerElement.style.font; + ctx.font = 24 / this.camera.getArmLength() + 'px arial'; + // calculate the length for the short grid lines + var gridLenX = 0.025 / this.scale.x; + var gridLenY = 0.025 / this.scale.y; + var textMargin = 5 / this.camera.getArmLength(); // px + var armAngle = this.camera.getArmRotation().horizontal; - /** - * Get the current datetime - * @return {String} current The current date - */ - DataStep.prototype.getCurrent = function(decimals) { - // prevent round-off errors when close to zero - var current = (Math.abs(this.current) < this.step / 2) ? 0 : this.current; - var toPrecision = '' + Number(current).toPrecision(5); + // draw x-grid lines + ctx.lineWidth = 1; + prettyStep = (this.defaultXStep === undefined); + step = new StepNumber(this.xMin, this.xMax, this.xStep, prettyStep); + step.start(); + if (step.getCurrent() < this.xMin) { + step.next(); + } + while (!step.end()) { + var x = step.getCurrent(); - // If decimals is specified, then limit or extend the string as required - if(decimals !== undefined && !isNaN(Number(decimals))) { - // If string includes exponent, then we need to add it to the end - var exp = ""; - var index = toPrecision.indexOf("e"); - if(index != -1) { - // Get the exponent - exp = toPrecision.slice(index); - // Remove the exponent in case we need to zero-extend - toPrecision = toPrecision.slice(0, index); + if (this.showGrid) { + from = this._convert3Dto2D(new Point3d(x, this.yMin, this.zMin)); + to = this._convert3Dto2D(new Point3d(x, this.yMax, this.zMin)); + ctx.strokeStyle = this.colorGrid; + ctx.beginPath(); + ctx.moveTo(from.x, from.y); + ctx.lineTo(to.x, to.y); + ctx.stroke(); } - index = Math.max(toPrecision.indexOf(","), toPrecision.indexOf(".")); - if(index === -1) { - // No decimal found - if we want decimals, then we need to add it - if(decimals !== 0) { - toPrecision += '.'; - } - // Calculate how long the string should be - index = toPrecision.length + decimals; + else { + from = this._convert3Dto2D(new Point3d(x, this.yMin, this.zMin)); + to = this._convert3Dto2D(new Point3d(x, this.yMin+gridLenX, this.zMin)); + ctx.strokeStyle = this.colorAxis; + ctx.beginPath(); + ctx.moveTo(from.x, from.y); + ctx.lineTo(to.x, to.y); + ctx.stroke(); + + from = this._convert3Dto2D(new Point3d(x, this.yMax, this.zMin)); + to = this._convert3Dto2D(new Point3d(x, this.yMax-gridLenX, this.zMin)); + ctx.strokeStyle = this.colorAxis; + ctx.beginPath(); + ctx.moveTo(from.x, from.y); + ctx.lineTo(to.x, to.y); + ctx.stroke(); } - else if(decimals !== 0) { - // Calculate how long the string should be - accounting for the decimal place - index += decimals + 1; + + yText = (Math.cos(armAngle) > 0) ? this.yMin : this.yMax; + text = this._convert3Dto2D(new Point3d(x, yText, this.zMin)); + if (Math.cos(armAngle * 2) > 0) { + ctx.textAlign = 'center'; + ctx.textBaseline = 'top'; + text.y += textMargin; } - if(index > toPrecision.length) { - // We need to add zeros! - for(var cnt = index - toPrecision.length; cnt > 0; cnt--) { - toPrecision += '0'; - } + else if (Math.sin(armAngle * 2) < 0){ + ctx.textAlign = 'right'; + ctx.textBaseline = 'middle'; } else { - // we need to remove characters - toPrecision = toPrecision.slice(0, index); + ctx.textAlign = 'left'; + ctx.textBaseline = 'middle'; } - // Add the exponent if there is one - toPrecision += exp; + ctx.fillStyle = this.colorAxis; + ctx.fillText(' ' + this.xValueLabel(step.getCurrent()) + ' ', text.x, text.y); + + step.next(); } - else { - if (toPrecision.indexOf(",") != -1 || toPrecision.indexOf(".") != -1) { - // If no decimal is specified, and there are decimal places, remove trailing zeros - for (var i = toPrecision.length - 1; i > 0; i--) { - if (toPrecision[i] == "0") { - toPrecision = toPrecision.slice(0, i); - } - else if (toPrecision[i] == "." || toPrecision[i] == ",") { - toPrecision = toPrecision.slice(0, i); - break; - } - else { - break; - } - } - } + + // draw y-grid lines + ctx.lineWidth = 1; + prettyStep = (this.defaultYStep === undefined); + step = new StepNumber(this.yMin, this.yMax, this.yStep, prettyStep); + step.start(); + if (step.getCurrent() < this.yMin) { + step.next(); } + while (!step.end()) { + if (this.showGrid) { + from = this._convert3Dto2D(new Point3d(this.xMin, step.getCurrent(), this.zMin)); + to = this._convert3Dto2D(new Point3d(this.xMax, step.getCurrent(), this.zMin)); + ctx.strokeStyle = this.colorGrid; + ctx.beginPath(); + ctx.moveTo(from.x, from.y); + ctx.lineTo(to.x, to.y); + ctx.stroke(); + } + else { + from = this._convert3Dto2D(new Point3d(this.xMin, step.getCurrent(), this.zMin)); + to = this._convert3Dto2D(new Point3d(this.xMin+gridLenY, step.getCurrent(), this.zMin)); + ctx.strokeStyle = this.colorAxis; + ctx.beginPath(); + ctx.moveTo(from.x, from.y); + ctx.lineTo(to.x, to.y); + ctx.stroke(); - return toPrecision; - }; + from = this._convert3Dto2D(new Point3d(this.xMax, step.getCurrent(), this.zMin)); + to = this._convert3Dto2D(new Point3d(this.xMax-gridLenY, step.getCurrent(), this.zMin)); + ctx.strokeStyle = this.colorAxis; + ctx.beginPath(); + ctx.moveTo(from.x, from.y); + ctx.lineTo(to.x, to.y); + ctx.stroke(); + } + xText = (Math.sin(armAngle ) > 0) ? this.xMin : this.xMax; + text = this._convert3Dto2D(new Point3d(xText, step.getCurrent(), this.zMin)); + if (Math.cos(armAngle * 2) < 0) { + ctx.textAlign = 'center'; + ctx.textBaseline = 'top'; + text.y += textMargin; + } + else if (Math.sin(armAngle * 2) > 0){ + ctx.textAlign = 'right'; + ctx.textBaseline = 'middle'; + } + else { + ctx.textAlign = 'left'; + ctx.textBaseline = 'middle'; + } + ctx.fillStyle = this.colorAxis; + ctx.fillText(' ' + this.yValueLabel(step.getCurrent()) + ' ', text.x, text.y); + step.next(); + } - /** - * Snap a date to a rounded value. - * The snap intervals are dependent on the current scale and step. - * @param {Date} date the date to be snapped. - * @return {Date} snappedDate - */ - DataStep.prototype.snap = function(date) { + // draw z-grid lines and axis + ctx.lineWidth = 1; + prettyStep = (this.defaultZStep === undefined); + step = new StepNumber(this.zMin, this.zMax, this.zStep, prettyStep); + step.start(); + if (step.getCurrent() < this.zMin) { + step.next(); + } + xText = (Math.cos(armAngle ) > 0) ? this.xMin : this.xMax; + yText = (Math.sin(armAngle ) < 0) ? this.yMin : this.yMax; + while (!step.end()) { + // TODO: make z-grid lines really 3d? + from = this._convert3Dto2D(new Point3d(xText, yText, step.getCurrent())); + ctx.strokeStyle = this.colorAxis; + ctx.beginPath(); + ctx.moveTo(from.x, from.y); + ctx.lineTo(from.x - textMargin, from.y); + ctx.stroke(); - }; + ctx.textAlign = 'right'; + ctx.textBaseline = 'middle'; + ctx.fillStyle = this.colorAxis; + ctx.fillText(this.zValueLabel(step.getCurrent()) + ' ', from.x - 5, from.y); - /** - * Check if the current value is a major value (for example when the step - * is DAY, a major value is each first day of the MONTH) - * @return {boolean} true if current date is major, else false. - */ - DataStep.prototype.isMajor = function() { - return (this.current % (this.scale * this.majorSteps[this.stepIndex]) == 0); - }; + step.next(); + } + ctx.lineWidth = 1; + from = this._convert3Dto2D(new Point3d(xText, yText, this.zMin)); + to = this._convert3Dto2D(new Point3d(xText, yText, this.zMax)); + ctx.strokeStyle = this.colorAxis; + ctx.beginPath(); + ctx.moveTo(from.x, from.y); + ctx.lineTo(to.x, to.y); + ctx.stroke(); - module.exports = DataStep; + // draw x-axis + ctx.lineWidth = 1; + // line at yMin + xMin2d = this._convert3Dto2D(new Point3d(this.xMin, this.yMin, this.zMin)); + xMax2d = this._convert3Dto2D(new Point3d(this.xMax, this.yMin, this.zMin)); + ctx.strokeStyle = this.colorAxis; + ctx.beginPath(); + ctx.moveTo(xMin2d.x, xMin2d.y); + ctx.lineTo(xMax2d.x, xMax2d.y); + ctx.stroke(); + // line at ymax + xMin2d = this._convert3Dto2D(new Point3d(this.xMin, this.yMax, this.zMin)); + xMax2d = this._convert3Dto2D(new Point3d(this.xMax, this.yMax, this.zMin)); + ctx.strokeStyle = this.colorAxis; + ctx.beginPath(); + ctx.moveTo(xMin2d.x, xMin2d.y); + ctx.lineTo(xMax2d.x, xMax2d.y); + ctx.stroke(); + + // draw y-axis + ctx.lineWidth = 1; + // line at xMin + from = this._convert3Dto2D(new Point3d(this.xMin, this.yMin, this.zMin)); + to = this._convert3Dto2D(new Point3d(this.xMin, this.yMax, this.zMin)); + ctx.strokeStyle = this.colorAxis; + ctx.beginPath(); + ctx.moveTo(from.x, from.y); + ctx.lineTo(to.x, to.y); + ctx.stroke(); + // line at xMax + from = this._convert3Dto2D(new Point3d(this.xMax, this.yMin, this.zMin)); + to = this._convert3Dto2D(new Point3d(this.xMax, this.yMax, this.zMin)); + ctx.strokeStyle = this.colorAxis; + ctx.beginPath(); + ctx.moveTo(from.x, from.y); + ctx.lineTo(to.x, to.y); + ctx.stroke(); + // draw x-label + var xLabel = this.xLabel; + if (xLabel.length > 0) { + yOffset = 0.1 / this.scale.y; + xText = (this.xMin + this.xMax) / 2; + yText = (Math.cos(armAngle) > 0) ? this.yMin - yOffset: this.yMax + yOffset; + text = this._convert3Dto2D(new Point3d(xText, yText, this.zMin)); + if (Math.cos(armAngle * 2) > 0) { + ctx.textAlign = 'center'; + ctx.textBaseline = 'top'; + } + else if (Math.sin(armAngle * 2) < 0){ + ctx.textAlign = 'right'; + ctx.textBaseline = 'middle'; + } + else { + ctx.textAlign = 'left'; + ctx.textBaseline = 'middle'; + } + ctx.fillStyle = this.colorAxis; + ctx.fillText(xLabel, text.x, text.y); + } -/***/ }, -/* 17 */ -/***/ function(module, exports, __webpack_require__) { + // draw y-label + var yLabel = this.yLabel; + if (yLabel.length > 0) { + xOffset = 0.1 / this.scale.x; + xText = (Math.sin(armAngle ) > 0) ? this.xMin - xOffset : this.xMax + xOffset; + yText = (this.yMin + this.yMax) / 2; + text = this._convert3Dto2D(new Point3d(xText, yText, this.zMin)); + if (Math.cos(armAngle * 2) < 0) { + ctx.textAlign = 'center'; + ctx.textBaseline = 'top'; + } + else if (Math.sin(armAngle * 2) > 0){ + ctx.textAlign = 'right'; + ctx.textBaseline = 'middle'; + } + else { + ctx.textAlign = 'left'; + ctx.textBaseline = 'middle'; + } + ctx.fillStyle = this.colorAxis; + ctx.fillText(yLabel, text.x, text.y); + } - var util = __webpack_require__(1); - var hammerUtil = __webpack_require__(47); - var moment = __webpack_require__(44); - var Component = __webpack_require__(20); - var DateUtil = __webpack_require__(15); + // draw z-label + var zLabel = this.zLabel; + if (zLabel.length > 0) { + offset = 30; // pixels. // TODO: relate to the max width of the values on the z axis? + xText = (Math.cos(armAngle ) > 0) ? this.xMin : this.xMax; + yText = (Math.sin(armAngle ) < 0) ? this.yMin : this.yMax; + zText = (this.zMin + this.zMax) / 2; + text = this._convert3Dto2D(new Point3d(xText, yText, zText)); + ctx.textAlign = 'right'; + ctx.textBaseline = 'middle'; + ctx.fillStyle = this.colorAxis; + ctx.fillText(zLabel, text.x - offset, text.y); + } + }; /** - * @constructor Range - * A Range controls a numeric range with a start and end value. - * The Range adjusts the range based on mouse events or programmatic changes, - * and triggers events when the range is changing or has been changed. - * @param {{dom: Object, domProps: Object, emitter: Emitter}} body - * @param {Object} [options] See description at Range.setOptions + * Calculate the color based on the given value. + * @param {Number} H Hue, a value be between 0 and 360 + * @param {Number} S Saturation, a value between 0 and 1 + * @param {Number} V Value, a value between 0 and 1 */ - function Range(body, options) { - var now = moment().hours(0).minutes(0).seconds(0).milliseconds(0); - this.start = now.clone().add(-3, 'days').valueOf(); // Number - this.end = now.clone().add(4, 'days').valueOf(); // Number + Graph3d.prototype._hsv2rgb = function(H, S, V) { + var R, G, B, C, Hi, X; - this.body = body; - this.deltaDifference = 0; - this.scaleOffset = 0; - this.startToFront = false; - this.endToFront = true; + C = V * S; + Hi = Math.floor(H/60); // hi = 0,1,2,3,4,5 + X = C * (1 - Math.abs(((H/60) % 2) - 1)); - // default options - this.defaultOptions = { - start: null, - end: null, - direction: 'horizontal', // 'horizontal' or 'vertical' - moveable: true, - zoomable: true, - min: null, - max: null, - zoomMin: 10, // milliseconds - zoomMax: 1000 * 60 * 60 * 24 * 365 * 10000 // milliseconds - }; - this.options = util.extend({}, this.defaultOptions); + switch (Hi) { + case 0: R = C; G = X; B = 0; break; + case 1: R = X; G = C; B = 0; break; + case 2: R = 0; G = C; B = X; break; + case 3: R = 0; G = X; B = C; break; + case 4: R = X; G = 0; B = C; break; + case 5: R = C; G = 0; B = X; break; - this.props = { - touch: {} - }; - this.animateTimer = null; + default: R = 0; G = 0; B = 0; break; + } - // drag listeners for dragging - this.body.emitter.on('dragstart', this._onDragStart.bind(this)); - this.body.emitter.on('drag', this._onDrag.bind(this)); - this.body.emitter.on('dragend', this._onDragEnd.bind(this)); + return 'RGB(' + parseInt(R*255) + ',' + parseInt(G*255) + ',' + parseInt(B*255) + ')'; + }; - // ignore dragging when holding - this.body.emitter.on('hold', this._onHold.bind(this)); - // mouse wheel for zooming - this.body.emitter.on('mousewheel', this._onMouseWheel.bind(this)); - this.body.emitter.on('DOMMouseScroll', this._onMouseWheel.bind(this)); // For FF + /** + * Draw all datapoints as a grid + * This function can be used when the style is 'grid' + */ + Graph3d.prototype._redrawDataGrid = function() { + var canvas = this.frame.canvas, + ctx = canvas.getContext('2d'), + point, right, top, cross, + i, + topSideVisible, fillStyle, strokeStyle, lineWidth, + h, s, v, zAvg; - // pinch to zoom - this.body.emitter.on('touch', this._onTouch.bind(this)); - this.body.emitter.on('pinch', this._onPinch.bind(this)); - this.setOptions(options); - } + if (this.dataPoints === undefined || this.dataPoints.length <= 0) + return; // TODO: throw exception? - Range.prototype = new Component(); + // calculate the translations and screen position of all points + for (i = 0; i < this.dataPoints.length; i++) { + var trans = this._convertPointToTranslation(this.dataPoints[i].point); + var screen = this._convertTranslationToScreen(trans); - /** - * Set options for the range controller - * @param {Object} options Available options: - * {Number | Date | String} start Start date for the range - * {Number | Date | String} end End date for the range - * {Number} min Minimum value for start - * {Number} max Maximum value for end - * {Number} zoomMin Set a minimum value for - * (end - start). - * {Number} zoomMax Set a maximum value for - * (end - start). - * {Boolean} moveable Enable moving of the range - * by dragging. True by default - * {Boolean} zoomable Enable zooming of the range - * by pinching/scrolling. True by default - */ - Range.prototype.setOptions = function (options) { - if (options) { - // copy the options that we know - var fields = ['direction', 'min', 'max', 'zoomMin', 'zoomMax', 'moveable', 'zoomable', 'activate', 'hiddenDates']; - util.selectiveExtend(fields, this.options, options); + this.dataPoints[i].trans = trans; + this.dataPoints[i].screen = screen; - if ('start' in options || 'end' in options) { - // apply a new range. both start and end are optional - this.setRange(options.start, options.end); - } + // calculate the translation of the point at the bottom (needed for sorting) + var transBottom = this._convertPointToTranslation(this.dataPoints[i].bottom); + this.dataPoints[i].dist = this.showPerspective ? transBottom.length() : -transBottom.z; } - }; - /** - * Test whether direction has a valid value - * @param {String} direction 'horizontal' or 'vertical' - */ - function validateDirection (direction) { - if (direction != 'horizontal' && direction != 'vertical') { - throw new TypeError('Unknown direction "' + direction + '". ' + - 'Choose "horizontal" or "vertical".'); - } - } + // sort the points on depth of their (x,y) position (not on z) + var sortDepth = function (a, b) { + return b.dist - a.dist; + }; + this.dataPoints.sort(sortDepth); - /** - * Set a new start and end range - * @param {Date | Number | String} [start] - * @param {Date | Number | String} [end] - * @param {boolean | number} [animate=false] If true, the range is animated - * smoothly to the new window. - * If animate is a number, the - * number is taken as duration - * Default duration is 500 ms. - * @param {Boolean} [byUser=false] - * - */ - Range.prototype.setRange = function(start, end, animate, byUser) { - if (byUser !== true) { - byUser = false; - } - var _start = start != undefined ? util.convert(start, 'Date').valueOf() : null; - var _end = end != undefined ? util.convert(end, 'Date').valueOf() : null; - this._cancelAnimation(); + if (this.style === Graph3d.STYLE.SURFACE) { + for (i = 0; i < this.dataPoints.length; i++) { + point = this.dataPoints[i]; + right = this.dataPoints[i].pointRight; + top = this.dataPoints[i].pointTop; + cross = this.dataPoints[i].pointCross; - if (animate) { - var me = this; - var initStart = this.start; - var initEnd = this.end; - var duration = typeof animate === 'number' ? animate : 500; - var initTime = new Date().valueOf(); - var anyChanged = false; + if (point !== undefined && right !== undefined && top !== undefined && cross !== undefined) { - var next = function () { - if (!me.props.touch.dragging) { - var now = new Date().valueOf(); - var time = now - initTime; - var done = time > duration; - var s = (done || _start === null) ? _start : util.easeInOutQuad(time, initStart, _start, duration); - var e = (done || _end === null) ? _end : util.easeInOutQuad(time, initEnd, _end, duration); + if (this.showGrayBottom || this.showShadow) { + // calculate the cross product of the two vectors from center + // to left and right, in order to know whether we are looking at the + // bottom or at the top side. We can also use the cross product + // for calculating light intensity + var aDiff = Point3d.subtract(cross.trans, point.trans); + var bDiff = Point3d.subtract(top.trans, right.trans); + var crossproduct = Point3d.crossProduct(aDiff, bDiff); + var len = crossproduct.length(); + // FIXME: there is a bug with determining the surface side (shadow or colored) - changed = me._applyRange(s, e); - DateUtil.updateHiddenDates(me.body, me.options.hiddenDates); - anyChanged = anyChanged || changed; - if (changed) { - me.body.emitter.emit('rangechange', {start: new Date(me.start), end: new Date(me.end), byUser:byUser}); + topSideVisible = (crossproduct.z > 0); + } + else { + topSideVisible = true; } - if (done) { - if (anyChanged) { - me.body.emitter.emit('rangechanged', {start: new Date(me.start), end: new Date(me.end), byUser:byUser}); + if (topSideVisible) { + // calculate Hue from the current value. At zMin the hue is 240, at zMax the hue is 0 + zAvg = (point.point.z + right.point.z + top.point.z + cross.point.z) / 4; + h = (1 - (zAvg - this.zMin) * this.scale.z / this.verticalRatio) * 240; + s = 1; // saturation + + if (this.showShadow) { + v = Math.min(1 + (crossproduct.x / len) / 2, 1); // value. TODO: scale + fillStyle = this._hsv2rgb(h, s, v); + strokeStyle = fillStyle; + } + else { + v = 1; + fillStyle = this._hsv2rgb(h, s, v); + strokeStyle = this.colorAxis; } } else { - // animate with as high as possible frame rate, leave 20 ms in between - // each to prevent the browser from blocking - me.animateTimer = setTimeout(next, 20); + fillStyle = 'gray'; + strokeStyle = this.colorAxis; } + lineWidth = 0.5; + + ctx.lineWidth = lineWidth; + ctx.fillStyle = fillStyle; + ctx.strokeStyle = strokeStyle; + ctx.beginPath(); + ctx.moveTo(point.screen.x, point.screen.y); + ctx.lineTo(right.screen.x, right.screen.y); + ctx.lineTo(cross.screen.x, cross.screen.y); + ctx.lineTo(top.screen.x, top.screen.y); + ctx.closePath(); + ctx.fill(); + ctx.stroke(); } } - - return next(); } - else { - var changed = this._applyRange(_start, _end); - DateUtil.updateHiddenDates(this.body, this.options.hiddenDates); - if (changed) { - var params = {start: new Date(this.start), end: new Date(this.end), byUser:byUser}; - this.body.emitter.emit('rangechange', params); - this.body.emitter.emit('rangechanged', params); + else { // grid style + for (i = 0; i < this.dataPoints.length; i++) { + point = this.dataPoints[i]; + right = this.dataPoints[i].pointRight; + top = this.dataPoints[i].pointTop; + + if (point !== undefined) { + if (this.showPerspective) { + lineWidth = 2 / -point.trans.z; + } + else { + lineWidth = 2 * -(this.eye.z / this.camera.getArmLength()); + } + } + + if (point !== undefined && right !== undefined) { + // calculate Hue from the current value. At zMin the hue is 240, at zMax the hue is 0 + zAvg = (point.point.z + right.point.z) / 2; + h = (1 - (zAvg - this.zMin) * this.scale.z / this.verticalRatio) * 240; + + ctx.lineWidth = lineWidth; + ctx.strokeStyle = this._hsv2rgb(h, 1, 1); + ctx.beginPath(); + ctx.moveTo(point.screen.x, point.screen.y); + ctx.lineTo(right.screen.x, right.screen.y); + ctx.stroke(); + } + + if (point !== undefined && top !== undefined) { + // calculate Hue from the current value. At zMin the hue is 240, at zMax the hue is 0 + zAvg = (point.point.z + top.point.z) / 2; + h = (1 - (zAvg - this.zMin) * this.scale.z / this.verticalRatio) * 240; + + ctx.lineWidth = lineWidth; + ctx.strokeStyle = this._hsv2rgb(h, 1, 1); + ctx.beginPath(); + ctx.moveTo(point.screen.x, point.screen.y); + ctx.lineTo(top.screen.x, top.screen.y); + ctx.stroke(); + } } } }; - /** - * Stop an animation - * @private - */ - Range.prototype._cancelAnimation = function () { - if (this.animateTimer) { - clearTimeout(this.animateTimer); - this.animateTimer = null; - } - }; /** - * Set a new start and end range. This method is the same as setRange, but - * does not trigger a range change and range changed event, and it returns - * true when the range is changed - * @param {Number} [start] - * @param {Number} [end] - * @return {Boolean} changed - * @private + * Draw all datapoints as dots. + * This function can be used when the style is 'dot' or 'dot-line' */ - Range.prototype._applyRange = function(start, end) { - var newStart = (start != null) ? util.convert(start, 'Date').valueOf() : this.start, - newEnd = (end != null) ? util.convert(end, 'Date').valueOf() : this.end, - max = (this.options.max != null) ? util.convert(this.options.max, 'Date').valueOf() : null, - min = (this.options.min != null) ? util.convert(this.options.min, 'Date').valueOf() : null, - diff; - - // check for valid number - if (isNaN(newStart) || newStart === null) { - throw new Error('Invalid start "' + start + '"'); - } - if (isNaN(newEnd) || newEnd === null) { - throw new Error('Invalid end "' + end + '"'); - } + Graph3d.prototype._redrawDataDot = function() { + var canvas = this.frame.canvas; + var ctx = canvas.getContext('2d'); + var i; - // prevent start < end - if (newEnd < newStart) { - newEnd = newStart; - } + if (this.dataPoints === undefined || this.dataPoints.length <= 0) + return; // TODO: throw exception? - // prevent start < min - if (min !== null) { - if (newStart < min) { - diff = (min - newStart); - newStart += diff; - newEnd += diff; + // calculate the translations of all points + for (i = 0; i < this.dataPoints.length; i++) { + var trans = this._convertPointToTranslation(this.dataPoints[i].point); + var screen = this._convertTranslationToScreen(trans); + this.dataPoints[i].trans = trans; + this.dataPoints[i].screen = screen; - // prevent end > max - if (max != null) { - if (newEnd > max) { - newEnd = max; - } - } - } + // calculate the distance from the point at the bottom to the camera + var transBottom = this._convertPointToTranslation(this.dataPoints[i].bottom); + this.dataPoints[i].dist = this.showPerspective ? transBottom.length() : -transBottom.z; } - // prevent end > max - if (max !== null) { - if (newEnd > max) { - diff = (newEnd - max); - newStart -= diff; - newEnd -= diff; + // order the translated points by depth + var sortDepth = function (a, b) { + return b.dist - a.dist; + }; + this.dataPoints.sort(sortDepth); - // prevent start < min - if (min != null) { - if (newStart < min) { - newStart = min; - } - } - } - } + // draw the datapoints as colored circles + var dotSize = this.frame.clientWidth * 0.02; // px + for (i = 0; i < this.dataPoints.length; i++) { + var point = this.dataPoints[i]; - // prevent (end-start) < zoomMin - if (this.options.zoomMin !== null) { - var zoomMin = parseFloat(this.options.zoomMin); - if (zoomMin < 0) { - zoomMin = 0; - } - if ((newEnd - newStart) < zoomMin) { - if ((this.end - this.start) === zoomMin) { - // ignore this action, we are already zoomed to the minimum - newStart = this.start; - newEnd = this.end; - } - else { - // zoom to the minimum - diff = (zoomMin - (newEnd - newStart)); - newStart -= diff / 2; - newEnd += diff / 2; - } + if (this.style === Graph3d.STYLE.DOTLINE) { + // draw a vertical line from the bottom to the graph value + //var from = this._convert3Dto2D(new Point3d(point.point.x, point.point.y, this.zMin)); + var from = this._convert3Dto2D(point.bottom); + ctx.lineWidth = 1; + ctx.strokeStyle = this.colorGrid; + ctx.beginPath(); + ctx.moveTo(from.x, from.y); + ctx.lineTo(point.screen.x, point.screen.y); + ctx.stroke(); } - } - // prevent (end-start) > zoomMax - if (this.options.zoomMax !== null) { - var zoomMax = parseFloat(this.options.zoomMax); - if (zoomMax < 0) { - zoomMax = 0; + // calculate radius for the circle + var size; + if (this.style === Graph3d.STYLE.DOTSIZE) { + size = dotSize/2 + 2*dotSize * (point.point.value - this.valueMin) / (this.valueMax - this.valueMin); } - if ((newEnd - newStart) > zoomMax) { - if ((this.end - this.start) === zoomMax) { - // ignore this action, we are already zoomed to the maximum - newStart = this.start; - newEnd = this.end; - } - else { - // zoom to the maximum - diff = ((newEnd - newStart) - zoomMax); - newStart += diff / 2; - newEnd -= diff / 2; - } + else { + size = dotSize; } - } - var changed = (this.start != newStart || this.end != newEnd); + var radius; + if (this.showPerspective) { + radius = size / -point.trans.z; + } + else { + radius = size * -(this.eye.z / this.camera.getArmLength()); + } + if (radius < 0) { + radius = 0; + } - // if the new range does NOT overlap with the old range, emit checkRangedItems to avoid not showing ranged items (ranged meaning has end time, not neccesarily of type Range) - if (!((newStart >= this.start && newStart <= this.end) || (newEnd >= this.start && newEnd <= this.end)) && - !((this.start >= newStart && this.start <= newEnd) || (this.end >= newStart && this.end <= newEnd) )) { - this.body.emitter.emit('checkRangedItems'); - } + var hue, color, borderColor; + if (this.style === Graph3d.STYLE.DOTCOLOR ) { + // calculate the color based on the value + hue = (1 - (point.point.value - this.valueMin) * this.scale.value) * 240; + color = this._hsv2rgb(hue, 1, 1); + borderColor = this._hsv2rgb(hue, 1, 0.8); + } + else if (this.style === Graph3d.STYLE.DOTSIZE) { + color = this.colorDot; + borderColor = this.colorDotBorder; + } + else { + // calculate Hue from the current value. At zMin the hue is 240, at zMax the hue is 0 + hue = (1 - (point.point.z - this.zMin) * this.scale.z / this.verticalRatio) * 240; + color = this._hsv2rgb(hue, 1, 1); + borderColor = this._hsv2rgb(hue, 1, 0.8); + } - this.start = newStart; - this.end = newEnd; - return changed; + // draw the circle + ctx.lineWidth = 1.0; + ctx.strokeStyle = borderColor; + ctx.fillStyle = color; + ctx.beginPath(); + ctx.arc(point.screen.x, point.screen.y, radius, 0, Math.PI*2, true); + ctx.fill(); + ctx.stroke(); + } }; /** - * Retrieve the current range. - * @return {Object} An object with start and end properties + * Draw all datapoints as bars. + * This function can be used when the style is 'bar', 'bar-color', or 'bar-size' */ - Range.prototype.getRange = function() { - return { - start: this.start, - end: this.end - }; - }; + Graph3d.prototype._redrawDataBar = function() { + var canvas = this.frame.canvas; + var ctx = canvas.getContext('2d'); + var i, j, surface, corners; - /** - * Calculate the conversion offset and scale for current range, based on - * the provided width - * @param {Number} width - * @returns {{offset: number, scale: number}} conversion - */ - Range.prototype.conversion = function (width, totalHidden) { - return Range.conversion(this.start, this.end, width, totalHidden); - }; + if (this.dataPoints === undefined || this.dataPoints.length <= 0) + return; // TODO: throw exception? - /** - * Static method to calculate the conversion offset and scale for a range, - * based on the provided start, end, and width - * @param {Number} start - * @param {Number} end - * @param {Number} width - * @returns {{offset: number, scale: number}} conversion - */ - Range.conversion = function (start, end, width, totalHidden) { - if (totalHidden === undefined) { - totalHidden = 0; + // calculate the translations of all points + for (i = 0; i < this.dataPoints.length; i++) { + var trans = this._convertPointToTranslation(this.dataPoints[i].point); + var screen = this._convertTranslationToScreen(trans); + this.dataPoints[i].trans = trans; + this.dataPoints[i].screen = screen; + + // calculate the distance from the point at the bottom to the camera + var transBottom = this._convertPointToTranslation(this.dataPoints[i].bottom); + this.dataPoints[i].dist = this.showPerspective ? transBottom.length() : -transBottom.z; } - if (width != 0 && (end - start != 0)) { - return { - offset: start, - scale: width / (end - start - totalHidden) + + // order the translated points by depth + var sortDepth = function (a, b) { + return b.dist - a.dist; + }; + this.dataPoints.sort(sortDepth); + + // draw the datapoints as bars + var xWidth = this.xBarWidth / 2; + var yWidth = this.yBarWidth / 2; + for (i = 0; i < this.dataPoints.length; i++) { + var point = this.dataPoints[i]; + + // determine color + var hue, color, borderColor; + if (this.style === Graph3d.STYLE.BARCOLOR ) { + // calculate the color based on the value + hue = (1 - (point.point.value - this.valueMin) * this.scale.value) * 240; + color = this._hsv2rgb(hue, 1, 1); + borderColor = this._hsv2rgb(hue, 1, 0.8); + } + else if (this.style === Graph3d.STYLE.BARSIZE) { + color = this.colorDot; + borderColor = this.colorDotBorder; + } + else { + // calculate Hue from the current value. At zMin the hue is 240, at zMax the hue is 0 + hue = (1 - (point.point.z - this.zMin) * this.scale.z / this.verticalRatio) * 240; + color = this._hsv2rgb(hue, 1, 1); + borderColor = this._hsv2rgb(hue, 1, 0.8); } - } - else { - return { - offset: 0, - scale: 1 - }; - } - }; - /** - * Start dragging horizontally or vertically - * @param {Event} event - * @private - */ - Range.prototype._onDragStart = function(event) { - this.deltaDifference = 0; - this.previousDelta = 0; - // only allow dragging when configured as movable - if (!this.options.moveable) return; + // calculate size for the bar + if (this.style === Graph3d.STYLE.BARSIZE) { + xWidth = (this.xBarWidth / 2) * ((point.point.value - this.valueMin) / (this.valueMax - this.valueMin) * 0.8 + 0.2); + yWidth = (this.yBarWidth / 2) * ((point.point.value - this.valueMin) / (this.valueMax - this.valueMin) * 0.8 + 0.2); + } - // refuse to drag when we where pinching to prevent the timeline make a jump - // when releasing the fingers in opposite order from the touch screen - if (!this.props.touch.allowDragging) return; + // calculate all corner points + var me = this; + var point3d = point.point; + var top = [ + {point: new Point3d(point3d.x - xWidth, point3d.y - yWidth, point3d.z)}, + {point: new Point3d(point3d.x + xWidth, point3d.y - yWidth, point3d.z)}, + {point: new Point3d(point3d.x + xWidth, point3d.y + yWidth, point3d.z)}, + {point: new Point3d(point3d.x - xWidth, point3d.y + yWidth, point3d.z)} + ]; + var bottom = [ + {point: new Point3d(point3d.x - xWidth, point3d.y - yWidth, this.zMin)}, + {point: new Point3d(point3d.x + xWidth, point3d.y - yWidth, this.zMin)}, + {point: new Point3d(point3d.x + xWidth, point3d.y + yWidth, this.zMin)}, + {point: new Point3d(point3d.x - xWidth, point3d.y + yWidth, this.zMin)} + ]; - this.props.touch.start = this.start; - this.props.touch.end = this.end; - this.props.touch.dragging = true; + // calculate screen location of the points + top.forEach(function (obj) { + obj.screen = me._convert3Dto2D(obj.point); + }); + bottom.forEach(function (obj) { + obj.screen = me._convert3Dto2D(obj.point); + }); - if (this.body.dom.root) { - this.body.dom.root.style.cursor = 'move'; + // create five sides, calculate both corner points and center points + var surfaces = [ + {corners: top, center: Point3d.avg(bottom[0].point, bottom[2].point)}, + {corners: [top[0], top[1], bottom[1], bottom[0]], center: Point3d.avg(bottom[1].point, bottom[0].point)}, + {corners: [top[1], top[2], bottom[2], bottom[1]], center: Point3d.avg(bottom[2].point, bottom[1].point)}, + {corners: [top[2], top[3], bottom[3], bottom[2]], center: Point3d.avg(bottom[3].point, bottom[2].point)}, + {corners: [top[3], top[0], bottom[0], bottom[3]], center: Point3d.avg(bottom[0].point, bottom[3].point)} + ]; + point.surfaces = surfaces; + + // calculate the distance of each of the surface centers to the camera + for (j = 0; j < surfaces.length; j++) { + surface = surfaces[j]; + var transCenter = this._convertPointToTranslation(surface.center); + surface.dist = this.showPerspective ? transCenter.length() : -transCenter.z; + // TODO: this dept calculation doesn't work 100% of the cases due to perspective, + // but the current solution is fast/simple and works in 99.9% of all cases + // the issue is visible in example 14, with graph.setCameraPosition({horizontal: 2.97, vertical: 0.5, distance: 0.9}) + } + + // order the surfaces by their (translated) depth + surfaces.sort(function (a, b) { + var diff = b.dist - a.dist; + if (diff) return diff; + + // if equal depth, sort the top surface last + if (a.corners === top) return 1; + if (b.corners === top) return -1; + + // both are equal + return 0; + }); + + // draw the ordered surfaces + ctx.lineWidth = 1; + ctx.strokeStyle = borderColor; + ctx.fillStyle = color; + // NOTE: we start at j=2 instead of j=0 as we don't need to draw the two surfaces at the backside + for (j = 2; j < surfaces.length; j++) { + surface = surfaces[j]; + corners = surface.corners; + ctx.beginPath(); + ctx.moveTo(corners[3].screen.x, corners[3].screen.y); + ctx.lineTo(corners[0].screen.x, corners[0].screen.y); + ctx.lineTo(corners[1].screen.x, corners[1].screen.y); + ctx.lineTo(corners[2].screen.x, corners[2].screen.y); + ctx.lineTo(corners[3].screen.x, corners[3].screen.y); + ctx.fill(); + ctx.stroke(); + } } }; + /** - * Perform dragging operation - * @param {Event} event - * @private + * Draw a line through all datapoints. + * This function can be used when the style is 'line' */ - Range.prototype._onDrag = function (event) { - // only allow dragging when configured as movable - if (!this.options.moveable) return; - // refuse to drag when we where pinching to prevent the timeline make a jump - // when releasing the fingers in opposite order from the touch screen - if (!this.props.touch.allowDragging) return; - - var direction = this.options.direction; - validateDirection(direction); + Graph3d.prototype._redrawDataLine = function() { + var canvas = this.frame.canvas, + ctx = canvas.getContext('2d'), + point, i; - var delta = (direction == 'horizontal') ? event.gesture.deltaX : event.gesture.deltaY; - delta -= this.deltaDifference; - var interval = (this.props.touch.end - this.props.touch.start); + if (this.dataPoints === undefined || this.dataPoints.length <= 0) + return; // TODO: throw exception? - // normalize dragging speed if cutout is in between. - var duration = DateUtil.getHiddenDurationBetween(this.body.hiddenDates, this.start, this.end); - interval -= duration; + // calculate the translations of all points + for (i = 0; i < this.dataPoints.length; i++) { + var trans = this._convertPointToTranslation(this.dataPoints[i].point); + var screen = this._convertTranslationToScreen(trans); - var width = (direction == 'horizontal') ? this.body.domProps.center.width : this.body.domProps.center.height; - var diffRange = -delta / width * interval; - var newStart = this.props.touch.start + diffRange; - var newEnd = this.props.touch.end + diffRange; + this.dataPoints[i].trans = trans; + this.dataPoints[i].screen = screen; + } + // start the line + if (this.dataPoints.length > 0) { + point = this.dataPoints[0]; - // snapping times away from hidden zones - var safeStart = DateUtil.snapAwayFromHidden(this.body.hiddenDates, newStart, this.previousDelta-delta, true); - var safeEnd = DateUtil.snapAwayFromHidden(this.body.hiddenDates, newEnd, this.previousDelta-delta, true); - if (safeStart != newStart || safeEnd != newEnd) { - this.deltaDifference += delta; - this.props.touch.start = safeStart; - this.props.touch.end = safeEnd; - this._onDrag(event); - return; + ctx.lineWidth = 1; // TODO: make customizable + ctx.strokeStyle = 'blue'; // TODO: make customizable + ctx.beginPath(); + ctx.moveTo(point.screen.x, point.screen.y); } - this.previousDelta = delta; - this._applyRange(newStart, newEnd); + // draw the datapoints as colored circles + for (i = 1; i < this.dataPoints.length; i++) { + point = this.dataPoints[i]; + ctx.lineTo(point.screen.x, point.screen.y); + } - // fire a rangechange event - this.body.emitter.emit('rangechange', { - start: new Date(this.start), - end: new Date(this.end), - byUser: true - }); + // finish the line + if (this.dataPoints.length > 0) { + ctx.stroke(); + } }; /** - * Stop dragging operation - * @param {event} event - * @private + * Start a moving operation inside the provided parent element + * @param {Event} event The event that occurred (required for + * retrieving the mouse position) */ - Range.prototype._onDragEnd = function (event) { - // only allow dragging when configured as movable - if (!this.options.moveable) return; - - // refuse to drag when we where pinching to prevent the timeline make a jump - // when releasing the fingers in opposite order from the touch screen - if (!this.props.touch.allowDragging) return; + Graph3d.prototype._onMouseDown = function(event) { + event = event || window.event; - this.props.touch.dragging = false; - if (this.body.dom.root) { - this.body.dom.root.style.cursor = 'auto'; + // check if mouse is still down (may be up when focus is lost for example + // in an iframe) + if (this.leftButtonDown) { + this._onMouseUp(event); } - // fire a rangechanged event - this.body.emitter.emit('rangechanged', { - start: new Date(this.start), - end: new Date(this.end), - byUser: true - }); + // only react on left mouse button down + this.leftButtonDown = event.which ? (event.which === 1) : (event.button === 1); + if (!this.leftButtonDown && !this.touchDown) return; + + // get mouse position (different code for IE and all other browsers) + this.startMouseX = getMouseX(event); + this.startMouseY = getMouseY(event); + + this.startStart = new Date(this.start); + this.startEnd = new Date(this.end); + this.startArmRotation = this.camera.getArmRotation(); + + this.frame.style.cursor = 'move'; + + // add event listeners to handle moving the contents + // we store the function onmousemove and onmouseup in the graph, so we can + // remove the eventlisteners lateron in the function mouseUp() + var me = this; + this.onmousemove = function (event) {me._onMouseMove(event);}; + this.onmouseup = function (event) {me._onMouseUp(event);}; + util.addEventListener(document, 'mousemove', me.onmousemove); + util.addEventListener(document, 'mouseup', me.onmouseup); + util.preventDefault(event); }; + /** - * Event handler for mouse wheel event, used to zoom - * Code from http://adomas.org/javascript-mouse-wheel/ - * @param {Event} event - * @private + * Perform moving operating. + * This function activated from within the funcion Graph.mouseDown(). + * @param {Event} event Well, eehh, the event */ - Range.prototype._onMouseWheel = function(event) { - // only allow zooming when configured as zoomable and moveable - if (!(this.options.zoomable && this.options.moveable)) return; + Graph3d.prototype._onMouseMove = function (event) { + event = event || window.event; - // retrieve delta - var delta = 0; - if (event.wheelDelta) { /* IE/Opera. */ - delta = event.wheelDelta / 120; - } else if (event.detail) { /* Mozilla case. */ - // In Mozilla, sign of delta is different than in IE. - // Also, delta is multiple of 3. - delta = -event.detail / 3; - } + // calculate change in mouse position + var diffX = parseFloat(getMouseX(event)) - this.startMouseX; + var diffY = parseFloat(getMouseY(event)) - this.startMouseY; - // If delta is nonzero, handle it. - // Basically, delta is now positive if wheel was scrolled up, - // and negative, if wheel was scrolled down. - if (delta) { - // perform the zoom action. Delta is normally 1 or -1 + var horizontalNew = this.startArmRotation.horizontal + diffX / 200; + var verticalNew = this.startArmRotation.vertical + diffY / 200; - // adjust a negative delta such that zooming in with delta 0.1 - // equals zooming out with a delta -0.1 - var scale; - if (delta < 0) { - scale = 1 - (delta / 5); - } - else { - scale = 1 / (1 + (delta / 5)) ; - } + var snapAngle = 4; // degrees + var snapValue = Math.sin(snapAngle / 360 * 2 * Math.PI); - // calculate center, the date to zoom around - var gesture = hammerUtil.fakeGesture(this, event), - pointer = getPointer(gesture.center, this.body.dom.center), - pointerDate = this._pointerToDate(pointer); + // snap horizontally to nice angles at 0pi, 0.5pi, 1pi, 1.5pi, etc... + // the -0.001 is to take care that the vertical axis is always drawn at the left front corner + if (Math.abs(Math.sin(horizontalNew)) < snapValue) { + horizontalNew = Math.round((horizontalNew / Math.PI)) * Math.PI - 0.001; + } + if (Math.abs(Math.cos(horizontalNew)) < snapValue) { + horizontalNew = (Math.round((horizontalNew/ Math.PI - 0.5)) + 0.5) * Math.PI - 0.001; + } - this.zoom(scale, pointerDate, delta); + // snap vertically to nice angles + if (Math.abs(Math.sin(verticalNew)) < snapValue) { + verticalNew = Math.round((verticalNew / Math.PI)) * Math.PI; + } + if (Math.abs(Math.cos(verticalNew)) < snapValue) { + verticalNew = (Math.round((verticalNew/ Math.PI - 0.5)) + 0.5) * Math.PI; } - // Prevent default actions caused by mouse wheel - // (else the page and timeline both zoom and scroll) - event.preventDefault(); - }; + this.camera.setArmRotation(horizontalNew, verticalNew); + this.redraw(); - /** - * Start of a touch gesture - * @private - */ - Range.prototype._onTouch = function (event) { - this.props.touch.start = this.start; - this.props.touch.end = this.end; - this.props.touch.allowDragging = true; - this.props.touch.center = null; - this.scaleOffset = 0; - this.deltaDifference = 0; + // fire a cameraPositionChange event + var parameters = this.getCameraPosition(); + this.emit('cameraPositionChange', parameters); + + util.preventDefault(event); }; + /** - * On start of a hold gesture - * @private + * Stop moving operating. + * This function activated from within the funcion Graph.mouseDown(). + * @param {event} event The event */ - Range.prototype._onHold = function () { - this.props.touch.allowDragging = false; + Graph3d.prototype._onMouseUp = function (event) { + this.frame.style.cursor = 'auto'; + this.leftButtonDown = false; + + // remove event listeners here + util.removeEventListener(document, 'mousemove', this.onmousemove); + util.removeEventListener(document, 'mouseup', this.onmouseup); + util.preventDefault(event); }; /** - * Handle pinch event - * @param {Event} event - * @private + * After having moved the mouse, a tooltip should pop up when the mouse is resting on a data point + * @param {Event} event A mouse move event */ - Range.prototype._onPinch = function (event) { - // only allow zooming when configured as zoomable and moveable - if (!(this.options.zoomable && this.options.moveable)) return; + Graph3d.prototype._onTooltip = function (event) { + var delay = 300; // ms + var boundingRect = this.frame.getBoundingClientRect(); + var mouseX = getMouseX(event) - boundingRect.left; + var mouseY = getMouseY(event) - boundingRect.top; - this.props.touch.allowDragging = false; + if (!this.showTooltip) { + return; + } - if (event.gesture.touches.length > 1) { - if (!this.props.touch.center) { - this.props.touch.center = getPointer(event.gesture.center, this.body.dom.center); - } - - var scale = 1 / (event.gesture.scale + this.scaleOffset); - var centerDate = this._pointerToDate(this.props.touch.center); - - var hiddenDuration = DateUtil.getHiddenDurationBetween(this.body.hiddenDates, this.start, this.end); - var hiddenDurationBefore = DateUtil.getHiddenDurationBefore(this.body.hiddenDates, this, centerDate); - var hiddenDurationAfter = hiddenDuration - hiddenDurationBefore; - - // calculate new start and end - var newStart = (centerDate - hiddenDurationBefore) + (this.props.touch.start - (centerDate - hiddenDurationBefore)) * scale; - var newEnd = (centerDate + hiddenDurationAfter) + (this.props.touch.end - (centerDate + hiddenDurationAfter)) * scale; + if (this.tooltipTimeout) { + clearTimeout(this.tooltipTimeout); + } - // snapping times away from hidden zones - this.startToFront = 1 - scale > 0 ? false : true; // used to do the right autocorrection with periodic hidden times - this.endToFront = scale - 1 > 0 ? false : true; // used to do the right autocorrection with periodic hidden times + // (delayed) display of a tooltip only if no mouse button is down + if (this.leftButtonDown) { + this._hideTooltip(); + return; + } - var safeStart = DateUtil.snapAwayFromHidden(this.body.hiddenDates, newStart, 1 - scale, true); - var safeEnd = DateUtil.snapAwayFromHidden(this.body.hiddenDates, newEnd, scale - 1, true); - if (safeStart != newStart || safeEnd != newEnd) { - this.props.touch.start = safeStart; - this.props.touch.end = safeEnd; - this.scaleOffset = 1 - event.gesture.scale; - newStart = safeStart; - newEnd = safeEnd; + if (this.tooltip && this.tooltip.dataPoint) { + // tooltip is currently visible + var dataPoint = this._dataPointFromXY(mouseX, mouseY); + if (dataPoint !== this.tooltip.dataPoint) { + // datapoint changed + if (dataPoint) { + this._showTooltip(dataPoint); + } + else { + this._hideTooltip(); + } } + } + else { + // tooltip is currently not visible + var me = this; + this.tooltipTimeout = setTimeout(function () { + me.tooltipTimeout = null; - this.setRange(newStart, newEnd, false, true); - - this.startToFront = false; // revert to default - this.endToFront = true; // revert to default + // show a tooltip if we have a data point + var dataPoint = me._dataPointFromXY(mouseX, mouseY); + if (dataPoint) { + me._showTooltip(dataPoint); + } + }, delay); } }; /** - * Helper function to calculate the center date for zooming - * @param {{x: Number, y: Number}} pointer - * @return {number} date - * @private + * Event handler for touchstart event on mobile devices */ - Range.prototype._pointerToDate = function (pointer) { - var conversion; - var direction = this.options.direction; + Graph3d.prototype._onTouchStart = function(event) { + this.touchDown = true; - validateDirection(direction); + var me = this; + this.ontouchmove = function (event) {me._onTouchMove(event);}; + this.ontouchend = function (event) {me._onTouchEnd(event);}; + util.addEventListener(document, 'touchmove', me.ontouchmove); + util.addEventListener(document, 'touchend', me.ontouchend); - if (direction == 'horizontal') { - return this.body.util.toTime(pointer.x).valueOf(); - } - else { - var height = this.body.domProps.center.height; - conversion = this.conversion(height); - return pointer.y / conversion.scale + conversion.offset; - } + this._onMouseDown(event); }; /** - * Get the pointer location relative to the location of the dom element - * @param {{pageX: Number, pageY: Number}} touch - * @param {Element} element HTML DOM element - * @return {{x: Number, y: Number}} pointer - * @private + * Event handler for touchmove event on mobile devices */ - function getPointer (touch, element) { - return { - x: touch.pageX - util.getAbsoluteLeft(element), - y: touch.pageY - util.getAbsoluteTop(element) - }; - } + Graph3d.prototype._onTouchMove = function(event) { + this._onMouseMove(event); + }; /** - * Zoom the range the given scale in or out. Start and end date will - * be adjusted, and the timeline will be redrawn. You can optionally give a - * date around which to zoom. - * For example, try scale = 0.9 or 1.1 - * @param {Number} scale Scaling factor. Values above 1 will zoom out, - * values below 1 will zoom in. - * @param {Number} [center] Value representing a date around which will - * be zoomed. + * Event handler for touchend event on mobile devices */ - Range.prototype.zoom = function(scale, center, delta) { - // if centerDate is not provided, take it half between start Date and end Date - if (center == null) { - center = (this.start + this.end) / 2; - } - - var hiddenDuration = DateUtil.getHiddenDurationBetween(this.body.hiddenDates, this.start, this.end); - var hiddenDurationBefore = DateUtil.getHiddenDurationBefore(this.body.hiddenDates, this, center); - var hiddenDurationAfter = hiddenDuration - hiddenDurationBefore; - - // calculate new start and end - var newStart = (center-hiddenDurationBefore) + (this.start - (center-hiddenDurationBefore)) * scale; - var newEnd = (center+hiddenDurationAfter) + (this.end - (center+hiddenDurationAfter)) * scale; - - // snapping times away from hidden zones - this.startToFront = delta > 0 ? false : true; // used to do the right autocorrection with periodic hidden times - this.endToFront = -delta > 0 ? false : true; // used to do the right autocorrection with periodic hidden times - var safeStart = DateUtil.snapAwayFromHidden(this.body.hiddenDates, newStart, delta, true); - var safeEnd = DateUtil.snapAwayFromHidden(this.body.hiddenDates, newEnd, -delta, true); - if (safeStart != newStart || safeEnd != newEnd) { - newStart = safeStart; - newEnd = safeEnd; - } + Graph3d.prototype._onTouchEnd = function(event) { + this.touchDown = false; - this.setRange(newStart, newEnd, false, true); + util.removeEventListener(document, 'touchmove', this.ontouchmove); + util.removeEventListener(document, 'touchend', this.ontouchend); - this.startToFront = false; // revert to default - this.endToFront = true; // revert to default + this._onMouseUp(event); }; - /** - * Move the range with a given delta to the left or right. Start and end - * value will be adjusted. For example, try delta = 0.1 or -0.1 - * @param {Number} delta Moving amount. Positive value will move right, - * negative value will move left + * Event handler for mouse wheel event, used to zoom the graph + * Code from http://adomas.org/javascript-mouse-wheel/ + * @param {event} event The event */ - Range.prototype.move = function(delta) { - // zoom start Date and end Date relative to the centerDate - var diff = (this.end - this.start); + Graph3d.prototype._onWheel = function(event) { + if (!event) /* For IE. */ + event = window.event; - // apply new values - var newStart = this.start + diff * delta; - var newEnd = this.end + diff * delta; + // retrieve delta + var delta = 0; + if (event.wheelDelta) { /* IE/Opera. */ + delta = event.wheelDelta/120; + } else if (event.detail) { /* Mozilla case. */ + // In Mozilla, sign of delta is different than in IE. + // Also, delta is multiple of 3. + delta = -event.detail/3; + } - // TODO: reckon with min and max range + // If delta is nonzero, handle it. + // Basically, delta is now positive if wheel was scrolled up, + // and negative, if wheel was scrolled down. + if (delta) { + var oldLength = this.camera.getArmLength(); + var newLength = oldLength * (1 - delta / 10); - this.start = newStart; - this.end = newEnd; + this.camera.setArmLength(newLength); + this.redraw(); + + this._hideTooltip(); + } + + // fire a cameraPositionChange event + var parameters = this.getCameraPosition(); + this.emit('cameraPositionChange', parameters); + + // Prevent default actions caused by mouse wheel. + // That might be ugly, but we handle scrolls somehow + // anyway, so don't bother here.. + util.preventDefault(event); }; /** - * Move the range to a new center point - * @param {Number} moveTo New center point of the range + * Test whether a point lies inside given 2D triangle + * @param {Point2d} point + * @param {Point2d[]} triangle + * @return {boolean} Returns true if given point lies inside or on the edge of the triangle + * @private */ - Range.prototype.moveTo = function(moveTo) { - var center = (this.start + this.end) / 2; + Graph3d.prototype._insideTriangle = function (point, triangle) { + var a = triangle[0], + b = triangle[1], + c = triangle[2]; - var diff = center - moveTo; + function sign (x) { + return x > 0 ? 1 : x < 0 ? -1 : 0; + } - // calculate new start and end - var newStart = this.start - diff; - var newEnd = this.end - diff; + var as = sign((b.x - a.x) * (point.y - a.y) - (b.y - a.y) * (point.x - a.x)); + var bs = sign((c.x - b.x) * (point.y - b.y) - (c.y - b.y) * (point.x - b.x)); + var cs = sign((a.x - c.x) * (point.y - c.y) - (a.y - c.y) * (point.x - c.x)); - this.setRange(newStart, newEnd); + // each of the three signs must be either equal to each other or zero + return (as == 0 || bs == 0 || as == bs) && + (bs == 0 || cs == 0 || bs == cs) && + (as == 0 || cs == 0 || as == cs); }; - module.exports = Range; + /** + * Find a data point close to given screen position (x, y) + * @param {Number} x + * @param {Number} y + * @return {Object | null} The closest data point or null if not close to any data point + * @private + */ + Graph3d.prototype._dataPointFromXY = function (x, y) { + var i, + distMax = 100, // px + dataPoint = null, + closestDataPoint = null, + closestDist = null, + center = new Point2d(x, y); + if (this.style === Graph3d.STYLE.BAR || + this.style === Graph3d.STYLE.BARCOLOR || + this.style === Graph3d.STYLE.BARSIZE) { + // the data points are ordered from far away to closest + for (i = this.dataPoints.length - 1; i >= 0; i--) { + dataPoint = this.dataPoints[i]; + var surfaces = dataPoint.surfaces; + if (surfaces) { + for (var s = surfaces.length - 1; s >= 0; s--) { + // split each surface in two triangles, and see if the center point is inside one of these + var surface = surfaces[s]; + var corners = surface.corners; + var triangle1 = [corners[0].screen, corners[1].screen, corners[2].screen]; + var triangle2 = [corners[2].screen, corners[3].screen, corners[0].screen]; + if (this._insideTriangle(center, triangle1) || + this._insideTriangle(center, triangle2)) { + // return immediately at the first hit + return dataPoint; + } + } + } + } + } + else { + // find the closest data point, using distance to the center of the point on 2d screen + for (i = 0; i < this.dataPoints.length; i++) { + dataPoint = this.dataPoints[i]; + var point = dataPoint.screen; + if (point) { + var distX = Math.abs(x - point.x); + var distY = Math.abs(y - point.y); + var dist = Math.sqrt(distX * distX + distY * distY); -/***/ }, -/* 18 */ -/***/ function(module, exports, __webpack_require__) { + if ((closestDist === null || dist < closestDist) && dist < distMax) { + closestDist = dist; + closestDataPoint = dataPoint; + } + } + } + } - // Utility functions for ordering and stacking of items - var EPSILON = 0.001; // used when checking collisions, to prevent round-off errors - /** - * Order items by their start data - * @param {Item[]} items - */ - exports.orderByStart = function(items) { - items.sort(function (a, b) { - return a.data.start - b.data.start; - }); + return closestDataPoint; }; /** - * Order items by their end date. If they have no end date, their start date - * is used. - * @param {Item[]} items + * Display a tooltip for given data point + * @param {Object} dataPoint + * @private */ - exports.orderByEnd = function(items) { - items.sort(function (a, b) { - var aTime = ('end' in a.data) ? a.data.end : a.data.start, - bTime = ('end' in b.data) ? b.data.end : b.data.start; + Graph3d.prototype._showTooltip = function (dataPoint) { + var content, line, dot; - return aTime - bTime; - }); - }; + if (!this.tooltip) { + content = document.createElement('div'); + content.style.position = 'absolute'; + content.style.padding = '10px'; + content.style.border = '1px solid #4d4d4d'; + content.style.color = '#1a1a1a'; + content.style.background = 'rgba(255,255,255,0.7)'; + content.style.borderRadius = '2px'; + content.style.boxShadow = '5px 5px 10px rgba(128,128,128,0.5)'; - /** - * Adjust vertical positions of the items such that they don't overlap each - * other. - * @param {Item[]} items - * All visible items - * @param {{item: {horizontal: number, vertical: number}, axis: number}} margin - * Margins between items and between items and the axis. - * @param {boolean} [force=false] - * If true, all items will be repositioned. If false (default), only - * items having a top===null will be re-stacked - */ - exports.stack = function(items, margin, force) { - var i, iMax; + line = document.createElement('div'); + line.style.position = 'absolute'; + line.style.height = '40px'; + line.style.width = '0'; + line.style.borderLeft = '1px solid #4d4d4d'; - if (force) { - // reset top position of all items - for (i = 0, iMax = items.length; i < iMax; i++) { - items[i].top = null; - } - } + dot = document.createElement('div'); + dot.style.position = 'absolute'; + dot.style.height = '0'; + dot.style.width = '0'; + dot.style.border = '5px solid #4d4d4d'; + dot.style.borderRadius = '5px'; - // calculate new, non-overlapping positions - for (i = 0, iMax = items.length; i < iMax; i++) { - var item = items[i]; - if (item.stack && item.top === null) { - // initialize top position - item.top = margin.axis; + this.tooltip = { + dataPoint: null, + dom: { + content: content, + line: line, + dot: dot + } + }; + } + else { + content = this.tooltip.dom.content; + line = this.tooltip.dom.line; + dot = this.tooltip.dom.dot; + } - do { - // TODO: optimize checking for overlap. when there is a gap without items, - // you only need to check for items from the next item on, not from zero - var collidingItem = null; - for (var j = 0, jj = items.length; j < jj; j++) { - var other = items[j]; - if (other.top !== null && other !== item && other.stack && exports.collision(item, other, margin.item)) { - collidingItem = other; - break; - } - } + this._hideTooltip(); - if (collidingItem != null) { - // There is a collision. Reposition the items above the colliding element - item.top = collidingItem.top + collidingItem.height + margin.item.vertical; - } - } while (collidingItem); - } + this.tooltip.dataPoint = dataPoint; + if (typeof this.showTooltip === 'function') { + content.innerHTML = this.showTooltip(dataPoint.point); } - }; + else { + content.innerHTML = '' + + '' + + '' + + '' + + '
x:' + dataPoint.point.x + '
y:' + dataPoint.point.y + '
z:' + dataPoint.point.z + '
'; + } + + content.style.left = '0'; + content.style.top = '0'; + this.frame.appendChild(content); + this.frame.appendChild(line); + this.frame.appendChild(dot); + + // calculate sizes + var contentWidth = content.offsetWidth; + var contentHeight = content.offsetHeight; + var lineHeight = line.offsetHeight; + var dotWidth = dot.offsetWidth; + var dotHeight = dot.offsetHeight; + + var left = dataPoint.screen.x - contentWidth / 2; + left = Math.min(Math.max(left, 10), this.frame.clientWidth - 10 - contentWidth); + line.style.left = dataPoint.screen.x + 'px'; + line.style.top = (dataPoint.screen.y - lineHeight) + 'px'; + content.style.left = left + 'px'; + content.style.top = (dataPoint.screen.y - lineHeight - contentHeight) + 'px'; + dot.style.left = (dataPoint.screen.x - dotWidth / 2) + 'px'; + dot.style.top = (dataPoint.screen.y - dotHeight / 2) + 'px'; + }; /** - * Adjust vertical positions of the items without stacking them - * @param {Item[]} items - * All visible items - * @param {{item: {horizontal: number, vertical: number}, axis: number}} margin - * Margins between items and between items and the axis. + * Hide the tooltip when displayed + * @private */ - exports.nostack = function(items, margin, subgroups) { - var i, iMax, newTop; + Graph3d.prototype._hideTooltip = function () { + if (this.tooltip) { + this.tooltip.dataPoint = null; - // reset top position of all items - for (i = 0, iMax = items.length; i < iMax; i++) { - if (items[i].data.subgroup !== undefined) { - newTop = margin.axis; - for (var subgroup in subgroups) { - if (subgroups.hasOwnProperty(subgroup)) { - if (subgroups[subgroup].visible == true && subgroups[subgroup].index < subgroups[items[i].data.subgroup].index) { - newTop += subgroups[subgroup].height + margin.item.vertical; - } + for (var prop in this.tooltip.dom) { + if (this.tooltip.dom.hasOwnProperty(prop)) { + var elem = this.tooltip.dom[prop]; + if (elem && elem.parentNode) { + elem.parentNode.removeChild(elem); } } - items[i].top = newTop; - } - else { - items[i].top = margin.axis; } } }; + /**--------------------------------------------------------------------------**/ + + /** - * Test if the two provided items collide - * The items must have parameters left, width, top, and height. - * @param {Item} a The first item - * @param {Item} b The second item - * @param {{horizontal: number, vertical: number}} margin - * An object containing a horizontal and vertical - * minimum required margin. - * @return {boolean} true if a and b collide, else false + * Get the horizontal mouse position from a mouse event + * @param {Event} event + * @return {Number} mouse x */ - exports.collision = function(a, b, margin) { - return ((a.left - margin.horizontal + EPSILON) < (b.left + b.width) && - (a.left + a.width + margin.horizontal - EPSILON) > b.left && - (a.top - margin.vertical + EPSILON) < (b.top + b.height) && - (a.top + a.height + margin.vertical - EPSILON) > b.top); - }; + function getMouseX (event) { + if ('clientX' in event) return event.clientX; + return event.targetTouches[0] && event.targetTouches[0].clientX || 0; + } + + /** + * Get the vertical mouse position from a mouse event + * @param {Event} event + * @return {Number} mouse y + */ + function getMouseY (event) { + if ('clientY' in event) return event.clientY; + return event.targetTouches[0] && event.targetTouches[0].clientY || 0; + } + + module.exports = Graph3d; /***/ }, -/* 19 */ +/* 11 */ /***/ function(module, exports, __webpack_require__) { - var moment = __webpack_require__(44); - var DateUtil = __webpack_require__(15); - var util = __webpack_require__(1); + + /** + * Expose `Emitter`. + */ + + module.exports = Emitter; /** - * @constructor TimeStep - * The class TimeStep is an iterator for dates. You provide a start date and an - * end date. The class itself determines the best scale (step size) based on the - * provided start Date, end Date, and minimumStep. - * - * If minimumStep is provided, the step size is chosen as close as possible - * to the minimumStep but larger than minimumStep. If minimumStep is not - * provided, the scale is set to 1 DAY. - * The minimumStep should correspond with the onscreen size of about 6 characters - * - * Alternatively, you can set a scale by hand. - * After creation, you can initialize the class by executing first(). Then you - * can iterate from the start date to the end date via next(). You can check if - * the end date is reached with the function hasNext(). After each step, you can - * retrieve the current date via getCurrent(). - * The TimeStep has scales ranging from milliseconds, seconds, minutes, hours, - * days, to years. - * - * Version: 1.2 + * Initialize a new `Emitter`. * - * @param {Date} [start] The start date, for example new Date(2010, 9, 21) - * or new Date(2010, 9, 21, 23, 45, 00) - * @param {Date} [end] The end date - * @param {Number} [minimumStep] Optional. Minimum step size in milliseconds + * @api public */ - function TimeStep(start, end, minimumStep, hiddenDates) { - // variables - this.current = new Date(); - this._start = new Date(); - this._end = new Date(); - this.autoScale = true; - this.scale = 'day'; - this.step = 1; + function Emitter(obj) { + if (obj) return mixin(obj); + }; - // initialize the range - this.setRange(start, end, minimumStep); + /** + * Mixin the emitter properties. + * + * @param {Object} obj + * @return {Object} + * @api private + */ - // hidden Dates options - this.switchedDay = false; - this.switchedMonth = false; - this.switchedYear = false; - this.hiddenDates = hiddenDates; - if (hiddenDates === undefined) { - this.hiddenDates = []; + function mixin(obj) { + for (var key in Emitter.prototype) { + obj[key] = Emitter.prototype[key]; } - - this.format = TimeStep.FORMAT; // default formatting + return obj; } - // Time formatting - TimeStep.FORMAT = { - minorLabels: { - millisecond:'SSS', - second: 's', - minute: 'HH:mm', - hour: 'HH:mm', - weekday: 'ddd D', - day: 'D', - month: 'MMM', - year: 'YYYY' - }, - majorLabels: { - millisecond:'HH:mm:ss', - second: 'D MMMM HH:mm', - minute: 'ddd D MMMM', - hour: 'ddd D MMMM', - weekday: 'MMMM YYYY', - day: 'MMMM YYYY', - month: 'YYYY', - year: '' - } - }; - /** - * Set custom formatting for the minor an major labels of the TimeStep. - * Both `minorLabels` and `majorLabels` are an Object with properties: - * 'millisecond, 'second, 'minute', 'hour', 'weekday, 'day, 'month, 'year'. - * @param {{minorLabels: Object, majorLabels: Object}} format + * Listen on the given `event` with `fn`. + * + * @param {String} event + * @param {Function} fn + * @return {Emitter} + * @api public */ - TimeStep.prototype.setFormat = function (format) { - var defaultFormat = util.deepExtend({}, TimeStep.FORMAT); - this.format = util.deepExtend(defaultFormat, format); + + Emitter.prototype.on = + Emitter.prototype.addEventListener = function(event, fn){ + this._callbacks = this._callbacks || {}; + (this._callbacks[event] = this._callbacks[event] || []) + .push(fn); + return this; }; /** - * Set a new range - * If minimumStep is provided, the step size is chosen as close as possible - * to the minimumStep but larger than minimumStep. If minimumStep is not - * provided, the scale is set to 1 DAY. - * The minimumStep should correspond with the onscreen size of about 6 characters - * @param {Date} [start] The start date and time. - * @param {Date} [end] The end date and time. - * @param {int} [minimumStep] Optional. Minimum step size in milliseconds + * Adds an `event` listener that will be invoked a single + * time then automatically removed. + * + * @param {String} event + * @param {Function} fn + * @return {Emitter} + * @api public */ - TimeStep.prototype.setRange = function(start, end, minimumStep) { - if (!(start instanceof Date) || !(end instanceof Date)) { - throw "No legal start or end date in method setRange"; - } - this._start = (start != undefined) ? new Date(start.valueOf()) : new Date(); - this._end = (end != undefined) ? new Date(end.valueOf()) : new Date(); + Emitter.prototype.once = function(event, fn){ + var self = this; + this._callbacks = this._callbacks || {}; - if (this.autoScale) { - this.setMinimumStep(minimumStep); + function on() { + self.off(event, on); + fn.apply(this, arguments); } + + on.fn = fn; + this.on(event, on); + return this; }; /** - * Set the range iterator to the start date. + * Remove the given callback for `event` or all + * registered callbacks. + * + * @param {String} event + * @param {Function} fn + * @return {Emitter} + * @api public */ - TimeStep.prototype.first = function() { - this.current = new Date(this._start.valueOf()); - this.roundToMinor(); + + 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; }; /** - * Round the current date to the first minor date value - * This must be executed once when the current date is set to start Date + * Emit `event` with the given args. + * + * @param {String} event + * @param {Mixed} ... + * @return {Emitter} */ - TimeStep.prototype.roundToMinor = function() { - // round to floor - // IMPORTANT: we have no breaks in this switch! (this is no bug) - // noinspection FallThroughInSwitchStatementJS - switch (this.scale) { - case 'year': - this.current.setFullYear(this.step * Math.floor(this.current.getFullYear() / this.step)); - this.current.setMonth(0); - case 'month': this.current.setDate(1); - case 'day': // intentional fall through - case 'weekday': this.current.setHours(0); - case 'hour': this.current.setMinutes(0); - case 'minute': this.current.setSeconds(0); - case 'second': this.current.setMilliseconds(0); - //case 'millisecond': // nothing to do for milliseconds - } - if (this.step != 1) { - // round down to the first minor value that is a multiple of the current step size - switch (this.scale) { - case 'millisecond': this.current.setMilliseconds(this.current.getMilliseconds() - this.current.getMilliseconds() % this.step); break; - case 'second': this.current.setSeconds(this.current.getSeconds() - this.current.getSeconds() % this.step); break; - case 'minute': this.current.setMinutes(this.current.getMinutes() - this.current.getMinutes() % this.step); break; - case 'hour': this.current.setHours(this.current.getHours() - this.current.getHours() % this.step); break; - case 'weekday': // intentional fall through - case 'day': this.current.setDate((this.current.getDate()-1) - (this.current.getDate()-1) % this.step + 1); break; - case 'month': this.current.setMonth(this.current.getMonth() - this.current.getMonth() % this.step); break; - case 'year': this.current.setFullYear(this.current.getFullYear() - this.current.getFullYear() % this.step); break; - default: break; + 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; }; /** - * Check if the there is a next step - * @return {boolean} true if the current date has not passed the end date + * Return array of callbacks for `event`. + * + * @param {String} event + * @return {Array} + * @api public */ - TimeStep.prototype.hasNext = function () { - return (this.current.valueOf() <= this._end.valueOf()); + + Emitter.prototype.listeners = function(event){ + this._callbacks = this._callbacks || {}; + return this._callbacks[event] || []; }; /** - * Do the next step + * Check if this emitter has `event` handlers. + * + * @param {String} event + * @return {Boolean} + * @api public */ - TimeStep.prototype.next = function() { - var prev = this.current.valueOf(); - // Two cases, needed to prevent issues with switching daylight savings - // (end of March and end of October) - if (this.current.getMonth() < 6) { - switch (this.scale) { - case 'millisecond': + Emitter.prototype.hasListeners = function(event){ + return !! this.listeners(event).length; + }; - this.current = new Date(this.current.valueOf() + this.step); break; - case 'second': this.current = new Date(this.current.valueOf() + this.step * 1000); break; - case 'minute': this.current = new Date(this.current.valueOf() + this.step * 1000 * 60); break; - case 'hour': - this.current = new Date(this.current.valueOf() + this.step * 1000 * 60 * 60); - // in case of skipping an hour for daylight savings, adjust the hour again (else you get: 0h 5h 9h ... instead of 0h 4h 8h ...) - var h = this.current.getHours(); - this.current.setHours(h - (h % this.step)); - break; - case 'weekday': // intentional fall through - case 'day': this.current.setDate(this.current.getDate() + this.step); break; - case 'month': this.current.setMonth(this.current.getMonth() + this.step); break; - case 'year': this.current.setFullYear(this.current.getFullYear() + this.step); break; - default: break; - } - } - else { - switch (this.scale) { - case 'millisecond': this.current = new Date(this.current.valueOf() + this.step); break; - case 'second': this.current.setSeconds(this.current.getSeconds() + this.step); break; - case 'minute': this.current.setMinutes(this.current.getMinutes() + this.step); break; - case 'hour': this.current.setHours(this.current.getHours() + this.step); break; - case 'weekday': // intentional fall through - case 'day': this.current.setDate(this.current.getDate() + this.step); break; - case 'month': this.current.setMonth(this.current.getMonth() + this.step); break; - case 'year': this.current.setFullYear(this.current.getFullYear() + this.step); break; - default: break; - } - } - if (this.step != 1) { - // round down to the correct major value - switch (this.scale) { - case 'millisecond': if(this.current.getMilliseconds() < this.step) this.current.setMilliseconds(0); break; - case 'second': if(this.current.getSeconds() < this.step) this.current.setSeconds(0); break; - case 'minute': if(this.current.getMinutes() < this.step) this.current.setMinutes(0); break; - case 'hour': if(this.current.getHours() < this.step) this.current.setHours(0); break; - case 'weekday': // intentional fall through - case 'day': if(this.current.getDate() < this.step+1) this.current.setDate(1); break; - case 'month': if(this.current.getMonth() < this.step) this.current.setMonth(0); break; - case 'year': break; // nothing to do for year - default: break; - } - } +/***/ }, +/* 12 */ +/***/ function(module, exports, __webpack_require__) { - // safety mechanism: if current time is still unchanged, move to the end - if (this.current.valueOf() == prev) { - this.current = new Date(this._end.valueOf()); - } + /** + * @prototype Point3d + * @param {Number} [x] + * @param {Number} [y] + * @param {Number} [z] + */ + function Point3d(x, y, z) { + this.x = x !== undefined ? x : 0; + this.y = y !== undefined ? y : 0; + this.z = z !== undefined ? z : 0; + }; - DateUtil.stepOverHiddenDates(this, prev); + /** + * Subtract the two provided points, returns a-b + * @param {Point3d} a + * @param {Point3d} b + * @return {Point3d} a-b + */ + Point3d.subtract = function(a, b) { + var sub = new Point3d(); + sub.x = a.x - b.x; + sub.y = a.y - b.y; + sub.z = a.z - b.z; + return sub; }; + /** + * Add the two provided points, returns a+b + * @param {Point3d} a + * @param {Point3d} b + * @return {Point3d} a+b + */ + Point3d.add = function(a, b) { + var sum = new Point3d(); + sum.x = a.x + b.x; + sum.y = a.y + b.y; + sum.z = a.z + b.z; + return sum; + }; /** - * Get the current datetime - * @return {Date} current The current date + * Calculate the average of two 3d points + * @param {Point3d} a + * @param {Point3d} b + * @return {Point3d} The average, (a+b)/2 */ - TimeStep.prototype.getCurrent = function() { - return this.current; + Point3d.avg = function(a, b) { + return new Point3d( + (a.x + b.x) / 2, + (a.y + b.y) / 2, + (a.z + b.z) / 2 + ); }; /** - * Set a custom scale. Autoscaling will be disabled. - * For example setScale('minute', 5) will result - * in minor steps of 5 minutes, and major steps of an hour. - * - * @param {{scale: string, step: number}} params - * An object containing two properties: - * - A string 'scale'. Choose from 'millisecond', 'second', - * 'minute', 'hour', 'weekday, 'day, 'month, 'year'. - * - A number 'step'. A step size, by default 1. - * Choose for example 1, 2, 5, or 10. + * Calculate the cross product of the two provided points, returns axb + * Documentation: http://en.wikipedia.org/wiki/Cross_product + * @param {Point3d} a + * @param {Point3d} b + * @return {Point3d} cross product axb */ - TimeStep.prototype.setScale = function(params) { - if (params && typeof params.scale == 'string') { - this.scale = params.scale; - this.step = params.step > 0 ? params.step : 1; - this.autoScale = false; - } + Point3d.crossProduct = function(a, b) { + var crossproduct = new Point3d(); + + crossproduct.x = a.y * b.z - a.z * b.y; + crossproduct.y = a.z * b.x - a.x * b.z; + crossproduct.z = a.x * b.y - a.y * b.x; + + return crossproduct; }; + /** - * Enable or disable autoscaling - * @param {boolean} enable If true, autoascaling is set true + * Rtrieve the length of the vector (or the distance from this point to the origin + * @return {Number} length */ - TimeStep.prototype.setAutoScale = function (enable) { - this.autoScale = enable; + Point3d.prototype.length = function() { + return Math.sqrt( + this.x * this.x + + this.y * this.y + + this.z * this.z + ); }; + module.exports = Point3d; + + +/***/ }, +/* 13 */ +/***/ function(module, exports, __webpack_require__) { /** - * Automatically determine the scale that bests fits the provided minimum step - * @param {Number} [minimumStep] The minimum step size in milliseconds + * @prototype Point2d + * @param {Number} [x] + * @param {Number} [y] */ - TimeStep.prototype.setMinimumStep = function(minimumStep) { - if (minimumStep == undefined) { - return; - } + function Point2d (x, y) { + this.x = x !== undefined ? x : 0; + this.y = y !== undefined ? y : 0; + } - //var b = asc + ds; + module.exports = Point2d; - var stepYear = (1000 * 60 * 60 * 24 * 30 * 12); - var stepMonth = (1000 * 60 * 60 * 24 * 30); - var stepDay = (1000 * 60 * 60 * 24); - var stepHour = (1000 * 60 * 60); - var stepMinute = (1000 * 60); - var stepSecond = (1000); - var stepMillisecond= (1); - // find the smallest step that is larger than the provided minimumStep - if (stepYear*1000 > minimumStep) {this.scale = 'year'; this.step = 1000;} - if (stepYear*500 > minimumStep) {this.scale = 'year'; this.step = 500;} - if (stepYear*100 > minimumStep) {this.scale = 'year'; this.step = 100;} - if (stepYear*50 > minimumStep) {this.scale = 'year'; this.step = 50;} - if (stepYear*10 > minimumStep) {this.scale = 'year'; this.step = 10;} - if (stepYear*5 > minimumStep) {this.scale = 'year'; this.step = 5;} - if (stepYear > minimumStep) {this.scale = 'year'; this.step = 1;} - if (stepMonth*3 > minimumStep) {this.scale = 'month'; this.step = 3;} - if (stepMonth > minimumStep) {this.scale = 'month'; this.step = 1;} - if (stepDay*5 > minimumStep) {this.scale = 'day'; this.step = 5;} - if (stepDay*2 > minimumStep) {this.scale = 'day'; this.step = 2;} - if (stepDay > minimumStep) {this.scale = 'day'; this.step = 1;} - if (stepDay/2 > minimumStep) {this.scale = 'weekday'; this.step = 1;} - if (stepHour*4 > minimumStep) {this.scale = 'hour'; this.step = 4;} - if (stepHour > minimumStep) {this.scale = 'hour'; this.step = 1;} - if (stepMinute*15 > minimumStep) {this.scale = 'minute'; this.step = 15;} - if (stepMinute*10 > minimumStep) {this.scale = 'minute'; this.step = 10;} - if (stepMinute*5 > minimumStep) {this.scale = 'minute'; this.step = 5;} - if (stepMinute > minimumStep) {this.scale = 'minute'; this.step = 1;} - if (stepSecond*15 > minimumStep) {this.scale = 'second'; this.step = 15;} - if (stepSecond*10 > minimumStep) {this.scale = 'second'; this.step = 10;} - if (stepSecond*5 > minimumStep) {this.scale = 'second'; this.step = 5;} - if (stepSecond > minimumStep) {this.scale = 'second'; this.step = 1;} - if (stepMillisecond*200 > minimumStep) {this.scale = 'millisecond'; this.step = 200;} - if (stepMillisecond*100 > minimumStep) {this.scale = 'millisecond'; this.step = 100;} - if (stepMillisecond*50 > minimumStep) {this.scale = 'millisecond'; this.step = 50;} - if (stepMillisecond*10 > minimumStep) {this.scale = 'millisecond'; this.step = 10;} - if (stepMillisecond*5 > minimumStep) {this.scale = 'millisecond'; this.step = 5;} - if (stepMillisecond > minimumStep) {this.scale = 'millisecond'; this.step = 1;} - }; +/***/ }, +/* 14 */ +/***/ function(module, exports, __webpack_require__) { + + var Point3d = __webpack_require__(12); /** - * Snap a date to a rounded value. - * The snap intervals are dependent on the current scale and step. - * @param {Date} date the date to be snapped. - * @return {Date} snappedDate + * @class Camera + * The camera is mounted on a (virtual) camera arm. The camera arm can rotate + * The camera is always looking in the direction of the origin of the arm. + * This way, the camera always rotates around one fixed point, the location + * of the camera arm. + * + * Documentation: + * http://en.wikipedia.org/wiki/3D_projection */ - TimeStep.prototype.snap = function(date) { - var clone = new Date(date.valueOf()); + function Camera() { + this.armLocation = new Point3d(); + this.armRotation = {}; + this.armRotation.horizontal = 0; + this.armRotation.vertical = 0; + this.armLength = 1.7; - if (this.scale == 'year') { - var year = clone.getFullYear() + Math.round(clone.getMonth() / 12); - clone.setFullYear(Math.round(year / this.step) * this.step); - clone.setMonth(0); - clone.setDate(0); - clone.setHours(0); - clone.setMinutes(0); - clone.setSeconds(0); - clone.setMilliseconds(0); + this.cameraLocation = new Point3d(); + this.cameraRotation = new Point3d(0.5*Math.PI, 0, 0); + + this.calculateCameraOrientation(); + } + + /** + * Set the location (origin) of the arm + * @param {Number} x Normalized value of x + * @param {Number} y Normalized value of y + * @param {Number} z Normalized value of z + */ + Camera.prototype.setArmLocation = function(x, y, z) { + this.armLocation.x = x; + this.armLocation.y = y; + this.armLocation.z = z; + + this.calculateCameraOrientation(); + }; + + /** + * Set the rotation of the camera arm + * @param {Number} horizontal The horizontal rotation, between 0 and 2*PI. + * Optional, can be left undefined. + * @param {Number} vertical The vertical rotation, between 0 and 0.5*PI + * if vertical=0.5*PI, the graph is shown from the + * top. Optional, can be left undefined. + */ + Camera.prototype.setArmRotation = function(horizontal, vertical) { + if (horizontal !== undefined) { + this.armRotation.horizontal = horizontal; } - else if (this.scale == 'month') { - if (clone.getDate() > 15) { - clone.setDate(1); - clone.setMonth(clone.getMonth() + 1); - // important: first set Date to 1, after that change the month. - } - else { - clone.setDate(1); - } - clone.setHours(0); - clone.setMinutes(0); - clone.setSeconds(0); - clone.setMilliseconds(0); + if (vertical !== undefined) { + this.armRotation.vertical = vertical; + if (this.armRotation.vertical < 0) this.armRotation.vertical = 0; + if (this.armRotation.vertical > 0.5*Math.PI) this.armRotation.vertical = 0.5*Math.PI; } - else if (this.scale == 'day') { - //noinspection FallthroughInSwitchStatementJS - switch (this.step) { - case 5: - case 2: - clone.setHours(Math.round(clone.getHours() / 24) * 24); break; - default: - clone.setHours(Math.round(clone.getHours() / 12) * 12); break; - } - clone.setMinutes(0); - clone.setSeconds(0); - clone.setMilliseconds(0); - } - else if (this.scale == 'weekday') { - //noinspection FallthroughInSwitchStatementJS - switch (this.step) { - case 5: - case 2: - clone.setHours(Math.round(clone.getHours() / 12) * 12); break; - default: - clone.setHours(Math.round(clone.getHours() / 6) * 6); break; - } - clone.setMinutes(0); - clone.setSeconds(0); - clone.setMilliseconds(0); - } - else if (this.scale == 'hour') { - switch (this.step) { - case 4: - clone.setMinutes(Math.round(clone.getMinutes() / 60) * 60); break; - default: - clone.setMinutes(Math.round(clone.getMinutes() / 30) * 30); break; - } - clone.setSeconds(0); - clone.setMilliseconds(0); - } else if (this.scale == 'minute') { - //noinspection FallthroughInSwitchStatementJS - switch (this.step) { - case 15: - case 10: - clone.setMinutes(Math.round(clone.getMinutes() / 5) * 5); - clone.setSeconds(0); - break; - case 5: - clone.setSeconds(Math.round(clone.getSeconds() / 60) * 60); break; - default: - clone.setSeconds(Math.round(clone.getSeconds() / 30) * 30); break; - } - clone.setMilliseconds(0); - } - else if (this.scale == 'second') { - //noinspection FallthroughInSwitchStatementJS - switch (this.step) { - case 15: - case 10: - clone.setSeconds(Math.round(clone.getSeconds() / 5) * 5); - clone.setMilliseconds(0); - break; - case 5: - clone.setMilliseconds(Math.round(clone.getMilliseconds() / 1000) * 1000); break; - default: - clone.setMilliseconds(Math.round(clone.getMilliseconds() / 500) * 500); break; - } - } - else if (this.scale == 'millisecond') { - var step = this.step > 5 ? this.step / 2 : 1; - clone.setMilliseconds(Math.round(clone.getMilliseconds() / step) * step); + + if (horizontal !== undefined || vertical !== undefined) { + this.calculateCameraOrientation(); } - - return clone; }; /** - * Check if the current value is a major value (for example when the step - * is DAY, a major value is each first day of the MONTH) - * @return {boolean} true if current date is major, else false. + * Retrieve the current arm rotation + * @return {object} An object with parameters horizontal and vertical */ - TimeStep.prototype.isMajor = function() { - if (this.switchedYear == true) { - this.switchedYear = false; - switch (this.scale) { - case 'year': - case 'month': - case 'weekday': - case 'day': - case 'hour': - case 'minute': - case 'second': - case 'millisecond': - return true; - default: - return false; - } - } - else if (this.switchedMonth == true) { - this.switchedMonth = false; - switch (this.scale) { - case 'weekday': - case 'day': - case 'hour': - case 'minute': - case 'second': - case 'millisecond': - return true; - default: - return false; - } - } - else if (this.switchedDay == true) { - this.switchedDay = false; - switch (this.scale) { - case 'millisecond': - case 'second': - case 'minute': - case 'hour': - return true; - default: - return false; - } - } + Camera.prototype.getArmRotation = function() { + var rot = {}; + rot.horizontal = this.armRotation.horizontal; + rot.vertical = this.armRotation.vertical; - switch (this.scale) { - case 'millisecond': - return (this.current.getMilliseconds() == 0); - case 'second': - return (this.current.getSeconds() == 0); - case 'minute': - return (this.current.getHours() == 0) && (this.current.getMinutes() == 0); - case 'hour': - return (this.current.getHours() == 0); - case 'weekday': // intentional fall through - case 'day': - return (this.current.getDate() == 1); - case 'month': - return (this.current.getMonth() == 0); - case 'year': - return false; - default: - return false; - } + return rot; }; - /** - * Returns formatted text for the minor axislabel, depending on the current - * date and the scale. For example when scale is MINUTE, the current time is - * formatted as "hh:mm". - * @param {Date} [date] custom date. if not provided, current date is taken + * Set the (normalized) length of the camera arm. + * @param {Number} length A length between 0.71 and 5.0 */ - TimeStep.prototype.getLabelMinor = function(date) { - if (date == undefined) { - date = this.current; - } + Camera.prototype.setArmLength = function(length) { + if (length === undefined) + return; - var format = this.format.minorLabels[this.scale]; - return (format && format.length > 0) ? moment(date).format(format) : ''; + this.armLength = length; + + // Radius must be larger than the corner of the graph, + // which has a distance of sqrt(0.5^2+0.5^2) = 0.71 from the center of the + // graph + if (this.armLength < 0.71) this.armLength = 0.71; + if (this.armLength > 5.0) this.armLength = 5.0; + + this.calculateCameraOrientation(); }; /** - * Returns formatted text for the major axis label, depending on the current - * date and the scale. For example when scale is MINUTE, the major scale is - * hours, and the hour will be formatted as "hh". - * @param {Date} [date] custom date. if not provided, current date is taken + * Retrieve the arm length + * @return {Number} length */ - TimeStep.prototype.getLabelMajor = function(date) { - if (date == undefined) { - date = this.current; - } - - var format = this.format.majorLabels[this.scale]; - return (format && format.length > 0) ? moment(date).format(format) : ''; + Camera.prototype.getArmLength = function() { + return this.armLength; }; - TimeStep.prototype.getClassName = function() { - var m = moment(this.current); - var date = m.locale ? m.locale('en') : m.lang('en'); // old versions of moment have .lang() function - var step = this.step; + /** + * Retrieve the camera location + * @return {Point3d} cameraLocation + */ + Camera.prototype.getCameraLocation = function() { + return this.cameraLocation; + }; - function even(value) { - return (value / step % 2 == 0) ? ' even' : ' odd'; - } + /** + * Retrieve the camera rotation + * @return {Point3d} cameraRotation + */ + Camera.prototype.getCameraRotation = function() { + return this.cameraRotation; + }; - function today(date) { - if (date.isSame(new Date(), 'day')) { - return ' today'; - } - if (date.isSame(moment().add(1, 'day'), 'day')) { - return ' tomorrow'; - } - if (date.isSame(moment().add(-1, 'day'), 'day')) { - return ' yesterday'; - } - return ''; - } + /** + * Calculate the location and rotation of the camera based on the + * position and orientation of the camera arm + */ + Camera.prototype.calculateCameraOrientation = function() { + // calculate location of the camera + this.cameraLocation.x = this.armLocation.x - this.armLength * Math.sin(this.armRotation.horizontal) * Math.cos(this.armRotation.vertical); + this.cameraLocation.y = this.armLocation.y - this.armLength * Math.cos(this.armRotation.horizontal) * Math.cos(this.armRotation.vertical); + this.cameraLocation.z = this.armLocation.z + this.armLength * Math.sin(this.armRotation.vertical); - function currentWeek(date) { - return date.isSame(new Date(), 'week') ? ' current-week' : ''; - } + // calculate rotation of the camera + this.cameraRotation.x = Math.PI/2 - this.armRotation.vertical; + this.cameraRotation.y = 0; + this.cameraRotation.z = -this.armRotation.horizontal; + }; - function currentMonth(date) { - return date.isSame(new Date(), 'month') ? ' current-month' : ''; - } + module.exports = Camera; - function currentYear(date) { - return date.isSame(new Date(), 'year') ? ' current-year' : ''; - } +/***/ }, +/* 15 */ +/***/ function(module, exports, __webpack_require__) { - switch (this.scale) { - case 'millisecond': - return even(date.milliseconds()).trim(); + var DataView = __webpack_require__(9); - case 'second': - return even(date.seconds()).trim(); + /** + * @class Filter + * + * @param {DataSet} data The google data table + * @param {Number} column The index of the column to be filtered + * @param {Graph} graph The graph + */ + function Filter (data, column, graph) { + this.data = data; + this.column = column; + this.graph = graph; // the parent graph - case 'minute': - return even(date.minutes()).trim(); + this.index = undefined; + this.value = undefined; - case 'hour': - var hours = date.hours(); - if (this.step == 4) { - hours = hours + '-' + (hours + 4); - } - return hours + 'h' + today(date) + even(date.hours()); + // read all distinct values and select the first one + this.values = graph.getDistinctValues(data.get(), this.column); - case 'weekday': - return date.format('dddd').toLowerCase() + - today(date) + currentWeek(date) + even(date.date()); + // sort both numeric and string values correctly + this.values.sort(function (a, b) { + return a > b ? 1 : a < b ? -1 : 0; + }); - case 'day': - var day = date.date(); - var month = date.format('MMMM').toLowerCase(); - return 'day' + day + ' ' + month + currentMonth(date) + even(day - 1); + if (this.values.length > 0) { + this.selectValue(0); + } - case 'month': - return date.format('MMMM').toLowerCase() + - currentMonth(date) + even(date.month()); + // create an array with the filtered datapoints. this will be loaded afterwards + this.dataPoints = []; - case 'year': - var year = date.year(); - return 'year' + year + currentYear(date)+ even(year); + this.loaded = false; + this.onLoadCallback = undefined; - default: - return ''; + if (graph.animationPreload) { + this.loaded = false; + this.loadInBackground(); + } + else { + this.loaded = true; } }; - module.exports = TimeStep; - - -/***/ }, -/* 20 */ -/***/ function(module, exports, __webpack_require__) { /** - * Prototype for visual components - * @param {{dom: Object, domProps: Object, emitter: Emitter, range: Range}} [body] - * @param {Object} [options] + * Return the label + * @return {string} label */ - function Component (body, options) { - this.options = null; - this.props = null; - } + Filter.prototype.isLoaded = function() { + return this.loaded; + }; + /** - * Set options for the component. The new options will be merged into the - * current options. - * @param {Object} options + * Return the loaded progress + * @return {Number} percentage between 0 and 100 */ - Component.prototype.setOptions = function(options) { - if (options) { - util.extend(this.options, options); + Filter.prototype.getLoadedProgress = function() { + var len = this.values.length; + + var i = 0; + while (this.dataPoints[i]) { + i++; } + + return Math.round(i / len * 100); }; + /** - * Repaint the component - * @return {boolean} Returns true if the component is resized + * Return the label + * @return {string} label */ - Component.prototype.redraw = function() { - // should be implemented by the component - return false; + Filter.prototype.getLabel = function() { + return this.graph.filterLabel; }; + /** - * Destroy the component. Cleanup DOM and event listeners + * Return the columnIndex of the filter + * @return {Number} columnIndex */ - Component.prototype.destroy = function() { - // should be implemented by the component + Filter.prototype.getColumn = function() { + return this.column; }; /** - * Test whether the component is resized since the last time _isResized() was - * called. - * @return {Boolean} Returns true if the component is resized - * @protected + * Return the currently selected value. Returns undefined if there is no selection + * @return {*} value */ - Component.prototype._isResized = function() { - var resized = (this.props._previousWidth !== this.props.width || - this.props._previousHeight !== this.props.height); - - this.props._previousWidth = this.props.width; - this.props._previousHeight = this.props.height; + Filter.prototype.getSelectedValue = function() { + if (this.index === undefined) + return undefined; - return resized; + return this.values[this.index]; }; - module.exports = Component; + /** + * Retrieve all values of the filter + * @return {Array} values + */ + Filter.prototype.getValues = function() { + return this.values; + }; + /** + * Retrieve one value of the filter + * @param {Number} index + * @return {*} value + */ + Filter.prototype.getValue = function(index) { + if (index >= this.values.length) + throw 'Error: index out of range'; -/***/ }, -/* 21 */ -/***/ function(module, exports, __webpack_require__) { + return this.values[index]; + }; - var util = __webpack_require__(1); - var Component = __webpack_require__(20); - var moment = __webpack_require__(44); - var locales = __webpack_require__(48); /** - * A current time bar - * @param {{range: Range, dom: Object, domProps: Object}} body - * @param {Object} [options] Available parameters: - * {Boolean} [showCurrentTime] - * @constructor CurrentTime - * @extends Component + * Retrieve the (filtered) dataPoints for the currently selected filter index + * @param {Number} [index] (optional) + * @return {Array} dataPoints */ - function CurrentTime (body, options) { - this.body = body; + Filter.prototype._getDataPoints = function(index) { + if (index === undefined) + index = this.index; - // default options - this.defaultOptions = { - showCurrentTime: true, + if (index === undefined) + return []; - locales: locales, - locale: 'en' - }; - this.options = util.extend({}, this.defaultOptions); - this.offset = 0; + var dataPoints; + if (this.dataPoints[index]) { + dataPoints = this.dataPoints[index]; + } + else { + var f = {}; + f.column = this.column; + f.value = this.values[index]; - this._create(); + var dataView = new DataView(this.data,{filter: function (item) {return (item[f.column] == f.value);}}).get(); + dataPoints = this.graph._getDataPoints(dataView); - this.setOptions(options); - } + this.dataPoints[index] = dataPoints; + } - CurrentTime.prototype = new Component(); + return dataPoints; + }; - /** - * Create the HTML DOM for the current time bar - * @private - */ - CurrentTime.prototype._create = function() { - var bar = document.createElement('div'); - bar.className = 'currenttime'; - bar.style.position = 'absolute'; - bar.style.top = '0px'; - bar.style.height = '100%'; - this.bar = bar; - }; /** - * Destroy the CurrentTime bar + * Set a callback function when the filter is fully loaded. */ - CurrentTime.prototype.destroy = function () { - this.options.showCurrentTime = false; - this.redraw(); // will remove the bar from the DOM and stop refreshing - - this.body = null; + Filter.prototype.setOnLoadCallback = function(callback) { + this.onLoadCallback = callback; }; + /** - * Set options for the component. Options will be merged in current options. - * @param {Object} options Available parameters: - * {boolean} [showCurrentTime] + * Add a value to the list with available values for this filter + * No double entries will be created. + * @param {Number} index */ - CurrentTime.prototype.setOptions = function(options) { - if (options) { - // copy all options that we know - util.selectiveExtend(['showCurrentTime', 'locale', 'locales'], this.options, options); - } + Filter.prototype.selectValue = function(index) { + if (index >= this.values.length) + throw 'Error: index out of range'; + + this.index = index; + this.value = this.values[index]; }; /** - * Repaint the component - * @return {boolean} Returns true if the component is resized + * Load all filtered rows in the background one by one + * Start this method without providing an index! */ - CurrentTime.prototype.redraw = function() { - if (this.options.showCurrentTime) { - var parent = this.body.dom.backgroundVertical; - if (this.bar.parentNode != parent) { - // attach to the dom - if (this.bar.parentNode) { - this.bar.parentNode.removeChild(this.bar); - } - parent.appendChild(this.bar); - - this.start(); - } + Filter.prototype.loadInBackground = function(index) { + if (index === undefined) + index = 0; - var now = new Date(new Date().valueOf() + this.offset); - var x = this.body.util.toScreen(now); + var frame = this.graph.frame; - var locale = this.options.locales[this.options.locale]; - var title = locale.current + ' ' + locale.time + ': ' + moment(now).format('dddd, MMMM Do YYYY, H:mm:ss'); - title = title.charAt(0).toUpperCase() + title.substring(1); + if (index < this.values.length) { + var dataPointsTemp = this._getDataPoints(index); + //this.graph.redrawInfo(); // TODO: not neat - this.bar.style.left = x + 'px'; - this.bar.title = title; - } - else { - // remove the line from the DOM - if (this.bar.parentNode) { - this.bar.parentNode.removeChild(this.bar); - } - this.stop(); + // create a progress box + if (frame.progress === undefined) { + frame.progress = document.createElement('DIV'); + frame.progress.style.position = 'absolute'; + frame.progress.style.color = 'gray'; + frame.appendChild(frame.progress); + } + var progress = this.getLoadedProgress(); + frame.progress.innerHTML = 'Loading animation... ' + progress + '%'; + // TODO: this is no nice solution... + frame.progress.style.bottom = 60 + 'px'; // TODO: use height of slider + frame.progress.style.left = 10 + 'px'; + + var me = this; + setTimeout(function() {me.loadInBackground(index+1);}, 10); + this.loaded = false; } + else { + this.loaded = true; - return false; + // remove the progress box + if (frame.progress !== undefined) { + frame.removeChild(frame.progress); + frame.progress = undefined; + } + + if (this.onLoadCallback) + this.onLoadCallback(); + } }; + module.exports = Filter; + + +/***/ }, +/* 16 */ +/***/ function(module, exports, __webpack_require__) { + + var util = __webpack_require__(1); + /** - * Start auto refreshing the current time bar + * @constructor Slider + * + * An html slider control with start/stop/prev/next buttons + * @param {Element} container The element where the slider will be created + * @param {Object} options Available options: + * {boolean} visible If true (default) the + * slider is visible. */ - CurrentTime.prototype.start = function() { - var me = this; + function Slider(container, options) { + if (container === undefined) { + throw 'Error: No container element defined'; + } + this.container = container; + this.visible = (options && options.visible != undefined) ? options.visible : true; - function update () { - me.stop(); + if (this.visible) { + this.frame = document.createElement('DIV'); + //this.frame.style.backgroundColor = '#E5E5E5'; + this.frame.style.width = '100%'; + this.frame.style.position = 'relative'; + this.container.appendChild(this.frame); - // determine interval to refresh - var scale = me.body.range.conversion(me.body.domProps.center.width).scale; - var interval = 1 / scale / 10; - if (interval < 30) interval = 30; - if (interval > 1000) interval = 1000; + this.frame.prev = document.createElement('INPUT'); + this.frame.prev.type = 'BUTTON'; + this.frame.prev.value = 'Prev'; + this.frame.appendChild(this.frame.prev); - me.redraw(); + this.frame.play = document.createElement('INPUT'); + this.frame.play.type = 'BUTTON'; + this.frame.play.value = 'Play'; + this.frame.appendChild(this.frame.play); - // start a timer to adjust for the new time - me.currentTimeTimer = setTimeout(update, interval); + this.frame.next = document.createElement('INPUT'); + this.frame.next.type = 'BUTTON'; + this.frame.next.value = 'Next'; + this.frame.appendChild(this.frame.next); + + this.frame.bar = document.createElement('INPUT'); + this.frame.bar.type = 'BUTTON'; + this.frame.bar.style.position = 'absolute'; + this.frame.bar.style.border = '1px solid red'; + this.frame.bar.style.width = '100px'; + this.frame.bar.style.height = '6px'; + this.frame.bar.style.borderRadius = '2px'; + this.frame.bar.style.MozBorderRadius = '2px'; + this.frame.bar.style.border = '1px solid #7F7F7F'; + this.frame.bar.style.backgroundColor = '#E5E5E5'; + this.frame.appendChild(this.frame.bar); + + this.frame.slide = document.createElement('INPUT'); + this.frame.slide.type = 'BUTTON'; + this.frame.slide.style.margin = '0px'; + this.frame.slide.value = ' '; + this.frame.slide.style.position = 'relative'; + this.frame.slide.style.left = '-100px'; + this.frame.appendChild(this.frame.slide); + + // create events + var me = this; + this.frame.slide.onmousedown = function (event) {me._onMouseDown(event);}; + this.frame.prev.onclick = function (event) {me.prev(event);}; + this.frame.play.onclick = function (event) {me.togglePlay(event);}; + this.frame.next.onclick = function (event) {me.next(event);}; } - update(); - }; + this.onChangeCallback = undefined; + + this.values = []; + this.index = undefined; + + this.playTimeout = undefined; + this.playInterval = 1000; // milliseconds + this.playLoop = true; + } /** - * Stop auto refreshing the current time bar + * Select the previous index */ - CurrentTime.prototype.stop = function() { - if (this.currentTimeTimer !== undefined) { - clearTimeout(this.currentTimeTimer); - delete this.currentTimeTimer; + Slider.prototype.prev = function() { + var index = this.getIndex(); + if (index > 0) { + index--; + this.setIndex(index); } }; /** - * Set a current time. This can be used for example to ensure that a client's - * time is synchronized with a shared server time. - * @param {Date | String | Number} time A Date, unix timestamp, or - * ISO date string. + * Select the next index */ - CurrentTime.prototype.setCurrentTime = function(time) { - var t = util.convert(time, 'Date').valueOf(); - var now = new Date().valueOf(); - this.offset = t - now; - this.redraw(); + Slider.prototype.next = function() { + var index = this.getIndex(); + if (index < this.values.length - 1) { + index++; + this.setIndex(index); + } }; /** - * Get the current time. - * @return {Date} Returns the current time. + * Select the next index */ - CurrentTime.prototype.getCurrentTime = function() { - return new Date(new Date().valueOf() + this.offset); - }; + Slider.prototype.playNext = function() { + var start = new Date(); - module.exports = CurrentTime; + var index = this.getIndex(); + if (index < this.values.length - 1) { + index++; + this.setIndex(index); + } + else if (this.playLoop) { + // jump to the start + index = 0; + this.setIndex(index); + } + var end = new Date(); + var diff = (end - start); -/***/ }, -/* 22 */ -/***/ function(module, exports, __webpack_require__) { + // calculate how much time it to to set the index and to execute the callback + // function. + var interval = Math.max(this.playInterval - diff, 0); + // document.title = diff // TODO: cleanup - var Hammer = __webpack_require__(45); - var util = __webpack_require__(1); - var Component = __webpack_require__(20); - var moment = __webpack_require__(44); - var locales = __webpack_require__(48); + var me = this; + this.playTimeout = setTimeout(function() {me.playNext();}, interval); + }; /** - * A custom time bar - * @param {{range: Range, dom: Object}} body - * @param {Object} [options] Available parameters: - * {Boolean} [showCustomTime] - * @constructor CustomTime - * @extends Component + * Toggle start or stop playing */ + Slider.prototype.togglePlay = function() { + if (this.playTimeout === undefined) { + this.play(); + } else { + this.stop(); + } + }; - function CustomTime (body, options) { - this.body = body; - - // default options - this.defaultOptions = { - showCustomTime: false, - locales: locales, - locale: 'en' - }; - this.options = util.extend({}, this.defaultOptions); - - this.customTime = new Date(); - this.eventParams = {}; // stores state parameters while dragging the bar - - // create the DOM - this._create(); + /** + * Start playing + */ + Slider.prototype.play = function() { + // Test whether already playing + if (this.playTimeout) return; - this.setOptions(options); - } + this.playNext(); - CustomTime.prototype = new Component(); + if (this.frame) { + this.frame.play.value = 'Stop'; + } + }; /** - * Set options for the component. Options will be merged in current options. - * @param {Object} options Available parameters: - * {boolean} [showCustomTime] + * Stop playing */ - CustomTime.prototype.setOptions = function(options) { - if (options) { - // copy all options that we know - util.selectiveExtend(['showCustomTime', 'locale', 'locales'], this.options, options); + Slider.prototype.stop = function() { + clearInterval(this.playTimeout); + this.playTimeout = undefined; + + if (this.frame) { + this.frame.play.value = 'Play'; } }; /** - * Create the DOM for the custom time - * @private + * Set a callback function which will be triggered when the value of the + * slider bar has changed. */ - CustomTime.prototype._create = function() { - var bar = document.createElement('div'); - bar.className = 'customtime'; - bar.style.position = 'absolute'; - bar.style.top = '0px'; - bar.style.height = '100%'; - this.bar = bar; + Slider.prototype.setOnChangeCallback = function(callback) { + this.onChangeCallback = callback; + }; - var drag = document.createElement('div'); - drag.style.position = 'relative'; - drag.style.top = '0px'; - drag.style.left = '-10px'; - drag.style.height = '100%'; - drag.style.width = '20px'; - bar.appendChild(drag); + /** + * Set the interval for playing the list + * @param {Number} interval The interval in milliseconds + */ + Slider.prototype.setPlayInterval = function(interval) { + this.playInterval = interval; + }; - // attach event listeners - this.hammer = Hammer(bar, { - prevent_default: true - }); - this.hammer.on('dragstart', this._onDragStart.bind(this)); - this.hammer.on('drag', this._onDrag.bind(this)); - this.hammer.on('dragend', this._onDragEnd.bind(this)); + /** + * Retrieve the current play interval + * @return {Number} interval The interval in milliseconds + */ + Slider.prototype.getPlayInterval = function(interval) { + return this.playInterval; }; /** - * Destroy the CustomTime bar + * Set looping on or off + * @pararm {boolean} doLoop If true, the slider will jump to the start when + * the end is passed, and will jump to the end + * when the start is passed. */ - CustomTime.prototype.destroy = function () { - this.options.showCustomTime = false; - this.redraw(); // will remove the bar from the DOM + Slider.prototype.setPlayLoop = function(doLoop) { + this.playLoop = doLoop; + }; - this.hammer.enable(false); - this.hammer = null; - this.body = null; + /** + * Execute the onchange callback function + */ + Slider.prototype.onChange = function() { + if (this.onChangeCallback !== undefined) { + this.onChangeCallback(); + } }; /** - * Repaint the component - * @return {boolean} Returns true if the component is resized + * redraw the slider on the correct place */ - CustomTime.prototype.redraw = function () { - if (this.options.showCustomTime) { - var parent = this.body.dom.backgroundVertical; - if (this.bar.parentNode != parent) { - // attach to the dom - if (this.bar.parentNode) { - this.bar.parentNode.removeChild(this.bar); - } - parent.appendChild(this.bar); - } - - var x = this.body.util.toScreen(this.customTime); - - var locale = this.options.locales[this.options.locale]; - var title = locale.time + ': ' + moment(this.customTime).format('dddd, MMMM Do YYYY, H:mm:ss'); - title = title.charAt(0).toUpperCase() + title.substring(1); + Slider.prototype.redraw = function() { + if (this.frame) { + // resize the bar + this.frame.bar.style.top = (this.frame.clientHeight/2 - + this.frame.bar.offsetHeight/2) + 'px'; + this.frame.bar.style.width = (this.frame.clientWidth - + this.frame.prev.clientWidth - + this.frame.play.clientWidth - + this.frame.next.clientWidth - 30) + 'px'; - this.bar.style.left = x + 'px'; - this.bar.title = title; - } - else { - // remove the line from the DOM - if (this.bar.parentNode) { - this.bar.parentNode.removeChild(this.bar); - } + // position the slider button + var left = this.indexToLeft(this.index); + this.frame.slide.style.left = (left) + 'px'; } - - return false; }; + /** - * Set custom time. - * @param {Date | number | string} time + * Set the list with values for the slider + * @param {Array} values A javascript array with values (any type) */ - CustomTime.prototype.setCustomTime = function(time) { - this.customTime = util.convert(time, 'Date'); - this.redraw(); + Slider.prototype.setValues = function(values) { + this.values = values; + + if (this.values.length > 0) + this.setIndex(0); + else + this.index = undefined; }; /** - * Retrieve the current custom time. - * @return {Date} customTime + * Select a value by its index + * @param {Number} index */ - CustomTime.prototype.getCustomTime = function() { - return new Date(this.customTime.valueOf()); + Slider.prototype.setIndex = function(index) { + if (index < this.values.length) { + this.index = index; + + this.redraw(); + this.onChange(); + } + else { + throw 'Error: index out of range'; + } }; /** - * Start moving horizontally - * @param {Event} event - * @private + * retrieve the index of the currently selected vaue + * @return {Number} index */ - CustomTime.prototype._onDragStart = function(event) { - this.eventParams.dragging = true; - this.eventParams.customTime = this.customTime; - - event.stopPropagation(); - event.preventDefault(); + Slider.prototype.getIndex = function() { + return this.index; }; + /** - * Perform moving operating. - * @param {Event} event - * @private + * retrieve the currently selected value + * @return {*} value */ - CustomTime.prototype._onDrag = function (event) { - if (!this.eventParams.dragging) return; + Slider.prototype.get = function() { + return this.values[this.index]; + }; - var deltaX = event.gesture.deltaX, - x = this.body.util.toScreen(this.eventParams.customTime) + deltaX, - time = this.body.util.toTime(x); - this.setCustomTime(time); + Slider.prototype._onMouseDown = function(event) { + // only react on left mouse button down + var leftButtonDown = event.which ? (event.which === 1) : (event.button === 1); + if (!leftButtonDown) return; - // fire a timechange event - this.body.emitter.emit('timechange', { - time: new Date(this.customTime.valueOf()) - }); + this.startClientX = event.clientX; + this.startSlideX = parseFloat(this.frame.slide.style.left); - event.stopPropagation(); - event.preventDefault(); + this.frame.style.cursor = 'move'; + + // add event listeners to handle moving the contents + // we store the function onmousemove and onmouseup in the graph, so we can + // remove the eventlisteners lateron in the function mouseUp() + var me = this; + this.onmousemove = function (event) {me._onMouseMove(event);}; + this.onmouseup = function (event) {me._onMouseUp(event);}; + util.addEventListener(document, 'mousemove', this.onmousemove); + util.addEventListener(document, 'mouseup', this.onmouseup); + util.preventDefault(event); }; - /** - * Stop moving operating. - * @param {event} event - * @private - */ - CustomTime.prototype._onDragEnd = function (event) { - if (!this.eventParams.dragging) return; - // fire a timechanged event - this.body.emitter.emit('timechanged', { - time: new Date(this.customTime.valueOf()) - }); + Slider.prototype.leftToIndex = function (left) { + var width = parseFloat(this.frame.bar.style.width) - + this.frame.slide.clientWidth - 10; + var x = left - 3; - event.stopPropagation(); - event.preventDefault(); + var index = Math.round(x / width * (this.values.length-1)); + if (index < 0) index = 0; + if (index > this.values.length-1) index = this.values.length-1; + + return index; }; - module.exports = CustomTime; + Slider.prototype.indexToLeft = function (index) { + var width = parseFloat(this.frame.bar.style.width) - + this.frame.slide.clientWidth - 10; + var x = index / (this.values.length-1) * width; + var left = x + 3; -/***/ }, -/* 23 */ -/***/ function(module, exports, __webpack_require__) { + return left; + }; - var util = __webpack_require__(1); - var DOMutil = __webpack_require__(2); - var Component = __webpack_require__(20); - var DataStep = __webpack_require__(16); - /** - * A horizontal time axis - * @param {Object} [options] See DataAxis.setOptions for the available - * options. - * @constructor DataAxis - * @extends Component - * @param body - */ - function DataAxis (body, options, svg, linegraphOptions) { - this.id = util.randomUUID(); - this.body = body; - this.defaultOptions = { - orientation: 'left', // supported: 'left', 'right' - showMinorLabels: true, - showMajorLabels: true, - icons: true, - majorLinesOffset: 7, - minorLinesOffset: 4, - labelOffsetX: 10, - labelOffsetY: 2, - iconWidth: 20, - width: '40px', - visible: true, - alignZeros: true, - customRange: { - left: {min:undefined, max:undefined}, - right: {min:undefined, max:undefined} - }, - title: { - left: {text:undefined}, - right: {text:undefined} - }, - format: { - left: {decimals: undefined}, - right: {decimals: undefined} - } - }; + Slider.prototype._onMouseMove = function (event) { + var diff = event.clientX - this.startClientX; + var x = this.startSlideX + diff; - this.linegraphOptions = linegraphOptions; - this.linegraphSVG = svg; - this.props = {}; - this.DOMelements = { // dynamic elements - lines: {}, - labels: {}, - title: {} - }; + var index = this.leftToIndex(x); - this.dom = {}; + this.setIndex(index); - this.range = {start:0, end:0}; + util.preventDefault(); + }; - this.options = util.extend({}, this.defaultOptions); - this.conversionFactor = 1; - this.setOptions(options); - this.width = Number(('' + this.options.width).replace("px","")); - this.minWidth = this.width; - this.height = this.linegraphSVG.offsetHeight; - this.hidden = false; - - this.stepPixels = 25; - this.stepPixelsForced = 25; - this.zeroCrossing = -1; - - this.lineOffset = 0; - this.master = true; - this.svgElements = {}; - this.iconsRemoved = false; - - - this.groups = {}; - this.amountOfGroups = 0; + Slider.prototype._onMouseUp = function (event) { + this.frame.style.cursor = 'auto'; - // create the HTML DOM - this._create(); + // remove event listeners + util.removeEventListener(document, 'mousemove', this.onmousemove); + util.removeEventListener(document, 'mouseup', this.onmouseup); - var me = this; - this.body.emitter.on("verticalDrag", function() { - me.dom.lineContainer.style.top = me.body.domProps.scrollTop + 'px'; - }); - } + util.preventDefault(); + }; - DataAxis.prototype = new Component(); + module.exports = Slider; - DataAxis.prototype.addGroup = function(label, graphOptions) { - if (!this.groups.hasOwnProperty(label)) { - this.groups[label] = graphOptions; - } - this.amountOfGroups += 1; - }; +/***/ }, +/* 17 */ +/***/ function(module, exports, __webpack_require__) { - DataAxis.prototype.updateGroup = function(label, graphOptions) { - this.groups[label] = graphOptions; - }; + /** + * @prototype StepNumber + * The class StepNumber is an iterator for Numbers. You provide a start and end + * value, and a best step size. StepNumber itself rounds to fixed values and + * a finds the step that best fits the provided step. + * + * If prettyStep is true, the step size is chosen as close as possible to the + * provided step, but being a round value like 1, 2, 5, 10, 20, 50, .... + * + * Example usage: + * var step = new StepNumber(0, 10, 2.5, true); + * step.start(); + * while (!step.end()) { + * alert(step.getCurrent()); + * step.next(); + * } + * + * Version: 1.0 + * + * @param {Number} start The start value + * @param {Number} end The end value + * @param {Number} step Optional. Step size. Must be a positive value. + * @param {boolean} prettyStep Optional. If true, the step size is rounded + * To a pretty step size (like 1, 2, 5, 10, 20, 50, ...) + */ + function StepNumber(start, end, step, prettyStep) { + // set default values + this._start = 0; + this._end = 0; + this._step = 1; + this.prettyStep = true; + this.precision = 5; - DataAxis.prototype.removeGroup = function(label) { - if (this.groups.hasOwnProperty(label)) { - delete this.groups[label]; - this.amountOfGroups -= 1; - } + this._current = 0; + this.setRange(start, end, step, prettyStep); }; + /** + * Set a new range: start, end and step. + * + * @param {Number} start The start value + * @param {Number} end The end value + * @param {Number} step Optional. Step size. Must be a positive value. + * @param {boolean} prettyStep Optional. If true, the step size is rounded + * To a pretty step size (like 1, 2, 5, 10, 20, 50, ...) + */ + StepNumber.prototype.setRange = function(start, end, step, prettyStep) { + this._start = start ? start : 0; + this._end = end ? end : 0; - DataAxis.prototype.setOptions = function (options) { - if (options) { - var redraw = false; - if (this.options.orientation != options.orientation && options.orientation !== undefined) { - redraw = true; - } - var fields = [ - 'orientation', - 'showMinorLabels', - 'showMajorLabels', - 'icons', - 'majorLinesOffset', - 'minorLinesOffset', - 'labelOffsetX', - 'labelOffsetY', - 'iconWidth', - 'width', - 'visible', - 'customRange', - 'title', - 'format', - 'alignZeros' - ]; - util.selectiveExtend(fields, this.options, options); - - this.minWidth = Number(('' + this.options.width).replace("px","")); - - if (redraw == true && this.dom.frame) { - this.hide(); - this.show(); - } - } + this.setStep(step, prettyStep); }; - /** - * Create the HTML DOM for the DataAxis + * Set a new step size + * @param {Number} step New step size. Must be a positive value + * @param {boolean} prettyStep Optional. If true, the provided step is rounded + * to a pretty step size (like 1, 2, 5, 10, 20, 50, ...) */ - DataAxis.prototype._create = function() { - this.dom.frame = document.createElement('div'); - this.dom.frame.style.width = this.options.width; - this.dom.frame.style.height = this.height; + StepNumber.prototype.setStep = function(step, prettyStep) { + if (step === undefined || step <= 0) + return; - this.dom.lineContainer = document.createElement('div'); - this.dom.lineContainer.style.width = '100%'; - this.dom.lineContainer.style.height = this.height; - this.dom.lineContainer.style.position = 'relative'; + if (prettyStep !== undefined) + this.prettyStep = prettyStep; - // create svg element for graph drawing. - this.svg = document.createElementNS('http://www.w3.org/2000/svg',"svg"); - this.svg.style.position = "absolute"; - this.svg.style.top = '0px'; - this.svg.style.height = '100%'; - this.svg.style.width = '100%'; - this.svg.style.display = "block"; - this.dom.frame.appendChild(this.svg); + if (this.prettyStep === true) + this._step = StepNumber.calculatePrettyStep(step); + else + this._step = step; }; - DataAxis.prototype._redrawGroupIcons = function () { - DOMutil.prepareElements(this.svgElements); + /** + * Calculate a nice step size, closest to the desired step size. + * Returns a value in one of the ranges 1*10^n, 2*10^n, or 5*10^n, where n is an + * integer Number. For example 1, 2, 5, 10, 20, 50, etc... + * @param {Number} step Desired step size + * @return {Number} Nice step size + */ + StepNumber.calculatePrettyStep = function (step) { + var log10 = function (x) {return Math.log(x) / Math.LN10;}; - var x; - var iconWidth = this.options.iconWidth; - var iconHeight = 15; - var iconOffset = 4; - var y = iconOffset + 0.5 * iconHeight; + // try three steps (multiple of 1, 2, or 5 + var step1 = Math.pow(10, Math.round(log10(step))), + step2 = 2 * Math.pow(10, Math.round(log10(step / 2))), + step5 = 5 * Math.pow(10, Math.round(log10(step / 5))); - if (this.options.orientation == 'left') { - x = iconOffset; - } - else { - x = this.width - iconWidth - iconOffset; - } + // choose the best step (closest to minimum step) + var prettyStep = step1; + if (Math.abs(step2 - step) <= Math.abs(prettyStep - step)) prettyStep = step2; + if (Math.abs(step5 - step) <= Math.abs(prettyStep - step)) prettyStep = step5; - for (var groupId in this.groups) { - if (this.groups.hasOwnProperty(groupId)) { - if (this.groups[groupId].visible == true && (this.linegraphOptions.visibility[groupId] === undefined || this.linegraphOptions.visibility[groupId] == true)) { - this.groups[groupId].drawIcon(x, y, this.svgElements, this.svg, iconWidth, iconHeight); - y += iconHeight + iconOffset; - } - } + // for safety + if (prettyStep <= 0) { + prettyStep = 1; } - DOMutil.cleanupElements(this.svgElements); - this.iconsRemoved = false; + return prettyStep; }; - DataAxis.prototype._cleanupIcons = function() { - if (this.iconsRemoved == false) { - DOMutil.prepareElements(this.svgElements); - DOMutil.cleanupElements(this.svgElements); - this.iconsRemoved = true; - } - } - /** - * Create the HTML DOM for the DataAxis + * returns the current value of the step + * @return {Number} current value */ - DataAxis.prototype.show = function() { - this.hidden = false; - if (!this.dom.frame.parentNode) { - if (this.options.orientation == 'left') { - this.body.dom.left.appendChild(this.dom.frame); - } - else { - this.body.dom.right.appendChild(this.dom.frame); - } - } - - if (!this.dom.lineContainer.parentNode) { - this.body.dom.backgroundHorizontal.appendChild(this.dom.lineContainer); - } + StepNumber.prototype.getCurrent = function () { + return parseFloat(this._current.toPrecision(this.precision)); }; /** - * Create the HTML DOM for the DataAxis + * returns the current step size + * @return {Number} current step size */ - DataAxis.prototype.hide = function() { - this.hidden = true; - if (this.dom.frame.parentNode) { - this.dom.frame.parentNode.removeChild(this.dom.frame); - } + StepNumber.prototype.getStep = function () { + return this._step; + }; - if (this.dom.lineContainer.parentNode) { - this.dom.lineContainer.parentNode.removeChild(this.dom.lineContainer); - } + /** + * Set the current value to the largest value smaller than start, which + * is a multiple of the step size + */ + StepNumber.prototype.start = function() { + this._current = this._start - this._start % this._step; }; /** - * Set a range (start and end) - * @param end - * @param start - * @param end + * Do a step, add the step size to the current value */ - DataAxis.prototype.setRange = function (start, end) { - if (this.master == false && this.options.alignZeros == true && this.zeroCrossing != -1) { - if (start > 0) { - start = 0; - } - } - this.range.start = start; - this.range.end = end; + StepNumber.prototype.next = function () { + this._current += this._step; }; /** - * Repaint the component - * @return {boolean} Returns true if the component is resized + * Returns true whether the end is reached + * @return {boolean} True if the current value has passed the end value. */ - DataAxis.prototype.redraw = function () { - var resized = false; - var activeGroups = 0; - - // Make sure the line container adheres to the vertical scrolling. - this.dom.lineContainer.style.top = this.body.domProps.scrollTop + 'px'; + StepNumber.prototype.end = function () { + return (this._current > this._end); + }; - for (var groupId in this.groups) { - if (this.groups.hasOwnProperty(groupId)) { - if (this.groups[groupId].visible == true && (this.linegraphOptions.visibility[groupId] === undefined || this.linegraphOptions.visibility[groupId] == true)) { - activeGroups++; - } - } - } - if (this.amountOfGroups == 0 || activeGroups == 0) { - this.hide(); - } - else { - this.show(); - this.height = Number(this.linegraphSVG.style.height.replace("px","")); + module.exports = StepNumber; - // svg offsetheight did not work in firefox and explorer... - this.dom.lineContainer.style.height = this.height + 'px'; - this.width = this.options.visible == true ? Number(('' + this.options.width).replace("px","")) : 0; - var props = this.props; - var frame = this.dom.frame; +/***/ }, +/* 18 */ +/***/ function(module, exports, __webpack_require__) { - // update classname - frame.className = 'dataaxis'; + var Emitter = __webpack_require__(11); + var Hammer = __webpack_require__(19); + var util = __webpack_require__(1); + var DataSet = __webpack_require__(7); + var DataView = __webpack_require__(9); + var Range = __webpack_require__(21); + var Core = __webpack_require__(25); + var TimeAxis = __webpack_require__(37); + var CurrentTime = __webpack_require__(39); + var CustomTime = __webpack_require__(41); + var ItemSet = __webpack_require__(26); - // calculate character width and height - this._calculateCharSize(); + /** + * Create a timeline visualization + * @param {HTMLElement} container + * @param {vis.DataSet | Array | google.visualization.DataTable} [items] + * @param {vis.DataSet | Array | google.visualization.DataTable} [groups] + * @param {Object} [options] See Timeline.setOptions for the available options. + * @constructor + * @extends Core + */ + function Timeline (container, items, groups, options) { + if (!(this instanceof Timeline)) { + throw new SyntaxError('Constructor must be called with the new operator'); + } - var orientation = this.options.orientation; - var showMinorLabels = this.options.showMinorLabels; - var showMajorLabels = this.options.showMajorLabels; + // if the third element is options, the forth is groups (optionally); + if (!(Array.isArray(groups) || groups instanceof DataSet) && groups instanceof Object) { + var forthArgument = options; + options = groups; + groups = forthArgument; + } - // determine the width and height of the elements for the axis - props.minorLabelHeight = showMinorLabels ? props.minorCharHeight : 0; - props.majorLabelHeight = showMajorLabels ? props.majorCharHeight : 0; + var me = this; + this.defaultOptions = { + start: null, + end: null, - props.minorLineWidth = this.body.dom.backgroundHorizontal.offsetWidth - this.lineOffset - this.width + 2 * this.options.minorLinesOffset; - props.minorLineHeight = 1; - props.majorLineWidth = this.body.dom.backgroundHorizontal.offsetWidth - this.lineOffset - this.width + 2 * this.options.majorLinesOffset; - props.majorLineHeight = 1; + autoResize: true, - // take frame offline while updating (is almost twice as fast) - if (orientation == 'left') { - frame.style.top = '0'; - frame.style.left = '0'; - frame.style.bottom = ''; - frame.style.width = this.width + 'px'; - frame.style.height = this.height + "px"; - this.props.width = this.body.domProps.left.width; - this.props.height = this.body.domProps.left.height; - } - else { // right - frame.style.top = ''; - frame.style.bottom = '0'; - frame.style.left = '0'; - frame.style.width = this.width + 'px'; - frame.style.height = this.height + "px"; - this.props.width = this.body.domProps.right.width; - this.props.height = this.body.domProps.right.height; - } + orientation: 'bottom', + width: null, + height: null, + maxHeight: null, + minHeight: null + }; + this.options = util.deepExtend({}, this.defaultOptions); - resized = this._redrawLabels(); - resized = this._isResized() || resized; + // Create the DOM, props, and emitter + this._create(container); - if (this.options.icons == true) { - this._redrawGroupIcons(); - } - else { - this._cleanupIcons(); - } + // all components listed here will be repainted automatically + this.components = []; - this._redrawTitle(orientation); - } - return resized; - }; + this.body = { + dom: this.dom, + domProps: this.props, + emitter: { + on: this.on.bind(this), + off: this.off.bind(this), + emit: this.emit.bind(this) + }, + hiddenDates: [], + util: { + snap: null, // will be specified after TimeAxis is created + toScreen: me._toScreen.bind(me), + toGlobalScreen: me._toGlobalScreen.bind(me), // this refers to the root.width + toTime: me._toTime.bind(me), + toGlobalTime : me._toGlobalTime.bind(me) + } + }; - /** - * Repaint major and minor text labels and vertical grid lines - * @private - */ - DataAxis.prototype._redrawLabels = function () { - var resized = false; - DOMutil.prepareElements(this.DOMelements.lines); - DOMutil.prepareElements(this.DOMelements.labels); + // range + this.range = new Range(this.body); + this.components.push(this.range); + this.body.range = this.range; - var orientation = this.options['orientation']; + // time axis + this.timeAxis = new TimeAxis(this.body); + this.components.push(this.timeAxis); + this.body.util.snap = this.timeAxis.snap.bind(this.timeAxis); - // calculate range and step (step such that we have space for 7 characters per label) - var minimumStep = this.master ? this.props.majorCharHeight || 10 : this.stepPixelsForced; + // current time bar + this.currentTime = new CurrentTime(this.body); + this.components.push(this.currentTime); - var step = new DataStep( - this.range.start, - this.range.end, - minimumStep, - this.dom.frame.offsetHeight, - this.options.customRange[this.options.orientation], - this.master == false && this.options.alignZeros // doess the step have to align zeros? only if not master and the options is on - ); + // custom time bar + // Note: time bar will be attached in this.setOptions when selected + this.customTime = new CustomTime(this.body); + this.components.push(this.customTime); - this.step = step; - // get the distance in pixels for a step - // dead space is space that is "left over" after a step - var stepPixels = (this.dom.frame.offsetHeight - (step.deadSpace * (this.dom.frame.offsetHeight / step.marginRange))) / (((step.marginRange - step.deadSpace) / step.step)); + // item set + this.itemSet = new ItemSet(this.body); + this.components.push(this.itemSet); - this.stepPixels = stepPixels; + this.itemsData = null; // DataSet + this.groupsData = null; // DataSet - var amountOfSteps = this.height / stepPixels; - var stepDifference = 0; + // apply options + if (options) { + this.setOptions(options); + } - // the slave axis needs to use the same horizontal lines as the master axis. - if (this.master == false) { - stepPixels = this.stepPixelsForced; - stepDifference = Math.round((this.dom.frame.offsetHeight / stepPixels) - amountOfSteps); - for (var i = 0; i < 0.5 * stepDifference; i++) { - step.previous(); - } - amountOfSteps = this.height / stepPixels; + // IMPORTANT: THIS HAPPENS BEFORE SET ITEMS! + if (groups) { + this.setGroups(groups); + } - if (this.zeroCrossing != -1 && this.options.alignZeros == true) { - var zeroStepDifference = (step.marginEnd / step.step) - this.zeroCrossing; - if (zeroStepDifference > 0) { - for (var i = 0; i < zeroStepDifference; i++) {step.next();} - } - else if (zeroStepDifference < 0) { - for (var i = 0; i < -zeroStepDifference; i++) {step.previous();} - } - } + // create itemset + if (items) { + this.setItems(items); } else { - amountOfSteps += 0.25; + this.redraw(); } + } + // Extend the functionality from Core + Timeline.prototype = new Core(); - this.valueAtZero = step.marginEnd; - var marginStartPos = 0; - - // do not draw the first label - var max = 1; + /** + * Set items + * @param {vis.DataSet | Array | google.visualization.DataTable | null} items + */ + Timeline.prototype.setItems = function(items) { + var initialLoad = (this.itemsData == null); - // Get the number of decimal places - var decimals; - if(this.options.format[orientation] !== undefined) { - decimals = this.options.format[orientation].decimals; + // convert to type DataSet when needed + var newDataSet; + if (!items) { + newDataSet = null; + } + else if (items instanceof DataSet || items instanceof DataView) { + newDataSet = items; + } + else { + // turn an array into a dataset + newDataSet = new DataSet(items, { + type: { + start: 'Date', + end: 'Date' + } + }); } - this.maxLabelSize = 0; - var y = 0; - while (max < Math.round(amountOfSteps)) { - step.next(); - y = Math.round(max * stepPixels); - marginStartPos = max * stepPixels; - var isMajor = step.isMajor(); - - if (this.options['showMinorLabels'] && isMajor == false || this.master == false && this.options['showMinorLabels'] == true) { - this._redrawLabel(y - 2, step.getCurrent(decimals), orientation, 'yAxis minor', this.props.minorCharHeight); - } + // set items + this.itemsData = newDataSet; + this.itemSet && this.itemSet.setItems(newDataSet); - if (isMajor && this.options['showMajorLabels'] && this.master == true || - this.options['showMinorLabels'] == false && this.master == false && isMajor == true) { - if (y >= 0) { - this._redrawLabel(y - 2, step.getCurrent(decimals), orientation, 'yAxis major', this.props.majorCharHeight); + if (initialLoad) { + if (this.options.start != undefined || this.options.end != undefined) { + if (this.options.start == undefined || this.options.end == undefined) { + var dataRange = this._getDataRange(); } - this._redrawLine(y, orientation, 'grid horizontal major', this.options.majorLinesOffset, this.props.majorLineWidth); + + var start = this.options.start != undefined ? this.options.start : dataRange.start; + var end = this.options.end != undefined ? this.options.end : dataRange.end; + + this.setWindow(start, end, {animate: false}); } else { - this._redrawLine(y, orientation, 'grid horizontal minor', this.options.minorLinesOffset, this.props.minorLineWidth); - } - - if (this.master == true && step.current == 0) { - this.zeroCrossing = max; + this.fit({animate: false}); } - - max++; - } - - if (this.master == false) { - this.conversionFactor = y / (this.valueAtZero - step.current); - } - else { - this.conversionFactor = this.dom.frame.offsetHeight / step.marginRange; } + }; - // Note that title is rotated, so we're using the height, not width! - var titleWidth = 0; - if (this.options.title[orientation] !== undefined && this.options.title[orientation].text !== undefined) { - titleWidth = this.props.titleCharHeight; - } - var offset = this.options.icons == true ? Math.max(this.options.iconWidth, titleWidth) + this.options.labelOffsetX + 15 : titleWidth + this.options.labelOffsetX + 15; - - // this will resize the yAxis to accommodate the labels. - if (this.maxLabelSize > (this.width - offset) && this.options.visible == true) { - this.width = this.maxLabelSize + offset; - this.options.width = this.width + "px"; - DOMutil.cleanupElements(this.DOMelements.lines); - DOMutil.cleanupElements(this.DOMelements.labels); - this.redraw(); - resized = true; + /** + * Set groups + * @param {vis.DataSet | Array | google.visualization.DataTable} groups + */ + Timeline.prototype.setGroups = function(groups) { + // convert to type DataSet when needed + var newDataSet; + if (!groups) { + newDataSet = null; } - // this will resize the yAxis if it is too big for the labels. - else if (this.maxLabelSize < (this.width - offset) && this.options.visible == true && this.width > this.minWidth) { - this.width = Math.max(this.minWidth,this.maxLabelSize + offset); - this.options.width = this.width + "px"; - DOMutil.cleanupElements(this.DOMelements.lines); - DOMutil.cleanupElements(this.DOMelements.labels); - this.redraw(); - resized = true; + else if (groups instanceof DataSet || groups instanceof DataView) { + newDataSet = groups; } else { - DOMutil.cleanupElements(this.DOMelements.lines); - DOMutil.cleanupElements(this.DOMelements.labels); - resized = false; + // turn an array into a dataset + newDataSet = new DataSet(groups); } - return resized; - }; - - DataAxis.prototype.convertValue = function (value) { - var invertedValue = this.valueAtZero - value; - var convertedValue = invertedValue * this.conversionFactor; - return convertedValue; + this.groupsData = newDataSet; + this.itemSet.setGroups(newDataSet); }; /** - * Create a label for the axis at position x - * @private - * @param y - * @param text - * @param orientation - * @param className - * @param characterHeight + * Set selected items by their id. Replaces the current selection + * Unknown id's are silently ignored. + * @param {string[] | string} [ids] An array with zero or more id's of the items to be + * selected. If ids is an empty array, all items will be + * unselected. + * @param {Object} [options] Available options: + * `focus: boolean` + * If true, focus will be set to the selected item(s) + * `animate: boolean | number` + * If true (default), the range is animated + * smoothly to the new window. + * If a number, the number is taken as duration + * for the animation. Default duration is 500 ms. + * Only applicable when option focus is true. */ - DataAxis.prototype._redrawLabel = function (y, text, orientation, className, characterHeight) { - // reuse redundant label - var label = DOMutil.getDOMElement('div',this.DOMelements.labels, this.dom.frame); //this.dom.redundant.labels.shift(); - label.className = className; - label.innerHTML = text; - if (orientation == 'left') { - label.style.left = '-' + this.options.labelOffsetX + 'px'; - label.style.textAlign = "right"; - } - else { - label.style.right = '-' + this.options.labelOffsetX + 'px'; - label.style.textAlign = "left"; - } - - label.style.top = y - 0.5 * characterHeight + this.options.labelOffsetY + 'px'; - - text += ''; + Timeline.prototype.setSelection = function(ids, options) { + this.itemSet && this.itemSet.setSelection(ids); - var largestWidth = Math.max(this.props.majorCharWidth,this.props.minorCharWidth); - if (this.maxLabelSize < text.length * largestWidth) { - this.maxLabelSize = text.length * largestWidth; + if (options && options.focus) { + this.focus(ids, options); } }; /** - * Create a minor line for the axis at position y - * @param y - * @param orientation - * @param className - * @param offset - * @param width + * Get the selected items by their id + * @return {Array} ids The ids of the selected items */ - DataAxis.prototype._redrawLine = function (y, orientation, className, offset, width) { - if (this.master == true) { - var line = DOMutil.getDOMElement('div',this.DOMelements.lines, this.dom.lineContainer);//this.dom.redundant.lines.shift(); - line.className = className; - line.innerHTML = ''; - - if (orientation == 'left') { - line.style.left = (this.width - offset) + 'px'; - } - else { - line.style.right = (this.width - offset) + 'px'; - } - - line.style.width = width + 'px'; - line.style.top = y + 'px'; - } + Timeline.prototype.getSelection = function() { + return this.itemSet && this.itemSet.getSelection() || []; }; /** - * Create a title for the axis - * @private - * @param orientation + * Adjust the visible window such that the selected item (or multiple items) + * are centered on screen. + * @param {String | String[]} id An item id or array with item ids + * @param {Object} [options] Available options: + * `animate: boolean | number` + * If true (default), the range is animated + * smoothly to the new window. + * If a number, the number is taken as duration + * for the animation. Default duration is 500 ms. + * Only applicable when option focus is true */ - DataAxis.prototype._redrawTitle = function (orientation) { - DOMutil.prepareElements(this.DOMelements.title); + Timeline.prototype.focus = function(id, options) { + if (!this.itemsData || id == undefined) return; - // Check if the title is defined for this axes - if (this.options.title[orientation] !== undefined && this.options.title[orientation].text !== undefined) { - var title = DOMutil.getDOMElement('div', this.DOMelements.title, this.dom.frame); - title.className = 'yAxis title ' + orientation; - title.innerHTML = this.options.title[orientation].text; + var ids = Array.isArray(id) ? id : [id]; - // Add style - if provided - if (this.options.title[orientation].style !== undefined) { - util.addCssText(title, this.options.title[orientation].style); + // get the specified item(s) + var itemsData = this.itemsData.getDataSet().get(ids, { + type: { + start: 'Date', + end: 'Date' } + }); - if (orientation == 'left') { - title.style.left = this.props.titleCharHeight + 'px'; + // calculate minimum start and maximum end of specified items + var start = null; + var end = null; + itemsData.forEach(function (itemData) { + var s = itemData.start.valueOf(); + var e = 'end' in itemData ? itemData.end.valueOf() : itemData.start.valueOf(); + + if (start === null || s < start) { + start = s; } - else { - title.style.right = this.props.titleCharHeight + 'px'; + + if (end === null || e > end) { + end = e; } + }); - title.style.width = this.height + 'px'; - } + if (start !== null && end !== null) { + // calculate the new middle and interval for the window + var middle = (start + end) / 2; + var interval = Math.max((this.range.end - this.range.start), (end - start) * 1.1); - // we need to clean up in case we did not use all elements. - DOMutil.cleanupElements(this.DOMelements.title); + var animate = (options && options.animate !== undefined) ? options.animate : true; + this.range.setRange(middle - interval / 2, middle + interval / 2, animate); + } }; - - - /** - * Determine the size of text on the axis (both major and minor axis). - * The size is calculated only once and then cached in this.props. - * @private + * Get the data range of the item set. + * @returns {{min: Date, max: Date}} range A range with a start and end Date. + * When no minimum is found, min==null + * When no maximum is found, max==null */ - DataAxis.prototype._calculateCharSize = function () { - // determine the char width and height on the minor axis - if (!('minorCharHeight' in this.props)) { - var textMinor = document.createTextNode('0'); - var measureCharMinor = document.createElement('div'); - measureCharMinor.className = 'yAxis minor measure'; - measureCharMinor.appendChild(textMinor); - this.dom.frame.appendChild(measureCharMinor); + Timeline.prototype.getItemRange = function() { + // calculate min from start filed + var dataset = this.itemsData.getDataSet(), + min = null, + max = null; - this.props.minorCharHeight = measureCharMinor.clientHeight; - this.props.minorCharWidth = measureCharMinor.clientWidth; + if (dataset) { + // calculate the minimum value of the field 'start' + var minItem = dataset.min('start'); + min = minItem ? util.convert(minItem.start, 'Date').valueOf() : null; + // Note: we convert first to Date and then to number because else + // a conversion from ISODate to Number will fail - this.dom.frame.removeChild(measureCharMinor); + // calculate maximum value of fields 'start' and 'end' + var maxStartItem = dataset.max('start'); + if (maxStartItem) { + max = util.convert(maxStartItem.start, 'Date').valueOf(); + } + var maxEndItem = dataset.max('end'); + if (maxEndItem) { + if (max == null) { + max = util.convert(maxEndItem.end, 'Date').valueOf(); + } + else { + max = Math.max(max, util.convert(maxEndItem.end, 'Date').valueOf()); + } + } } - if (!('majorCharHeight' in this.props)) { - var textMajor = document.createTextNode('0'); - var measureCharMajor = document.createElement('div'); - measureCharMajor.className = 'yAxis major measure'; - measureCharMajor.appendChild(textMajor); - this.dom.frame.appendChild(measureCharMajor); + return { + min: (min != null) ? new Date(min) : null, + max: (max != null) ? new Date(max) : null + }; + }; - this.props.majorCharHeight = measureCharMajor.clientHeight; - this.props.majorCharWidth = measureCharMajor.clientWidth; - this.dom.frame.removeChild(measureCharMajor); - } + module.exports = Timeline; - if (!('titleCharHeight' in this.props)) { - var textTitle = document.createTextNode('0'); - var measureCharTitle = document.createElement('div'); - measureCharTitle.className = 'yAxis title measure'; - measureCharTitle.appendChild(textTitle); - this.dom.frame.appendChild(measureCharTitle); - this.props.titleCharHeight = measureCharTitle.clientHeight; - this.props.titleCharWidth = measureCharTitle.clientWidth; +/***/ }, +/* 19 */ +/***/ function(module, exports, __webpack_require__) { - this.dom.frame.removeChild(measureCharTitle); + // Only load hammer.js when in a browser environment + // (loading hammer.js in a node.js environment gives errors) + if (typeof window !== 'undefined') { + module.exports = window['Hammer'] || __webpack_require__(20); + } + else { + module.exports = function () { + throw Error('hammer.js is only available in a browser, not in node.js.'); } - }; - - /** - * Snap a date to a rounded value. - * The snap intervals are dependent on the current scale and step. - * @param {Date} date the date to be snapped. - * @return {Date} snappedDate - */ - DataAxis.prototype.snap = function(date) { - return this.step.snap(date); - }; - - module.exports = DataAxis; + } /***/ }, -/* 24 */ +/* 20 */ /***/ function(module, exports, __webpack_require__) { - var util = __webpack_require__(1); - var DOMutil = __webpack_require__(2); - var Line = __webpack_require__(51); - var Bar = __webpack_require__(52); - var Points = __webpack_require__(53); + var __WEBPACK_AMD_DEFINE_RESULT__;/*! Hammer.JS - v1.1.3 - 2014-05-20 + * http://eightmedia.github.io/hammer.js + * + * Copyright (c) 2014 Jorik Tangelder ; + * Licensed under the MIT license */ + + (function(window, undefined) { + 'use strict'; /** - * /** - * @param {object} group | the object of the group from the dataset - * @param {string} groupId | ID of the group - * @param {object} options | the default options - * @param {array} groupsUsingDefaultStyles | this array has one entree. - * It is passed as an array so it is passed by reference. - * It enumerates through the default styles - * @constructor + * @main + * @module hammer + * + * @class Hammer + * @static */ - function GraphGroup (group, groupId, options, groupsUsingDefaultStyles) { - this.id = groupId; - var fields = ['sampling','style','sort','yAxisOrientation','barChart','drawPoints','shaded','catmullRom'] - this.options = util.selectiveBridgeObject(fields,options); - this.usingDefaultStyle = group.className === undefined; - this.groupsUsingDefaultStyles = groupsUsingDefaultStyles; - this.zeroPosition = 0; - this.update(group); - if (this.usingDefaultStyle == true) { - this.groupsUsingDefaultStyles[0] += 1; - } - this.itemsData = []; - this.visible = group.visible === undefined ? true : group.visible; - } - /** - * this loads a reference to all items in this group into this group. - * @param {array} items + * Hammer, use this to create instances + * ```` + * var hammertime = new Hammer(myElement); + * ```` + * + * @method Hammer + * @param {HTMLElement} element + * @param {Object} [options={}] + * @return {Hammer.Instance} */ - GraphGroup.prototype.setItems = function(items) { - if (items != null) { - this.itemsData = items; - if (this.options.sort == true) { - this.itemsData.sort(function (a,b) {return a.x - b.x;}) - } - } - else { - this.itemsData = []; - } + var Hammer = function Hammer(element, options) { + return new Hammer.Instance(element, options || {}); }; - /** - * this is used for plotting barcharts, this way, we only have to calculate it once. - * @param pos + * version, as defined in package.json + * the value will be set at each build + * @property VERSION + * @final + * @type {String} */ - GraphGroup.prototype.setZeroPosition = function(pos) { - this.zeroPosition = pos; - }; - + Hammer.VERSION = '1.1.3'; /** - * set the options of the graph group over the default options. - * @param options + * default settings. + * more settings are defined per gesture at `/gestures`. Each gesture can be disabled/enabled + * by setting it's name (like `swipe`) to false. + * You can set the defaults for all instances by changing this object before creating an instance. + * @example + * ```` + * Hammer.defaults.drag = false; + * Hammer.defaults.behavior.touchAction = 'pan-y'; + * delete Hammer.defaults.behavior.userSelect; + * ```` + * @property defaults + * @type {Object} */ - GraphGroup.prototype.setOptions = function(options) { - if (options !== undefined) { - var fields = ['sampling','style','sort','yAxisOrientation','barChart']; - util.selectiveDeepExtend(fields, this.options, options); + Hammer.defaults = { + /** + * this setting object adds styles and attributes to the element to prevent the browser from doing + * its native behavior. The css properties are auto prefixed for the browsers when needed. + * @property defaults.behavior + * @type {Object} + */ + behavior: { + /** + * Disables text selection to improve the dragging gesture. When the value is `none` it also sets + * `onselectstart=false` for IE on the element. Mainly for desktop browsers. + * @property defaults.behavior.userSelect + * @type {String} + * @default 'none' + */ + userSelect: 'none', - util.mergeOptions(this.options, options,'catmullRom'); - util.mergeOptions(this.options, options,'drawPoints'); - util.mergeOptions(this.options, options,'shaded'); + /** + * Specifies whether and how a given region can be manipulated by the user (for instance, by panning or zooming). + * Used by Chrome 35> and IE10>. By default this makes the element blocking any touch event. + * @property defaults.behavior.touchAction + * @type {String} + * @default: 'pan-y' + */ + touchAction: 'pan-y', - if (options.catmullRom) { - if (typeof options.catmullRom == 'object') { - if (options.catmullRom.parametrization) { - if (options.catmullRom.parametrization == 'uniform') { - this.options.catmullRom.alpha = 0; - } - else if (options.catmullRom.parametrization == 'chordal') { - this.options.catmullRom.alpha = 1.0; - } - else { - this.options.catmullRom.parametrization = 'centripetal'; - this.options.catmullRom.alpha = 0.5; - } - } - } - } - } + /** + * Disables the default callout shown when you touch and hold a touch target. + * On iOS, when you touch and hold a touch target such as a link, Safari displays + * a callout containing information about the link. This property allows you to disable that callout. + * @property defaults.behavior.touchCallout + * @type {String} + * @default 'none' + */ + touchCallout: 'none', - if (this.options.style == 'line') { - this.type = new Line(this.id, this.options); - } - else if (this.options.style == 'bar') { - this.type = new Bar(this.id, this.options); - } - else if (this.options.style == 'points') { - this.type = new Points(this.id, this.options); - } - }; + /** + * Specifies whether zooming is enabled. Used by IE10> + * @property defaults.behavior.contentZooming + * @type {String} + * @default 'none' + */ + contentZooming: 'none', + /** + * Specifies that an entire element should be draggable instead of its contents. + * Mainly for desktop browsers. + * @property defaults.behavior.userDrag + * @type {String} + * @default 'none' + */ + userDrag: 'none', - /** - * this updates the current group class with the latest group dataset entree, used in _updateGroup in linegraph - * @param group - */ - GraphGroup.prototype.update = function(group) { - this.group = group; - this.content = group.content || 'graph'; - this.className = group.className || this.className || "graphGroup" + this.groupsUsingDefaultStyles[0] % 10; - this.visible = group.visible === undefined ? true : group.visible; - this.style = group.style; - this.setOptions(group.options); + /** + * Overrides the highlight color shown when the user taps a link or a JavaScript + * clickable element in Safari on iPhone. This property obeys the alpha value, if specified. + * + * If you don't specify an alpha value, Safari on iPhone applies a default alpha value + * to the color. To disable tap highlighting, set the alpha value to 0 (invisible). + * If you set the alpha value to 1.0 (opaque), the element is not visible when tapped. + * @property defaults.behavior.tapHighlightColor + * @type {String} + * @default 'rgba(0,0,0,0)' + */ + tapHighlightColor: 'rgba(0,0,0,0)' + } }; - /** - * draw the icon for the legend. - * - * @param x - * @param y - * @param JSONcontainer - * @param SVGcontainer - * @param iconWidth - * @param iconHeight + * hammer document where the base events are added at + * @property DOCUMENT + * @type {HTMLElement} + * @default window.document */ - GraphGroup.prototype.drawIcon = function(x, y, JSONcontainer, SVGcontainer, iconWidth, iconHeight) { - var fillHeight = iconHeight * 0.5; - var path, fillPath; - - var outline = DOMutil.getSVGElement("rect", JSONcontainer, SVGcontainer); - outline.setAttributeNS(null, "x", x); - outline.setAttributeNS(null, "y", y - fillHeight); - outline.setAttributeNS(null, "width", iconWidth); - outline.setAttributeNS(null, "height", 2*fillHeight); - outline.setAttributeNS(null, "class", "outline"); - - if (this.options.style == 'line') { - path = DOMutil.getSVGElement("path", JSONcontainer, SVGcontainer); - path.setAttributeNS(null, "class", this.className); - if(this.style !== undefined) { - path.setAttributeNS(null, "style", this.style); - } - - path.setAttributeNS(null, "d", "M" + x + ","+y+" L" + (x + iconWidth) + ","+y+""); - if (this.options.shaded.enabled == true) { - fillPath = DOMutil.getSVGElement("path", JSONcontainer, SVGcontainer); - if (this.options.shaded.orientation == 'top') { - fillPath.setAttributeNS(null, "d", "M"+x+", " + (y - fillHeight) + - "L"+x+","+y+" L"+ (x + iconWidth) + ","+y+" L"+ (x + iconWidth) + "," + (y - fillHeight)); - } - else { - fillPath.setAttributeNS(null, "d", "M"+x+","+y+" " + - "L"+x+"," + (y + fillHeight) + " " + - "L"+ (x + iconWidth) + "," + (y + fillHeight) + - "L"+ (x + iconWidth) + ","+y); - } - fillPath.setAttributeNS(null, "class", this.className + " iconFill"); - } - - if (this.options.drawPoints.enabled == true) { - DOMutil.drawPoint(x + 0.5 * iconWidth,y, this, JSONcontainer, SVGcontainer); - } - } - else { - var barWidth = Math.round(0.3 * iconWidth); - var bar1Height = Math.round(0.4 * iconHeight); - var bar2Height = Math.round(0.75 * iconHeight); - - var offset = Math.round((iconWidth - (2 * barWidth))/3); - - DOMutil.drawBar(x + 0.5*barWidth + offset , y + fillHeight - bar1Height - 1, barWidth, bar1Height, this.className + ' bar', JSONcontainer, SVGcontainer); - DOMutil.drawBar(x + 1.5*barWidth + offset + 2, y + fillHeight - bar2Height - 1, barWidth, bar2Height, this.className + ' bar', JSONcontainer, SVGcontainer); - } - }; - + Hammer.DOCUMENT = document; /** - * return the legend entree for this group. - * - * @param iconWidth - * @param iconHeight - * @returns {{icon: HTMLElement, label: (group.content|*|string), orientation: (.options.yAxisOrientation|*)}} + * detect support for pointer events + * @property HAS_POINTEREVENTS + * @type {Boolean} */ - GraphGroup.prototype.getLegend = function(iconWidth, iconHeight) { - var svg = document.createElementNS('http://www.w3.org/2000/svg',"svg"); - this.drawIcon(0,0.5*iconHeight,[],svg,iconWidth,iconHeight); - return {icon: svg, label: this.content, orientation:this.options.yAxisOrientation}; - } - - GraphGroup.prototype.getYRange = function(groupData) { - return this.type.getYRange(groupData); - } - - GraphGroup.prototype.draw = function(dataset, group, framework) { - this.type.draw(dataset, group, framework); - } - - - module.exports = GraphGroup; - - -/***/ }, -/* 25 */ -/***/ function(module, exports, __webpack_require__) { - - var util = __webpack_require__(1); - var stack = __webpack_require__(18); - var RangeItem = __webpack_require__(35); + Hammer.HAS_POINTEREVENTS = navigator.pointerEnabled || navigator.msPointerEnabled; /** - * @constructor Group - * @param {Number | String} groupId - * @param {Object} data - * @param {ItemSet} itemSet + * detect support for touch events + * @property HAS_TOUCHEVENTS + * @type {Boolean} */ - function Group (groupId, data, itemSet) { - this.groupId = groupId; - this.subgroups = {}; - this.subgroupIndex = 0; - this.subgroupOrderer = data && data.subgroupOrder; - this.itemSet = itemSet; - - this.dom = {}; - this.props = { - label: { - width: 0, - height: 0 - } - }; - this.className = null; + Hammer.HAS_TOUCHEVENTS = ('ontouchstart' in window); - this.items = {}; // items filtered by groupId of this group - this.visibleItems = []; // items currently visible in window - this.orderedItems = { - byStart: [], - byEnd: [] - }; - this.checkRangedItems = false; // needed to refresh the ranged items if the window is programatically changed with NO overlap. - var me = this; - this.itemSet.body.emitter.on("checkRangedItems", function () { - me.checkRangedItems = true; - }) + /** + * detect mobile browsers + * @property IS_MOBILE + * @type {Boolean} + */ + Hammer.IS_MOBILE = /mobile|tablet|ip(ad|hone|od)|android|silk/i.test(navigator.userAgent); - this._create(); + /** + * detect if we want to support mouseevents at all + * @property NO_MOUSEEVENTS + * @type {Boolean} + */ + Hammer.NO_MOUSEEVENTS = (Hammer.HAS_TOUCHEVENTS && Hammer.IS_MOBILE) || Hammer.HAS_POINTEREVENTS; - this.setData(data); - } + /** + * interval in which Hammer recalculates current velocity/direction/angle in ms + * @property CALCULATE_INTERVAL + * @type {Number} + * @default 25 + */ + Hammer.CALCULATE_INTERVAL = 25; /** - * Create DOM elements for the group + * eventtypes per touchevent (start, move, end) are filled by `Event.determineEventTypes` on `setup` + * the object contains the DOM event names per type (`EVENT_START`, `EVENT_MOVE`, `EVENT_END`) + * @property EVENT_TYPES * @private + * @writeOnce + * @type {Object} */ - Group.prototype._create = function() { - var label = document.createElement('div'); - label.className = 'vlabel'; - this.dom.label = label; - - var inner = document.createElement('div'); - inner.className = 'inner'; - label.appendChild(inner); - this.dom.inner = inner; - - var foreground = document.createElement('div'); - foreground.className = 'group'; - foreground['timeline-group'] = this; - this.dom.foreground = foreground; - - this.dom.background = document.createElement('div'); - this.dom.background.className = 'group'; - - this.dom.axis = document.createElement('div'); - this.dom.axis.className = 'group'; - - // create a hidden marker to detect when the Timelines container is attached - // to the DOM, or the style of a parent of the Timeline is changed from - // display:none is changed to visible. - this.dom.marker = document.createElement('div'); - this.dom.marker.style.visibility = 'hidden'; // TODO: ask jos why this is not none? - this.dom.marker.innerHTML = '?'; - this.dom.background.appendChild(this.dom.marker); - }; + var EVENT_TYPES = {}; /** - * Set the group data for this group - * @param {Object} data Group data, can contain properties content and className + * direction strings, for safe comparisons + * @property DIRECTION_DOWN|LEFT|UP|RIGHT + * @final + * @type {String} + * @default 'down' 'left' 'up' 'right' */ - Group.prototype.setData = function(data) { - // update contents - var content = data && data.content; - if (content instanceof Element) { - this.dom.inner.appendChild(content); - } - else if (content !== undefined && content !== null) { - this.dom.inner.innerHTML = content; - } - else { - this.dom.inner.innerHTML = this.groupId || ''; // groupId can be null - } - - // update title - this.dom.label.title = data && data.title || ''; - - if (!this.dom.inner.firstChild) { - util.addClassName(this.dom.inner, 'hidden'); - } - else { - util.removeClassName(this.dom.inner, 'hidden'); - } - - // update className - var className = data && data.className || null; - if (className != this.className) { - if (this.className) { - util.removeClassName(this.dom.label, this.className); - util.removeClassName(this.dom.foreground, this.className); - util.removeClassName(this.dom.background, this.className); - util.removeClassName(this.dom.axis, this.className); - } - util.addClassName(this.dom.label, className); - util.addClassName(this.dom.foreground, className); - util.addClassName(this.dom.background, className); - util.addClassName(this.dom.axis, className); - this.className = className; - } - - // update style - if (this.style) { - util.removeCssText(this.dom.label, this.style); - this.style = null; - } - if (data && data.style) { - util.addCssText(this.dom.label, data.style); - this.style = data.style; - } - }; + var DIRECTION_DOWN = Hammer.DIRECTION_DOWN = 'down'; + var DIRECTION_LEFT = Hammer.DIRECTION_LEFT = 'left'; + var DIRECTION_UP = Hammer.DIRECTION_UP = 'up'; + var DIRECTION_RIGHT = Hammer.DIRECTION_RIGHT = 'right'; /** - * Get the width of the group label - * @return {number} width + * pointertype strings, for safe comparisons + * @property POINTER_MOUSE|TOUCH|PEN + * @final + * @type {String} + * @default 'mouse' 'touch' 'pen' */ - Group.prototype.getLabelWidth = function() { - return this.props.label.width; - }; - + var POINTER_MOUSE = Hammer.POINTER_MOUSE = 'mouse'; + var POINTER_TOUCH = Hammer.POINTER_TOUCH = 'touch'; + var POINTER_PEN = Hammer.POINTER_PEN = 'pen'; /** - * Repaint this group - * @param {{start: number, end: number}} range - * @param {{item: {horizontal: number, vertical: number}, axis: number}} margin - * @param {boolean} [restack=false] Force restacking of all items - * @return {boolean} Returns true if the group is resized + * eventtypes + * @property EVENT_START|MOVE|END|RELEASE|TOUCH + * @final + * @type {String} + * @default 'start' 'change' 'move' 'end' 'release' 'touch' */ - Group.prototype.redraw = function(range, margin, restack) { - var resized = false; - - this.visibleItems = this._updateVisibleItems(this.orderedItems, this.visibleItems, range); - - // force recalculation of the height of the items when the marker height changed - // (due to the Timeline being attached to the DOM or changed from display:none to visible) - var markerHeight = this.dom.marker.clientHeight; - if (markerHeight != this.lastMarkerHeight) { - this.lastMarkerHeight = markerHeight; - - util.forEach(this.items, function (item) { - item.dirty = true; - if (item.displayed) item.redraw(); - }); - - restack = true; - } - - // reposition visible items vertically - if (this.itemSet.options.stack) { // TODO: ugly way to access options... - stack.stack(this.visibleItems, margin, restack); - } - else { // no stacking - stack.nostack(this.visibleItems, margin, this.subgroups); - } - - // recalculate the height of the group - var height = this._calculateHeight(margin); - - // calculate actual size and position - var foreground = this.dom.foreground; - this.top = foreground.offsetTop; - this.left = foreground.offsetLeft; - this.width = foreground.offsetWidth; - resized = util.updateProperty(this, 'height', height) || resized; - - // recalculate size of label - resized = util.updateProperty(this.props.label, 'width', this.dom.inner.clientWidth) || resized; - resized = util.updateProperty(this.props.label, 'height', this.dom.inner.clientHeight) || resized; - - // apply new height - this.dom.background.style.height = height + 'px'; - this.dom.foreground.style.height = height + 'px'; - this.dom.label.style.height = height + 'px'; - - // update vertical position of items after they are re-stacked and the height of the group is calculated - for (var i = 0, ii = this.visibleItems.length; i < ii; i++) { - var item = this.visibleItems[i]; - item.repositionY(margin); - } - - return resized; - }; + var EVENT_START = Hammer.EVENT_START = 'start'; + var EVENT_MOVE = Hammer.EVENT_MOVE = 'move'; + var EVENT_END = Hammer.EVENT_END = 'end'; + var EVENT_RELEASE = Hammer.EVENT_RELEASE = 'release'; + var EVENT_TOUCH = Hammer.EVENT_TOUCH = 'touch'; /** - * recalculate the height of the group - * @param {{item: {horizontal: number, vertical: number}, axis: number}} margin - * @returns {number} Returns the height - * @private + * if the window events are set... + * @property READY + * @writeOnce + * @type {Boolean} + * @default false */ - Group.prototype._calculateHeight = function (margin) { - // recalculate the height of the group - var height; - var visibleItems = this.visibleItems; - //var visibleSubgroups = []; - //this.visibleSubgroups = 0; - this.resetSubgroups(); - var me = this; - if (visibleItems.length) { - var min = visibleItems[0].top; - var max = visibleItems[0].top + visibleItems[0].height; - util.forEach(visibleItems, function (item) { - min = Math.min(min, item.top); - max = Math.max(max, (item.top + item.height)); - if (item.data.subgroup !== undefined) { - me.subgroups[item.data.subgroup].height = Math.max(me.subgroups[item.data.subgroup].height,item.height); - me.subgroups[item.data.subgroup].visible = true; - //if (visibleSubgroups.indexOf(item.data.subgroup) == -1){ - // visibleSubgroups.push(item.data.subgroup); - // me.visibleSubgroups += 1; - //} - } - }); - if (min > margin.axis) { - // there is an empty gap between the lowest item and the axis - var offset = min - margin.axis; - max -= offset; - util.forEach(visibleItems, function (item) { - item.top -= offset; - }); - } - height = max + margin.item.vertical / 2; - } - else { - height = margin.axis + margin.item.vertical; - } - height = Math.max(height, this.props.label.height); - - return height; - }; + Hammer.READY = false; /** - * Show this group: attach to the DOM + * plugins namespace + * @property plugins + * @type {Object} */ - Group.prototype.show = function() { - if (!this.dom.label.parentNode) { - this.itemSet.dom.labelSet.appendChild(this.dom.label); - } - - if (!this.dom.foreground.parentNode) { - this.itemSet.dom.foreground.appendChild(this.dom.foreground); - } - - if (!this.dom.background.parentNode) { - this.itemSet.dom.background.appendChild(this.dom.background); - } - - if (!this.dom.axis.parentNode) { - this.itemSet.dom.axis.appendChild(this.dom.axis); - } - }; + Hammer.plugins = Hammer.plugins || {}; /** - * Hide this group: remove from the DOM + * gestures namespace + * see `/gestures` for the definitions + * @property gestures + * @type {Object} */ - Group.prototype.hide = function() { - var label = this.dom.label; - if (label.parentNode) { - label.parentNode.removeChild(label); - } - - var foreground = this.dom.foreground; - if (foreground.parentNode) { - foreground.parentNode.removeChild(foreground); - } - - var background = this.dom.background; - if (background.parentNode) { - background.parentNode.removeChild(background); - } - - var axis = this.dom.axis; - if (axis.parentNode) { - axis.parentNode.removeChild(axis); - } - }; + Hammer.gestures = Hammer.gestures || {}; /** - * Add an item to the group - * @param {Item} item + * setup events to detect gestures on the document + * this function is called when creating an new instance + * @private */ - Group.prototype.add = function(item) { - this.items[item.id] = item; - item.setParent(this); - - // add to - if (item.data.subgroup !== undefined) { - if (this.subgroups[item.data.subgroup] === undefined) { - this.subgroups[item.data.subgroup] = {height:0, visible: false, index:this.subgroupIndex, items: []}; - this.subgroupIndex++; + function setup() { + if(Hammer.READY) { + return; } - this.subgroups[item.data.subgroup].items.push(item); - } - this.orderSubgroups(); - if (this.visibleItems.indexOf(item) == -1) { - var range = this.itemSet.body.range; // TODO: not nice accessing the range like this - this._checkIfVisible(item, this.visibleItems, range); - } - }; + // find what eventtypes we add listeners to + Event.determineEventTypes(); - Group.prototype.orderSubgroups = function() { - if (this.subgroupOrderer !== undefined) { - var sortArray = []; - if (typeof this.subgroupOrderer == 'string') { - for (var subgroup in this.subgroups) { - sortArray.push({subgroup: subgroup, sortField: this.subgroups[subgroup].items[0].data[this.subgroupOrderer]}) - } - sortArray.sort(function (a, b) { - return a.sortField - b.sortField; - }) - } - else if (typeof this.subgroupOrderer == 'function') { - for (var subgroup in this.subgroups) { - sortArray.push(this.subgroups[subgroup].items[0].data); - } - sortArray.sort(this.subgroupOrderer); - } + // Register all gestures inside Hammer.gestures + Utils.each(Hammer.gestures, function(gesture) { + Detection.register(gesture); + }); - if (sortArray.length > 0) { - for (var i = 0; i < sortArray.length; i++) { - this.subgroups[sortArray[i].subgroup].index = i; - } - } - } - }; + // Add touch events on the document + Event.onTouch(Hammer.DOCUMENT, EVENT_MOVE, Detection.detect); + Event.onTouch(Hammer.DOCUMENT, EVENT_END, Detection.detect); - Group.prototype.resetSubgroups = function() { - for (var subgroup in this.subgroups) { - if (this.subgroups.hasOwnProperty(subgroup)) { - this.subgroups[subgroup].visible = false; - } - } - }; + // Hammer is ready...! + Hammer.READY = true; + } /** - * Remove an item from the group - * @param {Item} item + * @module hammer + * + * @class Utils + * @static */ - Group.prototype.remove = function(item) { - delete this.items[item.id]; - item.setParent(null); - - // remove from visible items - var index = this.visibleItems.indexOf(item); - if (index != -1) this.visibleItems.splice(index, 1); - - // TODO: also remove from ordered items? - }; + var Utils = Hammer.utils = { + /** + * extend method, could also be used for cloning when `dest` is an empty object. + * changes the dest object + * @method extend + * @param {Object} dest + * @param {Object} src + * @param {Boolean} [merge=false] do a merge + * @return {Object} dest + */ + extend: function extend(dest, src, merge) { + for(var key in src) { + if(!src.hasOwnProperty(key) || (dest[key] !== undefined && merge)) { + continue; + } + dest[key] = src[key]; + } + return dest; + }, + /** + * simple addEventListener wrapper + * @method on + * @param {HTMLElement} element + * @param {String} type + * @param {Function} handler + */ + on: function on(element, type, handler) { + element.addEventListener(type, handler, false); + }, - /** - * Remove an item from the corresponding DataSet - * @param {Item} item - */ - Group.prototype.removeFromDataSet = function(item) { - this.itemSet.removeItem(item.id); - }; + /** + * simple removeEventListener wrapper + * @method off + * @param {HTMLElement} element + * @param {String} type + * @param {Function} handler + */ + off: function off(element, type, handler) { + element.removeEventListener(type, handler, false); + }, + /** + * forEach over arrays and objects + * @method each + * @param {Object|Array} obj + * @param {Function} iterator + * @param {any} iterator.item + * @param {Number} iterator.index + * @param {Object|Array} iterator.obj the source object + * @param {Object} context value to use as `this` in the iterator + */ + each: function each(obj, iterator, context) { + var i, len; - /** - * Reorder the items - */ - Group.prototype.order = function() { - var array = util.toArray(this.items); - var startArray = []; - var endArray = []; + // native forEach on arrays + if('forEach' in obj) { + obj.forEach(iterator, context); + // arrays + } else if(obj.length !== undefined) { + for(i = 0, len = obj.length; i < len; i++) { + if(iterator.call(context, obj[i], i, obj) === false) { + return; + } + } + // objects + } else { + for(i in obj) { + if(obj.hasOwnProperty(i) && + iterator.call(context, obj[i], i, obj) === false) { + return; + } + } + } + }, - for (var i = 0; i < array.length; i++) { - if (array[i].data.end !== undefined) { - endArray.push(array[i]); - } - startArray.push(array[i]); - } - this.orderedItems = { - byStart: startArray, - byEnd: endArray - }; + /** + * find if a string contains the string using indexOf + * @method inStr + * @param {String} src + * @param {String} find + * @return {Boolean} found + */ + inStr: function inStr(src, find) { + return src.indexOf(find) > -1; + }, - stack.orderByStart(this.orderedItems.byStart); - stack.orderByEnd(this.orderedItems.byEnd); - }; + /** + * find if a array contains the object using indexOf or a simple polyfill + * @method inArray + * @param {String} src + * @param {String} find + * @return {Boolean|Number} false when not found, or the index + */ + inArray: function inArray(src, find) { + if(src.indexOf) { + var index = src.indexOf(find); + return (index === -1) ? false : index; + } else { + for(var i = 0, len = src.length; i < len; i++) { + if(src[i] === find) { + return i; + } + } + return false; + } + }, + /** + * convert an array-like object (`arguments`, `touchlist`) to an array + * @method toArray + * @param {Object} obj + * @return {Array} + */ + toArray: function toArray(obj) { + return Array.prototype.slice.call(obj, 0); + }, - /** - * Update the visible items - * @param {{byStart: Item[], byEnd: Item[]}} orderedItems All items ordered by start date and by end date - * @param {Item[]} visibleItems The previously visible items. - * @param {{start: number, end: number}} range Visible range - * @return {Item[]} visibleItems The new visible items. - * @private - */ - Group.prototype._updateVisibleItems = function(orderedItems, oldVisibleItems, range) { - var visibleItems = []; - var visibleItemsLookup = {}; // we keep this to quickly look up if an item already exists in the list without using indexOf on visibleItems - var interval = (range.end - range.start) / 4; - var lowerBound = range.start - interval; - var upperBound = range.end + interval; - var item, i; + /** + * find if a node is in the given parent + * @method hasParent + * @param {HTMLElement} node + * @param {HTMLElement} parent + * @return {Boolean} found + */ + hasParent: function hasParent(node, parent) { + while(node) { + if(node == parent) { + return true; + } + node = node.parentNode; + } + return false; + }, - // this function is used to do the binary search. - var searchFunction = function (value) { - if (value < lowerBound) {return -1;} - else if (value <= upperBound) {return 0;} - else {return 1;} - } + /** + * get the center of all the touches + * @method getCenter + * @param {Array} touches + * @return {Object} center contains `pageX`, `pageY`, `clientX` and `clientY` properties + */ + getCenter: function getCenter(touches) { + var pageX = [], + pageY = [], + clientX = [], + clientY = [], + min = Math.min, + max = Math.max; - // first check if the items that were in view previously are still in view. - // IMPORTANT: this handles the case for the items with startdate before the window and enddate after the window! - // also cleans up invisible items. - if (oldVisibleItems.length > 0) { - for (i = 0; i < oldVisibleItems.length; i++) { - this._checkIfVisibleWithReference(oldVisibleItems[i], visibleItems, visibleItemsLookup, range); - } - } + // no need to loop when only one touch + if(touches.length === 1) { + return { + pageX: touches[0].pageX, + pageY: touches[0].pageY, + clientX: touches[0].clientX, + clientY: touches[0].clientY + }; + } - // we do a binary search for the items that have only start values. - var initialPosByStart = util.binarySearchCustom(orderedItems.byStart, searchFunction, 'data','start'); + Utils.each(touches, function(touch) { + pageX.push(touch.pageX); + pageY.push(touch.pageY); + clientX.push(touch.clientX); + clientY.push(touch.clientY); + }); - // trace the visible items from the inital start pos both ways until an invisible item is found, we only look at the start values. - this._traceVisible(initialPosByStart, orderedItems.byStart, visibleItems, visibleItemsLookup, function (item) { - return (item.data.start < lowerBound || item.data.start > upperBound); - }); + return { + pageX: (min.apply(Math, pageX) + max.apply(Math, pageX)) / 2, + pageY: (min.apply(Math, pageY) + max.apply(Math, pageY)) / 2, + clientX: (min.apply(Math, clientX) + max.apply(Math, clientX)) / 2, + clientY: (min.apply(Math, clientY) + max.apply(Math, clientY)) / 2 + }; + }, - // if the window has changed programmatically without overlapping the old window, the ranged items with start < lowerBound and end > upperbound are not shown. - // We therefore have to brute force check all items in the byEnd list - if (this.checkRangedItems == true) { - this.checkRangedItems = false; - for (i = 0; i < orderedItems.byEnd.length; i++) { - this._checkIfVisibleWithReference(orderedItems.byEnd[i], visibleItems, visibleItemsLookup, range); - } - } - else { - // we do a binary search for the items that have defined end times. - var initialPosByEnd = util.binarySearchCustom(orderedItems.byEnd, searchFunction, 'data','end'); + /** + * calculate the velocity between two points. unit is in px per ms. + * @method getVelocity + * @param {Number} deltaTime + * @param {Number} deltaX + * @param {Number} deltaY + * @return {Object} velocity `x` and `y` + */ + getVelocity: function getVelocity(deltaTime, deltaX, deltaY) { + return { + x: Math.abs(deltaX / deltaTime) || 0, + y: Math.abs(deltaY / deltaTime) || 0 + }; + }, - // trace the visible items from the inital start pos both ways until an invisible item is found, we only look at the end values. - this._traceVisible(initialPosByEnd, orderedItems.byEnd, visibleItems, visibleItemsLookup, function (item) { - return (item.data.end < lowerBound || item.data.end > upperBound); - }); - } + /** + * calculate the angle between two coordinates + * @method getAngle + * @param {Touch} touch1 + * @param {Touch} touch2 + * @return {Number} angle + */ + getAngle: function getAngle(touch1, touch2) { + var x = touch2.clientX - touch1.clientX, + y = touch2.clientY - touch1.clientY; + return Math.atan2(y, x) * 180 / Math.PI; + }, - // finally, we reposition all the visible items. - for (i = 0; i < visibleItems.length; i++) { - item = visibleItems[i]; - if (!item.displayed) item.show(); - // reposition item horizontally - item.repositionX(); - } + /** + * do a small comparision to get the direction between two touches. + * @method getDirection + * @param {Touch} touch1 + * @param {Touch} touch2 + * @return {String} direction matches `DIRECTION_LEFT|RIGHT|UP|DOWN` + */ + getDirection: function getDirection(touch1, touch2) { + var x = Math.abs(touch1.clientX - touch2.clientX), + y = Math.abs(touch1.clientY - touch2.clientY); - // debug - //console.log("new line") - //if (this.groupId == null) { - // for (i = 0; i < orderedItems.byStart.length; i++) { - // item = orderedItems.byStart[i].data; - // console.log('start',i,initialPosByStart, item.start.valueOf(), item.content, item.start >= lowerBound && item.start <= upperBound,i == initialPosByStart ? "<------------------- HEREEEE" : "") - // } - // for (i = 0; i < orderedItems.byEnd.length; i++) { - // item = orderedItems.byEnd[i].data; - // console.log('rangeEnd',i,initialPosByEnd, item.end.valueOf(), item.content, item.end >= range.start && item.end <= range.end,i == initialPosByEnd ? "<------------------- HEREEEE" : "") - // } - //} + if(x >= y) { + return touch1.clientX - touch2.clientX > 0 ? DIRECTION_LEFT : DIRECTION_RIGHT; + } + return touch1.clientY - touch2.clientY > 0 ? DIRECTION_UP : DIRECTION_DOWN; + }, - return visibleItems; - }; + /** + * calculate the distance between two touches + * @method getDistance + * @param {Touch}touch1 + * @param {Touch} touch2 + * @return {Number} distance + */ + getDistance: function getDistance(touch1, touch2) { + var x = touch2.clientX - touch1.clientX, + y = touch2.clientY - touch1.clientY; - Group.prototype._traceVisible = function (initialPos, items, visibleItems, visibleItemsLookup, breakCondition) { - var item; - var i; + return Math.sqrt((x * x) + (y * y)); + }, - if (initialPos != -1) { - for (i = initialPos; i >= 0; i--) { - item = items[i]; - if (breakCondition(item)) { - break; - } - else { - if (visibleItemsLookup[item.id] === undefined) { - visibleItemsLookup[item.id] = true; - visibleItems.push(item); + /** + * calculate the scale factor between two touchLists + * no scale is 1, and goes down to 0 when pinched together, and bigger when pinched out + * @method getScale + * @param {Array} start array of touches + * @param {Array} end array of touches + * @return {Number} scale + */ + getScale: function getScale(start, end) { + // need two fingers... + if(start.length >= 2 && end.length >= 2) { + return this.getDistance(end[0], end[1]) / this.getDistance(start[0], start[1]); } - } - } + return 1; + }, - for (i = initialPos + 1; i < items.length; i++) { - item = items[i]; - if (breakCondition(item)) { - break; - } - else { - if (visibleItemsLookup[item.id] === undefined) { - visibleItemsLookup[item.id] = true; - visibleItems.push(item); + /** + * calculate the rotation degrees between two touchLists + * @method getRotation + * @param {Array} start array of touches + * @param {Array} end array of touches + * @return {Number} rotation + */ + getRotation: function getRotation(start, end) { + // need two fingers + if(start.length >= 2 && end.length >= 2) { + return this.getAngle(end[1], end[0]) - this.getAngle(start[1], start[0]); } - } - } - } - } + return 0; + }, + /** + * find out if the direction is vertical * + * @method isVertical + * @param {String} direction matches `DIRECTION_UP|DOWN` + * @return {Boolean} is_vertical + */ + isVertical: function isVertical(direction) { + return direction == DIRECTION_UP || direction == DIRECTION_DOWN; + }, - /** - * this function is very similar to the _checkIfInvisible() but it does not - * return booleans, hides the item if it should not be seen and always adds to - * the visibleItems. - * this one is for brute forcing and hiding. - * - * @param {Item} item - * @param {Array} visibleItems - * @param {{start:number, end:number}} range - * @private - */ - Group.prototype._checkIfVisible = function(item, visibleItems, range) { - if (item.isVisible(range)) { - if (!item.displayed) item.show(); - // reposition item horizontally - item.repositionX(); - visibleItems.push(item); - } - else { - if (item.displayed) item.hide(); - } - }; + /** + * set css properties with their prefixes + * @param {HTMLElement} element + * @param {String} prop + * @param {String} value + * @param {Boolean} [toggle=true] + * @return {Boolean} + */ + setPrefixedCss: function setPrefixedCss(element, prop, value, toggle) { + var prefixes = ['', 'Webkit', 'Moz', 'O', 'ms']; + prop = Utils.toCamelCase(prop); + for(var i = 0; i < prefixes.length; i++) { + var p = prop; + // prefixes + if(prefixes[i]) { + p = prefixes[i] + p.slice(0, 1).toUpperCase() + p.slice(1); + } - /** - * this function is very similar to the _checkIfInvisible() but it does not - * return booleans, hides the item if it should not be seen and always adds to - * the visibleItems. - * this one is for brute forcing and hiding. - * - * @param {Item} item - * @param {Array} visibleItems - * @param {{start:number, end:number}} range - * @private - */ - Group.prototype._checkIfVisibleWithReference = function(item, visibleItems, visibleItemsLookup, range) { - if (item.isVisible(range)) { - if (visibleItemsLookup[item.id] === undefined) { - visibleItemsLookup[item.id] = true; - visibleItems.push(item); - } - } - else { - if (item.displayed) item.hide(); - } - }; + // test the style + if(p in element.style) { + element.style[p] = (toggle == null || toggle) && value || ''; + break; + } + } + }, + /** + * toggle browser default behavior by setting css properties. + * `userSelect='none'` also sets `element.onselectstart` to false + * `userDrag='none'` also sets `element.ondragstart` to false + * + * @method toggleBehavior + * @param {HtmlElement} element + * @param {Object} props + * @param {Boolean} [toggle=true] + */ + toggleBehavior: function toggleBehavior(element, props, toggle) { + if(!props || !element || !element.style) { + return; + } + // set the css properties + Utils.each(props, function(value, prop) { + Utils.setPrefixedCss(element, prop, value, toggle); + }); - module.exports = Group; + var falseFn = toggle && function() { + return false; + }; + // also the disable onselectstart + if(props.userSelect == 'none') { + element.onselectstart = falseFn; + } + // and disable ondragstart + if(props.userDrag == 'none') { + element.ondragstart = falseFn; + } + }, -/***/ }, -/* 26 */ -/***/ function(module, exports, __webpack_require__) { + /** + * convert a string with underscores to camelCase + * so prevent_default becomes preventDefault + * @param {String} str + * @return {String} camelCaseStr + */ + toCamelCase: function toCamelCase(str) { + return str.replace(/[_-]([a-z])/g, function(s) { + return s[1].toUpperCase(); + }); + } + }; - var util = __webpack_require__(1); - var Group = __webpack_require__(25); /** - * @constructor BackgroundGroup - * @param {Number | String} groupId - * @param {Object} data - * @param {ItemSet} itemSet + * @module hammer */ - function BackgroundGroup (groupId, data, itemSet) { - Group.call(this, groupId, data, itemSet); - - this.width = 0; - this.height = 0; - this.top = 0; - this.left = 0; - } - - BackgroundGroup.prototype = Object.create(Group.prototype); - /** - * Repaint this group - * @param {{start: number, end: number}} range - * @param {{item: {horizontal: number, vertical: number}, axis: number}} margin - * @param {boolean} [restack=false] Force restacking of all items - * @return {boolean} Returns true if the group is resized + * @class Event + * @static */ - BackgroundGroup.prototype.redraw = function(range, margin, restack) { - var resized = false; - - this.visibleItems = this._updateVisibleItems(this.orderedItems, this.visibleItems, range); - - // calculate actual size - this.width = this.dom.background.offsetWidth; - - // apply new height (just always zero for BackgroundGroup - this.dom.background.style.height = '0'; - - // update vertical position of items after they are re-stacked and the height of the group is calculated - for (var i = 0, ii = this.visibleItems.length; i < ii; i++) { - var item = this.visibleItems[i]; - item.repositionY(margin); - } + var Event = Hammer.event = { + /** + * when touch events have been fired, this is true + * this is used to stop mouse events + * @property prevent_mouseevents + * @private + * @type {Boolean} + */ + preventMouseEvents: false, - return resized; - }; + /** + * if EVENT_START has been fired + * @property started + * @private + * @type {Boolean} + */ + started: false, - /** - * Show this group: attach to the DOM - */ - BackgroundGroup.prototype.show = function() { - if (!this.dom.background.parentNode) { - this.itemSet.dom.background.appendChild(this.dom.background); - } - }; + /** + * when the mouse is hold down, this is true + * @property should_detect + * @private + * @type {Boolean} + */ + shouldDetect: false, - module.exports = BackgroundGroup; + /** + * simple event binder with a hook and support for multiple types + * @method on + * @param {HTMLElement} element + * @param {String} type + * @param {Function} handler + * @param {Function} [hook] + * @param {Object} hook.type + */ + on: function on(element, type, handler, hook) { + var types = type.split(' '); + Utils.each(types, function(type) { + Utils.on(element, type, handler); + hook && hook(type); + }); + }, + /** + * simple event unbinder with a hook and support for multiple types + * @method off + * @param {HTMLElement} element + * @param {String} type + * @param {Function} handler + * @param {Function} [hook] + * @param {Object} hook.type + */ + off: function off(element, type, handler, hook) { + var types = type.split(' '); + Utils.each(types, function(type) { + Utils.off(element, type, handler); + hook && hook(type); + }); + }, -/***/ }, -/* 27 */ -/***/ function(module, exports, __webpack_require__) { + /** + * the core touch event handler. + * this finds out if we should to detect gestures + * @method onTouch + * @param {HTMLElement} element + * @param {String} eventType matches `EVENT_START|MOVE|END` + * @param {Function} handler + * @return onTouchHandler {Function} the core event handler + */ + onTouch: function onTouch(element, eventType, handler) { + var self = this; - var Hammer = __webpack_require__(45); - var util = __webpack_require__(1); - var DataSet = __webpack_require__(3); - var DataView = __webpack_require__(4); - var Component = __webpack_require__(20); - var Group = __webpack_require__(25); - var BackgroundGroup = __webpack_require__(26); - var BoxItem = __webpack_require__(33); - var PointItem = __webpack_require__(34); - var RangeItem = __webpack_require__(35); - var BackgroundItem = __webpack_require__(32); + var onTouchHandler = function onTouchHandler(ev) { + var srcType = ev.type.toLowerCase(), + isPointer = Hammer.HAS_POINTEREVENTS, + isMouse = Utils.inStr(srcType, 'mouse'), + triggerType; + // if we are in a mouseevent, but there has been a touchevent triggered in this session + // we want to do nothing. simply break out of the event. + if(isMouse && self.preventMouseEvents) { + return; - var UNGROUPED = '__ungrouped__'; // reserved group id for ungrouped items - var BACKGROUND = '__background__'; // reserved group id for background items without group + // mousebutton must be down + } else if(isMouse && eventType == EVENT_START && ev.button === 0) { + self.preventMouseEvents = false; + self.shouldDetect = true; + } else if(isPointer && eventType == EVENT_START) { + self.shouldDetect = (ev.buttons === 1 || PointerEvent.matchType(POINTER_TOUCH, ev)); + // just a valid start event, but no mouse + } else if(!isMouse && eventType == EVENT_START) { + self.preventMouseEvents = true; + self.shouldDetect = true; + } - /** - * An ItemSet holds a set of items and ranges which can be displayed in a - * range. The width is determined by the parent of the ItemSet, and the height - * is determined by the size of the items. - * @param {{dom: Object, domProps: Object, emitter: Emitter, range: Range}} body - * @param {Object} [options] See ItemSet.setOptions for the available options. - * @constructor ItemSet - * @extends Component - */ - function ItemSet(body, options) { - this.body = body; + // update the pointer event before entering the detection + if(isPointer && eventType != EVENT_END) { + PointerEvent.updatePointer(eventType, ev); + } - this.defaultOptions = { - type: null, // 'box', 'point', 'range', 'background' - orientation: 'bottom', // 'top' or 'bottom' - align: 'auto', // alignment of box items - stack: true, - groupOrder: null, + // we are in a touch/down state, so allowed detection of gestures + if(self.shouldDetect) { + triggerType = self.doDetect.call(self, ev, eventType, element, handler); + } - selectable: true, - editable: { - updateTime: false, - updateGroup: false, - add: false, - remove: false - }, + // ...and we are done with the detection + // so reset everything to start each detection totally fresh + if(triggerType == EVENT_END) { + self.preventMouseEvents = false; + self.shouldDetect = false; + PointerEvent.reset(); + // update the pointerevent object after the detection + } - onAdd: function (item, callback) { - callback(item); - }, - onUpdate: function (item, callback) { - callback(item); - }, - onMove: function (item, callback) { - callback(item); - }, - onRemove: function (item, callback) { - callback(item); - }, - onMoving: function (item, callback) { - callback(item); - }, + if(isPointer && eventType == EVENT_END) { + PointerEvent.updatePointer(eventType, ev); + } + }; - margin: { - item: { - horizontal: 10, - vertical: 10 - }, - axis: 20 + this.on(element, EVENT_TYPES[eventType], onTouchHandler); + return onTouchHandler; }, - padding: 5 - }; - // options is shared by this ItemSet and all its items - this.options = util.extend({}, this.defaultOptions); + /** + * the core detection method + * this finds out what hammer-touch-events to trigger + * @method doDetect + * @param {Object} ev + * @param {String} eventType matches `EVENT_START|MOVE|END` + * @param {HTMLElement} element + * @param {Function} handler + * @return {String} triggerType matches `EVENT_START|MOVE|END` + */ + doDetect: function doDetect(ev, eventType, element, handler) { + var touchList = this.getTouchList(ev, eventType); + var touchListLength = touchList.length; + var triggerType = eventType; + var triggerChange = touchList.trigger; // used by fakeMultitouch plugin + var changedLength = touchListLength; - // options for getting items from the DataSet with the correct type - this.itemOptions = { - type: {start: 'Date', end: 'Date'} - }; + // at each touchstart-like event we want also want to trigger a TOUCH event... + if(eventType == EVENT_START) { + triggerChange = EVENT_TOUCH; + // ...the same for a touchend-like event + } else if(eventType == EVENT_END) { + triggerChange = EVENT_RELEASE; - this.conversion = { - toScreen: body.util.toScreen, - toTime: body.util.toTime - }; - this.dom = {}; - this.props = {}; - this.hammer = null; + // keep track of how many touches have been removed + changedLength = touchList.length - ((ev.changedTouches) ? ev.changedTouches.length : 1); + } - var me = this; - this.itemsData = null; // DataSet - this.groupsData = null; // DataSet + // after there are still touches on the screen, + // we just want to trigger a MOVE event. so change the START or END to a MOVE + // but only after detection has been started, the first time we actualy want a START + if(changedLength > 0 && this.started) { + triggerType = EVENT_MOVE; + } - // listeners for the DataSet of the items - this.itemListeners = { - 'add': function (event, params, senderId) { - me._onAdd(params.items); - }, - 'update': function (event, params, senderId) { - me._onUpdate(params.items); - }, - 'remove': function (event, params, senderId) { - me._onRemove(params.items); - } - }; + // detection has been started, we keep track of this, see above + this.started = true; - // listeners for the DataSet of the groups - this.groupListeners = { - 'add': function (event, params, senderId) { - me._onAddGroups(params.items); - }, - 'update': function (event, params, senderId) { - me._onUpdateGroups(params.items); - }, - 'remove': function (event, params, senderId) { - me._onRemoveGroups(params.items); - } - }; + // generate some event data, some basic information + var evData = this.collectEventData(element, triggerType, touchList, ev); - this.items = {}; // object with an Item for every data item - this.groups = {}; // Group object for every group - this.groupIds = []; + // trigger the triggerType event before the change (TOUCH, RELEASE) events + // but the END event should be at last + if(eventType != EVENT_END) { + handler.call(Detection, evData); + } - this.selection = []; // list with the ids of all selected nodes - this.stackDirty = true; // if true, all items will be restacked on next redraw + // trigger a change (TOUCH, RELEASE) event, this means the length of the touches changed + if(triggerChange) { + evData.changedLength = changedLength; + evData.eventType = triggerChange; - this.touchParams = {}; // stores properties while dragging - // create the HTML DOM + handler.call(Detection, evData); - this._create(); + evData.eventType = triggerType; + delete evData.changedLength; + } - this.setOptions(options); - } + // trigger the END event + if(triggerType == EVENT_END) { + handler.call(Detection, evData); - ItemSet.prototype = new Component(); + // ...and we are done with the detection + // so reset everything to start each detection totally fresh + this.started = false; + } - // available item types will be registered here - ItemSet.types = { - background: BackgroundItem, - box: BoxItem, - range: RangeItem, - point: PointItem - }; + return triggerType; + }, - /** - * Create the HTML DOM for the ItemSet - */ - ItemSet.prototype._create = function(){ - var frame = document.createElement('div'); - frame.className = 'itemset'; - frame['timeline-itemset'] = this; - this.dom.frame = frame; + /** + * we have different events for each device/browser + * determine what we need and set them in the EVENT_TYPES constant + * the `onTouch` method is bind to these properties. + * @method determineEventTypes + * @return {Object} events + */ + determineEventTypes: function determineEventTypes() { + var types; + if(Hammer.HAS_POINTEREVENTS) { + if(window.PointerEvent) { + types = [ + 'pointerdown', + 'pointermove', + 'pointerup pointercancel lostpointercapture' + ]; + } else { + types = [ + 'MSPointerDown', + 'MSPointerMove', + 'MSPointerUp MSPointerCancel MSLostPointerCapture' + ]; + } + } else if(Hammer.NO_MOUSEEVENTS) { + types = [ + 'touchstart', + 'touchmove', + 'touchend touchcancel' + ]; + } else { + types = [ + 'touchstart mousedown', + 'touchmove mousemove', + 'touchend touchcancel mouseup' + ]; + } - // create background panel - var background = document.createElement('div'); - background.className = 'background'; - frame.appendChild(background); - this.dom.background = background; + EVENT_TYPES[EVENT_START] = types[0]; + EVENT_TYPES[EVENT_MOVE] = types[1]; + EVENT_TYPES[EVENT_END] = types[2]; + return EVENT_TYPES; + }, - // create foreground panel - var foreground = document.createElement('div'); - foreground.className = 'foreground'; - frame.appendChild(foreground); - this.dom.foreground = foreground; + /** + * create touchList depending on the event + * @method getTouchList + * @param {Object} ev + * @param {String} eventType + * @return {Array} touches + */ + getTouchList: function getTouchList(ev, eventType) { + // get the fake pointerEvent touchlist + if(Hammer.HAS_POINTEREVENTS) { + return PointerEvent.getTouchList(); + } - // create axis panel - var axis = document.createElement('div'); - axis.className = 'axis'; - this.dom.axis = axis; + // get the touchlist + if(ev.touches) { + if(eventType == EVENT_MOVE) { + return ev.touches; + } - // create labelset - var labelSet = document.createElement('div'); - labelSet.className = 'labelset'; - this.dom.labelSet = labelSet; + var identifiers = []; + var concat = [].concat(Utils.toArray(ev.touches), Utils.toArray(ev.changedTouches)); + var touchList = []; - // create ungrouped Group - this._updateUngrouped(); + Utils.each(concat, function(touch) { + if(Utils.inArray(identifiers, touch.identifier) === false) { + touchList.push(touch); + } + identifiers.push(touch.identifier); + }); - // create background Group - var backgroundGroup = new BackgroundGroup(BACKGROUND, null, this); - backgroundGroup.show(); - this.groups[BACKGROUND] = backgroundGroup; + return touchList; + } - // attach event listeners - // Note: we bind to the centerContainer for the case where the height - // of the center container is larger than of the ItemSet, so we - // can click in the empty area to create a new item or deselect an item. - this.hammer = Hammer(this.body.dom.centerContainer, { - preventDefault: true - }); + // make fake touchList from mouse position + ev.identifier = 1; + return [ev]; + }, - // drag items when selected - this.hammer.on('touch', this._onTouch.bind(this)); - this.hammer.on('dragstart', this._onDragStart.bind(this)); - this.hammer.on('drag', this._onDrag.bind(this)); - this.hammer.on('dragend', this._onDragEnd.bind(this)); + /** + * collect basic event data + * @method collectEventData + * @param {HTMLElement} element + * @param {String} eventType matches `EVENT_START|MOVE|END` + * @param {Array} touches + * @param {Object} ev + * @return {Object} ev + */ + collectEventData: function collectEventData(element, eventType, touches, ev) { + // find out pointerType + var pointerType = POINTER_TOUCH; + if(Utils.inStr(ev.type, 'mouse') || PointerEvent.matchType(POINTER_MOUSE, ev)) { + pointerType = POINTER_MOUSE; + } else if(PointerEvent.matchType(POINTER_PEN, ev)) { + pointerType = POINTER_PEN; + } - // single select (or unselect) when tapping an item - this.hammer.on('tap', this._onSelectItem.bind(this)); + return { + center: Utils.getCenter(touches), + timeStamp: Date.now(), + target: ev.target, + touches: touches, + eventType: eventType, + pointerType: pointerType, + srcEvent: ev, - // multi select when holding mouse/touch, or on ctrl+click - this.hammer.on('hold', this._onMultiSelectItem.bind(this)); + /** + * prevent the browser default actions + * mostly used to disable scrolling of the browser + */ + preventDefault: function() { + var srcEvent = this.srcEvent; + srcEvent.preventManipulation && srcEvent.preventManipulation(); + srcEvent.preventDefault && srcEvent.preventDefault(); + }, - // add item on doubletap - this.hammer.on('doubletap', this._onAddItem.bind(this)); + /** + * stop bubbling the event up to its parents + */ + stopPropagation: function() { + this.srcEvent.stopPropagation(); + }, - // attach to the DOM - this.show(); + /** + * immediately stop gesture detection + * might be useful after a swipe was detected + * @return {*} + */ + stopDetect: function() { + return Detection.stopDetect(); + } + }; + } }; + /** - * Set options for the ItemSet. Existing options will be extended/overwritten. - * @param {Object} [options] The following options are available: - * {String} type - * Default type for the items. Choose from 'box' - * (default), 'point', 'range', or 'background'. - * The default style can be overwritten by - * individual items. - * {String} align - * Alignment for the items, only applicable for - * BoxItem. Choose 'center' (default), 'left', or - * 'right'. - * {String} orientation - * Orientation of the item set. Choose 'top' or - * 'bottom' (default). - * {Function} groupOrder - * A sorting function for ordering groups - * {Boolean} stack - * If true (deafult), items will be stacked on - * top of each other. - * {Number} margin.axis - * Margin between the axis and the items in pixels. - * Default is 20. - * {Number} margin.item.horizontal - * Horizontal margin between items in pixels. - * Default is 10. - * {Number} margin.item.vertical - * Vertical Margin between items in pixels. - * Default is 10. - * {Number} margin.item - * Margin between items in pixels in both horizontal - * and vertical direction. Default is 10. - * {Number} margin - * Set margin for both axis and items in pixels. - * {Number} padding - * Padding of the contents of an item in pixels. - * Must correspond with the items css. Default is 5. - * {Boolean} selectable - * If true (default), items can be selected. - * {Boolean} editable - * Set all editable options to true or false - * {Boolean} editable.updateTime - * Allow dragging an item to an other moment in time - * {Boolean} editable.updateGroup - * Allow dragging an item to an other group - * {Boolean} editable.add - * Allow creating new items on double tap - * {Boolean} editable.remove - * Allow removing items by clicking the delete button - * top right of a selected item. - * {Function(item: Item, callback: Function)} onAdd - * Callback function triggered when an item is about to be added: - * when the user double taps an empty space in the Timeline. - * {Function(item: Item, callback: Function)} onUpdate - * Callback function fired when an item is about to be updated. - * This function typically has to show a dialog where the user - * change the item. If not implemented, nothing happens. - * {Function(item: Item, callback: Function)} onMove - * Fired when an item has been moved. If not implemented, - * the move action will be accepted. - * {Function(item: Item, callback: Function)} onRemove - * Fired when an item is about to be deleted. - * If not implemented, the item will be always removed. + * @module hammer + * + * @class PointerEvent + * @static */ - ItemSet.prototype.setOptions = function(options) { - if (options) { - // copy all options that we know - var fields = ['type', 'align', 'orientation', 'padding', 'stack', 'selectable', 'groupOrder', 'dataAttributes', 'template','hide']; - util.selectiveExtend(fields, this.options, options); - - if ('margin' in options) { - if (typeof options.margin === 'number') { - this.options.margin.axis = options.margin; - this.options.margin.item.horizontal = options.margin; - this.options.margin.item.vertical = options.margin; - } - else if (typeof options.margin === 'object') { - util.selectiveExtend(['axis'], this.options.margin, options.margin); - if ('item' in options.margin) { - if (typeof options.margin.item === 'number') { - this.options.margin.item.horizontal = options.margin.item; - this.options.margin.item.vertical = options.margin.item; - } - else if (typeof options.margin.item === 'object') { - util.selectiveExtend(['horizontal', 'vertical'], this.options.margin.item, options.margin.item); - } - } - } - } + var PointerEvent = Hammer.PointerEvent = { + /** + * holds all pointers, by `identifier` + * @property pointers + * @type {Object} + */ + pointers: {}, - if ('editable' in options) { - if (typeof options.editable === 'boolean') { - this.options.editable.updateTime = options.editable; - this.options.editable.updateGroup = options.editable; - this.options.editable.add = options.editable; - this.options.editable.remove = options.editable; - } - else if (typeof options.editable === 'object') { - util.selectiveExtend(['updateTime', 'updateGroup', 'add', 'remove'], this.options.editable, options.editable); - } - } + /** + * get the pointers as an array + * @method getTouchList + * @return {Array} touchlist + */ + getTouchList: function getTouchList() { + var touchlist = []; + // we can use forEach since pointerEvents only is in IE10 + Utils.each(this.pointers, function(pointer) { + touchlist.push(pointer); + }); + return touchlist; + }, - // callback functions - var addCallback = (function (name) { - var fn = options[name]; - if (fn) { - if (!(fn instanceof Function)) { - throw new Error('option ' + name + ' must be a function ' + name + '(item, callback)'); + /** + * update the position of a pointer + * @method updatePointer + * @param {String} eventType matches `EVENT_START|MOVE|END` + * @param {Object} pointerEvent + */ + updatePointer: function updatePointer(eventType, pointerEvent) { + if(eventType == EVENT_END || (eventType != EVENT_END && pointerEvent.buttons !== 1)) { + delete this.pointers[pointerEvent.pointerId]; + } else { + pointerEvent.identifier = pointerEvent.pointerId; + this.pointers[pointerEvent.pointerId] = pointerEvent; } - this.options[name] = fn; - } - }).bind(this); - ['onAdd', 'onUpdate', 'onRemove', 'onMove', 'onMoving'].forEach(addCallback); - - // force the itemSet to refresh: options like orientation and margins may be changed - this.markDirty(); - } - }; + }, - /** - * Mark the ItemSet dirty so it will refresh everything with next redraw - */ - ItemSet.prototype.markDirty = function() { - this.groupIds = []; - this.stackDirty = true; - }; + /** + * check if ev matches pointertype + * @method matchType + * @param {String} pointerType matches `POINTER_MOUSE|TOUCH|PEN` + * @param {PointerEvent} ev + */ + matchType: function matchType(pointerType, ev) { + if(!ev.pointerType) { + return false; + } - /** - * Destroy the ItemSet - */ - ItemSet.prototype.destroy = function() { - this.hide(); - this.setItems(null); - this.setGroups(null); + var pt = ev.pointerType, + types = {}; - this.hammer = null; + types[POINTER_MOUSE] = (pt === (ev.MSPOINTER_TYPE_MOUSE || POINTER_MOUSE)); + types[POINTER_TOUCH] = (pt === (ev.MSPOINTER_TYPE_TOUCH || POINTER_TOUCH)); + types[POINTER_PEN] = (pt === (ev.MSPOINTER_TYPE_PEN || POINTER_PEN)); + return types[pointerType]; + }, - this.body = null; - this.conversion = null; + /** + * reset the stored pointers + * @method reset + */ + reset: function resetList() { + this.pointers = {}; + } }; + /** - * Hide the component from the DOM + * @module hammer + * + * @class Detection + * @static */ - ItemSet.prototype.hide = function() { - // remove the frame containing the items - if (this.dom.frame.parentNode) { - this.dom.frame.parentNode.removeChild(this.dom.frame); - } - - // remove the axis with dots - if (this.dom.axis.parentNode) { - this.dom.axis.parentNode.removeChild(this.dom.axis); - } + var Detection = Hammer.detection = { + // contains all registred Hammer.gestures in the correct order + gestures: [], - // remove the labelset containing all group labels - if (this.dom.labelSet.parentNode) { - this.dom.labelSet.parentNode.removeChild(this.dom.labelSet); - } - }; + // data of the current Hammer.gesture detection session + current: null, - /** - * Show the component in the DOM (when not already visible). - * @return {Boolean} changed - */ - ItemSet.prototype.show = function() { - // show frame containing the items - if (!this.dom.frame.parentNode) { - this.body.dom.center.appendChild(this.dom.frame); - } + // the previous Hammer.gesture session data + // is a full clone of the previous gesture.current object + previous: null, - // show axis with dots - if (!this.dom.axis.parentNode) { - this.body.dom.backgroundVertical.appendChild(this.dom.axis); - } + // when this becomes true, no gestures are fired + stopped: false, - // show labelset containing labels - if (!this.dom.labelSet.parentNode) { - this.body.dom.left.appendChild(this.dom.labelSet); - } - }; + /** + * start Hammer.gesture detection + * @method startDetect + * @param {Hammer.Instance} inst + * @param {Object} eventData + */ + startDetect: function startDetect(inst, eventData) { + // already busy with a Hammer.gesture detection on an element + if(this.current) { + return; + } - /** - * Set selected items by their id. Replaces the current selection - * Unknown id's are silently ignored. - * @param {string[] | string} [ids] An array with zero or more id's of the items to be - * selected, or a single item id. If ids is undefined - * or an empty array, all items will be unselected. - */ - ItemSet.prototype.setSelection = function(ids) { - var i, ii, id, item; + this.stopped = false; - if (ids == undefined) ids = []; - if (!Array.isArray(ids)) ids = [ids]; + // holds current session + this.current = { + inst: inst, // reference to HammerInstance we're working for + startEvent: Utils.extend({}, eventData), // start eventData for distances, timing etc + lastEvent: false, // last eventData + lastCalcEvent: false, // last eventData for calculations. + futureCalcEvent: false, // last eventData for calculations. + lastCalcData: {}, // last lastCalcData + name: '' // current gesture we're in/detected, can be 'tap', 'hold' etc + }; - // unselect currently selected items - for (i = 0, ii = this.selection.length; i < ii; i++) { - id = this.selection[i]; - item = this.items[id]; - if (item) item.unselect(); - } + this.detect(eventData); + }, - // select items - this.selection = []; - for (i = 0, ii = ids.length; i < ii; i++) { - id = ids[i]; - item = this.items[id]; - if (item) { - this.selection.push(id); - item.select(); - } - } - }; + /** + * Hammer.gesture detection + * @method detect + * @param {Object} eventData + * @return {any} + */ + detect: function detect(eventData) { + if(!this.current || this.stopped) { + return; + } - /** - * Get the selected items by their id - * @return {Array} ids The ids of the selected items - */ - ItemSet.prototype.getSelection = function() { - return this.selection.concat([]); - }; + // extend event data with calculations about scale, distance etc + eventData = this.extendEventData(eventData); - /** - * Get the id's of the currently visible items. - * @returns {Array} The ids of the visible items - */ - ItemSet.prototype.getVisibleItems = function() { - var range = this.body.range.getRange(); - var left = this.body.util.toScreen(range.start); - var right = this.body.util.toScreen(range.end); + // hammer instance and instance options + var inst = this.current.inst, + instOptions = inst.options; - var ids = []; - for (var groupId in this.groups) { - if (this.groups.hasOwnProperty(groupId)) { - var group = this.groups[groupId]; - var rawVisibleItems = group.visibleItems; + // call Hammer.gesture handlers + Utils.each(this.gestures, function triggerGesture(gesture) { + // only when the instance options have enabled this gesture + if(!this.stopped && inst.enabled && instOptions[gesture.name]) { + gesture.handler.call(gesture, eventData, inst); + } + }, this); - // filter the "raw" set with visibleItems into a set which is really - // visible by pixels - for (var i = 0; i < rawVisibleItems.length; i++) { - var item = rawVisibleItems[i]; - // TODO: also check whether visible vertically - if ((item.left < right) && (item.left + item.width > left)) { - ids.push(item.id); + // store as previous event event + if(this.current) { + this.current.lastEvent = eventData; } - } - } - } - return ids; - }; + if(eventData.eventType == EVENT_END) { + this.stopDetect(); + } - /** - * Deselect a selected item - * @param {String | Number} id - * @private - */ - ItemSet.prototype._deselect = function(id) { - var selection = this.selection; - for (var i = 0, ii = selection.length; i < ii; i++) { - if (selection[i] == id) { // non-strict comparison! - selection.splice(i, 1); - break; - } - } - }; + return eventData; + }, - /** - * Repaint the component - * @return {boolean} Returns true if the component is resized - */ - ItemSet.prototype.redraw = function() { - var margin = this.options.margin, - range = this.body.range, - asSize = util.option.asSize, - options = this.options, - orientation = options.orientation, - resized = false, - frame = this.dom.frame, - editable = options.editable.updateTime || options.editable.updateGroup; + /** + * clear the Hammer.gesture vars + * this is called on endDetect, but can also be used when a final Hammer.gesture has been detected + * to stop other Hammer.gestures from being fired + * @method stopDetect + */ + stopDetect: function stopDetect() { + // clone current data to the store as the previous gesture + // used for the double tap gesture, since this is an other gesture detect session + this.previous = Utils.extend({}, this.current); - // recalculate absolute position (before redrawing groups) - this.props.top = this.body.domProps.top.height + this.body.domProps.border.top; - this.props.left = this.body.domProps.left.width + this.body.domProps.border.left; + // reset the current + this.current = null; + this.stopped = true; + }, - // update class name - frame.className = 'itemset' + (editable ? ' editable' : ''); + /** + * calculate velocity, angle and direction + * @method getVelocityData + * @param {Object} ev + * @param {Object} center + * @param {Number} deltaTime + * @param {Number} deltaX + * @param {Number} deltaY + */ + getCalculatedData: function getCalculatedData(ev, center, deltaTime, deltaX, deltaY) { + var cur = this.current, + recalc = false, + calcEv = cur.lastCalcEvent, + calcData = cur.lastCalcData; - // reorder the groups (if needed) - resized = this._orderGroups() || resized; + if(calcEv && ev.timeStamp - calcEv.timeStamp > Hammer.CALCULATE_INTERVAL) { + center = calcEv.center; + deltaTime = ev.timeStamp - calcEv.timeStamp; + deltaX = ev.center.clientX - calcEv.center.clientX; + deltaY = ev.center.clientY - calcEv.center.clientY; + recalc = true; + } - // check whether zoomed (in that case we need to re-stack everything) - // TODO: would be nicer to get this as a trigger from Range - var visibleInterval = range.end - range.start; - var zoomed = (visibleInterval != this.lastVisibleInterval) || (this.props.width != this.props.lastWidth); - if (zoomed) this.stackDirty = true; - this.lastVisibleInterval = visibleInterval; - this.props.lastWidth = this.props.width; + if(ev.eventType == EVENT_TOUCH || ev.eventType == EVENT_RELEASE) { + cur.futureCalcEvent = ev; + } - var restack = this.stackDirty; - var firstGroup = this._firstGroup(); - var firstMargin = { - item: margin.item, - axis: margin.axis - }; - var nonFirstMargin = { - item: margin.item, - axis: margin.item.vertical / 2 - }; - var height = 0; - var minHeight = margin.axis + margin.item.vertical; + if(!cur.lastCalcEvent || recalc) { + calcData.velocity = Utils.getVelocity(deltaTime, deltaX, deltaY); + calcData.angle = Utils.getAngle(center, ev.center); + calcData.direction = Utils.getDirection(center, ev.center); - // redraw the background group - this.groups[BACKGROUND].redraw(range, nonFirstMargin, restack); + cur.lastCalcEvent = cur.futureCalcEvent || ev; + cur.futureCalcEvent = ev; + } - // redraw all regular groups - util.forEach(this.groups, function (group) { - var groupMargin = (group == firstGroup) ? firstMargin : nonFirstMargin; - var groupResized = group.redraw(range, groupMargin, restack); - resized = groupResized || resized; - height += group.height; - }); - height = Math.max(height, minHeight); - this.stackDirty = false; + ev.velocityX = calcData.velocity.x; + ev.velocityY = calcData.velocity.y; + ev.interimAngle = calcData.angle; + ev.interimDirection = calcData.direction; + }, - // update frame height - frame.style.height = asSize(height); + /** + * extend eventData for Hammer.gestures + * @method extendEventData + * @param {Object} ev + * @return {Object} ev + */ + extendEventData: function extendEventData(ev) { + var cur = this.current, + startEv = cur.startEvent, + lastEv = cur.lastEvent || startEv; - // calculate actual size - this.props.width = frame.offsetWidth; - this.props.height = height; + // update the start touchlist to calculate the scale/rotation + if(ev.eventType == EVENT_TOUCH || ev.eventType == EVENT_RELEASE) { + startEv.touches = []; + Utils.each(ev.touches, function(touch) { + startEv.touches.push({ + clientX: touch.clientX, + clientY: touch.clientY + }); + }); + } - // reposition axis - this.dom.axis.style.top = asSize((orientation == 'top') ? - (this.body.domProps.top.height + this.body.domProps.border.top) : - (this.body.domProps.top.height + this.body.domProps.centerContainer.height)); - this.dom.axis.style.left = '0'; + var deltaTime = ev.timeStamp - startEv.timeStamp, + deltaX = ev.center.clientX - startEv.center.clientX, + deltaY = ev.center.clientY - startEv.center.clientY; - // check if this component is resized - resized = this._isResized() || resized; + this.getCalculatedData(ev, lastEv.center, deltaTime, deltaX, deltaY); - return resized; - }; + Utils.extend(ev, { + startEvent: startEv, - /** - * Get the first group, aligned with the axis - * @return {Group | null} firstGroup - * @private - */ - ItemSet.prototype._firstGroup = function() { - var firstGroupIndex = (this.options.orientation == 'top') ? 0 : (this.groupIds.length - 1); - var firstGroupId = this.groupIds[firstGroupIndex]; - var firstGroup = this.groups[firstGroupId] || this.groups[UNGROUPED]; + deltaTime: deltaTime, + deltaX: deltaX, + deltaY: deltaY, - return firstGroup || null; - }; + distance: Utils.getDistance(startEv.center, ev.center), + angle: Utils.getAngle(startEv.center, ev.center), + direction: Utils.getDirection(startEv.center, ev.center), + scale: Utils.getScale(startEv.touches, ev.touches), + rotation: Utils.getRotation(startEv.touches, ev.touches) + }); - /** - * Create or delete the group holding all ungrouped items. This group is used when - * there are no groups specified. - * @protected - */ - ItemSet.prototype._updateUngrouped = function() { - var ungrouped = this.groups[UNGROUPED]; - var background = this.groups[BACKGROUND]; - var item, itemId; + return ev; + }, - if (this.groupsData) { - // remove the group holding all ungrouped items - if (ungrouped) { - ungrouped.hide(); - delete this.groups[UNGROUPED]; - - for (itemId in this.items) { - if (this.items.hasOwnProperty(itemId)) { - item = this.items[itemId]; - item.parent && item.parent.remove(item); - var groupId = this._getGroupId(item.data); - var group = this.groups[groupId]; - group && group.add(item) || item.hide(); + /** + * register new gesture + * @method register + * @param {Object} gesture object, see `gestures/` for documentation + * @return {Array} gestures + */ + register: function register(gesture) { + // add an enable gesture options if there is no given + var options = gesture.defaults || {}; + if(options[gesture.name] === undefined) { + options[gesture.name] = true; } - } - } - } - else { - // create a group holding all (unfiltered) items - if (!ungrouped) { - var id = null; - var data = null; - ungrouped = new Group(id, data, this); - this.groups[UNGROUPED] = ungrouped; - for (itemId in this.items) { - if (this.items.hasOwnProperty(itemId)) { - item = this.items[itemId]; - ungrouped.add(item); - } - } + // extend Hammer default options with the Hammer.gesture options + Utils.extend(Hammer.defaults, options, true); - ungrouped.show(); + // set its index + gesture.index = gesture.index || 1000; + + // add Hammer.gesture to the list + this.gestures.push(gesture); + + // sort the list by index + this.gestures.sort(function(a, b) { + if(a.index < b.index) { + return -1; + } + if(a.index > b.index) { + return 1; + } + return 0; + }); + + return this.gestures; } - } }; + /** - * Get the element for the labelset - * @return {HTMLElement} labelSet + * @module hammer */ - ItemSet.prototype.getLabelSet = function() { - return this.dom.labelSet; - }; /** - * Set items - * @param {vis.DataSet | null} items + * create new hammer instance + * all methods should return the instance itself, so it is chainable. + * + * @class Instance + * @constructor + * @param {HTMLElement} element + * @param {Object} [options={}] options are merged with `Hammer.defaults` + * @return {Hammer.Instance} */ - ItemSet.prototype.setItems = function(items) { - var me = this, - ids, - oldItemsData = this.itemsData; + Hammer.Instance = function(element, options) { + var self = this; - // replace the dataset - if (!items) { - this.itemsData = null; - } - else if (items instanceof DataSet || items instanceof DataView) { - this.itemsData = items; - } - else { - throw new TypeError('Data must be an instance of DataSet or DataView'); - } + // setup HammerJS window events and register all gestures + // this also sets up the default options + setup(); - if (oldItemsData) { - // unsubscribe from old dataset - util.forEach(this.itemListeners, function (callback, event) { - oldItemsData.off(event, callback); - }); + /** + * @property element + * @type {HTMLElement} + */ + this.element = element; - // remove all drawn items - ids = oldItemsData.getIds(); - this._onRemove(ids); - } + /** + * @property enabled + * @type {Boolean} + * @protected + */ + this.enabled = true; - if (this.itemsData) { - // subscribe to new dataset - var id = this.id; - util.forEach(this.itemListeners, function (callback, event) { - me.itemsData.on(event, callback, id); + /** + * options, merged with the defaults + * options with an _ are converted to camelCase + * @property options + * @type {Object} + */ + Utils.each(options, function(value, name) { + delete options[name]; + options[Utils.toCamelCase(name)] = value; }); - // add all new items - ids = this.itemsData.getIds(); - this._onAdd(ids); + this.options = Utils.extend(Utils.extend({}, Hammer.defaults), options || {}); - // update the group holding all ungrouped items - this._updateUngrouped(); - } - }; + // add some css to the element to prevent the browser from doing its native behavoir + if(this.options.behavior) { + Utils.toggleBehavior(this.element, this.options.behavior, true); + } - /** - * Get the current items - * @returns {vis.DataSet | null} - */ - ItemSet.prototype.getItems = function() { - return this.itemsData; + /** + * event start handler on the element to start the detection + * @property eventStartHandler + * @type {Object} + */ + this.eventStartHandler = Event.onTouch(element, EVENT_START, function(ev) { + if(self.enabled && ev.eventType == EVENT_START) { + Detection.startDetect(self, ev); + } else if(ev.eventType == EVENT_TOUCH) { + Detection.detect(ev); + } + }); + + /** + * keep a list of user event handlers which needs to be removed when calling 'dispose' + * @property eventHandlers + * @type {Array} + */ + this.eventHandlers = []; }; - /** - * Set groups - * @param {vis.DataSet} groups - */ - ItemSet.prototype.setGroups = function(groups) { - var me = this, - ids; + Hammer.Instance.prototype = { + /** + * bind events to the instance + * @method on + * @chainable + * @param {String} gestures multiple gestures by splitting with a space + * @param {Function} handler + * @param {Object} handler.ev event object + */ + on: function onEvent(gestures, handler) { + var self = this; + Event.on(self.element, gestures, handler, function(type) { + self.eventHandlers.push({ gesture: type, handler: handler }); + }); + return self; + }, - // unsubscribe from current dataset - if (this.groupsData) { - util.forEach(this.groupListeners, function (callback, event) { - me.groupsData.unsubscribe(event, callback); - }); + /** + * unbind events to the instance + * @method off + * @chainable + * @param {String} gestures + * @param {Function} handler + */ + off: function offEvent(gestures, handler) { + var self = this; - // remove all drawn groups - ids = this.groupsData.getIds(); - this.groupsData = null; - this._onRemoveGroups(ids); // note: this will cause a redraw - } + Event.off(self.element, gestures, handler, function(type) { + var index = Utils.inArray({ gesture: type, handler: handler }); + if(index !== false) { + self.eventHandlers.splice(index, 1); + } + }); + return self; + }, - // replace the dataset - if (!groups) { - this.groupsData = null; - } - else if (groups instanceof DataSet || groups instanceof DataView) { - this.groupsData = groups; - } - else { - throw new TypeError('Data must be an instance of DataSet or DataView'); - } + /** + * trigger gesture event + * @method trigger + * @chainable + * @param {String} gesture + * @param {Object} [eventData] + */ + trigger: function triggerEvent(gesture, eventData) { + // optional + if(!eventData) { + eventData = {}; + } - if (this.groupsData) { - // subscribe to new dataset - var id = this.id; - util.forEach(this.groupListeners, function (callback, event) { - me.groupsData.on(event, callback, id); - }); + // create DOM event + var event = Hammer.DOCUMENT.createEvent('Event'); + event.initEvent(gesture, true, true); + event.gesture = eventData; - // draw all ms - ids = this.groupsData.getIds(); - this._onAddGroups(ids); - } + // trigger on the target if it is in the instance element, + // this is for event delegation tricks + var element = this.element; + if(Utils.hasParent(eventData.target, element)) { + element = eventData.target; + } - // update the group holding all ungrouped items - this._updateUngrouped(); + element.dispatchEvent(event); + return this; + }, - // update the order of all items in each group - this._order(); + /** + * enable of disable hammer.js detection + * @method enable + * @chainable + * @param {Boolean} state + */ + enable: function enable(state) { + this.enabled = state; + return this; + }, - this.body.emitter.emit('change', {queue: true}); + /** + * dispose this hammer instance + * @method dispose + * @return {Null} + */ + dispose: function dispose() { + var i, eh; + + // undo all changes made by stop_browser_behavior + Utils.toggleBehavior(this.element, this.options.behavior, false); + + // unbind all custom event handlers + for(i = -1; (eh = this.eventHandlers[++i]);) { + Utils.off(this.element, eh.gesture, eh.handler); + } + + this.eventHandlers = []; + + // unbind the start event listener + Event.off(this.element, EVENT_TYPES[EVENT_START], this.eventStartHandler); + + return null; + } }; + /** - * Get the current groups - * @returns {vis.DataSet | null} groups + * @module gestures */ - ItemSet.prototype.getGroups = function() { - return this.groupsData; - }; - /** - * Remove an item by its id - * @param {String | Number} id + * Move with x fingers (default 1) around on the page. + * Preventing the default browser behavior is a good way to improve feel and working. + * ```` + * hammertime.on("drag", function(ev) { + * console.log(ev); + * ev.gesture.preventDefault(); + * }); + * ```` + * + * @class Drag + * @static */ - ItemSet.prototype.removeItem = function(id) { - var item = this.itemsData.get(id), - dataset = this.itemsData.getDataSet(); - - if (item) { - // confirm deletion - this.options.onRemove(item, function (item) { - if (item) { - // remove by id here, it is possible that an item has no id defined - // itself, so better not delete by the item itself - dataset.remove(id); - } - }); - } - }; - /** - * Get the time of an item based on it's data and options.type - * @param {Object} itemData - * @returns {string} Returns the type - * @private + * @event drag + * @param {Object} ev */ - ItemSet.prototype._getType = function (itemData) { - return itemData.type || this.options.type || (itemData.end ? 'range' : 'box'); - }; - - /** - * Get the group id for an item - * @param {Object} itemData - * @returns {string} Returns the groupId - * @private + * @event dragstart + * @param {Object} ev */ - ItemSet.prototype._getGroupId = function (itemData) { - var type = this._getType(itemData); - if (type == 'background' && itemData.group == undefined) { - return BACKGROUND; - } - else { - return this.groupsData ? itemData.group : UNGROUPED; - } - }; - /** - * Handle updated items - * @param {Number[]} ids - * @protected + * @event dragend + * @param {Object} ev */ - ItemSet.prototype._onUpdate = function(ids) { - var me = this; - - ids.forEach(function (id) { - var itemData = me.itemsData.get(id, me.itemOptions); - var item = me.items[id]; - var type = me._getType(itemData); - - var constructor = ItemSet.types[type]; - - if (item) { - // update item - if (!constructor || !(item instanceof constructor)) { - // item type has changed, delete the item and recreate it - me._removeItem(item); - item = null; - } - else { - me._updateItem(item, itemData); - } - } - - if (!item) { - // create item - if (constructor) { - item = new constructor(itemData, me.conversion, me.options); - item.id = id; // TODO: not so nice setting id afterwards - me._addItem(item); - } - else if (type == 'rangeoverflow') { - // TODO: deprecated since version 2.1.0 (or 3.0.0?). cleanup some day - throw new TypeError('Item type "rangeoverflow" is deprecated. Use css styling instead: ' + - '.vis.timeline .item.range .content {overflow: visible;}'); - } - else { - throw new TypeError('Unknown item type "' + type + '"'); - } - } - }); - - this._order(); - this.stackDirty = true; // force re-stacking of all items next redraw - this.body.emitter.emit('change', {queue: true}); - }; - /** - * Handle added items - * @param {Number[]} ids - * @protected + * @event drapleft + * @param {Object} ev */ - ItemSet.prototype._onAdd = ItemSet.prototype._onUpdate; - /** - * Handle removed items - * @param {Number[]} ids - * @protected + * @event dragright + * @param {Object} ev */ - ItemSet.prototype._onRemove = function(ids) { - var count = 0; - var me = this; - ids.forEach(function (id) { - var item = me.items[id]; - if (item) { - count++; - me._removeItem(item); - } - }); - - if (count) { - // update order - this._order(); - this.stackDirty = true; // force re-stacking of all items next redraw - this.body.emitter.emit('change', {queue: true}); - } - }; - /** - * Update the order of item in all groups - * @private + * @event dragup + * @param {Object} ev */ - ItemSet.prototype._order = function() { - // reorder the items in all groups - // TODO: optimization: only reorder groups affected by the changed items - util.forEach(this.groups, function (group) { - group.order(); - }); - }; - /** - * Handle updated groups - * @param {Number[]} ids - * @private + * @event dragdown + * @param {Object} ev */ - ItemSet.prototype._onUpdateGroups = function(ids) { - this._onAddGroups(ids); - }; /** - * Handle changed groups (added or updated) - * @param {Number[]} ids - * @private + * @param {String} name */ - ItemSet.prototype._onAddGroups = function(ids) { - var me = this; + (function(name) { + var triggered = false; - ids.forEach(function (id) { - var groupData = me.groupsData.get(id); - var group = me.groups[id]; + function dragGesture(ev, inst) { + var cur = Detection.current; - if (!group) { - // check for reserved ids - if (id == UNGROUPED || id == BACKGROUND) { - throw new Error('Illegal group id. ' + id + ' is a reserved id.'); - } + // max touches + if(inst.options.dragMaxTouches > 0 && + ev.touches.length > inst.options.dragMaxTouches) { + return; + } - var groupOptions = Object.create(me.options); - util.extend(groupOptions, { - height: null - }); + switch(ev.eventType) { + case EVENT_START: + triggered = false; + break; - group = new Group(id, groupData, me); - me.groups[id] = group; + case EVENT_MOVE: + // when the distance we moved is too small we skip this gesture + // or we can be already in dragging + if(ev.distance < inst.options.dragMinDistance && + cur.name != name) { + return; + } - // add items with this groupId to the new group - for (var itemId in me.items) { - if (me.items.hasOwnProperty(itemId)) { - var item = me.items[itemId]; - if (item.data.group == id) { - group.add(item); - } - } - } + var startCenter = cur.startEvent.center; - group.order(); - group.show(); - } - else { - // update group - group.setData(groupData); - } - }); + // we are dragging! + if(cur.name != name) { + cur.name = name; + if(inst.options.dragDistanceCorrection && ev.distance > 0) { + // When a drag is triggered, set the event center to dragMinDistance pixels from the original event center. + // Without this correction, the dragged distance would jumpstart at dragMinDistance pixels instead of at 0. + // It might be useful to save the original start point somewhere + var factor = Math.abs(inst.options.dragMinDistance / ev.distance); + startCenter.pageX += ev.deltaX * factor; + startCenter.pageY += ev.deltaY * factor; + startCenter.clientX += ev.deltaX * factor; + startCenter.clientY += ev.deltaY * factor; - this.body.emitter.emit('change', {queue: true}); - }; + // recalculate event data using new start point + ev = Detection.extendEventData(ev); + } + } - /** - * Handle removed groups - * @param {Number[]} ids - * @private - */ - ItemSet.prototype._onRemoveGroups = function(ids) { - var groups = this.groups; - ids.forEach(function (id) { - var group = groups[id]; + // lock drag to axis? + if(cur.lastEvent.dragLockToAxis || + ( inst.options.dragLockToAxis && + inst.options.dragLockMinDistance <= ev.distance + )) { + ev.dragLockToAxis = true; + } - if (group) { - group.hide(); - delete groups[id]; - } - }); + // keep direction on the axis that the drag gesture started on + var lastDirection = cur.lastEvent.direction; + if(ev.dragLockToAxis && lastDirection !== ev.direction) { + if(Utils.isVertical(lastDirection)) { + ev.direction = (ev.deltaY < 0) ? DIRECTION_UP : DIRECTION_DOWN; + } else { + ev.direction = (ev.deltaX < 0) ? DIRECTION_LEFT : DIRECTION_RIGHT; + } + } - this.markDirty(); + // first time, trigger dragstart event + if(!triggered) { + inst.trigger(name + 'start', ev); + triggered = true; + } - this.body.emitter.emit('change', {queue: true}); - }; + // trigger events + inst.trigger(name, ev); + inst.trigger(name + ev.direction, ev); - /** - * Reorder the groups if needed - * @return {boolean} changed - * @private - */ - ItemSet.prototype._orderGroups = function () { - if (this.groupsData) { - // reorder the groups - var groupIds = this.groupsData.getIds({ - order: this.options.groupOrder - }); + var isVertical = Utils.isVertical(ev.direction); - var changed = !util.equalArray(groupIds, this.groupIds); - if (changed) { - // hide all groups, removes them from the DOM - var groups = this.groups; - groupIds.forEach(function (groupId) { - groups[groupId].hide(); - }); + // block the browser events + if((inst.options.dragBlockVertical && isVertical) || + (inst.options.dragBlockHorizontal && !isVertical)) { + ev.preventDefault(); + } + break; - // show the groups again, attach them to the DOM in correct order - groupIds.forEach(function (groupId) { - groups[groupId].show(); - }); + case EVENT_RELEASE: + if(triggered && ev.changedLength <= inst.options.dragMaxTouches) { + inst.trigger(name + 'end', ev); + triggered = false; + } + break; - this.groupIds = groupIds; + case EVENT_END: + triggered = false; + break; + } } - return changed; - } - else { - return false; - } - }; + Hammer.gestures.Drag = { + name: name, + index: 50, + handler: dragGesture, + defaults: { + /** + * minimal movement that have to be made before the drag event gets triggered + * @property dragMinDistance + * @type {Number} + * @default 10 + */ + dragMinDistance: 10, - /** - * Add a new item - * @param {Item} item - * @private - */ - ItemSet.prototype._addItem = function(item) { - this.items[item.id] = item; + /** + * Set dragDistanceCorrection to true to make the starting point of the drag + * be calculated from where the drag was triggered, not from where the touch started. + * Useful to avoid a jerk-starting drag, which can make fine-adjustments + * through dragging difficult, and be visually unappealing. + * @property dragDistanceCorrection + * @type {Boolean} + * @default true + */ + dragDistanceCorrection: true, - // add to group - var groupId = this._getGroupId(item.data); - var group = this.groups[groupId]; - if (group) group.add(item); - }; + /** + * set 0 for unlimited, but this can conflict with transform + * @property dragMaxTouches + * @type {Number} + * @default 1 + */ + dragMaxTouches: 1, - /** - * Update an existing item - * @param {Item} item - * @param {Object} itemData - * @private - */ - ItemSet.prototype._updateItem = function(item, itemData) { - var oldGroupId = item.data.group; + /** + * prevent default browser behavior when dragging occurs + * be careful with it, it makes the element a blocking element + * when you are using the drag gesture, it is a good practice to set this true + * @property dragBlockHorizontal + * @type {Boolean} + * @default false + */ + dragBlockHorizontal: false, - // update the items data (will redraw the item when displayed) - item.setData(itemData); + /** + * same as `dragBlockHorizontal`, but for vertical movement + * @property dragBlockVertical + * @type {Boolean} + * @default false + */ + dragBlockVertical: false, - // update group - if (oldGroupId != item.data.group) { - var oldGroup = this.groups[oldGroupId]; - if (oldGroup) oldGroup.remove(item); + /** + * dragLockToAxis keeps the drag gesture on the axis that it started on, + * It disallows vertical directions if the initial direction was horizontal, and vice versa. + * @property dragLockToAxis + * @type {Boolean} + * @default false + */ + dragLockToAxis: false, - var groupId = this._getGroupId(item.data); - var group = this.groups[groupId]; - if (group) group.add(item); - } - }; + /** + * drag lock only kicks in when distance > dragLockMinDistance + * This way, locking occurs only when the distance has become large enough to reliably determine the direction + * @property dragLockMinDistance + * @type {Number} + * @default 25 + */ + dragLockMinDistance: 25 + } + }; + })('drag'); /** - * Delete an item from the ItemSet: remove it from the DOM, from the map - * with items, and from the map with visible items, and from the selection - * @param {Item} item - * @private + * @module gestures */ - ItemSet.prototype._removeItem = function(item) { - // remove from DOM - item.hide(); - - // remove from items - delete this.items[item.id]; - - // remove from selection - var index = this.selection.indexOf(item.id); - if (index != -1) this.selection.splice(index, 1); - - // remove from group - item.parent && item.parent.remove(item); - }; - /** - * Create an array containing all items being a range (having an end date) - * @param array - * @returns {Array} - * @private + * trigger a simple gesture event, so you can do anything in your handler. + * only usable if you know what your doing... + * + * @class Gesture + * @static */ - ItemSet.prototype._constructByEndArray = function(array) { - var endArray = []; - - for (var i = 0; i < array.length; i++) { - if (array[i] instanceof RangeItem) { - endArray.push(array[i]); + /** + * @event gesture + * @param {Object} ev + */ + Hammer.gestures.Gesture = { + name: 'gesture', + index: 1337, + handler: function releaseGesture(ev, inst) { + inst.trigger(this.name, ev); } - } - return endArray; }; /** - * Register the clicked item on touch, before dragStart is initiated. - * - * dragStart is initiated from a mousemove event, which can have left the item - * already resulting in an item == null + * @module gestures + */ + /** + * Touch stays at the same place for x time * - * @param {Event} event - * @private + * @class Hold + * @static */ - ItemSet.prototype._onTouch = function (event) { - // store the touched item, used in _onDragStart - this.touchParams.item = ItemSet.itemFromTarget(event); - }; - /** - * Start dragging the selected events - * @param {Event} event - * @private + * @event hold + * @param {Object} ev */ - ItemSet.prototype._onDragStart = function (event) { - if (!this.options.editable.updateTime && !this.options.editable.updateGroup) { - return; - } - - var item = this.touchParams.item || null; - var me = this; - var props; - if (item && item.selected) { - var dragLeftItem = event.target.dragLeftItem; - var dragRightItem = event.target.dragRightItem; + /** + * @param {String} name + */ + (function(name) { + var timer; - if (dragLeftItem) { - props = { - item: dragLeftItem, - initialX: event.gesture.center.clientX - }; + function holdGesture(ev, inst) { + var options = inst.options, + current = Detection.current; - if (me.options.editable.updateTime) { - props.start = item.data.start.valueOf(); - } - if (me.options.editable.updateGroup) { - if ('group' in item.data) props.group = item.data.group; - } + switch(ev.eventType) { + case EVENT_START: + clearTimeout(timer); - this.touchParams.itemProps = [props]; - } - else if (dragRightItem) { - props = { - item: dragRightItem, - initialX: event.gesture.center.clientX - }; + // set the gesture so we can check in the timeout if it still is + current.name = name; - if (me.options.editable.updateTime) { - props.end = item.data.end.valueOf(); - } - if (me.options.editable.updateGroup) { - if ('group' in item.data) props.group = item.data.group; - } + // set timer and if after the timeout it still is hold, + // we trigger the hold event + timer = setTimeout(function() { + if(current && current.name == name) { + inst.trigger(name, ev); + } + }, options.holdTimeout); + break; - this.touchParams.itemProps = [props]; - } - else { - this.touchParams.itemProps = this.getSelection().map(function (id) { - var item = me.items[id]; - var props = { - item: item, - initialX: event.gesture.center.clientX - }; + case EVENT_MOVE: + if(ev.distance > options.holdThreshold) { + clearTimeout(timer); + } + break; - if (me.options.editable.updateTime) { - if ('start' in item.data) props.start = item.data.start.valueOf(); - if ('end' in item.data) props.end = item.data.end.valueOf(); - } - if (me.options.editable.updateGroup) { - if ('group' in item.data) props.group = item.data.group; + case EVENT_RELEASE: + clearTimeout(timer); + break; } - - return props; - }); } - event.stopPropagation(); - } - }; + Hammer.gestures.Hold = { + name: name, + index: 10, + defaults: { + /** + * @property holdTimeout + * @type {Number} + * @default 500 + */ + holdTimeout: 500, + + /** + * movement allowed while holding + * @property holdThreshold + * @type {Number} + * @default 2 + */ + holdThreshold: 2 + }, + handler: holdGesture + }; + })('hold'); /** - * Drag selected items - * @param {Event} event - * @private + * @module gestures */ - ItemSet.prototype._onDrag = function (event) { - event.preventDefault() - - if (this.touchParams.itemProps) { - var me = this; - var snap = this.body.util.snap || null; - var xOffset = this.body.dom.root.offsetLeft + this.body.domProps.left.width; - - // move - this.touchParams.itemProps.forEach(function (props) { - var newProps = {}; - var current = me.body.util.toTime(event.gesture.center.clientX - xOffset); - var initial = me.body.util.toTime(props.initialX - xOffset); - var offset = current - initial; - - if ('start' in props) { - var start = new Date(props.start + offset); - newProps.start = snap ? snap(start) : start; - } - - if ('end' in props) { - var end = new Date(props.end + offset); - newProps.end = snap ? snap(end) : end; - } - - if ('group' in props) { - // drag from one group to another - var group = ItemSet.groupFromTarget(event); - newProps.group = group && group.groupId; - } - - // confirm moving the item - var itemData = util.extend({}, props.item.data, newProps); - me.options.onMoving(itemData, function (itemData) { - if (itemData) { - me._updateItemProps(props.item, itemData); + /** + * when a touch is being released from the page + * + * @class Release + * @static + */ + /** + * @event release + * @param {Object} ev + */ + Hammer.gestures.Release = { + name: 'release', + index: Infinity, + handler: function releaseGesture(ev, inst) { + if(ev.eventType == EVENT_RELEASE) { + inst.trigger(this.name, ev); } - }); - }); - - this.stackDirty = true; // force re-stacking of all items next redraw - this.body.emitter.emit('change'); - - event.stopPropagation(); - } + } }; /** - * Update an items properties - * @param {Item} item - * @param {Object} props Can contain properties start, end, and group. - * @private + * @module gestures */ - ItemSet.prototype._updateItemProps = function(item, props) { - // TODO: copy all properties from props to item? (also new ones) - if ('start' in props) item.data.start = props.start; - if ('end' in props) item.data.end = props.end; - if ('group' in props && item.data.group != props.group) { - this._moveToGroup(item, props.group) - } - }; - /** - * Move an item to another group - * @param {Item} item - * @param {String | Number} groupId - * @private + * triggers swipe events when the end velocity is above the threshold + * for best usage, set `preventDefault` (on the drag gesture) to `true` + * ```` + * hammertime.on("dragleft swipeleft", function(ev) { + * console.log(ev); + * ev.gesture.preventDefault(); + * }); + * ```` + * + * @class Swipe + * @static */ - ItemSet.prototype._moveToGroup = function(item, groupId) { - var group = this.groups[groupId]; - if (group && group.groupId != item.data.group) { - var oldGroup = item.parent; - oldGroup.remove(item); - oldGroup.order(); - group.add(item); - group.order(); - - item.data.group = group.groupId; - } - }; - /** - * End of dragging selected items - * @param {Event} event - * @private + * @event swipe + * @param {Object} ev */ - ItemSet.prototype._onDragEnd = function (event) { - event.preventDefault() - - if (this.touchParams.itemProps) { - // prepare a change set for the changed items - var changes = [], - me = this, - dataset = this.itemsData.getDataSet(); - - var itemProps = this.touchParams.itemProps ; - this.touchParams.itemProps = null; - itemProps.forEach(function (props) { - var id = props.item.id, - itemData = me.itemsData.get(id, me.itemOptions); - - var changed = false; - if ('start' in props.item.data) { - changed = (props.start != props.item.data.start.valueOf()); - itemData.start = util.convert(props.item.data.start, - dataset._options.type && dataset._options.type.start || 'Date'); - } - if ('end' in props.item.data) { - changed = changed || (props.end != props.item.data.end.valueOf()); - itemData.end = util.convert(props.item.data.end, - dataset._options.type && dataset._options.type.end || 'Date'); - } - if ('group' in props.item.data) { - changed = changed || (props.group != props.item.data.group); - itemData.group = props.item.data.group; - } - - // only apply changes when start or end is actually changed - if (changed) { - me.options.onMove(itemData, function (itemData) { - if (itemData) { - // apply changes - itemData[dataset._fieldId] = id; // ensure the item contains its id (can be undefined) - changes.push(itemData); - } - else { - // restore original values - me._updateItemProps(props.item, props); - - me.stackDirty = true; // force re-stacking of all items next redraw - me.body.emitter.emit('change'); - } - }); - } - }); - - // apply the changes to the data (if there are changes) - if (changes.length) { - dataset.update(changes); - } - - event.stopPropagation(); - } - }; - /** - * Handle selecting/deselecting an item when tapping it - * @param {Event} event - * @private + * @event swipeleft + * @param {Object} ev */ - ItemSet.prototype._onSelectItem = function (event) { - if (!this.options.selectable) return; - - var ctrlKey = event.gesture.srcEvent && event.gesture.srcEvent.ctrlKey; - var shiftKey = event.gesture.srcEvent && event.gesture.srcEvent.shiftKey; - if (ctrlKey || shiftKey) { - this._onMultiSelectItem(event); - return; - } - - var oldSelection = this.getSelection(); - - var item = ItemSet.itemFromTarget(event); - var selection = item ? [item.id] : []; - this.setSelection(selection); - - var newSelection = this.getSelection(); - - // emit a select event, - // except when old selection is empty and new selection is still empty - if (newSelection.length > 0 || oldSelection.length > 0) { - this.body.emitter.emit('select', { - items: newSelection - }); - } - }; - /** - * Handle creation and updates of an item on double tap - * @param event - * @private + * @event swiperight + * @param {Object} ev */ - ItemSet.prototype._onAddItem = function (event) { - if (!this.options.selectable) return; - if (!this.options.editable.add) return; + /** + * @event swipeup + * @param {Object} ev + */ + /** + * @event swipedown + * @param {Object} ev + */ + Hammer.gestures.Swipe = { + name: 'swipe', + index: 40, + defaults: { + /** + * @property swipeMinTouches + * @type {Number} + * @default 1 + */ + swipeMinTouches: 1, - var me = this, - snap = this.body.util.snap || null, - item = ItemSet.itemFromTarget(event); + /** + * @property swipeMaxTouches + * @type {Number} + * @default 1 + */ + swipeMaxTouches: 1, - if (item) { - // update item + /** + * horizontal swipe velocity + * @property swipeVelocityX + * @type {Number} + * @default 0.6 + */ + swipeVelocityX: 0.6, - // execute async handler to update the item (or cancel it) - var itemData = me.itemsData.get(item.id); // get a clone of the data from the dataset - this.options.onUpdate(itemData, function (itemData) { - if (itemData) { - me.itemsData.getDataSet().update(itemData); - } - }); - } - else { - // add item - var xAbs = util.getAbsoluteLeft(this.dom.frame); - var x = event.gesture.center.pageX - xAbs; - var start = this.body.util.toTime(x); - var newItem = { - start: snap ? snap(start) : start, - content: 'new item' - }; + /** + * vertical swipe velocity + * @property swipeVelocityY + * @type {Number} + * @default 0.6 + */ + swipeVelocityY: 0.6 + }, - // when default type is a range, add a default end date to the new item - if (this.options.type === 'range') { - var end = this.body.util.toTime(x + this.props.width / 5); - newItem.end = snap ? snap(end) : end; - } + handler: function swipeGesture(ev, inst) { + if(ev.eventType == EVENT_RELEASE) { + var touches = ev.touches.length, + options = inst.options; - newItem[this.itemsData._fieldId] = util.randomUUID(); + // max touches + if(touches < options.swipeMinTouches || + touches > options.swipeMaxTouches) { + return; + } - var group = ItemSet.groupFromTarget(event); - if (group) { - newItem.group = group.groupId; + // when the distance we moved is too small we skip this gesture + // or we can be already in dragging + if(ev.velocityX > options.swipeVelocityX || + ev.velocityY > options.swipeVelocityY) { + // trigger swipe events + inst.trigger(this.name, ev); + inst.trigger(this.name + ev.direction, ev); + } + } } - - // execute async handler to customize (or cancel) adding an item - this.options.onAdd(newItem, function (item) { - if (item) { - me.itemsData.getDataSet().add(item); - // TODO: need to trigger a redraw? - } - }); - } }; /** - * Handle selecting/deselecting multiple items when holding an item - * @param {Event} event - * @private + * @module gestures + */ + /** + * Single tap and a double tap on a place + * + * @class Tap + * @static + */ + /** + * @event tap + * @param {Object} ev + */ + /** + * @event doubletap + * @param {Object} ev */ - ItemSet.prototype._onMultiSelectItem = function (event) { - if (!this.options.selectable) return; - var selection, - item = ItemSet.itemFromTarget(event); + /** + * @param {String} name + */ + (function(name) { + var hasMoved = false; - if (item) { - // multi select items - selection = this.getSelection(); // current selection + function tapGesture(ev, inst) { + var options = inst.options, + current = Detection.current, + prev = Detection.previous, + sincePrev, + didDoubleTap; - var shiftKey = event.gesture.touches[0] && event.gesture.touches[0].shiftKey || false; - if (shiftKey) { - // select all items between the old selection and the tapped item + switch(ev.eventType) { + case EVENT_START: + hasMoved = false; + break; - // determine the selection range - selection.push(item.id); - var range = ItemSet._getItemRange(this.itemsData.get(selection, this.itemOptions)); + case EVENT_MOVE: + hasMoved = hasMoved || (ev.distance > options.tapMaxDistance); + break; - // select all items within the selection range - selection = []; - for (var id in this.items) { - if (this.items.hasOwnProperty(id)) { - var _item = this.items[id]; - var start = _item.data.start; - var end = (_item.data.end !== undefined) ? _item.data.end : start; + case EVENT_END: + if(!Utils.inStr(ev.srcEvent.type, 'cancel') && ev.deltaTime < options.tapMaxTime && !hasMoved) { + // previous gesture, for the double tap since these are two different gesture detections + sincePrev = prev && prev.lastEvent && ev.timeStamp - prev.lastEvent.timeStamp; + didDoubleTap = false; - if (start >= range.min && end <= range.max) { - selection.push(_item.id); // do not use id but item.id, id itself is stringified - } + // check if double tap + if(prev && prev.name == name && + (sincePrev && sincePrev < options.doubleTapInterval) && + ev.distance < options.doubleTapDistance) { + inst.trigger('doubletap', ev); + didDoubleTap = true; + } + + // do a single tap + if(!didDoubleTap || options.tapAlways) { + current.name = name; + inst.trigger(current.name, ev); + } + } + break; } - } - } - else { - // add/remove this item from the current selection - var index = selection.indexOf(item.id); - if (index == -1) { - // item is not yet selected -> select it - selection.push(item.id); - } - else { - // item is already selected -> deselect it - selection.splice(index, 1); - } } - this.setSelection(selection); + Hammer.gestures.Tap = { + name: name, + index: 100, + handler: tapGesture, + defaults: { + /** + * max time of a tap, this is for the slow tappers + * @property tapMaxTime + * @type {Number} + * @default 250 + */ + tapMaxTime: 250, - this.body.emitter.emit('select', { - items: this.getSelection() - }); - } - }; + /** + * max distance of movement of a tap, this is for the slow tappers + * @property tapMaxDistance + * @type {Number} + * @default 10 + */ + tapMaxDistance: 10, + + /** + * always trigger the `tap` event, even while double-tapping + * @property tapAlways + * @type {Boolean} + * @default true + */ + tapAlways: true, + + /** + * max distance between two taps + * @property doubleTapDistance + * @type {Number} + * @default 20 + */ + doubleTapDistance: 20, + + /** + * max time between two taps + * @property doubleTapInterval + * @type {Number} + * @default 300 + */ + doubleTapInterval: 300 + } + }; + })('tap'); /** - * Calculate the time range of a list of items - * @param {Array.} itemsData - * @return {{min: Date, max: Date}} Returns the range of the provided items - * @private + * @module gestures */ - ItemSet._getItemRange = function(itemsData) { - var max = null; - var min = null; + /** + * when a touch is being touched at the page + * + * @class Touch + * @static + */ + /** + * @event touch + * @param {Object} ev + */ + Hammer.gestures.Touch = { + name: 'touch', + index: -Infinity, + defaults: { + /** + * call preventDefault at touchstart, and makes the element blocking by disabling the scrolling of the page, + * but it improves gestures like transforming and dragging. + * be careful with using this, it can be very annoying for users to be stuck on the page + * @property preventDefault + * @type {Boolean} + * @default false + */ + preventDefault: false, - itemsData.forEach(function (data) { - if (min == null || data.start < min) { - min = data.start; - } + /** + * disable mouse events, so only touch (or pen!) input triggers events + * @property preventMouse + * @type {Boolean} + * @default false + */ + preventMouse: false + }, + handler: function touchGesture(ev, inst) { + if(inst.options.preventMouse && ev.pointerType == POINTER_MOUSE) { + ev.stopDetect(); + return; + } - if (data.end != undefined) { - if (max == null || data.end > max) { - max = data.end; - } - } - else { - if (max == null || data.start > max) { - max = data.start; - } - } - }); + if(inst.options.preventDefault) { + ev.preventDefault(); + } - return { - min: min, - max: max - } + if(ev.eventType == EVENT_TOUCH) { + inst.trigger('touch', ev); + } + } }; /** - * Find an item from an event target: - * searches for the attribute 'timeline-item' in the event target's element tree - * @param {Event} event - * @return {Item | null} item + * @module gestures */ - ItemSet.itemFromTarget = function(event) { - var target = event.target; - while (target) { - if (target.hasOwnProperty('timeline-item')) { - return target['timeline-item']; - } - target = target.parentNode; - } - - return null; - }; - /** - * Find the Group from an event target: - * searches for the attribute 'timeline-group' in the event target's element tree - * @param {Event} event - * @return {Group | null} group + * User want to scale or rotate with 2 fingers + * Preventing the default browser behavior is a good way to improve feel and working. This can be done with the + * `preventDefault` option. + * + * @class Transform + * @static + */ + /** + * @event transform + * @param {Object} ev + */ + /** + * @event transformstart + * @param {Object} ev + */ + /** + * @event transformend + * @param {Object} ev + */ + /** + * @event pinchin + * @param {Object} ev + */ + /** + * @event pinchout + * @param {Object} ev + */ + /** + * @event rotate + * @param {Object} ev */ - ItemSet.groupFromTarget = function(event) { - var target = event.target; - while (target) { - if (target.hasOwnProperty('timeline-group')) { - return target['timeline-group']; - } - target = target.parentNode; - } - - return null; - }; /** - * Find the ItemSet from an event target: - * searches for the attribute 'timeline-itemset' in the event target's element tree - * @param {Event} event - * @return {ItemSet | null} item + * @param {String} name */ - ItemSet.itemSetFromTarget = function(event) { - var target = event.target; - while (target) { - if (target.hasOwnProperty('timeline-itemset')) { - return target['timeline-itemset']; - } - target = target.parentNode; - } + (function(name) { + var triggered = false; - return null; - }; + function transformGesture(ev, inst) { + switch(ev.eventType) { + case EVENT_START: + triggered = false; + break; - module.exports = ItemSet; + case EVENT_MOVE: + // at least multitouch + if(ev.touches.length < 2) { + return; + } + var scaleThreshold = Math.abs(1 - ev.scale); + var rotationThreshold = Math.abs(ev.rotation); -/***/ }, -/* 28 */ -/***/ function(module, exports, __webpack_require__) { + // when the distance we moved is too small we skip this gesture + // or we can be already in dragging + if(scaleThreshold < inst.options.transformMinScale && + rotationThreshold < inst.options.transformMinRotation) { + return; + } - var util = __webpack_require__(1); - var DOMutil = __webpack_require__(2); - var Component = __webpack_require__(20); + // we are transforming! + Detection.current.name = name; - /** - * Legend for Graph2d - */ - function Legend(body, options, side, linegraphOptions) { - this.body = body; - this.defaultOptions = { - enabled: true, - icons: true, - iconSize: 20, - iconSpacing: 6, - left: { - visible: true, - position: 'top-left' // top/bottom - left,center,right - }, - right: { - visible: true, - position: 'top-left' // top/bottom - left,center,right + // first time, trigger dragstart event + if(!triggered) { + inst.trigger(name + 'start', ev); + triggered = true; + } + + inst.trigger(name, ev); // basic transform event + + // trigger rotate event + if(rotationThreshold > inst.options.transformMinRotation) { + inst.trigger('rotate', ev); + } + + // trigger pinch event + if(scaleThreshold > inst.options.transformMinScale) { + inst.trigger('pinch', ev); + inst.trigger('pinch' + (ev.scale < 1 ? 'in' : 'out'), ev); + } + break; + + case EVENT_RELEASE: + if(triggered && ev.changedLength < 2) { + inst.trigger(name + 'end', ev); + triggered = false; + } + break; + } } - } - this.side = side; - this.options = util.extend({},this.defaultOptions); - this.linegraphOptions = linegraphOptions; - this.svgElements = {}; - this.dom = {}; - this.groups = {}; - this.amountOfGroups = 0; - this._create(); + Hammer.gestures.Transform = { + name: name, + index: 45, + defaults: { + /** + * minimal scale factor, no scale is 1, zoomin is to 0 and zoomout until higher then 1 + * @property transformMinScale + * @type {Number} + * @default 0.01 + */ + transformMinScale: 0.01, - this.setOptions(options); - } + /** + * rotation in degrees + * @property transformMinRotation + * @type {Number} + * @default 1 + */ + transformMinRotation: 1 + }, - Legend.prototype = new Component(); + handler: transformGesture + }; + })('transform'); - Legend.prototype.clear = function() { - this.groups = {}; - this.amountOfGroups = 0; + /** + * @module hammer + */ + + // AMD export + if(true) { + !(__WEBPACK_AMD_DEFINE_RESULT__ = function() { + return Hammer; + }.call(exports, __webpack_require__, exports, module), __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__)); + // commonjs export + } else if(typeof module !== 'undefined' && module.exports) { + module.exports = Hammer; + // browser export + } else { + window.Hammer = Hammer; } - Legend.prototype.addGroup = function(label, graphOptions) { + })(window); - if (!this.groups.hasOwnProperty(label)) { - this.groups[label] = graphOptions; - } - this.amountOfGroups += 1; - }; +/***/ }, +/* 21 */ +/***/ function(module, exports, __webpack_require__) { - Legend.prototype.updateGroup = function(label, graphOptions) { - this.groups[label] = graphOptions; - }; + var util = __webpack_require__(1); + var hammerUtil = __webpack_require__(22); + var moment = __webpack_require__(2); + var Component = __webpack_require__(23); + var DateUtil = __webpack_require__(24); - Legend.prototype.removeGroup = function(label) { - if (this.groups.hasOwnProperty(label)) { - delete this.groups[label]; - this.amountOfGroups -= 1; - } - }; + /** + * @constructor Range + * A Range controls a numeric range with a start and end value. + * The Range adjusts the range based on mouse events or programmatic changes, + * and triggers events when the range is changing or has been changed. + * @param {{dom: Object, domProps: Object, emitter: Emitter}} body + * @param {Object} [options] See description at Range.setOptions + */ + function Range(body, options) { + var now = moment().hours(0).minutes(0).seconds(0).milliseconds(0); + this.start = now.clone().add(-3, 'days').valueOf(); // Number + this.end = now.clone().add(4, 'days').valueOf(); // Number - Legend.prototype._create = function() { - this.dom.frame = document.createElement('div'); - this.dom.frame.className = 'legend'; - this.dom.frame.style.position = "absolute"; - this.dom.frame.style.top = "10px"; - this.dom.frame.style.display = "block"; + this.body = body; + this.deltaDifference = 0; + this.scaleOffset = 0; + this.startToFront = false; + this.endToFront = true; - this.dom.textArea = document.createElement('div'); - this.dom.textArea.className = 'legendText'; - this.dom.textArea.style.position = "relative"; - this.dom.textArea.style.top = "0px"; + // default options + this.defaultOptions = { + start: null, + end: null, + direction: 'horizontal', // 'horizontal' or 'vertical' + moveable: true, + zoomable: true, + min: null, + max: null, + zoomMin: 10, // milliseconds + zoomMax: 1000 * 60 * 60 * 24 * 365 * 10000 // milliseconds + }; + this.options = util.extend({}, this.defaultOptions); - this.svg = document.createElementNS('http://www.w3.org/2000/svg',"svg"); - this.svg.style.position = 'absolute'; - this.svg.style.top = 0 +'px'; - this.svg.style.width = this.options.iconSize + 5 + 'px'; - this.svg.style.height = '100%'; + this.props = { + touch: {} + }; + this.animateTimer = null; - this.dom.frame.appendChild(this.svg); - this.dom.frame.appendChild(this.dom.textArea); - }; + // drag listeners for dragging + this.body.emitter.on('dragstart', this._onDragStart.bind(this)); + this.body.emitter.on('drag', this._onDrag.bind(this)); + this.body.emitter.on('dragend', this._onDragEnd.bind(this)); + + // ignore dragging when holding + this.body.emitter.on('hold', this._onHold.bind(this)); + + // mouse wheel for zooming + this.body.emitter.on('mousewheel', this._onMouseWheel.bind(this)); + this.body.emitter.on('DOMMouseScroll', this._onMouseWheel.bind(this)); // For FF + + // pinch to zoom + this.body.emitter.on('touch', this._onTouch.bind(this)); + this.body.emitter.on('pinch', this._onPinch.bind(this)); + + this.setOptions(options); + } + + Range.prototype = new Component(); /** - * Hide the component from the DOM + * Set options for the range controller + * @param {Object} options Available options: + * {Number | Date | String} start Start date for the range + * {Number | Date | String} end End date for the range + * {Number} min Minimum value for start + * {Number} max Maximum value for end + * {Number} zoomMin Set a minimum value for + * (end - start). + * {Number} zoomMax Set a maximum value for + * (end - start). + * {Boolean} moveable Enable moving of the range + * by dragging. True by default + * {Boolean} zoomable Enable zooming of the range + * by pinching/scrolling. True by default */ - Legend.prototype.hide = function() { - // remove the frame containing the items - if (this.dom.frame.parentNode) { - this.dom.frame.parentNode.removeChild(this.dom.frame); + Range.prototype.setOptions = function (options) { + if (options) { + // copy the options that we know + var fields = ['direction', 'min', 'max', 'zoomMin', 'zoomMax', 'moveable', 'zoomable', 'activate', 'hiddenDates']; + util.selectiveExtend(fields, this.options, options); + + if ('start' in options || 'end' in options) { + // apply a new range. both start and end are optional + this.setRange(options.start, options.end); + } } }; /** - * Show the component in the DOM (when not already visible). - * @return {Boolean} changed + * Test whether direction has a valid value + * @param {String} direction 'horizontal' or 'vertical' */ - Legend.prototype.show = function() { - // show frame containing the items - if (!this.dom.frame.parentNode) { - this.body.dom.center.appendChild(this.dom.frame); + function validateDirection (direction) { + if (direction != 'horizontal' && direction != 'vertical') { + throw new TypeError('Unknown direction "' + direction + '". ' + + 'Choose "horizontal" or "vertical".'); } - }; + } - Legend.prototype.setOptions = function(options) { - var fields = ['enabled','orientation','icons','left','right']; - util.selectiveDeepExtend(fields, this.options, options); - }; + /** + * Set a new start and end range + * @param {Date | Number | String} [start] + * @param {Date | Number | String} [end] + * @param {boolean | number} [animate=false] If true, the range is animated + * smoothly to the new window. + * If animate is a number, the + * number is taken as duration + * Default duration is 500 ms. + * @param {Boolean} [byUser=false] + * + */ + Range.prototype.setRange = function(start, end, animate, byUser) { + if (byUser !== true) { + byUser = false; + } + var _start = start != undefined ? util.convert(start, 'Date').valueOf() : null; + var _end = end != undefined ? util.convert(end, 'Date').valueOf() : null; + this._cancelAnimation(); - Legend.prototype.redraw = function() { - var activeGroups = 0; - for (var groupId in this.groups) { - if (this.groups.hasOwnProperty(groupId)) { - if (this.groups[groupId].visible == true && (this.linegraphOptions.visibility[groupId] === undefined || this.linegraphOptions.visibility[groupId] == true)) { - activeGroups++; + if (animate) { + var me = this; + var initStart = this.start; + var initEnd = this.end; + var duration = typeof animate === 'number' ? animate : 500; + var initTime = new Date().valueOf(); + var anyChanged = false; + + var next = function () { + if (!me.props.touch.dragging) { + var now = new Date().valueOf(); + var time = now - initTime; + var done = time > duration; + var s = (done || _start === null) ? _start : util.easeInOutQuad(time, initStart, _start, duration); + var e = (done || _end === null) ? _end : util.easeInOutQuad(time, initEnd, _end, duration); + + changed = me._applyRange(s, e); + DateUtil.updateHiddenDates(me.body, me.options.hiddenDates); + anyChanged = anyChanged || changed; + if (changed) { + me.body.emitter.emit('rangechange', {start: new Date(me.start), end: new Date(me.end), byUser:byUser}); + } + + if (done) { + if (anyChanged) { + me.body.emitter.emit('rangechanged', {start: new Date(me.start), end: new Date(me.end), byUser:byUser}); + } + } + else { + // animate with as high as possible frame rate, leave 20 ms in between + // each to prevent the browser from blocking + me.animateTimer = setTimeout(next, 20); + } } } - } - if (this.options[this.side].visible == false || this.amountOfGroups == 0 || this.options.enabled == false || activeGroups == 0) { - this.hide(); + return next(); } else { - this.show(); - if (this.options[this.side].position == 'top-left' || this.options[this.side].position == 'bottom-left') { - this.dom.frame.style.left = '4px'; - this.dom.frame.style.textAlign = "left"; - this.dom.textArea.style.textAlign = "left"; - this.dom.textArea.style.left = (this.options.iconSize + 15) + 'px'; - this.dom.textArea.style.right = ''; - this.svg.style.left = 0 +'px'; - this.svg.style.right = ''; - } - else { - this.dom.frame.style.right = '4px'; - this.dom.frame.style.textAlign = "right"; - this.dom.textArea.style.textAlign = "right"; - this.dom.textArea.style.right = (this.options.iconSize + 15) + 'px'; - this.dom.textArea.style.left = ''; - this.svg.style.right = 0 +'px'; - this.svg.style.left = ''; + var changed = this._applyRange(_start, _end); + DateUtil.updateHiddenDates(this.body, this.options.hiddenDates); + if (changed) { + var params = {start: new Date(this.start), end: new Date(this.end), byUser:byUser}; + this.body.emitter.emit('rangechange', params); + this.body.emitter.emit('rangechanged', params); } + } + }; - if (this.options[this.side].position == 'top-left' || this.options[this.side].position == 'top-right') { - this.dom.frame.style.top = 4 - Number(this.body.dom.center.style.top.replace("px","")) + 'px'; - this.dom.frame.style.bottom = ''; - } - else { - var scrollableHeight = this.body.domProps.center.height - this.body.domProps.centerContainer.height; - this.dom.frame.style.bottom = 4 + scrollableHeight + Number(this.body.dom.center.style.top.replace("px","")) + 'px'; - this.dom.frame.style.top = ''; - } + /** + * Stop an animation + * @private + */ + Range.prototype._cancelAnimation = function () { + if (this.animateTimer) { + clearTimeout(this.animateTimer); + this.animateTimer = null; + } + }; - if (this.options.icons == false) { - this.dom.frame.style.width = this.dom.textArea.offsetWidth + 10 + 'px'; - this.dom.textArea.style.right = ''; - this.dom.textArea.style.left = ''; - this.svg.style.width = '0px'; - } - else { - this.dom.frame.style.width = this.options.iconSize + 15 + this.dom.textArea.offsetWidth + 10 + 'px' - this.drawLegendIcons(); - } + /** + * Set a new start and end range. This method is the same as setRange, but + * does not trigger a range change and range changed event, and it returns + * true when the range is changed + * @param {Number} [start] + * @param {Number} [end] + * @return {Boolean} changed + * @private + */ + Range.prototype._applyRange = function(start, end) { + var newStart = (start != null) ? util.convert(start, 'Date').valueOf() : this.start, + newEnd = (end != null) ? util.convert(end, 'Date').valueOf() : this.end, + max = (this.options.max != null) ? util.convert(this.options.max, 'Date').valueOf() : null, + min = (this.options.min != null) ? util.convert(this.options.min, 'Date').valueOf() : null, + diff; - var content = ''; - for (var groupId in this.groups) { - if (this.groups.hasOwnProperty(groupId)) { - if (this.groups[groupId].visible == true && (this.linegraphOptions.visibility[groupId] === undefined || this.linegraphOptions.visibility[groupId] == true)) { - content += this.groups[groupId].content + '
'; + // check for valid number + if (isNaN(newStart) || newStart === null) { + throw new Error('Invalid start "' + start + '"'); + } + if (isNaN(newEnd) || newEnd === null) { + throw new Error('Invalid end "' + end + '"'); + } + + // prevent start < end + if (newEnd < newStart) { + newEnd = newStart; + } + + // prevent start < min + if (min !== null) { + if (newStart < min) { + diff = (min - newStart); + newStart += diff; + newEnd += diff; + + // prevent end > max + if (max != null) { + if (newEnd > max) { + newEnd = max; } } } - this.dom.textArea.innerHTML = content; - this.dom.textArea.style.lineHeight = ((0.75 * this.options.iconSize) + this.options.iconSpacing) + 'px'; } - }; - - Legend.prototype.drawLegendIcons = function() { - if (this.dom.frame.parentNode) { - DOMutil.prepareElements(this.svgElements); - var padding = window.getComputedStyle(this.dom.frame).paddingTop; - var iconOffset = Number(padding.replace('px','')); - var x = iconOffset; - var iconWidth = this.options.iconSize; - var iconHeight = 0.75 * this.options.iconSize; - var y = iconOffset + 0.5 * iconHeight + 3; - this.svg.style.width = iconWidth + 5 + iconOffset + 'px'; + // prevent end > max + if (max !== null) { + if (newEnd > max) { + diff = (newEnd - max); + newStart -= diff; + newEnd -= diff; - for (var groupId in this.groups) { - if (this.groups.hasOwnProperty(groupId)) { - if (this.groups[groupId].visible == true && (this.linegraphOptions.visibility[groupId] === undefined || this.linegraphOptions.visibility[groupId] == true)) { - this.groups[groupId].drawIcon(x, y, this.svgElements, this.svg, iconWidth, iconHeight); - y += iconHeight + this.options.iconSpacing; + // prevent start < min + if (min != null) { + if (newStart < min) { + newStart = min; } } } + } - DOMutil.cleanupElements(this.svgElements); + // prevent (end-start) < zoomMin + if (this.options.zoomMin !== null) { + var zoomMin = parseFloat(this.options.zoomMin); + if (zoomMin < 0) { + zoomMin = 0; + } + if ((newEnd - newStart) < zoomMin) { + if ((this.end - this.start) === zoomMin) { + // ignore this action, we are already zoomed to the minimum + newStart = this.start; + newEnd = this.end; + } + else { + // zoom to the minimum + diff = (zoomMin - (newEnd - newStart)); + newStart -= diff / 2; + newEnd += diff / 2; + } + } } - }; - module.exports = Legend; + // prevent (end-start) > zoomMax + if (this.options.zoomMax !== null) { + var zoomMax = parseFloat(this.options.zoomMax); + if (zoomMax < 0) { + zoomMax = 0; + } + if ((newEnd - newStart) > zoomMax) { + if ((this.end - this.start) === zoomMax) { + // ignore this action, we are already zoomed to the maximum + newStart = this.start; + newEnd = this.end; + } + else { + // zoom to the maximum + diff = ((newEnd - newStart) - zoomMax); + newStart += diff / 2; + newEnd -= diff / 2; + } + } + } + var changed = (this.start != newStart || this.end != newEnd); -/***/ }, -/* 29 */ -/***/ function(module, exports, __webpack_require__) { + // if the new range does NOT overlap with the old range, emit checkRangedItems to avoid not showing ranged items (ranged meaning has end time, not neccesarily of type Range) + if (!((newStart >= this.start && newStart <= this.end) || (newEnd >= this.start && newEnd <= this.end)) && + !((this.start >= newStart && this.start <= newEnd) || (this.end >= newStart && this.end <= newEnd) )) { + this.body.emitter.emit('checkRangedItems'); + } - var util = __webpack_require__(1); - var DOMutil = __webpack_require__(2); - var DataSet = __webpack_require__(3); - var DataView = __webpack_require__(4); - var Component = __webpack_require__(20); - var DataAxis = __webpack_require__(23); - var GraphGroup = __webpack_require__(24); - var Legend = __webpack_require__(28); - var BarGraphFunctions = __webpack_require__(52); + this.start = newStart; + this.end = newEnd; + return changed; + }; - var UNGROUPED = '__ungrouped__'; // reserved group id for ungrouped items + /** + * Retrieve the current range. + * @return {Object} An object with start and end properties + */ + Range.prototype.getRange = function() { + return { + start: this.start, + end: this.end + }; + }; /** - * This is the constructor of the LineGraph. It requires a Timeline body and options. - * - * @param body - * @param options - * @constructor + * Calculate the conversion offset and scale for current range, based on + * the provided width + * @param {Number} width + * @returns {{offset: number, scale: number}} conversion */ - function LineGraph(body, options) { - this.id = util.randomUUID(); - this.body = body; + Range.prototype.conversion = function (width, totalHidden) { + return Range.conversion(this.start, this.end, width, totalHidden); + }; - this.defaultOptions = { - yAxisOrientation: 'left', - defaultGroup: 'default', - sort: true, - sampling: true, - graphHeight: '400px', - shaded: { - enabled: false, - orientation: 'bottom' // top, bottom - }, - style: 'line', // line, bar - barChart: { - width: 50, - handleOverlap: 'overlap', - align: 'center' // left, center, right - }, - catmullRom: { - enabled: true, - parametrization: 'centripetal', // uniform (alpha = 0.0), chordal (alpha = 1.0), centripetal (alpha = 0.5) - alpha: 0.5 - }, - drawPoints: { - enabled: true, - size: 6, - style: 'square' // square, circle - }, - dataAxis: { - showMinorLabels: true, - showMajorLabels: true, - icons: false, - width: '40px', - visible: true, - alignZeros: true, - customRange: { - left: {min:undefined, max:undefined}, - right: {min:undefined, max:undefined} - } - //, these options are not set by default, but this shows the format they will be in - //format: { - // left: {decimals: 2}, - // right: {decimals: 2} - //}, - //title: { - // left: { - // text: 'left', - // style: 'color:black;' - // }, - // right: { - // text: 'right', - // style: 'color:black;' - // } - //} - }, - legend: { - enabled: false, - icons: true, - left: { - visible: true, - position: 'top-left' // top/bottom - left,right - }, - right: { - visible: true, - position: 'top-right' // top/bottom - left,right - } - }, - groups: { - visibility: {} + /** + * Static method to calculate the conversion offset and scale for a range, + * based on the provided start, end, and width + * @param {Number} start + * @param {Number} end + * @param {Number} width + * @returns {{offset: number, scale: number}} conversion + */ + Range.conversion = function (start, end, width, totalHidden) { + if (totalHidden === undefined) { + totalHidden = 0; + } + if (width != 0 && (end - start != 0)) { + return { + offset: start, + scale: width / (end - start - totalHidden) } - }; + } + else { + return { + offset: 0, + scale: 1 + }; + } + }; - // options is shared by this ItemSet and all its items - this.options = util.extend({}, this.defaultOptions); - this.dom = {}; - this.props = {}; - this.hammer = null; - this.groups = {}; - this.abortedGraphUpdate = false; - this.updateSVGheight = false; - this.updateSVGheightOnResize = false; + /** + * Start dragging horizontally or vertically + * @param {Event} event + * @private + */ + Range.prototype._onDragStart = function(event) { + this.deltaDifference = 0; + this.previousDelta = 0; + // only allow dragging when configured as movable + if (!this.options.moveable) return; - var me = this; - this.itemsData = null; // DataSet - this.groupsData = null; // DataSet + // refuse to drag when we where pinching to prevent the timeline make a jump + // when releasing the fingers in opposite order from the touch screen + if (!this.props.touch.allowDragging) return; - // listeners for the DataSet of the items - this.itemListeners = { - 'add': function (event, params, senderId) { - me._onAdd(params.items); - }, - 'update': function (event, params, senderId) { - me._onUpdate(params.items); - }, - 'remove': function (event, params, senderId) { - me._onRemove(params.items); - } - }; + this.props.touch.start = this.start; + this.props.touch.end = this.end; + this.props.touch.dragging = true; - // listeners for the DataSet of the groups - this.groupListeners = { - 'add': function (event, params, senderId) { - me._onAddGroups(params.items); - }, - 'update': function (event, params, senderId) { - me._onUpdateGroups(params.items); - }, - 'remove': function (event, params, senderId) { - me._onRemoveGroups(params.items); - } - }; + if (this.body.dom.root) { + this.body.dom.root.style.cursor = 'move'; + } + }; - this.items = {}; // object with an Item for every data item - this.selection = []; // list with the ids of all selected nodes - this.lastStart = this.body.range.start; - this.touchParams = {}; // stores properties while dragging + /** + * Perform dragging operation + * @param {Event} event + * @private + */ + Range.prototype._onDrag = function (event) { + // only allow dragging when configured as movable + if (!this.options.moveable) return; + // refuse to drag when we where pinching to prevent the timeline make a jump + // when releasing the fingers in opposite order from the touch screen + if (!this.props.touch.allowDragging) return; - this.svgElements = {}; - this.setOptions(options); - this.groupsUsingDefaultStyles = [0]; - this.COUNTER = 0; - this.body.emitter.on('rangechanged', function() { - me.lastStart = me.body.range.start; - me.svg.style.left = util.option.asSize(-me.props.width); - me.redraw.call(me,true); - }); + var direction = this.options.direction; + validateDirection(direction); - // create the HTML DOM - this._create(); - this.framework = {svg: this.svg, svgElements: this.svgElements, options: this.options, groups: this.groups}; - this.body.emitter.emit('change'); + var delta = (direction == 'horizontal') ? event.gesture.deltaX : event.gesture.deltaY; + delta -= this.deltaDifference; + var interval = (this.props.touch.end - this.props.touch.start); - } + // normalize dragging speed if cutout is in between. + var duration = DateUtil.getHiddenDurationBetween(this.body.hiddenDates, this.start, this.end); + interval -= duration; - LineGraph.prototype = new Component(); + var width = (direction == 'horizontal') ? this.body.domProps.center.width : this.body.domProps.center.height; + var diffRange = -delta / width * interval; + var newStart = this.props.touch.start + diffRange; + var newEnd = this.props.touch.end + diffRange; - /** - * Create the HTML DOM for the ItemSet - */ - LineGraph.prototype._create = function(){ - var frame = document.createElement('div'); - frame.className = 'LineGraph'; - this.dom.frame = frame; - // create svg element for graph drawing. - this.svg = document.createElementNS('http://www.w3.org/2000/svg','svg'); - this.svg.style.position = 'relative'; - this.svg.style.height = ('' + this.options.graphHeight).replace('px','') + 'px'; - this.svg.style.display = 'block'; - frame.appendChild(this.svg); + // snapping times away from hidden zones + var safeStart = DateUtil.snapAwayFromHidden(this.body.hiddenDates, newStart, this.previousDelta-delta, true); + var safeEnd = DateUtil.snapAwayFromHidden(this.body.hiddenDates, newEnd, this.previousDelta-delta, true); + if (safeStart != newStart || safeEnd != newEnd) { + this.deltaDifference += delta; + this.props.touch.start = safeStart; + this.props.touch.end = safeEnd; + this._onDrag(event); + return; + } - // data axis - this.options.dataAxis.orientation = 'left'; - this.yAxisLeft = new DataAxis(this.body, this.options.dataAxis, this.svg, this.options.groups); + this.previousDelta = delta; + this._applyRange(newStart, newEnd); - this.options.dataAxis.orientation = 'right'; - this.yAxisRight = new DataAxis(this.body, this.options.dataAxis, this.svg, this.options.groups); - delete this.options.dataAxis.orientation; + // fire a rangechange event + this.body.emitter.emit('rangechange', { + start: new Date(this.start), + end: new Date(this.end), + byUser: true + }); + }; - // legends - this.legendLeft = new Legend(this.body, this.options.legend, 'left', this.options.groups); - this.legendRight = new Legend(this.body, this.options.legend, 'right', this.options.groups); + /** + * Stop dragging operation + * @param {event} event + * @private + */ + Range.prototype._onDragEnd = function (event) { + // only allow dragging when configured as movable + if (!this.options.moveable) return; - this.show(); + // refuse to drag when we where pinching to prevent the timeline make a jump + // when releasing the fingers in opposite order from the touch screen + if (!this.props.touch.allowDragging) return; + + this.props.touch.dragging = false; + if (this.body.dom.root) { + this.body.dom.root.style.cursor = 'auto'; + } + + // fire a rangechanged event + this.body.emitter.emit('rangechanged', { + start: new Date(this.start), + end: new Date(this.end), + byUser: true + }); }; /** - * set the options of the LineGraph. the mergeOptions is used for subObjects that have an enabled element. - * @param {object} options + * Event handler for mouse wheel event, used to zoom + * Code from http://adomas.org/javascript-mouse-wheel/ + * @param {Event} event + * @private */ - LineGraph.prototype.setOptions = function(options) { - if (options) { - var fields = ['sampling','defaultGroup','height','graphHeight','yAxisOrientation','style','barChart','dataAxis','sort','groups']; - if (options.graphHeight === undefined && options.height !== undefined && this.body.domProps.centerContainer.height !== undefined) { - this.updateSVGheight = true; - this.updateSVGheightOnResize = true; - } - else if (this.body.domProps.centerContainer.height !== undefined && options.graphHeight !== undefined) { - if (parseInt((options.graphHeight + '').replace("px",'')) < this.body.domProps.centerContainer.height) { - this.updateSVGheight = true; - } - } - util.selectiveDeepExtend(fields, this.options, options); - util.mergeOptions(this.options, options,'catmullRom'); - util.mergeOptions(this.options, options,'drawPoints'); - util.mergeOptions(this.options, options,'shaded'); - util.mergeOptions(this.options, options,'legend'); + Range.prototype._onMouseWheel = function(event) { + // only allow zooming when configured as zoomable and moveable + if (!(this.options.zoomable && this.options.moveable)) return; - if (options.catmullRom) { - if (typeof options.catmullRom == 'object') { - if (options.catmullRom.parametrization) { - if (options.catmullRom.parametrization == 'uniform') { - this.options.catmullRom.alpha = 0; - } - else if (options.catmullRom.parametrization == 'chordal') { - this.options.catmullRom.alpha = 1.0; - } - else { - this.options.catmullRom.parametrization = 'centripetal'; - this.options.catmullRom.alpha = 0.5; - } - } - } - } + // retrieve delta + var delta = 0; + if (event.wheelDelta) { /* IE/Opera. */ + delta = event.wheelDelta / 120; + } else if (event.detail) { /* Mozilla case. */ + // In Mozilla, sign of delta is different than in IE. + // Also, delta is multiple of 3. + delta = -event.detail / 3; + } - if (this.yAxisLeft) { - if (options.dataAxis !== undefined) { - this.yAxisLeft.setOptions(this.options.dataAxis); - this.yAxisRight.setOptions(this.options.dataAxis); - } - } + // If delta is nonzero, handle it. + // Basically, delta is now positive if wheel was scrolled up, + // and negative, if wheel was scrolled down. + if (delta) { + // perform the zoom action. Delta is normally 1 or -1 - if (this.legendLeft) { - if (options.legend !== undefined) { - this.legendLeft.setOptions(this.options.legend); - this.legendRight.setOptions(this.options.legend); - } + // adjust a negative delta such that zooming in with delta 0.1 + // equals zooming out with a delta -0.1 + var scale; + if (delta < 0) { + scale = 1 - (delta / 5); } - - if (this.groups.hasOwnProperty(UNGROUPED)) { - this.groups[UNGROUPED].setOptions(options); + else { + scale = 1 / (1 + (delta / 5)) ; } - } - // this is used to redraw the graph if the visibility of the groups is changed. - if (this.dom.frame) { - this.redraw(true); + // calculate center, the date to zoom around + var gesture = hammerUtil.fakeGesture(this, event), + pointer = getPointer(gesture.center, this.body.dom.center), + pointerDate = this._pointerToDate(pointer); + + this.zoom(scale, pointerDate, delta); } + + // Prevent default actions caused by mouse wheel + // (else the page and timeline both zoom and scroll) + event.preventDefault(); }; /** - * Hide the component from the DOM + * Start of a touch gesture + * @private */ - LineGraph.prototype.hide = function() { - // remove the frame containing the items - if (this.dom.frame.parentNode) { - this.dom.frame.parentNode.removeChild(this.dom.frame); - } + Range.prototype._onTouch = function (event) { + this.props.touch.start = this.start; + this.props.touch.end = this.end; + this.props.touch.allowDragging = true; + this.props.touch.center = null; + this.scaleOffset = 0; + this.deltaDifference = 0; }; - /** - * Show the component in the DOM (when not already visible). - * @return {Boolean} changed + * On start of a hold gesture + * @private */ - LineGraph.prototype.show = function() { - // show frame containing the items - if (!this.dom.frame.parentNode) { - this.body.dom.center.appendChild(this.dom.frame); - } + Range.prototype._onHold = function () { + this.props.touch.allowDragging = false; }; - /** - * Set items - * @param {vis.DataSet | null} items + * Handle pinch event + * @param {Event} event + * @private */ - LineGraph.prototype.setItems = function(items) { - var me = this, - ids, - oldItemsData = this.itemsData; - - // replace the dataset - if (!items) { - this.itemsData = null; - } - else if (items instanceof DataSet || items instanceof DataView) { - this.itemsData = items; - } - else { - throw new TypeError('Data must be an instance of DataSet or DataView'); - } - - if (oldItemsData) { - // unsubscribe from old dataset - util.forEach(this.itemListeners, function (callback, event) { - oldItemsData.off(event, callback); - }); - - // remove all drawn items - ids = oldItemsData.getIds(); - this._onRemove(ids); - } + Range.prototype._onPinch = function (event) { + // only allow zooming when configured as zoomable and moveable + if (!(this.options.zoomable && this.options.moveable)) return; - if (this.itemsData) { - // subscribe to new dataset - var id = this.id; - util.forEach(this.itemListeners, function (callback, event) { - me.itemsData.on(event, callback, id); - }); + this.props.touch.allowDragging = false; - // add all new items - ids = this.itemsData.getIds(); - this._onAdd(ids); - } - this._updateUngrouped(); - //this._updateGraph(); - this.redraw(true); - }; + if (event.gesture.touches.length > 1) { + if (!this.props.touch.center) { + this.props.touch.center = getPointer(event.gesture.center, this.body.dom.center); + } + var scale = 1 / (event.gesture.scale + this.scaleOffset); + var centerDate = this._pointerToDate(this.props.touch.center); - /** - * Set groups - * @param {vis.DataSet} groups - */ - LineGraph.prototype.setGroups = function(groups) { - var me = this; - var ids; + var hiddenDuration = DateUtil.getHiddenDurationBetween(this.body.hiddenDates, this.start, this.end); + var hiddenDurationBefore = DateUtil.getHiddenDurationBefore(this.body.hiddenDates, this, centerDate); + var hiddenDurationAfter = hiddenDuration - hiddenDurationBefore; - // unsubscribe from current dataset - if (this.groupsData) { - util.forEach(this.groupListeners, function (callback, event) { - me.groupsData.unsubscribe(event, callback); - }); + // calculate new start and end + var newStart = (centerDate - hiddenDurationBefore) + (this.props.touch.start - (centerDate - hiddenDurationBefore)) * scale; + var newEnd = (centerDate + hiddenDurationAfter) + (this.props.touch.end - (centerDate + hiddenDurationAfter)) * scale; - // remove all drawn groups - ids = this.groupsData.getIds(); - this.groupsData = null; - this._onRemoveGroups(ids); // note: this will cause a redraw - } + // snapping times away from hidden zones + this.startToFront = 1 - scale > 0 ? false : true; // used to do the right autocorrection with periodic hidden times + this.endToFront = scale - 1 > 0 ? false : true; // used to do the right autocorrection with periodic hidden times - // replace the dataset - if (!groups) { - this.groupsData = null; - } - else if (groups instanceof DataSet || groups instanceof DataView) { - this.groupsData = groups; - } - else { - throw new TypeError('Data must be an instance of DataSet or DataView'); - } + var safeStart = DateUtil.snapAwayFromHidden(this.body.hiddenDates, newStart, 1 - scale, true); + var safeEnd = DateUtil.snapAwayFromHidden(this.body.hiddenDates, newEnd, scale - 1, true); + if (safeStart != newStart || safeEnd != newEnd) { + this.props.touch.start = safeStart; + this.props.touch.end = safeEnd; + this.scaleOffset = 1 - event.gesture.scale; + newStart = safeStart; + newEnd = safeEnd; + } - if (this.groupsData) { - // subscribe to new dataset - var id = this.id; - util.forEach(this.groupListeners, function (callback, event) { - me.groupsData.on(event, callback, id); - }); + this.setRange(newStart, newEnd, false, true); - // draw all ms - ids = this.groupsData.getIds(); - this._onAddGroups(ids); + this.startToFront = false; // revert to default + this.endToFront = true; // revert to default } - this._onUpdate(); }; - /** - * Update the data - * @param [ids] + * Helper function to calculate the center date for zooming + * @param {{x: Number, y: Number}} pointer + * @return {number} date * @private */ - LineGraph.prototype._onUpdate = function(ids) { - this._updateUngrouped(); - this._updateAllGroupData(); - //this._updateGraph(); - this.redraw(true); - }; - LineGraph.prototype._onAdd = function (ids) {this._onUpdate(ids);}; - LineGraph.prototype._onRemove = function (ids) {this._onUpdate(ids);}; - LineGraph.prototype._onUpdateGroups = function (groupIds) { - for (var i = 0; i < groupIds.length; i++) { - var group = this.groupsData.get(groupIds[i]); - this._updateGroup(group, groupIds[i]); - } + Range.prototype._pointerToDate = function (pointer) { + var conversion; + var direction = this.options.direction; - //this._updateGraph(); - this.redraw(true); - }; - LineGraph.prototype._onAddGroups = function (groupIds) {this._onUpdateGroups(groupIds);}; + validateDirection(direction); + if (direction == 'horizontal') { + return this.body.util.toTime(pointer.x).valueOf(); + } + else { + var height = this.body.domProps.center.height; + conversion = this.conversion(height); + return pointer.y / conversion.scale + conversion.offset; + } + }; /** - * this cleans the group out off the legends and the dataaxis, updates the ungrouped and updates the graph - * @param {Array} groupIds + * Get the pointer location relative to the location of the dom element + * @param {{pageX: Number, pageY: Number}} touch + * @param {Element} element HTML DOM element + * @return {{x: Number, y: Number}} pointer * @private */ - LineGraph.prototype._onRemoveGroups = function (groupIds) { - for (var i = 0; i < groupIds.length; i++) { - if (this.groups.hasOwnProperty(groupIds[i])) { - if (this.groups[groupIds[i]].options.yAxisOrientation == 'right') { - this.yAxisRight.removeGroup(groupIds[i]); - this.legendRight.removeGroup(groupIds[i]); - this.legendRight.redraw(); - } - else { - this.yAxisLeft.removeGroup(groupIds[i]); - this.legendLeft.removeGroup(groupIds[i]); - this.legendLeft.redraw(); - } - delete this.groups[groupIds[i]]; - } - } - this._updateUngrouped(); - //this._updateGraph(); - this.redraw(true); - }; - + function getPointer (touch, element) { + return { + x: touch.pageX - util.getAbsoluteLeft(element), + y: touch.pageY - util.getAbsoluteTop(element) + }; + } /** - * update a group object with the group dataset entree - * - * @param group - * @param groupId - * @private + * Zoom the range the given scale in or out. Start and end date will + * be adjusted, and the timeline will be redrawn. You can optionally give a + * date around which to zoom. + * For example, try scale = 0.9 or 1.1 + * @param {Number} scale Scaling factor. Values above 1 will zoom out, + * values below 1 will zoom in. + * @param {Number} [center] Value representing a date around which will + * be zoomed. */ - LineGraph.prototype._updateGroup = function (group, groupId) { - if (!this.groups.hasOwnProperty(groupId)) { - this.groups[groupId] = new GraphGroup(group, groupId, this.options, this.groupsUsingDefaultStyles); - if (this.groups[groupId].options.yAxisOrientation == 'right') { - this.yAxisRight.addGroup(groupId, this.groups[groupId]); - this.legendRight.addGroup(groupId, this.groups[groupId]); - } - else { - this.yAxisLeft.addGroup(groupId, this.groups[groupId]); - this.legendLeft.addGroup(groupId, this.groups[groupId]); - } - } - else { - this.groups[groupId].update(group); - if (this.groups[groupId].options.yAxisOrientation == 'right') { - this.yAxisRight.updateGroup(groupId, this.groups[groupId]); - this.legendRight.updateGroup(groupId, this.groups[groupId]); - } - else { - this.yAxisLeft.updateGroup(groupId, this.groups[groupId]); - this.legendLeft.updateGroup(groupId, this.groups[groupId]); - } + Range.prototype.zoom = function(scale, center, delta) { + // if centerDate is not provided, take it half between start Date and end Date + if (center == null) { + center = (this.start + this.end) / 2; } - this.legendLeft.redraw(); - this.legendRight.redraw(); - }; + var hiddenDuration = DateUtil.getHiddenDurationBetween(this.body.hiddenDates, this.start, this.end); + var hiddenDurationBefore = DateUtil.getHiddenDurationBefore(this.body.hiddenDates, this, center); + var hiddenDurationAfter = hiddenDuration - hiddenDurationBefore; - /** - * this updates all groups, it is used when there is an update the the itemset. - * - * @private - */ - LineGraph.prototype._updateAllGroupData = function () { - if (this.itemsData != null) { - var groupsContent = {}; - var groupId; - for (groupId in this.groups) { - if (this.groups.hasOwnProperty(groupId)) { - groupsContent[groupId] = []; - } - } - for (var itemId in this.itemsData._data) { - if (this.itemsData._data.hasOwnProperty(itemId)) { - var item = this.itemsData._data[itemId]; - if (groupsContent[item.group] === undefined) { - throw new Error('Cannot find referenced group. Possible reason: items added before groups? Groups need to be added before items, as items refer to groups.') - } - item.x = util.convert(item.x,'Date'); - groupsContent[item.group].push(item); - } - } - for (groupId in this.groups) { - if (this.groups.hasOwnProperty(groupId)) { - this.groups[groupId].setItems(groupsContent[groupId]); - } - } + // calculate new start and end + var newStart = (center-hiddenDurationBefore) + (this.start - (center-hiddenDurationBefore)) * scale; + var newEnd = (center+hiddenDurationAfter) + (this.end - (center+hiddenDurationAfter)) * scale; + + // snapping times away from hidden zones + this.startToFront = delta > 0 ? false : true; // used to do the right autocorrection with periodic hidden times + this.endToFront = -delta > 0 ? false : true; // used to do the right autocorrection with periodic hidden times + var safeStart = DateUtil.snapAwayFromHidden(this.body.hiddenDates, newStart, delta, true); + var safeEnd = DateUtil.snapAwayFromHidden(this.body.hiddenDates, newEnd, -delta, true); + if (safeStart != newStart || safeEnd != newEnd) { + newStart = safeStart; + newEnd = safeEnd; } + + this.setRange(newStart, newEnd, false, true); + + this.startToFront = false; // revert to default + this.endToFront = true; // revert to default }; + /** - * Create or delete the group holding all ungrouped items. This group is used when - * there are no groups specified. This anonymous group is called 'graph'. - * @protected + * Move the range with a given delta to the left or right. Start and end + * value will be adjusted. For example, try delta = 0.1 or -0.1 + * @param {Number} delta Moving amount. Positive value will move right, + * negative value will move left */ - LineGraph.prototype._updateUngrouped = function() { - if (this.itemsData && this.itemsData != null) { - var ungroupedCounter = 0; - for (var itemId in this.itemsData._data) { - if (this.itemsData._data.hasOwnProperty(itemId)) { - var item = this.itemsData._data[itemId]; - if (item != undefined) { - if (item.hasOwnProperty('group')) { - if (item.group === undefined) { - item.group = UNGROUPED; - } - } - else { - item.group = UNGROUPED; - } - ungroupedCounter = item.group == UNGROUPED ? ungroupedCounter + 1 : ungroupedCounter; - } - } - } + Range.prototype.move = function(delta) { + // zoom start Date and end Date relative to the centerDate + var diff = (this.end - this.start); - if (ungroupedCounter == 0) { - delete this.groups[UNGROUPED]; - this.legendLeft.removeGroup(UNGROUPED); - this.legendRight.removeGroup(UNGROUPED); - this.yAxisLeft.removeGroup(UNGROUPED); - this.yAxisRight.removeGroup(UNGROUPED); - } - else { - var group = {id: UNGROUPED, content: this.options.defaultGroup}; - this._updateGroup(group, UNGROUPED); - } - } - else { - delete this.groups[UNGROUPED]; - this.legendLeft.removeGroup(UNGROUPED); - this.legendRight.removeGroup(UNGROUPED); - this.yAxisLeft.removeGroup(UNGROUPED); - this.yAxisRight.removeGroup(UNGROUPED); - } + // apply new values + var newStart = this.start + diff * delta; + var newEnd = this.end + diff * delta; - this.legendLeft.redraw(); - this.legendRight.redraw(); - }; + // TODO: reckon with min and max range + this.start = newStart; + this.end = newEnd; + }; /** - * Redraw the component, mandatory function - * @return {boolean} Returns true if the component is resized + * Move the range to a new center point + * @param {Number} moveTo New center point of the range */ - LineGraph.prototype.redraw = function(forceGraphUpdate) { - var resized = false; + Range.prototype.moveTo = function(moveTo) { + var center = (this.start + this.end) / 2; - // calculate actual size and position - this.props.width = this.dom.frame.offsetWidth; - this.props.height = this.body.domProps.centerContainer.height; + var diff = center - moveTo; - // update the graph if there is no lastWidth or with, used for the initial draw - if (this.lastWidth === undefined && this.props.width) { - forceGraphUpdate = true; - } + // calculate new start and end + var newStart = this.start - diff; + var newEnd = this.end - diff; - // check if this component is resized - resized = this._isResized() || resized; + this.setRange(newStart, newEnd); + }; - // check whether zoomed (in that case we need to re-stack everything) - var visibleInterval = this.body.range.end - this.body.range.start; - var zoomed = (visibleInterval != this.lastVisibleInterval); - this.lastVisibleInterval = visibleInterval; + module.exports = Range; - // the svg element is three times as big as the width, this allows for fully dragging left and right - // without reloading the graph. the controls for this are bound to events in the constructor - if (resized == true) { - this.svg.style.width = util.option.asSize(3*this.props.width); - this.svg.style.left = util.option.asSize(-this.props.width); +/***/ }, +/* 22 */ +/***/ function(module, exports, __webpack_require__) { - // if the height of the graph is set as proportional, change the height of the svg - if ((this.options.height + '').indexOf("%") != -1 || this.updateSVGheightOnResize == true) { - this.updateSVGheight = true; - } - } + var Hammer = __webpack_require__(19); - // update the height of the graph on each redraw of the graph. - if (this.updateSVGheight == true) { - if (this.options.graphHeight != this.body.domProps.centerContainer.height + 'px') { - this.options.graphHeight = this.body.domProps.centerContainer.height + 'px'; - this.svg.style.height = this.body.domProps.centerContainer.height + 'px'; - } - this.updateSVGheight = false; - } - else { - this.svg.style.height = ('' + this.options.graphHeight).replace('px','') + 'px'; - } + /** + * Fake a hammer.js gesture. Event can be a ScrollEvent or MouseMoveEvent + * @param {Element} element + * @param {Event} event + */ + exports.fakeGesture = function(element, event) { + var eventType = null; - // zoomed is here to ensure that animations are shown correctly. - if (resized == true || zoomed == true || this.abortedGraphUpdate == true || forceGraphUpdate == true) { - resized = this._updateGraph() || resized; + // for hammer.js 1.0.5 + // var gesture = Hammer.event.collectEventData(this, eventType, event); + + // for hammer.js 1.0.6+ + var touches = Hammer.event.getTouchList(event, eventType); + var gesture = Hammer.event.collectEventData(this, eventType, touches, event); + + // on IE in standards mode, no touches are recognized by hammer.js, + // resulting in NaN values for center.pageX and center.pageY + if (isNaN(gesture.center.pageX)) { + gesture.center.pageX = event.pageX; } - else { - // move the whole svg while dragging - if (this.lastStart != 0) { - var offset = this.body.range.start - this.lastStart; - var range = this.body.range.end - this.body.range.start; - if (this.props.width != 0) { - var rangePerPixelInv = this.props.width/range; - var xOffset = offset * rangePerPixelInv; - this.svg.style.left = (-this.props.width - xOffset) + 'px'; - } - } + if (isNaN(gesture.center.pageY)) { + gesture.center.pageY = event.pageY; } - this.legendLeft.redraw(); - this.legendRight.redraw(); - return resized; + return gesture; }; +/***/ }, +/* 23 */ +/***/ function(module, exports, __webpack_require__) { + /** - * Update and redraw the graph. - * + * Prototype for visual components + * @param {{dom: Object, domProps: Object, emitter: Emitter, range: Range}} [body] + * @param {Object} [options] */ - LineGraph.prototype._updateGraph = function () { - // reset the svg elements - DOMutil.prepareElements(this.svgElements); - if (this.props.width != 0 && this.itemsData != null) { - var group, i; - var preprocessedGroupData = {}; - var processedGroupData = {}; - var groupRanges = {}; - var changeCalled = false; + function Component (body, options) { + this.options = null; + this.props = null; + } - // getting group Ids - var groupIds = []; - for (var groupId in this.groups) { - if (this.groups.hasOwnProperty(groupId)) { - group = this.groups[groupId]; - if (group.visible == true && (this.options.groups.visibility[groupId] === undefined || this.options.groups.visibility[groupId] == true)) { - groupIds.push(groupId); - } - } - } - if (groupIds.length > 0) { - // this is the range of the SVG canvas - var minDate = this.body.util.toGlobalTime(-this.body.domProps.root.width); - var maxDate = this.body.util.toGlobalTime(2 * this.body.domProps.root.width); - var groupsData = {}; - // fill groups data, this only loads the data we require based on the timewindow - this._getRelevantData(groupIds, groupsData, minDate, maxDate); + /** + * Set options for the component. The new options will be merged into the + * current options. + * @param {Object} options + */ + Component.prototype.setOptions = function(options) { + if (options) { + util.extend(this.options, options); + } + }; - // apply sampling, if disabled, it will pass through this function. - this._applySampling(groupIds, groupsData); + /** + * Repaint the component + * @return {boolean} Returns true if the component is resized + */ + Component.prototype.redraw = function() { + // should be implemented by the component + return false; + }; - // we transform the X coordinates to detect collisions - for (i = 0; i < groupIds.length; i++) { - preprocessedGroupData[groupIds[i]] = this._convertXcoordinates(groupsData[groupIds[i]]); - } + /** + * Destroy the component. Cleanup DOM and event listeners + */ + Component.prototype.destroy = function() { + // should be implemented by the component + }; - // now all needed data has been collected we start the processing. - this._getYRanges(groupIds, preprocessedGroupData, groupRanges); + /** + * Test whether the component is resized since the last time _isResized() was + * called. + * @return {Boolean} Returns true if the component is resized + * @protected + */ + Component.prototype._isResized = function() { + var resized = (this.props._previousWidth !== this.props.width || + this.props._previousHeight !== this.props.height); - // update the Y axis first, we use this data to draw at the correct Y points - // changeCalled is required to clean the SVG on a change emit. - changeCalled = this._updateYAxis(groupIds, groupRanges); - var MAX_CYCLES = 5; - if (changeCalled == true && this.COUNTER < MAX_CYCLES) { - DOMutil.cleanupElements(this.svgElements); - this.abortedGraphUpdate = true; - this.COUNTER++; - this.body.emitter.emit('change'); - return true; - } - else { - if (this.COUNTER > MAX_CYCLES) { - console.log("WARNING: there may be an infinite loop in the _updateGraph emitter cycle.") - } - this.COUNTER = 0; - this.abortedGraphUpdate = false; + this.props._previousWidth = this.props.width; + this.props._previousHeight = this.props.height; - // With the yAxis scaled correctly, use this to get the Y values of the points. - for (i = 0; i < groupIds.length; i++) { - group = this.groups[groupIds[i]]; - processedGroupData[groupIds[i]] = this._convertYcoordinates(groupsData[groupIds[i]], group); - } + return resized; + }; - // draw the groups - for (i = 0; i < groupIds.length; i++) { - group = this.groups[groupIds[i]]; - if (group.options.style != 'bar') { // bar needs to be drawn enmasse - group.draw(processedGroupData[groupIds[i]], group, this.framework); - } - } - BarGraphFunctions.draw(groupIds, processedGroupData, this.framework); - } - } - } + module.exports = Component; - // cleanup unused svg elements - DOMutil.cleanupElements(this.svgElements); - return false; - }; +/***/ }, +/* 24 */ +/***/ function(module, exports, __webpack_require__) { /** - * first select and preprocess the data from the datasets. - * the groups have their preselection of data, we now loop over this data to see - * what data we need to draw. Sorted data is much faster. - * more optimization is possible by doing the sampling before and using the binary search - * to find the end date to determine the increment. - * - * @param {array} groupIds - * @param {object} groupsData - * @param {date} minDate - * @param {date} maxDate - * @private + * Created by Alex on 10/3/2014. */ - LineGraph.prototype._getRelevantData = function (groupIds, groupsData, minDate, maxDate) { - var group, i, j, item; - if (groupIds.length > 0) { - for (i = 0; i < groupIds.length; i++) { - group = this.groups[groupIds[i]]; - groupsData[groupIds[i]] = []; - var dataContainer = groupsData[groupIds[i]]; - // optimization for sorted data - if (group.options.sort == true) { - var guess = Math.max(0, util.binarySearchValue(group.itemsData, minDate, 'x', 'before')); - for (j = guess; j < group.itemsData.length; j++) { - item = group.itemsData[j]; - if (item !== undefined) { - if (item.x > maxDate) { - dataContainer.push(item); - break; - } - else { - dataContainer.push(item); - } - } - } - } - else { - for (j = 0; j < group.itemsData.length; j++) { - item = group.itemsData[j]; - if (item !== undefined) { - if (item.x > minDate && item.x < maxDate) { - dataContainer.push(item); - } - } + var moment = __webpack_require__(2); + + + /** + * used in Core to convert the options into a volatile variable + * + * @param Core + */ + exports.convertHiddenOptions = function(body, hiddenDates) { + body.hiddenDates = []; + if (hiddenDates) { + if (Array.isArray(hiddenDates) == true) { + for (var i = 0; i < hiddenDates.length; i++) { + if (hiddenDates[i].repeat === undefined) { + var dateItem = {}; + dateItem.start = moment(hiddenDates[i].start).toDate().valueOf(); + dateItem.end = moment(hiddenDates[i].end).toDate().valueOf(); + body.hiddenDates.push(dateItem); } } + body.hiddenDates.sort(function (a, b) { + return a.start - b.start; + }); // sort by start time } } }; /** - * - * @param groupIds - * @param groupsData - * @private + * create new entrees for the repeating hidden dates + * @param body + * @param hiddenDates */ - LineGraph.prototype._applySampling = function (groupIds, groupsData) { - var group; - if (groupIds.length > 0) { - for (var i = 0; i < groupIds.length; i++) { - group = this.groups[groupIds[i]]; - if (group.options.sampling == true) { - var dataContainer = groupsData[groupIds[i]]; - if (dataContainer.length > 0) { - var increment = 1; - var amountOfPoints = dataContainer.length; + exports.updateHiddenDates = function (body, hiddenDates) { + if (hiddenDates && body.domProps.centerContainer.width !== undefined) { + exports.convertHiddenOptions(body, hiddenDates); - // the global screen is used because changing the width of the yAxis may affect the increment, resulting in an endless loop - // of width changing of the yAxis. - var xDistance = this.body.util.toGlobalScreen(dataContainer[dataContainer.length - 1].x) - this.body.util.toGlobalScreen(dataContainer[0].x); - var pointsPerPixel = amountOfPoints / xDistance; - increment = Math.min(Math.ceil(0.2 * amountOfPoints), Math.max(1, Math.round(pointsPerPixel))); + var start = moment(body.range.start); + var end = moment(body.range.end); - var sampledData = []; - for (var j = 0; j < amountOfPoints; j += increment) { - sampledData.push(dataContainer[j]); + var totalRange = (body.range.end - body.range.start); + var pixelTime = totalRange / body.domProps.centerContainer.width; - } - groupsData[groupIds[i]] = sampledData; + for (var i = 0; i < hiddenDates.length; i++) { + if (hiddenDates[i].repeat !== undefined) { + var startDate = moment(hiddenDates[i].start); + var endDate = moment(hiddenDates[i].end); + + if (startDate._d == "Invalid Date") { + throw new Error("Supplied start date is not valid: " + hiddenDates[i].start); + } + if (endDate._d == "Invalid Date") { + throw new Error("Supplied end date is not valid: " + hiddenDates[i].end); } - } - } - } - }; + var duration = endDate - startDate; + if (duration >= 4 * pixelTime) { - /** - * - * - * @param {array} groupIds - * @param {object} groupsData - * @param {object} groupRanges | this is being filled here - * @private - */ - LineGraph.prototype._getYRanges = function (groupIds, groupsData, groupRanges) { - var groupData, group, i; - var barCombinedDataLeft = []; - var barCombinedDataRight = []; - var options; - if (groupIds.length > 0) { - for (i = 0; i < groupIds.length; i++) { - groupData = groupsData[groupIds[i]]; - options = this.groups[groupIds[i]].options; - if (groupData.length > 0) { - group = this.groups[groupIds[i]]; - // if bar graphs are stacked, their range need to be handled differently and accumulated over all groups. - if (options.barChart.handleOverlap == 'stack' && options.style == 'bar') { - if (options.yAxisOrientation == 'left') {barCombinedDataLeft = barCombinedDataLeft.concat(group.getYRange(groupData)) ;} - else {barCombinedDataRight = barCombinedDataRight.concat(group.getYRange(groupData));} - } - else { - groupRanges[groupIds[i]] = group.getYRange(groupData,groupIds[i]); - } - } - } + var offset = 0; + var runUntil = end.clone(); + switch (hiddenDates[i].repeat) { + case "daily": // case of time + if (startDate.day() != endDate.day()) { + offset = 1; + } + startDate.dayOfYear(start.dayOfYear()); + startDate.year(start.year()); + startDate.subtract(7,'days'); - // if bar graphs are stacked, their range need to be handled differently and accumulated over all groups. - BarGraphFunctions.getStackedBarYRange(barCombinedDataLeft , groupRanges, groupIds, '__barchartLeft' , 'left' ); - BarGraphFunctions.getStackedBarYRange(barCombinedDataRight, groupRanges, groupIds, '__barchartRight', 'right'); - } - }; + endDate.dayOfYear(start.dayOfYear()); + endDate.year(start.year()); + endDate.subtract(7 - offset,'days'); + runUntil.add(1, 'weeks'); + break; + case "weekly": + var dayOffset = endDate.diff(startDate,'days') + var day = startDate.day(); - /** - * this sets the Y ranges for the Y axis. It also determines which of the axis should be shown or hidden. - * @param {Array} groupIds - * @param {Object} groupRanges - * @private - */ - LineGraph.prototype._updateYAxis = function (groupIds, groupRanges) { - var resized = false; - var yAxisLeftUsed = false; - var yAxisRightUsed = false; - var minLeft = 1e9, minRight = 1e9, maxLeft = -1e9, maxRight = -1e9, minVal, maxVal; - // if groups are present - if (groupIds.length > 0) { - // this is here to make sure that if there are no items in the axis but there are groups, that there is no infinite draw/redraw loop. - for (var i = 0; i < groupIds.length; i++) { - var group = this.groups[groupIds[i]]; - if (group && group.options.yAxisOrientation != 'right') { - yAxisLeftUsed = true; - minLeft = 0; - maxLeft = 0; - } - else if (group && group.options.yAxisOrientation) { - yAxisRightUsed = true; - minRight = 0; - maxRight = 0; - } - } + // set the start date to the range.start + startDate.date(start.date()); + startDate.month(start.month()); + startDate.year(start.year()); + endDate = startDate.clone(); - // if there are items: - for (var i = 0; i < groupIds.length; i++) { - if (groupRanges.hasOwnProperty(groupIds[i])) { - if (groupRanges[groupIds[i]].ignore !== true) { - minVal = groupRanges[groupIds[i]].min; - maxVal = groupRanges[groupIds[i]].max; + // force + startDate.day(day); + endDate.day(day); + endDate.add(dayOffset,'days'); - if (groupRanges[groupIds[i]].yAxisOrientation != 'right') { - yAxisLeftUsed = true; - minLeft = minLeft > minVal ? minVal : minLeft; - maxLeft = maxLeft < maxVal ? maxVal : maxLeft; + startDate.subtract(1,'weeks'); + endDate.subtract(1,'weeks'); + + runUntil.add(1, 'weeks'); + break + case "monthly": + if (startDate.month() != endDate.month()) { + offset = 1; + } + startDate.month(start.month()); + startDate.year(start.year()); + startDate.subtract(1,'months'); + + endDate.month(start.month()); + endDate.year(start.year()); + endDate.subtract(1,'months'); + endDate.add(offset,'months'); + + runUntil.add(1, 'months'); + break; + case "yearly": + if (startDate.year() != endDate.year()) { + offset = 1; + } + startDate.year(start.year()); + startDate.subtract(1,'years'); + endDate.year(start.year()); + endDate.subtract(1,'years'); + endDate.add(offset,'years'); + + runUntil.add(1, 'years'); + break; + default: + console.log("Wrong repeat format, allowed are: daily, weekly, monthly, yearly. Given:", hiddenDates[i].repeat); + return; } - else { - yAxisRightUsed = true; - minRight = minRight > minVal ? minVal : minRight; - maxRight = maxRight < maxVal ? maxVal : maxRight; + while (startDate < runUntil) { + body.hiddenDates.push({start: startDate.valueOf(), end: endDate.valueOf()}); + switch (hiddenDates[i].repeat) { + case "daily": + startDate.add(1, 'days'); + endDate.add(1, 'days'); + break; + case "weekly": + startDate.add(1, 'weeks'); + endDate.add(1, 'weeks'); + break + case "monthly": + startDate.add(1, 'months'); + endDate.add(1, 'months'); + break; + case "yearly": + startDate.add(1, 'y'); + endDate.add(1, 'y'); + break; + default: + console.log("Wrong repeat format, allowed are: daily, weekly, monthly, yearly. Given:", hiddenDates[i].repeat); + return; + } } + body.hiddenDates.push({start: startDate.valueOf(), end: endDate.valueOf()}); } } } - - if (yAxisLeftUsed == true) { - this.yAxisLeft.setRange(minLeft, maxLeft); - } - if (yAxisRightUsed == true) { - this.yAxisRight.setRange(minRight, maxRight); + // remove duplicates, merge where possible + exports.removeDuplicates(body); + // ensure the new positions are not on hidden dates + var startHidden = exports.isHidden(body.range.start, body.hiddenDates); + var endHidden = exports.isHidden(body.range.end,body.hiddenDates); + var rangeStart = body.range.start; + var rangeEnd = body.range.end; + if (startHidden.hidden == true) {rangeStart = body.range.startToFront == true ? startHidden.startDate - 1 : startHidden.endDate + 1;} + if (endHidden.hidden == true) {rangeEnd = body.range.endToFront == true ? endHidden.startDate - 1 : endHidden.endDate + 1;} + if (startHidden.hidden == true || endHidden.hidden == true) { + body.range._applyRange(rangeStart, rangeEnd); } } - resized = this._toggleAxisVisiblity(yAxisLeftUsed , this.yAxisLeft) || resized; - resized = this._toggleAxisVisiblity(yAxisRightUsed, this.yAxisRight) || resized; - if (yAxisRightUsed == true && yAxisLeftUsed == true) { - this.yAxisLeft.drawIcons = true; - this.yAxisRight.drawIcons = true; - } - else { - this.yAxisLeft.drawIcons = false; - this.yAxisRight.drawIcons = false; - } - this.yAxisRight.master = !yAxisLeftUsed; - if (this.yAxisRight.master == false) { - if (yAxisRightUsed == true) {this.yAxisLeft.lineOffset = this.yAxisRight.width;} - else {this.yAxisLeft.lineOffset = 0;} + } - resized = this.yAxisLeft.redraw() || resized; - this.yAxisRight.stepPixelsForced = this.yAxisLeft.stepPixels; - this.yAxisRight.zeroCrossing = this.yAxisLeft.zeroCrossing; - resized = this.yAxisRight.redraw() || resized; + + /** + * remove duplicates from the hidden dates list. Duplicates are evil. They mess everything up. + * Scales with N^2 + * @param body + */ + exports.removeDuplicates = function(body) { + var hiddenDates = body.hiddenDates; + var safeDates = []; + for (var i = 0; i < hiddenDates.length; i++) { + for (var j = 0; j < hiddenDates.length; j++) { + if (i != j && hiddenDates[j].remove != true && hiddenDates[i].remove != true) { + // j inside i + if (hiddenDates[j].start >= hiddenDates[i].start && hiddenDates[j].end <= hiddenDates[i].end) { + hiddenDates[j].remove = true; + } + // j start inside i + else if (hiddenDates[j].start >= hiddenDates[i].start && hiddenDates[j].start <= hiddenDates[i].end) { + hiddenDates[i].end = hiddenDates[j].end; + hiddenDates[j].remove = true; + } + // j end inside i + else if (hiddenDates[j].end >= hiddenDates[i].start && hiddenDates[j].end <= hiddenDates[i].end) { + hiddenDates[i].start = hiddenDates[j].start; + hiddenDates[j].remove = true; + } + } + } } - else { - resized = this.yAxisRight.redraw() || resized; + + for (var i = 0; i < hiddenDates.length; i++) { + if (hiddenDates[i].remove !== true) { + safeDates.push(hiddenDates[i]); + } } - // clean the accumulated lists - if (groupIds.indexOf('__barchartLeft') != -1) { - groupIds.splice(groupIds.indexOf('__barchartLeft'),1); + body.hiddenDates = safeDates; + body.hiddenDates.sort(function (a, b) { + return a.start - b.start; + }); // sort by start time + } + + exports.printDates = function(dates) { + for (var i =0; i < dates.length; i++) { + console.log(i, new Date(dates[i].start),new Date(dates[i].end), dates[i].start, dates[i].end, dates[i].remove); } - if (groupIds.indexOf('__barchartRight') != -1) { - groupIds.splice(groupIds.indexOf('__barchartRight'),1); + } + + /** + * Used in TimeStep to avoid the hidden times. + * @param timeStep + * @param previousTime + */ + exports.stepOverHiddenDates = function(timeStep, previousTime) { + var stepInHidden = false; + var currentValue = timeStep.current.valueOf(); + for (var i = 0; i < timeStep.hiddenDates.length; i++) { + var startDate = timeStep.hiddenDates[i].start; + var endDate = timeStep.hiddenDates[i].end; + if (currentValue >= startDate && currentValue < endDate) { + stepInHidden = true; + break; + } } - return resized; + if (stepInHidden == true && currentValue < timeStep._end.valueOf() && currentValue != previousTime) { + var prevValue = moment(previousTime); + var newValue = moment(endDate); + //check if the next step should be major + if (prevValue.year() != newValue.year()) {timeStep.switchedYear = true;} + else if (prevValue.month() != newValue.month()) {timeStep.switchedMonth = true;} + else if (prevValue.dayOfYear() != newValue.dayOfYear()) {timeStep.switchedDay = true;} + + timeStep.current = newValue.toDate(); + } }; + ///** + // * Used in TimeStep to avoid the hidden times. + // * @param timeStep + // * @param previousTime + // */ + //exports.checkFirstStep = function(timeStep) { + // var stepInHidden = false; + // var currentValue = timeStep.current.valueOf(); + // for (var i = 0; i < timeStep.hiddenDates.length; i++) { + // var startDate = timeStep.hiddenDates[i].start; + // var endDate = timeStep.hiddenDates[i].end; + // if (currentValue >= startDate && currentValue < endDate) { + // stepInHidden = true; + // break; + // } + // } + // + // if (stepInHidden == true && currentValue <= timeStep._end.valueOf()) { + // var newValue = moment(endDate); + // timeStep.current = newValue.toDate(); + // } + //}; + /** - * This shows or hides the Y axis if needed. If there is a change, the changed event is emitted by the updateYAxis function - * - * @param {boolean} axisUsed - * @returns {boolean} - * @private - * @param axis + * replaces the Core toScreen methods + * @param Core + * @param time + * @param width + * @returns {number} */ - LineGraph.prototype._toggleAxisVisiblity = function (axisUsed, axis) { - var changed = false; - if (axisUsed == false) { - if (axis.dom.frame.parentNode && axis.hidden == false) { - axis.hide() - changed = true; - } + exports.toScreen = function(Core, time, width) { + if (Core.body.hiddenDates.length == 0) { + var conversion = Core.range.conversion(width); + return (time.valueOf() - conversion.offset) * conversion.scale; } else { - if (!axis.dom.frame.parentNode && axis.hidden == true) { - axis.show(); - changed = true; + var hidden = exports.isHidden(time, Core.body.hiddenDates) + if (hidden.hidden == true) { + time = hidden.startDate; } + + var duration = exports.getHiddenDurationBetween(Core.body.hiddenDates, Core.range.start, Core.range.end); + time = exports.correctTimeForHidden(Core.body.hiddenDates, Core.range, time); + + var conversion = Core.range.conversion(width, duration); + return (time.valueOf() - conversion.offset) * conversion.scale; } - return changed; }; /** - * This uses the DataAxis object to generate the correct X coordinate on the SVG window. It uses the - * util function toScreen to get the x coordinate from the timestamp. It also pre-filters the data and get the minMax ranges for - * the yAxis. - * - * @param datapoints - * @returns {Array} - * @private + * Replaces the core toTime methods + * @param body + * @param range + * @param x + * @param width + * @returns {Date} */ - LineGraph.prototype._convertXcoordinates = function (datapoints) { - var extractedData = []; - var xValue, yValue; - var toScreen = this.body.util.toScreen; - - for (var i = 0; i < datapoints.length; i++) { - xValue = toScreen(datapoints[i].x) + this.props.width; - yValue = datapoints[i].y; - extractedData.push({x: xValue, y: yValue}); + exports.toTime = function(Core, x, width) { + if (Core.body.hiddenDates.length == 0) { + var conversion = Core.range.conversion(width); + return new Date(x / conversion.scale + conversion.offset); } + else { + var hiddenDuration = exports.getHiddenDurationBetween(Core.body.hiddenDates, Core.range.start, Core.range.end); + var totalDuration = Core.range.end - Core.range.start - hiddenDuration; + var partialDuration = totalDuration * x / width; + var accumulatedHiddenDuration = exports.getAccumulatedHiddenDuration(Core.body.hiddenDates, Core.range, partialDuration); - return extractedData; + var newTime = new Date(accumulatedHiddenDuration + partialDuration + Core.range.start); + return newTime; + } }; /** - * This uses the DataAxis object to generate the correct X coordinate on the SVG window. It uses the - * util function toScreen to get the x coordinate from the timestamp. It also pre-filters the data and get the minMax ranges for - * the yAxis. + * Support function * - * @param datapoints - * @param group - * @returns {Array} - * @private + * @param hiddenDates + * @param range + * @returns {number} */ - LineGraph.prototype._convertYcoordinates = function (datapoints, group) { - var extractedData = []; - var xValue, yValue; - var toScreen = this.body.util.toScreen; - var axis = this.yAxisLeft; - var svgHeight = Number(this.svg.style.height.replace('px','')); - if (group.options.yAxisOrientation == 'right') { - axis = this.yAxisRight; + exports.getHiddenDurationBetween = function(hiddenDates, start, end) { + var duration = 0; + for (var i = 0; i < hiddenDates.length; i++) { + var startDate = hiddenDates[i].start; + var endDate = hiddenDates[i].end; + // if time after the cutout, and the + if (startDate >= start && endDate < end) { + duration += endDate - startDate; + } } + return duration; + }; - for (var i = 0; i < datapoints.length; i++) { - xValue = toScreen(datapoints[i].x) + this.props.width; - yValue = Math.round(axis.convertValue(datapoints[i].y)); - extractedData.push({x: xValue, y: yValue}); + + /** + * Support function + * @param hiddenDates + * @param range + * @param time + * @returns {{duration: number, time: *, offset: number}} + */ + exports.correctTimeForHidden = function(hiddenDates, range, time) { + time = moment(time).toDate().valueOf(); + time -= exports.getHiddenDurationBefore(hiddenDates,range,time); + return time; + }; + + exports.getHiddenDurationBefore = function(hiddenDates, range, time) { + var timeOffset = 0; + time = moment(time).toDate().valueOf(); + + for (var i = 0; i < hiddenDates.length; i++) { + var startDate = hiddenDates[i].start; + var endDate = hiddenDates[i].end; + // if time after the cutout, and the + if (startDate >= range.start && endDate < range.end) { + if (time >= endDate) { + timeOffset += (endDate - startDate); + } + } } + return timeOffset; + } - group.setZeroPosition(Math.min(svgHeight, axis.convertValue(0))); + /** + * sum the duration from start to finish, including the hidden duration, + * until the required amount has been reached, return the accumulated hidden duration + * @param hiddenDates + * @param range + * @param time + * @returns {{duration: number, time: *, offset: number}} + */ + exports.getAccumulatedHiddenDuration = function(hiddenDates, range, requiredDuration) { + var hiddenDuration = 0; + var duration = 0; + var previousPoint = range.start; + //exports.printDates(hiddenDates) + for (var i = 0; i < hiddenDates.length; i++) { + var startDate = hiddenDates[i].start; + var endDate = hiddenDates[i].end; + // if time after the cutout, and the + if (startDate >= range.start && endDate < range.end) { + duration += startDate - previousPoint; + previousPoint = endDate; + if (duration >= requiredDuration) { + break; + } + else { + hiddenDuration += endDate - startDate; + } + } + } - return extractedData; + return hiddenDuration; }; - module.exports = LineGraph; + /** + * used to step over to either side of a hidden block. Correction is disabled on tablets, might be set to true + * @param hiddenDates + * @param time + * @param direction + * @param correctionEnabled + * @returns {*} + */ + exports.snapAwayFromHidden = function(hiddenDates, time, direction, correctionEnabled) { + var isHidden = exports.isHidden(time, hiddenDates); + if (isHidden.hidden == true) { + if (direction < 0) { + if (correctionEnabled == true) { + return isHidden.startDate - (isHidden.endDate - time) - 1; + } + else { + return isHidden.startDate - 1; + } + } + else { + if (correctionEnabled == true) { + return isHidden.endDate + (time - isHidden.startDate) + 1; + } + else { + return isHidden.endDate + 1; + } + } + } + else { + return time; + } + + } + + + /** + * Check if a time is hidden + * + * @param time + * @param hiddenDates + * @returns {{hidden: boolean, startDate: Window.start, endDate: *}} + */ + exports.isHidden = function(time, hiddenDates) { + for (var i = 0; i < hiddenDates.length; i++) { + var startDate = hiddenDates[i].start; + var endDate = hiddenDates[i].end; + + if (time >= startDate && time < endDate) { // if the start is entering a hidden zone + return {hidden: true, startDate: startDate, endDate: endDate}; + break; + } + } + return {hidden: false, startDate: startDate, endDate: endDate}; + } /***/ }, -/* 30 */ +/* 25 */ /***/ function(module, exports, __webpack_require__) { + var Emitter = __webpack_require__(11); + var Hammer = __webpack_require__(19); var util = __webpack_require__(1); - var Component = __webpack_require__(20); - var TimeStep = __webpack_require__(19); - var DateUtil = __webpack_require__(15); - var moment = __webpack_require__(44); + var DataSet = __webpack_require__(7); + var DataView = __webpack_require__(9); + var Range = __webpack_require__(21); + var ItemSet = __webpack_require__(26); + var Activator = __webpack_require__(35); + var DateUtil = __webpack_require__(24); /** - * A horizontal time axis - * @param {{dom: Object, domProps: Object, emitter: Emitter, range: Range}} body - * @param {Object} [options] See TimeAxis.setOptions for the available - * options. - * @constructor TimeAxis - * @extends Component + * Create a timeline visualization + * @param {HTMLElement} container + * @param {vis.DataSet | Array | google.visualization.DataTable} [items] + * @param {Object} [options] See Core.setOptions for the available options. + * @constructor */ - function TimeAxis (body, options) { - this.dom = { - foreground: null, - lines: [], - majorTexts: [], - minorTexts: [], - redundant: { - lines: [], - majorTexts: [], - minorTexts: [] + function Core () {} + + // turn Core into an event emitter + Emitter(Core.prototype); + + /** + * Create the main DOM for the Core: a root panel containing left, right, + * top, bottom, content, and background panel. + * @param {Element} container The container element where the Core will + * be attached. + * @private + */ + Core.prototype._create = function (container) { + this.dom = {}; + + this.dom.root = document.createElement('div'); + this.dom.background = document.createElement('div'); + this.dom.backgroundVertical = document.createElement('div'); + this.dom.backgroundHorizontal = document.createElement('div'); + this.dom.centerContainer = document.createElement('div'); + this.dom.leftContainer = document.createElement('div'); + this.dom.rightContainer = document.createElement('div'); + this.dom.center = document.createElement('div'); + this.dom.left = document.createElement('div'); + this.dom.right = document.createElement('div'); + this.dom.top = document.createElement('div'); + this.dom.bottom = document.createElement('div'); + this.dom.shadowTop = document.createElement('div'); + this.dom.shadowBottom = document.createElement('div'); + this.dom.shadowTopLeft = document.createElement('div'); + this.dom.shadowBottomLeft = document.createElement('div'); + this.dom.shadowTopRight = document.createElement('div'); + this.dom.shadowBottomRight = document.createElement('div'); + + this.dom.root.className = 'vis timeline root'; + this.dom.background.className = 'vispanel background'; + this.dom.backgroundVertical.className = 'vispanel background vertical'; + this.dom.backgroundHorizontal.className = 'vispanel background horizontal'; + this.dom.centerContainer.className = 'vispanel center'; + this.dom.leftContainer.className = 'vispanel left'; + this.dom.rightContainer.className = 'vispanel right'; + this.dom.top.className = 'vispanel top'; + this.dom.bottom.className = 'vispanel bottom'; + this.dom.left.className = 'content'; + this.dom.center.className = 'content'; + this.dom.right.className = 'content'; + this.dom.shadowTop.className = 'shadow top'; + this.dom.shadowBottom.className = 'shadow bottom'; + this.dom.shadowTopLeft.className = 'shadow top'; + this.dom.shadowBottomLeft.className = 'shadow bottom'; + this.dom.shadowTopRight.className = 'shadow top'; + this.dom.shadowBottomRight.className = 'shadow bottom'; + + this.dom.root.appendChild(this.dom.background); + this.dom.root.appendChild(this.dom.backgroundVertical); + this.dom.root.appendChild(this.dom.backgroundHorizontal); + this.dom.root.appendChild(this.dom.centerContainer); + this.dom.root.appendChild(this.dom.leftContainer); + this.dom.root.appendChild(this.dom.rightContainer); + this.dom.root.appendChild(this.dom.top); + this.dom.root.appendChild(this.dom.bottom); + + this.dom.centerContainer.appendChild(this.dom.center); + this.dom.leftContainer.appendChild(this.dom.left); + this.dom.rightContainer.appendChild(this.dom.right); + + this.dom.centerContainer.appendChild(this.dom.shadowTop); + this.dom.centerContainer.appendChild(this.dom.shadowBottom); + this.dom.leftContainer.appendChild(this.dom.shadowTopLeft); + this.dom.leftContainer.appendChild(this.dom.shadowBottomLeft); + this.dom.rightContainer.appendChild(this.dom.shadowTopRight); + this.dom.rightContainer.appendChild(this.dom.shadowBottomRight); + + this.on('rangechange', this.redraw.bind(this)); + this.on('touch', this._onTouch.bind(this)); + this.on('pinch', this._onPinch.bind(this)); + this.on('dragstart', this._onDragStart.bind(this)); + this.on('drag', this._onDrag.bind(this)); + + var me = this; + this.on('change', function (properties) { + if (properties && properties.queue == true) { + // redraw once on next tick + if (!me._redrawTimer) { + me._redrawTimer = setTimeout(function () { + me._redrawTimer = null; + me.redraw(); + }, 0) + } } - }; - this.props = { - range: { - start: 0, - end: 0, - minimumStep: 0 - }, - lineTop: 0 - }; + else { + // redraw immediately + me.redraw(); + } + }); - this.defaultOptions = { - orientation: 'bottom', // supported: 'top', 'bottom' - // TODO: implement timeaxis orientations 'left' and 'right' - showMinorLabels: true, - showMajorLabels: true, - format: null, - timeAxis: null - }; - this.options = util.extend({}, this.defaultOptions); + // create event listeners for all interesting events, these events will be + // emitted via emitter + this.hammer = Hammer(this.dom.root, { + preventDefault: true + }); + this.listeners = {}; - this.body = body; + var events = [ + 'touch', 'pinch', + 'tap', 'doubletap', 'hold', + 'dragstart', 'drag', 'dragend', + 'mousewheel', 'DOMMouseScroll' // DOMMouseScroll is needed for Firefox + ]; + events.forEach(function (event) { + var listener = function () { + var args = [event].concat(Array.prototype.slice.call(arguments, 0)); + if (me.isActive()) { + me.emit.apply(me, args); + } + }; + me.hammer.on(event, listener); + me.listeners[event] = listener; + }); - // create the HTML DOM - this._create(); + // size properties of each of the panels + this.props = { + root: {}, + background: {}, + centerContainer: {}, + leftContainer: {}, + rightContainer: {}, + center: {}, + left: {}, + right: {}, + top: {}, + bottom: {}, + border: {}, + scrollTop: 0, + scrollTopMin: 0 + }; + this.touch = {}; // store state information needed for touch events - this.setOptions(options); - } + this.redrawCount = 0; - TimeAxis.prototype = new Component(); + // attach the root panel to the provided container + if (!container) throw new Error('No container provided'); + container.appendChild(this.dom.root); + }; /** - * Set options for the TimeAxis. - * Parameters will be merged in current options. - * @param {Object} options Available options: - * {string} [orientation] - * {boolean} [showMinorLabels] - * {boolean} [showMajorLabels] + * Set options. Options will be passed to all components loaded in the Timeline. + * @param {Object} [options] + * {String} orientation + * Vertical orientation for the Timeline, + * can be 'bottom' (default) or 'top'. + * {String | Number} width + * Width for the timeline, a number in pixels or + * a css string like '1000px' or '75%'. '100%' by default. + * {String | Number} height + * Fixed height for the Timeline, a number in pixels or + * a css string like '400px' or '75%'. If undefined, + * The Timeline will automatically size such that + * its contents fit. + * {String | Number} minHeight + * Minimum height for the Timeline, a number in pixels or + * a css string like '400px' or '75%'. + * {String | Number} maxHeight + * Maximum height for the Timeline, a number in pixels or + * a css string like '400px' or '75%'. + * {Number | Date | String} start + * Start date for the visible window + * {Number | Date | String} end + * End date for the visible window */ - TimeAxis.prototype.setOptions = function(options) { + Core.prototype.setOptions = function (options) { if (options) { - // copy all options that we know - util.selectiveExtend([ - 'orientation', - 'showMinorLabels', - 'showMajorLabels', - 'hiddenDates', - 'format', - 'timeAxis' - ], this.options, options); + // copy the known options + var fields = ['width', 'height', 'minHeight', 'maxHeight', 'autoResize', 'start', 'end', 'orientation', 'clickToUse', 'dataAttributes', 'hiddenDates']; + util.selectiveExtend(fields, this.options, options); - // apply locale to moment.js - // TODO: not so nice, this is applied globally to moment.js - if ('locale' in options) { - if (typeof moment.locale === 'function') { - // moment.js 2.8.1+ - moment.locale(options.locale); + if ('hiddenDates' in this.options) { + DateUtil.convertHiddenOptions(this.body, this.options.hiddenDates); + } + + if ('clickToUse' in options) { + if (options.clickToUse) { + if (!this.activator) { + this.activator = new Activator(this.dom.root); + } } else { - moment.lang(options.locale); + if (this.activator) { + this.activator.destroy(); + delete this.activator; + } } } + + // enable/disable autoResize + this._initAutoResize(); } - }; - /** - * Create the HTML DOM for the TimeAxis - */ - TimeAxis.prototype._create = function() { - this.dom.foreground = document.createElement('div'); - this.dom.background = document.createElement('div'); + // propagate options to all components + this.components.forEach(function (component) { + component.setOptions(options); + }); - this.dom.foreground.className = 'timeaxis foreground'; - this.dom.background.className = 'timeaxis background'; + // TODO: remove deprecation error one day (deprecated since version 0.8.0) + if (options && options.order) { + throw new Error('Option order is deprecated. There is no replacement for this feature.'); + } + + // redraw everything + this.redraw(); }; /** - * Destroy the TimeAxis + * Returns true when the Timeline is active. + * @returns {boolean} */ - TimeAxis.prototype.destroy = function() { - // remove from DOM - if (this.dom.foreground.parentNode) { - this.dom.foreground.parentNode.removeChild(this.dom.foreground); - } - if (this.dom.background.parentNode) { - this.dom.background.parentNode.removeChild(this.dom.background); - } - - this.body = null; + Core.prototype.isActive = function () { + return !this.activator || this.activator.active; }; /** - * Repaint the component - * @return {boolean} Returns true if the component is resized + * Destroy the Core, clean up all DOM elements and event listeners. */ - TimeAxis.prototype.redraw = function () { - var options = this.options; - var props = this.props; - var foreground = this.dom.foreground; - var background = this.dom.background; + Core.prototype.destroy = function () { + // unbind datasets + this.clear(); - // determine the correct parent DOM element (depending on option orientation) - var parent = (options.orientation == 'top') ? this.body.dom.top : this.body.dom.bottom; - var parentChanged = (foreground.parentNode !== parent); + // remove all event listeners + this.off(); - // calculate character width and height - this._calculateCharSize(); + // stop checking for changed size + this._stopAutoResize(); - // TODO: recalculate sizes only needed when parent is resized or options is changed - var orientation = this.options.orientation, - showMinorLabels = this.options.showMinorLabels, - showMajorLabels = this.options.showMajorLabels; + // remove from DOM + if (this.dom.root.parentNode) { + this.dom.root.parentNode.removeChild(this.dom.root); + } + this.dom = null; - // determine the width and height of the elemens for the axis - props.minorLabelHeight = showMinorLabels ? props.minorCharHeight : 0; - props.majorLabelHeight = showMajorLabels ? props.majorCharHeight : 0; - props.height = props.minorLabelHeight + props.majorLabelHeight; - props.width = foreground.offsetWidth; + // remove Activator + if (this.activator) { + this.activator.destroy(); + delete this.activator; + } - props.minorLineHeight = this.body.domProps.root.height - props.majorLabelHeight - - (options.orientation == 'top' ? this.body.domProps.bottom.height : this.body.domProps.top.height); - props.minorLineWidth = 1; // TODO: really calculate width - props.majorLineHeight = props.minorLineHeight + props.majorLabelHeight; - props.majorLineWidth = 1; // TODO: really calculate width + // cleanup hammer touch events + for (var event in this.listeners) { + if (this.listeners.hasOwnProperty(event)) { + delete this.listeners[event]; + } + } + this.listeners = null; + this.hammer = null; - // take foreground and background offline while updating (is almost twice as fast) - var foregroundNextSibling = foreground.nextSibling; - var backgroundNextSibling = background.nextSibling; - foreground.parentNode && foreground.parentNode.removeChild(foreground); - background.parentNode && background.parentNode.removeChild(background); + // give all components the opportunity to cleanup + this.components.forEach(function (component) { + component.destroy(); + }); - foreground.style.height = this.props.height + 'px'; + this.body = null; + }; - this._repaintLabels(); - // put DOM online again (at the same place) - if (foregroundNextSibling) { - parent.insertBefore(foreground, foregroundNextSibling); - } - else { - parent.appendChild(foreground) - } - if (backgroundNextSibling) { - this.body.dom.backgroundVertical.insertBefore(background, backgroundNextSibling); - } - else { - this.body.dom.backgroundVertical.appendChild(background) + /** + * Set a custom time bar + * @param {Date} time + */ + Core.prototype.setCustomTime = function (time) { + if (!this.customTime) { + throw new Error('Cannot get custom time: Custom time bar is not enabled'); } - return this._isResized() || parentChanged; + this.customTime.setCustomTime(time); }; /** - * Repaint major and minor text labels and vertical grid lines - * @private + * Retrieve the current custom time. + * @return {Date} customTime */ - TimeAxis.prototype._repaintLabels = function () { - var orientation = this.options.orientation; - - // calculate range and step (step such that we have space for 7 characters per label) - var start = util.convert(this.body.range.start, 'Number'); - var end = util.convert(this.body.range.end, 'Number'); - var timeLabelsize = this.body.util.toTime((this.props.minorCharWidth || 10) * 7).valueOf(); - var minimumStep = timeLabelsize - DateUtil.getHiddenDurationBefore(this.body.hiddenDates, this.body.range, timeLabelsize); - minimumStep -= this.body.util.toTime(0).valueOf(); - - var step = new TimeStep(new Date(start), new Date(end), minimumStep, this.body.hiddenDates); - if (this.options.format) { - step.setFormat(this.options.format); - } - if (this.options.timeAxis) { - step.setScale(this.options.timeAxis); + Core.prototype.getCustomTime = function() { + if (!this.customTime) { + throw new Error('Cannot get custom time: Custom time bar is not enabled'); } - this.step = step; - - // Move all DOM elements to a "redundant" list, where they - // can be picked for re-use, and clear the lists with lines and texts. - // At the end of the function _repaintLabels, left over elements will be cleaned up - var dom = this.dom; - dom.redundant.lines = dom.lines; - dom.redundant.majorTexts = dom.majorTexts; - dom.redundant.minorTexts = dom.minorTexts; - dom.lines = []; - dom.majorTexts = []; - dom.minorTexts = []; - var cur; - var x = 0; - var isMajor; - var xPrev = 0; - var width = 0; - var prevLine; - var xFirstMajorLabel = undefined; - var max = 0; - var className; + return this.customTime.getCustomTime(); + }; - step.first(); - while (step.hasNext() && max < 1000) { - max++; - cur = step.getCurrent(); - isMajor = step.isMajor(); - className = step.getClassName(); + /** + * Get the id's of the currently visible items. + * @returns {Array} The ids of the visible items + */ + Core.prototype.getVisibleItems = function() { + return this.itemSet && this.itemSet.getVisibleItems() || []; + }; - xPrev = x; - x = this.body.util.toScreen(cur); - width = x - xPrev; - if (prevLine) { - prevLine.style.width = width + 'px'; - } - if (this.options.showMinorLabels) { - this._repaintMinorText(x, step.getLabelMinor(), orientation, className); - } - if (isMajor && this.options.showMajorLabels) { - if (x > 0) { - if (xFirstMajorLabel == undefined) { - xFirstMajorLabel = x; - } - this._repaintMajorText(x, step.getLabelMajor(), orientation, className); - } - prevLine = this._repaintMajorLine(x, orientation, className); - } - else { - prevLine = this._repaintMinorLine(x, orientation, className); - } + /** + * Clear the Core. By Default, items, groups and options are cleared. + * Example usage: + * + * timeline.clear(); // clear items, groups, and options + * timeline.clear({options: true}); // clear options only + * + * @param {Object} [what] Optionally specify what to clear. By default: + * {items: true, groups: true, options: true} + */ + Core.prototype.clear = function(what) { + // clear items + if (!what || what.items) { + this.setItems(null); + } - step.next(); + // clear groups + if (!what || what.groups) { + this.setGroups(null); } - // create a major label on the left when needed - if (this.options.showMajorLabels) { - var leftTime = this.body.util.toTime(0), - leftText = step.getLabelMajor(leftTime), - widthText = leftText.length * (this.props.majorCharWidth || 10) + 10; // upper bound estimation + // clear options of timeline and of each of the components + if (!what || what.options) { + this.components.forEach(function (component) { + component.setOptions(component.defaultOptions); + }); - if (xFirstMajorLabel == undefined || widthText < xFirstMajorLabel) { - this._repaintMajorText(0, leftText, orientation, className); - } + this.setOptions(this.defaultOptions); // this will also do a redraw } - - // Cleanup leftover DOM elements from the redundant list - util.forEach(this.dom.redundant, function (arr) { - while (arr.length) { - var elem = arr.pop(); - if (elem && elem.parentNode) { - elem.parentNode.removeChild(elem); - } - } - }); }; /** - * Create a minor label for the axis at position x - * @param {Number} x - * @param {String} text - * @param {String} orientation "top" or "bottom" (default) - * @param {String} className - * @private + * Set Core window such that it fits all items + * @param {Object} [options] Available options: + * `animate: boolean | number` + * If true (default), the range is animated + * smoothly to the new window. + * If a number, the number is taken as duration + * for the animation. Default duration is 500 ms. */ - TimeAxis.prototype._repaintMinorText = function (x, text, orientation, className) { - // reuse redundant label - var label = this.dom.redundant.minorTexts.shift(); + Core.prototype.fit = function(options) { + var range = this._getDataRange(); - if (!label) { - // create new label - var content = document.createTextNode(''); - label = document.createElement('div'); - label.appendChild(content); - this.dom.foreground.appendChild(label); + // skip range set if there is no start and end date + if (range.start === null && range.end === null) { + return; } - this.dom.minorTexts.push(label); - label.childNodes[0].nodeValue = text; - - label.style.top = (orientation == 'top') ? (this.props.majorLabelHeight + 'px') : '0'; - label.style.left = x + 'px'; - label.className = 'text minor ' + className; - //label.title = title; // TODO: this is a heavy operation + var animate = (options && options.animate !== undefined) ? options.animate : true; + this.range.setRange(range.start, range.end, animate); }; /** - * Create a Major label for the axis at position x - * @param {Number} x - * @param {String} text - * @param {String} orientation "top" or "bottom" (default) - * @param {String} className - * @private + * Calculate the data range of the items and applies a 5% window around it. + * @returns {{start: Date | null, end: Date | null}} + * @protected */ - TimeAxis.prototype._repaintMajorText = function (x, text, orientation, className) { - // reuse redundant label - var label = this.dom.redundant.majorTexts.shift(); + Core.prototype._getDataRange = function() { + // apply the data range as range + var dataRange = this.getItemRange(); - if (!label) { - // create label - var content = document.createTextNode(text); - label = document.createElement('div'); - label.appendChild(content); - this.dom.foreground.appendChild(label); + // add 5% space on both sides + var start = dataRange.min; + var end = dataRange.max; + if (start != null && end != null) { + var interval = (end.valueOf() - start.valueOf()); + if (interval <= 0) { + // prevent an empty interval + interval = 24 * 60 * 60 * 1000; // 1 day + } + start = new Date(start.valueOf() - interval * 0.05); + end = new Date(end.valueOf() + interval * 0.05); } - this.dom.majorTexts.push(label); - - label.childNodes[0].nodeValue = text; - label.className = 'text major ' + className; - //label.title = title; // TODO: this is a heavy operation - label.style.top = (orientation == 'top') ? '0' : (this.props.minorLabelHeight + 'px'); - label.style.left = x + 'px'; + return { + start: start, + end: end + } }; /** - * Create a minor line for the axis at position x - * @param {Number} x - * @param {String} orientation "top" or "bottom" (default) - * @param {String} className - * @return {Element} Returns the created line - * @private + * Set the visible window. Both parameters are optional, you can change only + * start or only end. Syntax: + * + * TimeLine.setWindow(start, end) + * TimeLine.setWindow(range) + * + * Where start and end can be a Date, number, or string, and range is an + * object with properties start and end. + * + * @param {Date | Number | String | Object} [start] Start date of visible window + * @param {Date | Number | String} [end] End date of visible window + * @param {Object} [options] Available options: + * `animate: boolean | number` + * If true (default), the range is animated + * smoothly to the new window. + * If a number, the number is taken as duration + * for the animation. Default duration is 500 ms. */ - TimeAxis.prototype._repaintMinorLine = function (x, orientation, className) { - // reuse redundant line - var line = this.dom.redundant.lines.shift(); - if (!line) { - // create vertical line - line = document.createElement('div'); - this.dom.background.appendChild(line); - } - this.dom.lines.push(line); - - var props = this.props; - if (orientation == 'top') { - line.style.top = props.majorLabelHeight + 'px'; + Core.prototype.setWindow = function(start, end, options) { + var animate = (options && options.animate !== undefined) ? options.animate : true; + if (arguments.length == 1) { + var range = arguments[0]; + this.range.setRange(range.start, range.end, animate); } else { - line.style.top = this.body.domProps.top.height + 'px'; + this.range.setRange(start, end, animate); } - line.style.height = props.minorLineHeight + 'px'; - line.style.left = (x - props.minorLineWidth / 2) + 'px'; - - line.className = 'grid vertical minor ' + className; - - return line; }; /** - * Create a Major line for the axis at position x - * @param {Number} x - * @param {String} orientation "top" or "bottom" (default) - * @param {String} className - * @return {Element} Returns the created line - * @private + * Move the window such that given time is centered on screen. + * @param {Date | Number | String} time + * @param {Object} [options] Available options: + * `animate: boolean | number` + * If true (default), the range is animated + * smoothly to the new window. + * If a number, the number is taken as duration + * for the animation. Default duration is 500 ms. */ - TimeAxis.prototype._repaintMajorLine = function (x, orientation, className) { - // reuse redundant line - var line = this.dom.redundant.lines.shift(); - if (!line) { - // create vertical line - line = document.createElement('div'); - this.dom.background.appendChild(line); - } - this.dom.lines.push(line); + Core.prototype.moveTo = function(time, options) { + var interval = this.range.end - this.range.start; + var t = util.convert(time, 'Date').valueOf(); - var props = this.props; - if (orientation == 'top') { - line.style.top = '0'; - } - else { - line.style.top = this.body.domProps.top.height + 'px'; - } - line.style.left = (x - props.majorLineWidth / 2) + 'px'; - line.style.height = props.majorLineHeight + 'px'; + var start = t - interval / 2; + var end = t + interval / 2; + var animate = (options && options.animate !== undefined) ? options.animate : true; - line.className = 'grid vertical major ' + className; + this.range.setRange(start, end, animate); + }; - return line; + /** + * Get the visible window + * @return {{start: Date, end: Date}} Visible range + */ + Core.prototype.getWindow = function() { + var range = this.range.getRange(); + return { + start: new Date(range.start), + end: new Date(range.end) + }; }; /** - * Determine the size of text on the axis (both major and minor axis). - * The size is calculated only once and then cached in this.props. - * @private + * Force a redraw of the Core. Can be useful to manually redraw when + * option autoResize=false */ - TimeAxis.prototype._calculateCharSize = function () { - // Note: We calculate char size with every redraw. Size may change, for - // example when any of the timelines parents had display:none for example. + Core.prototype.redraw = function() { + var resized = false; + var options = this.options; + var props = this.props; + var dom = this.dom; - // determine the char width and height on the minor axis - if (!this.dom.measureCharMinor) { - this.dom.measureCharMinor = document.createElement('DIV'); - this.dom.measureCharMinor.className = 'text minor measure'; - this.dom.measureCharMinor.style.position = 'absolute'; + if (!dom) return; // when destroyed - this.dom.measureCharMinor.appendChild(document.createTextNode('0')); - this.dom.foreground.appendChild(this.dom.measureCharMinor); + DateUtil.updateHiddenDates(this.body, this.options.hiddenDates); + + // update class names + if (options.orientation == 'top') { + util.addClassName(dom.root, 'top'); + util.removeClassName(dom.root, 'bottom'); + } + else { + util.removeClassName(dom.root, 'top'); + util.addClassName(dom.root, 'bottom'); } - this.props.minorCharHeight = this.dom.measureCharMinor.clientHeight; - this.props.minorCharWidth = this.dom.measureCharMinor.clientWidth; - // determine the char width and height on the major axis - if (!this.dom.measureCharMajor) { - this.dom.measureCharMajor = document.createElement('DIV'); - this.dom.measureCharMajor.className = 'text major measure'; - this.dom.measureCharMajor.style.position = 'absolute'; + // update root width and height options + dom.root.style.maxHeight = util.option.asSize(options.maxHeight, ''); + dom.root.style.minHeight = util.option.asSize(options.minHeight, ''); + dom.root.style.width = util.option.asSize(options.width, ''); - this.dom.measureCharMajor.appendChild(document.createTextNode('0')); - this.dom.foreground.appendChild(this.dom.measureCharMajor); + // calculate border widths + props.border.left = (dom.centerContainer.offsetWidth - dom.centerContainer.clientWidth) / 2; + props.border.right = props.border.left; + props.border.top = (dom.centerContainer.offsetHeight - dom.centerContainer.clientHeight) / 2; + props.border.bottom = props.border.top; + var borderRootHeight= dom.root.offsetHeight - dom.root.clientHeight; + var borderRootWidth = dom.root.offsetWidth - dom.root.clientWidth; + + // workaround for a bug in IE: the clientWidth of an element with + // a height:0px and overflow:hidden is not calculated and always has value 0 + if (dom.centerContainer.clientHeight === 0) { + props.border.left = props.border.top; + props.border.right = props.border.left; + } + if (dom.root.clientHeight === 0) { + borderRootWidth = borderRootHeight; } - this.props.majorCharHeight = this.dom.measureCharMajor.clientHeight; - this.props.majorCharWidth = this.dom.measureCharMajor.clientWidth; - }; - /** - * Snap a date to a rounded value. - * The snap intervals are dependent on the current scale and step. - * @param {Date} date the date to be snapped. - * @return {Date} snappedDate - */ - TimeAxis.prototype.snap = function(date) { - return this.step.snap(date); - }; + // calculate the heights. If any of the side panels is empty, we set the height to + // minus the border width, such that the border will be invisible + props.center.height = dom.center.offsetHeight; + props.left.height = dom.left.offsetHeight; + props.right.height = dom.right.offsetHeight; + props.top.height = dom.top.clientHeight || -props.border.top; + props.bottom.height = dom.bottom.clientHeight || -props.border.bottom; - module.exports = TimeAxis; + // TODO: compensate borders when any of the panels is empty. + // apply auto height + // TODO: only calculate autoHeight when needed (else we cause an extra reflow/repaint of the DOM) + var contentHeight = Math.max(props.left.height, props.center.height, props.right.height); + var autoHeight = props.top.height + contentHeight + props.bottom.height + + borderRootHeight + props.border.top + props.border.bottom; + dom.root.style.height = util.option.asSize(options.height, autoHeight + 'px'); -/***/ }, -/* 31 */ -/***/ function(module, exports, __webpack_require__) { + // calculate heights of the content panels + props.root.height = dom.root.offsetHeight; + props.background.height = props.root.height - borderRootHeight; + var containerHeight = props.root.height - props.top.height - props.bottom.height - + borderRootHeight; + props.centerContainer.height = containerHeight; + props.leftContainer.height = containerHeight; + props.rightContainer.height = props.leftContainer.height; - var Hammer = __webpack_require__(45); - var util = __webpack_require__(1); + // calculate the widths of the panels + props.root.width = dom.root.offsetWidth; + props.background.width = props.root.width - borderRootWidth; + props.left.width = dom.leftContainer.clientWidth || -props.border.left; + props.leftContainer.width = props.left.width; + props.right.width = dom.rightContainer.clientWidth || -props.border.right; + props.rightContainer.width = props.right.width; + var centerWidth = props.root.width - props.left.width - props.right.width - borderRootWidth; + props.center.width = centerWidth; + props.centerContainer.width = centerWidth; + props.top.width = centerWidth; + props.bottom.width = centerWidth; - /** - * @constructor Item - * @param {Object} data Object containing (optional) parameters type, - * start, end, content, group, className. - * @param {{toScreen: function, toTime: function}} conversion - * Conversion functions from time to screen and vice versa - * @param {Object} options Configuration options - * // TODO: describe available options - */ - function Item (data, conversion, options) { - this.id = null; - this.parent = null; - this.data = data; - this.dom = null; - this.conversion = conversion || {}; - this.options = options || {}; + // resize the panels + dom.background.style.height = props.background.height + 'px'; + dom.backgroundVertical.style.height = props.background.height + 'px'; + dom.backgroundHorizontal.style.height = props.centerContainer.height + 'px'; + dom.centerContainer.style.height = props.centerContainer.height + 'px'; + dom.leftContainer.style.height = props.leftContainer.height + 'px'; + dom.rightContainer.style.height = props.rightContainer.height + 'px'; - this.selected = false; - this.displayed = false; - this.dirty = true; + dom.background.style.width = props.background.width + 'px'; + dom.backgroundVertical.style.width = props.centerContainer.width + 'px'; + dom.backgroundHorizontal.style.width = props.background.width + 'px'; + dom.centerContainer.style.width = props.center.width + 'px'; + dom.top.style.width = props.top.width + 'px'; + dom.bottom.style.width = props.bottom.width + 'px'; - this.top = null; - this.left = null; - this.width = null; - this.height = null; - } + // reposition the panels + dom.background.style.left = '0'; + dom.background.style.top = '0'; + dom.backgroundVertical.style.left = (props.left.width + props.border.left) + 'px'; + dom.backgroundVertical.style.top = '0'; + dom.backgroundHorizontal.style.left = '0'; + dom.backgroundHorizontal.style.top = props.top.height + 'px'; + dom.centerContainer.style.left = props.left.width + 'px'; + dom.centerContainer.style.top = props.top.height + 'px'; + dom.leftContainer.style.left = '0'; + dom.leftContainer.style.top = props.top.height + 'px'; + dom.rightContainer.style.left = (props.left.width + props.center.width) + 'px'; + dom.rightContainer.style.top = props.top.height + 'px'; + dom.top.style.left = props.left.width + 'px'; + dom.top.style.top = '0'; + dom.bottom.style.left = props.left.width + 'px'; + dom.bottom.style.top = (props.top.height + props.centerContainer.height) + 'px'; - Item.prototype.stack = true; + // update the scrollTop, feasible range for the offset can be changed + // when the height of the Core or of the contents of the center changed + this._updateScrollTop(); - /** - * Select current item - */ - Item.prototype.select = function() { - this.selected = true; - this.dirty = true; - if (this.displayed) this.redraw(); - }; + // reposition the scrollable contents + var offset = this.props.scrollTop; + if (options.orientation == 'bottom') { + offset += Math.max(this.props.centerContainer.height - this.props.center.height - + this.props.border.top - this.props.border.bottom, 0); + } + dom.center.style.left = '0'; + dom.center.style.top = offset + 'px'; + dom.left.style.left = '0'; + dom.left.style.top = offset + 'px'; + dom.right.style.left = '0'; + dom.right.style.top = offset + 'px'; - /** - * Unselect current item - */ - Item.prototype.unselect = function() { - this.selected = false; - this.dirty = true; - if (this.displayed) this.redraw(); + // show shadows when vertical scrolling is available + var visibilityTop = this.props.scrollTop == 0 ? 'hidden' : ''; + var visibilityBottom = this.props.scrollTop == this.props.scrollTopMin ? 'hidden' : ''; + dom.shadowTop.style.visibility = visibilityTop; + dom.shadowBottom.style.visibility = visibilityBottom; + dom.shadowTopLeft.style.visibility = visibilityTop; + dom.shadowBottomLeft.style.visibility = visibilityBottom; + dom.shadowTopRight.style.visibility = visibilityTop; + dom.shadowBottomRight.style.visibility = visibilityBottom; + + // redraw all components + this.components.forEach(function (component) { + resized = component.redraw() || resized; + }); + if (resized) { + // keep repainting until all sizes are settled + var MAX_REDRAWS = 3; // maximum number of consecutive redraws + if (this.redrawCount < MAX_REDRAWS) { + this.redrawCount++; + this.redraw(); + } + else { + console.log('WARNING: infinite loop in redraw?'); + } + this.redrawCount = 0; + } + + this.emit("finishedRedraw"); }; - /** - * Set data for the item. Existing data will be updated. The id should not - * be changed. When the item is displayed, it will be redrawn immediately. - * @param {Object} data - */ - Item.prototype.setData = function(data) { - this.data = data; - this.dirty = true; - if (this.displayed) this.redraw(); + // TODO: deprecated since version 1.1.0, remove some day + Core.prototype.repaint = function () { + throw new Error('Function repaint is deprecated. Use redraw instead.'); }; /** - * Set a parent for the item - * @param {ItemSet | Group} parent + * Set a current time. This can be used for example to ensure that a client's + * time is synchronized with a shared server time. + * Only applicable when option `showCurrentTime` is true. + * @param {Date | String | Number} time A Date, unix timestamp, or + * ISO date string. */ - Item.prototype.setParent = function(parent) { - if (this.displayed) { - this.hide(); - this.parent = parent; - if (this.parent) { - this.show(); - } - } - else { - this.parent = parent; + Core.prototype.setCurrentTime = function(time) { + if (!this.currentTime) { + throw new Error('Option showCurrentTime must be true'); } + + this.currentTime.setCurrentTime(time); }; /** - * Check whether this item is visible inside given range - * @returns {{start: Number, end: Number}} range with a timestamp for start and end - * @returns {boolean} True if visible + * Get the current time. + * Only applicable when option `showCurrentTime` is true. + * @return {Date} Returns the current time. */ - Item.prototype.isVisible = function(range) { - // Should be implemented by Item implementations - return false; + Core.prototype.getCurrentTime = function() { + if (!this.currentTime) { + throw new Error('Option showCurrentTime must be true'); + } + + return this.currentTime.getCurrentTime(); }; /** - * Show the Item in the DOM (when not already visible) - * @return {Boolean} changed + * Convert a position on screen (pixels) to a datetime + * @param {int} x Position on the screen in pixels + * @return {Date} time The datetime the corresponds with given position x + * @private */ - Item.prototype.show = function() { - return false; + // TODO: move this function to Range + Core.prototype._toTime = function(x) { + return DateUtil.toTime(this, x, this.props.center.width); }; /** - * Hide the Item from the DOM (when visible) - * @return {Boolean} changed + * Convert a position on the global screen (pixels) to a datetime + * @param {int} x Position on the screen in pixels + * @return {Date} time The datetime the corresponds with given position x + * @private */ - Item.prototype.hide = function() { - return false; + // TODO: move this function to Range + Core.prototype._toGlobalTime = function(x) { + return DateUtil.toTime(this, x, this.props.root.width); + //var conversion = this.range.conversion(this.props.root.width); + //return new Date(x / conversion.scale + conversion.offset); }; /** - * Repaint the item + * Convert a datetime (Date object) into a position on the screen + * @param {Date} time A date + * @return {int} x The position on the screen in pixels which corresponds + * with the given date. + * @private */ - Item.prototype.redraw = function() { - // should be implemented by the item + // TODO: move this function to Range + Core.prototype._toScreen = function(time) { + return DateUtil.toScreen(this, time, this.props.center.width); }; + + /** - * Reposition the Item horizontally + * Convert a datetime (Date object) into a position on the root + * This is used to get the pixel density estimate for the screen, not the center panel + * @param {Date} time A date + * @return {int} x The position on root in pixels which corresponds + * with the given date. + * @private */ - Item.prototype.repositionX = function() { - // should be implemented by the item + // TODO: move this function to Range + Core.prototype._toGlobalScreen = function(time) { + return DateUtil.toScreen(this, time, this.props.root.width); + //var conversion = this.range.conversion(this.props.root.width); + //return (time.valueOf() - conversion.offset) * conversion.scale; }; + /** - * Reposition the Item vertically + * Initialize watching when option autoResize is true + * @private */ - Item.prototype.repositionY = function() { - // should be implemented by the item + Core.prototype._initAutoResize = function () { + if (this.options.autoResize == true) { + this._startAutoResize(); + } + else { + this._stopAutoResize(); + } }; /** - * Repaint a delete button on the top right of the item when the item is selected - * @param {HTMLElement} anchor - * @protected + * Watch for changes in the size of the container. On resize, the Panel will + * automatically redraw itself. + * @private */ - Item.prototype._repaintDeleteButton = function (anchor) { - if (this.selected && this.options.editable.remove && !this.dom.deleteButton) { - // create and show button - var me = this; + Core.prototype._startAutoResize = function () { + var me = this; - var deleteButton = document.createElement('div'); - deleteButton.className = 'delete'; - deleteButton.title = 'Delete this item'; + this._stopAutoResize(); - Hammer(deleteButton, { - preventDefault: true - }).on('tap', function (event) { - me.parent.removeFromDataSet(me); - event.stopPropagation(); - }); + this._onResize = function() { + if (me.options.autoResize != true) { + // stop watching when the option autoResize is changed to false + me._stopAutoResize(); + return; + } - anchor.appendChild(deleteButton); - this.dom.deleteButton = deleteButton; - } - else if (!this.selected && this.dom.deleteButton) { - // remove button - if (this.dom.deleteButton.parentNode) { - this.dom.deleteButton.parentNode.removeChild(this.dom.deleteButton); + if (me.dom.root) { + // check whether the frame is resized + // Note: we compare offsetWidth here, not clientWidth. For some reason, + // IE does not restore the clientWidth from 0 to the actual width after + // changing the timeline's container display style from none to visible + if ((me.dom.root.offsetWidth != me.props.lastWidth) || + (me.dom.root.offsetHeight != me.props.lastHeight)) { + me.props.lastWidth = me.dom.root.offsetWidth; + me.props.lastHeight = me.dom.root.offsetHeight; + + me.emit('change'); + } } - this.dom.deleteButton = null; - } + }; + + // add event listener to window resize + util.addEventListener(window, 'resize', this._onResize); + + this.watchTimer = setInterval(this._onResize, 1000); }; /** - * Set HTML contents for the item - * @param {Element} element HTML element to fill with the contents + * Stop watching for a resize of the frame. * @private */ - Item.prototype._updateContents = function (element) { - var content; - if (this.options.template) { - var itemData = this.parent.itemSet.itemsData.get(this.id); // get a clone of the data from the dataset - content = this.options.template(itemData); - } - else { - content = this.data.content; + Core.prototype._stopAutoResize = function () { + if (this.watchTimer) { + clearInterval(this.watchTimer); + this.watchTimer = undefined; } - if(content !== this.content) { - // only replace the content when changed - if (content instanceof Element) { - element.innerHTML = ''; - element.appendChild(content); - } - else if (content != undefined) { - element.innerHTML = content; - } - else { - if (!(this.data.type == 'background' && this.data.content === undefined)) { - throw new Error('Property "content" missing in item ' + this.id); - } - } - - this.content = content; - } + // remove event listener on window.resize + util.removeEventListener(window, 'resize', this._onResize); + this._onResize = null; }; /** - * Set HTML contents for the item - * @param {Element} element HTML element to fill with the contents + * Start moving the timeline vertically + * @param {Event} event * @private */ - Item.prototype._updateTitle = function (element) { - if (this.data.title != null) { - element.title = this.data.title || ''; - } - else { - element.removeAttribute('title'); - } + Core.prototype._onTouch = function (event) { + this.touch.allowDragging = true; }; /** - * Process dataAttributes timeline option and set as data- attributes on dom.content - * @param {Element} element HTML element to which the attributes will be attached + * Start moving the timeline vertically + * @param {Event} event * @private */ - Item.prototype._updateDataAttributes = function(element) { - if (this.options.dataAttributes && this.options.dataAttributes.length > 0) { - var attributes = []; - - if (Array.isArray(this.options.dataAttributes)) { - attributes = this.options.dataAttributes; - } - else if (this.options.dataAttributes == 'all') { - attributes = Object.keys(this.data); - } - else { - return; - } - - for (var i = 0; i < attributes.length; i++) { - var name = attributes[i]; - var value = this.data[name]; - - if (value != null) { - element.setAttribute('data-' + name, value); - } - else { - element.removeAttribute('data-' + name); - } - } - } + Core.prototype._onPinch = function (event) { + this.touch.allowDragging = false; }; /** - * Update custom styles of the element - * @param element + * Start moving the timeline vertically + * @param {Event} event * @private */ - Item.prototype._updateStyle = function(element) { - // remove old styles - if (this.style) { - util.removeCssText(element, this.style); - this.style = null; - } - - // append new styles - if (this.data.style) { - util.addCssText(element, this.data.style); - this.style = this.data.style; - } + Core.prototype._onDragStart = function (event) { + this.touch.initialScrollTop = this.props.scrollTop; }; - module.exports = Item; - - -/***/ }, -/* 32 */ -/***/ function(module, exports, __webpack_require__) { - - var Hammer = __webpack_require__(45); - var Item = __webpack_require__(31); - var BackgroundGroup = __webpack_require__(26); - var RangeItem = __webpack_require__(35); - /** - * @constructor BackgroundItem - * @extends Item - * @param {Object} data Object containing parameters start, end - * content, className. - * @param {{toScreen: function, toTime: function}} conversion - * Conversion functions from time to screen and vice versa - * @param {Object} [options] Configuration options - * // TODO: describe options + * Move the timeline vertically + * @param {Event} event + * @private */ - // TODO: implement support for the BackgroundItem just having a start, then being displayed as a sort of an annotation - function BackgroundItem (data, conversion, options) { - this.props = { - content: { - width: 0 - } - }; - this.overflow = false; // if contents can overflow (css styling), this flag is set to true - - // validate data - if (data) { - if (data.start == undefined) { - throw new Error('Property "start" missing in item ' + data.id); - } - if (data.end == undefined) { - throw new Error('Property "end" missing in item ' + data.id); - } - } + Core.prototype._onDrag = function (event) { + // refuse to drag when we where pinching to prevent the timeline make a jump + // when releasing the fingers in opposite order from the touch screen + if (!this.touch.allowDragging) return; - Item.call(this, data, conversion, options); + var delta = event.gesture.deltaY; - this.emptyContent = false; - } + var oldScrollTop = this._getScrollTop(); + var newScrollTop = this._setScrollTop(this.touch.initialScrollTop + delta); - BackgroundItem.prototype = new Item (null, null, null); - BackgroundItem.prototype.baseClassName = 'item background'; - BackgroundItem.prototype.stack = false; + if (newScrollTop != oldScrollTop) { + this.redraw(); // TODO: this causes two redraws when dragging, the other is triggered by rangechange already + this.emit("verticalDrag"); + } + }; /** - * Check whether this item is visible inside given range - * @returns {{start: Number, end: Number}} range with a timestamp for start and end - * @returns {boolean} True if visible + * Apply a scrollTop + * @param {Number} scrollTop + * @returns {Number} scrollTop Returns the applied scrollTop + * @private */ - BackgroundItem.prototype.isVisible = function(range) { - // determine visibility - return (this.data.start < range.end) && (this.data.end > range.start); + Core.prototype._setScrollTop = function (scrollTop) { + this.props.scrollTop = scrollTop; + this._updateScrollTop(); + return this.props.scrollTop; }; /** - * Repaint the item + * Update the current scrollTop when the height of the containers has been changed + * @returns {Number} scrollTop Returns the applied scrollTop + * @private */ - BackgroundItem.prototype.redraw = function() { - var dom = this.dom; - if (!dom) { - // create DOM - this.dom = {}; - dom = this.dom; - - // background box - dom.box = document.createElement('div'); - // className is updated in redraw() - - // contents box - dom.content = document.createElement('div'); - dom.content.className = 'content'; - dom.box.appendChild(dom.content); + Core.prototype._updateScrollTop = function () { + // recalculate the scrollTopMin + var scrollTopMin = Math.min(this.props.centerContainer.height - this.props.center.height, 0); // is negative or zero + if (scrollTopMin != this.props.scrollTopMin) { + // in case of bottom orientation, change the scrollTop such that the contents + // do not move relative to the time axis at the bottom + if (this.options.orientation == 'bottom') { + this.props.scrollTop += (scrollTopMin - this.props.scrollTopMin); + } + this.props.scrollTopMin = scrollTopMin; + } - // Note: we do NOT attach this item as attribute to the DOM, - // such that background items cannot be selected - //dom.box['timeline-item'] = this; + // limit the scrollTop to the feasible scroll range + if (this.props.scrollTop > 0) this.props.scrollTop = 0; + if (this.props.scrollTop < scrollTopMin) this.props.scrollTop = scrollTopMin; - this.dirty = true; - } + return this.props.scrollTop; + }; - // append DOM to parent DOM - if (!this.parent) { - throw new Error('Cannot redraw item: no parent attached'); - } - if (!dom.box.parentNode) { - var background = this.parent.dom.background; - if (!background) { - throw new Error('Cannot redraw item: parent has no background container element'); - } - background.appendChild(dom.box); - } - this.displayed = true; + /** + * Get the current scrollTop + * @returns {number} scrollTop + * @private + */ + Core.prototype._getScrollTop = function () { + return this.props.scrollTop; + }; - // Update DOM when item is marked dirty. An item is marked dirty when: - // - the item is not yet rendered - // - the item's data is changed - // - the item is selected/deselected - if (this.dirty) { - this._updateContents(this.dom.content); - this._updateTitle(this.dom.content); - this._updateDataAttributes(this.dom.content); - this._updateStyle(this.dom.box); + module.exports = Core; - // update class - var className = (this.data.className ? (' ' + this.data.className) : '') + - (this.selected ? ' selected' : ''); - dom.box.className = this.baseClassName + className; - // determine from css whether this box has overflow - this.overflow = window.getComputedStyle(dom.content).overflow !== 'hidden'; +/***/ }, +/* 26 */ +/***/ function(module, exports, __webpack_require__) { - // recalculate size - this.props.content.width = this.dom.content.offsetWidth; - this.height = 0; // set height zero, so this item will be ignored when stacking items + var Hammer = __webpack_require__(19); + var util = __webpack_require__(1); + var DataSet = __webpack_require__(7); + var DataView = __webpack_require__(9); + var Component = __webpack_require__(23); + var Group = __webpack_require__(27); + var BackgroundGroup = __webpack_require__(31); + var BoxItem = __webpack_require__(32); + var PointItem = __webpack_require__(33); + var RangeItem = __webpack_require__(29); + var BackgroundItem = __webpack_require__(34); - this.dirty = false; - } - }; - /** - * Show the item in the DOM (when not already visible). The items DOM will - * be created when needed. - */ - BackgroundItem.prototype.show = RangeItem.prototype.show; + var UNGROUPED = '__ungrouped__'; // reserved group id for ungrouped items + var BACKGROUND = '__background__'; // reserved group id for background items without group /** - * Hide the item from the DOM (when visible) - * @return {Boolean} changed + * An ItemSet holds a set of items and ranges which can be displayed in a + * range. The width is determined by the parent of the ItemSet, and the height + * is determined by the size of the items. + * @param {{dom: Object, domProps: Object, emitter: Emitter, range: Range}} body + * @param {Object} [options] See ItemSet.setOptions for the available options. + * @constructor ItemSet + * @extends Component */ - BackgroundItem.prototype.hide = RangeItem.prototype.hide; + function ItemSet(body, options) { + this.body = body; - /** - * Reposition the item horizontally - * @Override - */ - BackgroundItem.prototype.repositionX = RangeItem.prototype.repositionX; + this.defaultOptions = { + type: null, // 'box', 'point', 'range', 'background' + orientation: 'bottom', // 'top' or 'bottom' + align: 'auto', // alignment of box items + stack: true, + groupOrder: null, - /** - * Reposition the item vertically - * @Override - */ - BackgroundItem.prototype.repositionY = function(margin) { - var onTop = this.options.orientation === 'top'; - this.dom.content.style.top = onTop ? '' : '0'; - this.dom.content.style.bottom = onTop ? '0' : ''; - var height; + selectable: true, + editable: { + updateTime: false, + updateGroup: false, + add: false, + remove: false + }, - // special positioning for subgroups - if (this.data.subgroup !== undefined) { - var itemSubgroup = this.data.subgroup; - var subgroups = this.parent.subgroups; - var subgroupIndex = subgroups[itemSubgroup].index; - // if the orientation is top, we need to take the difference in height into account. - if (onTop == true) { - // the first subgroup will have to account for the distance from the top to the first item. - height = this.parent.subgroups[itemSubgroup].height + margin.item.vertical; - height += subgroupIndex == 0 ? margin.axis - 0.5*margin.item.vertical : 0; - var newTop = this.parent.top; - for (var subgroup in subgroups) { - if (subgroups.hasOwnProperty(subgroup)) { - if (subgroups[subgroup].visible == true && subgroups[subgroup].index < subgroupIndex) { - newTop += subgroups[subgroup].height + margin.item.vertical; - } - } - } + onAdd: function (item, callback) { + callback(item); + }, + onUpdate: function (item, callback) { + callback(item); + }, + onMove: function (item, callback) { + callback(item); + }, + onRemove: function (item, callback) { + callback(item); + }, + onMoving: function (item, callback) { + callback(item); + }, - // the others will have to be offset downwards with this same distance. - newTop += subgroupIndex != 0 ? margin.axis - 0.5 * margin.item.vertical : 0; - this.dom.box.style.top = newTop + 'px'; - this.dom.box.style.bottom = ''; - } - // and when the orientation is bottom: - else { - var newTop = this.parent.top; - for (var subgroup in subgroups) { - if (subgroups.hasOwnProperty(subgroup)) { - if (subgroups[subgroup].visible == true && subgroups[subgroup].index > subgroupIndex) { - newTop += subgroups[subgroup].height + margin.item.vertical; - } - } - } - height = this.parent.subgroups[itemSubgroup].height + margin.item.vertical; - this.dom.box.style.top = newTop + 'px'; - this.dom.box.style.bottom = ''; - } - } - // and in the case of no subgroups: - else { - // we want backgrounds with groups to only show in groups. - if (this.parent instanceof BackgroundGroup) { - // if the item is not in a group: - height = Math.max(this.parent.height, - this.parent.itemSet.body.domProps.center.height, - this.parent.itemSet.body.domProps.centerContainer.height); - this.dom.box.style.top = onTop ? '0' : ''; - this.dom.box.style.bottom = onTop ? '' : '0'; - } - else { - height = this.parent.height; - // same alignment for items when orientation is top or bottom - this.dom.box.style.top = this.parent.top + 'px'; - this.dom.box.style.bottom = ''; - } - } - this.dom.box.style.height = height + 'px'; - }; + margin: { + item: { + horizontal: 10, + vertical: 10 + }, + axis: 20 + }, + padding: 5 + }; - module.exports = BackgroundItem; + // options is shared by this ItemSet and all its items + this.options = util.extend({}, this.defaultOptions); + // options for getting items from the DataSet with the correct type + this.itemOptions = { + type: {start: 'Date', end: 'Date'} + }; -/***/ }, -/* 33 */ -/***/ function(module, exports, __webpack_require__) { + this.conversion = { + toScreen: body.util.toScreen, + toTime: body.util.toTime + }; + this.dom = {}; + this.props = {}; + this.hammer = null; - var Item = __webpack_require__(31); - var util = __webpack_require__(1); + var me = this; + this.itemsData = null; // DataSet + this.groupsData = null; // DataSet - /** - * @constructor BoxItem - * @extends Item - * @param {Object} data Object containing parameters start - * content, className. - * @param {{toScreen: function, toTime: function}} conversion - * Conversion functions from time to screen and vice versa - * @param {Object} [options] Configuration options - * // TODO: describe available options - */ - function BoxItem (data, conversion, options) { - this.props = { - dot: { - width: 0, - height: 0 + // listeners for the DataSet of the items + this.itemListeners = { + 'add': function (event, params, senderId) { + me._onAdd(params.items); }, - line: { - width: 0, - height: 0 + 'update': function (event, params, senderId) { + me._onUpdate(params.items); + }, + 'remove': function (event, params, senderId) { + me._onRemove(params.items); } }; - // validate data - if (data) { - if (data.start == undefined) { - throw new Error('Property "start" missing in item ' + data); + // listeners for the DataSet of the groups + this.groupListeners = { + 'add': function (event, params, senderId) { + me._onAddGroups(params.items); + }, + 'update': function (event, params, senderId) { + me._onUpdateGroups(params.items); + }, + 'remove': function (event, params, senderId) { + me._onRemoveGroups(params.items); } - } + }; - Item.call(this, data, conversion, options); + this.items = {}; // object with an Item for every data item + this.groups = {}; // Group object for every group + this.groupIds = []; + + this.selection = []; // list with the ids of all selected nodes + this.stackDirty = true; // if true, all items will be restacked on next redraw + + this.touchParams = {}; // stores properties while dragging + // create the HTML DOM + + this._create(); + + this.setOptions(options); } - BoxItem.prototype = new Item (null, null, null); + ItemSet.prototype = new Component(); + + // available item types will be registered here + ItemSet.types = { + background: BackgroundItem, + box: BoxItem, + range: RangeItem, + point: PointItem + }; /** - * Check whether this item is visible inside given range - * @returns {{start: Number, end: Number}} range with a timestamp for start and end - * @returns {boolean} True if visible - */ - BoxItem.prototype.isVisible = function(range) { - // determine visibility - // TODO: account for the real width of the item. Right now we just add 1/4 to the window - var interval = (range.end - range.start) / 4; - return (this.data.start > range.start - interval) && (this.data.start < range.end + interval); - }; - - /** - * Repaint the item + * Create the HTML DOM for the ItemSet */ - BoxItem.prototype.redraw = function() { - var dom = this.dom; - if (!dom) { - // create DOM - this.dom = {}; - dom = this.dom; + ItemSet.prototype._create = function(){ + var frame = document.createElement('div'); + frame.className = 'itemset'; + frame['timeline-itemset'] = this; + this.dom.frame = frame; - // create main box - dom.box = document.createElement('DIV'); + // create background panel + var background = document.createElement('div'); + background.className = 'background'; + frame.appendChild(background); + this.dom.background = background; - // contents box (inside the background box). used for making margins - dom.content = document.createElement('DIV'); - dom.content.className = 'content'; - dom.box.appendChild(dom.content); + // create foreground panel + var foreground = document.createElement('div'); + foreground.className = 'foreground'; + frame.appendChild(foreground); + this.dom.foreground = foreground; - // line to axis - dom.line = document.createElement('DIV'); - dom.line.className = 'line'; + // create axis panel + var axis = document.createElement('div'); + axis.className = 'axis'; + this.dom.axis = axis; - // dot on axis - dom.dot = document.createElement('DIV'); - dom.dot.className = 'dot'; + // create labelset + var labelSet = document.createElement('div'); + labelSet.className = 'labelset'; + this.dom.labelSet = labelSet; - // attach this item as attribute - dom.box['timeline-item'] = this; + // create ungrouped Group + this._updateUngrouped(); - this.dirty = true; - } + // create background Group + var backgroundGroup = new BackgroundGroup(BACKGROUND, null, this); + backgroundGroup.show(); + this.groups[BACKGROUND] = backgroundGroup; - // append DOM to parent DOM - if (!this.parent) { - throw new Error('Cannot redraw item: no parent attached'); - } - if (!dom.box.parentNode) { - var foreground = this.parent.dom.foreground; - if (!foreground) throw new Error('Cannot redraw item: parent has no foreground container element'); - foreground.appendChild(dom.box); - } - if (!dom.line.parentNode) { - var background = this.parent.dom.background; - if (!background) throw new Error('Cannot redraw item: parent has no background container element'); - background.appendChild(dom.line); - } - if (!dom.dot.parentNode) { - var axis = this.parent.dom.axis; - if (!background) throw new Error('Cannot redraw item: parent has no axis container element'); - axis.appendChild(dom.dot); - } - this.displayed = true; + // attach event listeners + // Note: we bind to the centerContainer for the case where the height + // of the center container is larger than of the ItemSet, so we + // can click in the empty area to create a new item or deselect an item. + this.hammer = Hammer(this.body.dom.centerContainer, { + preventDefault: true + }); - // Update DOM when item is marked dirty. An item is marked dirty when: - // - the item is not yet rendered - // - the item's data is changed - // - the item is selected/deselected - if (this.dirty) { - this._updateContents(this.dom.content); - this._updateTitle(this.dom.box); - this._updateDataAttributes(this.dom.box); - this._updateStyle(this.dom.box); + // drag items when selected + this.hammer.on('touch', this._onTouch.bind(this)); + this.hammer.on('dragstart', this._onDragStart.bind(this)); + this.hammer.on('drag', this._onDrag.bind(this)); + this.hammer.on('dragend', this._onDragEnd.bind(this)); - // update class - var className = (this.data.className? ' ' + this.data.className : '') + - (this.selected ? ' selected' : ''); - dom.box.className = 'item box' + className; - dom.line.className = 'item line' + className; - dom.dot.className = 'item dot' + className; + // single select (or unselect) when tapping an item + this.hammer.on('tap', this._onSelectItem.bind(this)); - // recalculate size - this.props.dot.height = dom.dot.offsetHeight; - this.props.dot.width = dom.dot.offsetWidth; - this.props.line.width = dom.line.offsetWidth; - this.width = dom.box.offsetWidth; - this.height = dom.box.offsetHeight; + // multi select when holding mouse/touch, or on ctrl+click + this.hammer.on('hold', this._onMultiSelectItem.bind(this)); - this.dirty = false; - } + // add item on doubletap + this.hammer.on('doubletap', this._onAddItem.bind(this)); - this._repaintDeleteButton(dom.box); + // attach to the DOM + this.show(); }; /** - * Show the item in the DOM (when not already displayed). The items DOM will - * be created when needed. + * Set options for the ItemSet. Existing options will be extended/overwritten. + * @param {Object} [options] The following options are available: + * {String} type + * Default type for the items. Choose from 'box' + * (default), 'point', 'range', or 'background'. + * The default style can be overwritten by + * individual items. + * {String} align + * Alignment for the items, only applicable for + * BoxItem. Choose 'center' (default), 'left', or + * 'right'. + * {String} orientation + * Orientation of the item set. Choose 'top' or + * 'bottom' (default). + * {Function} groupOrder + * A sorting function for ordering groups + * {Boolean} stack + * If true (deafult), items will be stacked on + * top of each other. + * {Number} margin.axis + * Margin between the axis and the items in pixels. + * Default is 20. + * {Number} margin.item.horizontal + * Horizontal margin between items in pixels. + * Default is 10. + * {Number} margin.item.vertical + * Vertical Margin between items in pixels. + * Default is 10. + * {Number} margin.item + * Margin between items in pixels in both horizontal + * and vertical direction. Default is 10. + * {Number} margin + * Set margin for both axis and items in pixels. + * {Number} padding + * Padding of the contents of an item in pixels. + * Must correspond with the items css. Default is 5. + * {Boolean} selectable + * If true (default), items can be selected. + * {Boolean} editable + * Set all editable options to true or false + * {Boolean} editable.updateTime + * Allow dragging an item to an other moment in time + * {Boolean} editable.updateGroup + * Allow dragging an item to an other group + * {Boolean} editable.add + * Allow creating new items on double tap + * {Boolean} editable.remove + * Allow removing items by clicking the delete button + * top right of a selected item. + * {Function(item: Item, callback: Function)} onAdd + * Callback function triggered when an item is about to be added: + * when the user double taps an empty space in the Timeline. + * {Function(item: Item, callback: Function)} onUpdate + * Callback function fired when an item is about to be updated. + * This function typically has to show a dialog where the user + * change the item. If not implemented, nothing happens. + * {Function(item: Item, callback: Function)} onMove + * Fired when an item has been moved. If not implemented, + * the move action will be accepted. + * {Function(item: Item, callback: Function)} onRemove + * Fired when an item is about to be deleted. + * If not implemented, the item will be always removed. */ - BoxItem.prototype.show = function() { - if (!this.displayed) { - this.redraw(); + ItemSet.prototype.setOptions = function(options) { + if (options) { + // copy all options that we know + var fields = ['type', 'align', 'orientation', 'padding', 'stack', 'selectable', 'groupOrder', 'dataAttributes', 'template','hide']; + util.selectiveExtend(fields, this.options, options); + + if ('margin' in options) { + if (typeof options.margin === 'number') { + this.options.margin.axis = options.margin; + this.options.margin.item.horizontal = options.margin; + this.options.margin.item.vertical = options.margin; + } + else if (typeof options.margin === 'object') { + util.selectiveExtend(['axis'], this.options.margin, options.margin); + if ('item' in options.margin) { + if (typeof options.margin.item === 'number') { + this.options.margin.item.horizontal = options.margin.item; + this.options.margin.item.vertical = options.margin.item; + } + else if (typeof options.margin.item === 'object') { + util.selectiveExtend(['horizontal', 'vertical'], this.options.margin.item, options.margin.item); + } + } + } + } + + if ('editable' in options) { + if (typeof options.editable === 'boolean') { + this.options.editable.updateTime = options.editable; + this.options.editable.updateGroup = options.editable; + this.options.editable.add = options.editable; + this.options.editable.remove = options.editable; + } + else if (typeof options.editable === 'object') { + util.selectiveExtend(['updateTime', 'updateGroup', 'add', 'remove'], this.options.editable, options.editable); + } + } + + // callback functions + var addCallback = (function (name) { + var fn = options[name]; + if (fn) { + if (!(fn instanceof Function)) { + throw new Error('option ' + name + ' must be a function ' + name + '(item, callback)'); + } + this.options[name] = fn; + } + }).bind(this); + ['onAdd', 'onUpdate', 'onRemove', 'onMove', 'onMoving'].forEach(addCallback); + + // force the itemSet to refresh: options like orientation and margins may be changed + this.markDirty(); } }; /** - * Hide the item from the DOM (when visible) + * Mark the ItemSet dirty so it will refresh everything with next redraw */ - BoxItem.prototype.hide = function() { - if (this.displayed) { - var dom = this.dom; + ItemSet.prototype.markDirty = function() { + this.groupIds = []; + this.stackDirty = true; + }; - if (dom.box.parentNode) dom.box.parentNode.removeChild(dom.box); - if (dom.line.parentNode) dom.line.parentNode.removeChild(dom.line); - if (dom.dot.parentNode) dom.dot.parentNode.removeChild(dom.dot); + /** + * Destroy the ItemSet + */ + ItemSet.prototype.destroy = function() { + this.hide(); + this.setItems(null); + this.setGroups(null); - this.top = null; - this.left = null; + this.hammer = null; - this.displayed = false; - } + this.body = null; + this.conversion = null; }; /** - * Reposition the item horizontally - * @Override + * Hide the component from the DOM */ - BoxItem.prototype.repositionX = function() { - var start = this.conversion.toScreen(this.data.start); - var align = this.options.align; - var left; - var box = this.dom.box; - var line = this.dom.line; - var dot = this.dom.dot; - - // calculate left position of the box - if (align == 'right') { - this.left = start - this.width; + ItemSet.prototype.hide = function() { + // remove the frame containing the items + if (this.dom.frame.parentNode) { + this.dom.frame.parentNode.removeChild(this.dom.frame); } - else if (align == 'left') { - this.left = start; + + // remove the axis with dots + if (this.dom.axis.parentNode) { + this.dom.axis.parentNode.removeChild(this.dom.axis); } - else { - // default or 'center' - this.left = start - this.width / 2; + + // remove the labelset containing all group labels + if (this.dom.labelSet.parentNode) { + this.dom.labelSet.parentNode.removeChild(this.dom.labelSet); } + }; - // reposition box - box.style.left = this.left + 'px'; + /** + * Show the component in the DOM (when not already visible). + * @return {Boolean} changed + */ + ItemSet.prototype.show = function() { + // show frame containing the items + if (!this.dom.frame.parentNode) { + this.body.dom.center.appendChild(this.dom.frame); + } - // reposition line - line.style.left = (start - this.props.line.width / 2) + 'px'; + // show axis with dots + if (!this.dom.axis.parentNode) { + this.body.dom.backgroundVertical.appendChild(this.dom.axis); + } - // reposition dot - dot.style.left = (start - this.props.dot.width / 2) + 'px'; + // show labelset containing labels + if (!this.dom.labelSet.parentNode) { + this.body.dom.left.appendChild(this.dom.labelSet); + } }; /** - * Reposition the item vertically - * @Override + * Set selected items by their id. Replaces the current selection + * Unknown id's are silently ignored. + * @param {string[] | string} [ids] An array with zero or more id's of the items to be + * selected, or a single item id. If ids is undefined + * or an empty array, all items will be unselected. */ - BoxItem.prototype.repositionY = function() { - var orientation = this.options.orientation; - var box = this.dom.box; - var line = this.dom.line; - var dot = this.dom.dot; + ItemSet.prototype.setSelection = function(ids) { + var i, ii, id, item; - if (orientation == 'top') { - box.style.top = (this.top || 0) + 'px'; + if (ids == undefined) ids = []; + if (!Array.isArray(ids)) ids = [ids]; - line.style.top = '0'; - line.style.height = (this.parent.top + this.top + 1) + 'px'; - line.style.bottom = ''; + // unselect currently selected items + for (i = 0, ii = this.selection.length; i < ii; i++) { + id = this.selection[i]; + item = this.items[id]; + if (item) item.unselect(); } - else { // orientation 'bottom' - var itemSetHeight = this.parent.itemSet.props.height; // TODO: this is nasty - var lineHeight = itemSetHeight - this.parent.top - this.parent.height + this.top; - box.style.top = (this.parent.height - this.top - this.height || 0) + 'px'; - line.style.top = (itemSetHeight - lineHeight) + 'px'; - line.style.bottom = '0'; + // select items + this.selection = []; + for (i = 0, ii = ids.length; i < ii; i++) { + id = ids[i]; + item = this.items[id]; + if (item) { + this.selection.push(id); + item.select(); + } } - - dot.style.top = (-this.props.dot.height / 2) + 'px'; }; - module.exports = BoxItem; - - -/***/ }, -/* 34 */ -/***/ function(module, exports, __webpack_require__) { - - var Item = __webpack_require__(31); + /** + * Get the selected items by their id + * @return {Array} ids The ids of the selected items + */ + ItemSet.prototype.getSelection = function() { + return this.selection.concat([]); + }; /** - * @constructor PointItem - * @extends Item - * @param {Object} data Object containing parameters start - * content, className. - * @param {{toScreen: function, toTime: function}} conversion - * Conversion functions from time to screen and vice versa - * @param {Object} [options] Configuration options - * // TODO: describe available options + * Get the id's of the currently visible items. + * @returns {Array} The ids of the visible items */ - function PointItem (data, conversion, options) { - this.props = { - dot: { - top: 0, - width: 0, - height: 0 - }, - content: { - height: 0, - marginLeft: 0 - } - }; + ItemSet.prototype.getVisibleItems = function() { + var range = this.body.range.getRange(); + var left = this.body.util.toScreen(range.start); + var right = this.body.util.toScreen(range.end); - // validate data - if (data) { - if (data.start == undefined) { - throw new Error('Property "start" missing in item ' + data); + var ids = []; + for (var groupId in this.groups) { + if (this.groups.hasOwnProperty(groupId)) { + var group = this.groups[groupId]; + var rawVisibleItems = group.visibleItems; + + // filter the "raw" set with visibleItems into a set which is really + // visible by pixels + for (var i = 0; i < rawVisibleItems.length; i++) { + var item = rawVisibleItems[i]; + // TODO: also check whether visible vertically + if ((item.left < right) && (item.left + item.width > left)) { + ids.push(item.id); + } + } } } - Item.call(this, data, conversion, options); - } - - PointItem.prototype = new Item (null, null, null); + return ids; + }; /** - * Check whether this item is visible inside given range - * @returns {{start: Number, end: Number}} range with a timestamp for start and end - * @returns {boolean} True if visible + * Deselect a selected item + * @param {String | Number} id + * @private */ - PointItem.prototype.isVisible = function(range) { - // determine visibility - // TODO: account for the real width of the item. Right now we just add 1/4 to the window - var interval = (range.end - range.start) / 4; - return (this.data.start > range.start - interval) && (this.data.start < range.end + interval); + ItemSet.prototype._deselect = function(id) { + var selection = this.selection; + for (var i = 0, ii = selection.length; i < ii; i++) { + if (selection[i] == id) { // non-strict comparison! + selection.splice(i, 1); + break; + } + } }; /** - * Repaint the item + * Repaint the component + * @return {boolean} Returns true if the component is resized */ - PointItem.prototype.redraw = function() { - var dom = this.dom; - if (!dom) { - // create DOM - this.dom = {}; - dom = this.dom; + ItemSet.prototype.redraw = function() { + var margin = this.options.margin, + range = this.body.range, + asSize = util.option.asSize, + options = this.options, + orientation = options.orientation, + resized = false, + frame = this.dom.frame, + editable = options.editable.updateTime || options.editable.updateGroup; - // background box - dom.point = document.createElement('div'); - // className is updated in redraw() + // recalculate absolute position (before redrawing groups) + this.props.top = this.body.domProps.top.height + this.body.domProps.border.top; + this.props.left = this.body.domProps.left.width + this.body.domProps.border.left; - // contents box, right from the dot - dom.content = document.createElement('div'); - dom.content.className = 'content'; - dom.point.appendChild(dom.content); + // update class name + frame.className = 'itemset' + (editable ? ' editable' : ''); - // dot at start - dom.dot = document.createElement('div'); - dom.point.appendChild(dom.dot); + // reorder the groups (if needed) + resized = this._orderGroups() || resized; - // attach this item as attribute - dom.point['timeline-item'] = this; + // check whether zoomed (in that case we need to re-stack everything) + // TODO: would be nicer to get this as a trigger from Range + var visibleInterval = range.end - range.start; + var zoomed = (visibleInterval != this.lastVisibleInterval) || (this.props.width != this.props.lastWidth); + if (zoomed) this.stackDirty = true; + this.lastVisibleInterval = visibleInterval; + this.props.lastWidth = this.props.width; - this.dirty = true; - } + var restack = this.stackDirty; + var firstGroup = this._firstGroup(); + var firstMargin = { + item: margin.item, + axis: margin.axis + }; + var nonFirstMargin = { + item: margin.item, + axis: margin.item.vertical / 2 + }; + var height = 0; + var minHeight = margin.axis + margin.item.vertical; - // append DOM to parent DOM - if (!this.parent) { - throw new Error('Cannot redraw item: no parent attached'); - } - if (!dom.point.parentNode) { - var foreground = this.parent.dom.foreground; - if (!foreground) { - throw new Error('Cannot redraw item: parent has no foreground container element'); - } - foreground.appendChild(dom.point); - } - this.displayed = true; - - // Update DOM when item is marked dirty. An item is marked dirty when: - // - the item is not yet rendered - // - the item's data is changed - // - the item is selected/deselected - if (this.dirty) { - this._updateContents(this.dom.content); - this._updateTitle(this.dom.point); - this._updateDataAttributes(this.dom.point); - this._updateStyle(this.dom.point); + // redraw the background group + this.groups[BACKGROUND].redraw(range, nonFirstMargin, restack); - // update class - var className = (this.data.className? ' ' + this.data.className : '') + - (this.selected ? ' selected' : ''); - dom.point.className = 'item point' + className; - dom.dot.className = 'item dot' + className; + // redraw all regular groups + util.forEach(this.groups, function (group) { + var groupMargin = (group == firstGroup) ? firstMargin : nonFirstMargin; + var groupResized = group.redraw(range, groupMargin, restack); + resized = groupResized || resized; + height += group.height; + }); + height = Math.max(height, minHeight); + this.stackDirty = false; - // recalculate size - this.width = dom.point.offsetWidth; - this.height = dom.point.offsetHeight; - this.props.dot.width = dom.dot.offsetWidth; - this.props.dot.height = dom.dot.offsetHeight; - this.props.content.height = dom.content.offsetHeight; + // update frame height + frame.style.height = asSize(height); - // resize contents - dom.content.style.marginLeft = 2 * this.props.dot.width + 'px'; - //dom.content.style.marginRight = ... + 'px'; // TODO: margin right + // calculate actual size + this.props.width = frame.offsetWidth; + this.props.height = height; - dom.dot.style.top = ((this.height - this.props.dot.height) / 2) + 'px'; - dom.dot.style.left = (this.props.dot.width / 2) + 'px'; + // reposition axis + this.dom.axis.style.top = asSize((orientation == 'top') ? + (this.body.domProps.top.height + this.body.domProps.border.top) : + (this.body.domProps.top.height + this.body.domProps.centerContainer.height)); + this.dom.axis.style.left = '0'; - this.dirty = false; - } + // check if this component is resized + resized = this._isResized() || resized; - this._repaintDeleteButton(dom.point); + return resized; }; /** - * Show the item in the DOM (when not already visible). The items DOM will - * be created when needed. + * Get the first group, aligned with the axis + * @return {Group | null} firstGroup + * @private */ - PointItem.prototype.show = function() { - if (!this.displayed) { - this.redraw(); - } + ItemSet.prototype._firstGroup = function() { + var firstGroupIndex = (this.options.orientation == 'top') ? 0 : (this.groupIds.length - 1); + var firstGroupId = this.groupIds[firstGroupIndex]; + var firstGroup = this.groups[firstGroupId] || this.groups[UNGROUPED]; + + return firstGroup || null; }; /** - * Hide the item from the DOM (when visible) + * Create or delete the group holding all ungrouped items. This group is used when + * there are no groups specified. + * @protected */ - PointItem.prototype.hide = function() { - if (this.displayed) { - if (this.dom.point.parentNode) { - this.dom.point.parentNode.removeChild(this.dom.point); + ItemSet.prototype._updateUngrouped = function() { + var ungrouped = this.groups[UNGROUPED]; + var background = this.groups[BACKGROUND]; + var item, itemId; + + if (this.groupsData) { + // remove the group holding all ungrouped items + if (ungrouped) { + ungrouped.hide(); + delete this.groups[UNGROUPED]; + + for (itemId in this.items) { + if (this.items.hasOwnProperty(itemId)) { + item = this.items[itemId]; + item.parent && item.parent.remove(item); + var groupId = this._getGroupId(item.data); + var group = this.groups[groupId]; + group && group.add(item) || item.hide(); + } + } } + } + else { + // create a group holding all (unfiltered) items + if (!ungrouped) { + var id = null; + var data = null; + ungrouped = new Group(id, data, this); + this.groups[UNGROUPED] = ungrouped; - this.top = null; - this.left = null; + for (itemId in this.items) { + if (this.items.hasOwnProperty(itemId)) { + item = this.items[itemId]; + ungrouped.add(item); + } + } - this.displayed = false; + ungrouped.show(); + } } }; /** - * Reposition the item horizontally - * @Override + * Get the element for the labelset + * @return {HTMLElement} labelSet */ - PointItem.prototype.repositionX = function() { - var start = this.conversion.toScreen(this.data.start); - - this.left = start - this.props.dot.width; - - // reposition point - this.dom.point.style.left = this.left + 'px'; + ItemSet.prototype.getLabelSet = function() { + return this.dom.labelSet; }; /** - * Reposition the item vertically - * @Override + * Set items + * @param {vis.DataSet | null} items */ - PointItem.prototype.repositionY = function() { - var orientation = this.options.orientation, - point = this.dom.point; + ItemSet.prototype.setItems = function(items) { + var me = this, + ids, + oldItemsData = this.itemsData; - if (orientation == 'top') { - point.style.top = this.top + 'px'; + // replace the dataset + if (!items) { + this.itemsData = null; + } + else if (items instanceof DataSet || items instanceof DataView) { + this.itemsData = items; } else { - point.style.top = (this.parent.height - this.top - this.height) + 'px'; + throw new TypeError('Data must be an instance of DataSet or DataView'); } - }; - - module.exports = PointItem; - - -/***/ }, -/* 35 */ -/***/ function(module, exports, __webpack_require__) { - var Hammer = __webpack_require__(45); - var Item = __webpack_require__(31); - - /** - * @constructor RangeItem - * @extends Item - * @param {Object} data Object containing parameters start, end - * content, className. - * @param {{toScreen: function, toTime: function}} conversion - * Conversion functions from time to screen and vice versa - * @param {Object} [options] Configuration options - * // TODO: describe options - */ - function RangeItem (data, conversion, options) { - this.props = { - content: { - width: 0 - } - }; - this.overflow = false; // if contents can overflow (css styling), this flag is set to true + if (oldItemsData) { + // unsubscribe from old dataset + util.forEach(this.itemListeners, function (callback, event) { + oldItemsData.off(event, callback); + }); - // validate data - if (data) { - if (data.start == undefined) { - throw new Error('Property "start" missing in item ' + data.id); - } - if (data.end == undefined) { - throw new Error('Property "end" missing in item ' + data.id); - } + // remove all drawn items + ids = oldItemsData.getIds(); + this._onRemove(ids); } - Item.call(this, data, conversion, options); - } + if (this.itemsData) { + // subscribe to new dataset + var id = this.id; + util.forEach(this.itemListeners, function (callback, event) { + me.itemsData.on(event, callback, id); + }); - RangeItem.prototype = new Item (null, null, null); + // add all new items + ids = this.itemsData.getIds(); + this._onAdd(ids); - RangeItem.prototype.baseClassName = 'item range'; + // update the group holding all ungrouped items + this._updateUngrouped(); + } + }; /** - * Check whether this item is visible inside given range - * @returns {{start: Number, end: Number}} range with a timestamp for start and end - * @returns {boolean} True if visible + * Get the current items + * @returns {vis.DataSet | null} */ - RangeItem.prototype.isVisible = function(range) { - // determine visibility - return (this.data.start < range.end) && (this.data.end > range.start); + ItemSet.prototype.getItems = function() { + return this.itemsData; }; /** - * Repaint the item + * Set groups + * @param {vis.DataSet} groups */ - RangeItem.prototype.redraw = function() { - var dom = this.dom; - if (!dom) { - // create DOM - this.dom = {}; - dom = this.dom; - - // background box - dom.box = document.createElement('div'); - // className is updated in redraw() - - // contents box - dom.content = document.createElement('div'); - dom.content.className = 'content'; - dom.box.appendChild(dom.content); + ItemSet.prototype.setGroups = function(groups) { + var me = this, + ids; - // attach this item as attribute - dom.box['timeline-item'] = this; + // unsubscribe from current dataset + if (this.groupsData) { + util.forEach(this.groupListeners, function (callback, event) { + me.groupsData.unsubscribe(event, callback); + }); - this.dirty = true; + // remove all drawn groups + ids = this.groupsData.getIds(); + this.groupsData = null; + this._onRemoveGroups(ids); // note: this will cause a redraw } - // append DOM to parent DOM - if (!this.parent) { - throw new Error('Cannot redraw item: no parent attached'); + // replace the dataset + if (!groups) { + this.groupsData = null; } - if (!dom.box.parentNode) { - var foreground = this.parent.dom.foreground; - if (!foreground) { - throw new Error('Cannot redraw item: parent has no foreground container element'); - } - foreground.appendChild(dom.box); + else if (groups instanceof DataSet || groups instanceof DataView) { + this.groupsData = groups; + } + else { + throw new TypeError('Data must be an instance of DataSet or DataView'); } - this.displayed = true; - - // Update DOM when item is marked dirty. An item is marked dirty when: - // - the item is not yet rendered - // - the item's data is changed - // - the item is selected/deselected - if (this.dirty) { - this._updateContents(this.dom.content); - this._updateTitle(this.dom.box); - this._updateDataAttributes(this.dom.box); - this._updateStyle(this.dom.box); - // update class - var className = (this.data.className ? (' ' + this.data.className) : '') + - (this.selected ? ' selected' : ''); - dom.box.className = this.baseClassName + className; + if (this.groupsData) { + // subscribe to new dataset + var id = this.id; + util.forEach(this.groupListeners, function (callback, event) { + me.groupsData.on(event, callback, id); + }); - // determine from css whether this box has overflow - this.overflow = window.getComputedStyle(dom.content).overflow !== 'hidden'; + // draw all ms + ids = this.groupsData.getIds(); + this._onAddGroups(ids); + } - // recalculate size - // turn off max-width to be able to calculate the real width - // this causes an extra browser repaint/reflow, but so be it - this.dom.content.style.maxWidth = 'none'; - this.props.content.width = this.dom.content.offsetWidth; - this.height = this.dom.box.offsetHeight; - this.dom.content.style.maxWidth = ''; + // update the group holding all ungrouped items + this._updateUngrouped(); - this.dirty = false; - } + // update the order of all items in each group + this._order(); - this._repaintDeleteButton(dom.box); - this._repaintDragLeft(); - this._repaintDragRight(); + this.body.emitter.emit('change', {queue: true}); }; /** - * Show the item in the DOM (when not already visible). The items DOM will - * be created when needed. + * Get the current groups + * @returns {vis.DataSet | null} groups */ - RangeItem.prototype.show = function() { - if (!this.displayed) { - this.redraw(); - } + ItemSet.prototype.getGroups = function() { + return this.groupsData; }; /** - * Hide the item from the DOM (when visible) - * @return {Boolean} changed + * Remove an item by its id + * @param {String | Number} id */ - RangeItem.prototype.hide = function() { - if (this.displayed) { - var box = this.dom.box; - - if (box.parentNode) { - box.parentNode.removeChild(box); - } - - this.top = null; - this.left = null; + ItemSet.prototype.removeItem = function(id) { + var item = this.itemsData.get(id), + dataset = this.itemsData.getDataSet(); - this.displayed = false; + if (item) { + // confirm deletion + this.options.onRemove(item, function (item) { + if (item) { + // remove by id here, it is possible that an item has no id defined + // itself, so better not delete by the item itself + dataset.remove(id); + } + }); } }; /** - * Reposition the item horizontally - * @Override + * Get the time of an item based on it's data and options.type + * @param {Object} itemData + * @returns {string} Returns the type + * @private */ - RangeItem.prototype.repositionX = function() { - var parentWidth = this.parent.width; - var start = this.conversion.toScreen(this.data.start); - var end = this.conversion.toScreen(this.data.end); - var contentLeft; - var contentWidth; - - // limit the width of the this, as browsers cannot draw very wide divs - if (start < -parentWidth) { - start = -parentWidth; - } - if (end > 2 * parentWidth) { - end = 2 * parentWidth; - } - var boxWidth = Math.max(end - start, 1); + ItemSet.prototype._getType = function (itemData) { + return itemData.type || this.options.type || (itemData.end ? 'range' : 'box'); + }; - if (this.overflow) { - this.left = start; - this.width = boxWidth + this.props.content.width; - contentWidth = this.props.content.width; - // Note: The calculation of width is an optimistic calculation, giving - // a width which will not change when moving the Timeline - // So no re-stacking needed, which is nicer for the eye; + /** + * Get the group id for an item + * @param {Object} itemData + * @returns {string} Returns the groupId + * @private + */ + ItemSet.prototype._getGroupId = function (itemData) { + var type = this._getType(itemData); + if (type == 'background' && itemData.group == undefined) { + return BACKGROUND; } else { - this.left = start; - this.width = boxWidth; - contentWidth = Math.min(end - start - 2 * this.options.padding, this.props.content.width); + return this.groupsData ? itemData.group : UNGROUPED; } + }; - this.dom.box.style.left = this.left + 'px'; - this.dom.box.style.width = boxWidth + 'px'; + /** + * Handle updated items + * @param {Number[]} ids + * @protected + */ + ItemSet.prototype._onUpdate = function(ids) { + var me = this; - switch (this.options.align) { - case 'left': - this.dom.content.style.left = '0'; - break; + ids.forEach(function (id) { + var itemData = me.itemsData.get(id, me.itemOptions); + var item = me.items[id]; + var type = me._getType(itemData); - case 'right': - this.dom.content.style.left = Math.max((boxWidth - contentWidth - 2 * this.options.padding), 0) + 'px'; - break; + var constructor = ItemSet.types[type]; - case 'center': - this.dom.content.style.left = Math.max((boxWidth - contentWidth - 2 * this.options.padding) / 2, 0) + 'px'; - break; + if (item) { + // update item + if (!constructor || !(item instanceof constructor)) { + // item type has changed, delete the item and recreate it + me._removeItem(item); + item = null; + } + else { + me._updateItem(item, itemData); + } + } - default: // 'auto' - // when range exceeds left of the window, position the contents at the left of the visible area - if (this.overflow) { - if (end > 0) { - contentLeft = Math.max(-start, 0); - } - else { - contentLeft = -contentWidth; // ensure it's not visible anymore - } + if (!item) { + // create item + if (constructor) { + item = new constructor(itemData, me.conversion, me.options); + item.id = id; // TODO: not so nice setting id afterwards + me._addItem(item); + } + else if (type == 'rangeoverflow') { + // TODO: deprecated since version 2.1.0 (or 3.0.0?). cleanup some day + throw new TypeError('Item type "rangeoverflow" is deprecated. Use css styling instead: ' + + '.vis.timeline .item.range .content {overflow: visible;}'); } else { - if (start < 0) { - contentLeft = Math.min(-start, - (end - start - contentWidth - 2 * this.options.padding)); - // TODO: remove the need for options.padding. it's terrible. - } - else { - contentLeft = 0; - } + throw new TypeError('Unknown item type "' + type + '"'); } - this.dom.content.style.left = contentLeft + 'px'; - } + } + }); + + this._order(); + this.stackDirty = true; // force re-stacking of all items next redraw + this.body.emitter.emit('change', {queue: true}); }; /** - * Reposition the item vertically - * @Override + * Handle added items + * @param {Number[]} ids + * @protected */ - RangeItem.prototype.repositionY = function() { - var orientation = this.options.orientation, - box = this.dom.box; - - if (orientation == 'top') { - box.style.top = this.top + 'px'; - } - else { - box.style.top = (this.parent.height - this.top - this.height) + 'px'; - } - }; + ItemSet.prototype._onAdd = ItemSet.prototype._onUpdate; /** - * Repaint a drag area on the left side of the range when the range is selected + * Handle removed items + * @param {Number[]} ids * @protected */ - RangeItem.prototype._repaintDragLeft = function () { - if (this.selected && this.options.editable.updateTime && !this.dom.dragLeft) { - // create and show drag area - var dragLeft = document.createElement('div'); - dragLeft.className = 'drag-left'; - dragLeft.dragLeftItem = this; - - // TODO: this should be redundant? - Hammer(dragLeft, { - preventDefault: true - }).on('drag', function () { - //console.log('drag left') - }); - - this.dom.box.appendChild(dragLeft); - this.dom.dragLeft = dragLeft; - } - else if (!this.selected && this.dom.dragLeft) { - // delete drag area - if (this.dom.dragLeft.parentNode) { - this.dom.dragLeft.parentNode.removeChild(this.dom.dragLeft); + ItemSet.prototype._onRemove = function(ids) { + var count = 0; + var me = this; + ids.forEach(function (id) { + var item = me.items[id]; + if (item) { + count++; + me._removeItem(item); } - this.dom.dragLeft = null; + }); + + if (count) { + // update order + this._order(); + this.stackDirty = true; // force re-stacking of all items next redraw + this.body.emitter.emit('change', {queue: true}); } }; /** - * Repaint a drag area on the right side of the range when the range is selected - * @protected + * Update the order of item in all groups + * @private */ - RangeItem.prototype._repaintDragRight = function () { - if (this.selected && this.options.editable.updateTime && !this.dom.dragRight) { - // create and show drag area - var dragRight = document.createElement('div'); - dragRight.className = 'drag-right'; - dragRight.dragRightItem = this; - - // TODO: this should be redundant? - Hammer(dragRight, { - preventDefault: true - }).on('drag', function () { - //console.log('drag right') - }); - - this.dom.box.appendChild(dragRight); - this.dom.dragRight = dragRight; - } - else if (!this.selected && this.dom.dragRight) { - // delete drag area - if (this.dom.dragRight.parentNode) { - this.dom.dragRight.parentNode.removeChild(this.dom.dragRight); - } - this.dom.dragRight = null; - } + ItemSet.prototype._order = function() { + // reorder the items in all groups + // TODO: optimization: only reorder groups affected by the changed items + util.forEach(this.groups, function (group) { + group.order(); + }); }; - module.exports = RangeItem; - - -/***/ }, -/* 36 */ -/***/ function(module, exports, __webpack_require__) { - - var Emitter = __webpack_require__(56); - var Hammer = __webpack_require__(45); - var keycharm = __webpack_require__(63); - var util = __webpack_require__(1); - var hammerUtil = __webpack_require__(47); - var DataSet = __webpack_require__(3); - var DataView = __webpack_require__(4); - var dotparser = __webpack_require__(42); - var gephiParser = __webpack_require__(43); - var Groups = __webpack_require__(38); - var Images = __webpack_require__(39); - var Node = __webpack_require__(40); - var Edge = __webpack_require__(37); - var Popup = __webpack_require__(41); - var MixinLoader = __webpack_require__(54); - var Activator = __webpack_require__(55); - var locales = __webpack_require__(49); - - // Load custom shapes into CanvasRenderingContext2D - __webpack_require__(50); - /** - * @constructor Network - * Create a network visualization, displaying nodes and edges. - * - * @param {Element} container The DOM element in which the Network will - * be created. Normally a div element. - * @param {Object} data An object containing parameters - * {Array} nodes - * {Array} edges - * @param {Object} options Options + * Handle updated groups + * @param {Number[]} ids + * @private */ - function Network (container, data, options) { - if (!(this instanceof Network)) { - throw new SyntaxError('Constructor must be called with the new operator'); - } + ItemSet.prototype._onUpdateGroups = function(ids) { + this._onAddGroups(ids); + }; - this._determineBrowserMethod(); - this._initializeMixinLoaders(); + /** + * Handle changed groups (added or updated) + * @param {Number[]} ids + * @private + */ + ItemSet.prototype._onAddGroups = function(ids) { + var me = this; - // create variables and set default values - this.containerElement = container; + ids.forEach(function (id) { + var groupData = me.groupsData.get(id); + var group = me.groups[id]; - // render and calculation settings - this.renderRefreshRate = 60; // hz (fps) - this.renderTimestep = 1000 / this.renderRefreshRate; // ms -- saves calculation later on - this.renderTime = 0; // measured time it takes to render a frame - this.physicsTime = 0; // measured time it takes to render a frame - this.runDoubleSpeed = false; - this.physicsDiscreteStepsize = 0.50; // discrete stepsize of the simulation + if (!group) { + // check for reserved ids + if (id == UNGROUPED || id == BACKGROUND) { + throw new Error('Illegal group id. ' + id + ' is a reserved id.'); + } - this.initializing = true; + var groupOptions = Object.create(me.options); + util.extend(groupOptions, { + height: null + }); - this.triggerFunctions = {add:null,edit:null,editEdge:null,connect:null,del:null}; + group = new Group(id, groupData, me); + me.groups[id] = group; - // set constant values - this.defaultOptions = { - nodes: { - mass: 1, - radiusMin: 10, - radiusMax: 30, - radius: 10, - shape: 'ellipse', - image: undefined, - widthMin: 16, // px - widthMax: 64, // px - fontColor: 'black', - fontSize: 14, // px - fontFace: 'verdana', - fontFill: undefined, - fontStrokeWidth: 0, // px - fontStrokeColor: 'white', - level: -1, - color: { - border: '#2B7CE9', - background: '#97C2FC', - highlight: { - border: '#2B7CE9', - background: '#D2E5FF' - }, - hover: { - border: '#2B7CE9', - background: '#D2E5FF' + // add items with this groupId to the new group + for (var itemId in me.items) { + if (me.items.hasOwnProperty(itemId)) { + var item = me.items[itemId]; + if (item.data.group == id) { + group.add(item); + } } - }, - group: undefined, - borderWidth: 1, - borderWidthSelected: undefined - }, - edges: { - widthMin: 1, // - widthMax: 15,// - width: 1, - widthSelectionMultiplier: 2, - hoverWidth: 1.5, - style: 'line', - color: { - color:'#848484', - highlight:'#848484', - hover: '#848484' - }, - fontColor: '#343434', - fontSize: 14, // px - fontFace: 'arial', - fontFill: 'white', - fontStrokeWidth: 0, // px - fontStrokeColor: 'white', - labelAlignment:'horizontal', - arrowScaleFactor: 1, - dash: { - length: 10, - gap: 5, - altLength: undefined - }, - inheritColor: "from" // to, from, false, true (== from) - }, - configurePhysics:false, - physics: { - barnesHut: { - enabled: true, - thetaInverted: 1 / 0.5, // inverted to save time during calculation - gravitationalConstant: -2000, - centralGravity: 0.3, - springLength: 95, - springConstant: 0.04, - damping: 0.09 - }, - repulsion: { - centralGravity: 0.0, - springLength: 200, - springConstant: 0.05, - nodeDistance: 100, - damping: 0.09 - }, - hierarchicalRepulsion: { - enabled: false, - centralGravity: 0.0, - springLength: 100, - springConstant: 0.01, - nodeDistance: 150, - damping: 0.09 - }, - damping: null, - centralGravity: null, - springLength: null, - springConstant: null - }, - clustering: { // Per Node in Cluster = PNiC - enabled: false, // (Boolean) | global on/off switch for clustering. - initialMaxNodes: 100, // (# nodes) | if the initial amount of nodes is larger than this, we cluster until the total number is less than this threshold. - clusterThreshold:500, // (# nodes) | during calculate forces, we check if the total number of nodes is larger than this. If it is, cluster until reduced to reduceToNodes - reduceToNodes:300, // (# nodes) | during calculate forces, we check if the total number of nodes is larger than clusterThreshold. If it is, cluster until reduced to this - chainThreshold: 0.4, // (% of all drawn nodes)| maximum percentage of allowed chainnodes (long strings of connected nodes) within all nodes. (lower means less chains). - clusterEdgeThreshold: 20, // (px) | edge length threshold. if smaller, this node is clustered. - sectorThreshold: 100, // (# nodes in cluster) | cluster size threshold. If larger, expanding in own sector. - screenSizeThreshold: 0.2, // (% of canvas) | relative size threshold. If the width or height of a clusternode takes up this much of the screen, decluster node. - fontSizeMultiplier: 4.0, // (px PNiC) | how much the cluster font size grows per node in cluster (in px). - maxFontSize: 1000, - forceAmplification: 0.1, // (multiplier PNiC) | factor of increase fo the repulsion force of a cluster (per node in cluster). - distanceAmplification: 0.1, // (multiplier PNiC) | factor how much the repulsion distance of a cluster increases (per node in cluster). - edgeGrowth: 20, // (px PNiC) | amount of clusterSize connected to the edge is multiplied with this and added to edgeLength. - nodeScaling: {width: 1, // (px PNiC) | growth of the width per node in cluster. - height: 1, // (px PNiC) | growth of the height per node in cluster. - radius: 1}, // (px PNiC) | growth of the radius per node in cluster. - maxNodeSizeIncrements: 600, // (# increments) | max growth of the width per node in cluster. - activeAreaBoxSize: 80, // (px) | box area around the curser where clusters are popped open. - clusterLevelDifference: 2 - }, - navigation: { - enabled: false - }, - keyboard: { - enabled: false, - speed: {x: 10, y: 10, zoom: 0.02}, - bindToWindow: true - }, - dataManipulation: { - enabled: false, - initiallyVisible: false - }, - hierarchicalLayout: { - enabled:false, - levelSeparation: 150, - nodeSpacing: 100, - direction: "UD", // UD, DU, LR, RL - layout: "hubsize" // hubsize, directed - }, - freezeForStabilization: false, - smoothCurves: { - enabled: true, - dynamic: true, - type: "continuous", - roundness: 0.5 - }, - maxVelocity: 30, - minVelocity: 0.1, // px/s - stabilize: true, // stabilize before displaying the network - stabilizationIterations: 1000, // maximum number of iteration to stabilize - zoomExtentOnStabilize: true, - locale: 'en', - locales: locales, - tooltip: { - delay: 300, - fontColor: 'black', - fontSize: 14, // px - fontFace: 'verdana', - color: { - border: '#666', - background: '#FFFFC6' } - }, - dragNetwork: true, - dragNodes: true, - zoomable: true, - hover: false, - hideEdgesOnDrag: false, - hideNodesOnDrag: false, - width : '100%', - height : '100%', - selectable: true - }; - this.constants = util.extend({}, this.defaultOptions); - this.pixelRatio = 1; - - - this.hoverObj = {nodes:{},edges:{}}; - this.controlNodesActive = false; - this.navigationHammers = {existing:[], _new: []}; - - // animation properties - this.animationSpeed = 1/this.renderRefreshRate; - this.animationEasingFunction = "easeInOutQuint"; - this.animating = false; - this.easingTime = 0; - this.sourceScale = 0; - this.targetScale = 0; - this.sourceTranslation = 0; - this.targetTranslation = 0; - this.lockedOnNodeId = null; - this.lockedOnNodeOffset = null; - this.touchTime = 0; - // Node variables - var network = this; - this.groups = new Groups(); // object with groups - this.images = new Images(); // object with images - this.images.setOnloadCallback(function (status) { - network._redraw(); + group.order(); + group.show(); + } + else { + // update group + group.setData(groupData); + } }); - // keyboard navigation variables - this.xIncrement = 0; - this.yIncrement = 0; - this.zoomIncrement = 0; + this.body.emitter.emit('change', {queue: true}); + }; - // loading all the mixins: - // load the force calculation functions, grouped under the physics system. - this._loadPhysicsSystem(); - // create a frame and canvas - this._create(); - // load the sector system. (mandatory, fully integrated with Network) - this._loadSectorSystem(); - // load the cluster system. (mandatory, even when not using the cluster system, there are function calls to it) - this._loadClusterSystem(); - // load the selection system. (mandatory, required by Network) - this._loadSelectionSystem(); - // load the selection system. (mandatory, required by Network) - this._loadHierarchySystem(); + /** + * Handle removed groups + * @param {Number[]} ids + * @private + */ + ItemSet.prototype._onRemoveGroups = function(ids) { + var groups = this.groups; + ids.forEach(function (id) { + var group = groups[id]; + if (group) { + group.hide(); + delete groups[id]; + } + }); - // apply options - this._setTranslation(this.frame.clientWidth / 2, this.frame.clientHeight / 2); - this._setScale(1); - this.setOptions(options); + this.markDirty(); - // other vars - this.freezeSimulation = false;// freeze the simulation - this.cachedFunctions = {}; - this.startedStabilization = false; - this.stabilized = false; - this.stabilizationIterations = null; - this.draggingNodes = false; + this.body.emitter.emit('change', {queue: true}); + }; - // containers for nodes and edges - this.calculationNodes = {}; - this.calculationNodeIndices = []; - this.nodeIndices = []; // array with all the indices of the nodes. Used to speed up forces calculation - this.nodes = {}; // object with Node objects - this.edges = {}; // object with Edge objects + /** + * Reorder the groups if needed + * @return {boolean} changed + * @private + */ + ItemSet.prototype._orderGroups = function () { + if (this.groupsData) { + // reorder the groups + var groupIds = this.groupsData.getIds({ + order: this.options.groupOrder + }); - // position and scale variables and objects - this.canvasTopLeft = {"x": 0,"y": 0}; // coordinates of the top left of the canvas. they will be set during _redraw. - this.canvasBottomRight = {"x": 0,"y": 0}; // coordinates of the bottom right of the canvas. they will be set during _redraw - this.pointerPosition = {"x": 0,"y": 0}; // coordinates of the bottom right of the canvas. they will be set during _redraw - this.areaCenter = {}; // object with x and y elements used for determining the center of the zoom action - this.scale = 1; // defining the global scale variable in the constructor - this.previousScale = this.scale; // this is used to check if the zoom operation is zooming in or out + var changed = !util.equalArray(groupIds, this.groupIds); + if (changed) { + // hide all groups, removes them from the DOM + var groups = this.groups; + groupIds.forEach(function (groupId) { + groups[groupId].hide(); + }); - // datasets or dataviews - this.nodesData = null; // A DataSet or DataView - this.edgesData = null; // A DataSet or DataView + // show the groups again, attach them to the DOM in correct order + groupIds.forEach(function (groupId) { + groups[groupId].show(); + }); - // create event listeners used to subscribe on the DataSets of the nodes and edges - this.nodesListeners = { - 'add': function (event, params) { - network._addNodes(params.items); - network.start(); - }, - 'update': function (event, params) { - network._updateNodes(params.items, params.data); - network.start(); - }, - 'remove': function (event, params) { - network._removeNodes(params.items); - network.start(); - } - }; - this.edgesListeners = { - 'add': function (event, params) { - network._addEdges(params.items); - network.start(); - }, - 'update': function (event, params) { - network._updateEdges(params.items); - network.start(); - }, - 'remove': function (event, params) { - network._removeEdges(params.items); - network.start(); + this.groupIds = groupIds; } - }; - - // properties for the animation - this.moving = true; - this.timer = undefined; // Scheduling function. Is definded in this.start(); - - // load data (the disable start variable will be the same as the enabled clustering) - this.setData(data,this.constants.clustering.enabled || this.constants.hierarchicalLayout.enabled); - // hierarchical layout - this.initializing = false; - if (this.constants.hierarchicalLayout.enabled == true) { - this._setupHierarchicalLayout(); + return changed; } else { - // zoom so all data will fit on the screen, if clustering is enabled, we do not want start to be called here. - if (this.constants.stabilize == false) { - this.zoomExtent(undefined, true,this.constants.clustering.enabled); - } - } - - // if clustering is disabled, the simulation will have started in the setData function - if (this.constants.clustering.enabled) { - this.startWithClustering(); + return false; } - } - - // Extend Network with an Emitter mixin - Emitter(Network.prototype); + }; /** - * Determine if the browser requires a setTimeout or a requestAnimationFrame. This was required because - * some implementations (safari and IE9) did not support requestAnimationFrame + * Add a new item + * @param {Item} item * @private */ - Network.prototype._determineBrowserMethod = function() { - var browserType = navigator.userAgent.toLowerCase(); - this.requiresTimeout = false; - if (browserType.indexOf('msie 9.0') != -1) { // IE 9 - this.requiresTimeout = true; - } - else if (browserType.indexOf('safari') != -1) { // safari - if (browserType.indexOf('chrome') <= -1) { - this.requiresTimeout = true; - } - } - } + ItemSet.prototype._addItem = function(item) { + this.items[item.id] = item; + // add to group + var groupId = this._getGroupId(item.data); + var group = this.groups[groupId]; + if (group) group.add(item); + }; /** - * Get the script path where the vis.js library is located - * - * @returns {string | null} path Path or null when not found. Path does not - * end with a slash. + * Update an existing item + * @param {Item} item + * @param {Object} itemData * @private */ - Network.prototype._getScriptPath = function() { - var scripts = document.getElementsByTagName( 'script' ); + ItemSet.prototype._updateItem = function(item, itemData) { + var oldGroupId = item.data.group; - // find script named vis.js or vis.min.js - for (var i = 0; i < scripts.length; i++) { - var src = scripts[i].src; - var match = src && /\/?vis(.min)?\.js$/.exec(src); - if (match) { - // return path without the script name - return src.substring(0, src.length - match[0].length); - } - } - - return null; - }; + // update the items data (will redraw the item when displayed) + item.setData(itemData); + // update group + if (oldGroupId != item.data.group) { + var oldGroup = this.groups[oldGroupId]; + if (oldGroup) oldGroup.remove(item); - /** - * Find the center position of the network - * @private - */ - Network.prototype._getRange = function() { - var minY = 1e9, maxY = -1e9, minX = 1e9, maxX = -1e9, node; - for (var nodeId in this.nodes) { - if (this.nodes.hasOwnProperty(nodeId)) { - node = this.nodes[nodeId]; - if (minX > (node.boundingBox.left)) {minX = node.boundingBox.left;} - if (maxX < (node.boundingBox.right)) {maxX = node.boundingBox.right;} - if (minY > (node.boundingBox.bottom)) {minY = node.boundingBox.top;} // top is negative, bottom is positive - if (maxY < (node.boundingBox.top)) {maxY = node.boundingBox.bottom;} // top is negative, bottom is positive - } - } - if (minX == 1e9 && maxX == -1e9 && minY == 1e9 && maxY == -1e9) { - minY = 0, maxY = 0, minX = 0, maxX = 0; + var groupId = this._getGroupId(item.data); + var group = this.groups[groupId]; + if (group) group.add(item); } - return {minX: minX, maxX: maxX, minY: minY, maxY: maxY}; }; - /** - * @param {object} range = {minX: minX, maxX: maxX, minY: minY, maxY: maxY}; - * @returns {{x: number, y: number}} + * Delete an item from the ItemSet: remove it from the DOM, from the map + * with items, and from the map with visible items, and from the selection + * @param {Item} item * @private */ - Network.prototype._findCenter = function(range) { - return {x: (0.5 * (range.maxX + range.minX)), - y: (0.5 * (range.maxY + range.minY))}; - }; - - - /** - * This function zooms out to fit all data on screen based on amount of nodes - * - * @param {Boolean} [initialZoom] | zoom based on fitted formula or range, true = fitted, default = false; - * @param {Boolean} [disableStart] | If true, start is not called. - */ - Network.prototype.zoomExtent = function(animationOptions, initialZoom, disableStart) { - this._redraw(true); - - if (initialZoom === undefined) {initialZoom = false;} - if (disableStart === undefined) {disableStart = false;} - if (animationOptions === undefined) {animationOptions = false;} - - var range = this._getRange(); - var zoomLevel; - - if (initialZoom == true) { - var numberOfNodes = this.nodeIndices.length; - if (this.constants.smoothCurves == true) { - if (this.constants.clustering.enabled == true && - numberOfNodes >= this.constants.clustering.initialMaxNodes) { - zoomLevel = 49.07548 / (numberOfNodes + 142.05338) + 9.1444e-04; // this is obtained from fitting a dataset from 5 points with scale levels that looked good. - } - else { - zoomLevel = 12.662 / (numberOfNodes + 7.4147) + 0.0964822; // this is obtained from fitting a dataset from 5 points with scale levels that looked good. - } - } - else { - if (this.constants.clustering.enabled == true && - numberOfNodes >= this.constants.clustering.initialMaxNodes) { - zoomLevel = 77.5271985 / (numberOfNodes + 187.266146) + 4.76710517e-05; // this is obtained from fitting a dataset from 5 points with scale levels that looked good. - } - else { - zoomLevel = 30.5062972 / (numberOfNodes + 19.93597763) + 0.08413486; // this is obtained from fitting a dataset from 5 points with scale levels that looked good. - } - } - - // correct for larger canvasses. - var factor = Math.min(this.frame.canvas.clientWidth / 600, this.frame.canvas.clientHeight / 600); - zoomLevel *= factor; - } - else { - var xDistance = Math.abs(range.maxX - range.minX) * 1.1; - var yDistance = Math.abs(range.maxY - range.minY) * 1.1; - - var xZoomLevel = this.frame.canvas.clientWidth / xDistance; - var yZoomLevel = this.frame.canvas.clientHeight / yDistance; - zoomLevel = (xZoomLevel <= yZoomLevel) ? xZoomLevel : yZoomLevel; - } + ItemSet.prototype._removeItem = function(item) { + // remove from DOM + item.hide(); - if (zoomLevel > 1.0) { - zoomLevel = 1.0; - } + // remove from items + delete this.items[item.id]; + // remove from selection + var index = this.selection.indexOf(item.id); + if (index != -1) this.selection.splice(index, 1); - var center = this._findCenter(range); - if (disableStart == false) { - var options = {position: center, scale: zoomLevel, animation: animationOptions}; - this.moveTo(options); - this.moving = true; - this.start(); - } - else { - center.x *= zoomLevel; - center.y *= zoomLevel; - center.x -= 0.5 * this.frame.canvas.clientWidth; - center.y -= 0.5 * this.frame.canvas.clientHeight; - this._setScale(zoomLevel); - this._setTranslation(-center.x,-center.y); - } + // remove from group + item.parent && item.parent.remove(item); }; - /** - * Update the this.nodeIndices with the most recent node index list + * Create an array containing all items being a range (having an end date) + * @param array + * @returns {Array} * @private */ - Network.prototype._updateNodeIndexList = function() { - this._clearNodeIndexList(); - for (var idx in this.nodes) { - if (this.nodes.hasOwnProperty(idx)) { - this.nodeIndices.push(idx); + ItemSet.prototype._constructByEndArray = function(array) { + var endArray = []; + + for (var i = 0; i < array.length; i++) { + if (array[i] instanceof RangeItem) { + endArray.push(array[i]); } } + return endArray; }; - /** - * Set nodes and edges, and optionally options as well. + * Register the clicked item on touch, before dragStart is initiated. * - * @param {Object} data Object containing parameters: - * {Array | DataSet | DataView} [nodes] Array with nodes - * {Array | DataSet | DataView} [edges] Array with edges - * {String} [dot] String containing data in DOT format - * {String} [gephi] String containing data in gephi JSON format - * {Options} [options] Object with options - * @param {Boolean} [disableStart] | optional: disable the calling of the start function. + * dragStart is initiated from a mousemove event, which can have left the item + * already resulting in an item == null + * + * @param {Event} event + * @private */ - Network.prototype.setData = function(data, disableStart) { - if (disableStart === undefined) { - disableStart = false; - } - // we set initializing to true to ensure that the hierarchical layout is not performed until both nodes and edges are added. - this.initializing = true; - - if (data && data.dot && (data.nodes || data.edges)) { - throw new SyntaxError('Data must contain either parameter "dot" or ' + - ' parameter pair "nodes" and "edges", but not both.'); - } - - // clean up in case there is anyone in an active mode of the manipulation. This is the same option as bound to the escape button. - if (this.constants.dataManipulation.enabled == true) { - this._createManipulatorBar(); - } - - // set options - this.setOptions(data && data.options); - // set all data - if (data && data.dot) { - // parse DOT file - if(data && data.dot) { - var dotData = dotparser.DOTToGraph(data.dot); - this.setData(dotData); - return; - } - } - else if (data && data.gephi) { - // parse DOT file - if(data && data.gephi) { - var gephiData = gephiParser.parseGephi(data.gephi); - this.setData(gephiData); - return; - } - } - else { - this._setNodes(data && data.nodes); - this._setEdges(data && data.edges); - } - this._putDataInSector(); - if (disableStart == false) { - if (this.constants.hierarchicalLayout.enabled == true) { - this._resetLevels(); - this._setupHierarchicalLayout(); - } - else { - // find a stable position or start animating to a stable position - if (this.constants.stabilize) { - this._stabilize(); - } - } - this.start(); - } - this.initializing = false; + ItemSet.prototype._onTouch = function (event) { + // store the touched item, used in _onDragStart + this.touchParams.item = ItemSet.itemFromTarget(event); }; /** - * Set options - * @param {Object} options + * Start dragging the selected events + * @param {Event} event + * @private */ - Network.prototype.setOptions = function (options) { - if (options) { - var prop; - var fields = ['nodes','edges','smoothCurves','hierarchicalLayout','clustering','navigation', - 'keyboard','dataManipulation','onAdd','onEdit','onEditEdge','onConnect','onDelete','clickToUse' - ]; - // extend all but the values in fields - util.selectiveNotDeepExtend(fields,this.constants, options); - util.selectiveNotDeepExtend(['color'],this.constants.nodes, options.nodes); - util.selectiveNotDeepExtend(['color','length'],this.constants.edges, options.edges); - - if (options.physics) { - util.mergeOptions(this.constants.physics, options.physics,'barnesHut'); - util.mergeOptions(this.constants.physics, options.physics,'repulsion'); - - if (options.physics.hierarchicalRepulsion) { - this.constants.hierarchicalLayout.enabled = true; - this.constants.physics.hierarchicalRepulsion.enabled = true; - this.constants.physics.barnesHut.enabled = false; - for (prop in options.physics.hierarchicalRepulsion) { - if (options.physics.hierarchicalRepulsion.hasOwnProperty(prop)) { - this.constants.physics.hierarchicalRepulsion[prop] = options.physics.hierarchicalRepulsion[prop]; - } - } - } - } - - if (options.onAdd) {this.triggerFunctions.add = options.onAdd;} - if (options.onEdit) {this.triggerFunctions.edit = options.onEdit;} - if (options.onEditEdge) {this.triggerFunctions.editEdge = options.onEditEdge;} - if (options.onConnect) {this.triggerFunctions.connect = options.onConnect;} - if (options.onDelete) {this.triggerFunctions.del = options.onDelete;} - - util.mergeOptions(this.constants, options,'smoothCurves'); - util.mergeOptions(this.constants, options,'hierarchicalLayout'); - util.mergeOptions(this.constants, options,'clustering'); - util.mergeOptions(this.constants, options,'navigation'); - util.mergeOptions(this.constants, options,'keyboard'); - util.mergeOptions(this.constants, options,'dataManipulation'); + ItemSet.prototype._onDragStart = function (event) { + if (!this.options.editable.updateTime && !this.options.editable.updateGroup) { + return; + } + var item = this.touchParams.item || null; + var me = this; + var props; - if (options.dataManipulation) { - this.editMode = this.constants.dataManipulation.initiallyVisible; - } + if (item && item.selected) { + var dragLeftItem = event.target.dragLeftItem; + var dragRightItem = event.target.dragRightItem; + if (dragLeftItem) { + props = { + item: dragLeftItem, + initialX: event.gesture.center.clientX + }; - // TODO: work out these options and document them - if (options.edges) { - if (options.edges.color !== undefined) { - if (util.isString(options.edges.color)) { - this.constants.edges.color = {}; - this.constants.edges.color.color = options.edges.color; - this.constants.edges.color.highlight = options.edges.color; - this.constants.edges.color.hover = options.edges.color; - } - else { - if (options.edges.color.color !== undefined) {this.constants.edges.color.color = options.edges.color.color;} - if (options.edges.color.highlight !== undefined) {this.constants.edges.color.highlight = options.edges.color.highlight;} - if (options.edges.color.hover !== undefined) {this.constants.edges.color.hover = options.edges.color.hover;} - } - this.constants.edges.inheritColor = false; + if (me.options.editable.updateTime) { + props.start = item.data.start.valueOf(); } - - if (!options.edges.fontColor) { - if (options.edges.color !== undefined) { - if (util.isString(options.edges.color)) {this.constants.edges.fontColor = options.edges.color;} - else if (options.edges.color.color !== undefined) {this.constants.edges.fontColor = options.edges.color.color;} - } + if (me.options.editable.updateGroup) { + if ('group' in item.data) props.group = item.data.group; } - } - if (options.nodes) { - if (options.nodes.color) { - var newColorObj = util.parseColor(options.nodes.color); - this.constants.nodes.color.background = newColorObj.background; - this.constants.nodes.color.border = newColorObj.border; - this.constants.nodes.color.highlight.background = newColorObj.highlight.background; - this.constants.nodes.color.highlight.border = newColorObj.highlight.border; - this.constants.nodes.color.hover.background = newColorObj.hover.background; - this.constants.nodes.color.hover.border = newColorObj.hover.border; - } - } - if (options.groups) { - for (var groupname in options.groups) { - if (options.groups.hasOwnProperty(groupname)) { - var group = options.groups[groupname]; - this.groups.add(groupname, group); - } - } + this.touchParams.itemProps = [props]; } + else if (dragRightItem) { + props = { + item: dragRightItem, + initialX: event.gesture.center.clientX + }; - if (options.tooltip) { - for (prop in options.tooltip) { - if (options.tooltip.hasOwnProperty(prop)) { - this.constants.tooltip[prop] = options.tooltip[prop]; - } + if (me.options.editable.updateTime) { + props.end = item.data.end.valueOf(); } - if (options.tooltip.color) { - this.constants.tooltip.color = util.parseColor(options.tooltip.color); + if (me.options.editable.updateGroup) { + if ('group' in item.data) props.group = item.data.group; } + + this.touchParams.itemProps = [props]; } + else { + this.touchParams.itemProps = this.getSelection().map(function (id) { + var item = me.items[id]; + var props = { + item: item, + initialX: event.gesture.center.clientX + }; - if ('clickToUse' in options) { - if (options.clickToUse) { - if (!this.activator) { - this.activator = new Activator(this.frame); - this.activator.on('change', this._createKeyBinds.bind(this)); + if (me.options.editable.updateTime) { + if ('start' in item.data) props.start = item.data.start.valueOf(); + if ('end' in item.data) props.end = item.data.end.valueOf(); } - } - else { - if (this.activator) { - this.activator.destroy(); - delete this.activator; + if (me.options.editable.updateGroup) { + if ('group' in item.data) props.group = item.data.group; } - } - } - if (options.labels) { - throw new Error('Option "labels" is deprecated. Use options "locale" and "locales" instead.'); + return props; + }); } - - // (Re)loading the mixins that can be enabled or disabled in the options. - // load the force calculation functions, grouped under the physics system. - this._loadPhysicsSystem(); - // load the navigation system. - this._loadNavigationControls(); - // load the data manipulation system - this._loadManipulationSystem(); - // configure the smooth curves - this._configureSmoothCurves(); - - // bind hammer - this._bindHammer(); - - // bind keys. If disabled, this will not do anything; - this._createKeyBinds(); - - - this.setSize(this.constants.width, this.constants.height); - this.moving = true; - this.start(); + event.stopPropagation(); } }; - - /** - * Create the main frame for the Network. - * This function is executed once when a Network object is created. The frame - * contains a canvas, and this canvas contains all objects like the axis and - * nodes. + * Drag selected items + * @param {Event} event * @private */ - Network.prototype._create = function () { - // remove all elements from the container element. - while (this.containerElement.hasChildNodes()) { - this.containerElement.removeChild(this.containerElement.firstChild); - } + ItemSet.prototype._onDrag = function (event) { + event.preventDefault() - this.frame = document.createElement('div'); - this.frame.className = 'vis network-frame'; - this.frame.style.position = 'relative'; - this.frame.style.overflow = 'hidden'; - this.frame.tabIndex = 900; + if (this.touchParams.itemProps) { + var me = this; + var snap = this.body.util.snap || null; + var xOffset = this.body.dom.root.offsetLeft + this.body.domProps.left.width; + // move + this.touchParams.itemProps.forEach(function (props) { + var newProps = {}; + var current = me.body.util.toTime(event.gesture.center.clientX - xOffset); + var initial = me.body.util.toTime(props.initialX - xOffset); + var offset = current - initial; - ////////////////////////////////////////////////////////////////// + if ('start' in props) { + var start = new Date(props.start + offset); + newProps.start = snap ? snap(start) : start; + } - this.frame.canvas = document.createElement("canvas"); - this.frame.canvas.style.position = 'relative'; - this.frame.appendChild(this.frame.canvas); + if ('end' in props) { + var end = new Date(props.end + offset); + newProps.end = snap ? snap(end) : end; + } - if (!this.frame.canvas.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.frame.canvas.appendChild(noCanvas); - } - else { - var ctx = this.frame.canvas.getContext("2d"); - this.pixelRatio = (window.devicePixelRatio || 1) / (ctx.webkitBackingStorePixelRatio || - ctx.mozBackingStorePixelRatio || - ctx.msBackingStorePixelRatio || - ctx.oBackingStorePixelRatio || - ctx.backingStorePixelRatio || 1); + if ('group' in props) { + // drag from one group to another + var group = ItemSet.groupFromTarget(event); + newProps.group = group && group.groupId; + } - this.frame.canvas.getContext("2d").setTransform(this.pixelRatio, 0, 0, this.pixelRatio, 0, 0); - } + // confirm moving the item + var itemData = util.extend({}, props.item.data, newProps); + me.options.onMoving(itemData, function (itemData) { + if (itemData) { + me._updateItemProps(props.item, itemData); + } + }); + }); - this._bindHammer(); - }; + this.stackDirty = true; // force re-stacking of all items next redraw + this.body.emitter.emit('change'); + event.stopPropagation(); + } + }; /** - * This function binds hammer, it can be repeated over and over due to the uniqueness check. + * Update an items properties + * @param {Item} item + * @param {Object} props Can contain properties start, end, and group. * @private */ - Network.prototype._bindHammer = function() { - var me = this; - if (this.hammer !== undefined) { - this.hammer.dispose(); - } - this.drag = {}; - this.pinch = {}; - this.hammer = Hammer(this.frame.canvas, { - prevent_default: true - }); - this.hammer.on('tap', me._onTap.bind(me) ); - this.hammer.on('doubletap', me._onDoubleTap.bind(me) ); - this.hammer.on('hold', me._onHold.bind(me) ); - this.hammer.on('touch', me._onTouch.bind(me) ); - this.hammer.on('dragstart', me._onDragStart.bind(me) ); - this.hammer.on('drag', me._onDrag.bind(me) ); - this.hammer.on('dragend', me._onDragEnd.bind(me) ); - - if (this.constants.zoomable == true) { - this.hammer.on('mousewheel', me._onMouseWheel.bind(me)); - this.hammer.on('DOMMouseScroll', me._onMouseWheel.bind(me)); // for FF - this.hammer.on('pinch', me._onPinch.bind(me) ); + ItemSet.prototype._updateItemProps = function(item, props) { + // TODO: copy all properties from props to item? (also new ones) + if ('start' in props) item.data.start = props.start; + if ('end' in props) item.data.end = props.end; + if ('group' in props && item.data.group != props.group) { + this._moveToGroup(item, props.group) } - - this.hammer.on('mousemove', me._onMouseMoveTitle.bind(me) ); - - this.hammerFrame = Hammer(this.frame, { - prevent_default: true - }); - this.hammerFrame.on('release', me._onRelease.bind(me) ); - - // add the frame to the container element - this.containerElement.appendChild(this.frame); - } + }; /** - * Binding the keys for keyboard navigation. These functions are defined in the NavigationMixin + * Move an item to another group + * @param {Item} item + * @param {String | Number} groupId * @private */ - Network.prototype._createKeyBinds = function() { - var me = this; - if (this.keycharm !== undefined) { - this.keycharm.destroy(); - } - - if (this.constants.keyboard.bindToWindow == true) { - this.keycharm = keycharm({container: window, preventDefault: false}); - } - else { - this.keycharm = keycharm({container: this.frame, preventDefault: false}); - } - - this.keycharm.reset(); - - if (this.constants.keyboard.enabled && this.isActive()) { - this.keycharm.bind("up", this._moveUp.bind(me) , "keydown"); - this.keycharm.bind("up", this._yStopMoving.bind(me), "keyup"); - this.keycharm.bind("down", this._moveDown.bind(me) , "keydown"); - this.keycharm.bind("down", this._yStopMoving.bind(me), "keyup"); - this.keycharm.bind("left", this._moveLeft.bind(me) , "keydown"); - this.keycharm.bind("left", this._xStopMoving.bind(me), "keyup"); - this.keycharm.bind("right",this._moveRight.bind(me), "keydown"); - this.keycharm.bind("right",this._xStopMoving.bind(me), "keyup"); - this.keycharm.bind("=", this._zoomIn.bind(me), "keydown"); - this.keycharm.bind("=", this._stopZoom.bind(me), "keyup"); - this.keycharm.bind("num+", this._zoomIn.bind(me), "keydown"); - this.keycharm.bind("num+", this._stopZoom.bind(me), "keyup"); - this.keycharm.bind("num-", this._zoomOut.bind(me), "keydown"); - this.keycharm.bind("num-", this._stopZoom.bind(me), "keyup"); - this.keycharm.bind("-", this._zoomOut.bind(me), "keydown"); - this.keycharm.bind("-", this._stopZoom.bind(me), "keyup"); - this.keycharm.bind("[", this._zoomIn.bind(me), "keydown"); - this.keycharm.bind("[", this._stopZoom.bind(me), "keyup"); - this.keycharm.bind("]", this._zoomOut.bind(me), "keydown"); - this.keycharm.bind("]", this._stopZoom.bind(me), "keyup"); - this.keycharm.bind("pageup",this._zoomIn.bind(me), "keydown"); - this.keycharm.bind("pageup",this._stopZoom.bind(me), "keyup"); - this.keycharm.bind("pagedown",this._zoomOut.bind(me),"keydown"); - this.keycharm.bind("pagedown",this._stopZoom.bind(me), "keyup"); - } + ItemSet.prototype._moveToGroup = function(item, groupId) { + var group = this.groups[groupId]; + if (group && group.groupId != item.data.group) { + var oldGroup = item.parent; + oldGroup.remove(item); + oldGroup.order(); + group.add(item); + group.order(); - if (this.constants.dataManipulation.enabled == true) { - this.keycharm.bind("esc",this._createManipulatorBar.bind(me)); - this.keycharm.bind("delete",this._deleteSelected.bind(me)); + item.data.group = group.groupId; } }; /** - * Cleans up all bindings of the network, removing it fully from the memory IF the variable is set to null after calling this function. - * var network = new vis.Network(..); - * network.destroy(); - * network = null; + * End of dragging selected items + * @param {Event} event + * @private */ - Network.prototype.destroy = function() { - this.start = function () {}; - this.redraw = function () {}; - this.timer = false; + ItemSet.prototype._onDragEnd = function (event) { + event.preventDefault() - // cleanup physicsConfiguration if it exists - this._cleanupPhysicsConfiguration(); + if (this.touchParams.itemProps) { + // prepare a change set for the changed items + var changes = [], + me = this, + dataset = this.itemsData.getDataSet(); - // remove keybindings - this.keycharm.reset(); + var itemProps = this.touchParams.itemProps ; + this.touchParams.itemProps = null; + itemProps.forEach(function (props) { + var id = props.item.id, + itemData = me.itemsData.get(id, me.itemOptions); - // clear hammer bindings - this.hammer.dispose(); + var changed = false; + if ('start' in props.item.data) { + changed = (props.start != props.item.data.start.valueOf()); + itemData.start = util.convert(props.item.data.start, + dataset._options.type && dataset._options.type.start || 'Date'); + } + if ('end' in props.item.data) { + changed = changed || (props.end != props.item.data.end.valueOf()); + itemData.end = util.convert(props.item.data.end, + dataset._options.type && dataset._options.type.end || 'Date'); + } + if ('group' in props.item.data) { + changed = changed || (props.group != props.item.data.group); + itemData.group = props.item.data.group; + } - // clear events - this.off(); + // only apply changes when start or end is actually changed + if (changed) { + me.options.onMove(itemData, function (itemData) { + if (itemData) { + // apply changes + itemData[dataset._fieldId] = id; // ensure the item contains its id (can be undefined) + changes.push(itemData); + } + else { + // restore original values + me._updateItemProps(props.item, props); - this._recursiveDOMDelete(this.containerElement); - } + me.stackDirty = true; // force re-stacking of all items next redraw + me.body.emitter.emit('change'); + } + }); + } + }); - Network.prototype._recursiveDOMDelete = function(DOMobject) { - while (DOMobject.hasChildNodes() == true) { - this._recursiveDOMDelete(DOMobject.firstChild); - DOMobject.removeChild(DOMobject.firstChild); - } - } + // apply the changes to the data (if there are changes) + if (changes.length) { + dataset.update(changes); + } - /** - * Get the pointer location from a touch location - * @param {{pageX: Number, pageY: Number}} touch - * @return {{x: Number, y: Number}} pointer - * @private - */ - Network.prototype._getPointer = function (touch) { - return { - x: touch.pageX - util.getAbsoluteLeft(this.frame.canvas), - y: touch.pageY - util.getAbsoluteTop(this.frame.canvas) - }; + event.stopPropagation(); + } }; /** - * On start of a touch gesture, store the pointer - * @param event + * Handle selecting/deselecting an item when tapping it + * @param {Event} event * @private */ - Network.prototype._onTouch = function (event) { - if (new Date().valueOf() - this.touchTime > 100) { - this.drag.pointer = this._getPointer(event.gesture.center); - this.drag.pinched = false; - this.pinch.scale = this._getScale(); + ItemSet.prototype._onSelectItem = function (event) { + if (!this.options.selectable) return; - // to avoid double fireing of this event because we have two hammer instances. (on canvas and on frame) - this.touchTime = new Date().valueOf(); + var ctrlKey = event.gesture.srcEvent && event.gesture.srcEvent.ctrlKey; + var shiftKey = event.gesture.srcEvent && event.gesture.srcEvent.shiftKey; + if (ctrlKey || shiftKey) { + this._onMultiSelectItem(event); + return; + } - this._handleTouch(this.drag.pointer); + var oldSelection = this.getSelection(); + + var item = ItemSet.itemFromTarget(event); + var selection = item ? [item.id] : []; + this.setSelection(selection); + + var newSelection = this.getSelection(); + + // emit a select event, + // except when old selection is empty and new selection is still empty + if (newSelection.length > 0 || oldSelection.length > 0) { + this.body.emitter.emit('select', { + items: newSelection + }); } }; /** - * handle drag start event + * Handle creation and updates of an item on double tap + * @param event * @private */ - Network.prototype._onDragStart = function (event) { - this._handleDragStart(event); - }; - + ItemSet.prototype._onAddItem = function (event) { + if (!this.options.selectable) return; + if (!this.options.editable.add) return; - /** - * This function is called by _onDragStart. - * It is separated out because we can then overload it for the datamanipulation system. - * - * @private - */ - Network.prototype._handleDragStart = function(event) { - // in case the touch event was triggered on an external div, do the initial touch now. - if (this.drag.pointer === undefined) { - this._onTouch(event); - } + var me = this, + snap = this.body.util.snap || null, + item = ItemSet.itemFromTarget(event); - var node = this._getNodeAt(this.drag.pointer); - // note: drag.pointer is set in _onTouch to get the initial touch location + if (item) { + // update item - this.drag.dragging = true; - this.drag.selection = []; - this.drag.translation = this._getTranslation(); - this.drag.nodeId = null; - this.draggingNodes = false; + // execute async handler to update the item (or cancel it) + var itemData = me.itemsData.get(item.id); // get a clone of the data from the dataset + this.options.onUpdate(itemData, function (itemData) { + if (itemData) { + me.itemsData.getDataSet().update(itemData); + } + }); + } + else { + // add item + var xAbs = util.getAbsoluteLeft(this.dom.frame); + var x = event.gesture.center.pageX - xAbs; + var start = this.body.util.toTime(x); + var newItem = { + start: snap ? snap(start) : start, + content: 'new item' + }; - if (node != null && this.constants.dragNodes == true) { - this.draggingNodes = true; - this.drag.nodeId = node.id; - // select the clicked node if not yet selected - if (!node.isSelected()) { - this._selectObject(node,false); + // when default type is a range, add a default end date to the new item + if (this.options.type === 'range') { + var end = this.body.util.toTime(x + this.props.width / 5); + newItem.end = snap ? snap(end) : end; } - this.emit("dragStart",{nodeIds:this.getSelection().nodes}); - - // create an array with the selected nodes and their original location and status - for (var objectId in this.selectionObj.nodes) { - if (this.selectionObj.nodes.hasOwnProperty(objectId)) { - var object = this.selectionObj.nodes[objectId]; - var s = { - id: object.id, - node: object, - - // store original x, y, xFixed and yFixed, make the node temporarily Fixed - x: object.x, - y: object.y, - xFixed: object.xFixed, - yFixed: object.yFixed - }; + newItem[this.itemsData._fieldId] = util.randomUUID(); - object.xFixed = true; - object.yFixed = true; + var group = ItemSet.groupFromTarget(event); + if (group) { + newItem.group = group.groupId; + } - this.drag.selection.push(s); + // execute async handler to customize (or cancel) adding an item + this.options.onAdd(newItem, function (item) { + if (item) { + me.itemsData.getDataSet().add(item); + // TODO: need to trigger a redraw? } - } + }); } }; - /** - * handle drag event + * Handle selecting/deselecting multiple items when holding an item + * @param {Event} event * @private */ - Network.prototype._onDrag = function (event) { - this._handleOnDrag(event) - }; - + ItemSet.prototype._onMultiSelectItem = function (event) { + if (!this.options.selectable) return; - /** - * This function is called by _onDrag. - * It is separated out because we can then overload it for the datamanipulation system. - * - * @private - */ - Network.prototype._handleOnDrag = function(event) { - if (this.drag.pinched) { - return; - } + var selection, + item = ItemSet.itemFromTarget(event); - // remove the focus on node if it is focussed on by the focusOnNode - this.releaseNode(); + if (item) { + // multi select items + selection = this.getSelection(); // current selection - var pointer = this._getPointer(event.gesture.center); - var me = this; - var drag = this.drag; - var selection = drag.selection; - if (selection && selection.length && this.constants.dragNodes == true) { - // calculate delta's and new location - var deltaX = pointer.x - drag.pointer.x; - var deltaY = pointer.y - drag.pointer.y; + var shiftKey = event.gesture.touches[0] && event.gesture.touches[0].shiftKey || false; + if (shiftKey) { + // select all items between the old selection and the tapped item - // update position of all selected nodes - selection.forEach(function (s) { - var node = s.node; + // determine the selection range + selection.push(item.id); + var range = ItemSet._getItemRange(this.itemsData.get(selection, this.itemOptions)); - if (!s.xFixed) { - node.x = me._XconvertDOMtoCanvas(me._XconvertCanvasToDOM(s.x) + deltaX); - } + // select all items within the selection range + selection = []; + for (var id in this.items) { + if (this.items.hasOwnProperty(id)) { + var _item = this.items[id]; + var start = _item.data.start; + var end = (_item.data.end !== undefined) ? _item.data.end : start; - if (!s.yFixed) { - node.y = me._YconvertDOMtoCanvas(me._YconvertCanvasToDOM(s.y) + deltaY); + if (start >= range.min && end <= range.max) { + selection.push(_item.id); // do not use id but item.id, id itself is stringified + } + } } - }); - - - // start _animationStep if not yet running - if (!this.moving) { - this.moving = true; - this.start(); } - } - else { - // move the network - if (this.constants.dragNetwork == true) { - // if the drag was not started properly because the click started outside the network div, start it now. - if (this.drag.pointer === undefined) { - this._handleDragStart(event); - return; + else { + // add/remove this item from the current selection + var index = selection.indexOf(item.id); + if (index == -1) { + // item is not yet selected -> select it + selection.push(item.id); + } + else { + // item is already selected -> deselect it + selection.splice(index, 1); } - var diffX = pointer.x - this.drag.pointer.x; - var diffY = pointer.y - this.drag.pointer.y; - - this._setTranslation( - this.drag.translation.x + diffX, - this.drag.translation.y + diffY - ); - this._redraw(); } - } - }; - - /** - * handle drag start event - * @private - */ - Network.prototype._onDragEnd = function (event) { - this._handleDragEnd(event); - }; + this.setSelection(selection); - Network.prototype._handleDragEnd = function(event) { - this.drag.dragging = false; - var selection = this.drag.selection; - if (selection && selection.length) { - selection.forEach(function (s) { - // restore original xFixed and yFixed - s.node.xFixed = s.xFixed; - s.node.yFixed = s.yFixed; + this.body.emitter.emit('select', { + items: this.getSelection() }); - this.moving = true; - this.start(); - } - else { - this._redraw(); - } - if (this.draggingNodes == false) { - this.emit("dragEnd",{nodeIds:[]}); - } - else { - this.emit("dragEnd",{nodeIds:this.getSelection().nodes}); } + }; - } /** - * handle tap/click event: select/unselect a node + * Calculate the time range of a list of items + * @param {Array.} itemsData + * @return {{min: Date, max: Date}} Returns the range of the provided items * @private */ - Network.prototype._onTap = function (event) { - var pointer = this._getPointer(event.gesture.center); - this.pointerPosition = pointer; - this._handleTap(pointer); + ItemSet._getItemRange = function(itemsData) { + var max = null; + var min = null; - }; + itemsData.forEach(function (data) { + if (min == null || data.start < min) { + min = data.start; + } + if (data.end != undefined) { + if (max == null || data.end > max) { + max = data.end; + } + } + else { + if (max == null || data.start > max) { + max = data.start; + } + } + }); - /** - * handle doubletap event - * @private - */ - Network.prototype._onDoubleTap = function (event) { - var pointer = this._getPointer(event.gesture.center); - this._handleDoubleTap(pointer); + return { + min: min, + max: max + } }; - /** - * handle long tap event: multi select nodes - * @private + * Find an item from an event target: + * searches for the attribute 'timeline-item' in the event target's element tree + * @param {Event} event + * @return {Item | null} item */ - Network.prototype._onHold = function (event) { - var pointer = this._getPointer(event.gesture.center); - this.pointerPosition = pointer; - this._handleOnHold(pointer); + ItemSet.itemFromTarget = function(event) { + var target = event.target; + while (target) { + if (target.hasOwnProperty('timeline-item')) { + return target['timeline-item']; + } + target = target.parentNode; + } + + return null; }; /** - * handle the release of the screen - * - * @private + * Find the Group from an event target: + * searches for the attribute 'timeline-group' in the event target's element tree + * @param {Event} event + * @return {Group | null} group */ - Network.prototype._onRelease = function (event) { - var pointer = this._getPointer(event.gesture.center); - this._handleOnRelease(pointer); + ItemSet.groupFromTarget = function(event) { + var target = event.target; + while (target) { + if (target.hasOwnProperty('timeline-group')) { + return target['timeline-group']; + } + target = target.parentNode; + } + + return null; }; /** - * Handle pinch event - * @param event - * @private + * Find the ItemSet from an event target: + * searches for the attribute 'timeline-itemset' in the event target's element tree + * @param {Event} event + * @return {ItemSet | null} item */ - Network.prototype._onPinch = function (event) { - var pointer = this._getPointer(event.gesture.center); - - this.drag.pinched = true; - if (!('scale' in this.pinch)) { - this.pinch.scale = 1; + ItemSet.itemSetFromTarget = function(event) { + var target = event.target; + while (target) { + if (target.hasOwnProperty('timeline-itemset')) { + return target['timeline-itemset']; + } + target = target.parentNode; } - // TODO: enabled moving while pinching? - var scale = this.pinch.scale * event.gesture.scale; - this._zoom(scale, pointer) + return null; }; + module.exports = ItemSet; + + +/***/ }, +/* 27 */ +/***/ function(module, exports, __webpack_require__) { + + var util = __webpack_require__(1); + var stack = __webpack_require__(28); + var RangeItem = __webpack_require__(29); + /** - * Zoom the network in or out - * @param {Number} scale a number around 1, and between 0.01 and 10 - * @param {{x: Number, y: Number}} pointer Position on screen - * @return {Number} appliedScale scale is limited within the boundaries - * @private + * @constructor Group + * @param {Number | String} groupId + * @param {Object} data + * @param {ItemSet} itemSet */ - Network.prototype._zoom = function(scale, pointer) { - if (this.constants.zoomable == true) { - var scaleOld = this._getScale(); - if (scale < 0.00001) { - scale = 0.00001; - } - if (scale > 10) { - scale = 10; - } + function Group (groupId, data, itemSet) { + this.groupId = groupId; + this.subgroups = {}; + this.subgroupIndex = 0; + this.subgroupOrderer = data && data.subgroupOrder; + this.itemSet = itemSet; - var preScaleDragPointer = null; - if (this.drag !== undefined) { - if (this.drag.dragging == true) { - preScaleDragPointer = this.DOMtoCanvas(this.drag.pointer); - } + this.dom = {}; + this.props = { + label: { + width: 0, + height: 0 } - // + this.frame.canvas.clientHeight / 2 - var translation = this._getTranslation(); + }; + this.className = null; - var scaleFrac = scale / scaleOld; - var tx = (1 - scaleFrac) * pointer.x + translation.x * scaleFrac; - var ty = (1 - scaleFrac) * pointer.y + translation.y * scaleFrac; + this.items = {}; // items filtered by groupId of this group + this.visibleItems = []; // items currently visible in window + this.orderedItems = { + byStart: [], + byEnd: [] + }; + this.checkRangedItems = false; // needed to refresh the ranged items if the window is programatically changed with NO overlap. + var me = this; + this.itemSet.body.emitter.on("checkRangedItems", function () { + me.checkRangedItems = true; + }) - this.areaCenter = {"x" : this._XconvertDOMtoCanvas(pointer.x), - "y" : this._YconvertDOMtoCanvas(pointer.y)}; + this._create(); - this._setScale(scale); - this._setTranslation(tx, ty); - this.updateClustersDefault(); + this.setData(data); + } - if (preScaleDragPointer != null) { - var postScaleDragPointer = this.canvasToDOM(preScaleDragPointer); - this.drag.pointer.x = postScaleDragPointer.x; - this.drag.pointer.y = postScaleDragPointer.y; - } + /** + * Create DOM elements for the group + * @private + */ + Group.prototype._create = function() { + var label = document.createElement('div'); + label.className = 'vlabel'; + this.dom.label = label; - this._redraw(); + var inner = document.createElement('div'); + inner.className = 'inner'; + label.appendChild(inner); + this.dom.inner = inner; - if (scaleOld < scale) { - this.emit("zoom", {direction:"+"}); - } - else { - this.emit("zoom", {direction:"-"}); - } + var foreground = document.createElement('div'); + foreground.className = 'group'; + foreground['timeline-group'] = this; + this.dom.foreground = foreground; - return scale; - } - }; + this.dom.background = document.createElement('div'); + this.dom.background.className = 'group'; + + this.dom.axis = document.createElement('div'); + this.dom.axis.className = 'group'; + // create a hidden marker to detect when the Timelines container is attached + // to the DOM, or the style of a parent of the Timeline is changed from + // display:none is changed to visible. + this.dom.marker = document.createElement('div'); + this.dom.marker.style.visibility = 'hidden'; // TODO: ask jos why this is not none? + this.dom.marker.innerHTML = '?'; + this.dom.background.appendChild(this.dom.marker); + }; /** - * Event handler for mouse wheel event, used to zoom the timeline - * See http://adomas.org/javascript-mouse-wheel/ - * https://github.com/EightMedia/hammer.js/issues/256 - * @param {MouseEvent} event - * @private + * Set the group data for this group + * @param {Object} data Group data, can contain properties content and className */ - Network.prototype._onMouseWheel = function(event) { - // retrieve delta - var delta = 0; - if (event.wheelDelta) { /* IE/Opera. */ - delta = event.wheelDelta/120; - } else if (event.detail) { /* Mozilla case. */ - // In Mozilla, sign of delta is different than in IE. - // Also, delta is multiple of 3. - delta = -event.detail/3; + Group.prototype.setData = function(data) { + // update contents + var content = data && data.content; + if (content instanceof Element) { + this.dom.inner.appendChild(content); + } + else if (content !== undefined && content !== null) { + this.dom.inner.innerHTML = content; + } + else { + this.dom.inner.innerHTML = this.groupId || ''; // groupId can be null } - // If delta is nonzero, handle it. - // Basically, delta is now positive if wheel was scrolled up, - // and negative, if wheel was scrolled down. - if (delta) { + // update title + this.dom.label.title = data && data.title || ''; - // calculate the new scale - var scale = this._getScale(); - var zoom = delta / 10; - if (delta < 0) { - zoom = zoom / (1 - zoom); - } - scale *= (1 + zoom); + if (!this.dom.inner.firstChild) { + util.addClassName(this.dom.inner, 'hidden'); + } + else { + util.removeClassName(this.dom.inner, 'hidden'); + } - // calculate the pointer location - var gesture = hammerUtil.fakeGesture(this, event); - var pointer = this._getPointer(gesture.center); + // update className + var className = data && data.className || null; + if (className != this.className) { + if (this.className) { + util.removeClassName(this.dom.label, this.className); + util.removeClassName(this.dom.foreground, this.className); + util.removeClassName(this.dom.background, this.className); + util.removeClassName(this.dom.axis, this.className); + } + util.addClassName(this.dom.label, className); + util.addClassName(this.dom.foreground, className); + util.addClassName(this.dom.background, className); + util.addClassName(this.dom.axis, className); + this.className = className; + } - // apply the new scale - this._zoom(scale, pointer); + // update style + if (this.style) { + util.removeCssText(this.dom.label, this.style); + this.style = null; + } + if (data && data.style) { + util.addCssText(this.dom.label, data.style); + this.style = data.style; } + }; - // Prevent default actions caused by mouse wheel. - event.preventDefault(); + /** + * Get the width of the group label + * @return {number} width + */ + Group.prototype.getLabelWidth = function() { + return this.props.label.width; }; /** - * Mouse move handler for checking whether the title moves over a node with a title. - * @param {Event} event - * @private + * Repaint this group + * @param {{start: number, end: number}} range + * @param {{item: {horizontal: number, vertical: number}, axis: number}} margin + * @param {boolean} [restack=false] Force restacking of all items + * @return {boolean} Returns true if the group is resized */ - Network.prototype._onMouseMoveTitle = function (event) { - var gesture = hammerUtil.fakeGesture(this, event); - var pointer = this._getPointer(gesture.center); + Group.prototype.redraw = function(range, margin, restack) { + var resized = false; - // check if the previously selected node is still selected - if (this.popupObj) { - this._checkHidePopup(pointer); - } + this.visibleItems = this._updateVisibleItems(this.orderedItems, this.visibleItems, range); - // if we bind the keyboard to the div, we have to highlight it to use it. This highlights it on mouse over - if (this.constants.keyboard.bindToWindow == false && this.constants.keyboard.enabled == true) { - this.frame.focus(); + // force recalculation of the height of the items when the marker height changed + // (due to the Timeline being attached to the DOM or changed from display:none to visible) + var markerHeight = this.dom.marker.clientHeight; + if (markerHeight != this.lastMarkerHeight) { + this.lastMarkerHeight = markerHeight; + + util.forEach(this.items, function (item) { + item.dirty = true; + if (item.displayed) item.redraw(); + }); + + restack = true; } - // start a timeout that will check if the mouse is positioned above - // an element - var me = this; - var checkShow = function() { - me._checkShowPopup(pointer); - }; - if (this.popupTimer) { - clearInterval(this.popupTimer); // stop any running calculationTimer + // reposition visible items vertically + if (this.itemSet.options.stack) { // TODO: ugly way to access options... + stack.stack(this.visibleItems, margin, restack); } - if (!this.drag.dragging) { - this.popupTimer = setTimeout(checkShow, this.constants.tooltip.delay); + else { // no stacking + stack.nostack(this.visibleItems, margin, this.subgroups); } + // recalculate the height of the group + var height = this._calculateHeight(margin); - /** - * Adding hover highlights - */ - if (this.constants.hover == true) { - // removing all hover highlights - for (var edgeId in this.hoverObj.edges) { - if (this.hoverObj.edges.hasOwnProperty(edgeId)) { - this.hoverObj.edges[edgeId].hover = false; - delete this.hoverObj.edges[edgeId]; - } - } + // calculate actual size and position + var foreground = this.dom.foreground; + this.top = foreground.offsetTop; + this.left = foreground.offsetLeft; + this.width = foreground.offsetWidth; + resized = util.updateProperty(this, 'height', height) || resized; - // adding hover highlights - var obj = this._getNodeAt(pointer); - if (obj == null) { - obj = this._getEdgeAt(pointer); - } - if (obj != null) { - this._hoverObject(obj); - } + // recalculate size of label + resized = util.updateProperty(this.props.label, 'width', this.dom.inner.clientWidth) || resized; + resized = util.updateProperty(this.props.label, 'height', this.dom.inner.clientHeight) || resized; - // removing all node hover highlights except for the selected one. - for (var nodeId in this.hoverObj.nodes) { - if (this.hoverObj.nodes.hasOwnProperty(nodeId)) { - if (obj instanceof Node && obj.id != nodeId || obj instanceof Edge || obj == null) { - this._blurObject(this.hoverObj.nodes[nodeId]); - delete this.hoverObj.nodes[nodeId]; - } - } - } - this.redraw(); + // apply new height + this.dom.background.style.height = height + 'px'; + this.dom.foreground.style.height = height + 'px'; + this.dom.label.style.height = height + 'px'; + + // update vertical position of items after they are re-stacked and the height of the group is calculated + for (var i = 0, ii = this.visibleItems.length; i < ii; i++) { + var item = this.visibleItems[i]; + item.repositionY(margin); } + + return resized; }; /** - * Check if there is an element on the given position in the network - * (a node or edge). If so, and if this element has a title, - * show a popup window with its title. - * - * @param {{x:Number, y:Number}} pointer + * recalculate the height of the group + * @param {{item: {horizontal: number, vertical: number}, axis: number}} margin + * @returns {number} Returns the height * @private */ - Network.prototype._checkShowPopup = function (pointer) { - var obj = { - left: this._XconvertDOMtoCanvas(pointer.x), - top: this._YconvertDOMtoCanvas(pointer.y), - right: this._XconvertDOMtoCanvas(pointer.x), - bottom: this._YconvertDOMtoCanvas(pointer.y) - }; - - var id; - var lastPopupNode = this.popupObj; - var nodeUnderCursor = false; - - if (this.popupObj == undefined) { - // search the nodes for overlap, select the top one in case of multiple nodes - var nodes = this.nodes; - var overlappingNodes = []; - for (id in nodes) { - if (nodes.hasOwnProperty(id)) { - var node = nodes[id]; - if (node.isOverlappingWith(obj)) { - if (node.getTitle() !== undefined) { - overlappingNodes.push(id); - } - } + Group.prototype._calculateHeight = function (margin) { + // recalculate the height of the group + var height; + var visibleItems = this.visibleItems; + //var visibleSubgroups = []; + //this.visibleSubgroups = 0; + this.resetSubgroups(); + var me = this; + if (visibleItems.length) { + var min = visibleItems[0].top; + var max = visibleItems[0].top + visibleItems[0].height; + util.forEach(visibleItems, function (item) { + min = Math.min(min, item.top); + max = Math.max(max, (item.top + item.height)); + if (item.data.subgroup !== undefined) { + me.subgroups[item.data.subgroup].height = Math.max(me.subgroups[item.data.subgroup].height,item.height); + me.subgroups[item.data.subgroup].visible = true; + //if (visibleSubgroups.indexOf(item.data.subgroup) == -1){ + // visibleSubgroups.push(item.data.subgroup); + // me.visibleSubgroups += 1; + //} } + }); + if (min > margin.axis) { + // there is an empty gap between the lowest item and the axis + var offset = min - margin.axis; + max -= offset; + util.forEach(visibleItems, function (item) { + item.top -= offset; + }); } - - if (overlappingNodes.length > 0) { - // if there are overlapping nodes, select the last one, this is the - // one which is drawn on top of the others - this.popupObj = this.nodes[overlappingNodes[overlappingNodes.length - 1]]; - // if you hover over a node, the title of the edge is not supposed to be shown. - nodeUnderCursor = true; - } + height = max + margin.item.vertical / 2; } + else { + height = margin.axis + margin.item.vertical; + } + height = Math.max(height, this.props.label.height); - if (this.popupObj === undefined && nodeUnderCursor == false) { - // search the edges for overlap - var edges = this.edges; - var overlappingEdges = []; - for (id in edges) { - if (edges.hasOwnProperty(id)) { - var edge = edges[id]; - if (edge.connected && (edge.getTitle() !== undefined) && - edge.isOverlappingWith(obj)) { - overlappingEdges.push(id); - } - } - } + return height; + }; - if (overlappingEdges.length > 0) { - this.popupObj = this.edges[overlappingEdges[overlappingEdges.length - 1]]; - } + /** + * Show this group: attach to the DOM + */ + Group.prototype.show = function() { + if (!this.dom.label.parentNode) { + this.itemSet.dom.labelSet.appendChild(this.dom.label); } - if (this.popupObj) { - // show popup message window - if (this.popupObj != lastPopupNode) { - var me = this; - if (!me.popup) { - me.popup = new Popup(me.frame, me.constants.tooltip); - } + if (!this.dom.foreground.parentNode) { + this.itemSet.dom.foreground.appendChild(this.dom.foreground); + } - // adjust a small offset such that the mouse cursor is located in the - // bottom left location of the popup, and you can easily move over the - // popup area - me.popup.setPosition(pointer.x - 3, pointer.y - 3); - me.popup.setText(me.popupObj.getTitle()); - me.popup.show(); - } + if (!this.dom.background.parentNode) { + this.itemSet.dom.background.appendChild(this.dom.background); } - else { - if (this.popup) { - this.popup.hide(); - } + + if (!this.dom.axis.parentNode) { + this.itemSet.dom.axis.appendChild(this.dom.axis); } }; - /** - * Check if the popup must be hided, which is the case when the mouse is no - * longer hovering on the object - * @param {{x:Number, y:Number}} pointer - * @private + * Hide this group: remove from the DOM */ - Network.prototype._checkHidePopup = function (pointer) { - if (!this.popupObj || !this._getNodeAt(pointer) ) { - this.popupObj = undefined; - if (this.popup) { - this.popup.hide(); - } + Group.prototype.hide = function() { + var label = this.dom.label; + if (label.parentNode) { + label.parentNode.removeChild(label); } - }; + var foreground = this.dom.foreground; + if (foreground.parentNode) { + foreground.parentNode.removeChild(foreground); + } - /** - * Set a new size for the network - * @param {string} width Width in pixels or percentage (for example '800px' - * or '50%') - * @param {string} height Height in pixels or percentage (for example '400px' - * or '30%') - */ - Network.prototype.setSize = function(width, height) { - var emitEvent = false; - var oldWidth = this.frame.canvas.width; - var oldHeight = this.frame.canvas.height; - if (width != this.constants.width || height != this.constants.height || this.frame.style.width != width || this.frame.style.height != height) { - this.frame.style.width = width; - this.frame.style.height = height; + var background = this.dom.background; + if (background.parentNode) { + background.parentNode.removeChild(background); + } - this.frame.canvas.style.width = '100%'; - this.frame.canvas.style.height = '100%'; + var axis = this.dom.axis; + if (axis.parentNode) { + axis.parentNode.removeChild(axis); + } + }; - this.frame.canvas.width = this.frame.canvas.clientWidth * this.pixelRatio; - this.frame.canvas.height = this.frame.canvas.clientHeight * this.pixelRatio; + /** + * Add an item to the group + * @param {Item} item + */ + Group.prototype.add = function(item) { + this.items[item.id] = item; + item.setParent(this); - this.constants.width = width; - this.constants.height = height; + // add to + if (item.data.subgroup !== undefined) { + if (this.subgroups[item.data.subgroup] === undefined) { + this.subgroups[item.data.subgroup] = {height:0, visible: false, index:this.subgroupIndex, items: []}; + this.subgroupIndex++; + } + this.subgroups[item.data.subgroup].items.push(item); + } + this.orderSubgroups(); - emitEvent = true; + if (this.visibleItems.indexOf(item) == -1) { + var range = this.itemSet.body.range; // TODO: not nice accessing the range like this + this._checkIfVisible(item, this.visibleItems, range); } - else { - // this would adapt the width of the canvas to the width from 100% if and only if - // there is a change. + }; - if (this.frame.canvas.width != this.frame.canvas.clientWidth * this.pixelRatio) { - this.frame.canvas.width = this.frame.canvas.clientWidth * this.pixelRatio; - emitEvent = true; + Group.prototype.orderSubgroups = function() { + if (this.subgroupOrderer !== undefined) { + var sortArray = []; + if (typeof this.subgroupOrderer == 'string') { + for (var subgroup in this.subgroups) { + sortArray.push({subgroup: subgroup, sortField: this.subgroups[subgroup].items[0].data[this.subgroupOrderer]}) + } + sortArray.sort(function (a, b) { + return a.sortField - b.sortField; + }) } - if (this.frame.canvas.height != this.frame.canvas.clientHeight * this.pixelRatio) { - this.frame.canvas.height = this.frame.canvas.clientHeight * this.pixelRatio; - emitEvent = true; + else if (typeof this.subgroupOrderer == 'function') { + for (var subgroup in this.subgroups) { + sortArray.push(this.subgroups[subgroup].items[0].data); + } + sortArray.sort(this.subgroupOrderer); + } + + if (sortArray.length > 0) { + for (var i = 0; i < sortArray.length; i++) { + this.subgroups[sortArray[i].subgroup].index = i; + } } } + }; - if (emitEvent == true) { - this.emit('resize', {width:this.frame.canvas.width * this.pixelRatio,height:this.frame.canvas.height * this.pixelRatio, oldWidth: oldWidth * this.pixelRatio, oldHeight: oldHeight * this.pixelRatio}); + Group.prototype.resetSubgroups = function() { + for (var subgroup in this.subgroups) { + if (this.subgroups.hasOwnProperty(subgroup)) { + this.subgroups[subgroup].visible = false; + } } }; /** - * Set a data set with nodes for the network - * @param {Array | DataSet | DataView} nodes The data containing the nodes. - * @private + * Remove an item from the group + * @param {Item} item */ - Network.prototype._setNodes = function(nodes) { - var oldNodesData = this.nodesData; - - if (nodes instanceof DataSet || nodes instanceof DataView) { - this.nodesData = nodes; - } - else if (Array.isArray(nodes)) { - this.nodesData = new DataSet(); - this.nodesData.add(nodes); - } - else if (!nodes) { - this.nodesData = new DataSet(); - } - else { - throw new TypeError('Array or DataSet expected'); - } - - if (oldNodesData) { - // unsubscribe from old dataset - util.forEach(this.nodesListeners, function (callback, event) { - oldNodesData.off(event, callback); - }); - } - - // remove drawn nodes - this.nodes = {}; + Group.prototype.remove = function(item) { + delete this.items[item.id]; + item.setParent(null); - if (this.nodesData) { - // subscribe to new dataset - var me = this; - util.forEach(this.nodesListeners, function (callback, event) { - me.nodesData.on(event, callback); - }); + // remove from visible items + var index = this.visibleItems.indexOf(item); + if (index != -1) this.visibleItems.splice(index, 1); - // draw all new nodes - var ids = this.nodesData.getIds(); - this._addNodes(ids); - } - this._updateSelection(); + // TODO: also remove from ordered items? }; + /** - * Add nodes - * @param {Number[] | String[]} ids - * @private + * Remove an item from the corresponding DataSet + * @param {Item} item */ - Network.prototype._addNodes = function(ids) { - var id; - for (var i = 0, len = ids.length; i < len; i++) { - id = ids[i]; - var data = this.nodesData.get(id); - var node = new Node(data, this.images, this.groups, this.constants); - this.nodes[id] = node; // note: this may replace an existing node - if ((node.xFixed == false || node.yFixed == false) && (node.x === null || node.y === null)) { - var radius = 10 * 0.1*ids.length + 10; - var angle = 2 * Math.PI * Math.random(); - if (node.xFixed == false) {node.x = radius * Math.cos(angle);} - if (node.yFixed == false) {node.y = radius * Math.sin(angle);} - } - this.moving = true; - } - - this._updateNodeIndexList(); - if (this.constants.hierarchicalLayout.enabled == true && this.initializing == false) { - this._resetLevels(); - this._setupHierarchicalLayout(); - } - this._updateCalculationNodes(); - this._reconnectEdges(); - this._updateValueRange(this.nodes); - this.updateLabels(); + Group.prototype.removeFromDataSet = function(item) { + this.itemSet.removeItem(item.id); }; + /** - * Update existing nodes, or create them when not yet existing - * @param {Number[] | String[]} ids - * @private + * Reorder the items */ - Network.prototype._updateNodes = function(ids,changedData) { - var nodes = this.nodes; - for (var i = 0, len = ids.length; i < len; i++) { - var id = ids[i]; - var node = nodes[id]; - var data = changedData[i]; - if (node) { - // update node - node.setProperties(data, this.constants); - } - else { - // create node - node = new Node(properties, this.images, this.groups, this.constants); - nodes[id] = node; + Group.prototype.order = function() { + var array = util.toArray(this.items); + var startArray = []; + var endArray = []; + + for (var i = 0; i < array.length; i++) { + if (array[i].data.end !== undefined) { + endArray.push(array[i]); } + startArray.push(array[i]); } - this.moving = true; - if (this.constants.hierarchicalLayout.enabled == true && this.initializing == false) { - this._resetLevels(); - this._setupHierarchicalLayout(); - } - this._updateNodeIndexList(); - this._updateValueRange(nodes); - }; + this.orderedItems = { + byStart: startArray, + byEnd: endArray + }; - /** - * Remove existing nodes. If nodes do not exist, the method will just ignore it. - * @param {Number[] | String[]} ids - * @private - */ - Network.prototype._removeNodes = function(ids) { - var nodes = this.nodes; - for (var i = 0, len = ids.length; i < len; i++) { - var id = ids[i]; - delete nodes[id]; - } - this._updateNodeIndexList(); - if (this.constants.hierarchicalLayout.enabled == true && this.initializing == false) { - this._resetLevels(); - this._setupHierarchicalLayout(); - } - this._updateCalculationNodes(); - this._reconnectEdges(); - this._updateSelection(); - this._updateValueRange(nodes); + stack.orderByStart(this.orderedItems.byStart); + stack.orderByEnd(this.orderedItems.byEnd); }; + /** - * Load edges by reading the data table - * @param {Array | DataSet | DataView} edges The data containing the edges. - * @private + * Update the visible items + * @param {{byStart: Item[], byEnd: Item[]}} orderedItems All items ordered by start date and by end date + * @param {Item[]} visibleItems The previously visible items. + * @param {{start: number, end: number}} range Visible range + * @return {Item[]} visibleItems The new visible items. * @private */ - Network.prototype._setEdges = function(edges) { - var oldEdgesData = this.edgesData; + Group.prototype._updateVisibleItems = function(orderedItems, oldVisibleItems, range) { + var visibleItems = []; + var visibleItemsLookup = {}; // we keep this to quickly look up if an item already exists in the list without using indexOf on visibleItems + var interval = (range.end - range.start) / 4; + var lowerBound = range.start - interval; + var upperBound = range.end + interval; + var item, i; - if (edges instanceof DataSet || edges instanceof DataView) { - this.edgesData = edges; + // this function is used to do the binary search. + var searchFunction = function (value) { + if (value < lowerBound) {return -1;} + else if (value <= upperBound) {return 0;} + else {return 1;} } - else if (Array.isArray(edges)) { - this.edgesData = new DataSet(); - this.edgesData.add(edges); + + // first check if the items that were in view previously are still in view. + // IMPORTANT: this handles the case for the items with startdate before the window and enddate after the window! + // also cleans up invisible items. + if (oldVisibleItems.length > 0) { + for (i = 0; i < oldVisibleItems.length; i++) { + this._checkIfVisibleWithReference(oldVisibleItems[i], visibleItems, visibleItemsLookup, range); + } } - else if (!edges) { - this.edgesData = new DataSet(); + + // we do a binary search for the items that have only start values. + var initialPosByStart = util.binarySearchCustom(orderedItems.byStart, searchFunction, 'data','start'); + + // trace the visible items from the inital start pos both ways until an invisible item is found, we only look at the start values. + this._traceVisible(initialPosByStart, orderedItems.byStart, visibleItems, visibleItemsLookup, function (item) { + return (item.data.start < lowerBound || item.data.start > upperBound); + }); + + // if the window has changed programmatically without overlapping the old window, the ranged items with start < lowerBound and end > upperbound are not shown. + // We therefore have to brute force check all items in the byEnd list + if (this.checkRangedItems == true) { + this.checkRangedItems = false; + for (i = 0; i < orderedItems.byEnd.length; i++) { + this._checkIfVisibleWithReference(orderedItems.byEnd[i], visibleItems, visibleItemsLookup, range); + } } else { - throw new TypeError('Array or DataSet expected'); - } + // we do a binary search for the items that have defined end times. + var initialPosByEnd = util.binarySearchCustom(orderedItems.byEnd, searchFunction, 'data','end'); - if (oldEdgesData) { - // unsubscribe from old dataset - util.forEach(this.edgesListeners, function (callback, event) { - oldEdgesData.off(event, callback); + // trace the visible items from the inital start pos both ways until an invisible item is found, we only look at the end values. + this._traceVisible(initialPosByEnd, orderedItems.byEnd, visibleItems, visibleItemsLookup, function (item) { + return (item.data.end < lowerBound || item.data.end > upperBound); }); } - // remove drawn edges - this.edges = {}; - - if (this.edgesData) { - // subscribe to new dataset - var me = this; - util.forEach(this.edgesListeners, function (callback, event) { - me.edgesData.on(event, callback); - }); - // draw all new nodes - var ids = this.edgesData.getIds(); - this._addEdges(ids); + // finally, we reposition all the visible items. + for (i = 0; i < visibleItems.length; i++) { + item = visibleItems[i]; + if (!item.displayed) item.show(); + // reposition item horizontally + item.repositionX(); } - this._reconnectEdges(); - }; + // debug + //console.log("new line") + //if (this.groupId == null) { + // for (i = 0; i < orderedItems.byStart.length; i++) { + // item = orderedItems.byStart[i].data; + // console.log('start',i,initialPosByStart, item.start.valueOf(), item.content, item.start >= lowerBound && item.start <= upperBound,i == initialPosByStart ? "<------------------- HEREEEE" : "") + // } + // for (i = 0; i < orderedItems.byEnd.length; i++) { + // item = orderedItems.byEnd[i].data; + // console.log('rangeEnd',i,initialPosByEnd, item.end.valueOf(), item.content, item.end >= range.start && item.end <= range.end,i == initialPosByEnd ? "<------------------- HEREEEE" : "") + // } + //} - /** - * Add edges - * @param {Number[] | String[]} ids - * @private - */ - Network.prototype._addEdges = function (ids) { - var edges = this.edges, - edgesData = this.edgesData; + return visibleItems; + }; - for (var i = 0, len = ids.length; i < len; i++) { - var id = ids[i]; + Group.prototype._traceVisible = function (initialPos, items, visibleItems, visibleItemsLookup, breakCondition) { + var item; + var i; - var oldEdge = edges[id]; - if (oldEdge) { - oldEdge.disconnect(); + if (initialPos != -1) { + for (i = initialPos; i >= 0; i--) { + item = items[i]; + if (breakCondition(item)) { + break; + } + else { + if (visibleItemsLookup[item.id] === undefined) { + visibleItemsLookup[item.id] = true; + visibleItems.push(item); + } + } } - var data = edgesData.get(id, {"showInternalIds" : true}); - edges[id] = new Edge(data, this, this.constants); - } - this.moving = true; - this._updateValueRange(edges); - this._createBezierNodes(); - this._updateCalculationNodes(); - if (this.constants.hierarchicalLayout.enabled == true && this.initializing == false) { - this._resetLevels(); - this._setupHierarchicalLayout(); + for (i = initialPos + 1; i < items.length; i++) { + item = items[i]; + if (breakCondition(item)) { + break; + } + else { + if (visibleItemsLookup[item.id] === undefined) { + visibleItemsLookup[item.id] = true; + visibleItems.push(item); + } + } + } } - }; + } + /** - * Update existing edges, or create them when not yet existing - * @param {Number[] | String[]} ids + * this function is very similar to the _checkIfInvisible() but it does not + * return booleans, hides the item if it should not be seen and always adds to + * the visibleItems. + * this one is for brute forcing and hiding. + * + * @param {Item} item + * @param {Array} visibleItems + * @param {{start:number, end:number}} range * @private */ - Network.prototype._updateEdges = function (ids) { - var edges = this.edges, - edgesData = this.edgesData; - for (var i = 0, len = ids.length; i < len; i++) { - var id = ids[i]; - - var data = edgesData.get(id); - var edge = edges[id]; - if (edge) { - // update edge - edge.disconnect(); - edge.setProperties(data, this.constants); - edge.connect(); + Group.prototype._checkIfVisible = function(item, visibleItems, range) { + if (item.isVisible(range)) { + if (!item.displayed) item.show(); + // reposition item horizontally + item.repositionX(); + visibleItems.push(item); } else { - // create edge - edge = new Edge(data, this, this.constants); - this.edges[id] = edge; + if (item.displayed) item.hide(); } - } - - this._createBezierNodes(); - if (this.constants.hierarchicalLayout.enabled == true && this.initializing == false) { - this._resetLevels(); - this._setupHierarchicalLayout(); - } - this.moving = true; - this._updateValueRange(edges); }; + /** - * Remove existing edges. Non existing ids will be ignored - * @param {Number[] | String[]} ids + * this function is very similar to the _checkIfInvisible() but it does not + * return booleans, hides the item if it should not be seen and always adds to + * the visibleItems. + * this one is for brute forcing and hiding. + * + * @param {Item} item + * @param {Array} visibleItems + * @param {{start:number, end:number}} range * @private */ - Network.prototype._removeEdges = function (ids) { - var edges = this.edges; - for (var i = 0, len = ids.length; i < len; i++) { - var id = ids[i]; - var edge = edges[id]; - if (edge) { - if (edge.via != null) { - delete this.sectors['support']['nodes'][edge.via.id]; - } - edge.disconnect(); - delete edges[id]; + Group.prototype._checkIfVisibleWithReference = function(item, visibleItems, visibleItemsLookup, range) { + if (item.isVisible(range)) { + if (visibleItemsLookup[item.id] === undefined) { + visibleItemsLookup[item.id] = true; + visibleItems.push(item); } } - - this.moving = true; - this._updateValueRange(edges); - if (this.constants.hierarchicalLayout.enabled == true && this.initializing == false) { - this._resetLevels(); - this._setupHierarchicalLayout(); + else { + if (item.displayed) item.hide(); } - this._updateCalculationNodes(); }; + + + module.exports = Group; + + +/***/ }, +/* 28 */ +/***/ function(module, exports, __webpack_require__) { + + // Utility functions for ordering and stacking of items + var EPSILON = 0.001; // used when checking collisions, to prevent round-off errors + /** - * Reconnect all edges - * @private + * Order items by their start data + * @param {Item[]} items */ - Network.prototype._reconnectEdges = function() { - var id, - nodes = this.nodes, - edges = this.edges; - for (id in nodes) { - if (nodes.hasOwnProperty(id)) { - nodes[id].edges = []; - nodes[id].dynamicEdges = []; - } - } + exports.orderByStart = function(items) { + items.sort(function (a, b) { + return a.data.start - b.data.start; + }); + }; - for (id in edges) { - if (edges.hasOwnProperty(id)) { - var edge = edges[id]; - edge.from = null; - edge.to = null; - edge.connect(); - } - } + /** + * Order items by their end date. If they have no end date, their start date + * is used. + * @param {Item[]} items + */ + exports.orderByEnd = function(items) { + items.sort(function (a, b) { + var aTime = ('end' in a.data) ? a.data.end : a.data.start, + bTime = ('end' in b.data) ? b.data.end : b.data.start; + + return aTime - bTime; + }); }; /** - * Update the values of all object in the given array according to the current - * value range of the objects in the array. - * @param {Object} obj An object containing a set of Edges or Nodes - * The objects must have a method getValue() and - * setValueRange(min, max). - * @private + * Adjust vertical positions of the items such that they don't overlap each + * other. + * @param {Item[]} items + * All visible items + * @param {{item: {horizontal: number, vertical: number}, axis: number}} margin + * Margins between items and between items and the axis. + * @param {boolean} [force=false] + * If true, all items will be repositioned. If false (default), only + * items having a top===null will be re-stacked */ - Network.prototype._updateValueRange = function(obj) { - var id; + exports.stack = function(items, margin, force) { + var i, iMax; - // determine the range of the objects - var valueMin = undefined; - var valueMax = undefined; - for (id in obj) { - if (obj.hasOwnProperty(id)) { - var value = obj[id].getValue(); - if (value !== undefined) { - valueMin = (valueMin === undefined) ? value : Math.min(value, valueMin); - valueMax = (valueMax === undefined) ? value : Math.max(value, valueMax); - } + if (force) { + // reset top position of all items + for (i = 0, iMax = items.length; i < iMax; i++) { + items[i].top = null; } } - // adjust the range of all objects - if (valueMin !== undefined && valueMax !== undefined) { - for (id in obj) { - if (obj.hasOwnProperty(id)) { - obj[id].setValueRange(valueMin, valueMax); - } + // calculate new, non-overlapping positions + for (i = 0, iMax = items.length; i < iMax; i++) { + var item = items[i]; + if (item.stack && item.top === null) { + // initialize top position + item.top = margin.axis; + + do { + // TODO: optimize checking for overlap. when there is a gap without items, + // you only need to check for items from the next item on, not from zero + var collidingItem = null; + for (var j = 0, jj = items.length; j < jj; j++) { + var other = items[j]; + if (other.top !== null && other !== item && other.stack && exports.collision(item, other, margin.item)) { + collidingItem = other; + break; + } + } + + if (collidingItem != null) { + // There is a collision. Reposition the items above the colliding element + item.top = collidingItem.top + collidingItem.height + margin.item.vertical; + } + } while (collidingItem); } } }; - /** - * Redraw the network with the current data - * chart will be resized too. - */ - Network.prototype.redraw = function() { - this.setSize(this.constants.width, this.constants.height); - this._redraw(); - }; /** - * Redraw the network with the current data - * @param hidden | used to get the first estimate of the node sizes. only the nodes are drawn after which they are quickly drawn over. - * @private + * Adjust vertical positions of the items without stacking them + * @param {Item[]} items + * All visible items + * @param {{item: {horizontal: number, vertical: number}, axis: number}} margin + * Margins between items and between items and the axis. */ - Network.prototype._redraw = function(hidden) { - var ctx = this.frame.canvas.getContext('2d'); - - ctx.setTransform(this.pixelRatio, 0, 0, this.pixelRatio, 0, 0); - - // clear the canvas - var w = this.frame.canvas.width * this.pixelRatio; - var h = this.frame.canvas.height * this.pixelRatio; - ctx.clearRect(0, 0, w, h); + exports.nostack = function(items, margin, subgroups) { + var i, iMax, newTop; - // set scaling and translation - ctx.save(); - ctx.translate(this.translation.x, this.translation.y); - ctx.scale(this.scale, this.scale); - - this.canvasTopLeft = { - "x": this._XconvertDOMtoCanvas(0), - "y": this._YconvertDOMtoCanvas(0) - }; - this.canvasBottomRight = { - "x": this._XconvertDOMtoCanvas(this.frame.canvas.clientWidth * this.pixelRatio), - "y": this._YconvertDOMtoCanvas(this.frame.canvas.clientHeight * this.pixelRatio) - }; - - if (!(hidden == true)) { - this._doInAllSectors("_drawAllSectorNodes", ctx); - if (this.drag.dragging == false || this.drag.dragging === undefined || this.constants.hideEdgesOnDrag == false) { - this._doInAllSectors("_drawEdges", ctx); + // reset top position of all items + for (i = 0, iMax = items.length; i < iMax; i++) { + if (items[i].data.subgroup !== undefined) { + newTop = margin.axis; + for (var subgroup in subgroups) { + if (subgroups.hasOwnProperty(subgroup)) { + if (subgroups[subgroup].visible == true && subgroups[subgroup].index < subgroups[items[i].data.subgroup].index) { + newTop += subgroups[subgroup].height + margin.item.vertical; + } + } + } + items[i].top = newTop; } - } - - if (this.drag.dragging == false || this.drag.dragging === undefined || this.constants.hideNodesOnDrag == false) { - this._doInAllSectors("_drawNodes",ctx,false); - } - - if (!(hidden == true)) { - if (this.controlNodesActive == true) { - this._doInAllSectors("_drawControlNodes", ctx); + else { + items[i].top = margin.axis; } } - - // this._doInSupportSector("_drawNodes",ctx,true); - // this._drawTree(ctx,"#F00F0F"); - - // restore original scaling and translation - ctx.restore(); - - if (hidden == true) { - ctx.clearRect(0, 0, w, h); - } }; /** - * Set the translation of the network - * @param {Number} offsetX Horizontal offset - * @param {Number} offsetY Vertical offset - * @private + * Test if the two provided items collide + * The items must have parameters left, width, top, and height. + * @param {Item} a The first item + * @param {Item} b The second item + * @param {{horizontal: number, vertical: number}} margin + * An object containing a horizontal and vertical + * minimum required margin. + * @return {boolean} true if a and b collide, else false */ - Network.prototype._setTranslation = function(offsetX, offsetY) { - if (this.translation === undefined) { - this.translation = { - x: 0, - y: 0 - }; - } + exports.collision = function(a, b, margin) { + return ((a.left - margin.horizontal + EPSILON) < (b.left + b.width) && + (a.left + a.width + margin.horizontal - EPSILON) > b.left && + (a.top - margin.vertical + EPSILON) < (b.top + b.height) && + (a.top + a.height + margin.vertical - EPSILON) > b.top); + }; - if (offsetX !== undefined) { - this.translation.x = offsetX; - } - if (offsetY !== undefined) { - this.translation.y = offsetY; - } - this.emit('viewChanged'); - }; +/***/ }, +/* 29 */ +/***/ function(module, exports, __webpack_require__) { + + var Hammer = __webpack_require__(19); + var Item = __webpack_require__(30); /** - * Get the translation of the network - * @return {Object} translation An object with parameters x and y, both a number - * @private + * @constructor RangeItem + * @extends Item + * @param {Object} data Object containing parameters start, end + * content, className. + * @param {{toScreen: function, toTime: function}} conversion + * Conversion functions from time to screen and vice versa + * @param {Object} [options] Configuration options + * // TODO: describe options */ - Network.prototype._getTranslation = function() { - return { - x: this.translation.x, - y: this.translation.y + function RangeItem (data, conversion, options) { + this.props = { + content: { + width: 0 + } }; - }; + this.overflow = false; // if contents can overflow (css styling), this flag is set to true - /** - * Scale the network - * @param {Number} scale Scaling factor 1.0 is unscaled - * @private - */ - Network.prototype._setScale = function(scale) { - this.scale = scale; - }; + // validate data + if (data) { + if (data.start == undefined) { + throw new Error('Property "start" missing in item ' + data.id); + } + if (data.end == undefined) { + throw new Error('Property "end" missing in item ' + data.id); + } + } - /** - * Get the current scale of the network - * @return {Number} scale Scaling factor 1.0 is unscaled - * @private - */ - Network.prototype._getScale = function() { - return this.scale; - }; + Item.call(this, data, conversion, options); + } - /** - * Convert the X coordinate in DOM-space (coordinate point in browser relative to the container div) to - * the X coordinate in canvas-space (the simulation sandbox, which the camera looks upon) - * @param {number} x - * @returns {number} - * @private - */ - Network.prototype._XconvertDOMtoCanvas = function(x) { - return (x - this.translation.x) / this.scale; - }; + RangeItem.prototype = new Item (null, null, null); - /** - * Convert the X coordinate in canvas-space (the simulation sandbox, which the camera looks upon) to - * the X coordinate in DOM-space (coordinate point in browser relative to the container div) - * @param {number} x - * @returns {number} - * @private - */ - Network.prototype._XconvertCanvasToDOM = function(x) { - return x * this.scale + this.translation.x; - }; + RangeItem.prototype.baseClassName = 'item range'; /** - * Convert the Y coordinate in DOM-space (coordinate point in browser relative to the container div) to - * the Y coordinate in canvas-space (the simulation sandbox, which the camera looks upon) - * @param {number} y - * @returns {number} - * @private + * Check whether this item is visible inside given range + * @returns {{start: Number, end: Number}} range with a timestamp for start and end + * @returns {boolean} True if visible */ - Network.prototype._YconvertDOMtoCanvas = function(y) { - return (y - this.translation.y) / this.scale; + RangeItem.prototype.isVisible = function(range) { + // determine visibility + return (this.data.start < range.end) && (this.data.end > range.start); }; /** - * Convert the Y coordinate in canvas-space (the simulation sandbox, which the camera looks upon) to - * the Y coordinate in DOM-space (coordinate point in browser relative to the container div) - * @param {number} y - * @returns {number} - * @private + * Repaint the item */ - Network.prototype._YconvertCanvasToDOM = function(y) { - return y * this.scale + this.translation.y ; - }; + RangeItem.prototype.redraw = function() { + var dom = this.dom; + if (!dom) { + // create DOM + this.dom = {}; + dom = this.dom; + // background box + dom.box = document.createElement('div'); + // className is updated in redraw() - /** - * - * @param {object} pos = {x: number, y: number} - * @returns {{x: number, y: number}} - * @constructor - */ - Network.prototype.canvasToDOM = function (pos) { - return {x: this._XconvertCanvasToDOM(pos.x), y: this._YconvertCanvasToDOM(pos.y)}; - }; + // contents box + dom.content = document.createElement('div'); + dom.content.className = 'content'; + dom.box.appendChild(dom.content); - /** - * - * @param {object} pos = {x: number, y: number} - * @returns {{x: number, y: number}} - * @constructor - */ - Network.prototype.DOMtoCanvas = function (pos) { - return {x: this._XconvertDOMtoCanvas(pos.x), y: this._YconvertDOMtoCanvas(pos.y)}; - }; + // attach this item as attribute + dom.box['timeline-item'] = this; - /** - * Redraw all nodes - * The 2d context of a HTML canvas can be retrieved by canvas.getContext('2d'); - * @param {CanvasRenderingContext2D} ctx - * @param {Boolean} [alwaysShow] - * @private - */ - Network.prototype._drawNodes = function(ctx,alwaysShow) { - if (alwaysShow === undefined) { - alwaysShow = false; + this.dirty = true; } - // first draw the unselected nodes - var nodes = this.nodes; - var selected = []; - - for (var id in nodes) { - if (nodes.hasOwnProperty(id)) { - nodes[id].setScaleAndPos(this.scale,this.canvasTopLeft,this.canvasBottomRight); - if (nodes[id].isSelected()) { - selected.push(id); - } - else { - if (nodes[id].inArea() || alwaysShow) { - nodes[id].draw(ctx); - } - } + // append DOM to parent DOM + if (!this.parent) { + throw new Error('Cannot redraw item: no parent attached'); + } + if (!dom.box.parentNode) { + var foreground = this.parent.dom.foreground; + if (!foreground) { + throw new Error('Cannot redraw item: parent has no foreground container element'); } + foreground.appendChild(dom.box); } + this.displayed = true; - // draw the selected nodes on top - for (var s = 0, sMax = selected.length; s < sMax; s++) { - if (nodes[selected[s]].inArea() || alwaysShow) { - nodes[selected[s]].draw(ctx); - } + // Update DOM when item is marked dirty. An item is marked dirty when: + // - the item is not yet rendered + // - the item's data is changed + // - the item is selected/deselected + if (this.dirty) { + this._updateContents(this.dom.content); + this._updateTitle(this.dom.box); + this._updateDataAttributes(this.dom.box); + this._updateStyle(this.dom.box); + + // update class + var className = (this.data.className ? (' ' + this.data.className) : '') + + (this.selected ? ' selected' : ''); + dom.box.className = this.baseClassName + className; + + // determine from css whether this box has overflow + this.overflow = window.getComputedStyle(dom.content).overflow !== 'hidden'; + + // recalculate size + // turn off max-width to be able to calculate the real width + // this causes an extra browser repaint/reflow, but so be it + this.dom.content.style.maxWidth = 'none'; + this.props.content.width = this.dom.content.offsetWidth; + this.height = this.dom.box.offsetHeight; + this.dom.content.style.maxWidth = ''; + + this.dirty = false; } + + this._repaintDeleteButton(dom.box); + this._repaintDragLeft(); + this._repaintDragRight(); }; /** - * Redraw all edges - * The 2d context of a HTML canvas can be retrieved by canvas.getContext('2d'); - * @param {CanvasRenderingContext2D} ctx - * @private + * Show the item in the DOM (when not already visible). The items DOM will + * be created when needed. */ - Network.prototype._drawEdges = function(ctx) { - var edges = this.edges; - for (var id in edges) { - if (edges.hasOwnProperty(id)) { - var edge = edges[id]; - edge.setScale(this.scale); - if (edge.connected) { - edges[id].draw(ctx); - } - } + RangeItem.prototype.show = function() { + if (!this.displayed) { + this.redraw(); } }; /** - * Redraw all edges - * The 2d context of a HTML canvas can be retrieved by canvas.getContext('2d'); - * @param {CanvasRenderingContext2D} ctx - * @private + * Hide the item from the DOM (when visible) + * @return {Boolean} changed */ - Network.prototype._drawControlNodes = function(ctx) { - var edges = this.edges; - for (var id in edges) { - if (edges.hasOwnProperty(id)) { - edges[id]._drawControlNodes(ctx); + RangeItem.prototype.hide = function() { + if (this.displayed) { + var box = this.dom.box; + + if (box.parentNode) { + box.parentNode.removeChild(box); } + + this.top = null; + this.left = null; + + this.displayed = false; } }; /** - * Find a stable position for all nodes - * @private + * Reposition the item horizontally + * @Override */ - Network.prototype._stabilize = function() { - if (this.constants.freezeForStabilization == true) { - this._freezeDefinedNodes(); - } + RangeItem.prototype.repositionX = function() { + var parentWidth = this.parent.width; + var start = this.conversion.toScreen(this.data.start); + var end = this.conversion.toScreen(this.data.end); + var contentLeft; + var contentWidth; - // find stable position - var count = 0; - while (this.moving && count < this.constants.stabilizationIterations) { - this._physicsTick(); - count++; + // limit the width of the this, as browsers cannot draw very wide divs + if (start < -parentWidth) { + start = -parentWidth; + } + if (end > 2 * parentWidth) { + end = 2 * parentWidth; } + var boxWidth = Math.max(end - start, 1); - if (this.constants.zoomExtentOnStabilize == true) { - this.zoomExtent(undefined, false, true); + if (this.overflow) { + this.left = start; + this.width = boxWidth + this.props.content.width; + contentWidth = this.props.content.width; + + // Note: The calculation of width is an optimistic calculation, giving + // a width which will not change when moving the Timeline + // So no re-stacking needed, which is nicer for the eye; + } + else { + this.left = start; + this.width = boxWidth; + contentWidth = Math.min(end - start - 2 * this.options.padding, this.props.content.width); } - if (this.constants.freezeForStabilization == true) { - this._restoreFrozenNodes(); + this.dom.box.style.left = this.left + 'px'; + this.dom.box.style.width = boxWidth + 'px'; + + switch (this.options.align) { + case 'left': + this.dom.content.style.left = '0'; + break; + + case 'right': + this.dom.content.style.left = Math.max((boxWidth - contentWidth - 2 * this.options.padding), 0) + 'px'; + break; + + case 'center': + this.dom.content.style.left = Math.max((boxWidth - contentWidth - 2 * this.options.padding) / 2, 0) + 'px'; + break; + + default: // 'auto' + // when range exceeds left of the window, position the contents at the left of the visible area + if (this.overflow) { + if (end > 0) { + contentLeft = Math.max(-start, 0); + } + else { + contentLeft = -contentWidth; // ensure it's not visible anymore + } + } + else { + if (start < 0) { + contentLeft = Math.min(-start, + (end - start - contentWidth - 2 * this.options.padding)); + // TODO: remove the need for options.padding. it's terrible. + } + else { + contentLeft = 0; + } + } + this.dom.content.style.left = contentLeft + 'px'; } }; /** - * When initializing and stabilizing, we can freeze nodes with a predefined position. This greatly speeds up stabilization - * because only the supportnodes for the smoothCurves have to settle. - * - * @private + * Reposition the item vertically + * @Override */ - Network.prototype._freezeDefinedNodes = function() { - var nodes = this.nodes; - for (var id in nodes) { - if (nodes.hasOwnProperty(id)) { - if (nodes[id].x != null && nodes[id].y != null) { - nodes[id].fixedData.x = nodes[id].xFixed; - nodes[id].fixedData.y = nodes[id].yFixed; - nodes[id].xFixed = true; - nodes[id].yFixed = true; - } - } + RangeItem.prototype.repositionY = function() { + var orientation = this.options.orientation, + box = this.dom.box; + + if (orientation == 'top') { + box.style.top = this.top + 'px'; + } + else { + box.style.top = (this.parent.height - this.top - this.height) + 'px'; } }; /** - * Unfreezes the nodes that have been frozen by _freezeDefinedNodes. - * - * @private + * Repaint a drag area on the left side of the range when the range is selected + * @protected */ - Network.prototype._restoreFrozenNodes = function() { - var nodes = this.nodes; - for (var id in nodes) { - if (nodes.hasOwnProperty(id)) { - if (nodes[id].fixedData.x != null) { - nodes[id].xFixed = nodes[id].fixedData.x; - nodes[id].yFixed = nodes[id].fixedData.y; - } + RangeItem.prototype._repaintDragLeft = function () { + if (this.selected && this.options.editable.updateTime && !this.dom.dragLeft) { + // create and show drag area + var dragLeft = document.createElement('div'); + dragLeft.className = 'drag-left'; + dragLeft.dragLeftItem = this; + + // TODO: this should be redundant? + Hammer(dragLeft, { + preventDefault: true + }).on('drag', function () { + //console.log('drag left') + }); + + this.dom.box.appendChild(dragLeft); + this.dom.dragLeft = dragLeft; + } + else if (!this.selected && this.dom.dragLeft) { + // delete drag area + if (this.dom.dragLeft.parentNode) { + this.dom.dragLeft.parentNode.removeChild(this.dom.dragLeft); } + this.dom.dragLeft = null; } }; - /** - * Check if any of the nodes is still moving - * @param {number} vmin the minimum velocity considered as 'moving' - * @return {boolean} true if moving, false if non of the nodes is moving - * @private + * Repaint a drag area on the right side of the range when the range is selected + * @protected */ - Network.prototype._isMoving = function(vmin) { - var nodes = this.nodes; - for (var id in nodes) { - if (nodes.hasOwnProperty(id) && nodes[id].isMoving(vmin)) { - return true; + RangeItem.prototype._repaintDragRight = function () { + if (this.selected && this.options.editable.updateTime && !this.dom.dragRight) { + // create and show drag area + var dragRight = document.createElement('div'); + dragRight.className = 'drag-right'; + dragRight.dragRightItem = this; + + // TODO: this should be redundant? + Hammer(dragRight, { + preventDefault: true + }).on('drag', function () { + //console.log('drag right') + }); + + this.dom.box.appendChild(dragRight); + this.dom.dragRight = dragRight; + } + else if (!this.selected && this.dom.dragRight) { + // delete drag area + if (this.dom.dragRight.parentNode) { + this.dom.dragRight.parentNode.removeChild(this.dom.dragRight); } + this.dom.dragRight = null; } - return false; }; + module.exports = RangeItem; + + +/***/ }, +/* 30 */ +/***/ function(module, exports, __webpack_require__) { + + var Hammer = __webpack_require__(19); + var util = __webpack_require__(1); /** - * /** - * Perform one discrete step for all nodes - * - * @private + * @constructor Item + * @param {Object} data Object containing (optional) parameters type, + * start, end, content, group, className. + * @param {{toScreen: function, toTime: function}} conversion + * Conversion functions from time to screen and vice versa + * @param {Object} options Configuration options + * // TODO: describe available options */ - Network.prototype._discreteStepNodes = function() { - var interval = this.physicsDiscreteStepsize; - var nodes = this.nodes; - var nodeId; - var nodesPresent = false; - - if (this.constants.maxVelocity > 0) { - for (nodeId in nodes) { - if (nodes.hasOwnProperty(nodeId)) { - nodes[nodeId].discreteStepLimited(interval, this.constants.maxVelocity); - nodesPresent = true; - } - } - } - else { - for (nodeId in nodes) { - if (nodes.hasOwnProperty(nodeId)) { - nodes[nodeId].discreteStep(interval); - nodesPresent = true; - } - } - } - - if (nodesPresent == true) { - var vminCorrected = this.constants.minVelocity / Math.max(this.scale,0.05); - if (vminCorrected > 0.5*this.constants.maxVelocity) { - return true; - } - else { - return this._isMoving(vminCorrected); - } - } - return false; - }; + function Item (data, conversion, options) { + this.id = null; + this.parent = null; + this.data = data; + this.dom = null; + this.conversion = conversion || {}; + this.options = options || {}; + this.selected = false; + this.displayed = false; + this.dirty = true; - Network.prototype._revertPhysicsState = function() { - var nodes = this.nodes; - for (var nodeId in nodes) { - if (nodes.hasOwnProperty(nodeId)) { - nodes[nodeId].revertPosition(); - } - } + this.top = null; + this.left = null; + this.width = null; + this.height = null; } - Network.prototype._revertPhysicsTick = function() { - this._doInAllActiveSectors("_revertPhysicsState"); - if (this.constants.smoothCurves.enabled == true && this.constants.smoothCurves.dynamic == true) { - this._doInSupportSector("_revertPhysicsState"); - } - } + Item.prototype.stack = true; /** - * A single simulation step (or "tick") in the physics simulation - * - * @private + * Select current item */ - Network.prototype._physicsTick = function() { - if (!this.freezeSimulation) { - if (this.moving == true) { - var mainMovingStatus = false; - var supportMovingStatus = false; - - this._doInAllActiveSectors("_initializeForceCalculation"); - var mainMoving = this._doInAllActiveSectors("_discreteStepNodes"); - if (this.constants.smoothCurves.enabled == true && this.constants.smoothCurves.dynamic == true) { - supportMovingStatus = this._doInSupportSector("_discreteStepNodes"); - } - - // gather movement data from all sectors, if one moves, we are NOT stabilzied - for (var i = 0; i < mainMoving.length; i++) {mainMovingStatus = mainMoving[0] || mainMovingStatus;} - - // determine if the network has stabilzied - this.moving = mainMovingStatus || supportMovingStatus; - - if (this.moving == false) { - this._revertPhysicsTick(); - } - else { - // this is here to ensure that there is no start event when the network is already stable. - if (this.startedStabilization == false) { - this.emit("startStabilization"); - this.startedStabilization = true; - } - } - - this.stabilizationIterations++; - } - } + Item.prototype.select = function() { + this.selected = true; + this.dirty = true; + if (this.displayed) this.redraw(); }; - /** - * This function runs one step of the animation. It calls an x amount of physics ticks and one render tick. - * It reschedules itself at the beginning of the function - * - * @private + * Unselect current item */ - Network.prototype._animationStep = function() { - // reset the timer so a new scheduled animation step can be set - this.timer = undefined; - - // handle the keyboad movement - this._handleNavigation(); - - // check if the physics have settled - if (this.moving == true) { - var startTime = Date.now(); - this._physicsTick(); - var physicsTime = Date.now() - startTime; - - // run double speed if it is a little graph - if ((this.renderTimestep - this.renderTime > 2 * physicsTime || this.runDoubleSpeed == true) && this.moving == true) { - this._physicsTick(); - - // this makes sure there is no jitter. The decision is taken once to run it at double speed. - if (this.renderTime != 0) { - this.runDoubleSpeed = true - } - } - } - - var renderStartTime = Date.now(); - this._redraw(); - this.renderTime = Date.now() - renderStartTime; - - // this schedules a new animation step - this.start(); + Item.prototype.unselect = function() { + this.selected = false; + this.dirty = true; + if (this.displayed) this.redraw(); }; - if (typeof window !== 'undefined') { - window.requestAnimationFrame = window.requestAnimationFrame || window.mozRequestAnimationFrame || - window.webkitRequestAnimationFrame || window.msRequestAnimationFrame; - } + /** + * Set data for the item. Existing data will be updated. The id should not + * be changed. When the item is displayed, it will be redrawn immediately. + * @param {Object} data + */ + Item.prototype.setData = function(data) { + this.data = data; + this.dirty = true; + if (this.displayed) this.redraw(); + }; /** - * Schedule a animation step with the refreshrate interval. + * Set a parent for the item + * @param {ItemSet | Group} parent */ - Network.prototype.start = function() { - if (this.moving == true || this.xIncrement != 0 || this.yIncrement != 0 || this.zoomIncrement != 0 || this.animating == true) { - if (!this.timer) { - if (this.requiresTimeout == true) { - this.timer = window.setTimeout(this._animationStep.bind(this), this.renderTimestep); // wait this.renderTimeStep milliseconds and perform the animation step function - } - else { - this.timer = window.requestAnimationFrame(this._animationStep.bind(this)); // wait this.renderTimeStep milliseconds and perform the animation step function - } + Item.prototype.setParent = function(parent) { + if (this.displayed) { + this.hide(); + this.parent = parent; + if (this.parent) { + this.show(); } } else { - this._redraw(); - // this check is to ensure that the network does not emit these events if it was already stabilized and setOptions is called (setting moving to true and calling start()) - if (this.stabilizationIterations > 1) { - // trigger the "stabilized" event. - // The event is triggered on the next tick, to prevent the case that - // it is fired while initializing the Network, in which case you would not - // be able to catch it - var me = this; - var params = { - iterations: me.stabilizationIterations - }; - this.stabilizationIterations = 0; - this.startedStabilization = false; - setTimeout(function () { - me.emit("stabilized", params); - }, 0); - } - else { - this.stabilizationIterations = 0; - } + this.parent = parent; } }; - /** - * Move the network according to the keyboard presses. - * - * @private + * Check whether this item is visible inside given range + * @returns {{start: Number, end: Number}} range with a timestamp for start and end + * @returns {boolean} True if visible */ - Network.prototype._handleNavigation = function() { - if (this.xIncrement != 0 || this.yIncrement != 0) { - var translation = this._getTranslation(); - this._setTranslation(translation.x+this.xIncrement, translation.y+this.yIncrement); - } - if (this.zoomIncrement != 0) { - var center = { - x: this.frame.canvas.clientWidth / 2, - y: this.frame.canvas.clientHeight / 2 - }; - this._zoom(this.scale*(1 + this.zoomIncrement), center); - } + Item.prototype.isVisible = function(range) { + // Should be implemented by Item implementations + return false; }; - /** - * Freeze the _animationStep + * Show the Item in the DOM (when not already visible) + * @return {Boolean} changed */ - Network.prototype.toggleFreeze = function() { - if (this.freezeSimulation == false) { - this.freezeSimulation = true; - } - else { - this.freezeSimulation = false; - this.start(); - } + Item.prototype.show = function() { + return false; }; - /** - * This function cleans the support nodes if they are not needed and adds them when they are. - * - * @param {boolean} [disableStart] - * @private + * Hide the Item from the DOM (when visible) + * @return {Boolean} changed */ - Network.prototype._configureSmoothCurves = function(disableStart) { - if (disableStart === undefined) { - disableStart = true; - } - if (this.constants.smoothCurves.enabled == true && this.constants.smoothCurves.dynamic == true) { - this._createBezierNodes(); - // cleanup unused support nodes - for (var nodeId in this.sectors['support']['nodes']) { - if (this.sectors['support']['nodes'].hasOwnProperty(nodeId)) { - if (this.edges[this.sectors['support']['nodes'][nodeId].parentEdgeId] === undefined) { - delete this.sectors['support']['nodes'][nodeId]; - } - } - } - } - else { - // delete the support nodes - this.sectors['support']['nodes'] = {}; - for (var edgeId in this.edges) { - if (this.edges.hasOwnProperty(edgeId)) { - this.edges[edgeId].via = null; - } - } - } - - - this._updateCalculationNodes(); - if (!disableStart) { - this.moving = true; - this.start(); - } + Item.prototype.hide = function() { + return false; }; - /** - * 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. - * - * @private + * Repaint the item */ - Network.prototype._createBezierNodes = function() { - if (this.constants.smoothCurves.enabled == true && this.constants.smoothCurves.dynamic == true) { - for (var edgeId in this.edges) { - if (this.edges.hasOwnProperty(edgeId)) { - var edge = this.edges[edgeId]; - if (edge.via == null) { - var nodeId = "edgeId:".concat(edge.id); - this.sectors['support']['nodes'][nodeId] = new Node( - {id:nodeId, - mass:1, - shape:'circle', - image:"", - internalMultiplier:1 - },{},{},this.constants); - edge.via = this.sectors['support']['nodes'][nodeId]; - edge.via.parentEdgeId = edge.id; - edge.positionBezierNode(); - } - } - } - } + Item.prototype.redraw = function() { + // should be implemented by the item }; /** - * load the functions that load the mixins into the prototype. - * - * @private + * Reposition the Item horizontally */ - Network.prototype._initializeMixinLoaders = function () { - for (var mixin in MixinLoader) { - if (MixinLoader.hasOwnProperty(mixin)) { - Network.prototype[mixin] = MixinLoader[mixin]; - } - } + Item.prototype.repositionX = function() { + // should be implemented by the item }; /** - * Load the XY positions of the nodes into the dataset. + * Reposition the Item vertically */ - Network.prototype.storePosition = function() { - console.log("storePosition is depricated: use .storePositions() from now on.") - this.storePositions(); + Item.prototype.repositionY = function() { + // should be implemented by the item }; /** - * Load the XY positions of the nodes into the dataset. + * Repaint a delete button on the top right of the item when the item is selected + * @param {HTMLElement} anchor + * @protected */ - Network.prototype.storePositions = function() { - var dataArray = []; - for (var nodeId in this.nodes) { - if (this.nodes.hasOwnProperty(nodeId)) { - var node = this.nodes[nodeId]; - var allowedToMoveX = !this.nodes.xFixed; - var allowedToMoveY = !this.nodes.yFixed; - if (this.nodesData._data[nodeId].x != Math.round(node.x) || this.nodesData._data[nodeId].y != Math.round(node.y)) { - dataArray.push({id:nodeId,x:Math.round(node.x),y:Math.round(node.y),allowedToMoveX:allowedToMoveX,allowedToMoveY:allowedToMoveY}); - } + Item.prototype._repaintDeleteButton = function (anchor) { + if (this.selected && this.options.editable.remove && !this.dom.deleteButton) { + // create and show button + var me = this; + + var deleteButton = document.createElement('div'); + deleteButton.className = 'delete'; + deleteButton.title = 'Delete this item'; + + Hammer(deleteButton, { + preventDefault: true + }).on('tap', function (event) { + me.parent.removeFromDataSet(me); + event.stopPropagation(); + }); + + anchor.appendChild(deleteButton); + this.dom.deleteButton = deleteButton; + } + else if (!this.selected && this.dom.deleteButton) { + // remove button + if (this.dom.deleteButton.parentNode) { + this.dom.deleteButton.parentNode.removeChild(this.dom.deleteButton); } + this.dom.deleteButton = null; } - this.nodesData.update(dataArray); }; /** - * Return the positions of the nodes. + * Set HTML contents for the item + * @param {Element} element HTML element to fill with the contents + * @private */ - Network.prototype.getPositions = function(ids) { - var dataArray = {}; - if (ids !== undefined) { - if (Array.isArray(ids) == true) { - for (var i = 0; i < ids.length; i++) { - if (this.nodes[ids[i]] !== undefined) { - var node = this.nodes[ids[i]]; - dataArray[ids[i]] = {x: Math.round(node.x), y: Math.round(node.y)}; - } - } - } - else { - if (this.nodes[ids] !== undefined) { - var node = this.nodes[ids]; - dataArray[ids] = {x: Math.round(node.x), y: Math.round(node.y)}; - } - } + Item.prototype._updateContents = function (element) { + var content; + if (this.options.template) { + var itemData = this.parent.itemSet.itemsData.get(this.id); // get a clone of the data from the dataset + content = this.options.template(itemData); } else { - for (var nodeId in this.nodes) { - if (this.nodes.hasOwnProperty(nodeId)) { - var node = this.nodes[nodeId]; - dataArray[nodeId] = {x: Math.round(node.x), y: Math.round(node.y)}; + content = this.data.content; + } + + if(content !== this.content) { + // only replace the content when changed + if (content instanceof Element) { + element.innerHTML = ''; + element.appendChild(content); + } + else if (content != undefined) { + element.innerHTML = content; + } + else { + if (!(this.data.type == 'background' && this.data.content === undefined)) { + throw new Error('Property "content" missing in item ' + this.id); } } + + this.content = content; } - return dataArray; }; - - /** - * Center a node in view. - * - * @param {Number} nodeId - * @param {Number} [options] + * Set HTML contents for the item + * @param {Element} element HTML element to fill with the contents + * @private */ - Network.prototype.focusOnNode = function (nodeId, options) { - if (this.nodes.hasOwnProperty(nodeId)) { - if (options === undefined) { - options = {}; - } - var nodePosition = {x: this.nodes[nodeId].x, y: this.nodes[nodeId].y}; - options.position = nodePosition; - options.lockedOnNode = nodeId; - - this.moveTo(options) + Item.prototype._updateTitle = function (element) { + if (this.data.title != null) { + element.title = this.data.title || ''; } else { - console.log("This nodeId cannot be found."); + element.removeAttribute('title'); } }; /** - * - * @param {Object} options | options.offset = {x:Number, y:Number} // offset from the center in DOM pixels - * | options.scale = Number // scale to move to - * | options.position = {x:Number, y:Number} // position to move to - * | options.animation = {duration:Number, easingFunction:String} || Boolean // position to move to + * Process dataAttributes timeline option and set as data- attributes on dom.content + * @param {Element} element HTML element to which the attributes will be attached + * @private */ - Network.prototype.moveTo = function (options) { - if (options === undefined) { - options = {}; - return; - } - if (options.offset === undefined) {options.offset = {x: 0, y: 0}; } - if (options.offset.x === undefined) {options.offset.x = 0; } - if (options.offset.y === undefined) {options.offset.y = 0; } - if (options.scale === undefined) {options.scale = this._getScale(); } - if (options.position === undefined) {options.position = this._getTranslation();} - if (options.animation === undefined) {options.animation = {duration:0}; } - if (options.animation === false ) {options.animation = {duration:0}; } - if (options.animation === true ) {options.animation = {}; } - if (options.animation.duration === undefined) {options.animation.duration = 1000; } // default duration - if (options.animation.easingFunction === undefined) {options.animation.easingFunction = "easeInOutQuad"; } // default easing function + Item.prototype._updateDataAttributes = function(element) { + if (this.options.dataAttributes && this.options.dataAttributes.length > 0) { + var attributes = []; - this.animateView(options); + if (Array.isArray(this.options.dataAttributes)) { + attributes = this.options.dataAttributes; + } + else if (this.options.dataAttributes == 'all') { + attributes = Object.keys(this.data); + } + else { + return; + } + + for (var i = 0; i < attributes.length; i++) { + var name = attributes[i]; + var value = this.data[name]; + + if (value != null) { + element.setAttribute('data-' + name, value); + } + else { + element.removeAttribute('data-' + name); + } + } + } }; /** - * - * @param {Object} options | options.offset = {x:Number, y:Number} // offset from the center in DOM pixels - * | options.time = Number // animation time in milliseconds - * | options.scale = Number // scale to animate to - * | options.position = {x:Number, y:Number} // position to animate to - * | options.easingFunction = String // linear, easeInQuad, easeOutQuad, easeInOutQuad, - * // easeInCubic, easeOutCubic, easeInOutCubic, - * // easeInQuart, easeOutQuart, easeInOutQuart, - * // easeInQuint, easeOutQuint, easeInOutQuint + * Update custom styles of the element + * @param element + * @private */ - Network.prototype.animateView = function (options) { - if (options === undefined) { - options = {}; - return; + Item.prototype._updateStyle = function(element) { + // remove old styles + if (this.style) { + util.removeCssText(element, this.style); + this.style = null; } - // release if something focussed on the node - this.releaseNode(); - if (options.locked == true) { - this.lockedOnNodeId = options.lockedOnNode; - this.lockedOnNodeOffset = options.offset; + // append new styles + if (this.data.style) { + util.addCssText(element, this.data.style); + this.style = this.data.style; } + }; - // forcefully complete the old animation if it was still running - if (this.easingTime != 0) { - this._transitionRedraw(1); // by setting easingtime to 1, we finish the animation. - } + module.exports = Item; - this.sourceScale = this._getScale(); - this.sourceTranslation = this._getTranslation(); - this.targetScale = options.scale; - // set the scale so the viewCenter is based on the correct zoom level. This is overridden in the transitionRedraw - // but at least then we'll have the target transition - this._setScale(this.targetScale); - var viewCenter = this.DOMtoCanvas({x: 0.5 * this.frame.canvas.clientWidth, y: 0.5 * this.frame.canvas.clientHeight}); - var distanceFromCenter = { // offset from view, distance view has to change by these x and y to center the node - x: viewCenter.x - options.position.x, - y: viewCenter.y - options.position.y - }; - this.targetTranslation = { - x: this.sourceTranslation.x + distanceFromCenter.x * this.targetScale + options.offset.x, - y: this.sourceTranslation.y + distanceFromCenter.y * this.targetScale + options.offset.y - }; +/***/ }, +/* 31 */ +/***/ function(module, exports, __webpack_require__) { - // if the time is set to 0, don't do an animation - if (options.animation.duration == 0) { - if (this.lockedOnNodeId != null) { - this._classicRedraw = this._redraw; - this._redraw = this._lockedRedraw; - } - else { - this._setScale(this.targetScale); - this._setTranslation(this.targetTranslation.x, this.targetTranslation.y); - this._redraw(); - } - } - else { - this.animating = true; - this.animationSpeed = 1 / (this.renderRefreshRate * options.animation.duration * 0.001) || 1 / this.renderRefreshRate; - this.animationEasingFunction = options.animation.easingFunction; - this._classicRedraw = this._redraw; - this._redraw = this._transitionRedraw; - this._redraw(); - this.start(); - } - }; + var util = __webpack_require__(1); + var Group = __webpack_require__(27); /** - * used to animate smoothly by hijacking the redraw function. - * @private + * @constructor BackgroundGroup + * @param {Number | String} groupId + * @param {Object} data + * @param {ItemSet} itemSet */ - Network.prototype._lockedRedraw = function () { - var nodePosition = {x: this.nodes[this.lockedOnNodeId].x, y: this.nodes[this.lockedOnNodeId].y}; - var viewCenter = this.DOMtoCanvas({x: 0.5 * this.frame.canvas.clientWidth, y: 0.5 * this.frame.canvas.clientHeight}); - var distanceFromCenter = { // offset from view, distance view has to change by these x and y to center the node - x: viewCenter.x - nodePosition.x, - y: viewCenter.y - nodePosition.y - }; - var sourceTranslation = this._getTranslation(); - var targetTranslation = { - x: sourceTranslation.x + distanceFromCenter.x * this.scale + this.lockedOnNodeOffset.x, - y: sourceTranslation.y + distanceFromCenter.y * this.scale + this.lockedOnNodeOffset.y - }; + function BackgroundGroup (groupId, data, itemSet) { + Group.call(this, groupId, data, itemSet); - this._setTranslation(targetTranslation.x,targetTranslation.y); - this._classicRedraw(); + this.width = 0; + this.height = 0; + this.top = 0; + this.left = 0; } - Network.prototype.releaseNode = function () { - if (this.lockedOnNodeId != null) { - this._redraw = this._classicRedraw; - this.lockedOnNodeId = null; - this.lockedOnNodeOffset = null; - } - } + BackgroundGroup.prototype = Object.create(Group.prototype); /** - * - * @param easingTime - * @private + * Repaint this group + * @param {{start: number, end: number}} range + * @param {{item: {horizontal: number, vertical: number}, axis: number}} margin + * @param {boolean} [restack=false] Force restacking of all items + * @return {boolean} Returns true if the group is resized */ - Network.prototype._transitionRedraw = function (easingTime) { - this.easingTime = easingTime || this.easingTime + this.animationSpeed; - this.easingTime += this.animationSpeed; + BackgroundGroup.prototype.redraw = function(range, margin, restack) { + var resized = false; - var progress = util.easingFunctions[this.animationEasingFunction](this.easingTime); + this.visibleItems = this._updateVisibleItems(this.orderedItems, this.visibleItems, range); - this._setScale(this.sourceScale + (this.targetScale - this.sourceScale) * progress); - this._setTranslation( - this.sourceTranslation.x + (this.targetTranslation.x - this.sourceTranslation.x) * progress, - this.sourceTranslation.y + (this.targetTranslation.y - this.sourceTranslation.y) * progress - ); + // calculate actual size + this.width = this.dom.background.offsetWidth; - this._classicRedraw(); + // apply new height (just always zero for BackgroundGroup + this.dom.background.style.height = '0'; - // cleanup - if (this.easingTime >= 1.0) { - this.animating = false; - this.easingTime = 0; - if (this.lockedOnNodeId != null) { - this._redraw = this._lockedRedraw; - } - else { - this._redraw = this._classicRedraw; - } - this.emit("animationFinished"); + // update vertical position of items after they are re-stacked and the height of the group is calculated + for (var i = 0, ii = this.visibleItems.length; i < ii; i++) { + var item = this.visibleItems[i]; + item.repositionY(margin); } - }; - - Network.prototype._classicRedraw = function () { - // placeholder function to be overloaded by animations; - }; - - /** - * Returns true when the Network is active. - * @returns {boolean} - */ - Network.prototype.isActive = function () { - return !this.activator || this.activator.active; - }; - - - /** - * Sets the scale - * @returns {Number} - */ - Network.prototype.setScale = function () { - return this._setScale(); - }; - - /** - * Returns the scale - * @returns {Number} - */ - Network.prototype.getScale = function () { - return this._getScale(); + return resized; }; - /** - * Returns the scale - * @returns {Number} + * Show this group: attach to the DOM */ - Network.prototype.getCenterCoordinates = function () { - return this.DOMtoCanvas({x: 0.5 * this.frame.canvas.clientWidth, y: 0.5 * this.frame.canvas.clientHeight}); - }; - - - Network.prototype.getBoundingBox = function(nodeId) { - if (this.nodes[nodeId] !== undefined) { - return this.nodes[nodeId].boundingBox; + BackgroundGroup.prototype.show = function() { + if (!this.dom.background.parentNode) { + this.itemSet.dom.background.appendChild(this.dom.background); } - } + }; - module.exports = Network; + module.exports = BackgroundGroup; /***/ }, -/* 37 */ +/* 32 */ /***/ function(module, exports, __webpack_require__) { + var Item = __webpack_require__(30); var util = __webpack_require__(1); - var Node = __webpack_require__(40); /** - * @class Edge - * - * A edge connects two nodes - * @param {Object} properties Object with properties. Must contain - * At least properties from and to. - * Available properties: 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 + * @constructor BoxItem + * @extends Item + * @param {Object} data Object containing parameters start + * content, className. + * @param {{toScreen: function, toTime: function}} conversion + * Conversion functions from time to screen and vice versa + * @param {Object} [options] Configuration options + * // TODO: describe available options */ - function Edge (properties, network, networkConstants) { - if (!network) { - throw "No network provided"; - } - var fields = ['edges','physics']; - var constants = util.selectiveBridgeObject(fields,networkConstants); - this.options = constants.edges; - this.physics = constants.physics; - this.options['smoothCurves'] = networkConstants['smoothCurves']; + function BoxItem (data, conversion, options) { + this.props = { + dot: { + width: 0, + height: 0 + }, + line: { + width: 0, + height: 0 + } + }; + // validate data + if (data) { + if (data.start == undefined) { + throw new Error('Property "start" missing in item ' + data); + } + } - this.network = network; + Item.call(this, data, conversion, options); + } - // initialize variables - this.id = undefined; - this.fromId = undefined; - this.toId = undefined; - this.title = undefined; - this.widthSelected = this.options.width * this.options.widthSelectionMultiplier; - this.value = undefined; - this.selected = false; - this.hover = false; - this.labelDimensions = {top:0,left:0,width:0,height:0,yLine:0}; // could be cached - this.dirtyLabel = true; + BoxItem.prototype = new Item (null, null, null); - this.from = null; // a node - this.to = null; // a node - this.via = null; // a temp node + /** + * Check whether this item is visible inside given range + * @returns {{start: Number, end: Number}} range with a timestamp for start and end + * @returns {boolean} True if visible + */ + BoxItem.prototype.isVisible = function(range) { + // determine visibility + // TODO: account for the real width of the item. Right now we just add 1/4 to the window + var interval = (range.end - range.start) / 4; + return (this.data.start > range.start - interval) && (this.data.start < range.end + interval); + }; - this.fromBackup = null; // used to clean up after reconnect - this.toBackup = null;; // used to clean up after reconnect + /** + * Repaint the item + */ + BoxItem.prototype.redraw = function() { + var dom = this.dom; + if (!dom) { + // create DOM + this.dom = {}; + dom = this.dom; - // we use this to be able to reconnect the edge to a cluster if its node is put into a cluster - // by storing the original information we can revert to the original connection when the cluser is opened. - this.originalFromId = []; - this.originalToId = []; + // create main box + dom.box = document.createElement('DIV'); - this.connected = false; + // contents box (inside the background box). used for making margins + dom.content = document.createElement('DIV'); + dom.content.className = 'content'; + dom.box.appendChild(dom.content); - this.widthFixed = false; - this.lengthFixed = false; + // line to axis + dom.line = document.createElement('DIV'); + dom.line.className = 'line'; - this.setProperties(properties); + // dot on axis + dom.dot = document.createElement('DIV'); + dom.dot.className = 'dot'; - this.controlNodesEnabled = false; - this.controlNodes = {from:null, to:null, positions:{}}; - this.connectedNode = null; - } + // attach this item as attribute + dom.box['timeline-item'] = this; - /** - * Set or overwrite properties for the edge - * @param {Object} properties an object with properties - * @param {Object} constants and object with default, global properties - */ - Edge.prototype.setProperties = function(properties) { - if (!properties) { - return; + this.dirty = true; } - var fields = ['style','fontSize','fontFace','fontColor','fontFill','fontStrokeWidth','fontStrokeColor','width', - 'widthSelectionMultiplier','hoverWidth','arrowScaleFactor','dash','inheritColor','labelAlignment' - ]; - util.selectiveDeepExtend(fields, this.options, properties); + // append DOM to parent DOM + if (!this.parent) { + throw new Error('Cannot redraw item: no parent attached'); + } + if (!dom.box.parentNode) { + var foreground = this.parent.dom.foreground; + if (!foreground) throw new Error('Cannot redraw item: parent has no foreground container element'); + foreground.appendChild(dom.box); + } + if (!dom.line.parentNode) { + var background = this.parent.dom.background; + if (!background) throw new Error('Cannot redraw item: parent has no background container element'); + background.appendChild(dom.line); + } + if (!dom.dot.parentNode) { + var axis = this.parent.dom.axis; + if (!background) throw new Error('Cannot redraw item: parent has no axis container element'); + axis.appendChild(dom.dot); + } + this.displayed = true; - if (properties.from !== undefined) {this.fromId = properties.from;} - if (properties.to !== undefined) {this.toId = properties.to;} + // Update DOM when item is marked dirty. An item is marked dirty when: + // - the item is not yet rendered + // - the item's data is changed + // - the item is selected/deselected + if (this.dirty) { + this._updateContents(this.dom.content); + this._updateTitle(this.dom.box); + this._updateDataAttributes(this.dom.box); + this._updateStyle(this.dom.box); - if (properties.id !== undefined) {this.id = properties.id;} - if (properties.label !== undefined) {this.label = properties.label; this.dirtyLabel = true;} + // update class + var className = (this.data.className? ' ' + this.data.className : '') + + (this.selected ? ' selected' : ''); + dom.box.className = 'item box' + className; + dom.line.className = 'item line' + className; + dom.dot.className = 'item dot' + className; - if (properties.title !== undefined) {this.title = properties.title;} - if (properties.value !== undefined) {this.value = properties.value;} - if (properties.length !== undefined) {this.physics.springLength = properties.length;} + // recalculate size + this.props.dot.height = dom.dot.offsetHeight; + this.props.dot.width = dom.dot.offsetWidth; + this.props.line.width = dom.line.offsetWidth; + this.width = dom.box.offsetWidth; + this.height = dom.box.offsetHeight; - if (properties.color !== undefined) { - this.options.inheritColor = false; - if (util.isString(properties.color)) { - this.options.color.color = properties.color; - this.options.color.highlight = properties.color; - } - else { - if (properties.color.color !== undefined) {this.options.color.color = properties.color.color;} - if (properties.color.highlight !== undefined) {this.options.color.highlight = properties.color.highlight;} - if (properties.color.hover !== undefined) {this.options.color.hover = properties.color.hover;} - } + this.dirty = false; } - // A node is connected when it has a from and to node. - this.connect(); - - this.widthFixed = this.widthFixed || (properties.width !== undefined); - this.lengthFixed = this.lengthFixed || (properties.length !== undefined); - - this.widthSelected = this.options.width* this.options.widthSelectionMultiplier; + this._repaintDeleteButton(dom.box); + }; - // set draw method based on style - switch (this.options.style) { - case 'line': this.draw = this._drawLine; break; - case 'arrow': this.draw = this._drawArrow; break; - case 'arrow-center': this.draw = this._drawArrowCenter; break; - case 'dash-line': this.draw = this._drawDashLine; break; - default: this.draw = this._drawLine; break; + /** + * Show the item in the DOM (when not already displayed). The items DOM will + * be created when needed. + */ + BoxItem.prototype.show = function() { + if (!this.displayed) { + this.redraw(); } - }; /** - * Connect an edge to its nodes + * Hide the item from the DOM (when visible) */ - Edge.prototype.connect = function () { - this.disconnect(); + BoxItem.prototype.hide = function() { + if (this.displayed) { + var dom = this.dom; - this.from = this.network.nodes[this.fromId] || null; - this.to = this.network.nodes[this.toId] || null; - this.connected = (this.from && this.to); + if (dom.box.parentNode) dom.box.parentNode.removeChild(dom.box); + if (dom.line.parentNode) dom.line.parentNode.removeChild(dom.line); + if (dom.dot.parentNode) dom.dot.parentNode.removeChild(dom.dot); - if (this.connected) { - this.from.attachEdge(this); - this.to.attachEdge(this); - } - else { - if (this.from) { - this.from.detachEdge(this); - } - if (this.to) { - this.to.detachEdge(this); - } + this.top = null; + this.left = null; + + this.displayed = false; } }; /** - * Disconnect an edge from its nodes + * Reposition the item horizontally + * @Override */ - Edge.prototype.disconnect = function () { - if (this.from) { - this.from.detachEdge(this); - this.from = null; + BoxItem.prototype.repositionX = function() { + var start = this.conversion.toScreen(this.data.start); + var align = this.options.align; + var left; + var box = this.dom.box; + var line = this.dom.line; + var dot = this.dom.dot; + + // calculate left position of the box + if (align == 'right') { + this.left = start - this.width; } - if (this.to) { - this.to.detachEdge(this); - this.to = null; + else if (align == 'left') { + this.left = start; + } + else { + // default or 'center' + this.left = start - this.width / 2; } - this.connected = false; + // reposition box + box.style.left = this.left + 'px'; + + // reposition line + line.style.left = (start - this.props.line.width / 2) + 'px'; + + // reposition dot + dot.style.left = (start - this.props.dot.width / 2) + 'px'; }; /** - * get the title of this edge. - * @return {string} title The title of the edge, or undefined when no title - * has been set. + * Reposition the item vertically + * @Override */ - Edge.prototype.getTitle = function() { - return typeof this.title === "function" ? this.title() : this.title; - }; + BoxItem.prototype.repositionY = function() { + var orientation = this.options.orientation; + var box = this.dom.box; + var line = this.dom.line; + var dot = this.dom.dot; + if (orientation == 'top') { + box.style.top = (this.top || 0) + 'px'; - /** - * Retrieve the value of the edge. Can be undefined - * @return {Number} value - */ - Edge.prototype.getValue = function() { - return this.value; - }; + line.style.top = '0'; + line.style.height = (this.parent.top + this.top + 1) + 'px'; + line.style.bottom = ''; + } + else { // orientation 'bottom' + var itemSetHeight = this.parent.itemSet.props.height; // TODO: this is nasty + var lineHeight = itemSetHeight - this.parent.top - this.parent.height + this.top; - /** - * Adjust the value range of the edge. The edge will adjust it's width - * based on its value. - * @param {Number} min - * @param {Number} max - */ - Edge.prototype.setValueRange = function(min, max) { - if (!this.widthFixed && this.value !== undefined) { - var scale = (this.options.widthMax - this.options.widthMin) / (max - min); - this.options.width= (this.value - min) * scale + this.options.widthMin; - this.widthSelected = this.options.width* this.options.widthSelectionMultiplier; + box.style.top = (this.parent.height - this.top - this.height || 0) + 'px'; + line.style.top = (itemSetHeight - lineHeight) + 'px'; + line.style.bottom = '0'; } - }; - /** - * 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 - */ - Edge.prototype.draw = function(ctx) { - throw "Method draw not initialized in edge"; + dot.style.top = (-this.props.dot.height / 2) + 'px'; }; + module.exports = BoxItem; + + +/***/ }, +/* 33 */ +/***/ function(module, exports, __webpack_require__) { + + var Item = __webpack_require__(30); + /** - * 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 + * @constructor PointItem + * @extends Item + * @param {Object} data Object containing parameters start + * content, className. + * @param {{toScreen: function, toTime: function}} conversion + * Conversion functions from time to screen and vice versa + * @param {Object} [options] Configuration options + * // TODO: describe available options */ - Edge.prototype.isOverlappingWith = function(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._getDistanceToEdge(xFrom, yFrom, xTo, yTo, xObj, yObj); + function PointItem (data, conversion, options) { + this.props = { + dot: { + top: 0, + width: 0, + height: 0 + }, + content: { + height: 0, + marginLeft: 0 + } + }; - return (dist < distMax); - } - else { - return false + // validate data + if (data) { + if (data.start == undefined) { + throw new Error('Property "start" missing in item ' + data); + } } - }; - Edge.prototype._getColor = function() { - var colorObj = this.options.color; - if (this.options.inheritColor == "to") { - colorObj = { - highlight: this.to.options.color.highlight.border, - hover: this.to.options.color.hover.border, - color: this.to.options.color.border - }; - } - else if (this.options.inheritColor == "from" || this.options.inheritColor == true) { - colorObj = { - highlight: this.from.options.color.highlight.border, - hover: this.from.options.color.hover.border, - color: this.from.options.color.border - }; - } + Item.call(this, data, conversion, options); + } - if (this.selected == true) {return colorObj.highlight;} - else if (this.hover == true) {return colorObj.hover;} - else {return colorObj.color;} - }; + PointItem.prototype = new Item (null, null, null); + /** + * Check whether this item is visible inside given range + * @returns {{start: Number, end: Number}} range with a timestamp for start and end + * @returns {boolean} True if visible + */ + PointItem.prototype.isVisible = function(range) { + // determine visibility + // TODO: account for the real width of the item. Right now we just add 1/4 to the window + var interval = (range.end - range.start) / 4; + return (this.data.start > range.start - interval) && (this.data.start < range.end + interval); + }; /** - * Redraw a edge as a line - * Draw this edge in the given canvas - * The 2d context of a HTML canvas can be retrieved by canvas.getContext("2d"); - * @param {CanvasRenderingContext2D} ctx - * @private + * Repaint the item */ - Edge.prototype._drawLine = function(ctx) { - // set style - ctx.strokeStyle = this._getColor(); - ctx.lineWidth = this._getLineWidth(); + PointItem.prototype.redraw = function() { + var dom = this.dom; + if (!dom) { + // create DOM + this.dom = {}; + dom = this.dom; - if (this.from != this.to) { - // draw line - var via = this._line(ctx); + // background box + dom.point = document.createElement('div'); + // className is updated in redraw() - // draw label - var point; - if (this.label) { - if (this.options.smoothCurves.enabled == true && via != null) { - var midpointX = 0.5*(0.5*(this.from.x + via.x) + 0.5*(this.to.x + via.x)); - var midpointY = 0.5*(0.5*(this.from.y + via.y) + 0.5*(this.to.y + via.y)); - point = {x:midpointX, y:midpointY}; - } - else { - point = this._pointOnLine(0.5); - } - this._label(ctx, this.label, point.x, point.y); - } - } - else { - var x, y; - var radius = this.physics.springLength / 4; - var node = this.from; - if (!node.width) { - node.resize(ctx); - } - if (node.width > node.height) { - x = node.x + node.width / 2; - y = node.y - radius; - } - else { - x = node.x + radius; - y = node.y - node.height / 2; - } - this._circle(ctx, x, y, radius); - point = this._pointOnCircle(x, y, radius, 0.5); - this._label(ctx, this.label, point.x, point.y); + // contents box, right from the dot + dom.content = document.createElement('div'); + dom.content.className = 'content'; + dom.point.appendChild(dom.content); + + // dot at start + dom.dot = document.createElement('div'); + dom.point.appendChild(dom.dot); + + // attach this item as attribute + dom.point['timeline-item'] = this; + + this.dirty = true; } - }; - /** - * Get the line width of the edge. Depends on width and whether one of the - * connected nodes is selected. - * @return {Number} width - * @private - */ - Edge.prototype._getLineWidth = function() { - if (this.selected == true) { - return Math.max(Math.min(this.widthSelected, this.options.widthMax), 0.3*this.networkScaleInv); + // append DOM to parent DOM + if (!this.parent) { + throw new Error('Cannot redraw item: no parent attached'); } - else { - if (this.hover == true) { - return Math.max(Math.min(this.options.hoverWidth, this.options.widthMax), 0.3*this.networkScaleInv); - } - else { - return Math.max(this.options.width, 0.3*this.networkScaleInv); + if (!dom.point.parentNode) { + var foreground = this.parent.dom.foreground; + if (!foreground) { + throw new Error('Cannot redraw item: parent has no foreground container element'); } + foreground.appendChild(dom.point); } - }; + this.displayed = true; - Edge.prototype._getViaCoordinates = function () { - if (this.options.smoothCurves.dynamic == true && this.options.smoothCurves.enabled == true ) { - return this.via; - } - else if (this.options.smoothCurves.enabled == false) { - return {x:0,y:0}; - } - else { - var xVia = null; - var yVia = null; - var factor = this.options.smoothCurves.roundness; - var type = this.options.smoothCurves.type; + // Update DOM when item is marked dirty. An item is marked dirty when: + // - the item is not yet rendered + // - the item's data is changed + // - the item is selected/deselected + if (this.dirty) { + this._updateContents(this.dom.content); + this._updateTitle(this.dom.point); + this._updateDataAttributes(this.dom.point); + this._updateStyle(this.dom.point); - var dx = Math.abs(this.from.x - this.to.x); - var dy = Math.abs(this.from.y - this.to.y); - if (type == 'discrete' || type == 'diagonalCross') { - if (Math.abs(this.from.x - this.to.x) < Math.abs(this.from.y - this.to.y)) { - if (this.from.y > this.to.y) { - if (this.from.x < this.to.x) { - xVia = this.from.x + factor * dy; - yVia = this.from.y - factor * dy; - } - else if (this.from.x > this.to.x) { - xVia = this.from.x - factor * dy; - yVia = this.from.y - factor * dy; - } - } - else if (this.from.y < this.to.y) { - if (this.from.x < this.to.x) { - xVia = this.from.x + factor * dy; - yVia = this.from.y + factor * dy; - } - else if (this.from.x > this.to.x) { - xVia = this.from.x - factor * dy; - yVia = this.from.y + factor * dy; - } - } - if (type == "discrete") { - xVia = dx < factor * dy ? this.from.x : xVia; - } - } - else if (Math.abs(this.from.x - this.to.x) > Math.abs(this.from.y - this.to.y)) { - if (this.from.y > this.to.y) { - if (this.from.x < this.to.x) { - xVia = this.from.x + factor * dx; - yVia = this.from.y - factor * dx; - } - else if (this.from.x > this.to.x) { - xVia = this.from.x - factor * dx; - yVia = this.from.y - factor * dx; - } - } - else if (this.from.y < this.to.y) { - if (this.from.x < this.to.x) { - xVia = this.from.x + factor * dx; - yVia = this.from.y + factor * dx; - } - else if (this.from.x > this.to.x) { - xVia = this.from.x - factor * dx; - yVia = this.from.y + factor * dx; - } - } - if (type == "discrete") { - yVia = dy < factor * dx ? this.from.y : yVia; - } - } - } - else if (type == "straightCross") { - if (Math.abs(this.from.x - this.to.x) < Math.abs(this.from.y - this.to.y)) { // up - down - xVia = this.from.x; - if (this.from.y < this.to.y) { - yVia = this.to.y - (1 - factor) * dy; - } - else { - yVia = this.to.y + (1 - factor) * dy; - } - } - else if (Math.abs(this.from.x - this.to.x) > Math.abs(this.from.y - this.to.y)) { // left - right - if (this.from.x < this.to.x) { - xVia = this.to.x - (1 - factor) * dx; - } - else { - xVia = this.to.x + (1 - factor) * dx; - } - yVia = this.from.y; - } - } - else if (type == 'horizontal') { - if (this.from.x < this.to.x) { - xVia = this.to.x - (1 - factor) * dx; - } - else { - xVia = this.to.x + (1 - factor) * dx; - } - yVia = this.from.y; - } - else if (type == 'vertical') { - xVia = this.from.x; - if (this.from.y < this.to.y) { - yVia = this.to.y - (1 - factor) * dy; - } - else { - yVia = this.to.y + (1 - factor) * dy; - } - } - else { // continuous - if (Math.abs(this.from.x - this.to.x) < Math.abs(this.from.y - this.to.y)) { - if (this.from.y > this.to.y) { - if (this.from.x < this.to.x) { - // console.log(1) - xVia = this.from.x + factor * dy; - yVia = this.from.y - factor * dy; - xVia = this.to.x < xVia ? this.to.x : xVia; - } - else if (this.from.x > this.to.x) { - // console.log(2) - xVia = this.from.x - factor * dy; - yVia = this.from.y - factor * dy; - xVia = this.to.x > xVia ? this.to.x : xVia; - } - } - else if (this.from.y < this.to.y) { - if (this.from.x < this.to.x) { - // console.log(3) - xVia = this.from.x + factor * dy; - yVia = this.from.y + factor * dy; - xVia = this.to.x < xVia ? this.to.x : xVia; - } - else if (this.from.x > this.to.x) { - // console.log(4, this.from.x, this.to.x) - xVia = this.from.x - factor * dy; - yVia = this.from.y + factor * dy; - xVia = this.to.x > xVia ? this.to.x : xVia; - } - } - } - else if (Math.abs(this.from.x - this.to.x) > Math.abs(this.from.y - this.to.y)) { - if (this.from.y > this.to.y) { - if (this.from.x < this.to.x) { - // console.log(5) - xVia = this.from.x + factor * dx; - yVia = this.from.y - factor * dx; - yVia = this.to.y > yVia ? this.to.y : yVia; - } - else if (this.from.x > this.to.x) { - // console.log(6) - xVia = this.from.x - factor * dx; - yVia = this.from.y - factor * dx; - yVia = this.to.y > yVia ? this.to.y : yVia; - } - } - else if (this.from.y < this.to.y) { - if (this.from.x < this.to.x) { - // console.log(7) - xVia = this.from.x + factor * dx; - yVia = this.from.y + factor * dx; - yVia = this.to.y < yVia ? this.to.y : yVia; - } - else if (this.from.x > this.to.x) { - // console.log(8) - xVia = this.from.x - factor * dx; - yVia = this.from.y + factor * dx; - yVia = this.to.y < yVia ? this.to.y : yVia; - } - } - } - } + // update class + var className = (this.data.className? ' ' + this.data.className : '') + + (this.selected ? ' selected' : ''); + dom.point.className = 'item point' + className; + dom.dot.className = 'item dot' + className; + // recalculate size + this.width = dom.point.offsetWidth; + this.height = dom.point.offsetHeight; + this.props.dot.width = dom.dot.offsetWidth; + this.props.dot.height = dom.dot.offsetHeight; + this.props.content.height = dom.content.offsetHeight; - return {x: xVia, y: yVia}; - } - }; + // resize contents + dom.content.style.marginLeft = 2 * this.props.dot.width + 'px'; + //dom.content.style.marginRight = ... + 'px'; // TODO: margin right - /** - * Draw a line between two nodes - * @param {CanvasRenderingContext2D} ctx - * @private - */ - Edge.prototype._line = function (ctx) { - // draw a straight line - ctx.beginPath(); - ctx.moveTo(this.from.x, this.from.y); - if (this.options.smoothCurves.enabled == true) { - if (this.options.smoothCurves.dynamic == false) { - var via = this._getViaCoordinates(); - if (via.x == null) { - ctx.lineTo(this.to.x, this.to.y); - ctx.stroke(); - return null; - } - else { - // this.via.x = via.x; - // this.via.y = via.y; - ctx.quadraticCurveTo(via.x,via.y,this.to.x, this.to.y); - ctx.stroke(); - return via; - } - } - else { - ctx.quadraticCurveTo(this.via.x,this.via.y,this.to.x, this.to.y); - ctx.stroke(); - return this.via; - } - } - else { - ctx.lineTo(this.to.x, this.to.y); - ctx.stroke(); - return null; + dom.dot.style.top = ((this.height - this.props.dot.height) / 2) + 'px'; + dom.dot.style.left = (this.props.dot.width / 2) + 'px'; + + this.dirty = false; } + + this._repaintDeleteButton(dom.point); }; /** - * Draw a line from a node to itself, a circle - * @param {CanvasRenderingContext2D} ctx - * @param {Number} x - * @param {Number} y - * @param {Number} radius - * @private + * Show the item in the DOM (when not already visible). The items DOM will + * be created when needed. */ - Edge.prototype._circle = function (ctx, x, y, radius) { - // draw a circle - ctx.beginPath(); - ctx.arc(x, y, radius, 0, 2 * Math.PI, false); - ctx.stroke(); + PointItem.prototype.show = function() { + if (!this.displayed) { + this.redraw(); + } }; /** - * Draw label with white background and with the middle at (x, y) - * @param {CanvasRenderingContext2D} ctx - * @param {String} text - * @param {Number} x - * @param {Number} y - * @private + * Hide the item from the DOM (when visible) */ - Edge.prototype._label = function (ctx, text, x, y) { - if (text) { - ctx.font = ((this.from.selected || this.to.selected) ? "bold " : "") + - this.options.fontSize + "px " + this.options.fontFace; - var yLine; - - if (this.dirtyLabel == true) { - var lines = String(text).split('\n'); - var lineCount = lines.length; - var fontSize = Number(this.options.fontSize); - yLine = y + (1 - lineCount) / 2 * fontSize; - - var 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; - } - var height = this.options.fontSize * lineCount; - var left = x - width / 2; - var top = y - height / 2; - - // cache - this.labelDimensions = {top:top,left:left,width:width,height:height,yLine:yLine}; + PointItem.prototype.hide = function() { + if (this.displayed) { + if (this.dom.point.parentNode) { + this.dom.point.parentNode.removeChild(this.dom.point); } - var yLine = this.labelDimensions.yLine; - - ctx.save(); - - if (this.options.labelAlignment != "horizontal"){ - ctx.translate(x, yLine); - this._rotateForLabelAlignment(ctx); - x = 0; - yLine = 0; - } + this.top = null; + this.left = null; - - this._drawLabelRect(ctx); - this._drawLabelText(ctx,x,yLine, lines, lineCount, fontSize); - - ctx.restore(); + this.displayed = false; } }; /** - * Rotates the canvas so the text is most readable - * @param {CanvasRenderingContext2D} ctx - * @private + * Reposition the item horizontally + * @Override */ - Edge.prototype._rotateForLabelAlignment = function(ctx) { - var dy = this.from.y - this.to.y; - var dx = this.from.x - this.to.x; - var angleInDegrees = Math.atan2(dy, dx); + PointItem.prototype.repositionX = function() { + var start = this.conversion.toScreen(this.data.start); - // rotate so label it is readable - if((angleInDegrees < -1 && dx < 0) || (angleInDegrees > 0 && dx < 0)){ - angleInDegrees = angleInDegrees + Math.PI; - } - - ctx.rotate(angleInDegrees); + this.left = start - this.props.dot.width; + + // reposition point + this.dom.point.style.left = this.left + 'px'; }; /** - * Draws the label rectangle - * @param {CanvasRenderingContext2D} ctx - * @param {String} labelAlignment - * @private + * Reposition the item vertically + * @Override */ - Edge.prototype._drawLabelRect = function(ctx) { - if (this.options.fontFill !== undefined && this.options.fontFill !== null && this.options.fontFill !== "none") { - ctx.fillStyle = this.options.fontFill; - - var lineMargin = 2; + PointItem.prototype.repositionY = function() { + var orientation = this.options.orientation, + point = this.dom.point; - if (this.options.labelAlignment == 'line-center') { - ctx.fillRect(-this.labelDimensions.width * 0.5, -this.labelDimensions.height * 0.5, this.labelDimensions.width, this.labelDimensions.height); - } - else if (this.options.labelAlignment == 'line-above') { - ctx.fillRect(-this.labelDimensions.width * 0.5, -(this.labelDimensions.height + lineMargin), this.labelDimensions.width, this.labelDimensions.height); - } - else if (this.options.labelAlignment == 'line-below') { - ctx.fillRect(-this.labelDimensions.width * 0.5, lineMargin, this.labelDimensions.width, this.labelDimensions.height); - } - else { - ctx.fillRect(this.labelDimensions.left, this.labelDimensions.top, this.labelDimensions.width, this.labelDimensions.height); - } + if (orientation == 'top') { + point.style.top = this.top + 'px'; + } + else { + point.style.top = (this.parent.height - this.top - this.height) + 'px'; } }; + module.exports = PointItem; + + +/***/ }, +/* 34 */ +/***/ function(module, exports, __webpack_require__) { + + var Hammer = __webpack_require__(19); + var Item = __webpack_require__(30); + var BackgroundGroup = __webpack_require__(31); + var RangeItem = __webpack_require__(29); + /** - * Draws the label text - * @param {CanvasRenderingContext2D} ctx - * @param {Number} x - * @param {Number} yLine - * @param {Array} lines - * @param {Number} lineCount - * @param {Number} fontSize - * @private + * @constructor BackgroundItem + * @extends Item + * @param {Object} data Object containing parameters start, end + * content, className. + * @param {{toScreen: function, toTime: function}} conversion + * Conversion functions from time to screen and vice versa + * @param {Object} [options] Configuration options + * // TODO: describe options */ - Edge.prototype._drawLabelText = function(ctx, x, yLine, lines, lineCount, fontSize) { - // draw text - ctx.fillStyle = this.options.fontColor || "black"; - ctx.textAlign = "center"; - - // check for label alignment - if (this.options.labelAlignment != 'horizontal') { - var lineMargin = 2; - if (this.options.labelAlignment == 'line-above') { - ctx.textBaseline = "alphabetic"; - yLine -= 2 * lineMargin; // distance from edge, required because we use alphabetic. Alphabetic has less difference between browsers + // TODO: implement support for the BackgroundItem just having a start, then being displayed as a sort of an annotation + function BackgroundItem (data, conversion, options) { + this.props = { + content: { + width: 0 } - else if (this.options.labelAlignment == 'line-below') { - ctx.textBaseline = "hanging"; - yLine += 2 * lineMargin;// distance from edge, required because we use hanging. Hanging has less difference between browsers + }; + this.overflow = false; // if contents can overflow (css styling), this flag is set to true + + // validate data + if (data) { + if (data.start == undefined) { + throw new Error('Property "start" missing in item ' + data.id); } - else { - ctx.textBaseline = "middle"; + if (data.end == undefined) { + throw new Error('Property "end" missing in item ' + data.id); } } - else { - ctx.textBaseline = "middle"; - } - // check for strokeWidth - if (this.options.fontStrokeWidth > 0){ - ctx.lineWidth = this.options.fontStrokeWidth; - ctx.strokeStyle = this.options.fontStrokeColor; - ctx.lineJoin = 'round'; - } - for (var i = 0; i < lineCount; i++) { - if(this.options.fontStrokeWidth > 0){ - ctx.strokeText(lines[i], x, yLine); - } - ctx.fillText(lines[i], x, yLine); - yLine += fontSize; - } + Item.call(this, data, conversion, options); + + this.emptyContent = false; + } + + BackgroundItem.prototype = new Item (null, null, null); + + BackgroundItem.prototype.baseClassName = 'item background'; + BackgroundItem.prototype.stack = false; + + /** + * Check whether this item is visible inside given range + * @returns {{start: Number, end: Number}} range with a timestamp for start and end + * @returns {boolean} True if visible + */ + BackgroundItem.prototype.isVisible = function(range) { + // determine visibility + return (this.data.start < range.end) && (this.data.end > range.start); }; /** - * Redraw a edge as a dashed line - * Draw this edge in the given canvas - * @author David Jordan - * @date 2012-08-08 - * The 2d context of a HTML canvas can be retrieved by canvas.getContext("2d"); - * @param {CanvasRenderingContext2D} ctx - * @private + * Repaint the item */ - Edge.prototype._drawDashLine = function(ctx) { - // set style - ctx.strokeStyle = this._getColor(); - ctx.lineWidth = this._getLineWidth(); + BackgroundItem.prototype.redraw = function() { + var dom = this.dom; + if (!dom) { + // create DOM + this.dom = {}; + dom = this.dom; - var via = null; - // only firefox and chrome support this method, else we use the legacy one. - if (ctx.setLineDash !== undefined) { - ctx.save(); - // configure the dash pattern - var pattern = [0]; - if (this.options.dash.length !== undefined && this.options.dash.gap !== undefined) { - pattern = [this.options.dash.length,this.options.dash.gap]; - } - else { - pattern = [5,5]; - } + // background box + dom.box = document.createElement('div'); + // className is updated in redraw() - // set dash settings for chrome or firefox - ctx.setLineDash(pattern); - ctx.lineDashOffset = 0; + // contents box + dom.content = document.createElement('div'); + dom.content.className = 'content'; + dom.box.appendChild(dom.content); - // draw the line - via = this._line(ctx); + // Note: we do NOT attach this item as attribute to the DOM, + // such that background items cannot be selected + //dom.box['timeline-item'] = this; - // restore the dash settings. - ctx.setLineDash([0]); - ctx.lineDashOffset = 0; - ctx.restore(); + this.dirty = true; } - else { // unsupporting smooth lines - // draw dashed line - ctx.beginPath(); - ctx.lineCap = 'round'; - if (this.options.dash.altLength !== undefined) //If an alt dash value has been set add to the array this value - { - ctx.dashedLine(this.from.x,this.from.y,this.to.x,this.to.y, - [this.options.dash.length,this.options.dash.gap,this.options.dash.altLength,this.options.dash.gap]); - } - else if (this.options.dash.length !== undefined && this.options.dash.gap !== undefined) //If a dash and gap value has been set add to the array this value - { - ctx.dashedLine(this.from.x,this.from.y,this.to.x,this.to.y, - [this.options.dash.length,this.options.dash.gap]); - } - else //If all else fails draw a line - { - ctx.moveTo(this.from.x, this.from.y); - ctx.lineTo(this.to.x, this.to.y); + + // append DOM to parent DOM + if (!this.parent) { + throw new Error('Cannot redraw item: no parent attached'); + } + if (!dom.box.parentNode) { + var background = this.parent.dom.background; + if (!background) { + throw new Error('Cannot redraw item: parent has no background container element'); } - ctx.stroke(); + background.appendChild(dom.box); } + this.displayed = true; - // draw label - if (this.label) { - var point; - if (this.options.smoothCurves.enabled == true && via != null) { - var midpointX = 0.5*(0.5*(this.from.x + via.x) + 0.5*(this.to.x + via.x)); - var midpointY = 0.5*(0.5*(this.from.y + via.y) + 0.5*(this.to.y + via.y)); - point = {x:midpointX, y:midpointY}; - } - else { - point = this._pointOnLine(0.5); - } - this._label(ctx, this.label, point.x, point.y); + // Update DOM when item is marked dirty. An item is marked dirty when: + // - the item is not yet rendered + // - the item's data is changed + // - the item is selected/deselected + if (this.dirty) { + this._updateContents(this.dom.content); + this._updateTitle(this.dom.content); + this._updateDataAttributes(this.dom.content); + this._updateStyle(this.dom.box); + + // update class + var className = (this.data.className ? (' ' + this.data.className) : '') + + (this.selected ? ' selected' : ''); + dom.box.className = this.baseClassName + className; + + // determine from css whether this box has overflow + this.overflow = window.getComputedStyle(dom.content).overflow !== 'hidden'; + + // recalculate size + this.props.content.width = this.dom.content.offsetWidth; + this.height = 0; // set height zero, so this item will be ignored when stacking items + + this.dirty = false; } }; /** - * Get a point on a line - * @param {Number} percentage. Value between 0 (line start) and 1 (line end) - * @return {Object} point - * @private + * Show the item in the DOM (when not already visible). The items DOM will + * be created when needed. */ - Edge.prototype._pointOnLine = function (percentage) { - return { - x: (1 - percentage) * this.from.x + percentage * this.to.x, - y: (1 - percentage) * this.from.y + percentage * this.to.y - } - }; + BackgroundItem.prototype.show = RangeItem.prototype.show; /** - * 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 + * Hide the item from the DOM (when visible) + * @return {Boolean} changed */ - Edge.prototype._pointOnCircle = function (x, y, radius, percentage) { - var angle = (percentage - 3/8) * 2 * Math.PI; - return { - x: x + radius * Math.cos(angle), - y: y - radius * Math.sin(angle) - } - }; + BackgroundItem.prototype.hide = RangeItem.prototype.hide; /** - * Redraw a edge as a line with an arrow halfway the line - * Draw this edge in the given canvas - * The 2d context of a HTML canvas can be retrieved by canvas.getContext("2d"); - * @param {CanvasRenderingContext2D} ctx - * @private + * Reposition the item horizontally + * @Override */ - Edge.prototype._drawArrowCenter = function(ctx) { - var point; - // set style - ctx.strokeStyle = this._getColor(); - ctx.fillStyle = ctx.strokeStyle; - ctx.lineWidth = this._getLineWidth(); + BackgroundItem.prototype.repositionX = RangeItem.prototype.repositionX; - if (this.from != this.to) { - // draw line - var via = this._line(ctx); + /** + * Reposition the item vertically + * @Override + */ + BackgroundItem.prototype.repositionY = function(margin) { + var onTop = this.options.orientation === 'top'; + this.dom.content.style.top = onTop ? '' : '0'; + this.dom.content.style.bottom = onTop ? '0' : ''; + var height; - var angle = Math.atan2((this.to.y - this.from.y), (this.to.x - this.from.x)); - var length = (10 + 5 * this.options.width) * this.options.arrowScaleFactor; - // draw an arrow halfway the line - if (this.options.smoothCurves.enabled == true && via != null) { - var midpointX = 0.5*(0.5*(this.from.x + via.x) + 0.5*(this.to.x + via.x)); - var midpointY = 0.5*(0.5*(this.from.y + via.y) + 0.5*(this.to.y + via.y)); - point = {x:midpointX, y:midpointY}; + // special positioning for subgroups + if (this.data.subgroup !== undefined) { + var itemSubgroup = this.data.subgroup; + var subgroups = this.parent.subgroups; + var subgroupIndex = subgroups[itemSubgroup].index; + // if the orientation is top, we need to take the difference in height into account. + if (onTop == true) { + // the first subgroup will have to account for the distance from the top to the first item. + height = this.parent.subgroups[itemSubgroup].height + margin.item.vertical; + height += subgroupIndex == 0 ? margin.axis - 0.5*margin.item.vertical : 0; + var newTop = this.parent.top; + for (var subgroup in subgroups) { + if (subgroups.hasOwnProperty(subgroup)) { + if (subgroups[subgroup].visible == true && subgroups[subgroup].index < subgroupIndex) { + newTop += subgroups[subgroup].height + margin.item.vertical; + } + } + } + + // the others will have to be offset downwards with this same distance. + newTop += subgroupIndex != 0 ? margin.axis - 0.5 * margin.item.vertical : 0; + this.dom.box.style.top = newTop + 'px'; + this.dom.box.style.bottom = ''; } + // and when the orientation is bottom: else { - point = this._pointOnLine(0.5); - } - - ctx.arrow(point.x, point.y, angle, length); - ctx.fill(); - ctx.stroke(); - - // draw label - if (this.label) { - this._label(ctx, this.label, point.x, point.y); + var newTop = this.parent.top; + for (var subgroup in subgroups) { + if (subgroups.hasOwnProperty(subgroup)) { + if (subgroups[subgroup].visible == true && subgroups[subgroup].index > subgroupIndex) { + newTop += subgroups[subgroup].height + margin.item.vertical; + } + } + } + height = this.parent.subgroups[itemSubgroup].height + margin.item.vertical; + this.dom.box.style.top = newTop + 'px'; + this.dom.box.style.bottom = ''; } } + // and in the case of no subgroups: else { - // draw circle - var x, y; - var radius = 0.25 * Math.max(100,this.physics.springLength); - var node = this.from; - if (!node.width) { - node.resize(ctx); - } - if (node.width > node.height) { - x = node.x + node.width * 0.5; - y = node.y - radius; + // we want backgrounds with groups to only show in groups. + if (this.parent instanceof BackgroundGroup) { + // if the item is not in a group: + height = Math.max(this.parent.height, + this.parent.itemSet.body.domProps.center.height, + this.parent.itemSet.body.domProps.centerContainer.height); + this.dom.box.style.top = onTop ? '0' : ''; + this.dom.box.style.bottom = onTop ? '' : '0'; } else { - x = node.x + radius; - y = node.y - node.height * 0.5; - } - this._circle(ctx, x, y, radius); - - // draw all arrows - var angle = 0.2 * Math.PI; - var length = (10 + 5 * this.options.width) * this.options.arrowScaleFactor; - point = this._pointOnCircle(x, y, radius, 0.5); - ctx.arrow(point.x, point.y, angle, length); - ctx.fill(); - ctx.stroke(); - - // draw label - if (this.label) { - point = this._pointOnCircle(x, y, radius, 0.5); - this._label(ctx, this.label, point.x, point.y); + height = this.parent.height; + // same alignment for items when orientation is top or bottom + this.dom.box.style.top = this.parent.top + 'px'; + this.dom.box.style.bottom = ''; } } + this.dom.box.style.height = height + 'px'; }; - Edge.prototype._pointOnBezier = function(t) { - var via = this._getViaCoordinates(); + module.exports = BackgroundItem; - var x = Math.pow(1-t,2)*this.from.x + (2*t*(1 - t))*via.x + Math.pow(t,2)*this.to.x; - var y = Math.pow(1-t,2)*this.from.y + (2*t*(1 - t))*via.y + Math.pow(t,2)*this.to.y; - return {x:x,y:y}; - } +/***/ }, +/* 35 */ +/***/ function(module, exports, __webpack_require__) { + + var keycharm = __webpack_require__(36); + var Emitter = __webpack_require__(11); + var Hammer = __webpack_require__(19); + var util = __webpack_require__(1); /** - * This function uses binary search to look for the point where the bezier curve crosses the border of the node. - * - * @param from - * @param ctx - * @returns {*} - * @private + * Turn an element into an clickToUse element. + * When not active, the element has a transparent overlay. When the overlay is + * clicked, the mode is changed to active. + * When active, the element is displayed with a blue border around it, and + * the interactive contents of the element can be used. When clicked outside + * the element, the elements mode is changed to inactive. + * @param {Element} container + * @constructor */ - Edge.prototype._findBorderPosition = function(from,ctx) { - var maxIterations = 10; - var iteration = 0; - var low = 0; - var high = 1; - var pos,angle,distanceToBorder, distanceToNodes, difference; - var threshold = 0.2; - var node = this.to; - if (from == true) { - node = this.from; - } + function Activator(container) { + this.active = false; - while (low <= high && iteration < maxIterations) { - var middle = (low + high) * 0.5; + this.dom = { + container: container + }; - pos = this._pointOnBezier(middle); - angle = Math.atan2((node.y - pos.y), (node.x - pos.x)); - distanceToBorder = node.distanceToBorder(ctx,angle); - distanceToNodes = Math.sqrt(Math.pow(pos.x-node.x,2) + Math.pow(pos.y-node.y,2)); - difference = distanceToBorder - distanceToNodes; - if (Math.abs(difference) < threshold) { - break; // found - } - else if (difference < 0) { // distance to nodes is larger than distance to border --> t needs to be bigger if we're looking at the to node. - if (from == false) { - low = middle; - } - else { - high = middle; - } - } - else { - if (from == false) { - high = middle; - } - else { - low = middle; - } + this.dom.overlay = document.createElement('div'); + this.dom.overlay.className = 'overlay'; + + this.dom.container.appendChild(this.dom.overlay); + + this.hammer = Hammer(this.dom.overlay, {prevent_default: false}); + this.hammer.on('tap', this._onTapOverlay.bind(this)); + + // block all touch events (except tap) + var me = this; + var events = [ + 'touch', 'pinch', + 'doubletap', 'hold', + 'dragstart', 'drag', 'dragend', + 'mousewheel', 'DOMMouseScroll' // DOMMouseScroll is needed for Firefox + ]; + events.forEach(function (event) { + me.hammer.on(event, function (event) { + event.stopPropagation(); + }); + }); + + // attach a tap event to the window, in order to deactivate when clicking outside the timeline + this.windowHammer = Hammer(window, {prevent_default: false}); + this.windowHammer.on('tap', function (event) { + // deactivate when clicked outside the container + if (!_hasParent(event.target, container)) { + me.deactivate(); } + }); - iteration++; + if (this.keycharm !== undefined) { + this.keycharm.destroy(); } - pos.t = middle; + this.keycharm = keycharm(); - return pos; - }; + // keycharm listener only bounded when active) + this.escListener = this.deactivate.bind(this); + } + + // turn into an event emitter + Emitter(Activator.prototype); + + // The currently active activator + Activator.current = null; /** - * Redraw a edge as a line with an arrow - * Draw this edge in the given canvas - * The 2d context of a HTML canvas can be retrieved by canvas.getContext("2d"); - * @param {CanvasRenderingContext2D} ctx - * @private + * Destroy the activator. Cleans up all created DOM and event listeners */ - Edge.prototype._drawArrow = function(ctx) { - // set style - ctx.strokeStyle = this._getColor(); - ctx.fillStyle = ctx.strokeStyle; - ctx.lineWidth = this._getLineWidth(); + Activator.prototype.destroy = function () { + this.deactivate(); - // set vars - var angle, length, arrowPos; + // remove dom + this.dom.overlay.parentNode.removeChild(this.dom.overlay); - // if not connected to itself - if (this.from != this.to) { - // draw line - this._line(ctx); - - // draw arrow head - if (this.options.smoothCurves.enabled == true) { - var via = this._getViaCoordinates(); - arrowPos = this._findBorderPosition(false, ctx); - var guidePos = this._pointOnBezier(Math.max(0.0, arrowPos.t - 0.1)) - angle = Math.atan2((arrowPos.y - guidePos.y), (arrowPos.x - guidePos.x)); - } - else { - angle = Math.atan2((this.to.y - this.from.y), (this.to.x - this.from.x)); - var dx = (this.to.x - this.from.x); - var dy = (this.to.y - this.from.y); - var edgeSegmentLength = Math.sqrt(dx * dx + dy * dy); - var toBorderDist = this.to.distanceToBorder(ctx, angle); - var toBorderPoint = (edgeSegmentLength - toBorderDist) / edgeSegmentLength; - - arrowPos = {}; - arrowPos.x = (1 - toBorderPoint) * this.from.x + toBorderPoint * this.to.x; - arrowPos.y = (1 - toBorderPoint) * this.from.y + toBorderPoint * this.to.y; - } - - // draw arrow at the end of the line - length = (10 + 5 * this.options.width) * this.options.arrowScaleFactor; - ctx.arrow(arrowPos.x,arrowPos.y, angle, length); - ctx.fill(); - ctx.stroke(); - - // draw label - if (this.label) { - var point; - if (this.options.smoothCurves.enabled == true && via != null) { - point = this._pointOnBezier(0.5); - } - else { - point = this._pointOnLine(0.5); - } - this._label(ctx, this.label, point.x, point.y); - } - } - else { - // draw circle - var node = this.from; - var x, y, arrow; - var radius = 0.25 * Math.max(100,this.physics.springLength); - if (!node.width) { - node.resize(ctx); - } - if (node.width > node.height) { - x = node.x + node.width * 0.5; - y = node.y - radius; - arrow = { - x: x, - y: node.y, - angle: 0.9 * Math.PI - }; - } - else { - x = node.x + radius; - y = node.y - node.height * 0.5; - arrow = { - x: node.x, - y: y, - angle: 0.6 * Math.PI - }; - } - ctx.beginPath(); - // TODO: similarly, for a line without arrows, draw to the border of the nodes instead of the center - ctx.arc(x, y, radius, 0, 2 * Math.PI, false); - ctx.stroke(); - - // draw all arrows - var length = (10 + 5 * this.options.width) * this.options.arrowScaleFactor; - ctx.arrow(arrow.x, arrow.y, arrow.angle, length); - ctx.fill(); - ctx.stroke(); - - // draw label - if (this.label) { - point = this._pointOnCircle(x, y, radius, 0.5); - this._label(ctx, this.label, point.x, point.y); - } - } - }; + // cleanup hammer instances + this.hammer = null; + this.windowHammer = null; + // FIXME: cleaning up hammer instances doesn't work (Timeline not removed from memory) + }; /** - * Calculate the distance between a point (x3,y3) and a line segment from - * (x1,y1) to (x2,y2). - * http://stackoverflow.com/questions/849211/shortest-distancae-between-a-point-and-a-line-segment - * @param {number} x1 - * @param {number} y1 - * @param {number} x2 - * @param {number} y2 - * @param {number} x3 - * @param {number} y3 - * @private + * Activate the element + * Overlay is hidden, element is decorated with a blue shadow border */ - Edge.prototype._getDistanceToEdge = function (x1,y1, x2,y2, x3,y3) { // x3,y3 is the point - var returnValue = 0; - if (this.from != this.to) { - if (this.options.smoothCurves.enabled == true) { - var xVia, yVia; - if (this.options.smoothCurves.enabled == true && this.options.smoothCurves.dynamic == true) { - xVia = this.via.x; - yVia = this.via.y; - } - else { - var via = this._getViaCoordinates(); - xVia = via.x; - yVia = via.y; - } - var minDistance = 1e9; - var distance; - var i,t,x,y, lastX, lastY; - for (i = 0; i < 10; i++) { - t = 0.1*i; - x = Math.pow(1-t,2)*x1 + (2*t*(1 - t))*xVia + Math.pow(t,2)*x2; - y = Math.pow(1-t,2)*y1 + (2*t*(1 - t))*yVia + Math.pow(t,2)*y2; - if (i > 0) { - distance = this._getDistanceToLine(lastX,lastY,x,y, x3,y3); - minDistance = distance < minDistance ? distance : minDistance; - } - lastX = x; lastY = y; - } - returnValue = minDistance; - } - else { - returnValue = this._getDistanceToLine(x1,y1,x2,y2,x3,y3); - } - } - else { - var x, y, dx, dy; - var radius = 0.25 * this.physics.springLength; - var node = this.from; - if (node.width > node.height) { - x = node.x + 0.5 * node.width; - y = node.y - radius; - } - else { - x = node.x + radius; - y = node.y - 0.5 * node.height; - } - dx = x - x3; - dy = y - y3; - returnValue = Math.abs(Math.sqrt(dx*dx + dy*dy) - radius); - } - - if (this.labelDimensions.left < x3 && - this.labelDimensions.left + this.labelDimensions.width > x3 && - this.labelDimensions.top < y3 && - this.labelDimensions.top + this.labelDimensions.height > y3) { - return 0; - } - else { - return returnValue; - } - }; - - Edge.prototype._getDistanceToLine = function(x1,y1,x2,y2,x3,y3) { - var px = x2-x1, - py = y2-y1, - something = px*px + py*py, - u = ((x3 - x1) * px + (y3 - y1) * py) / something; - - if (u > 1) { - u = 1; - } - else if (u < 0) { - u = 0; + Activator.prototype.activate = function () { + // we allow only one active activator at a time + if (Activator.current) { + Activator.current.deactivate(); } + Activator.current = this; - var x = x1 + u * px, - y = y1 + u * py, - dx = x - x3, - dy = y - y3; - - //# Note: If the actual distance does not matter, - //# if you only want to compare what this function - //# returns to other results of this function, you - //# can just return the squared distance instead - //# (i.e. remove the sqrt) to gain a little performance - - return Math.sqrt(dx*dx + dy*dy); - }; - - /** - * This allows the zoom level of the network to influence the rendering - * - * @param scale - */ - Edge.prototype.setScale = function(scale) { - this.networkScaleInv = 1.0/scale; - }; - - - Edge.prototype.select = function() { - this.selected = true; - }; + this.active = true; + this.dom.overlay.style.display = 'none'; + util.addClassName(this.dom.container, 'vis-active'); - Edge.prototype.unselect = function() { - this.selected = false; - }; + this.emit('change'); + this.emit('activate'); - Edge.prototype.positionBezierNode = function() { - if (this.via !== null && this.from !== null && this.to !== null) { - 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 !== null) { - this.via.x = 0; - this.via.y = 0; - } + // ugly hack: bind ESC after emitting the events, as the Network rebinds all + // keyboard events on a 'change' event + this.keycharm.bind('esc', this.escListener); }; /** - * This function draws the control nodes for the manipulator. - * In order to enable this, only set the this.controlNodesEnabled to true. - * @param ctx + * Deactivate the element + * Overlay is displayed on top of the element */ - Edge.prototype._drawControlNodes = function(ctx) { - if (this.controlNodesEnabled == true) { - if (this.controlNodes.from === null && this.controlNodes.to === null) { - var nodeIdFrom = "edgeIdFrom:".concat(this.id); - var nodeIdTo = "edgeIdTo:".concat(this.id); - var constants = { - nodes:{group:'', radius:7, borderWidth:2, borderWidthSelected: 2}, - physics:{damping:0}, - clustering: {maxNodeSizeIncrements: 0 ,nodeScaling: {width:0, height: 0, radius:0}} - }; - this.controlNodes.from = new Node( - {id:nodeIdFrom, - shape:'dot', - color:{background:'#ff0000', border:'#3c3c3c', highlight: {background:'#07f968'}} - },{},{},constants); - this.controlNodes.to = new Node( - {id:nodeIdTo, - shape:'dot', - color:{background:'#ff0000', border:'#3c3c3c', highlight: {background:'#07f968'}} - },{},{},constants); - } - - this.controlNodes.positions = {}; - if (this.controlNodes.from.selected == false) { - this.controlNodes.positions.from = this.getControlNodeFromPosition(ctx); - this.controlNodes.from.x = this.controlNodes.positions.from.x; - this.controlNodes.from.y = this.controlNodes.positions.from.y; - } - if (this.controlNodes.to.selected == false) { - this.controlNodes.positions.to = this.getControlNodeToPosition(ctx); - this.controlNodes.to.x = this.controlNodes.positions.to.x; - this.controlNodes.to.y = this.controlNodes.positions.to.y; - } + Activator.prototype.deactivate = function () { + this.active = false; + this.dom.overlay.style.display = ''; + util.removeClassName(this.dom.container, 'vis-active'); + this.keycharm.unbind('esc', this.escListener); - this.controlNodes.from.draw(ctx); - this.controlNodes.to.draw(ctx); - } - else { - this.controlNodes = {from:null, to:null, positions:{}}; - } + this.emit('change'); + this.emit('deactivate'); }; /** - * Enable control nodes. + * Handle a tap event: activate the container + * @param event * @private */ - Edge.prototype._enableControlNodes = function() { - this.fromBackup = this.from; - this.toBackup = this.to; - this.controlNodesEnabled = true; + Activator.prototype._onTapOverlay = function (event) { + // activate the container + this.activate(); + event.stopPropagation(); }; /** - * disable control nodes and remove from dynamicEdges from old node + * Test whether the element has the requested parent element somewhere in + * its chain of parent nodes. + * @param {HTMLElement} element + * @param {HTMLElement} parent + * @returns {boolean} Returns true when the parent is found somewhere in the + * chain of parent nodes. * @private */ - Edge.prototype._disableControlNodes = function() { - this.fromId = this.from.id; - this.toId = this.to.id; - if (this.fromId != this.fromBackup.id) { // from was changed, remove edge from old 'from' node dynamic edges - this.fromBackup.detachEdge(this); - } - else if (this.toId != this.toBackup.id) { // to was changed, remove edge from old 'to' node dynamic edges - this.toBackup.detachEdge(this); + function _hasParent(element, parent) { + while (element) { + if (element === parent) { + return true + } + element = element.parentNode; } + return false; + } - this.fromBackup = null; - this.toBackup = null; - this.controlNodesEnabled = false; - }; - - - /** - * This checks if one of the control nodes is selected and if so, returns the control node object. Else it returns null. - * @param x - * @param y - * @returns {null} - * @private - */ - Edge.prototype._getSelectedControlNode = function(x,y) { - var positions = this.controlNodes.positions; - var fromDistance = Math.sqrt(Math.pow(x - positions.from.x,2) + Math.pow(y - positions.from.y,2)); - var toDistance = Math.sqrt(Math.pow(x - positions.to.x ,2) + Math.pow(y - positions.to.y ,2)); + module.exports = Activator; - if (fromDistance < 15) { - this.connectedNode = this.from; - this.from = this.controlNodes.from; - return this.controlNodes.from; - } - else if (toDistance < 15) { - this.connectedNode = this.to; - this.to = this.controlNodes.to; - return this.controlNodes.to; - } - else { - return null; - } - }; +/***/ }, +/* 36 */ +/***/ function(module, exports, __webpack_require__) { + var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;"use strict"; /** - * this resets the control nodes to their original position. - * @private + * Created by Alex on 11/6/2014. */ - Edge.prototype._restoreControlNodes = function() { - if (this.controlNodes.from.selected == true) { - this.from = this.connectedNode; - this.connectedNode = null; - this.controlNodes.from.unselect(); - } - else if (this.controlNodes.to.selected == true) { - this.to = this.connectedNode; - this.connectedNode = null; - this.controlNodes.to.unselect(); - } - }; - /** - * this calculates the position of the control nodes on the edges of the parent nodes. - * - * @param ctx - * @returns {x: *, y: *} - */ - Edge.prototype.getControlNodeFromPosition = function(ctx) { - // draw arrow head - var controlnodeFromPos; - if (this.options.smoothCurves.enabled == true) { - controlnodeFromPos = this._findBorderPosition(true, ctx); + // 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(); } - else { - var angle = Math.atan2((this.to.y - this.from.y), (this.to.x - this.from.x)); - var dx = (this.to.x - this.from.x); - var dy = (this.to.y - this.from.y); - var edgeSegmentLength = Math.sqrt(dx * dx + dy * dy); + }(this, function () { - var fromBorderDist = this.from.distanceToBorder(ctx, angle + Math.PI); - var fromBorderPoint = (edgeSegmentLength - fromBorderDist) / edgeSegmentLength; - controlnodeFromPos = {}; - controlnodeFromPos.x = (fromBorderPoint) * this.from.x + (1 - fromBorderPoint) * this.to.x; - controlnodeFromPos.y = (fromBorderPoint) * this.from.y + (1 - fromBorderPoint) * this.to.y; - } + function keycharm(options) { + var preventDefault = options && options.preventDefault || false; - return controlnodeFromPos; - }; + var container = options && options.container || window; + var _exportFunctions = {}; + var _bound = {keydown:{}, keyup:{}}; + var _keys = {}; + var i; - /** - * this calculates the position of the control nodes on the edges of the parent nodes. - * - * @param ctx - * @returns {{from: {x: number, y: number}, to: {x: *, y: *}}} - */ - Edge.prototype.getControlNodeToPosition = function(ctx) { - // draw arrow head - var controlnodeFromPos,controlnodeToPos; - if (this.options.smoothCurves.enabled == true) { - controlnodeToPos = this._findBorderPosition(false, ctx); - } - else { - var angle = Math.atan2((this.to.y - this.from.y), (this.to.x - this.from.x)); - var dx = (this.to.x - this.from.x); - var dy = (this.to.y - this.from.y); - var edgeSegmentLength = Math.sqrt(dx * dx + dy * dy); - var toBorderDist = this.to.distanceToBorder(ctx, angle); - var toBorderPoint = (edgeSegmentLength - toBorderDist) / edgeSegmentLength; + // 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};} - controlnodeToPos = {}; - controlnodeToPos.x = (1 - toBorderPoint) * this.from.x + toBorderPoint * this.to.x; - controlnodeToPos.y = (1 - toBorderPoint) * this.from.y + toBorderPoint * this.to.y; - } + // 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}; - return controlnodeToPos; - }; - module.exports = Edge; -/***/ }, -/* 38 */ -/***/ function(module, exports, __webpack_require__) { + var down = function(event) {handleEvent(event,'keydown');}; + var up = function(event) {handleEvent(event,'keyup');}; - var util = __webpack_require__(1); + // 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); + } + } - /** - * @class Groups - * This class can store groups and properties specific for groups. - */ - function Groups() { - this.clear(); - this.defaultIndex = 0; - } + if (preventDefault == true) { + event.preventDefault(); + } + } + }; + // bind a key to a callback + _exportFunctions.bind = function(key, callback, type) { + if (type === undefined) { + type = 'keydown'; + } + if (_keys[key] === undefined) { + throw new Error("unsupported key: " + key); + } + if (_bound[type][_keys[key].code] === undefined) { + _bound[type][_keys[key].code] = []; + } + _bound[type][_keys[key].code].push({fn:callback, shift:_keys[key].shift}); + }; - /** - * default constants for group colors - */ - Groups.DEFAULT = [ - {border: "#2B7CE9", background: "#97C2FC", highlight: {border: "#2B7CE9", background: "#D2E5FF"}, hover: {border: "#2B7CE9", background: "#D2E5FF"}}, // blue - {border: "#FFA500", background: "#FFFF00", highlight: {border: "#FFA500", background: "#FFFFA3"}, hover: {border: "#FFA500", background: "#FFFFA3"}}, // yellow - {border: "#FA0A10", background: "#FB7E81", highlight: {border: "#FA0A10", background: "#FFAFB1"}, hover: {border: "#FA0A10", background: "#FFAFB1"}}, // red - {border: "#41A906", background: "#7BE141", highlight: {border: "#41A906", background: "#A1EC76"}, hover: {border: "#41A906", background: "#A1EC76"}}, // green - {border: "#E129F0", background: "#EB7DF4", highlight: {border: "#E129F0", background: "#F0B3F5"}, hover: {border: "#E129F0", background: "#F0B3F5"}}, // magenta - {border: "#7C29F0", background: "#AD85E4", highlight: {border: "#7C29F0", background: "#D3BDF0"}, hover: {border: "#7C29F0", background: "#D3BDF0"}}, // purple - {border: "#C37F00", background: "#FFA807", highlight: {border: "#C37F00", background: "#FFCA66"}, hover: {border: "#C37F00", background: "#FFCA66"}}, // orange - {border: "#4220FB", background: "#6E6EFD", highlight: {border: "#4220FB", background: "#9B9BFD"}, hover: {border: "#4220FB", background: "#9B9BFD"}}, // darkblue - {border: "#FD5A77", background: "#FFC0CB", highlight: {border: "#FD5A77", background: "#FFD1D9"}, hover: {border: "#FD5A77", background: "#FFD1D9"}}, // pink - {border: "#4AD63A", background: "#C2FABC", highlight: {border: "#4AD63A", background: "#E6FFE3"}, hover: {border: "#4AD63A", background: "#E6FFE3"}} // mint - ]; - - /** - * Clear all groups - */ - Groups.prototype.clear = function () { - this.groups = {}; - this.groups.length = function() - { - var i = 0; - for ( var p in this ) { - if (this.hasOwnProperty(p)) { - i++; + // bind all keys to a call back (demo purposes) + _exportFunctions.bindAll = function(callback, type) { + if (type === undefined) { + type = 'keydown'; } - } - return i; - } - }; - - - /** - * get group properties of a groupname. If groupname is not found, a new group - * is added. - * @param {*} groupname Can be a number, string, Date, etc. - * @return {Object} group The created group, containing all group properties - */ - Groups.prototype.get = function (groupname) { - var group = this.groups[groupname]; - if (group == undefined) { - // create new group - var index = this.defaultIndex % Groups.DEFAULT.length; - this.defaultIndex++; - group = {}; - group.color = Groups.DEFAULT[index]; - this.groups[groupname] = group; - } - - return group; - }; - - /** - * Add a custom group style - * @param {String} groupname - * @param {Object} style An object containing borderColor, - * backgroundColor, etc. - * @return {Object} group The created group object - */ - Groups.prototype.add = function (groupname, style) { - this.groups[groupname] = style; - return style; - }; - - module.exports = Groups; - - -/***/ }, -/* 39 */ -/***/ function(module, exports, __webpack_require__) { - - /** - * @class Images - * This class loads images and keeps them stored. - */ - function Images() { - this.images = {}; - this.imageBroken = {}; - this.callback = undefined; - } - - /** - * Set an onload callback function. This will be called each time an image - * is loaded - * @param {function} callback - */ - Images.prototype.setOnloadCallback = function(callback) { - this.callback = callback; - }; - - /** - * - * @param {string} url Url of the image - * @param {string} url Url of an image to use if the url image is not found - * @return {Image} img The image object - */ - Images.prototype.load = function(url, brokenUrl) { - var img = this.images[url]; // make a pointer - if (img === undefined) { - // create the image - var me = this; - img = new Image(); - img.onload = function () { - // IE11 fix -- thanks dponch! - if (this.width == 0) { - document.body.appendChild(this); - this.width = this.offsetWidth; - this.height = this.offsetHeight; - document.body.removeChild(this); + for (var key in _keys) { + if (_keys.hasOwnProperty(key)) { + _exportFunctions.bind(key,callback,type); + } } + }; - if (me.callback) { - me.images[url] = img; - me.callback(this); + // 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"; }; - img.onerror = function () { - if (brokenUrl === undefined) { - console.error("Could not load image:", url); - delete this.src; - if (me.callback) { - me.callback(this); - } + // 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'; } - else { - if (me.imageBroken[url] === true) { - if (this.src == brokenUrl) { - console.error("Could not load brokenImage:", brokenUrl); - delete this.src; - if (me.callback) { - me.callback(this); + 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]); } } - else { - console.error("Could not load image:", url); - this.src = brokenUrl; - } - } - else { - console.error("Could not load image:", url); - this.src = brokenUrl; - me.imageBroken[url] = true; } + _bound[type][_keys[key].code] = newBindings; + } + else { + _bound[type][_keys[key].code] = []; } }; - img.src = url; + // reset all bound variables. + _exportFunctions.reset = function() { + _bound = {keydown:{}, keyup:{}}; + }; + + // unbind all listeners and reset all variables. + _exportFunctions.destroy = function() { + _bound = {keydown:{}, keyup:{}}; + container.removeEventListener('keydown', down, true); + container.removeEventListener('keyup', up, true); + }; + + // create listeners. + container.addEventListener('keydown',down,true); + container.addEventListener('keyup',up,true); + + // return the public functions. + return _exportFunctions; } - return img; - }; + return keycharm; + })); + - module.exports = Images; /***/ }, -/* 40 */ +/* 37 */ /***/ function(module, exports, __webpack_require__) { var util = __webpack_require__(1); + var Component = __webpack_require__(23); + var TimeStep = __webpack_require__(38); + var DateUtil = __webpack_require__(24); + var moment = __webpack_require__(2); /** - * @class Node - * A node. A node can be connected to other nodes via one or multiple edges. - * @param {object} properties An object containing properties for the node. All - * properties 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" - * {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 properties - * @param {Object} constants An object with default values for - * example for the color - * + * A horizontal time axis + * @param {{dom: Object, domProps: Object, emitter: Emitter, range: Range}} body + * @param {Object} [options] See TimeAxis.setOptions for the available + * options. + * @constructor TimeAxis + * @extends Component */ - function Node(properties, imagelist, grouplist, networkConstants) { - var constants = util.selectiveBridgeObject(['nodes'],networkConstants); - this.options = constants.nodes; - - this.selected = false; - this.hover = false; - - this.edges = []; // all edges connected to this node - this.dynamicEdges = []; - this.reroutedEdges = {}; - - this.fontDrawThreshold = 3; - - // set defaults for the properties - this.id = undefined; - this.allowedToMoveX = false; - this.allowedToMoveY = false; - this.xFixed = false; - this.yFixed = false; - this.horizontalAlignLeft = true; // these are for the navigation controls - this.verticalAlignTop = true; // these are for the navigation controls - this.baseRadiusValue = networkConstants.nodes.radius; - this.radiusFixed = false; - this.level = -1; - this.preassignedLevel = false; - this.hierarchyEnumerated = false; - this.labelDimensions = {top:0, left:0, width:0, height:0, yLine:0}; // could be cached - this.boundingBox = {top:0, left:0, right:0, bottom:0}; - - this.imagelist = imagelist; - this.grouplist = grouplist; - - // physics properties - this.fx = 0.0; // external force x - this.fy = 0.0; // external force y - this.vx = 0.0; // velocity x - this.vy = 0.0; // velocity y - this.x = null; - this.y = null; - - // used for reverting to previous position on stabilization - this.previousState = {vx:0,vy:0,x:0,y:0}; + function TimeAxis (body, options) { + this.dom = { + foreground: null, + lines: [], + majorTexts: [], + minorTexts: [], + redundant: { + lines: [], + majorTexts: [], + minorTexts: [] + } + }; + this.props = { + range: { + start: 0, + end: 0, + minimumStep: 0 + }, + lineTop: 0 + }; - this.damping = networkConstants.physics.damping; // written every time gravity is calculated - this.fixedData = {x:null,y:null}; + this.defaultOptions = { + orientation: 'bottom', // supported: 'top', 'bottom' + // TODO: implement timeaxis orientations 'left' and 'right' + showMinorLabels: true, + showMajorLabels: true, + format: null, + timeAxis: null + }; + this.options = util.extend({}, this.defaultOptions); - this.setProperties(properties, constants); + this.body = body; - // creating the variables for clustering - this.resetCluster(); - this.dynamicEdgesLength = 0; - this.clusterSession = 0; - this.clusterSizeWidthFactor = networkConstants.clustering.nodeScaling.width; - this.clusterSizeHeightFactor = networkConstants.clustering.nodeScaling.height; - this.clusterSizeRadiusFactor = networkConstants.clustering.nodeScaling.radius; - this.maxNodeSizeIncrements = networkConstants.clustering.maxNodeSizeIncrements; - this.growthIndicator = 0; + // create the HTML DOM + this._create(); - // variables to tell the node about the network. - this.networkScaleInv = 1; - this.networkScale = 1; - this.canvasTopLeft = {"x": -300, "y": -300}; - this.canvasBottomRight = {"x": 300, "y": 300}; - this.parentEdgeId = null; + this.setOptions(options); } + TimeAxis.prototype = new Component(); /** - * Revert the position and velocity of the previous step. + * Set options for the TimeAxis. + * Parameters will be merged in current options. + * @param {Object} options Available options: + * {string} [orientation] + * {boolean} [showMinorLabels] + * {boolean} [showMajorLabels] */ - Node.prototype.revertPosition = function() { - this.x = this.previousState.x; - this.y = this.previousState.y; - this.vx = this.previousState.vx; - this.vy = this.previousState.vy; - } + TimeAxis.prototype.setOptions = function(options) { + if (options) { + // copy all options that we know + util.selectiveExtend([ + 'orientation', + 'showMinorLabels', + 'showMajorLabels', + 'hiddenDates', + 'format', + 'timeAxis' + ], this.options, options); + // apply locale to moment.js + // TODO: not so nice, this is applied globally to moment.js + if ('locale' in options) { + if (typeof moment.locale === 'function') { + // moment.js 2.8.1+ + moment.locale(options.locale); + } + else { + moment.lang(options.locale); + } + } + } + }; /** - * (re)setting the clustering variables and objects + * Create the HTML DOM for the TimeAxis */ - Node.prototype.resetCluster = function() { - // clustering variables - this.formationScale = undefined; // this is used to determine when to open the cluster - this.clusterSize = 1; // this signifies the total amount of nodes in this cluster - this.containedNodes = {}; - this.containedEdges = {}; - this.clusterSessions = []; + TimeAxis.prototype._create = function() { + this.dom.foreground = document.createElement('div'); + this.dom.background = document.createElement('div'); + + this.dom.foreground.className = 'timeaxis foreground'; + this.dom.background.className = 'timeaxis background'; }; /** - * Attach a edge to the node - * @param {Edge} edge + * Destroy the TimeAxis */ - Node.prototype.attachEdge = function(edge) { - if (this.edges.indexOf(edge) == -1) { - this.edges.push(edge); + TimeAxis.prototype.destroy = function() { + // remove from DOM + if (this.dom.foreground.parentNode) { + this.dom.foreground.parentNode.removeChild(this.dom.foreground); } - if (this.dynamicEdges.indexOf(edge) == -1) { - this.dynamicEdges.push(edge); + if (this.dom.background.parentNode) { + this.dom.background.parentNode.removeChild(this.dom.background); } - this.dynamicEdgesLength = this.dynamicEdges.length; + + this.body = null; }; /** - * Detach a edge from the node - * @param {Edge} edge + * Repaint the component + * @return {boolean} Returns true if the component is resized */ - Node.prototype.detachEdge = function(edge) { - var index = this.edges.indexOf(edge); - if (index != -1) { - this.edges.splice(index, 1); - } - index = this.dynamicEdges.indexOf(edge); - if (index != -1) { - this.dynamicEdges.splice(index, 1); - } - this.dynamicEdgesLength = this.dynamicEdges.length; - }; + TimeAxis.prototype.redraw = function () { + var options = this.options; + var props = this.props; + var foreground = this.dom.foreground; + var background = this.dom.background; + // determine the correct parent DOM element (depending on option orientation) + var parent = (options.orientation == 'top') ? this.body.dom.top : this.body.dom.bottom; + var parentChanged = (foreground.parentNode !== parent); - /** - * Set or overwrite properties for the node - * @param {Object} properties an object with properties - * @param {Object} constants and object with default, global properties - */ - Node.prototype.setProperties = function(properties, constants) { - if (!properties) { - return; - } + // calculate character width and height + this._calculateCharSize(); - var fields = ['borderWidth','borderWidthSelected','shape','image','brokenImage','radius','fontColor', - 'fontSize','fontFace','fontFill','fontStrokeWidth','fontStrokeColor','group','mass' - ]; - util.selectiveDeepExtend(fields, this.options, properties); + // TODO: recalculate sizes only needed when parent is resized or options is changed + var orientation = this.options.orientation, + showMinorLabels = this.options.showMinorLabels, + showMajorLabels = this.options.showMajorLabels; - // basic properties - if (properties.id !== undefined) {this.id = properties.id;} - if (properties.label !== undefined) {this.label = properties.label; this.originalLabel = properties.label;} - if (properties.title !== undefined) {this.title = properties.title;} - if (properties.x !== undefined) {this.x = properties.x;} - if (properties.y !== undefined) {this.y = properties.y;} - if (properties.value !== undefined) {this.value = properties.value;} - if (properties.level !== undefined) {this.level = properties.level; this.preassignedLevel = true;} + // determine the width and height of the elemens for the axis + props.minorLabelHeight = showMinorLabels ? props.minorCharHeight : 0; + props.majorLabelHeight = showMajorLabels ? props.majorCharHeight : 0; + props.height = props.minorLabelHeight + props.majorLabelHeight; + props.width = foreground.offsetWidth; - // navigation controls properties - if (properties.horizontalAlignLeft !== undefined) {this.horizontalAlignLeft = properties.horizontalAlignLeft;} - if (properties.verticalAlignTop !== undefined) {this.verticalAlignTop = properties.verticalAlignTop;} - if (properties.triggerFunction !== undefined) {this.triggerFunction = properties.triggerFunction;} + props.minorLineHeight = this.body.domProps.root.height - props.majorLabelHeight - + (options.orientation == 'top' ? this.body.domProps.bottom.height : this.body.domProps.top.height); + props.minorLineWidth = 1; // TODO: really calculate width + props.majorLineHeight = props.minorLineHeight + props.majorLabelHeight; + props.majorLineWidth = 1; // TODO: really calculate width - if (this.id === undefined) { - throw "Node must have an id"; - } + // take foreground and background offline while updating (is almost twice as fast) + var foregroundNextSibling = foreground.nextSibling; + var backgroundNextSibling = background.nextSibling; + foreground.parentNode && foreground.parentNode.removeChild(foreground); + background.parentNode && background.parentNode.removeChild(background); - // copy group properties - if (typeof properties.group === 'number' || (typeof properties.group === 'string' && properties.group != '')) { - var groupObj = this.grouplist.get(properties.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); - } - // individual shape properties - if (properties.radius !== undefined) {this.baseRadiusValue = this.options.radius;} - if (properties.color !== undefined) {this.options.color = util.parseColor(properties.color);} + foreground.style.height = this.props.height + 'px'; - 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._repaintLabels(); - if (properties.allowedToMoveX !== undefined) { - this.xFixed = !properties.allowedToMoveX; - this.allowedToMoveX = properties.allowedToMoveX; + // put DOM online again (at the same place) + if (foregroundNextSibling) { + parent.insertBefore(foreground, foregroundNextSibling); } - else if (properties.x !== undefined && this.allowedToMoveX == false) { - this.xFixed = true; + else { + parent.appendChild(foreground) } - - - if (properties.allowedToMoveY !== undefined) { - this.yFixed = !properties.allowedToMoveY; - this.allowedToMoveY = properties.allowedToMoveY; + if (backgroundNextSibling) { + this.body.dom.backgroundVertical.insertBefore(background, backgroundNextSibling); } - else if (properties.y !== undefined && this.allowedToMoveY == false) { - this.yFixed = true; + else { + this.body.dom.backgroundVertical.appendChild(background) } - this.radiusFixed = this.radiusFixed || (properties.radius !== undefined); - - if (this.options.shape === 'image' || this.options.shape === 'circularImage') { - this.options.radiusMin = constants.nodes.widthMin; - this.options.radiusMax = constants.nodes.widthMax; - } - - // choose draw method depending on the shape - switch (this.options.shape) { - case 'database': this.draw = this._drawDatabase; this.resize = this._resizeDatabase; break; - case 'box': this.draw = this._drawBox; this.resize = this._resizeBox; break; - case 'circle': this.draw = this._drawCircle; this.resize = this._resizeCircle; break; - case 'ellipse': this.draw = this._drawEllipse; this.resize = this._resizeEllipse; break; - // TODO: add diamond shape - case 'image': this.draw = this._drawImage; this.resize = this._resizeImage; break; - case 'circularImage': this.draw = this._drawCircularImage; this.resize = this._resizeCircularImage; break; - case 'text': this.draw = this._drawText; this.resize = this._resizeText; break; - case 'dot': this.draw = this._drawDot; this.resize = this._resizeShape; break; - case 'square': this.draw = this._drawSquare; this.resize = this._resizeShape; break; - case 'triangle': this.draw = this._drawTriangle; this.resize = this._resizeShape; break; - case 'triangleDown': this.draw = this._drawTriangleDown; this.resize = this._resizeShape; break; - case 'star': this.draw = this._drawStar; this.resize = this._resizeShape; break; - default: this.draw = this._drawEllipse; this.resize = this._resizeEllipse; break; - } - // reset the size of the node, this can be changed - this._reset(); - - }; + return this._isResized() || parentChanged; + }; /** - * select this node + * Repaint major and minor text labels and vertical grid lines + * @private */ - Node.prototype.select = function() { - this.selected = true; - this._reset(); - }; + TimeAxis.prototype._repaintLabels = function () { + var orientation = this.options.orientation; - /** - * unselect this node - */ - Node.prototype.unselect = function() { - this.selected = false; - this._reset(); - }; + // calculate range and step (step such that we have space for 7 characters per label) + var start = util.convert(this.body.range.start, 'Number'); + var end = util.convert(this.body.range.end, 'Number'); + var timeLabelsize = this.body.util.toTime((this.props.minorCharWidth || 10) * 7).valueOf(); + var minimumStep = timeLabelsize - DateUtil.getHiddenDurationBefore(this.body.hiddenDates, this.body.range, timeLabelsize); + minimumStep -= this.body.util.toTime(0).valueOf(); + var step = new TimeStep(new Date(start), new Date(end), minimumStep, this.body.hiddenDates); + if (this.options.format) { + step.setFormat(this.options.format); + } + if (this.options.timeAxis) { + step.setScale(this.options.timeAxis); + } + this.step = step; - /** - * Reset the calculated size of the node, forces it to recalculate its size - */ - Node.prototype.clearSizeCache = function() { - this._reset(); - }; + // Move all DOM elements to a "redundant" list, where they + // can be picked for re-use, and clear the lists with lines and texts. + // At the end of the function _repaintLabels, left over elements will be cleaned up + var dom = this.dom; + dom.redundant.lines = dom.lines; + dom.redundant.majorTexts = dom.majorTexts; + dom.redundant.minorTexts = dom.minorTexts; + dom.lines = []; + dom.majorTexts = []; + dom.minorTexts = []; - /** - * Reset the calculated size of the node, forces it to recalculate its size - * @private - */ - Node.prototype._reset = function() { - this.width = undefined; - this.height = undefined; - }; + var cur; + var x = 0; + var isMajor; + var xPrev = 0; + var width = 0; + var prevLine; + var xFirstMajorLabel = undefined; + var max = 0; + var className; - /** - * get the title of this node. - * @return {string} title The title of the node, or undefined when no title - * has been set. - */ - Node.prototype.getTitle = function() { - return typeof this.title === "function" ? this.title() : this.title; - }; + step.first(); + while (step.hasNext() && max < 1000) { + max++; - /** - * 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 - */ - Node.prototype.distanceToBorder = function (ctx, angle) { - var borderWidth = 1; + cur = step.getCurrent(); + isMajor = step.isMajor(); + className = step.getClassName(); - if (!this.width) { - this.resize(ctx); - } + xPrev = x; + x = this.body.util.toScreen(cur); + width = x - xPrev; + if (prevLine) { + prevLine.style.width = width + 'px'; + } - switch (this.options.shape) { - case 'circle': - case 'dot': - return this.options.radius+ borderWidth; + if (this.options.showMinorLabels) { + this._repaintMinorText(x, step.getLabelMinor(), orientation, className); + } - case 'ellipse': - 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); + if (isMajor && this.options.showMajorLabels) { + if (x > 0) { + if (xFirstMajorLabel == undefined) { + xFirstMajorLabel = x; + } + this._repaintMajorText(x, step.getLabelMajor(), orientation, className); + } + prevLine = this._repaintMajorLine(x, orientation, className); + } + else { + prevLine = this._repaintMinorLine(x, orientation, className); + } - // TODO: implement distanceToBorder for database - // TODO: implement distanceToBorder for triangle - // TODO: implement distanceToBorder for triangleDown + step.next(); + } - case 'box': - case 'image': - case 'text': - default: - if (this.width) { - return Math.min( - Math.abs(this.width / 2 / Math.cos(angle)), - Math.abs(this.height / 2 / Math.sin(angle))) + borderWidth; - // TODO: reckon with border radius too in case of box - } - else { - return 0; - } + // create a major label on the left when needed + if (this.options.showMajorLabels) { + var leftTime = this.body.util.toTime(0), + leftText = step.getLabelMajor(leftTime), + widthText = leftText.length * (this.props.majorCharWidth || 10) + 10; // upper bound estimation + if (xFirstMajorLabel == undefined || widthText < xFirstMajorLabel) { + this._repaintMajorText(0, leftText, orientation, className); + } } - // TODO: implement calculation of distance to border for all shapes - }; - /** - * Set forces acting on the node - * @param {number} fx Force in horizontal direction - * @param {number} fy Force in vertical direction - */ - Node.prototype._setForce = function(fx, fy) { - this.fx = fx; - this.fy = fy; + // Cleanup leftover DOM elements from the redundant list + util.forEach(this.dom.redundant, function (arr) { + while (arr.length) { + var elem = arr.pop(); + if (elem && elem.parentNode) { + elem.parentNode.removeChild(elem); + } + } + }); }; /** - * Add forces acting on the node - * @param {number} fx Force in horizontal direction - * @param {number} fy Force in vertical direction + * Create a minor label for the axis at position x + * @param {Number} x + * @param {String} text + * @param {String} orientation "top" or "bottom" (default) + * @param {String} className * @private */ - Node.prototype._addForce = function(fx, fy) { - this.fx += fx; - this.fy += fy; + TimeAxis.prototype._repaintMinorText = function (x, text, orientation, className) { + // reuse redundant label + var label = this.dom.redundant.minorTexts.shift(); + + if (!label) { + // create new label + var content = document.createTextNode(''); + label = document.createElement('div'); + label.appendChild(content); + this.dom.foreground.appendChild(label); + } + this.dom.minorTexts.push(label); + + label.childNodes[0].nodeValue = text; + + label.style.top = (orientation == 'top') ? (this.props.majorLabelHeight + 'px') : '0'; + label.style.left = x + 'px'; + label.className = 'text minor ' + className; + //label.title = title; // TODO: this is a heavy operation }; /** - * Store the state before the next step + * Create a Major label for the axis at position x + * @param {Number} x + * @param {String} text + * @param {String} orientation "top" or "bottom" (default) + * @param {String} className + * @private */ - Node.prototype.storeState = function() { - this.previousState.x = this.x; - this.previousState.y = this.y; - this.previousState.vx = this.vx; - this.previousState.vy = this.vy; - } + TimeAxis.prototype._repaintMajorText = function (x, text, orientation, className) { + // reuse redundant label + var label = this.dom.redundant.majorTexts.shift(); + + if (!label) { + // create label + var content = document.createTextNode(text); + label = document.createElement('div'); + label.appendChild(content); + this.dom.foreground.appendChild(label); + } + this.dom.majorTexts.push(label); + + label.childNodes[0].nodeValue = text; + label.className = 'text major ' + className; + //label.title = title; // TODO: this is a heavy operation + + label.style.top = (orientation == 'top') ? '0' : (this.props.minorLabelHeight + 'px'); + label.style.left = x + 'px'; + }; /** - * Perform one discrete step for the node - * @param {number} interval Time interval in seconds + * Create a minor line for the axis at position x + * @param {Number} x + * @param {String} orientation "top" or "bottom" (default) + * @param {String} className + * @return {Element} Returns the created line + * @private */ - Node.prototype.discreteStep = function(interval) { - this.storeState(); - if (!this.xFixed) { - var dx = this.damping * this.vx; // damping force - var ax = (this.fx - dx) / this.options.mass; // acceleration - this.vx += ax * interval; // velocity - this.x += this.vx * interval; // position - } - else { - this.fx = 0; - this.vx = 0; + TimeAxis.prototype._repaintMinorLine = function (x, orientation, className) { + // reuse redundant line + var line = this.dom.redundant.lines.shift(); + if (!line) { + // create vertical line + line = document.createElement('div'); + this.dom.background.appendChild(line); } + this.dom.lines.push(line); - if (!this.yFixed) { - var dy = this.damping * this.vy; // damping force - var ay = (this.fy - dy) / this.options.mass; // acceleration - this.vy += ay * interval; // velocity - this.y += this.vy * interval; // position + var props = this.props; + if (orientation == 'top') { + line.style.top = props.majorLabelHeight + 'px'; } else { - this.fy = 0; - this.vy = 0; + line.style.top = this.body.domProps.top.height + 'px'; } - }; + line.style.height = props.minorLineHeight + 'px'; + line.style.left = (x - props.minorLineWidth / 2) + 'px'; + line.className = 'grid vertical minor ' + className; + return line; + }; /** - * Perform one discrete step for the node - * @param {number} interval Time interval in seconds - * @param {number} maxVelocity The speed limit imposed on the velocity + * Create a Major line for the axis at position x + * @param {Number} x + * @param {String} orientation "top" or "bottom" (default) + * @param {String} className + * @return {Element} Returns the created line + * @private */ - Node.prototype.discreteStepLimited = function(interval, maxVelocity) { - this.storeState(); - if (!this.xFixed) { - var dx = this.damping * this.vx; // damping force - var ax = (this.fx - dx) / this.options.mass; // acceleration - this.vx += ax * interval; // velocity - this.vx = (Math.abs(this.vx) > maxVelocity) ? ((this.vx > 0) ? maxVelocity : -maxVelocity) : this.vx; - this.x += this.vx * interval; // position - } - else { - this.fx = 0; - this.vx = 0; + TimeAxis.prototype._repaintMajorLine = function (x, orientation, className) { + // reuse redundant line + var line = this.dom.redundant.lines.shift(); + if (!line) { + // create vertical line + line = document.createElement('div'); + this.dom.background.appendChild(line); } + this.dom.lines.push(line); - if (!this.yFixed) { - var dy = this.damping * this.vy; // damping force - var ay = (this.fy - dy) / this.options.mass; // acceleration - this.vy += ay * interval; // velocity - this.vy = (Math.abs(this.vy) > maxVelocity) ? ((this.vy > 0) ? maxVelocity : -maxVelocity) : this.vy; - this.y += this.vy * interval; // position + var props = this.props; + if (orientation == 'top') { + line.style.top = '0'; } else { - this.fy = 0; - this.vy = 0; + line.style.top = this.body.domProps.top.height + 'px'; } - }; + line.style.left = (x - props.majorLineWidth / 2) + 'px'; + line.style.height = props.majorLineHeight + 'px'; - /** - * Check if this node has a fixed x and y position - * @return {boolean} true if fixed, false if not - */ - Node.prototype.isFixed = function() { - return (this.xFixed && this.yFixed); - }; + line.className = 'grid vertical major ' + className; - /** - * Check if this node is moving - * @param {number} vmin the minimum velocity considered as "moving" - * @return {boolean} true if moving, false if it has no velocity - */ - Node.prototype.isMoving = function(vmin) { - var velocity = Math.sqrt(Math.pow(this.vx,2) + Math.pow(this.vy,2)); - // this.velocity = Math.sqrt(Math.pow(this.vx,2) + Math.pow(this.vy,2)) - return (velocity > vmin); + return line; }; /** - * check if this node is selecte - * @return {boolean} selected True if node is selected, else false + * Determine the size of text on the axis (both major and minor axis). + * The size is calculated only once and then cached in this.props. + * @private */ - Node.prototype.isSelected = function() { - return this.selected; - }; + TimeAxis.prototype._calculateCharSize = function () { + // Note: We calculate char size with every redraw. Size may change, for + // example when any of the timelines parents had display:none for example. - /** - * Retrieve the value of the node. Can be undefined - * @return {Number} value - */ - Node.prototype.getValue = function() { - return this.value; + // determine the char width and height on the minor axis + if (!this.dom.measureCharMinor) { + this.dom.measureCharMinor = document.createElement('DIV'); + this.dom.measureCharMinor.className = 'text minor measure'; + this.dom.measureCharMinor.style.position = 'absolute'; + + this.dom.measureCharMinor.appendChild(document.createTextNode('0')); + this.dom.foreground.appendChild(this.dom.measureCharMinor); + } + this.props.minorCharHeight = this.dom.measureCharMinor.clientHeight; + this.props.minorCharWidth = this.dom.measureCharMinor.clientWidth; + + // determine the char width and height on the major axis + if (!this.dom.measureCharMajor) { + this.dom.measureCharMajor = document.createElement('DIV'); + this.dom.measureCharMajor.className = 'text major measure'; + this.dom.measureCharMajor.style.position = 'absolute'; + + this.dom.measureCharMajor.appendChild(document.createTextNode('0')); + this.dom.foreground.appendChild(this.dom.measureCharMajor); + } + this.props.majorCharHeight = this.dom.measureCharMajor.clientHeight; + this.props.majorCharWidth = this.dom.measureCharMajor.clientWidth; }; /** - * Calculate the distance from the nodes location to the given location (x,y) - * @param {Number} x - * @param {Number} y - * @return {Number} value + * Snap a date to a rounded value. + * The snap intervals are dependent on the current scale and step. + * @param {Date} date the date to be snapped. + * @return {Date} snappedDate */ - Node.prototype.getDistance = function(x, y) { - var dx = this.x - x, - dy = this.y - y; - return Math.sqrt(dx * dx + dy * dy); + TimeAxis.prototype.snap = function(date) { + return this.step.snap(date); }; + module.exports = TimeAxis; + + +/***/ }, +/* 38 */ +/***/ function(module, exports, __webpack_require__) { + + var moment = __webpack_require__(2); + var DateUtil = __webpack_require__(24); + var util = __webpack_require__(1); /** - * Adjust the value range of the node. The node will adjust it's radius - * based on its value. - * @param {Number} min - * @param {Number} max + * @constructor TimeStep + * The class TimeStep is an iterator for dates. You provide a start date and an + * end date. The class itself determines the best scale (step size) based on the + * provided start Date, end Date, and minimumStep. + * + * If minimumStep is provided, the step size is chosen as close as possible + * to the minimumStep but larger than minimumStep. If minimumStep is not + * provided, the scale is set to 1 DAY. + * The minimumStep should correspond with the onscreen size of about 6 characters + * + * Alternatively, you can set a scale by hand. + * After creation, you can initialize the class by executing first(). Then you + * can iterate from the start date to the end date via next(). You can check if + * the end date is reached with the function hasNext(). After each step, you can + * retrieve the current date via getCurrent(). + * The TimeStep has scales ranging from milliseconds, seconds, minutes, hours, + * days, to years. + * + * Version: 1.2 + * + * @param {Date} [start] The start date, for example new Date(2010, 9, 21) + * or new Date(2010, 9, 21, 23, 45, 00) + * @param {Date} [end] The end date + * @param {Number} [minimumStep] Optional. Minimum step size in milliseconds */ - Node.prototype.setValueRange = function(min, max) { - if (!this.radiusFixed && this.value !== undefined) { - if (max == min) { - this.options.radius= (this.options.radiusMin + this.options.radiusMax) / 2; - } - else { - var scale = (this.options.radiusMax - this.options.radiusMin) / (max - min); - this.options.radius= (this.value - min) * scale + this.options.radiusMin; - } + function TimeStep(start, end, minimumStep, hiddenDates) { + // variables + this.current = new Date(); + this._start = new Date(); + this._end = new Date(); + + this.autoScale = true; + this.scale = 'day'; + this.step = 1; + + // initialize the range + this.setRange(start, end, minimumStep); + + // hidden Dates options + this.switchedDay = false; + this.switchedMonth = false; + this.switchedYear = false; + this.hiddenDates = hiddenDates; + if (hiddenDates === undefined) { + this.hiddenDates = []; + } + + this.format = TimeStep.FORMAT; // default formatting + } + + // Time formatting + TimeStep.FORMAT = { + minorLabels: { + millisecond:'SSS', + second: 's', + minute: 'HH:mm', + hour: 'HH:mm', + weekday: 'ddd D', + day: 'D', + month: 'MMM', + year: 'YYYY' + }, + majorLabels: { + millisecond:'HH:mm:ss', + second: 'D MMMM HH:mm', + minute: 'ddd D MMMM', + hour: 'ddd D MMMM', + weekday: 'MMMM YYYY', + day: 'MMMM YYYY', + month: 'YYYY', + year: '' } - this.baseRadiusValue = this.options.radius; }; /** - * Draw this node in the given canvas - * The 2d context of a HTML canvas can be retrieved by canvas.getContext("2d"); - * @param {CanvasRenderingContext2D} ctx + * Set custom formatting for the minor an major labels of the TimeStep. + * Both `minorLabels` and `majorLabels` are an Object with properties: + * 'millisecond, 'second, 'minute', 'hour', 'weekday, 'day, 'month, 'year'. + * @param {{minorLabels: Object, majorLabels: Object}} format */ - Node.prototype.draw = function(ctx) { - throw "Draw method not initialized for node"; + TimeStep.prototype.setFormat = function (format) { + var defaultFormat = util.deepExtend({}, TimeStep.FORMAT); + this.format = util.deepExtend(defaultFormat, format); }; /** - * 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 + * Set a new range + * If minimumStep is provided, the step size is chosen as close as possible + * to the minimumStep but larger than minimumStep. If minimumStep is not + * provided, the scale is set to 1 DAY. + * The minimumStep should correspond with the onscreen size of about 6 characters + * @param {Date} [start] The start date and time. + * @param {Date} [end] The end date and time. + * @param {int} [minimumStep] Optional. Minimum step size in milliseconds */ - Node.prototype.resize = function(ctx) { - throw "Resize method not initialized for node"; + TimeStep.prototype.setRange = function(start, end, minimumStep) { + if (!(start instanceof Date) || !(end instanceof Date)) { + throw "No legal start or end date in method setRange"; + } + + this._start = (start != undefined) ? new Date(start.valueOf()) : new Date(); + this._end = (end != undefined) ? new Date(end.valueOf()) : new Date(); + + if (this.autoScale) { + this.setMinimumStep(minimumStep); + } }; /** - * 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 + * Set the range iterator to the start date. */ - Node.prototype.isOverlappingWith = function(obj) { - return (this.left < obj.right && - this.left + this.width > obj.left && - this.top < obj.bottom && - this.top + this.height > obj.top); + TimeStep.prototype.first = function() { + this.current = new Date(this._start.valueOf()); + this.roundToMinor(); }; - Node.prototype._resizeImage = function (ctx) { - // TODO: pre calculate the image size - - if (!this.width || !this.height) { // undefined or 0 - var width, height; - if (this.value) { - this.options.radius= this.baseRadiusValue; - var scale = this.imageObj.height / this.imageObj.width; - if (scale !== undefined) { - width = this.options.radius|| this.imageObj.width; - height = this.options.radius* scale || this.imageObj.height; - } - else { - width = 0; - height = 0; - } - } - else { - width = this.imageObj.width; - height = this.imageObj.height; - } - this.width = width; - this.height = height; - - this.growthIndicator = 0; - if (this.width > 0 && this.height > 0) { - this.width += Math.min(this.clusterSize - 1, this.maxNodeSizeIncrements) * this.clusterSizeWidthFactor; - this.height += Math.min(this.clusterSize - 1, this.maxNodeSizeIncrements) * this.clusterSizeHeightFactor; - this.options.radius+= Math.min(this.clusterSize - 1, this.maxNodeSizeIncrements) * this.clusterSizeRadiusFactor; - this.growthIndicator = this.width - width; - } + /** + * Round the current date to the first minor date value + * This must be executed once when the current date is set to start Date + */ + TimeStep.prototype.roundToMinor = function() { + // round to floor + // IMPORTANT: we have no breaks in this switch! (this is no bug) + // noinspection FallThroughInSwitchStatementJS + switch (this.scale) { + case 'year': + this.current.setFullYear(this.step * Math.floor(this.current.getFullYear() / this.step)); + this.current.setMonth(0); + case 'month': this.current.setDate(1); + case 'day': // intentional fall through + case 'weekday': this.current.setHours(0); + case 'hour': this.current.setMinutes(0); + case 'minute': this.current.setSeconds(0); + case 'second': this.current.setMilliseconds(0); + //case 'millisecond': // nothing to do for milliseconds } - }; - - Node.prototype._drawImageAtPosition = function (ctx) { - if (this.imageObj.width != 0 ) { - // draw the shade - if (this.clusterSize > 1) { - var lineWidth = ((this.clusterSize > 1) ? 10 : 0.0); - lineWidth *= this.networkScaleInv; - lineWidth = Math.min(0.2 * this.width,lineWidth); - ctx.globalAlpha = 0.5; - ctx.drawImage(this.imageObj, this.left - lineWidth, this.top - lineWidth, this.width + 2*lineWidth, this.height + 2*lineWidth); + if (this.step != 1) { + // round down to the first minor value that is a multiple of the current step size + switch (this.scale) { + case 'millisecond': this.current.setMilliseconds(this.current.getMilliseconds() - this.current.getMilliseconds() % this.step); break; + case 'second': this.current.setSeconds(this.current.getSeconds() - this.current.getSeconds() % this.step); break; + case 'minute': this.current.setMinutes(this.current.getMinutes() - this.current.getMinutes() % this.step); break; + case 'hour': this.current.setHours(this.current.getHours() - this.current.getHours() % this.step); break; + case 'weekday': // intentional fall through + case 'day': this.current.setDate((this.current.getDate()-1) - (this.current.getDate()-1) % this.step + 1); break; + case 'month': this.current.setMonth(this.current.getMonth() - this.current.getMonth() % this.step); break; + case 'year': this.current.setFullYear(this.current.getFullYear() - this.current.getFullYear() % this.step); break; + default: break; } - - // draw the image - ctx.globalAlpha = 1.0; - ctx.drawImage(this.imageObj, this.left, this.top, this.width, this.height); } }; - Node.prototype._drawImageLabel = function (ctx) { - var yLabel; - var offset = 0; - - if (this.height){ - offset = this.height / 2; - var labelDimensions = this.getTextSize(ctx); - - if (labelDimensions.lineCount >= 1){ - offset += labelDimensions.height / 2; - offset += 3; - } - } - - yLabel = this.y + offset; - - this._label(ctx, this.label, this.x, yLabel, undefined); + /** + * Check if the there is a next step + * @return {boolean} true if the current date has not passed the end date + */ + TimeStep.prototype.hasNext = function () { + return (this.current.valueOf() <= this._end.valueOf()); }; - Node.prototype._drawImage = function (ctx) { - this._resizeImage(ctx); - this.left = this.x - this.width / 2; - this.top = this.y - this.height / 2; - - this._drawImageAtPosition(ctx); - - this.boundingBox.top = this.top; - this.boundingBox.left = this.left; - this.boundingBox.right = this.left + this.width; - this.boundingBox.bottom = this.top + this.height; - - this._drawImageLabel(ctx); - this.boundingBox.left = Math.min(this.boundingBox.left, this.labelDimensions.left); - this.boundingBox.right = Math.max(this.boundingBox.right, this.labelDimensions.left + this.labelDimensions.width); - this.boundingBox.bottom = Math.max(this.boundingBox.bottom, this.boundingBox.bottom + this.labelDimensions.height); - }; + /** + * Do the next step + */ + TimeStep.prototype.next = function() { + var prev = this.current.valueOf(); - Node.prototype._resizeCircularImage = function (ctx) { - if(!this.imageObj.src || !this.imageObj.width || !this.imageObj.height){ - if (!this.width) { - var diameter = this.options.radius * 2; - this.width = diameter; - this.height = diameter; + // Two cases, needed to prevent issues with switching daylight savings + // (end of March and end of October) + if (this.current.getMonth() < 6) { + switch (this.scale) { + case 'millisecond': - // scaling used for clustering - //this.width += Math.min(this.clusterSize - 1, this.maxNodeSizeIncrements) * 0.5 * this.clusterSizeWidthFactor; - //this.height += Math.min(this.clusterSize - 1, this.maxNodeSizeIncrements) * 0.5 * this.clusterSizeHeightFactor; - this.options.radius += Math.min(this.clusterSize - 1, this.maxNodeSizeIncrements) * 0.5 * this.clusterSizeRadiusFactor; - this.growthIndicator = this.options.radius- 0.5*diameter; - this._swapToImageResizeWhenImageLoaded = true; + this.current = new Date(this.current.valueOf() + this.step); break; + case 'second': this.current = new Date(this.current.valueOf() + this.step * 1000); break; + case 'minute': this.current = new Date(this.current.valueOf() + this.step * 1000 * 60); break; + case 'hour': + this.current = new Date(this.current.valueOf() + this.step * 1000 * 60 * 60); + // in case of skipping an hour for daylight savings, adjust the hour again (else you get: 0h 5h 9h ... instead of 0h 4h 8h ...) + var h = this.current.getHours(); + this.current.setHours(h - (h % this.step)); + break; + case 'weekday': // intentional fall through + case 'day': this.current.setDate(this.current.getDate() + this.step); break; + case 'month': this.current.setMonth(this.current.getMonth() + this.step); break; + case 'year': this.current.setFullYear(this.current.getFullYear() + this.step); break; + default: break; } } else { - if (this._swapToImageResizeWhenImageLoaded) { - this.width = 0; - this.height = 0; - delete this._swapToImageResizeWhenImageLoaded; + switch (this.scale) { + case 'millisecond': this.current = new Date(this.current.valueOf() + this.step); break; + case 'second': this.current.setSeconds(this.current.getSeconds() + this.step); break; + case 'minute': this.current.setMinutes(this.current.getMinutes() + this.step); break; + case 'hour': this.current.setHours(this.current.getHours() + this.step); break; + case 'weekday': // intentional fall through + case 'day': this.current.setDate(this.current.getDate() + this.step); break; + case 'month': this.current.setMonth(this.current.getMonth() + this.step); break; + case 'year': this.current.setFullYear(this.current.getFullYear() + this.step); break; + default: break; } - this._resizeImage(ctx); } - }; - - Node.prototype._drawCircularImage = function (ctx) { - this._resizeCircularImage(ctx); - - this.left = this.x - this.width / 2; - this.top = this.y - this.height / 2; - - var centerX = this.left + (this.width / 2); - var centerY = this.top + (this.height / 2); - var radius = Math.abs(this.height / 2); - - this._drawRawCircle(ctx, centerX, centerY, radius); - - ctx.save(); - ctx.circle(this.x, this.y, radius); - ctx.stroke(); - ctx.clip(); - - this._drawImageAtPosition(ctx); - - ctx.restore(); - - this.boundingBox.top = this.y - this.options.radius; - this.boundingBox.left = this.x - this.options.radius; - this.boundingBox.right = this.x + this.options.radius; - this.boundingBox.bottom = this.y + this.options.radius; - - this._drawImageLabel(ctx); - - this.boundingBox.left = Math.min(this.boundingBox.left, this.labelDimensions.left); - this.boundingBox.right = Math.max(this.boundingBox.right, this.labelDimensions.left + this.labelDimensions.width); - this.boundingBox.bottom = Math.max(this.boundingBox.bottom, this.boundingBox.bottom + this.labelDimensions.height); - }; - - Node.prototype._resizeBox = function (ctx) { - if (!this.width) { - var margin = 5; - var textSize = this.getTextSize(ctx); - this.width = textSize.width + 2 * margin; - this.height = textSize.height + 2 * margin; - - this.width += Math.min(this.clusterSize - 1, this.maxNodeSizeIncrements) * 0.5 * this.clusterSizeWidthFactor; - this.height += Math.min(this.clusterSize - 1, this.maxNodeSizeIncrements) * 0.5 * this.clusterSizeHeightFactor; - this.growthIndicator = this.width - (textSize.width + 2 * margin); - // this.options.radius+= Math.min(this.clusterSize - 1, this.maxNodeSizeIncrements) * 0.5 * this.clusterSizeRadiusFactor; - + if (this.step != 1) { + // round down to the correct major value + switch (this.scale) { + case 'millisecond': if(this.current.getMilliseconds() < this.step) this.current.setMilliseconds(0); break; + case 'second': if(this.current.getSeconds() < this.step) this.current.setSeconds(0); break; + case 'minute': if(this.current.getMinutes() < this.step) this.current.setMinutes(0); break; + case 'hour': if(this.current.getHours() < this.step) this.current.setHours(0); break; + case 'weekday': // intentional fall through + case 'day': if(this.current.getDate() < this.step+1) this.current.setDate(1); break; + case 'month': if(this.current.getMonth() < this.step) this.current.setMonth(0); break; + case 'year': break; // nothing to do for year + default: break; + } } - }; - - Node.prototype._drawBox = function (ctx) { - this._resizeBox(ctx); - - this.left = this.x - this.width / 2; - this.top = this.y - this.height / 2; - - var clusterLineWidth = 2.5; - var borderWidth = this.options.borderWidth; - var selectionLineWidth = this.options.borderWidthSelected || 2 * this.options.borderWidth; - - ctx.strokeStyle = this.selected ? this.options.color.highlight.border : this.hover ? this.options.color.hover.border : this.options.color.border; - - // draw the outer border - if (this.clusterSize > 1) { - ctx.lineWidth = (this.selected ? selectionLineWidth : borderWidth) + ((this.clusterSize > 1) ? clusterLineWidth : 0.0); - ctx.lineWidth *= this.networkScaleInv; - ctx.lineWidth = Math.min(this.width,ctx.lineWidth); - ctx.roundRect(this.left-2*ctx.lineWidth, this.top-2*ctx.lineWidth, this.width+4*ctx.lineWidth, this.height+4*ctx.lineWidth, this.options.radius); - ctx.stroke(); + // safety mechanism: if current time is still unchanged, move to the end + if (this.current.valueOf() == prev) { + this.current = new Date(this._end.valueOf()); } - ctx.lineWidth = (this.selected ? selectionLineWidth : borderWidth) + ((this.clusterSize > 1) ? clusterLineWidth : 0.0); - ctx.lineWidth *= this.networkScaleInv; - ctx.lineWidth = Math.min(this.width,ctx.lineWidth); - - ctx.fillStyle = this.selected ? this.options.color.highlight.background : this.hover ? this.options.color.hover.background : this.options.color.background; - - ctx.roundRect(this.left, this.top, this.width, this.height, this.options.radius); - ctx.fill(); - ctx.stroke(); - - this.boundingBox.top = this.top; - this.boundingBox.left = this.left; - this.boundingBox.right = this.left + this.width; - this.boundingBox.bottom = this.top + this.height; - this._label(ctx, this.label, this.x, this.y); + DateUtil.stepOverHiddenDates(this, prev); }; - Node.prototype._resizeDatabase = function (ctx) { - if (!this.width) { - var margin = 5; - var textSize = this.getTextSize(ctx); - var size = textSize.width + 2 * margin; - this.width = size; - this.height = size; - - // scaling used for clustering - this.width += Math.min(this.clusterSize - 1, this.maxNodeSizeIncrements) * this.clusterSizeWidthFactor; - this.height += Math.min(this.clusterSize - 1, this.maxNodeSizeIncrements) * this.clusterSizeHeightFactor; - this.options.radius+= Math.min(this.clusterSize - 1, this.maxNodeSizeIncrements) * this.clusterSizeRadiusFactor; - this.growthIndicator = this.width - size; - } + /** + * Get the current datetime + * @return {Date} current The current date + */ + TimeStep.prototype.getCurrent = function() { + return this.current; }; - Node.prototype._drawDatabase = function (ctx) { - this._resizeDatabase(ctx); - this.left = this.x - this.width / 2; - this.top = this.y - this.height / 2; - - var clusterLineWidth = 2.5; - var borderWidth = this.options.borderWidth; - var selectionLineWidth = this.options.borderWidthSelected || 2 * this.options.borderWidth; - - ctx.strokeStyle = this.selected ? this.options.color.highlight.border : this.hover ? this.options.color.hover.border : this.options.color.border; - - // draw the outer border - if (this.clusterSize > 1) { - ctx.lineWidth = (this.selected ? selectionLineWidth : borderWidth) + ((this.clusterSize > 1) ? clusterLineWidth : 0.0); - ctx.lineWidth *= this.networkScaleInv; - ctx.lineWidth = Math.min(this.width,ctx.lineWidth); - - ctx.database(this.x - this.width/2 - 2*ctx.lineWidth, this.y - this.height*0.5 - 2*ctx.lineWidth, this.width + 4*ctx.lineWidth, this.height + 4*ctx.lineWidth); - ctx.stroke(); + /** + * Set a custom scale. Autoscaling will be disabled. + * For example setScale('minute', 5) will result + * in minor steps of 5 minutes, and major steps of an hour. + * + * @param {{scale: string, step: number}} params + * An object containing two properties: + * - A string 'scale'. Choose from 'millisecond', 'second', + * 'minute', 'hour', 'weekday, 'day, 'month, 'year'. + * - A number 'step'. A step size, by default 1. + * Choose for example 1, 2, 5, or 10. + */ + TimeStep.prototype.setScale = function(params) { + if (params && typeof params.scale == 'string') { + this.scale = params.scale; + this.step = params.step > 0 ? params.step : 1; + this.autoScale = false; } - ctx.lineWidth = (this.selected ? selectionLineWidth : borderWidth) + ((this.clusterSize > 1) ? clusterLineWidth : 0.0); - ctx.lineWidth *= this.networkScaleInv; - ctx.lineWidth = Math.min(this.width,ctx.lineWidth); - - ctx.fillStyle = this.selected ? this.options.color.highlight.background : this.hover ? this.options.color.hover.background : this.options.color.background; - ctx.database(this.x - this.width/2, this.y - this.height*0.5, this.width, this.height); - ctx.fill(); - ctx.stroke(); - - this.boundingBox.top = this.top; - this.boundingBox.left = this.left; - this.boundingBox.right = this.left + this.width; - this.boundingBox.bottom = this.top + this.height; - - this._label(ctx, this.label, this.x, this.y); }; - - Node.prototype._resizeCircle = function (ctx) { - if (!this.width) { - var margin = 5; - var textSize = this.getTextSize(ctx); - var diameter = Math.max(textSize.width, textSize.height) + 2 * margin; - this.options.radius = diameter / 2; - - this.width = diameter; - this.height = diameter; - - // scaling used for clustering - // this.width += Math.min(this.clusterSize - 1, this.maxNodeSizeIncrements) * 0.5 * this.clusterSizeWidthFactor; - // this.height += Math.min(this.clusterSize - 1, this.maxNodeSizeIncrements) * 0.5 * this.clusterSizeHeightFactor; - this.options.radius += Math.min(this.clusterSize - 1, this.maxNodeSizeIncrements) * 0.5 * this.clusterSizeRadiusFactor; - this.growthIndicator = this.options.radius- 0.5*diameter; - } + /** + * Enable or disable autoscaling + * @param {boolean} enable If true, autoascaling is set true + */ + TimeStep.prototype.setAutoScale = function (enable) { + this.autoScale = enable; }; - Node.prototype._drawRawCircle = function (ctx, x, y, radius) { - var clusterLineWidth = 2.5; - var borderWidth = this.options.borderWidth; - var selectionLineWidth = this.options.borderWidthSelected || 2 * this.options.borderWidth; - - ctx.strokeStyle = this.selected ? this.options.color.highlight.border : this.hover ? this.options.color.hover.border : this.options.color.border; - - // draw the outer border - if (this.clusterSize > 1) { - ctx.lineWidth = (this.selected ? selectionLineWidth : borderWidth) + ((this.clusterSize > 1) ? clusterLineWidth : 0.0); - ctx.lineWidth *= this.networkScaleInv; - ctx.lineWidth = Math.min(this.width,ctx.lineWidth); - ctx.circle(x, y, radius+2*ctx.lineWidth); - ctx.stroke(); + /** + * Automatically determine the scale that bests fits the provided minimum step + * @param {Number} [minimumStep] The minimum step size in milliseconds + */ + TimeStep.prototype.setMinimumStep = function(minimumStep) { + if (minimumStep == undefined) { + return; } - ctx.lineWidth = (this.selected ? selectionLineWidth : borderWidth) + ((this.clusterSize > 1) ? clusterLineWidth : 0.0); - ctx.lineWidth *= this.networkScaleInv; - ctx.lineWidth = Math.min(this.width,ctx.lineWidth); - - ctx.fillStyle = this.selected ? this.options.color.highlight.background : this.hover ? this.options.color.hover.background : this.options.color.background; - ctx.circle(this.x, this.y, radius); - ctx.fill(); - ctx.stroke(); - }; - - Node.prototype._drawCircle = function (ctx) { - this._resizeCircle(ctx); - this.left = this.x - this.width / 2; - this.top = this.y - this.height / 2; - this._drawRawCircle(ctx, this.x, this.y, this.options.radius); + //var b = asc + ds; - this.boundingBox.top = this.y - this.options.radius; - this.boundingBox.left = this.x - this.options.radius; - this.boundingBox.right = this.x + this.options.radius; - this.boundingBox.bottom = this.y + this.options.radius; + var stepYear = (1000 * 60 * 60 * 24 * 30 * 12); + var stepMonth = (1000 * 60 * 60 * 24 * 30); + var stepDay = (1000 * 60 * 60 * 24); + var stepHour = (1000 * 60 * 60); + var stepMinute = (1000 * 60); + var stepSecond = (1000); + var stepMillisecond= (1); - this._label(ctx, this.label, this.x, this.y); + // find the smallest step that is larger than the provided minimumStep + if (stepYear*1000 > minimumStep) {this.scale = 'year'; this.step = 1000;} + if (stepYear*500 > minimumStep) {this.scale = 'year'; this.step = 500;} + if (stepYear*100 > minimumStep) {this.scale = 'year'; this.step = 100;} + if (stepYear*50 > minimumStep) {this.scale = 'year'; this.step = 50;} + if (stepYear*10 > minimumStep) {this.scale = 'year'; this.step = 10;} + if (stepYear*5 > minimumStep) {this.scale = 'year'; this.step = 5;} + if (stepYear > minimumStep) {this.scale = 'year'; this.step = 1;} + if (stepMonth*3 > minimumStep) {this.scale = 'month'; this.step = 3;} + if (stepMonth > minimumStep) {this.scale = 'month'; this.step = 1;} + if (stepDay*5 > minimumStep) {this.scale = 'day'; this.step = 5;} + if (stepDay*2 > minimumStep) {this.scale = 'day'; this.step = 2;} + if (stepDay > minimumStep) {this.scale = 'day'; this.step = 1;} + if (stepDay/2 > minimumStep) {this.scale = 'weekday'; this.step = 1;} + if (stepHour*4 > minimumStep) {this.scale = 'hour'; this.step = 4;} + if (stepHour > minimumStep) {this.scale = 'hour'; this.step = 1;} + if (stepMinute*15 > minimumStep) {this.scale = 'minute'; this.step = 15;} + if (stepMinute*10 > minimumStep) {this.scale = 'minute'; this.step = 10;} + if (stepMinute*5 > minimumStep) {this.scale = 'minute'; this.step = 5;} + if (stepMinute > minimumStep) {this.scale = 'minute'; this.step = 1;} + if (stepSecond*15 > minimumStep) {this.scale = 'second'; this.step = 15;} + if (stepSecond*10 > minimumStep) {this.scale = 'second'; this.step = 10;} + if (stepSecond*5 > minimumStep) {this.scale = 'second'; this.step = 5;} + if (stepSecond > minimumStep) {this.scale = 'second'; this.step = 1;} + if (stepMillisecond*200 > minimumStep) {this.scale = 'millisecond'; this.step = 200;} + if (stepMillisecond*100 > minimumStep) {this.scale = 'millisecond'; this.step = 100;} + if (stepMillisecond*50 > minimumStep) {this.scale = 'millisecond'; this.step = 50;} + if (stepMillisecond*10 > minimumStep) {this.scale = 'millisecond'; this.step = 10;} + if (stepMillisecond*5 > minimumStep) {this.scale = 'millisecond'; this.step = 5;} + if (stepMillisecond > minimumStep) {this.scale = 'millisecond'; this.step = 1;} }; - Node.prototype._resizeEllipse = function (ctx) { - if (!this.width) { - var textSize = this.getTextSize(ctx); + /** + * Snap a date to a rounded value. + * The snap intervals are dependent on the current scale and step. + * @param {Date} date the date to be snapped. + * @return {Date} snappedDate + */ + TimeStep.prototype.snap = function(date) { + var clone = new Date(date.valueOf()); - this.width = textSize.width * 1.5; - this.height = textSize.height * 2; - if (this.width < this.height) { - this.width = this.height; + if (this.scale == 'year') { + var year = clone.getFullYear() + Math.round(clone.getMonth() / 12); + clone.setFullYear(Math.round(year / this.step) * this.step); + clone.setMonth(0); + clone.setDate(0); + clone.setHours(0); + clone.setMinutes(0); + clone.setSeconds(0); + clone.setMilliseconds(0); + } + else if (this.scale == 'month') { + if (clone.getDate() > 15) { + clone.setDate(1); + clone.setMonth(clone.getMonth() + 1); + // important: first set Date to 1, after that change the month. + } + else { + clone.setDate(1); } - var defaultSize = this.width; - // scaling used for clustering - this.width += Math.min(this.clusterSize - 1, this.maxNodeSizeIncrements) * this.clusterSizeWidthFactor; - this.height += Math.min(this.clusterSize - 1, this.maxNodeSizeIncrements) * this.clusterSizeHeightFactor; - this.options.radius += Math.min(this.clusterSize - 1, this.maxNodeSizeIncrements) * this.clusterSizeRadiusFactor; - this.growthIndicator = this.width - defaultSize; + clone.setHours(0); + clone.setMinutes(0); + clone.setSeconds(0); + clone.setMilliseconds(0); + } + else if (this.scale == 'day') { + //noinspection FallthroughInSwitchStatementJS + switch (this.step) { + case 5: + case 2: + clone.setHours(Math.round(clone.getHours() / 24) * 24); break; + default: + clone.setHours(Math.round(clone.getHours() / 12) * 12); break; + } + clone.setMinutes(0); + clone.setSeconds(0); + clone.setMilliseconds(0); + } + else if (this.scale == 'weekday') { + //noinspection FallthroughInSwitchStatementJS + switch (this.step) { + case 5: + case 2: + clone.setHours(Math.round(clone.getHours() / 12) * 12); break; + default: + clone.setHours(Math.round(clone.getHours() / 6) * 6); break; + } + clone.setMinutes(0); + clone.setSeconds(0); + clone.setMilliseconds(0); + } + else if (this.scale == 'hour') { + switch (this.step) { + case 4: + clone.setMinutes(Math.round(clone.getMinutes() / 60) * 60); break; + default: + clone.setMinutes(Math.round(clone.getMinutes() / 30) * 30); break; + } + clone.setSeconds(0); + clone.setMilliseconds(0); + } else if (this.scale == 'minute') { + //noinspection FallthroughInSwitchStatementJS + switch (this.step) { + case 15: + case 10: + clone.setMinutes(Math.round(clone.getMinutes() / 5) * 5); + clone.setSeconds(0); + break; + case 5: + clone.setSeconds(Math.round(clone.getSeconds() / 60) * 60); break; + default: + clone.setSeconds(Math.round(clone.getSeconds() / 30) * 30); break; + } + clone.setMilliseconds(0); + } + else if (this.scale == 'second') { + //noinspection FallthroughInSwitchStatementJS + switch (this.step) { + case 15: + case 10: + clone.setSeconds(Math.round(clone.getSeconds() / 5) * 5); + clone.setMilliseconds(0); + break; + case 5: + clone.setMilliseconds(Math.round(clone.getMilliseconds() / 1000) * 1000); break; + default: + clone.setMilliseconds(Math.round(clone.getMilliseconds() / 500) * 500); break; + } + } + else if (this.scale == 'millisecond') { + var step = this.step > 5 ? this.step / 2 : 1; + clone.setMilliseconds(Math.round(clone.getMilliseconds() / step) * step); } + + return clone; }; - Node.prototype._drawEllipse = function (ctx) { - this._resizeEllipse(ctx); - this.left = this.x - this.width / 2; - this.top = this.y - this.height / 2; - - var clusterLineWidth = 2.5; - var borderWidth = this.options.borderWidth; - var selectionLineWidth = this.options.borderWidthSelected || 2 * this.options.borderWidth; - - ctx.strokeStyle = this.selected ? this.options.color.highlight.border : this.hover ? this.options.color.hover.border : this.options.color.border; - - // draw the outer border - if (this.clusterSize > 1) { - ctx.lineWidth = (this.selected ? selectionLineWidth : borderWidth) + ((this.clusterSize > 1) ? clusterLineWidth : 0.0); - ctx.lineWidth *= this.networkScaleInv; - ctx.lineWidth = Math.min(this.width,ctx.lineWidth); - - ctx.ellipse(this.left-2*ctx.lineWidth, this.top-2*ctx.lineWidth, this.width+4*ctx.lineWidth, this.height+4*ctx.lineWidth); - ctx.stroke(); + /** + * Check if the current value is a major value (for example when the step + * is DAY, a major value is each first day of the MONTH) + * @return {boolean} true if current date is major, else false. + */ + TimeStep.prototype.isMajor = function() { + if (this.switchedYear == true) { + this.switchedYear = false; + switch (this.scale) { + case 'year': + case 'month': + case 'weekday': + case 'day': + case 'hour': + case 'minute': + case 'second': + case 'millisecond': + return true; + default: + return false; + } + } + else if (this.switchedMonth == true) { + this.switchedMonth = false; + switch (this.scale) { + case 'weekday': + case 'day': + case 'hour': + case 'minute': + case 'second': + case 'millisecond': + return true; + default: + return false; + } + } + else if (this.switchedDay == true) { + this.switchedDay = false; + switch (this.scale) { + case 'millisecond': + case 'second': + case 'minute': + case 'hour': + return true; + default: + return false; + } } - ctx.lineWidth = (this.selected ? selectionLineWidth : borderWidth) + ((this.clusterSize > 1) ? clusterLineWidth : 0.0); - ctx.lineWidth *= this.networkScaleInv; - ctx.lineWidth = Math.min(this.width,ctx.lineWidth); - - ctx.fillStyle = this.selected ? this.options.color.highlight.background : this.hover ? this.options.color.hover.background : this.options.color.background; - - ctx.ellipse(this.left, this.top, this.width, this.height); - ctx.fill(); - ctx.stroke(); - - this.boundingBox.top = this.top; - this.boundingBox.left = this.left; - this.boundingBox.right = this.left + this.width; - this.boundingBox.bottom = this.top + this.height; - this._label(ctx, this.label, this.x, this.y); + switch (this.scale) { + case 'millisecond': + return (this.current.getMilliseconds() == 0); + case 'second': + return (this.current.getSeconds() == 0); + case 'minute': + return (this.current.getHours() == 0) && (this.current.getMinutes() == 0); + case 'hour': + return (this.current.getHours() == 0); + case 'weekday': // intentional fall through + case 'day': + return (this.current.getDate() == 1); + case 'month': + return (this.current.getMonth() == 0); + case 'year': + return false; + default: + return false; + } }; - Node.prototype._drawDot = function (ctx) { - this._drawShape(ctx, 'circle'); - }; - Node.prototype._drawTriangle = function (ctx) { - this._drawShape(ctx, 'triangle'); - }; + /** + * Returns formatted text for the minor axislabel, depending on the current + * date and the scale. For example when scale is MINUTE, the current time is + * formatted as "hh:mm". + * @param {Date} [date] custom date. if not provided, current date is taken + */ + TimeStep.prototype.getLabelMinor = function(date) { + if (date == undefined) { + date = this.current; + } - Node.prototype._drawTriangleDown = function (ctx) { - this._drawShape(ctx, 'triangleDown'); + var format = this.format.minorLabels[this.scale]; + return (format && format.length > 0) ? moment(date).format(format) : ''; }; - Node.prototype._drawSquare = function (ctx) { - this._drawShape(ctx, 'square'); - }; + /** + * Returns formatted text for the major axis label, depending on the current + * date and the scale. For example when scale is MINUTE, the major scale is + * hours, and the hour will be formatted as "hh". + * @param {Date} [date] custom date. if not provided, current date is taken + */ + TimeStep.prototype.getLabelMajor = function(date) { + if (date == undefined) { + date = this.current; + } - Node.prototype._drawStar = function (ctx) { - this._drawShape(ctx, 'star'); + var format = this.format.majorLabels[this.scale]; + return (format && format.length > 0) ? moment(date).format(format) : ''; }; - Node.prototype._resizeShape = function (ctx) { - if (!this.width) { - this.options.radius= this.baseRadiusValue; - var size = 2 * this.options.radius; - this.width = size; - this.height = size; + TimeStep.prototype.getClassName = function() { + var m = moment(this.current); + var date = m.locale ? m.locale('en') : m.lang('en'); // old versions of moment have .lang() function + var step = this.step; - // scaling used for clustering - this.width += Math.min(this.clusterSize - 1, this.maxNodeSizeIncrements) * this.clusterSizeWidthFactor; - this.height += Math.min(this.clusterSize - 1, this.maxNodeSizeIncrements) * this.clusterSizeHeightFactor; - this.options.radius+= Math.min(this.clusterSize - 1, this.maxNodeSizeIncrements) * 0.5 * this.clusterSizeRadiusFactor; - this.growthIndicator = this.width - size; + function even(value) { + return (value / step % 2 == 0) ? ' even' : ' odd'; } - }; - - Node.prototype._drawShape = function (ctx, shape) { - this._resizeShape(ctx); - - this.left = this.x - this.width / 2; - this.top = this.y - this.height / 2; - - var clusterLineWidth = 2.5; - var borderWidth = this.options.borderWidth; - var selectionLineWidth = this.options.borderWidthSelected || 2 * this.options.borderWidth; - var radiusMultiplier = 2; - // choose draw method depending on the shape - switch (shape) { - case 'dot': radiusMultiplier = 2; break; - case 'square': radiusMultiplier = 2; break; - case 'triangle': radiusMultiplier = 3; break; - case 'triangleDown': radiusMultiplier = 3; break; - case 'star': radiusMultiplier = 4; break; + function today(date) { + if (date.isSame(new Date(), 'day')) { + return ' today'; + } + if (date.isSame(moment().add(1, 'day'), 'day')) { + return ' tomorrow'; + } + if (date.isSame(moment().add(-1, 'day'), 'day')) { + return ' yesterday'; + } + return ''; } - ctx.strokeStyle = this.selected ? this.options.color.highlight.border : this.hover ? this.options.color.hover.border : this.options.color.border; - // draw the outer border - if (this.clusterSize > 1) { - ctx.lineWidth = (this.selected ? selectionLineWidth : borderWidth) + ((this.clusterSize > 1) ? clusterLineWidth : 0.0); - ctx.lineWidth *= this.networkScaleInv; - ctx.lineWidth = Math.min(this.width,ctx.lineWidth); - - ctx[shape](this.x, this.y, this.options.radius+ radiusMultiplier * ctx.lineWidth); - ctx.stroke(); + function currentWeek(date) { + return date.isSame(new Date(), 'week') ? ' current-week' : ''; } - ctx.lineWidth = (this.selected ? selectionLineWidth : borderWidth) + ((this.clusterSize > 1) ? clusterLineWidth : 0.0); - ctx.lineWidth *= this.networkScaleInv; - ctx.lineWidth = Math.min(this.width,ctx.lineWidth); - - ctx.fillStyle = this.selected ? this.options.color.highlight.background : this.hover ? this.options.color.hover.background : this.options.color.background; - ctx[shape](this.x, this.y, this.options.radius); - ctx.fill(); - ctx.stroke(); - - this.boundingBox.top = this.y - this.options.radius; - this.boundingBox.left = this.x - this.options.radius; - this.boundingBox.right = this.x + this.options.radius; - this.boundingBox.bottom = this.y + this.options.radius; - if (this.label) { - this._label(ctx, this.label, this.x, this.y + this.height / 2, undefined, 'hanging',true); - this.boundingBox.left = Math.min(this.boundingBox.left, this.labelDimensions.left); - this.boundingBox.right = Math.max(this.boundingBox.right, this.labelDimensions.left + this.labelDimensions.width); - this.boundingBox.bottom = Math.max(this.boundingBox.bottom, this.boundingBox.bottom + this.labelDimensions.height); + function currentMonth(date) { + return date.isSame(new Date(), 'month') ? ' current-month' : ''; } - }; - - Node.prototype._resizeText = function (ctx) { - if (!this.width) { - var margin = 5; - var textSize = this.getTextSize(ctx); - this.width = textSize.width + 2 * margin; - this.height = textSize.height + 2 * margin; - // scaling used for clustering - this.width += Math.min(this.clusterSize - 1, this.maxNodeSizeIncrements) * this.clusterSizeWidthFactor; - this.height += Math.min(this.clusterSize - 1, this.maxNodeSizeIncrements) * this.clusterSizeHeightFactor; - this.options.radius+= Math.min(this.clusterSize - 1, this.maxNodeSizeIncrements) * this.clusterSizeRadiusFactor; - this.growthIndicator = this.width - (textSize.width + 2 * margin); + function currentYear(date) { + return date.isSame(new Date(), 'year') ? ' current-year' : ''; } - }; - Node.prototype._drawText = function (ctx) { - this._resizeText(ctx); - this.left = this.x - this.width / 2; - this.top = this.y - this.height / 2; + switch (this.scale) { + case 'millisecond': + return even(date.milliseconds()).trim(); - this._label(ctx, this.label, this.x, this.y); + case 'second': + return even(date.seconds()).trim(); - this.boundingBox.top = this.top; - this.boundingBox.left = this.left; - this.boundingBox.right = this.left + this.width; - this.boundingBox.bottom = this.top + this.height; - }; + case 'minute': + return even(date.minutes()).trim(); + case 'hour': + var hours = date.hours(); + if (this.step == 4) { + hours = hours + '-' + (hours + 4); + } + return hours + 'h' + today(date) + even(date.hours()); - Node.prototype._label = function (ctx, text, x, y, align, baseline, labelUnderNode) { - if (text && Number(this.options.fontSize) * this.networkScale > this.fontDrawThreshold) { - ctx.font = (this.selected ? "bold " : "") + this.options.fontSize + "px " + this.options.fontFace; + case 'weekday': + return date.format('dddd').toLowerCase() + + today(date) + currentWeek(date) + even(date.date()); - var lines = text.split('\n'); - var lineCount = lines.length; - var fontSize = Number(this.options.fontSize); - var yLine = y + (1 - lineCount) / 2 * fontSize; - if (labelUnderNode == true) { - yLine = y + (1 - lineCount) / (2 * fontSize); - } + case 'day': + var day = date.date(); + var month = date.format('MMMM').toLowerCase(); + return 'day' + day + ' ' + month + currentMonth(date) + even(day - 1); - // font fill from edges now for nodes! - var 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; - } - var height = this.options.fontSize * lineCount; - var left = x - width / 2; - var top = y - height / 2; - if (baseline == "hanging") { - top += 0.5 * fontSize; - top += 4; // distance from node, required because we use hanging. Hanging has less difference between browsers - yLine += 4; // distance from node - } - this.labelDimensions = {top:top,left:left,width:width,height:height,yLine:yLine}; + case 'month': + return date.format('MMMM').toLowerCase() + + currentMonth(date) + even(date.month()); - // create the fontfill background - if (this.options.fontFill !== undefined && this.options.fontFill !== null && this.options.fontFill !== "none") { - ctx.fillStyle = this.options.fontFill; - ctx.fillRect(left, top, width, height); - } + case 'year': + var year = date.year(); + return 'year' + year + currentYear(date)+ even(year); - // draw text - ctx.fillStyle = this.options.fontColor || "black"; - ctx.textAlign = align || "center"; - ctx.textBaseline = baseline || "middle"; - if (this.options.fontStrokeWidth > 0){ - ctx.lineWidth = this.options.fontStrokeWidth; - ctx.strokeStyle = this.options.fontStrokeColor; - ctx.lineJoin = 'round'; - } - for (var i = 0; i < lineCount; i++) { - if(this.options.fontStrokeWidth){ - ctx.strokeText(lines[i], x, yLine); - } - ctx.fillText(lines[i], x, yLine); - yLine += fontSize; - } + default: + return ''; } }; + module.exports = TimeStep; - Node.prototype.getTextSize = function(ctx) { - if (this.label !== undefined) { - ctx.font = (this.selected ? "bold " : "") + this.options.fontSize + "px " + this.options.fontFace; - - var lines = this.label.split('\n'), - height = (Number(this.options.fontSize) + 4) * lines.length, - width = 0; - for (var i = 0, iMax = lines.length; i < iMax; i++) { - width = Math.max(width, ctx.measureText(lines[i]).width); - } +/***/ }, +/* 39 */ +/***/ function(module, exports, __webpack_require__) { - return {"width": width, "height": height, lineCount: lines.length}; - } - else { - return {"width": 0, "height": 0, lineCount: 0}; - } - }; + var util = __webpack_require__(1); + var Component = __webpack_require__(23); + var moment = __webpack_require__(2); + var locales = __webpack_require__(40); /** - * this is used to determine if a node is visible at all. this is used to determine when it needs to be drawn. - * there is a safety margin of 0.3 * width; - * - * @returns {boolean} + * A current time bar + * @param {{range: Range, dom: Object, domProps: Object}} body + * @param {Object} [options] Available parameters: + * {Boolean} [showCurrentTime] + * @constructor CurrentTime + * @extends Component */ - Node.prototype.inArea = function() { - if (this.width !== undefined) { - return (this.x + this.width *this.networkScaleInv >= this.canvasTopLeft.x && - this.x - this.width *this.networkScaleInv < this.canvasBottomRight.x && - this.y + this.height*this.networkScaleInv >= this.canvasTopLeft.y && - this.y - this.height*this.networkScaleInv < this.canvasBottomRight.y); - } - else { - return true; - } - }; + function CurrentTime (body, options) { + this.body = body; - /** - * checks if the core of the node is in the display area, this is used for opening clusters around zoom - * @returns {boolean} - */ - Node.prototype.inView = function() { - return (this.x >= this.canvasTopLeft.x && - this.x < this.canvasBottomRight.x && - this.y >= this.canvasTopLeft.y && - this.y < this.canvasBottomRight.y); - }; + // default options + this.defaultOptions = { + showCurrentTime: true, - /** - * This allows the zoom level of the network to influence the rendering - * We store the inverted scale and the coordinates of the top left, and bottom right points of the canvas - * - * @param scale - * @param canvasTopLeft - * @param canvasBottomRight - */ - Node.prototype.setScaleAndPos = function(scale,canvasTopLeft,canvasBottomRight) { - this.networkScaleInv = 1.0/scale; - this.networkScale = scale; - this.canvasTopLeft = canvasTopLeft; - this.canvasBottomRight = canvasBottomRight; - }; + locales: locales, + locale: 'en' + }; + this.options = util.extend({}, this.defaultOptions); + this.offset = 0; + + this._create(); + + this.setOptions(options); + } + CurrentTime.prototype = new Component(); /** - * This allows the zoom level of the network to influence the rendering - * - * @param scale + * Create the HTML DOM for the current time bar + * @private */ - Node.prototype.setScale = function(scale) { - this.networkScaleInv = 1.0/scale; - this.networkScale = scale; + CurrentTime.prototype._create = function() { + var bar = document.createElement('div'); + bar.className = 'currenttime'; + bar.style.position = 'absolute'; + bar.style.top = '0px'; + bar.style.height = '100%'; + + this.bar = bar; }; + /** + * Destroy the CurrentTime bar + */ + CurrentTime.prototype.destroy = function () { + this.options.showCurrentTime = false; + this.redraw(); // will remove the bar from the DOM and stop refreshing + this.body = null; + }; /** - * set the velocity at 0. Is called when this node is contained in another during clustering + * Set options for the component. Options will be merged in current options. + * @param {Object} options Available parameters: + * {boolean} [showCurrentTime] */ - Node.prototype.clearVelocity = function() { - this.vx = 0; - this.vy = 0; + CurrentTime.prototype.setOptions = function(options) { + if (options) { + // copy all options that we know + util.selectiveExtend(['showCurrentTime', 'locale', 'locales'], this.options, options); + } }; - /** - * Basic preservation of (kinectic) energy - * - * @param massBeforeClustering + * Repaint the component + * @return {boolean} Returns true if the component is resized */ - Node.prototype.updateVelocity = function(massBeforeClustering) { - var energyBefore = this.vx * this.vx * massBeforeClustering; - //this.vx = (this.vx < 0) ? -Math.sqrt(energyBefore/this.options.mass) : Math.sqrt(energyBefore/this.options.mass); - this.vx = Math.sqrt(energyBefore/this.options.mass); - energyBefore = this.vy * this.vy * massBeforeClustering; - //this.vy = (this.vy < 0) ? -Math.sqrt(energyBefore/this.options.mass) : Math.sqrt(energyBefore/this.options.mass); - this.vy = Math.sqrt(energyBefore/this.options.mass); - }; + CurrentTime.prototype.redraw = function() { + if (this.options.showCurrentTime) { + var parent = this.body.dom.backgroundVertical; + if (this.bar.parentNode != parent) { + // attach to the dom + if (this.bar.parentNode) { + this.bar.parentNode.removeChild(this.bar); + } + parent.appendChild(this.bar); - module.exports = Node; + this.start(); + } + var now = new Date(new Date().valueOf() + this.offset); + var x = this.body.util.toScreen(now); -/***/ }, -/* 41 */ -/***/ function(module, exports, __webpack_require__) { + var locale = this.options.locales[this.options.locale]; + var title = locale.current + ' ' + locale.time + ': ' + moment(now).format('dddd, MMMM Do YYYY, H:mm:ss'); + title = title.charAt(0).toUpperCase() + title.substring(1); - /** - * 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 Popup(container, x, y, text, style) { - if (container) { - this.container = container; + this.bar.style.left = x + 'px'; + this.bar.title = title; } else { - this.container = document.body; - } - - // x, y and text are optional, see if a style object was passed in their place - if (style === undefined) { - if (typeof x === "object") { - style = x; - x = undefined; - } else if (typeof text === "object") { - style = text; - text = undefined; - } else { - // for backwards compatibility, in case clients other than Network are creating Popup directly - style = { - fontColor: 'black', - fontSize: 14, // px - fontFace: 'verdana', - color: { - border: '#666', - background: '#FFFFC6' - } - } + // remove the line from the DOM + if (this.bar.parentNode) { + this.bar.parentNode.removeChild(this.bar); } + this.stop(); } - this.x = 0; - this.y = 0; - this.padding = 5; + return false; + }; - if (x !== undefined && y !== undefined ) { - this.setPosition(x, y); - } - if (text !== undefined) { - this.setText(text); + /** + * Start auto refreshing the current time bar + */ + CurrentTime.prototype.start = function() { + var me = this; + + function update () { + me.stop(); + + // determine interval to refresh + var scale = me.body.range.conversion(me.body.domProps.center.width).scale; + var interval = 1 / scale / 10; + if (interval < 30) interval = 30; + if (interval > 1000) interval = 1000; + + me.redraw(); + + // start a timer to adjust for the new time + me.currentTimeTimer = setTimeout(update, interval); } - // create the frame - this.frame = document.createElement("div"); - var styleAttr = this.frame.style; - styleAttr.position = "absolute"; - styleAttr.visibility = "hidden"; - styleAttr.border = "1px solid " + style.color.border; - styleAttr.color = style.fontColor; - styleAttr.fontSize = style.fontSize + "px"; - styleAttr.fontFamily = style.fontFace; - styleAttr.padding = this.padding + "px"; - styleAttr.backgroundColor = style.color.background; - styleAttr.borderRadius = "3px"; - styleAttr.MozBorderRadius = "3px"; - styleAttr.WebkitBorderRadius = "3px"; - styleAttr.boxShadow = "3px 3px 10px rgba(128, 128, 128, 0.5)"; - styleAttr.whiteSpace = "nowrap"; - this.container.appendChild(this.frame); - } + update(); + }; /** - * @param {number} x Horizontal position of the popup window - * @param {number} y Vertical position of the popup window + * Stop auto refreshing the current time bar */ - Popup.prototype.setPosition = function(x, y) { - this.x = parseInt(x); - this.y = parseInt(y); + CurrentTime.prototype.stop = function() { + if (this.currentTimeTimer !== undefined) { + clearTimeout(this.currentTimeTimer); + delete this.currentTimeTimer; + } }; /** - * Set the content for the popup window. This can be HTML code or text. - * @param {string | Element} content + * Set a current time. This can be used for example to ensure that a client's + * time is synchronized with a shared server time. + * @param {Date | String | Number} time A Date, unix timestamp, or + * ISO date string. */ - Popup.prototype.setText = function(content) { - if (content instanceof Element) { - this.frame.innerHTML = ''; - this.frame.appendChild(content); - } - else { - this.frame.innerHTML = content; // string containing text or HTML - } + CurrentTime.prototype.setCurrentTime = function(time) { + var t = util.convert(time, 'Date').valueOf(); + var now = new Date().valueOf(); + this.offset = t - now; + this.redraw(); }; /** - * Show the popup window - * @param {boolean} show Optional. Show or hide the window + * Get the current time. + * @return {Date} Returns the current time. */ - Popup.prototype.show = function (show) { - if (show === undefined) { - show = true; - } + CurrentTime.prototype.getCurrentTime = function() { + return new Date(new Date().valueOf() + this.offset); + }; - if (show) { - var height = this.frame.clientHeight; - var width = this.frame.clientWidth; - var maxHeight = this.frame.parentNode.clientHeight; - var maxWidth = this.frame.parentNode.clientWidth; + module.exports = CurrentTime; - 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; - } +/***/ }, +/* 40 */ +/***/ function(module, exports, __webpack_require__) { - this.frame.style.left = left + "px"; - this.frame.style.top = top + "px"; - this.frame.style.visibility = "visible"; - } - else { - this.hide(); - } + // English + exports['en'] = { + current: 'current', + time: 'time' }; + exports['en_EN'] = exports['en']; + exports['en_US'] = exports['en']; - /** - * Hide the popup window - */ - Popup.prototype.hide = function () { - this.frame.style.visibility = "hidden"; + // Dutch + exports['nl'] = { + custom: 'aangepaste', + time: 'tijd' }; - - module.exports = Popup; + exports['nl_NL'] = exports['nl']; + exports['nl_BE'] = exports['nl']; /***/ }, -/* 42 */ +/* 41 */ /***/ function(module, exports, __webpack_require__) { + var Hammer = __webpack_require__(19); + var util = __webpack_require__(1); + var Component = __webpack_require__(23); + var moment = __webpack_require__(2); + var locales = __webpack_require__(40); + /** - * Parse a text source containing data in DOT language into a JSON object. - * The object contains two lists: one with nodes and one with edges. - * - * DOT language reference: http://www.graphviz.org/doc/info/lang.html - * - * @param {String} data Text containing a graph in DOT-notation - * @return {Object} graph An object containing two parameters: - * {Object[]} nodes - * {Object[]} edges + * A custom time bar + * @param {{range: Range, dom: Object}} body + * @param {Object} [options] Available parameters: + * {Boolean} [showCustomTime] + * @constructor CustomTime + * @extends Component */ - function parseDOT (data) { - dot = data; - return parseGraph(); - } - // token types enumeration - var TOKENTYPE = { - NULL : 0, - DELIMITER : 1, - IDENTIFIER: 2, - UNKNOWN : 3 - }; + function CustomTime (body, options) { + this.body = body; - // map with all delimiters - var DELIMITERS = { - '{': true, - '}': true, - '[': true, - ']': true, - ';': true, - '=': true, - ',': true, + // default options + this.defaultOptions = { + showCustomTime: false, + locales: locales, + locale: 'en' + }; + this.options = util.extend({}, this.defaultOptions); - '->': true, - '--': true - }; + this.customTime = new Date(); + this.eventParams = {}; // stores state parameters while dragging the bar - var dot = ''; // current dot file - var index = 0; // current index in dot file - var c = ''; // current token character in expr - var token = ''; // current token - var tokenType = TOKENTYPE.NULL; // type of the token + // create the DOM + this._create(); - /** - * Get the first character from the dot file. - * The character is stored into the char c. If the end of the dot file is - * reached, the function puts an empty string in c. - */ - function first() { - index = 0; - c = dot.charAt(0); + this.setOptions(options); } - /** - * Get the next character from the dot file. - * The character is stored into the char c. If the end of the dot file is - * reached, the function puts an empty string in c. - */ - function next() { - index++; - c = dot.charAt(index); - } + CustomTime.prototype = new Component(); /** - * Preview the next character from the dot file. - * @return {String} cNext + * Set options for the component. Options will be merged in current options. + * @param {Object} options Available parameters: + * {boolean} [showCustomTime] */ - function nextPreview() { - return dot.charAt(index + 1); - } + CustomTime.prototype.setOptions = function(options) { + if (options) { + // copy all options that we know + util.selectiveExtend(['showCustomTime', 'locale', 'locales'], this.options, options); + } + }; /** - * Test whether given character is alphabetic or numeric - * @param {String} c - * @return {Boolean} isAlphaNumeric + * Create the DOM for the custom time + * @private */ - var regexAlphaNumeric = /[a-zA-Z_0-9.:#]/; - function isAlphaNumeric(c) { - return regexAlphaNumeric.test(c); - } + CustomTime.prototype._create = function() { + var bar = document.createElement('div'); + bar.className = 'customtime'; + bar.style.position = 'absolute'; + bar.style.top = '0px'; + bar.style.height = '100%'; + this.bar = bar; - /** - * Merge all properties of object b into object b - * @param {Object} a - * @param {Object} b - * @return {Object} a - */ - function merge (a, b) { - if (!a) { - a = {}; - } + var drag = document.createElement('div'); + drag.style.position = 'relative'; + drag.style.top = '0px'; + drag.style.left = '-10px'; + drag.style.height = '100%'; + drag.style.width = '20px'; + bar.appendChild(drag); - if (b) { - for (var name in b) { - if (b.hasOwnProperty(name)) { - a[name] = b[name]; - } - } - } - return a; - } + // attach event listeners + this.hammer = Hammer(bar, { + prevent_default: true + }); + this.hammer.on('dragstart', this._onDragStart.bind(this)); + this.hammer.on('drag', this._onDrag.bind(this)); + this.hammer.on('dragend', this._onDragEnd.bind(this)); + }; /** - * Set a value in an object, where the provided parameter name can be a - * path with nested parameters. For example: - * - * var obj = {a: 2}; - * setValue(obj, 'b.c', 3); // obj = {a: 2, b: {c: 3}} - * - * @param {Object} obj - * @param {String} path A parameter name or dot-separated parameter path, - * like "color.highlight.border". - * @param {*} value + * Destroy the CustomTime bar */ - function setValue(obj, path, value) { - var keys = path.split('.'); - var o = obj; - while (keys.length) { - var key = keys.shift(); - if (keys.length) { - // this isn't the end point - if (!o[key]) { - o[key] = {}; - } - o = o[key]; - } - else { - // this is the end point - o[key] = value; - } - } - } + CustomTime.prototype.destroy = function () { + this.options.showCustomTime = false; + this.redraw(); // will remove the bar from the DOM - /** - * Add a node to a graph object. If there is already a node with - * the same id, their attributes will be merged. - * @param {Object} graph - * @param {Object} node - */ - function addNode(graph, node) { - var i, len; - var current = null; + this.hammer.enable(false); + this.hammer = null; - // find root graph (in case of subgraph) - var graphs = [graph]; // list with all graphs from current graph to root graph - var root = graph; - while (root.parent) { - graphs.push(root.parent); - root = root.parent; - } + this.body = null; + }; - // find existing node (at root level) by its id - if (root.nodes) { - for (i = 0, len = root.nodes.length; i < len; i++) { - if (node.id === root.nodes[i].id) { - current = root.nodes[i]; - break; + /** + * Repaint the component + * @return {boolean} Returns true if the component is resized + */ + CustomTime.prototype.redraw = function () { + if (this.options.showCustomTime) { + var parent = this.body.dom.backgroundVertical; + if (this.bar.parentNode != parent) { + // attach to the dom + if (this.bar.parentNode) { + this.bar.parentNode.removeChild(this.bar); } + parent.appendChild(this.bar); } - } - if (!current) { - // this is a new node - current = { - id: node.id - }; - if (graph.node) { - // clone default attributes - current.attr = merge(current.attr, graph.node); - } - } + var x = this.body.util.toScreen(this.customTime); - // add node to this (sub)graph and all its parent graphs - for (i = graphs.length - 1; i >= 0; i--) { - var g = graphs[i]; + var locale = this.options.locales[this.options.locale]; + var title = locale.time + ': ' + moment(this.customTime).format('dddd, MMMM Do YYYY, H:mm:ss'); + title = title.charAt(0).toUpperCase() + title.substring(1); - if (!g.nodes) { - g.nodes = []; - } - if (g.nodes.indexOf(current) == -1) { - g.nodes.push(current); + this.bar.style.left = x + 'px'; + this.bar.title = title; + } + else { + // remove the line from the DOM + if (this.bar.parentNode) { + this.bar.parentNode.removeChild(this.bar); } } - // merge attributes - if (node.attr) { - current.attr = merge(current.attr, node.attr); - } - } + return false; + }; /** - * Add an edge to a graph object - * @param {Object} graph - * @param {Object} edge + * Set custom time. + * @param {Date | number | string} time */ - function addEdge(graph, edge) { - if (!graph.edges) { - graph.edges = []; - } - graph.edges.push(edge); - if (graph.edge) { - var attr = merge({}, graph.edge); // clone default attributes - edge.attr = merge(attr, edge.attr); // merge attributes - } - } + CustomTime.prototype.setCustomTime = function(time) { + this.customTime = util.convert(time, 'Date'); + this.redraw(); + }; /** - * Create an edge to a graph object - * @param {Object} graph - * @param {String | Number | Object} from - * @param {String | Number | Object} to - * @param {String} type - * @param {Object | null} attr - * @return {Object} edge + * Retrieve the current custom time. + * @return {Date} customTime */ - function createEdge(graph, from, to, type, attr) { - var edge = { - from: from, - to: to, - type: type - }; - - if (graph.edge) { - edge.attr = merge({}, graph.edge); // clone default attributes - } - edge.attr = merge(edge.attr || {}, attr); // merge attributes - - return edge; - } + CustomTime.prototype.getCustomTime = function() { + return new Date(this.customTime.valueOf()); + }; /** - * Get next token in the current dot file. - * The token and token type are available as token and tokenType + * Start moving horizontally + * @param {Event} event + * @private */ - function getToken() { - tokenType = TOKENTYPE.NULL; - token = ''; + CustomTime.prototype._onDragStart = function(event) { + this.eventParams.dragging = true; + this.eventParams.customTime = this.customTime; - // skip over whitespaces - while (c == ' ' || c == '\t' || c == '\n' || c == '\r') { // space, tab, enter - next(); - } + event.stopPropagation(); + event.preventDefault(); + }; - do { - var isComment = false; + /** + * Perform moving operating. + * @param {Event} event + * @private + */ + CustomTime.prototype._onDrag = function (event) { + if (!this.eventParams.dragging) return; - // skip comment - if (c == '#') { - // find the previous non-space character - var i = index - 1; - while (dot.charAt(i) == ' ' || dot.charAt(i) == '\t') { - i--; - } - if (dot.charAt(i) == '\n' || dot.charAt(i) == '') { - // the # is at the start of a line, this is indeed a line comment - while (c != '' && c != '\n') { - next(); - } - isComment = true; - } - } - if (c == '/' && nextPreview() == '/') { - // skip line comment - while (c != '' && c != '\n') { - next(); - } - isComment = true; - } - if (c == '/' && nextPreview() == '*') { - // skip block comment - while (c != '') { - if (c == '*' && nextPreview() == '/') { - // end of block comment found. skip these last two characters - next(); - next(); - break; - } - else { - next(); - } - } - isComment = true; - } + var deltaX = event.gesture.deltaX, + x = this.body.util.toScreen(this.eventParams.customTime) + deltaX, + time = this.body.util.toTime(x); - // skip over whitespaces - while (c == ' ' || c == '\t' || c == '\n' || c == '\r') { // space, tab, enter - next(); - } - } - while (isComment); + this.setCustomTime(time); - // check for end of dot file - if (c == '') { - // token is still empty - tokenType = TOKENTYPE.DELIMITER; - return; - } + // fire a timechange event + this.body.emitter.emit('timechange', { + time: new Date(this.customTime.valueOf()) + }); - // check for delimiters consisting of 2 characters - var c2 = c + nextPreview(); - if (DELIMITERS[c2]) { - tokenType = TOKENTYPE.DELIMITER; - token = c2; - next(); - next(); - return; - } + event.stopPropagation(); + event.preventDefault(); + }; - // check for delimiters consisting of 1 character - if (DELIMITERS[c]) { - tokenType = TOKENTYPE.DELIMITER; - token = c; - next(); - return; - } + /** + * Stop moving operating. + * @param {event} event + * @private + */ + CustomTime.prototype._onDragEnd = function (event) { + if (!this.eventParams.dragging) return; - // check for an identifier (number or string) - // TODO: more precise parsing of numbers/strings (and the port separator ':') - if (isAlphaNumeric(c) || c == '-') { - token += c; - next(); + // fire a timechanged event + this.body.emitter.emit('timechanged', { + time: new Date(this.customTime.valueOf()) + }); - while (isAlphaNumeric(c)) { - token += c; - next(); - } - if (token == 'false') { - token = false; // convert to boolean - } - else if (token == 'true') { - token = true; // convert to boolean - } - else if (!isNaN(Number(token))) { - token = Number(token); // convert to number - } - tokenType = TOKENTYPE.IDENTIFIER; - return; - } + event.stopPropagation(); + event.preventDefault(); + }; - // check for a string enclosed by double quotes - if (c == '"') { - next(); - while (c != '' && (c != '"' || (c == '"' && nextPreview() == '"'))) { - token += c; - if (c == '"') { // skip the escape character - next(); - } - next(); - } - if (c != '"') { - throw newSyntaxError('End of string " expected'); - } - next(); - tokenType = TOKENTYPE.IDENTIFIER; - return; - } + module.exports = CustomTime; - // something unknown is found, wrong characters, a syntax error - tokenType = TOKENTYPE.UNKNOWN; - while (c != '') { - token += c; - next(); - } - throw new SyntaxError('Syntax error in part "' + chop(token, 30) + '"'); - } - /** - * Parse a graph. - * @returns {Object} graph - */ - function parseGraph() { - var graph = {}; +/***/ }, +/* 42 */ +/***/ function(module, exports, __webpack_require__) { - first(); - getToken(); + var Emitter = __webpack_require__(11); + var Hammer = __webpack_require__(19); + var util = __webpack_require__(1); + var DataSet = __webpack_require__(7); + var DataView = __webpack_require__(9); + var Range = __webpack_require__(21); + var Core = __webpack_require__(25); + var TimeAxis = __webpack_require__(37); + var CurrentTime = __webpack_require__(39); + var CustomTime = __webpack_require__(41); + var LineGraph = __webpack_require__(43); - // optional strict keyword - if (token == 'strict') { - graph.strict = true; - getToken(); + /** + * Create a timeline visualization + * @param {HTMLElement} container + * @param {vis.DataSet | Array | google.visualization.DataTable} [items] + * @param {Object} [options] See Graph2d.setOptions for the available options. + * @constructor + * @extends Core + */ + function Graph2d (container, items, groups, options) { + // if the third element is options, the forth is groups (optionally); + if (!(Array.isArray(groups) || groups instanceof DataSet) && groups instanceof Object) { + var forthArgument = options; + options = groups; + groups = forthArgument; } - // graph or digraph keyword - if (token == 'graph' || token == 'digraph') { - graph.type = token; - getToken(); - } + var me = this; + this.defaultOptions = { + start: null, + end: null, - // optional graph id - if (tokenType == TOKENTYPE.IDENTIFIER) { - graph.id = token; - getToken(); - } + autoResize: true, - // open angle bracket - if (token != '{') { - throw newSyntaxError('Angle bracket { expected'); - } - getToken(); + orientation: 'bottom', + width: null, + height: null, + maxHeight: null, + minHeight: null + }; + this.options = util.deepExtend({}, this.defaultOptions); - // statements - parseStatements(graph); + // Create the DOM, props, and emitter + this._create(container); - // close angle bracket - if (token != '}') { - throw newSyntaxError('Angle bracket } expected'); - } - getToken(); + // all components listed here will be repainted automatically + this.components = []; - // end of file - if (token !== '') { - throw newSyntaxError('End of file expected'); - } - getToken(); + this.body = { + dom: this.dom, + domProps: this.props, + emitter: { + on: this.on.bind(this), + off: this.off.bind(this), + emit: this.emit.bind(this) + }, + hiddenDates: [], + util: { + snap: null, // will be specified after TimeAxis is created + toScreen: me._toScreen.bind(me), + toGlobalScreen: me._toGlobalScreen.bind(me), // this refers to the root.width + toTime: me._toTime.bind(me), + toGlobalTime : me._toGlobalTime.bind(me) + } + }; - // remove temporary default properties - delete graph.node; - delete graph.edge; - delete graph.graph; + // range + this.range = new Range(this.body); + this.components.push(this.range); + this.body.range = this.range; - return graph; - } + // time axis + this.timeAxis = new TimeAxis(this.body); + this.components.push(this.timeAxis); + this.body.util.snap = this.timeAxis.snap.bind(this.timeAxis); - /** - * Parse a list with statements. - * @param {Object} graph - */ - function parseStatements (graph) { - while (token !== '' && token != '}') { - parseStatement(graph); - if (token == ';') { - getToken(); - } - } - } + // current time bar + this.currentTime = new CurrentTime(this.body); + this.components.push(this.currentTime); - /** - * Parse a single statement. Can be a an attribute statement, node - * statement, a series of node statements and edge statements, or a - * parameter. - * @param {Object} graph - */ - function parseStatement(graph) { - // parse subgraph - var subgraph = parseSubgraph(graph); - if (subgraph) { - // edge statements - parseEdge(graph, subgraph); + // custom time bar + // Note: time bar will be attached in this.setOptions when selected + this.customTime = new CustomTime(this.body); + this.components.push(this.customTime); - return; - } + // item set + this.linegraph = new LineGraph(this.body); + this.components.push(this.linegraph); - // parse an attribute statement - var attr = parseAttributeStatement(graph); - if (attr) { - return; + this.itemsData = null; // DataSet + this.groupsData = null; // DataSet + + // apply options + if (options) { + this.setOptions(options); } - // parse node - if (tokenType != TOKENTYPE.IDENTIFIER) { - throw newSyntaxError('Identifier expected'); + // IMPORTANT: THIS HAPPENS BEFORE SET ITEMS! + if (groups) { + this.setGroups(groups); } - var id = token; // id can be a string or a number - getToken(); - if (token == '=') { - // id statement - getToken(); - if (tokenType != TOKENTYPE.IDENTIFIER) { - throw newSyntaxError('Identifier expected'); - } - graph[id] = token; - getToken(); - // TODO: implement comma separated list with "a_list: ID=ID [','] [a_list] " + // create itemset + if (items) { + this.setItems(items); } else { - parseNodeStatement(graph, id); + this.redraw(); } } + // Extend the functionality from Core + Graph2d.prototype = new Core(); + /** - * Parse a subgraph - * @param {Object} graph parent graph object - * @return {Object | null} subgraph + * Set items + * @param {vis.DataSet | Array | google.visualization.DataTable | null} items */ - function parseSubgraph (graph) { - var subgraph = null; - - // optional subgraph keyword - if (token == 'subgraph') { - subgraph = {}; - subgraph.type = 'subgraph'; - getToken(); + Graph2d.prototype.setItems = function(items) { + var initialLoad = (this.itemsData == null); - // optional graph id - if (tokenType == TOKENTYPE.IDENTIFIER) { - subgraph.id = token; - getToken(); - } + // convert to type DataSet when needed + var newDataSet; + if (!items) { + newDataSet = null; + } + else if (items instanceof DataSet || items instanceof DataView) { + newDataSet = items; + } + else { + // turn an array into a dataset + newDataSet = new DataSet(items, { + type: { + start: 'Date', + end: 'Date' + } + }); } - // open angle bracket - if (token == '{') { - getToken(); - - if (!subgraph) { - subgraph = {}; - } - subgraph.parent = graph; - subgraph.node = graph.node; - subgraph.edge = graph.edge; - subgraph.graph = graph.graph; + // set items + this.itemsData = newDataSet; + this.linegraph && this.linegraph.setItems(newDataSet); - // statements - parseStatements(subgraph); + if (initialLoad) { + if (this.options.start != undefined || this.options.end != undefined) { + var start = this.options.start != undefined ? this.options.start : null; + var end = this.options.end != undefined ? this.options.end : null; - // close angle bracket - if (token != '}') { - throw newSyntaxError('Angle bracket } expected'); + this.setWindow(start, end, {animate: false}); } - getToken(); - - // remove temporary default properties - delete subgraph.node; - delete subgraph.edge; - delete subgraph.graph; - delete subgraph.parent; - - // register at the parent graph - if (!graph.subgraphs) { - graph.subgraphs = []; + else { + this.fit({animate: false}); } - graph.subgraphs.push(subgraph); } - - return subgraph; - } + }; /** - * parse an attribute statement like "node [shape=circle fontSize=16]". - * Available keywords are 'node', 'edge', 'graph'. - * The previous list with default attributes will be replaced - * @param {Object} graph - * @returns {String | null} keyword Returns the name of the parsed attribute - * (node, edge, graph), or null if nothing - * is parsed. + * Set groups + * @param {vis.DataSet | Array | google.visualization.DataTable} groups */ - function parseAttributeStatement (graph) { - // attribute statements - if (token == 'node') { - getToken(); - - // node attributes - graph.node = parseAttributeList(); - return 'node'; + Graph2d.prototype.setGroups = function(groups) { + // convert to type DataSet when needed + var newDataSet; + if (!groups) { + newDataSet = null; } - else if (token == 'edge') { - getToken(); - - // edge attributes - graph.edge = parseAttributeList(); - return 'edge'; + else if (groups instanceof DataSet || groups instanceof DataView) { + newDataSet = groups; } - else if (token == 'graph') { - getToken(); - - // graph attributes - graph.graph = parseAttributeList(); - return 'graph'; + else { + // turn an array into a dataset + newDataSet = new DataSet(groups); } - return null; - } + this.groupsData = newDataSet; + this.linegraph.setGroups(newDataSet); + }; /** - * parse a node statement - * @param {Object} graph - * @param {String | Number} id + * Returns an object containing an SVG element with the icon of the group (size determined by iconWidth and iconHeight), the label of the group (content) and the yAxisOrientation of the group (left or right). + * @param groupId + * @param width + * @param height */ - function parseNodeStatement(graph, id) { - // node statement - var node = { - id: id - }; - var attr = parseAttributeList(); - if (attr) { - node.attr = attr; + Graph2d.prototype.getLegend = function(groupId, width, height) { + if (width === undefined) {width = 15;} + if (height === undefined) {height = 15;} + if (this.linegraph.groups[groupId] !== undefined) { + return this.linegraph.groups[groupId].getLegend(width,height); } - addNode(graph, node); + else { + return "cannot find group:" + groupId; + } + } - // edge statements - parseEdge(graph, id); + /** + * This checks if the visible option of the supplied group (by ID) is true or false. + * @param groupId + * @returns {*} + */ + Graph2d.prototype.isGroupVisible = function(groupId) { + if (this.linegraph.groups[groupId] !== undefined) { + return (this.linegraph.groups[groupId].visible && (this.linegraph.options.groups.visibility[groupId] === undefined || this.linegraph.options.groups.visibility[groupId] == true)); + } + else { + return false; + } } + /** - * Parse an edge or a series of edges - * @param {Object} graph - * @param {String | Number} from Id of the from node + * Get the data range of the item set. + * @returns {{min: Date, max: Date}} range A range with a start and end Date. + * When no minimum is found, min==null + * When no maximum is found, max==null */ - function parseEdge(graph, from) { - while (token == '->' || token == '--') { - var to; - var type = token; - getToken(); + Graph2d.prototype.getItemRange = function() { + var min = null; + var max = null; - var subgraph = parseSubgraph(graph); - if (subgraph) { - to = subgraph; - } - else { - if (tokenType != TOKENTYPE.IDENTIFIER) { - throw newSyntaxError('Identifier or subgraph expected'); + // calculate min from start filed + for (var groupId in this.linegraph.groups) { + if (this.linegraph.groups.hasOwnProperty(groupId)) { + if (this.linegraph.groups[groupId].visible == true) { + for (var i = 0; i < this.linegraph.groups[groupId].itemsData.length; i++) { + var item = this.linegraph.groups[groupId].itemsData[i]; + var value = util.convert(item.x, 'Date').valueOf(); + min = min == null ? value : min > value ? value : min; + max = max == null ? value : max < value ? value : max; + } } - to = token; - addNode(graph, { - id: to - }); - getToken(); } + } - // parse edge attributes - var attr = parseAttributeList(); + return { + min: (min != null) ? new Date(min) : null, + max: (max != null) ? new Date(max) : null + }; + }; - // create edge - var edge = createEdge(graph, from, to, type, attr); - addEdge(graph, edge); - from = to; - } - } + + module.exports = Graph2d; + + +/***/ }, +/* 43 */ +/***/ function(module, exports, __webpack_require__) { + + var util = __webpack_require__(1); + var DOMutil = __webpack_require__(6); + var DataSet = __webpack_require__(7); + var DataView = __webpack_require__(9); + var Component = __webpack_require__(23); + var DataAxis = __webpack_require__(44); + var GraphGroup = __webpack_require__(46); + var Legend = __webpack_require__(50); + var BarGraphFunctions = __webpack_require__(49); + + var UNGROUPED = '__ungrouped__'; // reserved group id for ungrouped items /** - * Parse a set with attributes, - * for example [label="1.000", shape=solid] - * @return {Object | null} attr + * This is the constructor of the LineGraph. It requires a Timeline body and options. + * + * @param body + * @param options + * @constructor */ - function parseAttributeList() { - var attr = null; + function LineGraph(body, options) { + this.id = util.randomUUID(); + this.body = body; - while (token == '[') { - getToken(); - attr = {}; - while (token !== '' && token != ']') { - if (tokenType != TOKENTYPE.IDENTIFIER) { - throw newSyntaxError('Attribute name expected'); + this.defaultOptions = { + yAxisOrientation: 'left', + defaultGroup: 'default', + sort: true, + sampling: true, + graphHeight: '400px', + shaded: { + enabled: false, + orientation: 'bottom' // top, bottom + }, + style: 'line', // line, bar + barChart: { + width: 50, + handleOverlap: 'overlap', + align: 'center' // left, center, right + }, + catmullRom: { + enabled: true, + parametrization: 'centripetal', // uniform (alpha = 0.0), chordal (alpha = 1.0), centripetal (alpha = 0.5) + alpha: 0.5 + }, + drawPoints: { + enabled: true, + size: 6, + style: 'square' // square, circle + }, + dataAxis: { + showMinorLabels: true, + showMajorLabels: true, + icons: false, + width: '40px', + visible: true, + alignZeros: true, + customRange: { + left: {min:undefined, max:undefined}, + right: {min:undefined, max:undefined} } - var name = token; - - getToken(); - if (token != '=') { - throw newSyntaxError('Equal sign = expected'); + //, these options are not set by default, but this shows the format they will be in + //format: { + // left: {decimals: 2}, + // right: {decimals: 2} + //}, + //title: { + // left: { + // text: 'left', + // style: 'color:black;' + // }, + // right: { + // text: 'right', + // style: 'color:black;' + // } + //} + }, + legend: { + enabled: false, + icons: true, + left: { + visible: true, + position: 'top-left' // top/bottom - left,right + }, + right: { + visible: true, + position: 'top-right' // top/bottom - left,right } - getToken(); + }, + groups: { + visibility: {} + } + }; - if (tokenType != TOKENTYPE.IDENTIFIER) { - throw newSyntaxError('Attribute value expected'); - } - var value = token; - setValue(attr, name, value); // name can be a path + // options is shared by this ItemSet and all its items + this.options = util.extend({}, this.defaultOptions); + this.dom = {}; + this.props = {}; + this.hammer = null; + this.groups = {}; + this.abortedGraphUpdate = false; + this.updateSVGheight = false; + this.updateSVGheightOnResize = false; - getToken(); - if (token ==',') { - getToken(); - } + var me = this; + this.itemsData = null; // DataSet + this.groupsData = null; // DataSet + + // listeners for the DataSet of the items + this.itemListeners = { + 'add': function (event, params, senderId) { + me._onAdd(params.items); + }, + 'update': function (event, params, senderId) { + me._onUpdate(params.items); + }, + 'remove': function (event, params, senderId) { + me._onRemove(params.items); } + }; - if (token != ']') { - throw newSyntaxError('Bracket ] expected'); + // listeners for the DataSet of the groups + this.groupListeners = { + 'add': function (event, params, senderId) { + me._onAddGroups(params.items); + }, + 'update': function (event, params, senderId) { + me._onUpdateGroups(params.items); + }, + 'remove': function (event, params, senderId) { + me._onRemoveGroups(params.items); } - getToken(); - } + }; - return attr; - } + this.items = {}; // object with an Item for every data item + this.selection = []; // list with the ids of all selected nodes + this.lastStart = this.body.range.start; + this.touchParams = {}; // stores properties while dragging - /** - * Create a syntax error with extra information on current token and index. - * @param {String} message - * @returns {SyntaxError} err - */ - function newSyntaxError(message) { - return new SyntaxError(message + ', got "' + chop(token, 30) + '" (char ' + index + ')'); - } + this.svgElements = {}; + this.setOptions(options); + this.groupsUsingDefaultStyles = [0]; + this.COUNTER = 0; + this.body.emitter.on('rangechanged', function() { + me.lastStart = me.body.range.start; + me.svg.style.left = util.option.asSize(-me.props.width); + me.redraw.call(me,true); + }); + + // create the HTML DOM + this._create(); + this.framework = {svg: this.svg, svgElements: this.svgElements, options: this.options, groups: this.groups}; + this.body.emitter.emit('change'); - /** - * Chop off text after a maximum length - * @param {String} text - * @param {Number} maxLength - * @returns {String} - */ - function chop (text, maxLength) { - return (text.length <= maxLength) ? text : (text.substr(0, 27) + '...'); } + LineGraph.prototype = new Component(); + /** - * Execute a function fn for each pair of elements in two arrays - * @param {Array | *} array1 - * @param {Array | *} array2 - * @param {function} fn + * Create the HTML DOM for the ItemSet */ - function forEach2(array1, array2, fn) { - if (Array.isArray(array1)) { - array1.forEach(function (elem1) { - if (Array.isArray(array2)) { - array2.forEach(function (elem2) { - fn(elem1, elem2); - }); - } - else { - fn(elem1, array2); - } - }); - } - else { - if (Array.isArray(array2)) { - array2.forEach(function (elem2) { - fn(array1, elem2); - }); - } - else { - fn(array1, array2); - } - } - } + LineGraph.prototype._create = function(){ + var frame = document.createElement('div'); + frame.className = 'LineGraph'; + this.dom.frame = frame; + + // create svg element for graph drawing. + this.svg = document.createElementNS('http://www.w3.org/2000/svg','svg'); + this.svg.style.position = 'relative'; + this.svg.style.height = ('' + this.options.graphHeight).replace('px','') + 'px'; + this.svg.style.display = 'block'; + frame.appendChild(this.svg); + + // data axis + this.options.dataAxis.orientation = 'left'; + this.yAxisLeft = new DataAxis(this.body, this.options.dataAxis, this.svg, this.options.groups); + + this.options.dataAxis.orientation = 'right'; + this.yAxisRight = new DataAxis(this.body, this.options.dataAxis, this.svg, this.options.groups); + delete this.options.dataAxis.orientation; + + // legends + this.legendLeft = new Legend(this.body, this.options.legend, 'left', this.options.groups); + this.legendRight = new Legend(this.body, this.options.legend, 'right', this.options.groups); + + this.show(); + }; /** - * Convert a string containing a graph in DOT language into a map containing - * with nodes and edges in the format of graph. - * @param {String} data Text containing a graph in DOT-notation - * @return {Object} graphData + * set the options of the LineGraph. the mergeOptions is used for subObjects that have an enabled element. + * @param {object} options */ - function DOTToGraph (data) { - // parse the DOT file - var dotData = parseDOT(data); - var graphData = { - nodes: [], - edges: [], - options: {} - }; - - // copy the nodes - if (dotData.nodes) { - dotData.nodes.forEach(function (dotNode) { - var graphNode = { - id: dotNode.id, - label: String(dotNode.label || dotNode.id) - }; - merge(graphNode, dotNode.attr); - if (graphNode.image) { - graphNode.shape = 'image'; + LineGraph.prototype.setOptions = function(options) { + if (options) { + var fields = ['sampling','defaultGroup','height','graphHeight','yAxisOrientation','style','barChart','dataAxis','sort','groups']; + if (options.graphHeight === undefined && options.height !== undefined && this.body.domProps.centerContainer.height !== undefined) { + this.updateSVGheight = true; + this.updateSVGheightOnResize = true; + } + else if (this.body.domProps.centerContainer.height !== undefined && options.graphHeight !== undefined) { + if (parseInt((options.graphHeight + '').replace("px",'')) < this.body.domProps.centerContainer.height) { + this.updateSVGheight = true; } - graphData.nodes.push(graphNode); - }); - } - - // copy the edges - if (dotData.edges) { - /** - * Convert an edge in DOT format to an edge with VisGraph format - * @param {Object} dotEdge - * @returns {Object} graphEdge - */ - var convertEdge = function (dotEdge) { - var graphEdge = { - from: dotEdge.from, - to: dotEdge.to - }; - merge(graphEdge, dotEdge.attr); - graphEdge.style = (dotEdge.type == '->') ? 'arrow' : 'line'; - return graphEdge; } + util.selectiveDeepExtend(fields, this.options, options); + util.mergeOptions(this.options, options,'catmullRom'); + util.mergeOptions(this.options, options,'drawPoints'); + util.mergeOptions(this.options, options,'shaded'); + util.mergeOptions(this.options, options,'legend'); - dotData.edges.forEach(function (dotEdge) { - var from, to; - if (dotEdge.from instanceof Object) { - from = dotEdge.from.nodes; - } - else { - from = { - id: dotEdge.from + if (options.catmullRom) { + if (typeof options.catmullRom == 'object') { + if (options.catmullRom.parametrization) { + if (options.catmullRom.parametrization == 'uniform') { + this.options.catmullRom.alpha = 0; + } + else if (options.catmullRom.parametrization == 'chordal') { + this.options.catmullRom.alpha = 1.0; + } + else { + this.options.catmullRom.parametrization = 'centripetal'; + this.options.catmullRom.alpha = 0.5; + } } } + } - if (dotEdge.to instanceof Object) { - to = dotEdge.to.nodes; - } - else { - to = { - id: dotEdge.to - } + if (this.yAxisLeft) { + if (options.dataAxis !== undefined) { + this.yAxisLeft.setOptions(this.options.dataAxis); + this.yAxisRight.setOptions(this.options.dataAxis); } + } - if (dotEdge.from instanceof Object && dotEdge.from.edges) { - dotEdge.from.edges.forEach(function (subEdge) { - var graphEdge = convertEdge(subEdge); - graphData.edges.push(graphEdge); - }); + if (this.legendLeft) { + if (options.legend !== undefined) { + this.legendLeft.setOptions(this.options.legend); + this.legendRight.setOptions(this.options.legend); } + } - forEach2(from, to, function (from, to) { - var subEdge = createEdge(graphData, from.id, to.id, dotEdge.type, dotEdge.attr); - var graphEdge = convertEdge(subEdge); - graphData.edges.push(graphEdge); - }); - - if (dotEdge.to instanceof Object && dotEdge.to.edges) { - dotEdge.to.edges.forEach(function (subEdge) { - var graphEdge = convertEdge(subEdge); - graphData.edges.push(graphEdge); - }); - } - }); + if (this.groups.hasOwnProperty(UNGROUPED)) { + this.groups[UNGROUPED].setOptions(options); + } } - // copy the options - if (dotData.attr) { - graphData.options = dotData.attr; + // this is used to redraw the graph if the visibility of the groups is changed. + if (this.dom.frame) { + this.redraw(true); } + }; - return graphData; - } + /** + * Hide the component from the DOM + */ + LineGraph.prototype.hide = function() { + // remove the frame containing the items + if (this.dom.frame.parentNode) { + this.dom.frame.parentNode.removeChild(this.dom.frame); + } + }; - // exports - exports.parseDOT = parseDOT; - exports.DOTToGraph = DOTToGraph; + /** + * Show the component in the DOM (when not already visible). + * @return {Boolean} changed + */ + LineGraph.prototype.show = function() { + // show frame containing the items + if (!this.dom.frame.parentNode) { + this.body.dom.center.appendChild(this.dom.frame); + } + }; -/***/ }, -/* 43 */ -/***/ function(module, exports, __webpack_require__) { - - function parseGephi(gephiJSON, options) { - var edges = []; - var nodes = []; - this.options = { - edges: { - inheritColor: true - }, - nodes: { - allowedToMove: false, - parseColor: false - } - }; + /** + * Set items + * @param {vis.DataSet | null} items + */ + LineGraph.prototype.setItems = function(items) { + var me = this, + ids, + oldItemsData = this.itemsData; - if (options !== undefined) { - this.options.nodes['allowedToMove'] = options.allowedToMove | false; - this.options.nodes['parseColor'] = options.parseColor | false; - this.options.edges['inheritColor'] = options.inheritColor | true; + // replace the dataset + if (!items) { + this.itemsData = null; } - - var gEdges = gephiJSON.edges; - var gNodes = gephiJSON.nodes; - for (var i = 0; i < gEdges.length; i++) { - var edge = {}; - var gEdge = gEdges[i]; - edge['id'] = gEdge.id; - edge['from'] = gEdge.source; - edge['to'] = gEdge.target; - edge['attributes'] = gEdge.attributes; - // edge['value'] = gEdge.attributes !== undefined ? gEdge.attributes.Weight : undefined; - // edge['width'] = edge['value'] !== undefined ? undefined : edgegEdge.size; - edge['color'] = gEdge.color; - edge['inheritColor'] = edge['color'] !== undefined ? false : this.options.inheritColor; - edges.push(edge); + else if (items instanceof DataSet || items instanceof DataView) { + this.itemsData = items; } - - for (var i = 0; i < gNodes.length; i++) { - var node = {}; - var gNode = gNodes[i]; - node['id'] = gNode.id; - node['attributes'] = gNode.attributes; - node['x'] = gNode.x; - node['y'] = gNode.y; - node['label'] = gNode.label; - if (this.options.nodes.parseColor == true) { - node['color'] = gNode.color; - } - else { - node['color'] = gNode.color !== undefined ? {background:gNode.color, border:gNode.color} : undefined; - } - node['radius'] = gNode.size; - node['allowedToMoveX'] = this.options.nodes.allowedToMove; - node['allowedToMoveY'] = this.options.nodes.allowedToMove; - nodes.push(node); + else { + throw new TypeError('Data must be an instance of DataSet or DataView'); } - return {nodes:nodes, edges:edges}; - } - - exports.parseGephi = parseGephi; + if (oldItemsData) { + // unsubscribe from old dataset + util.forEach(this.itemListeners, function (callback, event) { + oldItemsData.off(event, callback); + }); -/***/ }, -/* 44 */ -/***/ function(module, exports, __webpack_require__) { + // remove all drawn items + ids = oldItemsData.getIds(); + this._onRemove(ids); + } - // first check if moment.js is already loaded in the browser window, if so, - // use this instance. Else, load via commonjs. - module.exports = (typeof window !== 'undefined') && window['moment'] || __webpack_require__(65); - - -/***/ }, -/* 45 */ -/***/ function(module, exports, __webpack_require__) { + if (this.itemsData) { + // subscribe to new dataset + var id = this.id; + util.forEach(this.itemListeners, function (callback, event) { + me.itemsData.on(event, callback, id); + }); - // Only load hammer.js when in a browser environment - // (loading hammer.js in a node.js environment gives errors) - if (typeof window !== 'undefined') { - module.exports = window['Hammer'] || __webpack_require__(64); - } - else { - module.exports = function () { - throw Error('hammer.js is only available in a browser, not in node.js.'); + // add all new items + ids = this.itemsData.getIds(); + this._onAdd(ids); } - } - - -/***/ }, -/* 46 */ -/***/ function(module, exports, __webpack_require__) { - - var Emitter = __webpack_require__(56); - var Hammer = __webpack_require__(45); - var util = __webpack_require__(1); - var DataSet = __webpack_require__(3); - var DataView = __webpack_require__(4); - var Range = __webpack_require__(17); - var ItemSet = __webpack_require__(27); - var Activator = __webpack_require__(55); - var DateUtil = __webpack_require__(15); - - /** - * Create a timeline visualization - * @param {HTMLElement} container - * @param {vis.DataSet | Array | google.visualization.DataTable} [items] - * @param {Object} [options] See Core.setOptions for the available options. - * @constructor - */ - function Core () {} + this._updateUngrouped(); + //this._updateGraph(); + this.redraw(true); + }; - // turn Core into an event emitter - Emitter(Core.prototype); /** - * Create the main DOM for the Core: a root panel containing left, right, - * top, bottom, content, and background panel. - * @param {Element} container The container element where the Core will - * be attached. - * @private + * Set groups + * @param {vis.DataSet} groups */ - Core.prototype._create = function (container) { - this.dom = {}; - - this.dom.root = document.createElement('div'); - this.dom.background = document.createElement('div'); - this.dom.backgroundVertical = document.createElement('div'); - this.dom.backgroundHorizontal = document.createElement('div'); - this.dom.centerContainer = document.createElement('div'); - this.dom.leftContainer = document.createElement('div'); - this.dom.rightContainer = document.createElement('div'); - this.dom.center = document.createElement('div'); - this.dom.left = document.createElement('div'); - this.dom.right = document.createElement('div'); - this.dom.top = document.createElement('div'); - this.dom.bottom = document.createElement('div'); - this.dom.shadowTop = document.createElement('div'); - this.dom.shadowBottom = document.createElement('div'); - this.dom.shadowTopLeft = document.createElement('div'); - this.dom.shadowBottomLeft = document.createElement('div'); - this.dom.shadowTopRight = document.createElement('div'); - this.dom.shadowBottomRight = document.createElement('div'); - - this.dom.root.className = 'vis timeline root'; - this.dom.background.className = 'vispanel background'; - this.dom.backgroundVertical.className = 'vispanel background vertical'; - this.dom.backgroundHorizontal.className = 'vispanel background horizontal'; - this.dom.centerContainer.className = 'vispanel center'; - this.dom.leftContainer.className = 'vispanel left'; - this.dom.rightContainer.className = 'vispanel right'; - this.dom.top.className = 'vispanel top'; - this.dom.bottom.className = 'vispanel bottom'; - this.dom.left.className = 'content'; - this.dom.center.className = 'content'; - this.dom.right.className = 'content'; - this.dom.shadowTop.className = 'shadow top'; - this.dom.shadowBottom.className = 'shadow bottom'; - this.dom.shadowTopLeft.className = 'shadow top'; - this.dom.shadowBottomLeft.className = 'shadow bottom'; - this.dom.shadowTopRight.className = 'shadow top'; - this.dom.shadowBottomRight.className = 'shadow bottom'; - - this.dom.root.appendChild(this.dom.background); - this.dom.root.appendChild(this.dom.backgroundVertical); - this.dom.root.appendChild(this.dom.backgroundHorizontal); - this.dom.root.appendChild(this.dom.centerContainer); - this.dom.root.appendChild(this.dom.leftContainer); - this.dom.root.appendChild(this.dom.rightContainer); - this.dom.root.appendChild(this.dom.top); - this.dom.root.appendChild(this.dom.bottom); - - this.dom.centerContainer.appendChild(this.dom.center); - this.dom.leftContainer.appendChild(this.dom.left); - this.dom.rightContainer.appendChild(this.dom.right); - - this.dom.centerContainer.appendChild(this.dom.shadowTop); - this.dom.centerContainer.appendChild(this.dom.shadowBottom); - this.dom.leftContainer.appendChild(this.dom.shadowTopLeft); - this.dom.leftContainer.appendChild(this.dom.shadowBottomLeft); - this.dom.rightContainer.appendChild(this.dom.shadowTopRight); - this.dom.rightContainer.appendChild(this.dom.shadowBottomRight); - - this.on('rangechange', this.redraw.bind(this)); - this.on('touch', this._onTouch.bind(this)); - this.on('pinch', this._onPinch.bind(this)); - this.on('dragstart', this._onDragStart.bind(this)); - this.on('drag', this._onDrag.bind(this)); - + LineGraph.prototype.setGroups = function(groups) { var me = this; - this.on('change', function (properties) { - if (properties && properties.queue == true) { - // redraw once on next tick - if (!me._redrawTimer) { - me._redrawTimer = setTimeout(function () { - me._redrawTimer = null; - me.redraw(); - }, 0) - } - } - else { - // redraw immediately - me.redraw(); - } - }); + var ids; - // create event listeners for all interesting events, these events will be - // emitted via emitter - this.hammer = Hammer(this.dom.root, { - preventDefault: true - }); - this.listeners = {}; + // unsubscribe from current dataset + if (this.groupsData) { + util.forEach(this.groupListeners, function (callback, event) { + me.groupsData.unsubscribe(event, callback); + }); - var events = [ - 'touch', 'pinch', - 'tap', 'doubletap', 'hold', - 'dragstart', 'drag', 'dragend', - 'mousewheel', 'DOMMouseScroll' // DOMMouseScroll is needed for Firefox - ]; - events.forEach(function (event) { - var listener = function () { - var args = [event].concat(Array.prototype.slice.call(arguments, 0)); - if (me.isActive()) { - me.emit.apply(me, args); - } - }; - me.hammer.on(event, listener); - me.listeners[event] = listener; - }); + // remove all drawn groups + ids = this.groupsData.getIds(); + this.groupsData = null; + this._onRemoveGroups(ids); // note: this will cause a redraw + } - // size properties of each of the panels - this.props = { - root: {}, - background: {}, - centerContainer: {}, - leftContainer: {}, - rightContainer: {}, - center: {}, - left: {}, - right: {}, - top: {}, - bottom: {}, - border: {}, - scrollTop: 0, - scrollTopMin: 0 - }; - this.touch = {}; // store state information needed for touch events + // replace the dataset + if (!groups) { + this.groupsData = null; + } + else if (groups instanceof DataSet || groups instanceof DataView) { + this.groupsData = groups; + } + else { + throw new TypeError('Data must be an instance of DataSet or DataView'); + } - this.redrawCount = 0; + if (this.groupsData) { + // subscribe to new dataset + var id = this.id; + util.forEach(this.groupListeners, function (callback, event) { + me.groupsData.on(event, callback, id); + }); - // attach the root panel to the provided container - if (!container) throw new Error('No container provided'); - container.appendChild(this.dom.root); + // draw all ms + ids = this.groupsData.getIds(); + this._onAddGroups(ids); + } + this._onUpdate(); }; + /** - * Set options. Options will be passed to all components loaded in the Timeline. - * @param {Object} [options] - * {String} orientation - * Vertical orientation for the Timeline, - * can be 'bottom' (default) or 'top'. - * {String | Number} width - * Width for the timeline, a number in pixels or - * a css string like '1000px' or '75%'. '100%' by default. - * {String | Number} height - * Fixed height for the Timeline, a number in pixels or - * a css string like '400px' or '75%'. If undefined, - * The Timeline will automatically size such that - * its contents fit. - * {String | Number} minHeight - * Minimum height for the Timeline, a number in pixels or - * a css string like '400px' or '75%'. - * {String | Number} maxHeight - * Maximum height for the Timeline, a number in pixels or - * a css string like '400px' or '75%'. - * {Number | Date | String} start - * Start date for the visible window - * {Number | Date | String} end - * End date for the visible window + * Update the data + * @param [ids] + * @private */ - Core.prototype.setOptions = function (options) { - if (options) { - // copy the known options - var fields = ['width', 'height', 'minHeight', 'maxHeight', 'autoResize', 'start', 'end', 'orientation', 'clickToUse', 'dataAttributes', 'hiddenDates']; - util.selectiveExtend(fields, this.options, options); + LineGraph.prototype._onUpdate = function(ids) { + this._updateUngrouped(); + this._updateAllGroupData(); + //this._updateGraph(); + this.redraw(true); + }; + LineGraph.prototype._onAdd = function (ids) {this._onUpdate(ids);}; + LineGraph.prototype._onRemove = function (ids) {this._onUpdate(ids);}; + LineGraph.prototype._onUpdateGroups = function (groupIds) { + for (var i = 0; i < groupIds.length; i++) { + var group = this.groupsData.get(groupIds[i]); + this._updateGroup(group, groupIds[i]); + } - if ('hiddenDates' in this.options) { - DateUtil.convertHiddenOptions(this.body, this.options.hiddenDates); - } + //this._updateGraph(); + this.redraw(true); + }; + LineGraph.prototype._onAddGroups = function (groupIds) {this._onUpdateGroups(groupIds);}; - if ('clickToUse' in options) { - if (options.clickToUse) { - if (!this.activator) { - this.activator = new Activator(this.dom.root); - } + + /** + * this cleans the group out off the legends and the dataaxis, updates the ungrouped and updates the graph + * @param {Array} groupIds + * @private + */ + LineGraph.prototype._onRemoveGroups = function (groupIds) { + for (var i = 0; i < groupIds.length; i++) { + if (this.groups.hasOwnProperty(groupIds[i])) { + if (this.groups[groupIds[i]].options.yAxisOrientation == 'right') { + this.yAxisRight.removeGroup(groupIds[i]); + this.legendRight.removeGroup(groupIds[i]); + this.legendRight.redraw(); } else { - if (this.activator) { - this.activator.destroy(); - delete this.activator; - } + this.yAxisLeft.removeGroup(groupIds[i]); + this.legendLeft.removeGroup(groupIds[i]); + this.legendLeft.redraw(); } + delete this.groups[groupIds[i]]; } - - // enable/disable autoResize - this._initAutoResize(); - } - - // propagate options to all components - this.components.forEach(function (component) { - component.setOptions(options); - }); - - // TODO: remove deprecation error one day (deprecated since version 0.8.0) - if (options && options.order) { - throw new Error('Option order is deprecated. There is no replacement for this feature.'); } - - // redraw everything - this.redraw(); + this._updateUngrouped(); + //this._updateGraph(); + this.redraw(true); }; - /** - * Returns true when the Timeline is active. - * @returns {boolean} - */ - Core.prototype.isActive = function () { - return !this.activator || this.activator.active; - }; /** - * Destroy the Core, clean up all DOM elements and event listeners. + * update a group object with the group dataset entree + * + * @param group + * @param groupId + * @private */ - Core.prototype.destroy = function () { - // unbind datasets - this.clear(); - - // remove all event listeners - this.off(); - - // stop checking for changed size - this._stopAutoResize(); - - // remove from DOM - if (this.dom.root.parentNode) { - this.dom.root.parentNode.removeChild(this.dom.root); - } - this.dom = null; - - // remove Activator - if (this.activator) { - this.activator.destroy(); - delete this.activator; + LineGraph.prototype._updateGroup = function (group, groupId) { + if (!this.groups.hasOwnProperty(groupId)) { + this.groups[groupId] = new GraphGroup(group, groupId, this.options, this.groupsUsingDefaultStyles); + if (this.groups[groupId].options.yAxisOrientation == 'right') { + this.yAxisRight.addGroup(groupId, this.groups[groupId]); + this.legendRight.addGroup(groupId, this.groups[groupId]); + } + else { + this.yAxisLeft.addGroup(groupId, this.groups[groupId]); + this.legendLeft.addGroup(groupId, this.groups[groupId]); + } } - - // cleanup hammer touch events - for (var event in this.listeners) { - if (this.listeners.hasOwnProperty(event)) { - delete this.listeners[event]; + else { + this.groups[groupId].update(group); + if (this.groups[groupId].options.yAxisOrientation == 'right') { + this.yAxisRight.updateGroup(groupId, this.groups[groupId]); + this.legendRight.updateGroup(groupId, this.groups[groupId]); + } + else { + this.yAxisLeft.updateGroup(groupId, this.groups[groupId]); + this.legendLeft.updateGroup(groupId, this.groups[groupId]); } } - this.listeners = null; - this.hammer = null; - - // give all components the opportunity to cleanup - this.components.forEach(function (component) { - component.destroy(); - }); - - this.body = null; + this.legendLeft.redraw(); + this.legendRight.redraw(); }; /** - * Set a custom time bar - * @param {Date} time + * this updates all groups, it is used when there is an update the the itemset. + * + * @private */ - Core.prototype.setCustomTime = function (time) { - if (!this.customTime) { - throw new Error('Cannot get custom time: Custom time bar is not enabled'); + LineGraph.prototype._updateAllGroupData = function () { + if (this.itemsData != null) { + var groupsContent = {}; + var groupId; + for (groupId in this.groups) { + if (this.groups.hasOwnProperty(groupId)) { + groupsContent[groupId] = []; + } + } + for (var itemId in this.itemsData._data) { + if (this.itemsData._data.hasOwnProperty(itemId)) { + var item = this.itemsData._data[itemId]; + if (groupsContent[item.group] === undefined) { + throw new Error('Cannot find referenced group. Possible reason: items added before groups? Groups need to be added before items, as items refer to groups.') + } + item.x = util.convert(item.x,'Date'); + groupsContent[item.group].push(item); + } + } + for (groupId in this.groups) { + if (this.groups.hasOwnProperty(groupId)) { + this.groups[groupId].setItems(groupsContent[groupId]); + } + } } - - this.customTime.setCustomTime(time); }; + /** - * Retrieve the current custom time. - * @return {Date} customTime + * Create or delete the group holding all ungrouped items. This group is used when + * there are no groups specified. This anonymous group is called 'graph'. + * @protected */ - Core.prototype.getCustomTime = function() { - if (!this.customTime) { - throw new Error('Cannot get custom time: Custom time bar is not enabled'); + LineGraph.prototype._updateUngrouped = function() { + if (this.itemsData && this.itemsData != null) { + var ungroupedCounter = 0; + for (var itemId in this.itemsData._data) { + if (this.itemsData._data.hasOwnProperty(itemId)) { + var item = this.itemsData._data[itemId]; + if (item != undefined) { + if (item.hasOwnProperty('group')) { + if (item.group === undefined) { + item.group = UNGROUPED; + } + } + else { + item.group = UNGROUPED; + } + ungroupedCounter = item.group == UNGROUPED ? ungroupedCounter + 1 : ungroupedCounter; + } + } + } + + if (ungroupedCounter == 0) { + delete this.groups[UNGROUPED]; + this.legendLeft.removeGroup(UNGROUPED); + this.legendRight.removeGroup(UNGROUPED); + this.yAxisLeft.removeGroup(UNGROUPED); + this.yAxisRight.removeGroup(UNGROUPED); + } + else { + var group = {id: UNGROUPED, content: this.options.defaultGroup}; + this._updateGroup(group, UNGROUPED); + } + } + else { + delete this.groups[UNGROUPED]; + this.legendLeft.removeGroup(UNGROUPED); + this.legendRight.removeGroup(UNGROUPED); + this.yAxisLeft.removeGroup(UNGROUPED); + this.yAxisRight.removeGroup(UNGROUPED); } - return this.customTime.getCustomTime(); + this.legendLeft.redraw(); + this.legendRight.redraw(); }; /** - * Get the id's of the currently visible items. - * @returns {Array} The ids of the visible items + * Redraw the component, mandatory function + * @return {boolean} Returns true if the component is resized */ - Core.prototype.getVisibleItems = function() { - return this.itemSet && this.itemSet.getVisibleItems() || []; - }; - + LineGraph.prototype.redraw = function(forceGraphUpdate) { + var resized = false; + // calculate actual size and position + this.props.width = this.dom.frame.offsetWidth; + this.props.height = this.body.domProps.centerContainer.height; - /** - * Clear the Core. By Default, items, groups and options are cleared. - * Example usage: - * - * timeline.clear(); // clear items, groups, and options - * timeline.clear({options: true}); // clear options only - * - * @param {Object} [what] Optionally specify what to clear. By default: - * {items: true, groups: true, options: true} - */ - Core.prototype.clear = function(what) { - // clear items - if (!what || what.items) { - this.setItems(null); + // update the graph if there is no lastWidth or with, used for the initial draw + if (this.lastWidth === undefined && this.props.width) { + forceGraphUpdate = true; } - // clear groups - if (!what || what.groups) { - this.setGroups(null); - } + // check if this component is resized + resized = this._isResized() || resized; - // clear options of timeline and of each of the components - if (!what || what.options) { - this.components.forEach(function (component) { - component.setOptions(component.defaultOptions); - }); + // check whether zoomed (in that case we need to re-stack everything) + var visibleInterval = this.body.range.end - this.body.range.start; + var zoomed = (visibleInterval != this.lastVisibleInterval); + this.lastVisibleInterval = visibleInterval; - this.setOptions(this.defaultOptions); // this will also do a redraw - } - }; - /** - * Set Core window such that it fits all items - * @param {Object} [options] Available options: - * `animate: boolean | number` - * If true (default), the range is animated - * smoothly to the new window. - * If a number, the number is taken as duration - * for the animation. Default duration is 500 ms. - */ - Core.prototype.fit = function(options) { - var range = this._getDataRange(); + // the svg element is three times as big as the width, this allows for fully dragging left and right + // without reloading the graph. the controls for this are bound to events in the constructor + if (resized == true) { + this.svg.style.width = util.option.asSize(3*this.props.width); + this.svg.style.left = util.option.asSize(-this.props.width); - // skip range set if there is no start and end date - if (range.start === null && range.end === null) { - return; + // if the height of the graph is set as proportional, change the height of the svg + if ((this.options.height + '').indexOf("%") != -1 || this.updateSVGheightOnResize == true) { + this.updateSVGheight = true; + } } - var animate = (options && options.animate !== undefined) ? options.animate : true; - this.range.setRange(range.start, range.end, animate); + // update the height of the graph on each redraw of the graph. + if (this.updateSVGheight == true) { + if (this.options.graphHeight != this.body.domProps.centerContainer.height + 'px') { + this.options.graphHeight = this.body.domProps.centerContainer.height + 'px'; + this.svg.style.height = this.body.domProps.centerContainer.height + 'px'; + } + this.updateSVGheight = false; + } + else { + this.svg.style.height = ('' + this.options.graphHeight).replace('px','') + 'px'; + } + + // zoomed is here to ensure that animations are shown correctly. + if (resized == true || zoomed == true || this.abortedGraphUpdate == true || forceGraphUpdate == true) { + resized = this._updateGraph() || resized; + } + else { + // move the whole svg while dragging + if (this.lastStart != 0) { + var offset = this.body.range.start - this.lastStart; + var range = this.body.range.end - this.body.range.start; + if (this.props.width != 0) { + var rangePerPixelInv = this.props.width/range; + var xOffset = offset * rangePerPixelInv; + this.svg.style.left = (-this.props.width - xOffset) + 'px'; + } + } + } + + this.legendLeft.redraw(); + this.legendRight.redraw(); + return resized; }; + /** - * Calculate the data range of the items and applies a 5% window around it. - * @returns {{start: Date | null, end: Date | null}} - * @protected + * Update and redraw the graph. + * */ - Core.prototype._getDataRange = function() { - // apply the data range as range - var dataRange = this.getItemRange(); + LineGraph.prototype._updateGraph = function () { + // reset the svg elements + DOMutil.prepareElements(this.svgElements); + if (this.props.width != 0 && this.itemsData != null) { + var group, i; + var preprocessedGroupData = {}; + var processedGroupData = {}; + var groupRanges = {}; + var changeCalled = false; - // add 5% space on both sides - var start = dataRange.min; - var end = dataRange.max; - if (start != null && end != null) { - var interval = (end.valueOf() - start.valueOf()); - if (interval <= 0) { - // prevent an empty interval - interval = 24 * 60 * 60 * 1000; // 1 day + // getting group Ids + var groupIds = []; + for (var groupId in this.groups) { + if (this.groups.hasOwnProperty(groupId)) { + group = this.groups[groupId]; + if (group.visible == true && (this.options.groups.visibility[groupId] === undefined || this.options.groups.visibility[groupId] == true)) { + groupIds.push(groupId); + } + } } - start = new Date(start.valueOf() - interval * 0.05); - end = new Date(end.valueOf() + interval * 0.05); - } + if (groupIds.length > 0) { + // this is the range of the SVG canvas + var minDate = this.body.util.toGlobalTime(-this.body.domProps.root.width); + var maxDate = this.body.util.toGlobalTime(2 * this.body.domProps.root.width); + var groupsData = {}; + // fill groups data, this only loads the data we require based on the timewindow + this._getRelevantData(groupIds, groupsData, minDate, maxDate); - return { - start: start, - end: end + // apply sampling, if disabled, it will pass through this function. + this._applySampling(groupIds, groupsData); + + // we transform the X coordinates to detect collisions + for (i = 0; i < groupIds.length; i++) { + preprocessedGroupData[groupIds[i]] = this._convertXcoordinates(groupsData[groupIds[i]]); + } + + // now all needed data has been collected we start the processing. + this._getYRanges(groupIds, preprocessedGroupData, groupRanges); + + // update the Y axis first, we use this data to draw at the correct Y points + // changeCalled is required to clean the SVG on a change emit. + changeCalled = this._updateYAxis(groupIds, groupRanges); + var MAX_CYCLES = 5; + if (changeCalled == true && this.COUNTER < MAX_CYCLES) { + DOMutil.cleanupElements(this.svgElements); + this.abortedGraphUpdate = true; + this.COUNTER++; + this.body.emitter.emit('change'); + return true; + } + else { + if (this.COUNTER > MAX_CYCLES) { + console.log("WARNING: there may be an infinite loop in the _updateGraph emitter cycle.") + } + this.COUNTER = 0; + this.abortedGraphUpdate = false; + + // With the yAxis scaled correctly, use this to get the Y values of the points. + for (i = 0; i < groupIds.length; i++) { + group = this.groups[groupIds[i]]; + processedGroupData[groupIds[i]] = this._convertYcoordinates(groupsData[groupIds[i]], group); + } + + // draw the groups + for (i = 0; i < groupIds.length; i++) { + group = this.groups[groupIds[i]]; + if (group.options.style != 'bar') { // bar needs to be drawn enmasse + group.draw(processedGroupData[groupIds[i]], group, this.framework); + } + } + BarGraphFunctions.draw(groupIds, processedGroupData, this.framework); + } + } } + + // cleanup unused svg elements + DOMutil.cleanupElements(this.svgElements); + return false; }; + /** - * Set the visible window. Both parameters are optional, you can change only - * start or only end. Syntax: - * - * TimeLine.setWindow(start, end) - * TimeLine.setWindow(range) - * - * Where start and end can be a Date, number, or string, and range is an - * object with properties start and end. + * first select and preprocess the data from the datasets. + * the groups have their preselection of data, we now loop over this data to see + * what data we need to draw. Sorted data is much faster. + * more optimization is possible by doing the sampling before and using the binary search + * to find the end date to determine the increment. * - * @param {Date | Number | String | Object} [start] Start date of visible window - * @param {Date | Number | String} [end] End date of visible window - * @param {Object} [options] Available options: - * `animate: boolean | number` - * If true (default), the range is animated - * smoothly to the new window. - * If a number, the number is taken as duration - * for the animation. Default duration is 500 ms. + * @param {array} groupIds + * @param {object} groupsData + * @param {date} minDate + * @param {date} maxDate + * @private */ - Core.prototype.setWindow = function(start, end, options) { - var animate = (options && options.animate !== undefined) ? options.animate : true; - if (arguments.length == 1) { - var range = arguments[0]; - this.range.setRange(range.start, range.end, animate); - } - else { - this.range.setRange(start, end, animate); + LineGraph.prototype._getRelevantData = function (groupIds, groupsData, minDate, maxDate) { + var group, i, j, item; + if (groupIds.length > 0) { + for (i = 0; i < groupIds.length; i++) { + group = this.groups[groupIds[i]]; + groupsData[groupIds[i]] = []; + var dataContainer = groupsData[groupIds[i]]; + // optimization for sorted data + if (group.options.sort == true) { + var guess = Math.max(0, util.binarySearchValue(group.itemsData, minDate, 'x', 'before')); + for (j = guess; j < group.itemsData.length; j++) { + item = group.itemsData[j]; + if (item !== undefined) { + if (item.x > maxDate) { + dataContainer.push(item); + break; + } + else { + dataContainer.push(item); + } + } + } + } + else { + for (j = 0; j < group.itemsData.length; j++) { + item = group.itemsData[j]; + if (item !== undefined) { + if (item.x > minDate && item.x < maxDate) { + dataContainer.push(item); + } + } + } + } + } } }; + /** - * Move the window such that given time is centered on screen. - * @param {Date | Number | String} time - * @param {Object} [options] Available options: - * `animate: boolean | number` - * If true (default), the range is animated - * smoothly to the new window. - * If a number, the number is taken as duration - * for the animation. Default duration is 500 ms. + * + * @param groupIds + * @param groupsData + * @private */ - Core.prototype.moveTo = function(time, options) { - var interval = this.range.end - this.range.start; - var t = util.convert(time, 'Date').valueOf(); + LineGraph.prototype._applySampling = function (groupIds, groupsData) { + var group; + if (groupIds.length > 0) { + for (var i = 0; i < groupIds.length; i++) { + group = this.groups[groupIds[i]]; + if (group.options.sampling == true) { + var dataContainer = groupsData[groupIds[i]]; + if (dataContainer.length > 0) { + var increment = 1; + var amountOfPoints = dataContainer.length; - var start = t - interval / 2; - var end = t + interval / 2; - var animate = (options && options.animate !== undefined) ? options.animate : true; + // the global screen is used because changing the width of the yAxis may affect the increment, resulting in an endless loop + // of width changing of the yAxis. + var xDistance = this.body.util.toGlobalScreen(dataContainer[dataContainer.length - 1].x) - this.body.util.toGlobalScreen(dataContainer[0].x); + var pointsPerPixel = amountOfPoints / xDistance; + increment = Math.min(Math.ceil(0.2 * amountOfPoints), Math.max(1, Math.round(pointsPerPixel))); - this.range.setRange(start, end, animate); + var sampledData = []; + for (var j = 0; j < amountOfPoints; j += increment) { + sampledData.push(dataContainer[j]); + + } + groupsData[groupIds[i]] = sampledData; + } + } + } + } }; + /** - * Get the visible window - * @return {{start: Date, end: Date}} Visible range + * + * + * @param {array} groupIds + * @param {object} groupsData + * @param {object} groupRanges | this is being filled here + * @private */ - Core.prototype.getWindow = function() { - var range = this.range.getRange(); - return { - start: new Date(range.start), - end: new Date(range.end) - }; + LineGraph.prototype._getYRanges = function (groupIds, groupsData, groupRanges) { + var groupData, group, i; + var barCombinedDataLeft = []; + var barCombinedDataRight = []; + var options; + if (groupIds.length > 0) { + for (i = 0; i < groupIds.length; i++) { + groupData = groupsData[groupIds[i]]; + options = this.groups[groupIds[i]].options; + if (groupData.length > 0) { + group = this.groups[groupIds[i]]; + // if bar graphs are stacked, their range need to be handled differently and accumulated over all groups. + if (options.barChart.handleOverlap == 'stack' && options.style == 'bar') { + if (options.yAxisOrientation == 'left') {barCombinedDataLeft = barCombinedDataLeft.concat(group.getYRange(groupData)) ;} + else {barCombinedDataRight = barCombinedDataRight.concat(group.getYRange(groupData));} + } + else { + groupRanges[groupIds[i]] = group.getYRange(groupData,groupIds[i]); + } + } + } + + // if bar graphs are stacked, their range need to be handled differently and accumulated over all groups. + BarGraphFunctions.getStackedBarYRange(barCombinedDataLeft , groupRanges, groupIds, '__barchartLeft' , 'left' ); + BarGraphFunctions.getStackedBarYRange(barCombinedDataRight, groupRanges, groupIds, '__barchartRight', 'right'); + } }; + /** - * Force a redraw of the Core. Can be useful to manually redraw when - * option autoResize=false + * this sets the Y ranges for the Y axis. It also determines which of the axis should be shown or hidden. + * @param {Array} groupIds + * @param {Object} groupRanges + * @private */ - Core.prototype.redraw = function() { + LineGraph.prototype._updateYAxis = function (groupIds, groupRanges) { var resized = false; - var options = this.options; - var props = this.props; - var dom = this.dom; + var yAxisLeftUsed = false; + var yAxisRightUsed = false; + var minLeft = 1e9, minRight = 1e9, maxLeft = -1e9, maxRight = -1e9, minVal, maxVal; + // if groups are present + if (groupIds.length > 0) { + // this is here to make sure that if there are no items in the axis but there are groups, that there is no infinite draw/redraw loop. + for (var i = 0; i < groupIds.length; i++) { + var group = this.groups[groupIds[i]]; + if (group && group.options.yAxisOrientation != 'right') { + yAxisLeftUsed = true; + minLeft = 0; + maxLeft = 0; + } + else if (group && group.options.yAxisOrientation) { + yAxisRightUsed = true; + minRight = 0; + maxRight = 0; + } + } - if (!dom) return; // when destroyed + // if there are items: + for (var i = 0; i < groupIds.length; i++) { + if (groupRanges.hasOwnProperty(groupIds[i])) { + if (groupRanges[groupIds[i]].ignore !== true) { + minVal = groupRanges[groupIds[i]].min; + maxVal = groupRanges[groupIds[i]].max; - DateUtil.updateHiddenDates(this.body, this.options.hiddenDates); + if (groupRanges[groupIds[i]].yAxisOrientation != 'right') { + yAxisLeftUsed = true; + minLeft = minLeft > minVal ? minVal : minLeft; + maxLeft = maxLeft < maxVal ? maxVal : maxLeft; + } + else { + yAxisRightUsed = true; + minRight = minRight > minVal ? minVal : minRight; + maxRight = maxRight < maxVal ? maxVal : maxRight; + } + } + } + } - // update class names - if (options.orientation == 'top') { - util.addClassName(dom.root, 'top'); - util.removeClassName(dom.root, 'bottom'); + if (yAxisLeftUsed == true) { + this.yAxisLeft.setRange(minLeft, maxLeft); + } + if (yAxisRightUsed == true) { + this.yAxisRight.setRange(minRight, maxRight); + } + } + resized = this._toggleAxisVisiblity(yAxisLeftUsed , this.yAxisLeft) || resized; + resized = this._toggleAxisVisiblity(yAxisRightUsed, this.yAxisRight) || resized; + + if (yAxisRightUsed == true && yAxisLeftUsed == true) { + this.yAxisLeft.drawIcons = true; + this.yAxisRight.drawIcons = true; } else { - util.removeClassName(dom.root, 'top'); - util.addClassName(dom.root, 'bottom'); + this.yAxisLeft.drawIcons = false; + this.yAxisRight.drawIcons = false; } + this.yAxisRight.master = !yAxisLeftUsed; + if (this.yAxisRight.master == false) { + if (yAxisRightUsed == true) {this.yAxisLeft.lineOffset = this.yAxisRight.width;} + else {this.yAxisLeft.lineOffset = 0;} - // update root width and height options - dom.root.style.maxHeight = util.option.asSize(options.maxHeight, ''); - dom.root.style.minHeight = util.option.asSize(options.minHeight, ''); - dom.root.style.width = util.option.asSize(options.width, ''); - - // calculate border widths - props.border.left = (dom.centerContainer.offsetWidth - dom.centerContainer.clientWidth) / 2; - props.border.right = props.border.left; - props.border.top = (dom.centerContainer.offsetHeight - dom.centerContainer.clientHeight) / 2; - props.border.bottom = props.border.top; - var borderRootHeight= dom.root.offsetHeight - dom.root.clientHeight; - var borderRootWidth = dom.root.offsetWidth - dom.root.clientWidth; + resized = this.yAxisLeft.redraw() || resized; + this.yAxisRight.stepPixelsForced = this.yAxisLeft.stepPixels; + this.yAxisRight.zeroCrossing = this.yAxisLeft.zeroCrossing; + resized = this.yAxisRight.redraw() || resized; + } + else { + resized = this.yAxisRight.redraw() || resized; + } - // workaround for a bug in IE: the clientWidth of an element with - // a height:0px and overflow:hidden is not calculated and always has value 0 - if (dom.centerContainer.clientHeight === 0) { - props.border.left = props.border.top; - props.border.right = props.border.left; + // clean the accumulated lists + if (groupIds.indexOf('__barchartLeft') != -1) { + groupIds.splice(groupIds.indexOf('__barchartLeft'),1); } - if (dom.root.clientHeight === 0) { - borderRootWidth = borderRootHeight; + if (groupIds.indexOf('__barchartRight') != -1) { + groupIds.splice(groupIds.indexOf('__barchartRight'),1); } - // calculate the heights. If any of the side panels is empty, we set the height to - // minus the border width, such that the border will be invisible - props.center.height = dom.center.offsetHeight; - props.left.height = dom.left.offsetHeight; - props.right.height = dom.right.offsetHeight; - props.top.height = dom.top.clientHeight || -props.border.top; - props.bottom.height = dom.bottom.clientHeight || -props.border.bottom; + return resized; + }; - // TODO: compensate borders when any of the panels is empty. - // apply auto height - // TODO: only calculate autoHeight when needed (else we cause an extra reflow/repaint of the DOM) - var contentHeight = Math.max(props.left.height, props.center.height, props.right.height); - var autoHeight = props.top.height + contentHeight + props.bottom.height + - borderRootHeight + props.border.top + props.border.bottom; - dom.root.style.height = util.option.asSize(options.height, autoHeight + 'px'); + /** + * This shows or hides the Y axis if needed. If there is a change, the changed event is emitted by the updateYAxis function + * + * @param {boolean} axisUsed + * @returns {boolean} + * @private + * @param axis + */ + LineGraph.prototype._toggleAxisVisiblity = function (axisUsed, axis) { + var changed = false; + if (axisUsed == false) { + if (axis.dom.frame.parentNode && axis.hidden == false) { + axis.hide() + changed = true; + } + } + else { + if (!axis.dom.frame.parentNode && axis.hidden == true) { + axis.show(); + changed = true; + } + } + return changed; + }; - // calculate heights of the content panels - props.root.height = dom.root.offsetHeight; - props.background.height = props.root.height - borderRootHeight; - var containerHeight = props.root.height - props.top.height - props.bottom.height - - borderRootHeight; - props.centerContainer.height = containerHeight; - props.leftContainer.height = containerHeight; - props.rightContainer.height = props.leftContainer.height; - // calculate the widths of the panels - props.root.width = dom.root.offsetWidth; - props.background.width = props.root.width - borderRootWidth; - props.left.width = dom.leftContainer.clientWidth || -props.border.left; - props.leftContainer.width = props.left.width; - props.right.width = dom.rightContainer.clientWidth || -props.border.right; - props.rightContainer.width = props.right.width; - var centerWidth = props.root.width - props.left.width - props.right.width - borderRootWidth; - props.center.width = centerWidth; - props.centerContainer.width = centerWidth; - props.top.width = centerWidth; - props.bottom.width = centerWidth; + /** + * This uses the DataAxis object to generate the correct X coordinate on the SVG window. It uses the + * util function toScreen to get the x coordinate from the timestamp. It also pre-filters the data and get the minMax ranges for + * the yAxis. + * + * @param datapoints + * @returns {Array} + * @private + */ + LineGraph.prototype._convertXcoordinates = function (datapoints) { + var extractedData = []; + var xValue, yValue; + var toScreen = this.body.util.toScreen; - // resize the panels - dom.background.style.height = props.background.height + 'px'; - dom.backgroundVertical.style.height = props.background.height + 'px'; - dom.backgroundHorizontal.style.height = props.centerContainer.height + 'px'; - dom.centerContainer.style.height = props.centerContainer.height + 'px'; - dom.leftContainer.style.height = props.leftContainer.height + 'px'; - dom.rightContainer.style.height = props.rightContainer.height + 'px'; + for (var i = 0; i < datapoints.length; i++) { + xValue = toScreen(datapoints[i].x) + this.props.width; + yValue = datapoints[i].y; + extractedData.push({x: xValue, y: yValue}); + } - dom.background.style.width = props.background.width + 'px'; - dom.backgroundVertical.style.width = props.centerContainer.width + 'px'; - dom.backgroundHorizontal.style.width = props.background.width + 'px'; - dom.centerContainer.style.width = props.center.width + 'px'; - dom.top.style.width = props.top.width + 'px'; - dom.bottom.style.width = props.bottom.width + 'px'; + return extractedData; + }; - // reposition the panels - dom.background.style.left = '0'; - dom.background.style.top = '0'; - dom.backgroundVertical.style.left = (props.left.width + props.border.left) + 'px'; - dom.backgroundVertical.style.top = '0'; - dom.backgroundHorizontal.style.left = '0'; - dom.backgroundHorizontal.style.top = props.top.height + 'px'; - dom.centerContainer.style.left = props.left.width + 'px'; - dom.centerContainer.style.top = props.top.height + 'px'; - dom.leftContainer.style.left = '0'; - dom.leftContainer.style.top = props.top.height + 'px'; - dom.rightContainer.style.left = (props.left.width + props.center.width) + 'px'; - dom.rightContainer.style.top = props.top.height + 'px'; - dom.top.style.left = props.left.width + 'px'; - dom.top.style.top = '0'; - dom.bottom.style.left = props.left.width + 'px'; - dom.bottom.style.top = (props.top.height + props.centerContainer.height) + 'px'; - - // update the scrollTop, feasible range for the offset can be changed - // when the height of the Core or of the contents of the center changed - this._updateScrollTop(); - // reposition the scrollable contents - var offset = this.props.scrollTop; - if (options.orientation == 'bottom') { - offset += Math.max(this.props.centerContainer.height - this.props.center.height - - this.props.border.top - this.props.border.bottom, 0); + /** + * This uses the DataAxis object to generate the correct X coordinate on the SVG window. It uses the + * util function toScreen to get the x coordinate from the timestamp. It also pre-filters the data and get the minMax ranges for + * the yAxis. + * + * @param datapoints + * @param group + * @returns {Array} + * @private + */ + LineGraph.prototype._convertYcoordinates = function (datapoints, group) { + var extractedData = []; + var xValue, yValue; + var toScreen = this.body.util.toScreen; + var axis = this.yAxisLeft; + var svgHeight = Number(this.svg.style.height.replace('px','')); + if (group.options.yAxisOrientation == 'right') { + axis = this.yAxisRight; } - dom.center.style.left = '0'; - dom.center.style.top = offset + 'px'; - dom.left.style.left = '0'; - dom.left.style.top = offset + 'px'; - dom.right.style.left = '0'; - dom.right.style.top = offset + 'px'; - - // show shadows when vertical scrolling is available - var visibilityTop = this.props.scrollTop == 0 ? 'hidden' : ''; - var visibilityBottom = this.props.scrollTop == this.props.scrollTopMin ? 'hidden' : ''; - dom.shadowTop.style.visibility = visibilityTop; - dom.shadowBottom.style.visibility = visibilityBottom; - dom.shadowTopLeft.style.visibility = visibilityTop; - dom.shadowBottomLeft.style.visibility = visibilityBottom; - dom.shadowTopRight.style.visibility = visibilityTop; - dom.shadowBottomRight.style.visibility = visibilityBottom; - // redraw all components - this.components.forEach(function (component) { - resized = component.redraw() || resized; - }); - if (resized) { - // keep repainting until all sizes are settled - var MAX_REDRAWS = 3; // maximum number of consecutive redraws - if (this.redrawCount < MAX_REDRAWS) { - this.redrawCount++; - this.redraw(); - } - else { - console.log('WARNING: infinite loop in redraw?'); - } - this.redrawCount = 0; + for (var i = 0; i < datapoints.length; i++) { + xValue = toScreen(datapoints[i].x) + this.props.width; + yValue = Math.round(axis.convertValue(datapoints[i].y)); + extractedData.push({x: xValue, y: yValue}); } - this.emit("finishedRedraw"); - }; + group.setZeroPosition(Math.min(svgHeight, axis.convertValue(0))); - // TODO: deprecated since version 1.1.0, remove some day - Core.prototype.repaint = function () { - throw new Error('Function repaint is deprecated. Use redraw instead.'); + return extractedData; }; - /** - * Set a current time. This can be used for example to ensure that a client's - * time is synchronized with a shared server time. - * Only applicable when option `showCurrentTime` is true. - * @param {Date | String | Number} time A Date, unix timestamp, or - * ISO date string. - */ - Core.prototype.setCurrentTime = function(time) { - if (!this.currentTime) { - throw new Error('Option showCurrentTime must be true'); - } - this.currentTime.setCurrentTime(time); - }; + module.exports = LineGraph; - /** - * Get the current time. - * Only applicable when option `showCurrentTime` is true. - * @return {Date} Returns the current time. - */ - Core.prototype.getCurrentTime = function() { - if (!this.currentTime) { - throw new Error('Option showCurrentTime must be true'); - } - return this.currentTime.getCurrentTime(); - }; +/***/ }, +/* 44 */ +/***/ function(module, exports, __webpack_require__) { - /** - * Convert a position on screen (pixels) to a datetime - * @param {int} x Position on the screen in pixels - * @return {Date} time The datetime the corresponds with given position x - * @private - */ - // TODO: move this function to Range - Core.prototype._toTime = function(x) { - return DateUtil.toTime(this, x, this.props.center.width); - }; + var util = __webpack_require__(1); + var DOMutil = __webpack_require__(6); + var Component = __webpack_require__(23); + var DataStep = __webpack_require__(45); /** - * Convert a position on the global screen (pixels) to a datetime - * @param {int} x Position on the screen in pixels - * @return {Date} time The datetime the corresponds with given position x - * @private + * A horizontal time axis + * @param {Object} [options] See DataAxis.setOptions for the available + * options. + * @constructor DataAxis + * @extends Component + * @param body */ - // TODO: move this function to Range - Core.prototype._toGlobalTime = function(x) { - return DateUtil.toTime(this, x, this.props.root.width); - //var conversion = this.range.conversion(this.props.root.width); - //return new Date(x / conversion.scale + conversion.offset); - }; + function DataAxis (body, options, svg, linegraphOptions) { + this.id = util.randomUUID(); + this.body = body; - /** - * Convert a datetime (Date object) into a position on the screen - * @param {Date} time A date - * @return {int} x The position on the screen in pixels which corresponds - * with the given date. - * @private - */ - // TODO: move this function to Range - Core.prototype._toScreen = function(time) { - return DateUtil.toScreen(this, time, this.props.center.width); - }; + this.defaultOptions = { + orientation: 'left', // supported: 'left', 'right' + showMinorLabels: true, + showMajorLabels: true, + icons: true, + majorLinesOffset: 7, + minorLinesOffset: 4, + labelOffsetX: 10, + labelOffsetY: 2, + iconWidth: 20, + width: '40px', + visible: true, + alignZeros: true, + customRange: { + left: {min:undefined, max:undefined}, + right: {min:undefined, max:undefined} + }, + title: { + left: {text:undefined}, + right: {text:undefined} + }, + format: { + left: {decimals: undefined}, + right: {decimals: undefined} + } + }; + this.linegraphOptions = linegraphOptions; + this.linegraphSVG = svg; + this.props = {}; + this.DOMelements = { // dynamic elements + lines: {}, + labels: {}, + title: {} + }; + this.dom = {}; - /** - * Convert a datetime (Date object) into a position on the root - * This is used to get the pixel density estimate for the screen, not the center panel - * @param {Date} time A date - * @return {int} x The position on root in pixels which corresponds - * with the given date. - * @private - */ - // TODO: move this function to Range - Core.prototype._toGlobalScreen = function(time) { - return DateUtil.toScreen(this, time, this.props.root.width); - //var conversion = this.range.conversion(this.props.root.width); - //return (time.valueOf() - conversion.offset) * conversion.scale; - }; + this.range = {start:0, end:0}; + this.options = util.extend({}, this.defaultOptions); + this.conversionFactor = 1; - /** - * Initialize watching when option autoResize is true - * @private - */ - Core.prototype._initAutoResize = function () { - if (this.options.autoResize == true) { - this._startAutoResize(); - } - else { - this._stopAutoResize(); - } - }; + this.setOptions(options); + this.width = Number(('' + this.options.width).replace("px","")); + this.minWidth = this.width; + this.height = this.linegraphSVG.offsetHeight; + this.hidden = false; - /** - * Watch for changes in the size of the container. On resize, the Panel will - * automatically redraw itself. - * @private - */ - Core.prototype._startAutoResize = function () { - var me = this; + this.stepPixels = 25; + this.stepPixelsForced = 25; + this.zeroCrossing = -1; - this._stopAutoResize(); + this.lineOffset = 0; + this.master = true; + this.svgElements = {}; + this.iconsRemoved = false; - this._onResize = function() { - if (me.options.autoResize != true) { - // stop watching when the option autoResize is changed to false - me._stopAutoResize(); - return; - } - if (me.dom.root) { - // check whether the frame is resized - // Note: we compare offsetWidth here, not clientWidth. For some reason, - // IE does not restore the clientWidth from 0 to the actual width after - // changing the timeline's container display style from none to visible - if ((me.dom.root.offsetWidth != me.props.lastWidth) || - (me.dom.root.offsetHeight != me.props.lastHeight)) { - me.props.lastWidth = me.dom.root.offsetWidth; - me.props.lastHeight = me.dom.root.offsetHeight; + this.groups = {}; + this.amountOfGroups = 0; - me.emit('change'); - } - } - }; + // create the HTML DOM + this._create(); - // add event listener to window resize - util.addEventListener(window, 'resize', this._onResize); + var me = this; + this.body.emitter.on("verticalDrag", function() { + me.dom.lineContainer.style.top = me.body.domProps.scrollTop + 'px'; + }); + } - this.watchTimer = setInterval(this._onResize, 1000); - }; + DataAxis.prototype = new Component(); - /** - * Stop watching for a resize of the frame. - * @private - */ - Core.prototype._stopAutoResize = function () { - if (this.watchTimer) { - clearInterval(this.watchTimer); - this.watchTimer = undefined; - } - // remove event listener on window.resize - util.removeEventListener(window, 'resize', this._onResize); - this._onResize = null; + DataAxis.prototype.addGroup = function(label, graphOptions) { + if (!this.groups.hasOwnProperty(label)) { + this.groups[label] = graphOptions; + } + this.amountOfGroups += 1; }; - /** - * Start moving the timeline vertically - * @param {Event} event - * @private - */ - Core.prototype._onTouch = function (event) { - this.touch.allowDragging = true; + DataAxis.prototype.updateGroup = function(label, graphOptions) { + this.groups[label] = graphOptions; }; - /** - * Start moving the timeline vertically - * @param {Event} event - * @private - */ - Core.prototype._onPinch = function (event) { - this.touch.allowDragging = false; + DataAxis.prototype.removeGroup = function(label) { + if (this.groups.hasOwnProperty(label)) { + delete this.groups[label]; + this.amountOfGroups -= 1; + } }; - /** - * Start moving the timeline vertically - * @param {Event} event - * @private - */ - Core.prototype._onDragStart = function (event) { - this.touch.initialScrollTop = this.props.scrollTop; + + DataAxis.prototype.setOptions = function (options) { + if (options) { + var redraw = false; + if (this.options.orientation != options.orientation && options.orientation !== undefined) { + redraw = true; + } + var fields = [ + 'orientation', + 'showMinorLabels', + 'showMajorLabels', + 'icons', + 'majorLinesOffset', + 'minorLinesOffset', + 'labelOffsetX', + 'labelOffsetY', + 'iconWidth', + 'width', + 'visible', + 'customRange', + 'title', + 'format', + 'alignZeros' + ]; + util.selectiveExtend(fields, this.options, options); + + this.minWidth = Number(('' + this.options.width).replace("px","")); + + if (redraw == true && this.dom.frame) { + this.hide(); + this.show(); + } + } }; + /** - * Move the timeline vertically - * @param {Event} event - * @private + * Create the HTML DOM for the DataAxis */ - Core.prototype._onDrag = function (event) { - // refuse to drag when we where pinching to prevent the timeline make a jump - // when releasing the fingers in opposite order from the touch screen - if (!this.touch.allowDragging) return; + DataAxis.prototype._create = function() { + this.dom.frame = document.createElement('div'); + this.dom.frame.style.width = this.options.width; + this.dom.frame.style.height = this.height; - var delta = event.gesture.deltaY; + this.dom.lineContainer = document.createElement('div'); + this.dom.lineContainer.style.width = '100%'; + this.dom.lineContainer.style.height = this.height; + this.dom.lineContainer.style.position = 'relative'; - var oldScrollTop = this._getScrollTop(); - var newScrollTop = this._setScrollTop(this.touch.initialScrollTop + delta); + // create svg element for graph drawing. + this.svg = document.createElementNS('http://www.w3.org/2000/svg',"svg"); + this.svg.style.position = "absolute"; + this.svg.style.top = '0px'; + this.svg.style.height = '100%'; + this.svg.style.width = '100%'; + this.svg.style.display = "block"; + this.dom.frame.appendChild(this.svg); + }; + DataAxis.prototype._redrawGroupIcons = function () { + DOMutil.prepareElements(this.svgElements); - if (newScrollTop != oldScrollTop) { - this.redraw(); // TODO: this causes two redraws when dragging, the other is triggered by rangechange already - this.emit("verticalDrag"); + var x; + var iconWidth = this.options.iconWidth; + var iconHeight = 15; + var iconOffset = 4; + var y = iconOffset + 0.5 * iconHeight; + + if (this.options.orientation == 'left') { + x = iconOffset; + } + else { + x = this.width - iconWidth - iconOffset; + } + + for (var groupId in this.groups) { + if (this.groups.hasOwnProperty(groupId)) { + if (this.groups[groupId].visible == true && (this.linegraphOptions.visibility[groupId] === undefined || this.linegraphOptions.visibility[groupId] == true)) { + this.groups[groupId].drawIcon(x, y, this.svgElements, this.svg, iconWidth, iconHeight); + y += iconHeight + iconOffset; + } + } } + + DOMutil.cleanupElements(this.svgElements); + this.iconsRemoved = false; }; + DataAxis.prototype._cleanupIcons = function() { + if (this.iconsRemoved == false) { + DOMutil.prepareElements(this.svgElements); + DOMutil.cleanupElements(this.svgElements); + this.iconsRemoved = true; + } + } + /** - * Apply a scrollTop - * @param {Number} scrollTop - * @returns {Number} scrollTop Returns the applied scrollTop - * @private + * Create the HTML DOM for the DataAxis */ - Core.prototype._setScrollTop = function (scrollTop) { - this.props.scrollTop = scrollTop; - this._updateScrollTop(); - return this.props.scrollTop; + DataAxis.prototype.show = function() { + this.hidden = false; + if (!this.dom.frame.parentNode) { + if (this.options.orientation == 'left') { + this.body.dom.left.appendChild(this.dom.frame); + } + else { + this.body.dom.right.appendChild(this.dom.frame); + } + } + + if (!this.dom.lineContainer.parentNode) { + this.body.dom.backgroundHorizontal.appendChild(this.dom.lineContainer); + } }; /** - * Update the current scrollTop when the height of the containers has been changed - * @returns {Number} scrollTop Returns the applied scrollTop - * @private + * Create the HTML DOM for the DataAxis */ - Core.prototype._updateScrollTop = function () { - // recalculate the scrollTopMin - var scrollTopMin = Math.min(this.props.centerContainer.height - this.props.center.height, 0); // is negative or zero - if (scrollTopMin != this.props.scrollTopMin) { - // in case of bottom orientation, change the scrollTop such that the contents - // do not move relative to the time axis at the bottom - if (this.options.orientation == 'bottom') { - this.props.scrollTop += (scrollTopMin - this.props.scrollTopMin); - } - this.props.scrollTopMin = scrollTopMin; + DataAxis.prototype.hide = function() { + this.hidden = true; + if (this.dom.frame.parentNode) { + this.dom.frame.parentNode.removeChild(this.dom.frame); } - // limit the scrollTop to the feasible scroll range - if (this.props.scrollTop > 0) this.props.scrollTop = 0; - if (this.props.scrollTop < scrollTopMin) this.props.scrollTop = scrollTopMin; - - return this.props.scrollTop; + if (this.dom.lineContainer.parentNode) { + this.dom.lineContainer.parentNode.removeChild(this.dom.lineContainer); + } }; /** - * Get the current scrollTop - * @returns {number} scrollTop - * @private + * Set a range (start and end) + * @param end + * @param start + * @param end */ - Core.prototype._getScrollTop = function () { - return this.props.scrollTop; + DataAxis.prototype.setRange = function (start, end) { + if (this.master == false && this.options.alignZeros == true && this.zeroCrossing != -1) { + if (start > 0) { + start = 0; + } + } + this.range.start = start; + this.range.end = end; }; - module.exports = Core; + /** + * Repaint the component + * @return {boolean} Returns true if the component is resized + */ + DataAxis.prototype.redraw = function () { + var resized = false; + var activeGroups = 0; + + // Make sure the line container adheres to the vertical scrolling. + this.dom.lineContainer.style.top = this.body.domProps.scrollTop + 'px'; + for (var groupId in this.groups) { + if (this.groups.hasOwnProperty(groupId)) { + if (this.groups[groupId].visible == true && (this.linegraphOptions.visibility[groupId] === undefined || this.linegraphOptions.visibility[groupId] == true)) { + activeGroups++; + } + } + } + if (this.amountOfGroups == 0 || activeGroups == 0) { + this.hide(); + } + else { + this.show(); + this.height = Number(this.linegraphSVG.style.height.replace("px","")); -/***/ }, -/* 47 */ -/***/ function(module, exports, __webpack_require__) { + // svg offsetheight did not work in firefox and explorer... + this.dom.lineContainer.style.height = this.height + 'px'; + this.width = this.options.visible == true ? Number(('' + this.options.width).replace("px","")) : 0; - var Hammer = __webpack_require__(45); + var props = this.props; + var frame = this.dom.frame; - /** - * Fake a hammer.js gesture. Event can be a ScrollEvent or MouseMoveEvent - * @param {Element} element - * @param {Event} event - */ - exports.fakeGesture = function(element, event) { - var eventType = null; + // update classname + frame.className = 'dataaxis'; - // for hammer.js 1.0.5 - // var gesture = Hammer.event.collectEventData(this, eventType, event); + // calculate character width and height + this._calculateCharSize(); - // for hammer.js 1.0.6+ - var touches = Hammer.event.getTouchList(event, eventType); - var gesture = Hammer.event.collectEventData(this, eventType, touches, event); + var orientation = this.options.orientation; + var showMinorLabels = this.options.showMinorLabels; + var showMajorLabels = this.options.showMajorLabels; - // on IE in standards mode, no touches are recognized by hammer.js, - // resulting in NaN values for center.pageX and center.pageY - if (isNaN(gesture.center.pageX)) { - gesture.center.pageX = event.pageX; - } - if (isNaN(gesture.center.pageY)) { - gesture.center.pageY = event.pageY; - } + // determine the width and height of the elements for the axis + props.minorLabelHeight = showMinorLabels ? props.minorCharHeight : 0; + props.majorLabelHeight = showMajorLabels ? props.majorCharHeight : 0; - return gesture; - }; + props.minorLineWidth = this.body.dom.backgroundHorizontal.offsetWidth - this.lineOffset - this.width + 2 * this.options.minorLinesOffset; + props.minorLineHeight = 1; + props.majorLineWidth = this.body.dom.backgroundHorizontal.offsetWidth - this.lineOffset - this.width + 2 * this.options.majorLinesOffset; + props.majorLineHeight = 1; + // take frame offline while updating (is almost twice as fast) + if (orientation == 'left') { + frame.style.top = '0'; + frame.style.left = '0'; + frame.style.bottom = ''; + frame.style.width = this.width + 'px'; + frame.style.height = this.height + "px"; + this.props.width = this.body.domProps.left.width; + this.props.height = this.body.domProps.left.height; + } + else { // right + frame.style.top = ''; + frame.style.bottom = '0'; + frame.style.left = '0'; + frame.style.width = this.width + 'px'; + frame.style.height = this.height + "px"; + this.props.width = this.body.domProps.right.width; + this.props.height = this.body.domProps.right.height; + } -/***/ }, -/* 48 */ -/***/ function(module, exports, __webpack_require__) { + resized = this._redrawLabels(); + resized = this._isResized() || resized; - // English - exports['en'] = { - current: 'current', - time: 'time' - }; - exports['en_EN'] = exports['en']; - exports['en_US'] = exports['en']; + if (this.options.icons == true) { + this._redrawGroupIcons(); + } + else { + this._cleanupIcons(); + } - // Dutch - exports['nl'] = { - custom: 'aangepaste', - time: 'tijd' + this._redrawTitle(orientation); + } + return resized; }; - exports['nl_NL'] = exports['nl']; - exports['nl_BE'] = exports['nl']; + /** + * Repaint major and minor text labels and vertical grid lines + * @private + */ + DataAxis.prototype._redrawLabels = function () { + var resized = false; + DOMutil.prepareElements(this.DOMelements.lines); + DOMutil.prepareElements(this.DOMelements.labels); -/***/ }, -/* 49 */ -/***/ function(module, exports, __webpack_require__) { + var orientation = this.options['orientation']; - // English - exports['en'] = { - edit: 'Edit', - del: 'Delete selected', - back: 'Back', - addNode: 'Add Node', - addEdge: 'Add Edge', - editNode: 'Edit Node', - editEdge: 'Edit Edge', - addDescription: 'Click in an empty space to place a new node.', - edgeDescription: 'Click on a node and drag the edge to another node to connect them.', - editEdgeDescription: 'Click on the control points and drag them to a node to connect to it.', - createEdgeError: 'Cannot link edges to a cluster.', - deleteClusterError: 'Clusters cannot be deleted.' - }; - exports['en_EN'] = exports['en']; - exports['en_US'] = exports['en']; + // calculate range and step (step such that we have space for 7 characters per label) + var minimumStep = this.master ? this.props.majorCharHeight || 10 : this.stepPixelsForced; - // Dutch - exports['nl'] = { - edit: 'Wijzigen', - del: 'Selectie verwijderen', - back: 'Terug', - addNode: 'Node toevoegen', - addEdge: 'Link toevoegen', - editNode: 'Node wijzigen', - editEdge: 'Link wijzigen', - addDescription: 'Klik op een leeg gebied om een nieuwe node te maken.', - edgeDescription: 'Klik op een node en sleep de link naar een andere node om ze te verbinden.', - editEdgeDescription: 'Klik op de verbindingspunten en sleep ze naar een node om daarmee te verbinden.', - createEdgeError: 'Kan geen link maken naar een cluster.', - deleteClusterError: 'Clusters kunnen niet worden verwijderd.' - }; - exports['nl_NL'] = exports['nl']; - exports['nl_BE'] = exports['nl']; + var step = new DataStep( + this.range.start, + this.range.end, + minimumStep, + this.dom.frame.offsetHeight, + this.options.customRange[this.options.orientation], + this.master == false && this.options.alignZeros // doess the step have to align zeros? only if not master and the options is on + ); + this.step = step; + // get the distance in pixels for a step + // dead space is space that is "left over" after a step + var stepPixels = (this.dom.frame.offsetHeight - (step.deadSpace * (this.dom.frame.offsetHeight / step.marginRange))) / (((step.marginRange - step.deadSpace) / step.step)); -/***/ }, -/* 50 */ -/***/ function(module, exports, __webpack_require__) { + this.stepPixels = stepPixels; - /** - * Canvas shapes used by Network - */ - if (typeof CanvasRenderingContext2D !== 'undefined') { + var amountOfSteps = this.height / stepPixels; + var stepDifference = 0; - /** - * Draw a circle shape - */ - CanvasRenderingContext2D.prototype.circle = function(x, y, r) { - this.beginPath(); - this.arc(x, y, r, 0, 2*Math.PI, false); - }; + // the slave axis needs to use the same horizontal lines as the master axis. + if (this.master == false) { + stepPixels = this.stepPixelsForced; + stepDifference = Math.round((this.dom.frame.offsetHeight / stepPixels) - amountOfSteps); + for (var i = 0; i < 0.5 * stepDifference; i++) { + step.previous(); + } + amountOfSteps = this.height / stepPixels; - /** - * Draw a square shape - * @param {Number} x horizontal center - * @param {Number} y vertical center - * @param {Number} r size, width and height of the square - */ - CanvasRenderingContext2D.prototype.square = function(x, y, r) { - this.beginPath(); - this.rect(x - r, y - r, r * 2, r * 2); - }; + if (this.zeroCrossing != -1 && this.options.alignZeros == true) { + var zeroStepDifference = (step.marginEnd / step.step) - this.zeroCrossing; + if (zeroStepDifference > 0) { + for (var i = 0; i < zeroStepDifference; i++) {step.next();} + } + else if (zeroStepDifference < 0) { + for (var i = 0; i < -zeroStepDifference; i++) {step.previous();} + } + } + } + else { + amountOfSteps += 0.25; + } - /** - * Draw a triangle shape - * @param {Number} x horizontal center - * @param {Number} y vertical center - * @param {Number} r radius, half the length of the sides of the triangle - */ - CanvasRenderingContext2D.prototype.triangle = function(x, y, r) { - // http://en.wikipedia.org/wiki/Equilateral_triangle - this.beginPath(); - var s = r * 2; - var s2 = s / 2; - var ir = Math.sqrt(3) / 6 * s; // radius of inner circle - var h = Math.sqrt(s * s - s2 * s2); // height + this.valueAtZero = step.marginEnd; + var marginStartPos = 0; - this.moveTo(x, y - (h - ir)); - this.lineTo(x + s2, y + ir); - this.lineTo(x - s2, y + ir); - this.lineTo(x, y - (h - ir)); - this.closePath(); - }; + // do not draw the first label + var max = 1; - /** - * Draw a triangle shape in downward orientation - * @param {Number} x horizontal center - * @param {Number} y vertical center - * @param {Number} r radius - */ - CanvasRenderingContext2D.prototype.triangleDown = function(x, y, r) { - // http://en.wikipedia.org/wiki/Equilateral_triangle - this.beginPath(); + // Get the number of decimal places + var decimals; + if(this.options.format[orientation] !== undefined) { + decimals = this.options.format[orientation].decimals; + } - var s = r * 2; - var s2 = s / 2; - var ir = Math.sqrt(3) / 6 * s; // radius of inner circle - var h = Math.sqrt(s * s - s2 * s2); // height + this.maxLabelSize = 0; + var y = 0; + while (max < Math.round(amountOfSteps)) { + step.next(); + y = Math.round(max * stepPixels); + marginStartPos = max * stepPixels; + var isMajor = step.isMajor(); - this.moveTo(x, y + (h - ir)); - this.lineTo(x + s2, y - ir); - this.lineTo(x - s2, y - ir); - this.lineTo(x, y + (h - ir)); - this.closePath(); - }; + if (this.options['showMinorLabels'] && isMajor == false || this.master == false && this.options['showMinorLabels'] == true) { + this._redrawLabel(y - 2, step.getCurrent(decimals), orientation, 'yAxis minor', this.props.minorCharHeight); + } - /** - * Draw a star shape, a star with 5 points - * @param {Number} x horizontal center - * @param {Number} y vertical center - * @param {Number} r radius, half the length of the sides of the triangle - */ - CanvasRenderingContext2D.prototype.star = function(x, y, r) { - // http://www.html5canvastutorials.com/labs/html5-canvas-star-spinner/ - this.beginPath(); + if (isMajor && this.options['showMajorLabels'] && this.master == true || + this.options['showMinorLabels'] == false && this.master == false && isMajor == true) { + if (y >= 0) { + this._redrawLabel(y - 2, step.getCurrent(decimals), orientation, 'yAxis major', this.props.majorCharHeight); + } + this._redrawLine(y, orientation, 'grid horizontal major', this.options.majorLinesOffset, this.props.majorLineWidth); + } + else { + this._redrawLine(y, orientation, 'grid horizontal minor', this.options.minorLinesOffset, this.props.minorLineWidth); + } - for (var n = 0; n < 10; n++) { - var radius = (n % 2 === 0) ? r * 1.3 : r * 0.5; - this.lineTo( - x + radius * Math.sin(n * 2 * Math.PI / 10), - y - radius * Math.cos(n * 2 * Math.PI / 10) - ); + if (this.master == true && step.current == 0) { + this.zeroCrossing = max; } - this.closePath(); - }; + max++; + } - /** - * http://stackoverflow.com/questions/1255512/how-to-draw-a-rounded-rectangle-on-html-canvas - */ - CanvasRenderingContext2D.prototype.roundRect = function(x, y, w, h, r) { - var r2d = Math.PI/180; - if( w - ( 2 * r ) < 0 ) { r = ( w / 2 ); } //ensure that the radius isn't too large for x - if( h - ( 2 * r ) < 0 ) { r = ( h / 2 ); } //ensure that the radius isn't too large for y - this.beginPath(); - this.moveTo(x+r,y); - this.lineTo(x+w-r,y); - this.arc(x+w-r,y+r,r,r2d*270,r2d*360,false); - this.lineTo(x+w,y+h-r); - this.arc(x+w-r,y+h-r,r,0,r2d*90,false); - this.lineTo(x+r,y+h); - this.arc(x+r,y+h-r,r,r2d*90,r2d*180,false); - this.lineTo(x,y+r); - this.arc(x+r,y+r,r,r2d*180,r2d*270,false); - }; + if (this.master == false) { + this.conversionFactor = y / (this.valueAtZero - step.current); + } + else { + this.conversionFactor = this.dom.frame.offsetHeight / step.marginRange; + } - /** - * http://stackoverflow.com/questions/2172798/how-to-draw-an-oval-in-html5-canvas - */ - CanvasRenderingContext2D.prototype.ellipse = function(x, y, w, h) { - var kappa = .5522848, - ox = (w / 2) * kappa, // control point offset horizontal - oy = (h / 2) * kappa, // control point offset vertical - xe = x + w, // x-end - ye = y + h, // y-end - xm = x + w / 2, // x-middle - ym = y + h / 2; // y-middle + // Note that title is rotated, so we're using the height, not width! + var titleWidth = 0; + if (this.options.title[orientation] !== undefined && this.options.title[orientation].text !== undefined) { + titleWidth = this.props.titleCharHeight; + } + var offset = this.options.icons == true ? Math.max(this.options.iconWidth, titleWidth) + this.options.labelOffsetX + 15 : titleWidth + this.options.labelOffsetX + 15; - this.beginPath(); - this.moveTo(x, ym); - this.bezierCurveTo(x, ym - oy, xm - ox, y, xm, y); - this.bezierCurveTo(xm + ox, y, xe, ym - oy, xe, ym); - this.bezierCurveTo(xe, ym + oy, xm + ox, ye, xm, ye); - this.bezierCurveTo(xm - ox, ye, x, ym + oy, x, ym); - }; + // this will resize the yAxis to accommodate the labels. + if (this.maxLabelSize > (this.width - offset) && this.options.visible == true) { + this.width = this.maxLabelSize + offset; + this.options.width = this.width + "px"; + DOMutil.cleanupElements(this.DOMelements.lines); + DOMutil.cleanupElements(this.DOMelements.labels); + this.redraw(); + resized = true; + } + // this will resize the yAxis if it is too big for the labels. + else if (this.maxLabelSize < (this.width - offset) && this.options.visible == true && this.width > this.minWidth) { + this.width = Math.max(this.minWidth,this.maxLabelSize + offset); + this.options.width = this.width + "px"; + DOMutil.cleanupElements(this.DOMelements.lines); + DOMutil.cleanupElements(this.DOMelements.labels); + this.redraw(); + resized = true; + } + else { + DOMutil.cleanupElements(this.DOMelements.lines); + DOMutil.cleanupElements(this.DOMelements.labels); + resized = false; + } + return resized; + }; + DataAxis.prototype.convertValue = function (value) { + var invertedValue = this.valueAtZero - value; + var convertedValue = invertedValue * this.conversionFactor; + return convertedValue; + }; - /** - * http://stackoverflow.com/questions/2172798/how-to-draw-an-oval-in-html5-canvas - */ - CanvasRenderingContext2D.prototype.database = function(x, y, w, h) { - var f = 1/3; - var wEllipse = w; - var hEllipse = h * f; + /** + * Create a label for the axis at position x + * @private + * @param y + * @param text + * @param orientation + * @param className + * @param characterHeight + */ + DataAxis.prototype._redrawLabel = function (y, text, orientation, className, characterHeight) { + // reuse redundant label + var label = DOMutil.getDOMElement('div',this.DOMelements.labels, this.dom.frame); //this.dom.redundant.labels.shift(); + label.className = className; + label.innerHTML = text; + if (orientation == 'left') { + label.style.left = '-' + this.options.labelOffsetX + 'px'; + label.style.textAlign = "right"; + } + else { + label.style.right = '-' + this.options.labelOffsetX + 'px'; + label.style.textAlign = "left"; + } - var kappa = .5522848, - ox = (wEllipse / 2) * kappa, // control point offset horizontal - oy = (hEllipse / 2) * kappa, // control point offset vertical - xe = x + wEllipse, // x-end - ye = y + hEllipse, // y-end - xm = x + wEllipse / 2, // x-middle - ym = y + hEllipse / 2, // y-middle - ymb = y + (h - hEllipse/2), // y-midlle, bottom ellipse - yeb = y + h; // y-end, bottom ellipse + label.style.top = y - 0.5 * characterHeight + this.options.labelOffsetY + 'px'; - this.beginPath(); - this.moveTo(xe, ym); + text += ''; - this.bezierCurveTo(xe, ym + oy, xm + ox, ye, xm, ye); - this.bezierCurveTo(xm - ox, ye, x, ym + oy, x, ym); + var largestWidth = Math.max(this.props.majorCharWidth,this.props.minorCharWidth); + if (this.maxLabelSize < text.length * largestWidth) { + this.maxLabelSize = text.length * largestWidth; + } + }; - this.bezierCurveTo(x, ym - oy, xm - ox, y, xm, y); - this.bezierCurveTo(xm + ox, y, xe, ym - oy, xe, ym); + /** + * Create a minor line for the axis at position y + * @param y + * @param orientation + * @param className + * @param offset + * @param width + */ + DataAxis.prototype._redrawLine = function (y, orientation, className, offset, width) { + if (this.master == true) { + var line = DOMutil.getDOMElement('div',this.DOMelements.lines, this.dom.lineContainer);//this.dom.redundant.lines.shift(); + line.className = className; + line.innerHTML = ''; - this.lineTo(xe, ymb); + if (orientation == 'left') { + line.style.left = (this.width - offset) + 'px'; + } + else { + line.style.right = (this.width - offset) + 'px'; + } - this.bezierCurveTo(xe, ymb + oy, xm + ox, yeb, xm, yeb); - this.bezierCurveTo(xm - ox, yeb, x, ymb + oy, x, ymb); + line.style.width = width + 'px'; + line.style.top = y + 'px'; + } + }; - this.lineTo(x, ym); - }; + /** + * Create a title for the axis + * @private + * @param orientation + */ + DataAxis.prototype._redrawTitle = function (orientation) { + DOMutil.prepareElements(this.DOMelements.title); + // Check if the title is defined for this axes + if (this.options.title[orientation] !== undefined && this.options.title[orientation].text !== undefined) { + var title = DOMutil.getDOMElement('div', this.DOMelements.title, this.dom.frame); + title.className = 'yAxis title ' + orientation; + title.innerHTML = this.options.title[orientation].text; - /** - * Draw an arrow point (no line) - */ - CanvasRenderingContext2D.prototype.arrow = function(x, y, angle, length) { - // tail - var xt = x - length * Math.cos(angle); - var yt = y - length * Math.sin(angle); + // Add style - if provided + if (this.options.title[orientation].style !== undefined) { + util.addCssText(title, this.options.title[orientation].style); + } - // inner tail - // TODO: allow to customize different shapes - var xi = x - length * 0.9 * Math.cos(angle); - var yi = y - length * 0.9 * Math.sin(angle); + if (orientation == 'left') { + title.style.left = this.props.titleCharHeight + 'px'; + } + else { + title.style.right = this.props.titleCharHeight + 'px'; + } - // left - var xl = xt + length / 3 * Math.cos(angle + 0.5 * Math.PI); - var yl = yt + length / 3 * Math.sin(angle + 0.5 * Math.PI); + title.style.width = this.height + 'px'; + } - // right - var xr = xt + length / 3 * Math.cos(angle - 0.5 * Math.PI); - var yr = yt + length / 3 * Math.sin(angle - 0.5 * Math.PI); + // we need to clean up in case we did not use all elements. + DOMutil.cleanupElements(this.DOMelements.title); + }; - this.beginPath(); - this.moveTo(x, y); - this.lineTo(xl, yl); - this.lineTo(xi, yi); - this.lineTo(xr, yr); - this.closePath(); - }; - /** - * Sets up the dashedLine functionality for drawing - * Original code came from http://stackoverflow.com/questions/4576724/dotted-stroke-in-canvas - * @author David Jordan - * @date 2012-08-08 - */ - CanvasRenderingContext2D.prototype.dashedLine = function(x,y,x2,y2,dashArray){ - if (!dashArray) dashArray=[10,5]; - if (dashLength==0) dashLength = 0.001; // Hack for Safari - var dashCount = dashArray.length; - this.moveTo(x, y); - var dx = (x2-x), dy = (y2-y); - var slope = dy/dx; - var distRemaining = Math.sqrt( dx*dx + dy*dy ); - var dashIndex=0, draw=true; - while (distRemaining>=0.1){ - var dashLength = dashArray[dashIndex++%dashCount]; - if (dashLength > distRemaining) dashLength = distRemaining; - var xStep = Math.sqrt( dashLength*dashLength / (1 + slope*slope) ); - if (dx<0) xStep = -xStep; - x += xStep; - y += slope*xStep; - this[draw ? 'lineTo' : 'moveTo'](x,y); - distRemaining -= dashLength; - draw = !draw; - } - }; - // TODO: add diamond shape - } + + /** + * Determine the size of text on the axis (both major and minor axis). + * The size is calculated only once and then cached in this.props. + * @private + */ + DataAxis.prototype._calculateCharSize = function () { + // determine the char width and height on the minor axis + if (!('minorCharHeight' in this.props)) { + var textMinor = document.createTextNode('0'); + var measureCharMinor = document.createElement('div'); + measureCharMinor.className = 'yAxis minor measure'; + measureCharMinor.appendChild(textMinor); + this.dom.frame.appendChild(measureCharMinor); + + this.props.minorCharHeight = measureCharMinor.clientHeight; + this.props.minorCharWidth = measureCharMinor.clientWidth; + + this.dom.frame.removeChild(measureCharMinor); + } + + if (!('majorCharHeight' in this.props)) { + var textMajor = document.createTextNode('0'); + var measureCharMajor = document.createElement('div'); + measureCharMajor.className = 'yAxis major measure'; + measureCharMajor.appendChild(textMajor); + this.dom.frame.appendChild(measureCharMajor); + + this.props.majorCharHeight = measureCharMajor.clientHeight; + this.props.majorCharWidth = measureCharMajor.clientWidth; + + this.dom.frame.removeChild(measureCharMajor); + } + + if (!('titleCharHeight' in this.props)) { + var textTitle = document.createTextNode('0'); + var measureCharTitle = document.createElement('div'); + measureCharTitle.className = 'yAxis title measure'; + measureCharTitle.appendChild(textTitle); + this.dom.frame.appendChild(measureCharTitle); + + this.props.titleCharHeight = measureCharTitle.clientHeight; + this.props.titleCharWidth = measureCharTitle.clientWidth; + + this.dom.frame.removeChild(measureCharTitle); + } + }; + + /** + * Snap a date to a rounded value. + * The snap intervals are dependent on the current scale and step. + * @param {Date} date the date to be snapped. + * @return {Date} snappedDate + */ + DataAxis.prototype.snap = function(date) { + return this.step.snap(date); + }; + + module.exports = DataAxis; /***/ }, -/* 51 */ +/* 45 */ /***/ function(module, exports, __webpack_require__) { /** - * Created by Alex on 11/11/2014. + * @constructor DataStep + * The class DataStep is an iterator for data for the lineGraph. You provide a start data point and an + * end data point. The class itself determines the best scale (step size) based on the + * provided start Date, end Date, and minimumStep. + * + * If minimumStep is provided, the step size is chosen as close as possible + * to the minimumStep but larger than minimumStep. If minimumStep is not + * provided, the scale is set to 1 DAY. + * The minimumStep should correspond with the onscreen size of about 6 characters + * + * Alternatively, you can set a scale by hand. + * After creation, you can initialize the class by executing first(). Then you + * can iterate from the start date to the end date via next(). You can check if + * the end date is reached with the function hasNext(). After each step, you can + * retrieve the current date via getCurrent(). + * The DataStep has scales ranging from milliseconds, seconds, minutes, hours, + * days, to years. + * + * Version: 1.2 + * + * @param {Date} [start] The start date, for example new Date(2010, 9, 21) + * or new Date(2010, 9, 21, 23, 45, 00) + * @param {Date} [end] The end date + * @param {Number} [minimumStep] Optional. Minimum step size in milliseconds */ - var DOMutil = __webpack_require__(2); - var Points = __webpack_require__(53); + function DataStep(start, end, minimumStep, containerHeight, customRange, alignZeros) { + // variables + this.current = 0; - function Line(groupId, options) { - this.groupId = groupId; - this.options = options; + this.autoScale = true; + this.stepIndex = 0; + this.step = 1; + this.scale = 1; + + this.marginStart; + this.marginEnd; + this.deadSpace = 0; + + this.majorSteps = [1, 2, 5, 10]; + this.minorSteps = [0.25, 0.5, 1, 2]; + + this.alignZeros = alignZeros; + + this.setRange(start, end, minimumStep, containerHeight, customRange); } - Line.prototype.getYRange = function(groupData) { - var yMin = groupData[0].y; - var yMax = groupData[0].y; - for (var j = 0; j < groupData.length; j++) { - yMin = yMin > groupData[j].y ? groupData[j].y : yMin; - yMax = yMax < groupData[j].y ? groupData[j].y : yMax; + + + /** + * Set a new range + * If minimumStep is provided, the step size is chosen as close as possible + * to the minimumStep but larger than minimumStep. If minimumStep is not + * provided, the scale is set to 1 DAY. + * The minimumStep should correspond with the onscreen size of about 6 characters + * @param {Number} [start] The start date and time. + * @param {Number} [end] The end date and time. + * @param {Number} [minimumStep] Optional. Minimum step size in milliseconds + */ + DataStep.prototype.setRange = function(start, end, minimumStep, containerHeight, customRange) { + this._start = customRange.min === undefined ? start : customRange.min; + this._end = customRange.max === undefined ? end : customRange.max; + + if (this._start == this._end) { + this._start -= 0.75; + this._end += 1; + } + + if (this.autoScale == true) { + this.setMinimumStep(minimumStep, containerHeight); } - return {min: yMin, max: yMax, yAxisOrientation: this.options.yAxisOrientation}; - }; + this.setFirst(customRange); + }; /** - * draw a line graph - * - * @param dataset - * @param group + * Automatically determine the scale that bests fits the provided minimum step + * @param {Number} [minimumStep] The minimum step size in milliseconds */ - Line.prototype.draw = function (dataset, group, framework) { - if (dataset != null) { - if (dataset.length > 0) { - var path, d; - var svgHeight = Number(framework.svg.style.height.replace('px','')); - path = DOMutil.getSVGElement('path', framework.svgElements, framework.svg); - path.setAttributeNS(null, "class", group.className); - if(group.style !== undefined) { - path.setAttributeNS(null, "style", group.style); - } + DataStep.prototype.setMinimumStep = function(minimumStep, containerHeight) { + // round to floor + var size = this._end - this._start; + var safeSize = size * 1.2; + var minimumStepValue = minimumStep * (safeSize / containerHeight); + var orderOfMagnitude = Math.round(Math.log(safeSize)/Math.LN10); - // construct path from dataset - if (group.options.catmullRom.enabled == true) { - d = Line._catmullRom(dataset, group); - } - else { - d = Line._linear(dataset); - } + var minorStepIdx = -1; + var magnitudefactor = Math.pow(10,orderOfMagnitude); - // append with points for fill and finalize the path - if (group.options.shaded.enabled == true) { - var fillPath = DOMutil.getSVGElement('path', framework.svgElements, framework.svg); - var dFill; - if (group.options.shaded.orientation == 'top') { - dFill = 'M' + dataset[0].x + ',' + 0 + ' ' + d + 'L' + dataset[dataset.length - 1].x + ',' + 0; - } - else { - dFill = 'M' + dataset[0].x + ',' + svgHeight + ' ' + d + 'L' + dataset[dataset.length - 1].x + ',' + svgHeight; - } - fillPath.setAttributeNS(null, "class", group.className + " fill"); - if(group.options.shaded.style !== undefined) { - fillPath.setAttributeNS(null, "style", group.options.shaded.style); - } - fillPath.setAttributeNS(null, "d", dFill); - } - // copy properties to path for drawing. - path.setAttributeNS(null, 'd', 'M' + d); + var start = 0; + if (orderOfMagnitude < 0) { + start = orderOfMagnitude; + } - // draw points - if (group.options.drawPoints.enabled == true) { - Points.draw(dataset, group, framework); + var solutionFound = false; + for (var i = start; Math.abs(i) <= Math.abs(orderOfMagnitude); i++) { + magnitudefactor = Math.pow(10,i); + for (var j = 0; j < this.minorSteps.length; j++) { + var stepSize = magnitudefactor * this.minorSteps[j]; + if (stepSize >= minimumStepValue) { + solutionFound = true; + minorStepIdx = j; + break; } } + if (solutionFound == true) { + break; + } } + this.stepIndex = minorStepIdx; + this.scale = magnitudefactor; + this.step = magnitudefactor * this.minorSteps[minorStepIdx]; }; /** - * This uses an uniform parametrization of the CatmullRom algorithm: - * 'On the Parameterization of Catmull-Rom Curves' by Cem Yuksel et al. - * @param data - * @returns {string} - * @private + * Round the current date to the first minor date value + * This must be executed once when the current date is set to start Date */ - Line._catmullRomUniform = function(data) { - // catmull rom - var p0, p1, p2, p3, bp1, bp2; - var d = Math.round(data[0].x) + ',' + Math.round(data[0].y) + ' '; - var normalization = 1/6; - var length = data.length; - for (var i = 0; i < length - 1; i++) { + DataStep.prototype.setFirst = function(customRange) { + if (customRange === undefined) { + customRange = {}; + } - p0 = (i == 0) ? data[0] : data[i-1]; - p1 = data[i]; - p2 = data[i+1]; - p3 = (i + 2 < length) ? data[i+2] : p2; + var niceStart = customRange.min === undefined ? this._start - (this.scale * 2 * this.minorSteps[this.stepIndex]) : customRange.min; + var niceEnd = customRange.max === undefined ? this._end + (this.scale * this.minorSteps[this.stepIndex]) : customRange.max; + this.marginEnd = customRange.max === undefined ? this.roundToMinor(niceEnd) : customRange.max; + this.marginStart = customRange.min === undefined ? this.roundToMinor(niceStart) : customRange.min; - // Catmull-Rom to Cubic Bezier conversion matrix - // 0 1 0 0 - // -1/6 1 1/6 0 - // 0 1/6 1 -1/6 - // 0 0 1 0 + // if we need to align the zero's we need to make sure that there is a zero to use. + if (this.alignZeros == true && (this.marginEnd - this.marginStart) % this.step != 0) { + this.marginEnd += this.marginEnd % this.step; + } - // bp0 = { x: p1.x, y: p1.y }; - bp1 = { x: ((-p0.x + 6*p1.x + p2.x) *normalization), y: ((-p0.y + 6*p1.y + p2.y) *normalization)}; - bp2 = { x: (( p1.x + 6*p2.x - p3.x) *normalization), y: (( p1.y + 6*p2.y - p3.y) *normalization)}; - // bp0 = { x: p2.x, y: p2.y }; + this.deadSpace = this.roundToMinor(niceEnd) - niceEnd + this.roundToMinor(niceStart) - niceStart; + this.marginRange = this.marginEnd - this.marginStart; - d += 'C' + - bp1.x + ',' + - bp1.y + ' ' + - bp2.x + ',' + - bp2.y + ' ' + - p2.x + ',' + - p2.y + ' '; + + this.current = this.marginEnd; + }; + + DataStep.prototype.roundToMinor = function(value) { + var rounded = value - (value % (this.scale * this.minorSteps[this.stepIndex])); + if (value % (this.scale * this.minorSteps[this.stepIndex]) > 0.5 * (this.scale * this.minorSteps[this.stepIndex])) { + return rounded + (this.scale * this.minorSteps[this.stepIndex]); + } + else { + return rounded; } + } - return d; + + /** + * Check if the there is a next step + * @return {boolean} true if the current date has not passed the end date + */ + DataStep.prototype.hasNext = function () { + return (this.current >= this.marginStart); }; /** - * This uses either the chordal or centripetal parameterization of the catmull-rom algorithm. - * By default, the centripetal parameterization is used because this gives the nicest results. - * These parameterizations are relatively heavy because the distance between 4 points have to be calculated. - * - * One optimization can be used to reuse distances since this is a sliding window approach. - * @param data - * @param group - * @returns {string} - * @private + * Do the next step */ - Line._catmullRom = function(data, group) { - var alpha = group.options.catmullRom.alpha; - if (alpha == 0 || alpha === undefined) { - return this._catmullRomUniform(data); - } - else { - var p0, p1, p2, p3, bp1, bp2, d1,d2,d3, A, B, N, M; - var d3powA, d2powA, d3pow2A, d2pow2A, d1pow2A, d1powA; - var d = Math.round(data[0].x) + ',' + Math.round(data[0].y) + ' '; - var length = data.length; - for (var i = 0; i < length - 1; i++) { + DataStep.prototype.next = function() { + var prev = this.current; + this.current -= this.step; - p0 = (i == 0) ? data[0] : data[i-1]; - p1 = data[i]; - p2 = data[i+1]; - p3 = (i + 2 < length) ? data[i+2] : p2; + // safety mechanism: if current time is still unchanged, move to the end + if (this.current == prev) { + this.current = this._end; + } + }; - d1 = Math.sqrt(Math.pow(p0.x - p1.x,2) + Math.pow(p0.y - p1.y,2)); - d2 = Math.sqrt(Math.pow(p1.x - p2.x,2) + Math.pow(p1.y - p2.y,2)); - d3 = Math.sqrt(Math.pow(p2.x - p3.x,2) + Math.pow(p2.y - p3.y,2)); + /** + * Do the next step + */ + DataStep.prototype.previous = function() { + this.current += this.step; + this.marginEnd += this.step; + this.marginRange = this.marginEnd - this.marginStart; + }; - // Catmull-Rom to Cubic Bezier conversion matrix - // A = 2d1^2a + 3d1^a * d2^a + d3^2a - // B = 2d3^2a + 3d3^a * d2^a + d2^2a - // [ 0 1 0 0 ] - // [ -d2^2a /N A/N d1^2a /N 0 ] - // [ 0 d3^2a /M B/M -d2^2a /M ] - // [ 0 0 1 0 ] + /** + * Get the current datetime + * @return {String} current The current date + */ + DataStep.prototype.getCurrent = function(decimals) { + // prevent round-off errors when close to zero + var current = (Math.abs(this.current) < this.step / 2) ? 0 : this.current; + var toPrecision = '' + Number(current).toPrecision(5); - d3powA = Math.pow(d3, alpha); - d3pow2A = Math.pow(d3,2*alpha); - d2powA = Math.pow(d2, alpha); - d2pow2A = Math.pow(d2,2*alpha); - d1powA = Math.pow(d1, alpha); - d1pow2A = Math.pow(d1,2*alpha); + // If decimals is specified, then limit or extend the string as required + if(decimals !== undefined && !isNaN(Number(decimals))) { + // If string includes exponent, then we need to add it to the end + var exp = ""; + var index = toPrecision.indexOf("e"); + if(index != -1) { + // Get the exponent + exp = toPrecision.slice(index); + // Remove the exponent in case we need to zero-extend + toPrecision = toPrecision.slice(0, index); + } + index = Math.max(toPrecision.indexOf(","), toPrecision.indexOf(".")); + if(index === -1) { + // No decimal found - if we want decimals, then we need to add it + if(decimals !== 0) { + toPrecision += '.'; + } + // Calculate how long the string should be + index = toPrecision.length + decimals; + } + else if(decimals !== 0) { + // Calculate how long the string should be - accounting for the decimal place + index += decimals + 1; + } + if(index > toPrecision.length) { + // We need to add zeros! + for(var cnt = index - toPrecision.length; cnt > 0; cnt--) { + toPrecision += '0'; + } + } + else { + // we need to remove characters + toPrecision = toPrecision.slice(0, index); + } + // Add the exponent if there is one + toPrecision += exp; + } + else { + if (toPrecision.indexOf(",") != -1 || toPrecision.indexOf(".") != -1) { + // If no decimal is specified, and there are decimal places, remove trailing zeros + for (var i = toPrecision.length - 1; i > 0; i--) { + if (toPrecision[i] == "0") { + toPrecision = toPrecision.slice(0, i); + } + else if (toPrecision[i] == "." || toPrecision[i] == ",") { + toPrecision = toPrecision.slice(0, i); + break; + } + else { + break; + } + } + } + } - A = 2*d1pow2A + 3*d1powA * d2powA + d2pow2A; - B = 2*d3pow2A + 3*d3powA * d2powA + d2pow2A; - N = 3*d1powA * (d1powA + d2powA); - if (N > 0) {N = 1 / N;} - M = 3*d3powA * (d3powA + d2powA); - if (M > 0) {M = 1 / M;} + return toPrecision; + }; - bp1 = { x: ((-d2pow2A * p0.x + A*p1.x + d1pow2A * p2.x) * N), - y: ((-d2pow2A * p0.y + A*p1.y + d1pow2A * p2.y) * N)}; - bp2 = { x: (( d3pow2A * p1.x + B*p2.x - d2pow2A * p3.x) * M), - y: (( d3pow2A * p1.y + B*p2.y - d2pow2A * p3.y) * M)}; - if (bp1.x == 0 && bp1.y == 0) {bp1 = p1;} - if (bp2.x == 0 && bp2.y == 0) {bp2 = p2;} - d += 'C' + - bp1.x + ',' + - bp1.y + ' ' + - bp2.x + ',' + - bp2.y + ' ' + - p2.x + ',' + - p2.y + ' '; - } + /** + * Snap a date to a rounded value. + * The snap intervals are dependent on the current scale and step. + * @param {Date} date the date to be snapped. + * @return {Date} snappedDate + */ + DataStep.prototype.snap = function(date) { - return d; - } }; /** - * this generates the SVG path for a linear drawing between datapoints. - * @param data - * @returns {string} - * @private + * Check if the current value is a major value (for example when the step + * is DAY, a major value is each first day of the MONTH) + * @return {boolean} true if current date is major, else false. */ - Line._linear = function(data) { - // linear - var d = ''; - for (var i = 0; i < data.length; i++) { - if (i == 0) { - d += data[i].x + ',' + data[i].y; - } - else { - d += ' ' + data[i].x + ',' + data[i].y; - } - } - return d; + DataStep.prototype.isMajor = function() { + return (this.current % (this.scale * this.majorSteps[this.stepIndex]) == 0); }; - module.exports = Line; + module.exports = DataStep; /***/ }, -/* 52 */ +/* 46 */ /***/ function(module, exports, __webpack_require__) { + var util = __webpack_require__(1); + var DOMutil = __webpack_require__(6); + var Line = __webpack_require__(47); + var Bar = __webpack_require__(49); + var Points = __webpack_require__(48); + /** - * Created by Alex on 11/11/2014. + * /** + * @param {object} group | the object of the group from the dataset + * @param {string} groupId | ID of the group + * @param {object} options | the default options + * @param {array} groupsUsingDefaultStyles | this array has one entree. + * It is passed as an array so it is passed by reference. + * It enumerates through the default styles + * @constructor */ - var DOMutil = __webpack_require__(2); - var Points = __webpack_require__(53); - - function Bargraph(groupId, options) { - this.groupId = groupId; - this.options = options; + function GraphGroup (group, groupId, options, groupsUsingDefaultStyles) { + this.id = groupId; + var fields = ['sampling','style','sort','yAxisOrientation','barChart','drawPoints','shaded','catmullRom'] + this.options = util.selectiveBridgeObject(fields,options); + this.usingDefaultStyle = group.className === undefined; + this.groupsUsingDefaultStyles = groupsUsingDefaultStyles; + this.zeroPosition = 0; + this.update(group); + if (this.usingDefaultStyle == true) { + this.groupsUsingDefaultStyles[0] += 1; + } + this.itemsData = []; + this.visible = group.visible === undefined ? true : group.visible; } - Bargraph.prototype.getYRange = function(groupData) { - if (this.options.barChart.handleOverlap != 'stack') { - var yMin = groupData[0].y; - var yMax = groupData[0].y; - for (var j = 0; j < groupData.length; j++) { - yMin = yMin > groupData[j].y ? groupData[j].y : yMin; - yMax = yMax < groupData[j].y ? groupData[j].y : yMax; + + /** + * this loads a reference to all items in this group into this group. + * @param {array} items + */ + GraphGroup.prototype.setItems = function(items) { + if (items != null) { + this.itemsData = items; + if (this.options.sort == true) { + this.itemsData.sort(function (a,b) {return a.x - b.x;}) } - return {min: yMin, max: yMax, yAxisOrientation: this.options.yAxisOrientation}; } else { - var barCombinedData = []; - for (var j = 0; j < groupData.length; j++) { - barCombinedData.push({ - x: groupData[j].x, - y: groupData[j].y, - groupId: this.groupId - }); - } - return barCombinedData; + this.itemsData = []; } }; - /** - * draw a bar graph + * this is used for plotting barcharts, this way, we only have to calculate it once. + * @param pos + */ + GraphGroup.prototype.setZeroPosition = function(pos) { + this.zeroPosition = pos; + }; + + + /** + * set the options of the graph group over the default options. + * @param options + */ + GraphGroup.prototype.setOptions = function(options) { + if (options !== undefined) { + var fields = ['sampling','style','sort','yAxisOrientation','barChart']; + util.selectiveDeepExtend(fields, this.options, options); + + util.mergeOptions(this.options, options,'catmullRom'); + util.mergeOptions(this.options, options,'drawPoints'); + util.mergeOptions(this.options, options,'shaded'); + + if (options.catmullRom) { + if (typeof options.catmullRom == 'object') { + if (options.catmullRom.parametrization) { + if (options.catmullRom.parametrization == 'uniform') { + this.options.catmullRom.alpha = 0; + } + else if (options.catmullRom.parametrization == 'chordal') { + this.options.catmullRom.alpha = 1.0; + } + else { + this.options.catmullRom.parametrization = 'centripetal'; + this.options.catmullRom.alpha = 0.5; + } + } + } + } + } + + if (this.options.style == 'line') { + this.type = new Line(this.id, this.options); + } + else if (this.options.style == 'bar') { + this.type = new Bar(this.id, this.options); + } + else if (this.options.style == 'points') { + this.type = new Points(this.id, this.options); + } + }; + + + /** + * this updates the current group class with the latest group dataset entree, used in _updateGroup in linegraph + * @param group + */ + GraphGroup.prototype.update = function(group) { + this.group = group; + this.content = group.content || 'graph'; + this.className = group.className || this.className || "graphGroup" + this.groupsUsingDefaultStyles[0] % 10; + this.visible = group.visible === undefined ? true : group.visible; + this.style = group.style; + this.setOptions(group.options); + }; + + + /** + * draw the icon for the legend. + * + * @param x + * @param y + * @param JSONcontainer + * @param SVGcontainer + * @param iconWidth + * @param iconHeight + */ + GraphGroup.prototype.drawIcon = function(x, y, JSONcontainer, SVGcontainer, iconWidth, iconHeight) { + var fillHeight = iconHeight * 0.5; + var path, fillPath; + + var outline = DOMutil.getSVGElement("rect", JSONcontainer, SVGcontainer); + outline.setAttributeNS(null, "x", x); + outline.setAttributeNS(null, "y", y - fillHeight); + outline.setAttributeNS(null, "width", iconWidth); + outline.setAttributeNS(null, "height", 2*fillHeight); + outline.setAttributeNS(null, "class", "outline"); + + if (this.options.style == 'line') { + path = DOMutil.getSVGElement("path", JSONcontainer, SVGcontainer); + path.setAttributeNS(null, "class", this.className); + if(this.style !== undefined) { + path.setAttributeNS(null, "style", this.style); + } + + path.setAttributeNS(null, "d", "M" + x + ","+y+" L" + (x + iconWidth) + ","+y+""); + if (this.options.shaded.enabled == true) { + fillPath = DOMutil.getSVGElement("path", JSONcontainer, SVGcontainer); + if (this.options.shaded.orientation == 'top') { + fillPath.setAttributeNS(null, "d", "M"+x+", " + (y - fillHeight) + + "L"+x+","+y+" L"+ (x + iconWidth) + ","+y+" L"+ (x + iconWidth) + "," + (y - fillHeight)); + } + else { + fillPath.setAttributeNS(null, "d", "M"+x+","+y+" " + + "L"+x+"," + (y + fillHeight) + " " + + "L"+ (x + iconWidth) + "," + (y + fillHeight) + + "L"+ (x + iconWidth) + ","+y); + } + fillPath.setAttributeNS(null, "class", this.className + " iconFill"); + } + + if (this.options.drawPoints.enabled == true) { + DOMutil.drawPoint(x + 0.5 * iconWidth,y, this, JSONcontainer, SVGcontainer); + } + } + else { + var barWidth = Math.round(0.3 * iconWidth); + var bar1Height = Math.round(0.4 * iconHeight); + var bar2Height = Math.round(0.75 * iconHeight); + + var offset = Math.round((iconWidth - (2 * barWidth))/3); + + DOMutil.drawBar(x + 0.5*barWidth + offset , y + fillHeight - bar1Height - 1, barWidth, bar1Height, this.className + ' bar', JSONcontainer, SVGcontainer); + DOMutil.drawBar(x + 1.5*barWidth + offset + 2, y + fillHeight - bar2Height - 1, barWidth, bar2Height, this.className + ' bar', JSONcontainer, SVGcontainer); + } + }; + + + /** + * return the legend entree for this group. + * + * @param iconWidth + * @param iconHeight + * @returns {{icon: HTMLElement, label: (group.content|*|string), orientation: (.options.yAxisOrientation|*)}} + */ + GraphGroup.prototype.getLegend = function(iconWidth, iconHeight) { + var svg = document.createElementNS('http://www.w3.org/2000/svg',"svg"); + this.drawIcon(0,0.5*iconHeight,[],svg,iconWidth,iconHeight); + return {icon: svg, label: this.content, orientation:this.options.yAxisOrientation}; + } + + GraphGroup.prototype.getYRange = function(groupData) { + return this.type.getYRange(groupData); + } + + GraphGroup.prototype.draw = function(dataset, group, framework) { + this.type.draw(dataset, group, framework); + } + + + module.exports = GraphGroup; + + +/***/ }, +/* 47 */ +/***/ function(module, exports, __webpack_require__) { + + /** + * Created by Alex on 11/11/2014. + */ + var DOMutil = __webpack_require__(6); + var Points = __webpack_require__(48); + + function Line(groupId, options) { + this.groupId = groupId; + this.options = options; + } + + Line.prototype.getYRange = function(groupData) { + var yMin = groupData[0].y; + var yMax = groupData[0].y; + for (var j = 0; j < groupData.length; j++) { + yMin = yMin > groupData[j].y ? groupData[j].y : yMin; + yMax = yMax < groupData[j].y ? groupData[j].y : yMax; + } + return {min: yMin, max: yMax, yAxisOrientation: this.options.yAxisOrientation}; + }; + + + /** + * draw a line graph + * + * @param dataset + * @param group + */ + Line.prototype.draw = function (dataset, group, framework) { + if (dataset != null) { + if (dataset.length > 0) { + var path, d; + var svgHeight = Number(framework.svg.style.height.replace('px','')); + path = DOMutil.getSVGElement('path', framework.svgElements, framework.svg); + path.setAttributeNS(null, "class", group.className); + if(group.style !== undefined) { + path.setAttributeNS(null, "style", group.style); + } + + // construct path from dataset + if (group.options.catmullRom.enabled == true) { + d = Line._catmullRom(dataset, group); + } + else { + d = Line._linear(dataset); + } + + // append with points for fill and finalize the path + if (group.options.shaded.enabled == true) { + var fillPath = DOMutil.getSVGElement('path', framework.svgElements, framework.svg); + var dFill; + if (group.options.shaded.orientation == 'top') { + dFill = 'M' + dataset[0].x + ',' + 0 + ' ' + d + 'L' + dataset[dataset.length - 1].x + ',' + 0; + } + else { + dFill = 'M' + dataset[0].x + ',' + svgHeight + ' ' + d + 'L' + dataset[dataset.length - 1].x + ',' + svgHeight; + } + fillPath.setAttributeNS(null, "class", group.className + " fill"); + if(group.options.shaded.style !== undefined) { + fillPath.setAttributeNS(null, "style", group.options.shaded.style); + } + fillPath.setAttributeNS(null, "d", dFill); + } + // copy properties to path for drawing. + path.setAttributeNS(null, 'd', 'M' + d); + + // draw points + if (group.options.drawPoints.enabled == true) { + Points.draw(dataset, group, framework); + } + } + } + }; + + + + /** + * This uses an uniform parametrization of the CatmullRom algorithm: + * 'On the Parameterization of Catmull-Rom Curves' by Cem Yuksel et al. + * @param data + * @returns {string} + * @private + */ + Line._catmullRomUniform = function(data) { + // catmull rom + var p0, p1, p2, p3, bp1, bp2; + var d = Math.round(data[0].x) + ',' + Math.round(data[0].y) + ' '; + var normalization = 1/6; + var length = data.length; + for (var i = 0; i < length - 1; i++) { + + p0 = (i == 0) ? data[0] : data[i-1]; + p1 = data[i]; + p2 = data[i+1]; + p3 = (i + 2 < length) ? data[i+2] : p2; + + + // Catmull-Rom to Cubic Bezier conversion matrix + // 0 1 0 0 + // -1/6 1 1/6 0 + // 0 1/6 1 -1/6 + // 0 0 1 0 + + // bp0 = { x: p1.x, y: p1.y }; + bp1 = { x: ((-p0.x + 6*p1.x + p2.x) *normalization), y: ((-p0.y + 6*p1.y + p2.y) *normalization)}; + bp2 = { x: (( p1.x + 6*p2.x - p3.x) *normalization), y: (( p1.y + 6*p2.y - p3.y) *normalization)}; + // bp0 = { x: p2.x, y: p2.y }; + + d += 'C' + + bp1.x + ',' + + bp1.y + ' ' + + bp2.x + ',' + + bp2.y + ' ' + + p2.x + ',' + + p2.y + ' '; + } + + return d; + }; + + /** + * This uses either the chordal or centripetal parameterization of the catmull-rom algorithm. + * By default, the centripetal parameterization is used because this gives the nicest results. + * These parameterizations are relatively heavy because the distance between 4 points have to be calculated. + * + * One optimization can be used to reuse distances since this is a sliding window approach. + * @param data + * @param group + * @returns {string} + * @private + */ + Line._catmullRom = function(data, group) { + var alpha = group.options.catmullRom.alpha; + if (alpha == 0 || alpha === undefined) { + return this._catmullRomUniform(data); + } + else { + var p0, p1, p2, p3, bp1, bp2, d1,d2,d3, A, B, N, M; + var d3powA, d2powA, d3pow2A, d2pow2A, d1pow2A, d1powA; + var d = Math.round(data[0].x) + ',' + Math.round(data[0].y) + ' '; + var length = data.length; + for (var i = 0; i < length - 1; i++) { + + p0 = (i == 0) ? data[0] : data[i-1]; + p1 = data[i]; + p2 = data[i+1]; + p3 = (i + 2 < length) ? data[i+2] : p2; + + d1 = Math.sqrt(Math.pow(p0.x - p1.x,2) + Math.pow(p0.y - p1.y,2)); + d2 = Math.sqrt(Math.pow(p1.x - p2.x,2) + Math.pow(p1.y - p2.y,2)); + d3 = Math.sqrt(Math.pow(p2.x - p3.x,2) + Math.pow(p2.y - p3.y,2)); + + // Catmull-Rom to Cubic Bezier conversion matrix + + // A = 2d1^2a + 3d1^a * d2^a + d3^2a + // B = 2d3^2a + 3d3^a * d2^a + d2^2a + + // [ 0 1 0 0 ] + // [ -d2^2a /N A/N d1^2a /N 0 ] + // [ 0 d3^2a /M B/M -d2^2a /M ] + // [ 0 0 1 0 ] + + d3powA = Math.pow(d3, alpha); + d3pow2A = Math.pow(d3,2*alpha); + d2powA = Math.pow(d2, alpha); + d2pow2A = Math.pow(d2,2*alpha); + d1powA = Math.pow(d1, alpha); + d1pow2A = Math.pow(d1,2*alpha); + + A = 2*d1pow2A + 3*d1powA * d2powA + d2pow2A; + B = 2*d3pow2A + 3*d3powA * d2powA + d2pow2A; + N = 3*d1powA * (d1powA + d2powA); + if (N > 0) {N = 1 / N;} + M = 3*d3powA * (d3powA + d2powA); + if (M > 0) {M = 1 / M;} + + bp1 = { x: ((-d2pow2A * p0.x + A*p1.x + d1pow2A * p2.x) * N), + y: ((-d2pow2A * p0.y + A*p1.y + d1pow2A * p2.y) * N)}; + + bp2 = { x: (( d3pow2A * p1.x + B*p2.x - d2pow2A * p3.x) * M), + y: (( d3pow2A * p1.y + B*p2.y - d2pow2A * p3.y) * M)}; + + if (bp1.x == 0 && bp1.y == 0) {bp1 = p1;} + if (bp2.x == 0 && bp2.y == 0) {bp2 = p2;} + d += 'C' + + bp1.x + ',' + + bp1.y + ' ' + + bp2.x + ',' + + bp2.y + ' ' + + p2.x + ',' + + p2.y + ' '; + } + + return d; + } + }; + + /** + * this generates the SVG path for a linear drawing between datapoints. + * @param data + * @returns {string} + * @private + */ + Line._linear = function(data) { + // linear + var d = ''; + for (var i = 0; i < data.length; i++) { + if (i == 0) { + d += data[i].x + ',' + data[i].y; + } + else { + d += ' ' + data[i].x + ',' + data[i].y; + } + } + return d; + }; + + module.exports = Line; + + +/***/ }, +/* 48 */ +/***/ function(module, exports, __webpack_require__) { + + /** + * Created by Alex on 11/11/2014. + */ + var DOMutil = __webpack_require__(6); + + function Points(groupId, options) { + this.groupId = groupId; + this.options = options; + } + + + Points.prototype.getYRange = function(groupData) { + var yMin = groupData[0].y; + var yMax = groupData[0].y; + for (var j = 0; j < groupData.length; j++) { + yMin = yMin > groupData[j].y ? groupData[j].y : yMin; + yMax = yMax < groupData[j].y ? groupData[j].y : yMax; + } + return {min: yMin, max: yMax, yAxisOrientation: this.options.yAxisOrientation}; + }; + + Points.prototype.draw = function(dataset, group, framework, offset) { + Points.draw(dataset, group, framework, offset); + } + + /** + * draw the data points + * + * @param {Array} dataset + * @param {Object} JSONcontainer + * @param {Object} svg | SVG DOM element + * @param {GraphGroup} group + * @param {Number} [offset] + */ + Points.draw = function (dataset, group, framework, offset) { + if (offset === undefined) {offset = 0;} + for (var i = 0; i < dataset.length; i++) { + DOMutil.drawPoint(dataset[i].x + offset, dataset[i].y, group, framework.svgElements, framework.svg); + } + }; + + + module.exports = Points; + +/***/ }, +/* 49 */ +/***/ function(module, exports, __webpack_require__) { + + /** + * Created by Alex on 11/11/2014. + */ + var DOMutil = __webpack_require__(6); + var Points = __webpack_require__(48); + + function Bargraph(groupId, options) { + this.groupId = groupId; + this.options = options; + } + + Bargraph.prototype.getYRange = function(groupData) { + if (this.options.barChart.handleOverlap != 'stack') { + var yMin = groupData[0].y; + var yMax = groupData[0].y; + for (var j = 0; j < groupData.length; j++) { + yMin = yMin > groupData[j].y ? groupData[j].y : yMin; + yMax = yMax < groupData[j].y ? groupData[j].y : yMax; + } + return {min: yMin, max: yMax, yAxisOrientation: this.options.yAxisOrientation}; + } + else { + var barCombinedData = []; + for (var j = 0; j < groupData.length; j++) { + barCombinedData.push({ + x: groupData[j].x, + y: groupData[j].y, + groupId: this.groupId + }); + } + return barCombinedData; + } + }; + + + + /** + * draw a bar graph * * @param groupIds * @param processedGroupData @@ -23601,10506 +22442,11594 @@ return /******/ (function(modules) { // webpackBootstrap module.exports = Bargraph; /***/ }, -/* 53 */ +/* 50 */ /***/ function(module, exports, __webpack_require__) { + var util = __webpack_require__(1); + var DOMutil = __webpack_require__(6); + var Component = __webpack_require__(23); + /** - * Created by Alex on 11/11/2014. + * Legend for Graph2d */ - var DOMutil = __webpack_require__(2); + function Legend(body, options, side, linegraphOptions) { + this.body = body; + this.defaultOptions = { + enabled: true, + icons: true, + iconSize: 20, + iconSpacing: 6, + left: { + visible: true, + position: 'top-left' // top/bottom - left,center,right + }, + right: { + visible: true, + position: 'top-left' // top/bottom - left,center,right + } + } + this.side = side; + this.options = util.extend({},this.defaultOptions); + this.linegraphOptions = linegraphOptions; - function Points(groupId, options) { - this.groupId = groupId; - this.options = options; - } + this.svgElements = {}; + this.dom = {}; + this.groups = {}; + this.amountOfGroups = 0; + this._create(); + this.setOptions(options); + } - Points.prototype.getYRange = function(groupData) { - var yMin = groupData[0].y; - var yMax = groupData[0].y; - for (var j = 0; j < groupData.length; j++) { - yMin = yMin > groupData[j].y ? groupData[j].y : yMin; - yMax = yMax < groupData[j].y ? groupData[j].y : yMax; - } - return {min: yMin, max: yMax, yAxisOrientation: this.options.yAxisOrientation}; - }; + Legend.prototype = new Component(); - Points.prototype.draw = function(dataset, group, framework, offset) { - Points.draw(dataset, group, framework, offset); + Legend.prototype.clear = function() { + this.groups = {}; + this.amountOfGroups = 0; } - /** - * draw the data points - * - * @param {Array} dataset - * @param {Object} JSONcontainer - * @param {Object} svg | SVG DOM element - * @param {GraphGroup} group - * @param {Number} [offset] - */ - Points.draw = function (dataset, group, framework, offset) { - if (offset === undefined) {offset = 0;} - for (var i = 0; i < dataset.length; i++) { - DOMutil.drawPoint(dataset[i].x + offset, dataset[i].y, group, framework.svgElements, framework.svg); - } - }; - - - module.exports = Points; - -/***/ }, -/* 54 */ -/***/ function(module, exports, __webpack_require__) { - - var PhysicsMixin = __webpack_require__(66); - var ClusterMixin = __webpack_require__(57); - var SectorsMixin = __webpack_require__(58); - var SelectionMixin = __webpack_require__(59); - var ManipulationMixin = __webpack_require__(60); - var NavigationMixin = __webpack_require__(61); - var HierarchicalLayoutMixin = __webpack_require__(62); + Legend.prototype.addGroup = function(label, graphOptions) { - /** - * Load a mixin into the network object - * - * @param {Object} sourceVariable | this object has to contain functions. - * @private - */ - exports._loadMixin = function (sourceVariable) { - for (var mixinFunction in sourceVariable) { - if (sourceVariable.hasOwnProperty(mixinFunction)) { - this[mixinFunction] = sourceVariable[mixinFunction]; - } + if (!this.groups.hasOwnProperty(label)) { + this.groups[label] = graphOptions; } + this.amountOfGroups += 1; }; - - /** - * removes a mixin from the network object. - * - * @param {Object} sourceVariable | this object has to contain functions. - * @private - */ - exports._clearMixin = function (sourceVariable) { - for (var mixinFunction in sourceVariable) { - if (sourceVariable.hasOwnProperty(mixinFunction)) { - this[mixinFunction] = undefined; - } - } + Legend.prototype.updateGroup = function(label, graphOptions) { + this.groups[label] = graphOptions; }; - - /** - * Mixin the physics system and initialize the parameters required. - * - * @private - */ - exports._loadPhysicsSystem = function () { - this._loadMixin(PhysicsMixin); - this._loadSelectedForceSolver(); - if (this.constants.configurePhysics == true) { - this._loadPhysicsConfiguration(); - } - else { - this._cleanupPhysicsConfiguration(); + Legend.prototype.removeGroup = function(label) { + if (this.groups.hasOwnProperty(label)) { + delete this.groups[label]; + this.amountOfGroups -= 1; } }; + Legend.prototype._create = function() { + this.dom.frame = document.createElement('div'); + this.dom.frame.className = 'legend'; + this.dom.frame.style.position = "absolute"; + this.dom.frame.style.top = "10px"; + this.dom.frame.style.display = "block"; - /** - * Mixin the cluster system and initialize the parameters required. - * - * @private - */ - exports._loadClusterSystem = function () { - this.clusterSession = 0; - this.hubThreshold = 5; - this._loadMixin(ClusterMixin); - }; - - - /** - * Mixin the sector system and initialize the parameters required - * - * @private - */ - exports._loadSectorSystem = function () { - this.sectors = {}; - this.activeSector = ["default"]; - this.sectors["active"] = {}; - this.sectors["active"]["default"] = {"nodes": {}, - "edges": {}, - "nodeIndices": [], - "formationScale": 1.0, - "drawingNode": undefined }; - this.sectors["frozen"] = {}; - this.sectors["support"] = {"nodes": {}, - "edges": {}, - "nodeIndices": [], - "formationScale": 1.0, - "drawingNode": undefined }; + this.dom.textArea = document.createElement('div'); + this.dom.textArea.className = 'legendText'; + this.dom.textArea.style.position = "relative"; + this.dom.textArea.style.top = "0px"; - this.nodeIndices = this.sectors["active"]["default"]["nodeIndices"]; // the node indices list is used to speed up the computation of the repulsion fields + this.svg = document.createElementNS('http://www.w3.org/2000/svg',"svg"); + this.svg.style.position = 'absolute'; + this.svg.style.top = 0 +'px'; + this.svg.style.width = this.options.iconSize + 5 + 'px'; + this.svg.style.height = '100%'; - this._loadMixin(SectorsMixin); + this.dom.frame.appendChild(this.svg); + this.dom.frame.appendChild(this.dom.textArea); }; - /** - * Mixin the selection system and initialize the parameters required - * - * @private + * Hide the component from the DOM */ - exports._loadSelectionSystem = function () { - this.selectionObj = {nodes: {}, edges: {}}; - - this._loadMixin(SelectionMixin); + Legend.prototype.hide = function() { + // remove the frame containing the items + if (this.dom.frame.parentNode) { + this.dom.frame.parentNode.removeChild(this.dom.frame); + } }; - /** - * Mixin the navigationUI (User Interface) system and initialize the parameters required - * - * @private + * Show the component in the DOM (when not already visible). + * @return {Boolean} changed */ - exports._loadManipulationSystem = function () { - // reset global variables -- these are used by the selection of nodes and edges. - this.blockConnectingEdgeSelection = false; - this.forceAppendSelection = false; + Legend.prototype.show = function() { + // show frame containing the items + if (!this.dom.frame.parentNode) { + this.body.dom.center.appendChild(this.dom.frame); + } + }; - if (this.constants.dataManipulation.enabled == true) { - // load the manipulator HTML elements. All styling done in css. - if (this.manipulationDiv === undefined) { - this.manipulationDiv = document.createElement('div'); - this.manipulationDiv.className = 'network-manipulationDiv'; - if (this.editMode == true) { - this.manipulationDiv.style.display = "block"; - } - else { - this.manipulationDiv.style.display = "none"; - } - this.frame.appendChild(this.manipulationDiv); - } + Legend.prototype.setOptions = function(options) { + var fields = ['enabled','orientation','icons','left','right']; + util.selectiveDeepExtend(fields, this.options, options); + }; - if (this.editModeDiv === undefined) { - this.editModeDiv = document.createElement('div'); - this.editModeDiv.className = 'network-manipulation-editMode'; - if (this.editMode == true) { - this.editModeDiv.style.display = "none"; - } - else { - this.editModeDiv.style.display = "block"; + Legend.prototype.redraw = function() { + var activeGroups = 0; + for (var groupId in this.groups) { + if (this.groups.hasOwnProperty(groupId)) { + if (this.groups[groupId].visible == true && (this.linegraphOptions.visibility[groupId] === undefined || this.linegraphOptions.visibility[groupId] == true)) { + activeGroups++; } - this.frame.appendChild(this.editModeDiv); - } - - if (this.closeDiv === undefined) { - this.closeDiv = document.createElement('div'); - this.closeDiv.className = 'network-manipulation-closeDiv'; - this.closeDiv.style.display = this.manipulationDiv.style.display; - this.frame.appendChild(this.closeDiv); } + } - // load the manipulation functions - this._loadMixin(ManipulationMixin); - - // create the manipulator toolbar - this._createManipulatorBar(); + if (this.options[this.side].visible == false || this.amountOfGroups == 0 || this.options.enabled == false || activeGroups == 0) { + this.hide(); } else { - if (this.manipulationDiv !== undefined) { - // removes all the bindings and overloads - this._createManipulatorBar(); - - // remove the manipulation divs - this.frame.removeChild(this.manipulationDiv); - this.frame.removeChild(this.editModeDiv); - this.frame.removeChild(this.closeDiv); + this.show(); + if (this.options[this.side].position == 'top-left' || this.options[this.side].position == 'bottom-left') { + this.dom.frame.style.left = '4px'; + this.dom.frame.style.textAlign = "left"; + this.dom.textArea.style.textAlign = "left"; + this.dom.textArea.style.left = (this.options.iconSize + 15) + 'px'; + this.dom.textArea.style.right = ''; + this.svg.style.left = 0 +'px'; + this.svg.style.right = ''; + } + else { + this.dom.frame.style.right = '4px'; + this.dom.frame.style.textAlign = "right"; + this.dom.textArea.style.textAlign = "right"; + this.dom.textArea.style.right = (this.options.iconSize + 15) + 'px'; + this.dom.textArea.style.left = ''; + this.svg.style.right = 0 +'px'; + this.svg.style.left = ''; + } - this.manipulationDiv = undefined; - this.editModeDiv = undefined; - this.closeDiv = undefined; - // remove the mixin functions - this._clearMixin(ManipulationMixin); + if (this.options[this.side].position == 'top-left' || this.options[this.side].position == 'top-right') { + this.dom.frame.style.top = 4 - Number(this.body.dom.center.style.top.replace("px","")) + 'px'; + this.dom.frame.style.bottom = ''; + } + else { + var scrollableHeight = this.body.domProps.center.height - this.body.domProps.centerContainer.height; + this.dom.frame.style.bottom = 4 + scrollableHeight + Number(this.body.dom.center.style.top.replace("px","")) + 'px'; + this.dom.frame.style.top = ''; } - } - }; + if (this.options.icons == false) { + this.dom.frame.style.width = this.dom.textArea.offsetWidth + 10 + 'px'; + this.dom.textArea.style.right = ''; + this.dom.textArea.style.left = ''; + this.svg.style.width = '0px'; + } + else { + this.dom.frame.style.width = this.options.iconSize + 15 + this.dom.textArea.offsetWidth + 10 + 'px' + this.drawLegendIcons(); + } - /** - * Mixin the navigation (User Interface) system and initialize the parameters required - * - * @private - */ - exports._loadNavigationControls = function () { - this._loadMixin(NavigationMixin); - // the clean function removes the button divs, this is done to remove the bindings. - this._cleanNavigation(); - if (this.constants.navigation.enabled == true) { - this._loadNavigationElements(); + var content = ''; + for (var groupId in this.groups) { + if (this.groups.hasOwnProperty(groupId)) { + if (this.groups[groupId].visible == true && (this.linegraphOptions.visibility[groupId] === undefined || this.linegraphOptions.visibility[groupId] == true)) { + content += this.groups[groupId].content + '
'; + } + } + } + this.dom.textArea.innerHTML = content; + this.dom.textArea.style.lineHeight = ((0.75 * this.options.iconSize) + this.options.iconSpacing) + 'px'; } }; + Legend.prototype.drawLegendIcons = function() { + if (this.dom.frame.parentNode) { + DOMutil.prepareElements(this.svgElements); + var padding = window.getComputedStyle(this.dom.frame).paddingTop; + var iconOffset = Number(padding.replace('px','')); + var x = iconOffset; + var iconWidth = this.options.iconSize; + var iconHeight = 0.75 * this.options.iconSize; + var y = iconOffset + 0.5 * iconHeight + 3; - /** - * Mixin the hierarchical layout system. - * - * @private - */ - exports._loadHierarchySystem = function () { - this._loadMixin(HierarchicalLayoutMixin); - }; - - -/***/ }, -/* 55 */ -/***/ function(module, exports, __webpack_require__) { - - var keycharm = __webpack_require__(63); - var Emitter = __webpack_require__(56); - var Hammer = __webpack_require__(45); - var util = __webpack_require__(1); - - /** - * Turn an element into an clickToUse element. - * When not active, the element has a transparent overlay. When the overlay is - * clicked, the mode is changed to active. - * When active, the element is displayed with a blue border around it, and - * the interactive contents of the element can be used. When clicked outside - * the element, the elements mode is changed to inactive. - * @param {Element} container - * @constructor - */ - function Activator(container) { - this.active = false; - - this.dom = { - container: container - }; - - this.dom.overlay = document.createElement('div'); - this.dom.overlay.className = 'overlay'; - - this.dom.container.appendChild(this.dom.overlay); - - this.hammer = Hammer(this.dom.overlay, {prevent_default: false}); - this.hammer.on('tap', this._onTapOverlay.bind(this)); - - // block all touch events (except tap) - var me = this; - var events = [ - 'touch', 'pinch', - 'doubletap', 'hold', - 'dragstart', 'drag', 'dragend', - 'mousewheel', 'DOMMouseScroll' // DOMMouseScroll is needed for Firefox - ]; - events.forEach(function (event) { - me.hammer.on(event, function (event) { - event.stopPropagation(); - }); - }); + this.svg.style.width = iconWidth + 5 + iconOffset + 'px'; - // attach a tap event to the window, in order to deactivate when clicking outside the timeline - this.windowHammer = Hammer(window, {prevent_default: false}); - this.windowHammer.on('tap', function (event) { - // deactivate when clicked outside the container - if (!_hasParent(event.target, container)) { - me.deactivate(); + for (var groupId in this.groups) { + if (this.groups.hasOwnProperty(groupId)) { + if (this.groups[groupId].visible == true && (this.linegraphOptions.visibility[groupId] === undefined || this.linegraphOptions.visibility[groupId] == true)) { + this.groups[groupId].drawIcon(x, y, this.svgElements, this.svg, iconWidth, iconHeight); + y += iconHeight + this.options.iconSpacing; + } + } } - }); - - if (this.keycharm !== undefined) { - this.keycharm.destroy(); - } - this.keycharm = keycharm(); - - // keycharm listener only bounded when active) - this.escListener = this.deactivate.bind(this); - } - - // turn into an event emitter - Emitter(Activator.prototype); - - // The currently active activator - Activator.current = null; - - /** - * Destroy the activator. Cleans up all created DOM and event listeners - */ - Activator.prototype.destroy = function () { - this.deactivate(); - - // remove dom - this.dom.overlay.parentNode.removeChild(this.dom.overlay); - - // cleanup hammer instances - this.hammer = null; - this.windowHammer = null; - // FIXME: cleaning up hammer instances doesn't work (Timeline not removed from memory) - }; - /** - * Activate the element - * Overlay is hidden, element is decorated with a blue shadow border - */ - Activator.prototype.activate = function () { - // we allow only one active activator at a time - if (Activator.current) { - Activator.current.deactivate(); + DOMutil.cleanupElements(this.svgElements); } - Activator.current = this; - - this.active = true; - this.dom.overlay.style.display = 'none'; - util.addClassName(this.dom.container, 'vis-active'); - - this.emit('change'); - this.emit('activate'); - - // ugly hack: bind ESC after emitting the events, as the Network rebinds all - // keyboard events on a 'change' event - this.keycharm.bind('esc', this.escListener); - }; - - /** - * Deactivate the element - * Overlay is displayed on top of the element - */ - Activator.prototype.deactivate = function () { - this.active = false; - this.dom.overlay.style.display = ''; - util.removeClassName(this.dom.container, 'vis-active'); - this.keycharm.unbind('esc', this.escListener); - - this.emit('change'); - this.emit('deactivate'); - }; - - /** - * Handle a tap event: activate the container - * @param event - * @private - */ - Activator.prototype._onTapOverlay = function (event) { - // activate the container - this.activate(); - event.stopPropagation(); }; - /** - * Test whether the element has the requested parent element somewhere in - * its chain of parent nodes. - * @param {HTMLElement} element - * @param {HTMLElement} parent - * @returns {boolean} Returns true when the parent is found somewhere in the - * chain of parent nodes. - * @private - */ - function _hasParent(element, parent) { - while (element) { - if (element === parent) { - return true - } - element = element.parentNode; - } - return false; - } - - module.exports = Activator; + module.exports = Legend; /***/ }, -/* 56 */ +/* 51 */ /***/ function(module, exports, __webpack_require__) { - - /** - * Expose `Emitter`. - */ + var Emitter = __webpack_require__(11); + var Hammer = __webpack_require__(19); + var keycharm = __webpack_require__(36); + var util = __webpack_require__(1); + var hammerUtil = __webpack_require__(22); + var DataSet = __webpack_require__(7); + var DataView = __webpack_require__(9); + var dotparser = __webpack_require__(52); + var gephiParser = __webpack_require__(53); + var Groups = __webpack_require__(54); + var Images = __webpack_require__(55); + var Node = __webpack_require__(56); + var Edge = __webpack_require__(57); + var Popup = __webpack_require__(58); + var MixinLoader = __webpack_require__(59); + var Activator = __webpack_require__(35); + var locales = __webpack_require__(70); - module.exports = Emitter; + // Load custom shapes into CanvasRenderingContext2D + __webpack_require__(71); /** - * Initialize a new `Emitter`. + * @constructor Network + * Create a network visualization, displaying nodes and edges. * - * @api public + * @param {Element} container The DOM element in which the Network will + * be created. Normally a div element. + * @param {Object} data An object containing parameters + * {Array} nodes + * {Array} edges + * @param {Object} options Options */ + function Network (container, data, options) { + if (!(this instanceof Network)) { + throw new SyntaxError('Constructor must be called with the new operator'); + } - function Emitter(obj) { - if (obj) return mixin(obj); - }; + this._determineBrowserMethod(); + this._initializeMixinLoaders(); - /** - * Mixin the emitter properties. - * - * @param {Object} obj - * @return {Object} - * @api private - */ + // create variables and set default values + this.containerElement = container; - function mixin(obj) { - for (var key in Emitter.prototype) { - obj[key] = Emitter.prototype[key]; - } - return obj; - } + // render and calculation settings + this.renderRefreshRate = 60; // hz (fps) + this.renderTimestep = 1000 / this.renderRefreshRate; // ms -- saves calculation later on + this.renderTime = 0; // measured time it takes to render a frame + this.physicsTime = 0; // measured time it takes to render a frame + this.runDoubleSpeed = false; + this.physicsDiscreteStepsize = 0.50; // discrete stepsize of the simulation - /** - * Listen on the given `event` with `fn`. - * - * @param {String} event - * @param {Function} fn - * @return {Emitter} - * @api public - */ + this.initializing = true; - Emitter.prototype.on = - Emitter.prototype.addEventListener = function(event, fn){ - this._callbacks = this._callbacks || {}; - (this._callbacks[event] = this._callbacks[event] || []) - .push(fn); - return this; - }; + this.triggerFunctions = {add:null,edit:null,editEdge:null,connect:null,del:null}; - /** - * Adds an `event` listener that will be invoked a single - * time then automatically removed. - * - * @param {String} event - * @param {Function} fn - * @return {Emitter} - * @api public - */ + // set constant values + this.defaultOptions = { + nodes: { + mass: 1, + radiusMin: 10, + radiusMax: 30, + radius: 10, + shape: 'ellipse', + image: undefined, + widthMin: 16, // px + widthMax: 64, // px + fontColor: 'black', + fontSize: 14, // px + fontFace: 'verdana', + fontFill: undefined, + fontStrokeWidth: 0, // px + fontStrokeColor: 'white', + level: -1, + color: { + border: '#2B7CE9', + background: '#97C2FC', + highlight: { + border: '#2B7CE9', + background: '#D2E5FF' + }, + hover: { + border: '#2B7CE9', + background: '#D2E5FF' + } + }, + group: undefined, + borderWidth: 1, + borderWidthSelected: undefined + }, + edges: { + widthMin: 1, // + widthMax: 15,// + width: 1, + widthSelectionMultiplier: 2, + hoverWidth: 1.5, + style: 'line', + color: { + color:'#848484', + highlight:'#848484', + hover: '#848484' + }, + fontColor: '#343434', + fontSize: 14, // px + fontFace: 'arial', + fontFill: 'white', + fontStrokeWidth: 0, // px + fontStrokeColor: 'white', + labelAlignment:'horizontal', + arrowScaleFactor: 1, + dash: { + length: 10, + gap: 5, + altLength: undefined + }, + inheritColor: "from" // to, from, false, true (== from) + }, + configurePhysics:false, + physics: { + barnesHut: { + enabled: true, + thetaInverted: 1 / 0.5, // inverted to save time during calculation + gravitationalConstant: -2000, + centralGravity: 0.3, + springLength: 95, + springConstant: 0.04, + damping: 0.09 + }, + repulsion: { + centralGravity: 0.0, + springLength: 200, + springConstant: 0.05, + nodeDistance: 100, + damping: 0.09 + }, + hierarchicalRepulsion: { + enabled: false, + centralGravity: 0.0, + springLength: 100, + springConstant: 0.01, + nodeDistance: 150, + damping: 0.09 + }, + damping: null, + centralGravity: null, + springLength: null, + springConstant: null + }, + clustering: { // Per Node in Cluster = PNiC + enabled: false, // (Boolean) | global on/off switch for clustering. + initialMaxNodes: 100, // (# nodes) | if the initial amount of nodes is larger than this, we cluster until the total number is less than this threshold. + clusterThreshold:500, // (# nodes) | during calculate forces, we check if the total number of nodes is larger than this. If it is, cluster until reduced to reduceToNodes + reduceToNodes:300, // (# nodes) | during calculate forces, we check if the total number of nodes is larger than clusterThreshold. If it is, cluster until reduced to this + chainThreshold: 0.4, // (% of all drawn nodes)| maximum percentage of allowed chainnodes (long strings of connected nodes) within all nodes. (lower means less chains). + clusterEdgeThreshold: 20, // (px) | edge length threshold. if smaller, this node is clustered. + sectorThreshold: 100, // (# nodes in cluster) | cluster size threshold. If larger, expanding in own sector. + screenSizeThreshold: 0.2, // (% of canvas) | relative size threshold. If the width or height of a clusternode takes up this much of the screen, decluster node. + fontSizeMultiplier: 4.0, // (px PNiC) | how much the cluster font size grows per node in cluster (in px). + maxFontSize: 1000, + forceAmplification: 0.1, // (multiplier PNiC) | factor of increase fo the repulsion force of a cluster (per node in cluster). + distanceAmplification: 0.1, // (multiplier PNiC) | factor how much the repulsion distance of a cluster increases (per node in cluster). + edgeGrowth: 20, // (px PNiC) | amount of clusterSize connected to the edge is multiplied with this and added to edgeLength. + nodeScaling: {width: 1, // (px PNiC) | growth of the width per node in cluster. + height: 1, // (px PNiC) | growth of the height per node in cluster. + radius: 1}, // (px PNiC) | growth of the radius per node in cluster. + maxNodeSizeIncrements: 600, // (# increments) | max growth of the width per node in cluster. + activeAreaBoxSize: 80, // (px) | box area around the curser where clusters are popped open. + clusterLevelDifference: 2, + clusterByZoom: true // enable clustering through zooming in and out + }, + navigation: { + enabled: false + }, + keyboard: { + enabled: false, + speed: {x: 10, y: 10, zoom: 0.02}, + bindToWindow: true + }, + dataManipulation: { + enabled: false, + initiallyVisible: false + }, + hierarchicalLayout: { + enabled:false, + levelSeparation: 150, + nodeSpacing: 100, + direction: "UD", // UD, DU, LR, RL + layout: "hubsize" // hubsize, directed + }, + freezeForStabilization: false, + smoothCurves: { + enabled: true, + dynamic: true, + type: "continuous", + roundness: 0.5 + }, + maxVelocity: 30, + minVelocity: 0.1, // px/s + stabilize: true, // stabilize before displaying the network + stabilizationIterations: 1000, // maximum number of iteration to stabilize + zoomExtentOnStabilize: true, + locale: 'en', + locales: locales, + tooltip: { + delay: 300, + fontColor: 'black', + fontSize: 14, // px + fontFace: 'verdana', + color: { + border: '#666', + background: '#FFFFC6' + } + }, + dragNetwork: true, + dragNodes: true, + zoomable: true, + hover: false, + hideEdgesOnDrag: false, + hideNodesOnDrag: false, + width : '100%', + height : '100%', + selectable: true + }; + this.constants = util.extend({}, this.defaultOptions); + this.pixelRatio = 1; + + + this.hoverObj = {nodes:{},edges:{}}; + this.controlNodesActive = false; + this.navigationHammers = {existing:[], _new: []}; - Emitter.prototype.once = function(event, fn){ - var self = this; - this._callbacks = this._callbacks || {}; + // animation properties + this.animationSpeed = 1/this.renderRefreshRate; + this.animationEasingFunction = "easeInOutQuint"; + this.animating = false; + this.easingTime = 0; + this.sourceScale = 0; + this.targetScale = 0; + this.sourceTranslation = 0; + this.targetTranslation = 0; + this.lockedOnNodeId = null; + this.lockedOnNodeOffset = null; + this.touchTime = 0; - function on() { - self.off(event, on); - fn.apply(this, arguments); - } + // Node variables + var network = this; + this.groups = new Groups(); // object with groups + this.images = new Images(); // object with images + this.images.setOnloadCallback(function (status) { + network._redraw(); + }); - on.fn = fn; - this.on(event, on); - return this; - }; + // keyboard navigation variables + this.xIncrement = 0; + this.yIncrement = 0; + this.zoomIncrement = 0; - /** - * Remove the given callback for `event` or all - * registered callbacks. - * - * @param {String} event - * @param {Function} fn - * @return {Emitter} - * @api public - */ + // loading all the mixins: + // load the force calculation functions, grouped under the physics system. + this._loadPhysicsSystem(); + // create a frame and canvas + this._create(); + // load the sector system. (mandatory, fully integrated with Network) + this._loadSectorSystem(); + // load the cluster system. (mandatory, even when not using the cluster system, there are function calls to it) + this._loadClusterSystem(); + // load the selection system. (mandatory, required by Network) + this._loadSelectionSystem(); + // load the selection system. (mandatory, required by Network) + this._loadHierarchySystem(); - 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; - } + // apply options + this._setTranslation(this.frame.clientWidth / 2, this.frame.clientHeight / 2); + this._setScale(1); + this.setOptions(options); - // specific event - var callbacks = this._callbacks[event]; - if (!callbacks) return this; + // other vars + this.freezeSimulation = false;// freeze the simulation + this.cachedFunctions = {}; + this.startedStabilization = false; + this.stabilized = false; + this.stabilizationIterations = null; + this.draggingNodes = false; - // remove all handlers - if (1 == arguments.length) { - delete this._callbacks[event]; - return this; - } + // containers for nodes and edges + this.calculationNodes = {}; + this.calculationNodeIndices = []; + this.nodeIndices = []; // array with all the indices of the nodes. Used to speed up forces calculation + this.nodes = {}; // object with Node objects + this.edges = {}; // object with Edge objects - // 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; + // position and scale variables and objects + this.canvasTopLeft = {"x": 0,"y": 0}; // coordinates of the top left of the canvas. they will be set during _redraw. + this.canvasBottomRight = {"x": 0,"y": 0}; // coordinates of the bottom right of the canvas. they will be set during _redraw + this.pointerPosition = {"x": 0,"y": 0}; // coordinates of the bottom right of the canvas. they will be set during _redraw + this.areaCenter = {}; // object with x and y elements used for determining the center of the zoom action + this.scale = 1; // defining the global scale variable in the constructor + this.previousScale = this.scale; // this is used to check if the zoom operation is zooming in or out + + // datasets or dataviews + this.nodesData = null; // A DataSet or DataView + this.edgesData = null; // A DataSet or DataView + + // create event listeners used to subscribe on the DataSets of the nodes and edges + this.nodesListeners = { + 'add': function (event, params) { + network._addNodes(params.items); + network.start(); + }, + 'update': function (event, params) { + network._updateNodes(params.items, params.data); + network.start(); + }, + 'remove': function (event, params) { + network._removeNodes(params.items); + network.start(); } - } - return this; - }; + }; + this.edgesListeners = { + 'add': function (event, params) { + network._addEdges(params.items); + network.start(); + }, + 'update': function (event, params) { + network._updateEdges(params.items); + network.start(); + }, + 'remove': function (event, params) { + network._removeEdges(params.items); + network.start(); + } + }; - /** - * Emit `event` with the given args. - * - * @param {String} event - * @param {Mixed} ... - * @return {Emitter} - */ + // properties for the animation + this.moving = true; + this.timer = undefined; // Scheduling function. Is definded in this.start(); - Emitter.prototype.emit = function(event){ - this._callbacks = this._callbacks || {}; - var args = [].slice.call(arguments, 1) - , callbacks = this._callbacks[event]; + // load data (the disable start variable will be the same as the enabled clustering) + this.setData(data,this.constants.clustering.enabled || this.constants.hierarchicalLayout.enabled); - if (callbacks) { - callbacks = callbacks.slice(0); - for (var i = 0, len = callbacks.length; i < len; ++i) { - callbacks[i].apply(this, args); + // hierarchical layout + this.initializing = false; + if (this.constants.hierarchicalLayout.enabled == true) { + this._setupHierarchicalLayout(); + } + else { + // zoom so all data will fit on the screen, if clustering is enabled, we do not want start to be called here. + if (this.constants.stabilize == false) { + this.zoomExtent(undefined, true,this.constants.clustering.enabled); } } - return this; - }; + // if clustering is disabled, the simulation will have started in the setData function + if (this.constants.clustering.enabled) { + this.startWithClustering(); + } + } + + // Extend Network with an Emitter mixin + Emitter(Network.prototype); /** - * Return array of callbacks for `event`. - * - * @param {String} event - * @return {Array} - * @api public + * Determine if the browser requires a setTimeout or a requestAnimationFrame. This was required because + * some implementations (safari and IE9) did not support requestAnimationFrame + * @private */ + Network.prototype._determineBrowserMethod = function() { + var browserType = navigator.userAgent.toLowerCase(); + this.requiresTimeout = false; + if (browserType.indexOf('msie 9.0') != -1) { // IE 9 + this.requiresTimeout = true; + } + else if (browserType.indexOf('safari') != -1) { // safari + if (browserType.indexOf('chrome') <= -1) { + this.requiresTimeout = true; + } + } + } - Emitter.prototype.listeners = function(event){ - this._callbacks = this._callbacks || {}; - return this._callbacks[event] || []; - }; /** - * Check if this emitter has `event` handlers. + * Get the script path where the vis.js library is located * - * @param {String} event - * @return {Boolean} - * @api public + * @returns {string | null} path Path or null when not found. Path does not + * end with a slash. + * @private */ + Network.prototype._getScriptPath = function() { + var scripts = document.getElementsByTagName( 'script' ); - Emitter.prototype.hasListeners = function(event){ - return !! this.listeners(event).length; - }; + // find script named vis.js or vis.min.js + for (var i = 0; i < scripts.length; i++) { + var src = scripts[i].src; + var match = src && /\/?vis(.min)?\.js$/.exec(src); + if (match) { + // return path without the script name + return src.substring(0, src.length - match[0].length); + } + } + return null; + }; -/***/ }, -/* 57 */ -/***/ function(module, exports, __webpack_require__) { /** - * Creation of the ClusterMixin var. - * - * This contains all the functions the Network object can use to employ clustering + * Find the center position of the network + * @private */ + Network.prototype._getRange = function() { + var minY = 1e9, maxY = -1e9, minX = 1e9, maxX = -1e9, node; + for (var nodeId in this.nodes) { + if (this.nodes.hasOwnProperty(nodeId)) { + node = this.nodes[nodeId]; + if (minX > (node.boundingBox.left)) {minX = node.boundingBox.left;} + if (maxX < (node.boundingBox.right)) {maxX = node.boundingBox.right;} + if (minY > (node.boundingBox.bottom)) {minY = node.boundingBox.top;} // top is negative, bottom is positive + if (maxY < (node.boundingBox.top)) {maxY = node.boundingBox.bottom;} // top is negative, bottom is positive + } + } + if (minX == 1e9 && maxX == -1e9 && minY == 1e9 && maxY == -1e9) { + minY = 0, maxY = 0, minX = 0, maxX = 0; + } + return {minX: minX, maxX: maxX, minY: minY, maxY: maxY}; + }; - /** - * This is only called in the constructor of the network object - * - */ - exports.startWithClustering = function() { - // cluster if the data set is big - this.clusterToFit(this.constants.clustering.initialMaxNodes, true); - - // updates the lables after clustering - this.updateLabels(); - // this is called here because if clusterin is disabled, the start and stabilize are called in - // the setData function. - if (this.stabilize) { - this._stabilize(); - } - this.start(); + /** + * @param {object} range = {minX: minX, maxX: maxX, minY: minY, maxY: maxY}; + * @returns {{x: number, y: number}} + * @private + */ + Network.prototype._findCenter = function(range) { + return {x: (0.5 * (range.maxX + range.minX)), + y: (0.5 * (range.maxY + range.minY))}; }; + /** - * This function clusters until the initialMaxNodes has been reached + * This function zooms out to fit all data on screen based on amount of nodes * - * @param {Number} maxNumberOfNodes - * @param {Boolean} reposition + * @param {Boolean} [initialZoom] | zoom based on fitted formula or range, true = fitted, default = false; + * @param {Boolean} [disableStart] | If true, start is not called. */ - exports.clusterToFit = function(maxNumberOfNodes, reposition) { - var numberOfNodes = this.nodeIndices.length; + Network.prototype.zoomExtent = function(animationOptions, initialZoom, disableStart) { + this._redraw(true); - var maxLevels = 50; - var level = 0; + if (initialZoom === undefined) {initialZoom = false;} + if (disableStart === undefined) {disableStart = false;} + if (animationOptions === undefined) {animationOptions = false;} - // we first cluster the hubs, then we pull in the outliers, repeat - while (numberOfNodes > maxNumberOfNodes && level < maxLevels) { - if (level % 3 == 0) { - this.forceAggregateHubs(true); - this.normalizeClusterLevels(); + var range = this._getRange(); + var zoomLevel; + + if (initialZoom == true) { + var numberOfNodes = this.nodeIndices.length; + if (this.constants.smoothCurves == true) { + if (this.constants.clustering.enabled == true && + numberOfNodes >= this.constants.clustering.initialMaxNodes) { + zoomLevel = 49.07548 / (numberOfNodes + 142.05338) + 9.1444e-04; // this is obtained from fitting a dataset from 5 points with scale levels that looked good. + } + else { + zoomLevel = 12.662 / (numberOfNodes + 7.4147) + 0.0964822; // this is obtained from fitting a dataset from 5 points with scale levels that looked good. + } } else { - this.increaseClusterLevel(); // this also includes a cluster normalization + if (this.constants.clustering.enabled == true && + numberOfNodes >= this.constants.clustering.initialMaxNodes) { + zoomLevel = 77.5271985 / (numberOfNodes + 187.266146) + 4.76710517e-05; // this is obtained from fitting a dataset from 5 points with scale levels that looked good. + } + else { + zoomLevel = 30.5062972 / (numberOfNodes + 19.93597763) + 0.08413486; // this is obtained from fitting a dataset from 5 points with scale levels that looked good. + } } - numberOfNodes = this.nodeIndices.length; - level += 1; + // correct for larger canvasses. + var factor = Math.min(this.frame.canvas.clientWidth / 600, this.frame.canvas.clientHeight / 600); + zoomLevel *= factor; } + else { + var xDistance = Math.abs(range.maxX - range.minX) * 1.1; + var yDistance = Math.abs(range.maxY - range.minY) * 1.1; - // after the clustering we reposition the nodes to reduce the initial chaos - if (level > 0 && reposition == true) { - this.repositionNodes(); + var xZoomLevel = this.frame.canvas.clientWidth / xDistance; + var yZoomLevel = this.frame.canvas.clientHeight / yDistance; + zoomLevel = (xZoomLevel <= yZoomLevel) ? xZoomLevel : yZoomLevel; } - this._updateCalculationNodes(); - }; - - /** - * This function can be called to open up a specific cluster. It is only called by - * It will unpack the cluster back one level. - * - * @param node | Node object: cluster to open. - */ - exports.openCluster = function(node) { - var isMovingBeforeClustering = this.moving; - if (node.clusterSize > this.constants.clustering.sectorThreshold && this._nodeInActiveArea(node) && - !(this._sector() == "default" && this.nodeIndices.length == 1)) { - // this loads a new sector, loads the nodes and edges and nodeIndices of it. - this._addSector(node); - var level = 0; - - // we decluster until we reach a decent number of nodes - while ((this.nodeIndices.length < this.constants.clustering.initialMaxNodes) && (level < 10)) { - this.decreaseClusterLevel(); - level += 1; - } + if (zoomLevel > 1.0) { + zoomLevel = 1.0; } - else { - this._expandClusterNode(node,false,true); - // update the index list, dynamic edges and labels - this._updateNodeIndexList(); - this._updateDynamicEdges(); - this._updateCalculationNodes(); - this.updateLabels(); - } - // if the simulation was settled, we restart the simulation if a cluster has been formed or expanded - if (this.moving != isMovingBeforeClustering) { + var center = this._findCenter(range); + if (disableStart == false) { + var options = {position: center, scale: zoomLevel, animation: animationOptions}; + this.moveTo(options); + this.moving = true; this.start(); } - }; - - - /** - * This calls the updateClustes with default arguments - */ - exports.updateClustersDefault = function() { - if (this.constants.clustering.enabled == true) { - this.updateClusters(0,false,false); + else { + center.x *= zoomLevel; + center.y *= zoomLevel; + center.x -= 0.5 * this.frame.canvas.clientWidth; + center.y -= 0.5 * this.frame.canvas.clientHeight; + this._setScale(zoomLevel); + this._setTranslation(-center.x,-center.y); } }; /** - * This function can be called to increase the cluster level. This means that the nodes with only one edge connection will - * be clustered with their connected node. This can be repeated as many times as needed. - * This can be called externally (by a keybind for instance) to reduce the complexity of big datasets. - */ - exports.increaseClusterLevel = function() { - this.updateClusters(-1,false,true); - }; - - - /** - * This function can be called to decrease the cluster level. This means that the nodes with only one edge connection will - * be unpacked if they are a cluster. This can be repeated as many times as needed. - * This can be called externally (by a key-bind for instance) to look into clusters without zooming. + * Update the this.nodeIndices with the most recent node index list + * @private */ - exports.decreaseClusterLevel = function() { - this.updateClusters(1,false,true); + Network.prototype._updateNodeIndexList = function() { + this._clearNodeIndexList(); + for (var idx in this.nodes) { + if (this.nodes.hasOwnProperty(idx)) { + this.nodeIndices.push(idx); + } + } }; /** - * This is the main clustering function. It clusters and declusters on zoom or forced - * This function clusters on zoom, it can be called with a predefined zoom direction - * If out, check if we can form clusters, if in, check if we can open clusters. - * This function is only called from _zoom() - * - * @param {Number} zoomDirection | -1 / 0 / +1 for zoomOut / determineByZoom / zoomIn - * @param {Boolean} recursive | enabled or disable recursive calling of the opening of clusters - * @param {Boolean} force | enabled or disable forcing - * @param {Boolean} doNotStart | if true do not call start + * Set nodes and edges, and optionally options as well. * + * @param {Object} data Object containing parameters: + * {Array | DataSet | DataView} [nodes] Array with nodes + * {Array | DataSet | DataView} [edges] Array with edges + * {String} [dot] String containing data in DOT format + * {String} [gephi] String containing data in gephi JSON format + * {Options} [options] Object with options + * @param {Boolean} [disableStart] | optional: disable the calling of the start function. */ - exports.updateClusters = function(zoomDirection,recursive,force,doNotStart) { - var isMovingBeforeClustering = this.moving; - var amountOfNodes = this.nodeIndices.length; - - // on zoom out collapse the sector if the scale is at the level the sector was made - if (this.previousScale > this.scale && zoomDirection == 0) { - this._collapseSector(); - } - - // check if we zoom in or out - if (this.previousScale > this.scale || zoomDirection == -1) { // zoom out - // forming clusters when forced pulls outliers in. When not forced, the edge length of the - // outer nodes determines if it is being clustered - this._formClusters(force); - } - else if (this.previousScale < this.scale || zoomDirection == 1) { // zoom in - if (force == true) { - // _openClusters checks for each node if the formationScale of the cluster is smaller than - // the current scale and if so, declusters. When forced, all clusters are reduced by one step - this._openClusters(recursive,force); - } - else { - // if a cluster takes up a set percentage of the active window - this._openClustersBySize(); - } - } - this._updateNodeIndexList(); - - // if a cluster was NOT formed and the user zoomed out, we try clustering by hubs - if (this.nodeIndices.length == amountOfNodes && (this.previousScale > this.scale || zoomDirection == -1)) { - this._aggregateHubs(force); - this._updateNodeIndexList(); + Network.prototype.setData = function(data, disableStart) { + if (disableStart === undefined) { + disableStart = false; } + // we set initializing to true to ensure that the hierarchical layout is not performed until both nodes and edges are added. + this.initializing = true; - // we now reduce chains. - if (this.previousScale > this.scale || zoomDirection == -1) { // zoom out - this.handleChains(); - this._updateNodeIndexList(); + if (data && data.dot && (data.nodes || data.edges)) { + throw new SyntaxError('Data must contain either parameter "dot" or ' + + ' parameter pair "nodes" and "edges", but not both.'); } - this.previousScale = this.scale; - - // rest of the update the index list, dynamic edges and labels - this._updateDynamicEdges(); - this.updateLabels(); - - // if a cluster was formed, we increase the clusterSession - if (this.nodeIndices.length < amountOfNodes) { // this means a clustering operation has taken place - this.clusterSession += 1; - // if clusters have been made, we normalize the cluster level - this.normalizeClusterLevels(); + // clean up in case there is anyone in an active mode of the manipulation. This is the same option as bound to the escape button. + if (this.constants.dataManipulation.enabled == true) { + this._createManipulatorBar(); } - if (doNotStart == false || doNotStart === undefined) { - // if the simulation was settled, we restart the simulation if a cluster has been formed or expanded - if (this.moving != isMovingBeforeClustering) { - this.start(); + // set options + this.setOptions(data && data.options); + // set all data + if (data && data.dot) { + // parse DOT file + if(data && data.dot) { + var dotData = dotparser.DOTToGraph(data.dot); + this.setData(dotData); + return; } } - - this._updateCalculationNodes(); - }; - - /** - * This function handles the chains. It is called on every updateClusters(). - */ - exports.handleChains = function() { - // after clustering we check how many chains there are - var chainPercentage = this._getChainFraction(); - if (chainPercentage > this.constants.clustering.chainThreshold) { - this._reduceAmountOfChains(1 - this.constants.clustering.chainThreshold / chainPercentage) - + else if (data && data.gephi) { + // parse DOT file + if(data && data.gephi) { + var gephiData = gephiParser.parseGephi(data.gephi); + this.setData(gephiData); + return; + } } - }; - - /** - * this functions starts clustering by hubs - * The minimum hub threshold is set globally - * - * @private - */ - exports._aggregateHubs = function(force) { - this._getHubSize(); - this._formClustersByHub(force,false); - }; - - - /** - * This function is fired by keypress. It forces hubs to form. - * - */ - exports.forceAggregateHubs = function(doNotStart) { - var isMovingBeforeClustering = this.moving; - var amountOfNodes = this.nodeIndices.length; - - this._aggregateHubs(true); - - // update the index list, dynamic edges and labels - this._updateNodeIndexList(); - this._updateDynamicEdges(); - this.updateLabels(); - - // if a cluster was formed, we increase the clusterSession - if (this.nodeIndices.length != amountOfNodes) { - this.clusterSession += 1; + else { + this._setNodes(data && data.nodes); + this._setEdges(data && data.edges); } - - if (doNotStart == false || doNotStart === undefined) { - // if the simulation was settled, we restart the simulation if a cluster has been formed or expanded - if (this.moving != isMovingBeforeClustering) { - this.start(); + this._putDataInSector(); + if (disableStart == false) { + if (this.constants.hierarchicalLayout.enabled == true) { + this._resetLevels(); + this._setupHierarchicalLayout(); } - } - }; - - /** - * If a cluster takes up more than a set percentage of the screen, open the cluster - * - * @private - */ - exports._openClustersBySize = function() { - for (var nodeId in this.nodes) { - if (this.nodes.hasOwnProperty(nodeId)) { - var node = this.nodes[nodeId]; - if (node.inView() == true) { - if ((node.width*this.scale > this.constants.clustering.screenSizeThreshold * this.frame.canvas.clientWidth) || - (node.height*this.scale > this.constants.clustering.screenSizeThreshold * this.frame.canvas.clientHeight)) { - this.openCluster(node); - } + else { + // find a stable position or start animating to a stable position + if (this.constants.stabilize) { + this._stabilize(); } } + this.start(); } - }; - - - /** - * This function loops over all nodes in the nodeIndices list. For each node it checks if it is a cluster and if it - * has to be opened based on the current zoom level. - * - * @private - */ - exports._openClusters = function(recursive,force) { - for (var i = 0; i < this.nodeIndices.length; i++) { - var node = this.nodes[this.nodeIndices[i]]; - this._expandClusterNode(node,recursive,force); - this._updateCalculationNodes(); - } + this.initializing = false; }; /** - * This function checks if a node has to be opened. This is done by checking the zoom level. - * If the node contains child nodes, this function is recursively called on the child nodes as well. - * This recursive behaviour is optional and can be set by the recursive argument. - * - * @param {Node} parentNode | to check for cluster and expand - * @param {Boolean} recursive | enabled or disable recursive calling - * @param {Boolean} force | enabled or disable forcing - * @param {Boolean} [openAll] | This will recursively force all nodes in the parent to be released - * @private + * Set options + * @param {Object} options */ - exports._expandClusterNode = function(parentNode, recursive, force, openAll) { - // first check if node is a cluster - if (parentNode.clusterSize > 1) { - // this means that on a double tap event or a zoom event, the cluster fully unpacks if it is smaller than 20 - if (parentNode.clusterSize < this.constants.clustering.sectorThreshold) { - openAll = true; - } - recursive = openAll ? true : recursive; + Network.prototype.setOptions = function (options) { + if (options) { + var prop; + var fields = ['nodes','edges','smoothCurves','hierarchicalLayout','clustering','navigation', + 'keyboard','dataManipulation','onAdd','onEdit','onEditEdge','onConnect','onDelete','clickToUse' + ]; + // extend all but the values in fields + util.selectiveNotDeepExtend(fields,this.constants, options); + util.selectiveNotDeepExtend(['color'],this.constants.nodes, options.nodes); + util.selectiveNotDeepExtend(['color','length'],this.constants.edges, options.edges); - // if the last child has been added on a smaller scale than current scale decluster - if (parentNode.formationScale < this.scale || force == true) { - // we will check if any of the contained child nodes should be removed from the cluster - for (var containedNodeId in parentNode.containedNodes) { - if (parentNode.containedNodes.hasOwnProperty(containedNodeId)) { - var childNode = parentNode.containedNodes[containedNodeId]; + if (options.physics) { + util.mergeOptions(this.constants.physics, options.physics,'barnesHut'); + util.mergeOptions(this.constants.physics, options.physics,'repulsion'); - // force expand will expand the largest cluster size clusters. Since we cluster from outside in, we assume that - // the largest cluster is the one that comes from outside - if (force == true) { - if (childNode.clusterSession == parentNode.clusterSessions[parentNode.clusterSessions.length-1] - || openAll) { - this._expelChildFromParent(parentNode,containedNodeId,recursive,force,openAll); - } - } - else { - if (this._nodeInActiveArea(parentNode)) { - this._expelChildFromParent(parentNode,containedNodeId,recursive,force,openAll); - } + if (options.physics.hierarchicalRepulsion) { + this.constants.hierarchicalLayout.enabled = true; + this.constants.physics.hierarchicalRepulsion.enabled = true; + this.constants.physics.barnesHut.enabled = false; + for (prop in options.physics.hierarchicalRepulsion) { + if (options.physics.hierarchicalRepulsion.hasOwnProperty(prop)) { + this.constants.physics.hierarchicalRepulsion[prop] = options.physics.hierarchicalRepulsion[prop]; } } } } - } - }; - /** - * ONLY CALLED FROM _expandClusterNode - * - * This function will expel a child_node from a parent_node. This is to de-cluster the node. This function will remove - * the child node from the parent contained_node object and put it back into the global nodes object. - * The same holds for the edge that was connected to the child node. It is moved back into the global edges object. - * - * @param {Node} parentNode | the parent node - * @param {String} containedNodeId | child_node id as it is contained in the containedNodes object of the parent node - * @param {Boolean} recursive | This will also check if the child needs to be expanded. - * With force and recursive both true, the entire cluster is unpacked - * @param {Boolean} force | This will disregard the zoom level and will expel this child from the parent - * @param {Boolean} openAll | This will recursively force all nodes in the parent to be released - * @private - */ - exports._expelChildFromParent = function(parentNode, containedNodeId, recursive, force, openAll) { - var childNode = parentNode.containedNodes[containedNodeId]; + if (options.onAdd) {this.triggerFunctions.add = options.onAdd;} + if (options.onEdit) {this.triggerFunctions.edit = options.onEdit;} + if (options.onEditEdge) {this.triggerFunctions.editEdge = options.onEditEdge;} + if (options.onConnect) {this.triggerFunctions.connect = options.onConnect;} + if (options.onDelete) {this.triggerFunctions.del = options.onDelete;} - // if child node has been added on smaller scale than current, kick out - if (childNode.formationScale < this.scale || force == true) { - // unselect all selected items - this._unselectAll(); + util.mergeOptions(this.constants, options,'smoothCurves'); + util.mergeOptions(this.constants, options,'hierarchicalLayout'); + util.mergeOptions(this.constants, options,'clustering'); + util.mergeOptions(this.constants, options,'navigation'); + util.mergeOptions(this.constants, options,'keyboard'); + util.mergeOptions(this.constants, options,'dataManipulation'); - // put the child node back in the global nodes object - this.nodes[containedNodeId] = childNode; - // release the contained edges from this childNode back into the global edges - this._releaseContainedEdges(parentNode,childNode); + if (options.dataManipulation) { + this.editMode = this.constants.dataManipulation.initiallyVisible; + } - // reconnect rerouted edges to the childNode - this._connectEdgeBackToChild(parentNode,childNode); - // validate all edges in dynamicEdges - this._validateEdges(parentNode); + // TODO: work out these options and document them + if (options.edges) { + if (options.edges.color !== undefined) { + if (util.isString(options.edges.color)) { + this.constants.edges.color = {}; + this.constants.edges.color.color = options.edges.color; + this.constants.edges.color.highlight = options.edges.color; + this.constants.edges.color.hover = options.edges.color; + } + else { + if (options.edges.color.color !== undefined) {this.constants.edges.color.color = options.edges.color.color;} + if (options.edges.color.highlight !== undefined) {this.constants.edges.color.highlight = options.edges.color.highlight;} + if (options.edges.color.hover !== undefined) {this.constants.edges.color.hover = options.edges.color.hover;} + } + this.constants.edges.inheritColor = false; + } - // undo the changes from the clustering operation on the parent node - parentNode.options.mass -= childNode.options.mass; - parentNode.clusterSize -= childNode.clusterSize; - parentNode.options.fontSize = Math.min(this.constants.clustering.maxFontSize, this.constants.nodes.fontSize + this.constants.clustering.fontSizeMultiplier*(parentNode.clusterSize-1)); - parentNode.dynamicEdgesLength = parentNode.dynamicEdges.length; + if (!options.edges.fontColor) { + if (options.edges.color !== undefined) { + if (util.isString(options.edges.color)) {this.constants.edges.fontColor = options.edges.color;} + else if (options.edges.color.color !== undefined) {this.constants.edges.fontColor = options.edges.color.color;} + } + } + } - // place the child node near the parent, not at the exact same location to avoid chaos in the system - childNode.x = parentNode.x + parentNode.growthIndicator * (0.5 - Math.random()); - childNode.y = parentNode.y + parentNode.growthIndicator * (0.5 - Math.random()); + if (options.nodes) { + if (options.nodes.color) { + var newColorObj = util.parseColor(options.nodes.color); + this.constants.nodes.color.background = newColorObj.background; + this.constants.nodes.color.border = newColorObj.border; + this.constants.nodes.color.highlight.background = newColorObj.highlight.background; + this.constants.nodes.color.highlight.border = newColorObj.highlight.border; + this.constants.nodes.color.hover.background = newColorObj.hover.background; + this.constants.nodes.color.hover.border = newColorObj.hover.border; + } + } + if (options.groups) { + for (var groupname in options.groups) { + if (options.groups.hasOwnProperty(groupname)) { + var group = options.groups[groupname]; + this.groups.add(groupname, group); + } + } + } - // remove node from the list - delete parentNode.containedNodes[containedNodeId]; + if (options.tooltip) { + for (prop in options.tooltip) { + if (options.tooltip.hasOwnProperty(prop)) { + this.constants.tooltip[prop] = options.tooltip[prop]; + } + } + if (options.tooltip.color) { + this.constants.tooltip.color = util.parseColor(options.tooltip.color); + } + } - // check if there are other childs with this clusterSession in the parent. - var othersPresent = false; - for (var childNodeId in parentNode.containedNodes) { - if (parentNode.containedNodes.hasOwnProperty(childNodeId)) { - if (parentNode.containedNodes[childNodeId].clusterSession == childNode.clusterSession) { - othersPresent = true; - break; + if ('clickToUse' in options) { + if (options.clickToUse) { + if (!this.activator) { + this.activator = new Activator(this.frame); + this.activator.on('change', this._createKeyBinds.bind(this)); + } + } + else { + if (this.activator) { + this.activator.destroy(); + delete this.activator; } } } - // if there are no others, remove the cluster session from the list - if (othersPresent == false) { - parentNode.clusterSessions.pop(); + + if (options.labels) { + throw new Error('Option "labels" is deprecated. Use options "locale" and "locales" instead.'); } - this._repositionBezierNodes(childNode); - // this._repositionBezierNodes(parentNode); - // remove the clusterSession from the child node - childNode.clusterSession = 0; + // (Re)loading the mixins that can be enabled or disabled in the options. + // load the force calculation functions, grouped under the physics system. + this._loadPhysicsSystem(); + // load the navigation system. + this._loadNavigationControls(); + // load the data manipulation system + this._loadManipulationSystem(); + // configure the smooth curves + this._configureSmoothCurves(); - // recalculate the size of the node on the next time the node is rendered - parentNode.clearSizeCache(); + // bind hammer + this._bindHammer(); - // restart the simulation to reorganise all nodes - this.moving = true; - } + // bind keys. If disabled, this will not do anything; + this._createKeyBinds(); - // check if a further expansion step is possible if recursivity is enabled - if (recursive == true) { - this._expandClusterNode(childNode,recursive,force,openAll); + + this.setSize(this.constants.width, this.constants.height); + this.moving = true; + this.start(); } }; + /** - * position the bezier nodes at the center of the edges - * - * @param node + * Create the main frame for the Network. + * This function is executed once when a Network object is created. The frame + * contains a canvas, and this canvas contains all objects like the axis and + * nodes. * @private */ - exports._repositionBezierNodes = function(node) { - for (var i = 0; i < node.dynamicEdges.length; i++) { - node.dynamicEdges[i].positionBezierNode(); + Network.prototype._create = function () { + // remove all elements from the container element. + while (this.containerElement.hasChildNodes()) { + this.containerElement.removeChild(this.containerElement.firstChild); } - }; + this.frame = document.createElement('div'); + this.frame.className = 'vis network-frame'; + this.frame.style.position = 'relative'; + this.frame.style.overflow = 'hidden'; + this.frame.tabIndex = 900; - /** - * This function checks if any nodes at the end of their trees have edges below a threshold length - * This function is called only from updateClusters() - * forceLevelCollapse ignores the length of the edge and collapses one level - * This means that a node with only one edge will be clustered with its connected node - * - * @private - * @param {Boolean} force - */ - exports._formClusters = function(force) { - if (force == false) { - this._formClustersByZoom(); + + ////////////////////////////////////////////////////////////////// + + this.frame.canvas = document.createElement("canvas"); + this.frame.canvas.style.position = 'relative'; + this.frame.appendChild(this.frame.canvas); + + if (!this.frame.canvas.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.frame.canvas.appendChild(noCanvas); } else { - this._forceClustersByZoom(); + var ctx = this.frame.canvas.getContext("2d"); + this.pixelRatio = (window.devicePixelRatio || 1) / (ctx.webkitBackingStorePixelRatio || + ctx.mozBackingStorePixelRatio || + ctx.msBackingStorePixelRatio || + ctx.oBackingStorePixelRatio || + ctx.backingStorePixelRatio || 1); + + this.frame.canvas.getContext("2d").setTransform(this.pixelRatio, 0, 0, this.pixelRatio, 0, 0); } + + this._bindHammer(); }; /** - * This function handles the clustering by zooming out, this is based on a minimum edge distance - * + * This function binds hammer, it can be repeated over and over due to the uniqueness check. * @private */ - exports._formClustersByZoom = function() { - var dx,dy,length, - minLength = this.constants.clustering.clusterEdgeThreshold/this.scale; + Network.prototype._bindHammer = function() { + var me = this; + if (this.hammer !== undefined) { + this.hammer.dispose(); + } + this.drag = {}; + this.pinch = {}; + this.hammer = Hammer(this.frame.canvas, { + prevent_default: true + }); + this.hammer.on('tap', me._onTap.bind(me) ); + this.hammer.on('doubletap', me._onDoubleTap.bind(me) ); + this.hammer.on('hold', me._onHold.bind(me) ); + this.hammer.on('touch', me._onTouch.bind(me) ); + this.hammer.on('dragstart', me._onDragStart.bind(me) ); + this.hammer.on('drag', me._onDrag.bind(me) ); + this.hammer.on('dragend', me._onDragEnd.bind(me) ); - // check if any edges are shorter than minLength and start the clustering - // the clustering favours the node with the larger mass - for (var edgeId in this.edges) { - if (this.edges.hasOwnProperty(edgeId)) { - var edge = this.edges[edgeId]; - if (edge.connected) { - if (edge.toId != edge.fromId) { - dx = (edge.to.x - edge.from.x); - dy = (edge.to.y - edge.from.y); - length = Math.sqrt(dx * dx + dy * dy); + if (this.constants.zoomable == true) { + this.hammer.on('mousewheel', me._onMouseWheel.bind(me)); + this.hammer.on('DOMMouseScroll', me._onMouseWheel.bind(me)); // for FF + this.hammer.on('pinch', me._onPinch.bind(me) ); + } + this.hammer.on('mousemove', me._onMouseMoveTitle.bind(me) ); - if (length < minLength) { - // first check which node is larger - var parentNode = edge.from; - var childNode = edge.to; - if (edge.to.options.mass > edge.from.options.mass) { - parentNode = edge.to; - childNode = edge.from; - } + this.hammerFrame = Hammer(this.frame, { + prevent_default: true + }); + this.hammerFrame.on('release', me._onRelease.bind(me) ); - if (childNode.dynamicEdgesLength == 1) { - this._addToCluster(parentNode,childNode,false); - } - else if (parentNode.dynamicEdgesLength == 1) { - this._addToCluster(childNode,parentNode,false); - } - } - } - } - } - } - }; + // add the frame to the container element + this.containerElement.appendChild(this.frame); + } /** - * This function forces the network to cluster all nodes with only one connecting edge to their - * connected node. - * + * Binding the keys for keyboard navigation. These functions are defined in the NavigationMixin * @private */ - exports._forceClustersByZoom = function() { - for (var nodeId in this.nodes) { - // another node could have absorbed this child. - if (this.nodes.hasOwnProperty(nodeId)) { - var childNode = this.nodes[nodeId]; + Network.prototype._createKeyBinds = function() { + var me = this; + if (this.keycharm !== undefined) { + this.keycharm.destroy(); + } - // the edges can be swallowed by another decrease - if (childNode.dynamicEdgesLength == 1 && childNode.dynamicEdges.length != 0) { - var edge = childNode.dynamicEdges[0]; - var parentNode = (edge.toId == childNode.id) ? this.nodes[edge.fromId] : this.nodes[edge.toId]; + if (this.constants.keyboard.bindToWindow == true) { + this.keycharm = keycharm({container: window, preventDefault: false}); + } + else { + this.keycharm = keycharm({container: this.frame, preventDefault: false}); + } - // group to the largest node - if (childNode.id != parentNode.id) { - if (parentNode.options.mass > childNode.options.mass) { - this._addToCluster(parentNode,childNode,true); - } - else { - this._addToCluster(childNode,parentNode,true); - } - } - } - } + this.keycharm.reset(); + + if (this.constants.keyboard.enabled && this.isActive()) { + this.keycharm.bind("up", this._moveUp.bind(me) , "keydown"); + this.keycharm.bind("up", this._yStopMoving.bind(me), "keyup"); + this.keycharm.bind("down", this._moveDown.bind(me) , "keydown"); + this.keycharm.bind("down", this._yStopMoving.bind(me), "keyup"); + this.keycharm.bind("left", this._moveLeft.bind(me) , "keydown"); + this.keycharm.bind("left", this._xStopMoving.bind(me), "keyup"); + this.keycharm.bind("right",this._moveRight.bind(me), "keydown"); + this.keycharm.bind("right",this._xStopMoving.bind(me), "keyup"); + this.keycharm.bind("=", this._zoomIn.bind(me), "keydown"); + this.keycharm.bind("=", this._stopZoom.bind(me), "keyup"); + this.keycharm.bind("num+", this._zoomIn.bind(me), "keydown"); + this.keycharm.bind("num+", this._stopZoom.bind(me), "keyup"); + this.keycharm.bind("num-", this._zoomOut.bind(me), "keydown"); + this.keycharm.bind("num-", this._stopZoom.bind(me), "keyup"); + this.keycharm.bind("-", this._zoomOut.bind(me), "keydown"); + this.keycharm.bind("-", this._stopZoom.bind(me), "keyup"); + this.keycharm.bind("[", this._zoomIn.bind(me), "keydown"); + this.keycharm.bind("[", this._stopZoom.bind(me), "keyup"); + this.keycharm.bind("]", this._zoomOut.bind(me), "keydown"); + this.keycharm.bind("]", this._stopZoom.bind(me), "keyup"); + this.keycharm.bind("pageup",this._zoomIn.bind(me), "keydown"); + this.keycharm.bind("pageup",this._stopZoom.bind(me), "keyup"); + this.keycharm.bind("pagedown",this._zoomOut.bind(me),"keydown"); + this.keycharm.bind("pagedown",this._stopZoom.bind(me), "keyup"); + } + this.keycharm.bind("1",this.increaseClusterLevel.bind(me), "keydown"); + this.keycharm.bind("2",this.decreaseClusterLevel.bind(me), "keydown"); + this.keycharm.bind("3",this.forceAggregateHubs.bind(me,true),"keydown"); + this.keycharm.bind("4",this.normalizeClusterLevels.bind(me), "keydown"); + if (this.constants.dataManipulation.enabled == true) { + this.keycharm.bind("esc",this._createManipulatorBar.bind(me)); + this.keycharm.bind("delete",this._deleteSelected.bind(me)); } }; + /** + * Cleans up all bindings of the network, removing it fully from the memory IF the variable is set to null after calling this function. + * var network = new vis.Network(..); + * network.destroy(); + * network = null; + */ + Network.prototype.destroy = function() { + this.start = function () {}; + this.redraw = function () {}; + this.timer = false; + + // cleanup physicsConfiguration if it exists + this._cleanupPhysicsConfiguration(); + + // remove keybindings + this.keycharm.reset(); + + // clear hammer bindings + this.hammer.dispose(); + + // clear events + this.off(); + + this._recursiveDOMDelete(this.containerElement); + } + + Network.prototype._recursiveDOMDelete = function(DOMobject) { + while (DOMobject.hasChildNodes() == true) { + this._recursiveDOMDelete(DOMobject.firstChild); + DOMobject.removeChild(DOMobject.firstChild); + } + } /** - * To keep the nodes of roughly equal size we normalize the cluster levels. - * This function clusters a node to its smallest connected neighbour. - * - * @param node + * Get the pointer location from a touch location + * @param {{pageX: Number, pageY: Number}} touch + * @return {{x: Number, y: Number}} pointer * @private */ - exports._clusterToSmallestNeighbour = function(node) { - var smallestNeighbour = -1; - var smallestNeighbourNode = null; - for (var i = 0; i < node.dynamicEdges.length; i++) { - if (node.dynamicEdges[i] !== undefined) { - var neighbour = null; - if (node.dynamicEdges[i].fromId != node.id) { - neighbour = node.dynamicEdges[i].from; - } - else if (node.dynamicEdges[i].toId != node.id) { - neighbour = node.dynamicEdges[i].to; - } + Network.prototype._getPointer = function (touch) { + return { + x: touch.pageX - util.getAbsoluteLeft(this.frame.canvas), + y: touch.pageY - util.getAbsoluteTop(this.frame.canvas) + }; + }; + /** + * On start of a touch gesture, store the pointer + * @param event + * @private + */ + Network.prototype._onTouch = function (event) { + if (new Date().valueOf() - this.touchTime > 100) { + this.drag.pointer = this._getPointer(event.gesture.center); + this.drag.pinched = false; + this.pinch.scale = this._getScale(); - if (neighbour != null && smallestNeighbour > neighbour.clusterSessions.length) { - smallestNeighbour = neighbour.clusterSessions.length; - smallestNeighbourNode = neighbour; - } - } - } + // to avoid double fireing of this event because we have two hammer instances. (on canvas and on frame) + this.touchTime = new Date().valueOf(); - if (neighbour != null && this.nodes[neighbour.id] !== undefined) { - this._addToCluster(neighbour, node, true); + this._handleTouch(this.drag.pointer); } }; - /** - * This function forms clusters from hubs, it loops over all nodes - * - * @param {Boolean} force | Disregard zoom level - * @param {Boolean} onlyEqual | This only clusters a hub with a specific number of edges + * handle drag start event * @private */ - exports._formClustersByHub = function(force, onlyEqual) { - // we loop over all nodes in the list - for (var nodeId in this.nodes) { - // we check if it is still available since it can be used by the clustering in this loop - if (this.nodes.hasOwnProperty(nodeId)) { - this._formClusterFromHub(this.nodes[nodeId],force,onlyEqual); - } - } + Network.prototype._onDragStart = function (event) { + this._handleDragStart(event); }; + /** - * This function forms a cluster from a specific preselected hub node + * This function is called by _onDragStart. + * It is separated out because we can then overload it for the datamanipulation system. * - * @param {Node} hubNode | the node we will cluster as a hub - * @param {Boolean} force | Disregard zoom level - * @param {Boolean} onlyEqual | This only clusters a hub with a specific number of edges - * @param {Number} [absorptionSizeOffset] | * @private */ - exports._formClusterFromHub = function(hubNode, force, onlyEqual, absorptionSizeOffset) { - if (absorptionSizeOffset === undefined) { - absorptionSizeOffset = 0; + Network.prototype._handleDragStart = function(event) { + // in case the touch event was triggered on an external div, do the initial touch now. + if (this.drag.pointer === undefined) { + this._onTouch(event); } - // we decide if the node is a hub - if ((hubNode.dynamicEdgesLength >= this.hubThreshold && onlyEqual == false) || - (hubNode.dynamicEdgesLength == this.hubThreshold && onlyEqual == true)) { - // initialize variables - var dx,dy,length; - var minLength = this.constants.clustering.clusterEdgeThreshold/this.scale; - var allowCluster = false; - // we create a list of edges because the dynamicEdges change over the course of this loop - var edgesIdarray = []; - var amountOfInitialEdges = hubNode.dynamicEdges.length; - for (var j = 0; j < amountOfInitialEdges; j++) { - edgesIdarray.push(hubNode.dynamicEdges[j].id); - } + var node = this._getNodeAt(this.drag.pointer); + // note: drag.pointer is set in _onTouch to get the initial touch location - // if the hub clustering is not forces, we check if one of the edges connected - // to a cluster is small enough based on the constants.clustering.clusterEdgeThreshold - if (force == false) { - allowCluster = false; - for (j = 0; j < amountOfInitialEdges; j++) { - var edge = this.edges[edgesIdarray[j]]; - if (edge !== undefined) { - if (edge.connected) { - if (edge.toId != edge.fromId) { - dx = (edge.to.x - edge.from.x); - dy = (edge.to.y - edge.from.y); - length = Math.sqrt(dx * dx + dy * dy); + this.drag.dragging = true; + this.drag.selection = []; + this.drag.translation = this._getTranslation(); + this.drag.nodeId = null; + this.draggingNodes = false; - if (length < minLength) { - allowCluster = true; - break; - } - } - } - } - } + if (node != null && this.constants.dragNodes == true) { + this.draggingNodes = true; + this.drag.nodeId = node.id; + // select the clicked node if not yet selected + if (!node.isSelected()) { + this._selectObject(node,false); } - // start the clustering if allowed - if ((!force && allowCluster) || force) { - // we loop over all edges INITIALLY connected to this hub - for (j = 0; j < amountOfInitialEdges; j++) { - edge = this.edges[edgesIdarray[j]]; - // the edge can be clustered by this function in a previous loop - if (edge !== undefined) { - var childNode = this.nodes[(edge.fromId == hubNode.id) ? edge.toId : edge.fromId]; - // we do not want hubs to merge with other hubs nor do we want to cluster itself. - if ((childNode.dynamicEdges.length <= (this.hubThreshold + absorptionSizeOffset)) && - (childNode.id != hubNode.id)) { - this._addToCluster(hubNode,childNode,force); - } - } + this.emit("dragStart",{nodeIds:this.getSelection().nodes}); + + // create an array with the selected nodes and their original location and status + for (var objectId in this.selectionObj.nodes) { + if (this.selectionObj.nodes.hasOwnProperty(objectId)) { + var object = this.selectionObj.nodes[objectId]; + var s = { + id: object.id, + node: object, + + // store original x, y, xFixed and yFixed, make the node temporarily Fixed + x: object.x, + y: object.y, + xFixed: object.xFixed, + yFixed: object.yFixed + }; + + object.xFixed = true; + object.yFixed = true; + + this.drag.selection.push(s); } } } }; + /** + * handle drag event + * @private + */ + Network.prototype._onDrag = function (event) { + this._handleOnDrag(event) + }; + /** - * This function adds the child node to the parent node, creating a cluster if it is not already. + * This function is called by _onDrag. + * It is separated out because we can then overload it for the datamanipulation system. * - * @param {Node} parentNode | this is the node that will house the child node - * @param {Node} childNode | this node will be deleted from the global this.nodes and stored in the parent node - * @param {Boolean} force | true will only update the remainingEdges at the very end of the clustering, ensuring single level collapse * @private */ - exports._addToCluster = function(parentNode, childNode, force) { - // join child node in the parent node - parentNode.containedNodes[childNode.id] = childNode; - - // manage all the edges connected to the child and parent nodes - for (var i = 0; i < childNode.dynamicEdges.length; i++) { - var edge = childNode.dynamicEdges[i]; - if (edge.toId == parentNode.id || edge.fromId == parentNode.id) { // edge connected to parentNode - this._addToContainedEdges(parentNode,childNode,edge); - } - else { - this._connectEdgeToCluster(parentNode,childNode,edge); - } + Network.prototype._handleOnDrag = function(event) { + if (this.drag.pinched) { + return; } - // a contained node has no dynamic edges. - childNode.dynamicEdges = []; - // remove circular edges from clusters - this._containCircularEdgesFromNode(parentNode,childNode); + // remove the focus on node if it is focussed on by the focusOnNode + this.releaseNode(); + + var pointer = this._getPointer(event.gesture.center); + var me = this; + var drag = this.drag; + var selection = drag.selection; + if (selection && selection.length && this.constants.dragNodes == true) { + // calculate delta's and new location + var deltaX = pointer.x - drag.pointer.x; + var deltaY = pointer.y - drag.pointer.y; + // update position of all selected nodes + selection.forEach(function (s) { + var node = s.node; - // remove the childNode from the global nodes object - delete this.nodes[childNode.id]; + if (!s.xFixed) { + node.x = me._XconvertDOMtoCanvas(me._XconvertCanvasToDOM(s.x) + deltaX); + } - // update the properties of the child and parent - var massBefore = parentNode.options.mass; - childNode.clusterSession = this.clusterSession; - parentNode.options.mass += childNode.options.mass; - parentNode.clusterSize += childNode.clusterSize; - parentNode.options.fontSize = Math.min(this.constants.clustering.maxFontSize, this.constants.nodes.fontSize + this.constants.clustering.fontSizeMultiplier*parentNode.clusterSize); + if (!s.yFixed) { + node.y = me._YconvertDOMtoCanvas(me._YconvertCanvasToDOM(s.y) + deltaY); + } + }); - // keep track of the clustersessions so we can open the cluster up as it has been formed. - if (parentNode.clusterSessions[parentNode.clusterSessions.length - 1] != this.clusterSession) { - parentNode.clusterSessions.push(this.clusterSession); - } - // forced clusters only open from screen size and double tap - if (force == true) { - // parentNode.formationScale = Math.pow(1 - (1.0/11.0),this.clusterSession+3); - parentNode.formationScale = 0; + // start _animationStep if not yet running + if (!this.moving) { + this.moving = true; + this.start(); + } } else { - parentNode.formationScale = this.scale; // The latest child has been added on this scale + // move the network + if (this.constants.dragNetwork == true) { + // if the drag was not started properly because the click started outside the network div, start it now. + if (this.drag.pointer === undefined) { + this._handleDragStart(event); + return; + } + var diffX = pointer.x - this.drag.pointer.x; + var diffY = pointer.y - this.drag.pointer.y; + + this._setTranslation( + this.drag.translation.x + diffX, + this.drag.translation.y + diffY + ); + this._redraw(); + } } + }; - // recalculate the size of the node on the next time the node is rendered - parentNode.clearSizeCache(); + /** + * handle drag start event + * @private + */ + Network.prototype._onDragEnd = function (event) { + this._handleDragEnd(event); + }; - // set the pop-out scale for the childnode - parentNode.containedNodes[childNode.id].formationScale = parentNode.formationScale; - // nullify the movement velocity of the child, this is to avoid hectic behaviour - childNode.clearVelocity(); + Network.prototype._handleDragEnd = function(event) { + this.drag.dragging = false; + var selection = this.drag.selection; + if (selection && selection.length) { + selection.forEach(function (s) { + // restore original xFixed and yFixed + s.node.xFixed = s.xFixed; + s.node.yFixed = s.yFixed; + }); + this.moving = true; + this.start(); + } + else { + this._redraw(); + } + if (this.draggingNodes == false) { + this.emit("dragEnd",{nodeIds:[]}); + } + else { + this.emit("dragEnd",{nodeIds:this.getSelection().nodes}); + } - // the mass has altered, preservation of energy dictates the velocity to be updated - parentNode.updateVelocity(massBefore); + } + /** + * handle tap/click event: select/unselect a node + * @private + */ + Network.prototype._onTap = function (event) { + var pointer = this._getPointer(event.gesture.center); + this.pointerPosition = pointer; + this._handleTap(pointer); - // restart the simulation to reorganise all nodes - this.moving = true; }; /** - * This function will apply the changes made to the remainingEdges during the formation of the clusters. - * This is a seperate function to allow for level-wise collapsing of the node barnesHutTree. - * It has to be called if a level is collapsed. It is called by _formClusters(). + * handle doubletap event * @private */ - exports._updateDynamicEdges = function() { - for (var i = 0; i < this.nodeIndices.length; i++) { - var node = this.nodes[this.nodeIndices[i]]; - node.dynamicEdgesLength = node.dynamicEdges.length; - - // this corrects for multiple edges pointing at the same other node - var correction = 0; - if (node.dynamicEdgesLength > 1) { - for (var j = 0; j < node.dynamicEdgesLength - 1; j++) { - var edgeToId = node.dynamicEdges[j].toId; - var edgeFromId = node.dynamicEdges[j].fromId; - for (var k = j+1; k < node.dynamicEdgesLength; k++) { - if ((node.dynamicEdges[k].toId == edgeToId && node.dynamicEdges[k].fromId == edgeFromId) || - (node.dynamicEdges[k].fromId == edgeToId && node.dynamicEdges[k].toId == edgeFromId)) { - correction += 1; - } - } - } - } - node.dynamicEdgesLength -= correction; - } + Network.prototype._onDoubleTap = function (event) { + var pointer = this._getPointer(event.gesture.center); + this._handleDoubleTap(pointer); }; /** - * This adds an edge from the childNode to the contained edges of the parent node - * - * @param parentNode | Node object - * @param childNode | Node object - * @param edge | Edge object + * handle long tap event: multi select nodes * @private */ - exports._addToContainedEdges = function(parentNode, childNode, edge) { - // create an array object if it does not yet exist for this childNode - if (!(parentNode.containedEdges.hasOwnProperty(childNode.id))) { - parentNode.containedEdges[childNode.id] = [] - } - // add this edge to the list - parentNode.containedEdges[childNode.id].push(edge); - - // remove the edge from the global edges object - delete this.edges[edge.id]; - - // remove the edge from the parent object - for (var i = 0; i < parentNode.dynamicEdges.length; i++) { - if (parentNode.dynamicEdges[i].id == edge.id) { - parentNode.dynamicEdges.splice(i,1); - break; - } - } + Network.prototype._onHold = function (event) { + var pointer = this._getPointer(event.gesture.center); + this.pointerPosition = pointer; + this._handleOnHold(pointer); }; /** - * This function connects an edge that was connected to a child node to the parent node. - * It keeps track of which nodes it has been connected to with the originalId array. + * handle the release of the screen * - * @param {Node} parentNode | Node object - * @param {Node} childNode | Node object - * @param {Edge} edge | Edge object * @private */ - exports._connectEdgeToCluster = function(parentNode, childNode, edge) { - // handle circular edges - if (edge.toId == edge.fromId) { - this._addToContainedEdges(parentNode, childNode, edge); - } - else { - if (edge.toId == childNode.id) { // edge connected to other node on the "to" side - edge.originalToId.push(childNode.id); - edge.to = parentNode; - edge.toId = parentNode.id; - } - else { // edge connected to other node with the "from" side - - edge.originalFromId.push(childNode.id); - edge.from = parentNode; - edge.fromId = parentNode.id; - } - - this._addToReroutedEdges(parentNode,childNode,edge); - } + Network.prototype._onRelease = function (event) { + var pointer = this._getPointer(event.gesture.center); + this._handleOnRelease(pointer); }; - /** - * If a node is connected to itself, a circular edge is drawn. When clustering we want to contain - * these edges inside of the cluster. - * - * @param parentNode - * @param childNode + * Handle pinch event + * @param event * @private */ - exports._containCircularEdgesFromNode = function(parentNode, childNode) { - // manage all the edges connected to the child and parent nodes - for (var i = 0; i < parentNode.dynamicEdges.length; i++) { - var edge = parentNode.dynamicEdges[i]; - // handle circular edges - if (edge.toId == edge.fromId) { - this._addToContainedEdges(parentNode, childNode, edge); - } + Network.prototype._onPinch = function (event) { + var pointer = this._getPointer(event.gesture.center); + + this.drag.pinched = true; + if (!('scale' in this.pinch)) { + this.pinch.scale = 1; } - }; + // TODO: enabled moving while pinching? + var scale = this.pinch.scale * event.gesture.scale; + this._zoom(scale, pointer) + }; /** - * This adds an edge from the childNode to the rerouted edges of the parent node - * - * @param parentNode | Node object - * @param childNode | Node object - * @param edge | Edge object + * Zoom the network in or out + * @param {Number} scale a number around 1, and between 0.01 and 10 + * @param {{x: Number, y: Number}} pointer Position on screen + * @return {Number} appliedScale scale is limited within the boundaries * @private */ - exports._addToReroutedEdges = function(parentNode, childNode, edge) { - // create an array object if it does not yet exist for this childNode - // we store the edge in the rerouted edges so we can restore it when the cluster pops open - if (!(parentNode.reroutedEdges.hasOwnProperty(childNode.id))) { - parentNode.reroutedEdges[childNode.id] = []; - } - parentNode.reroutedEdges[childNode.id].push(edge); + Network.prototype._zoom = function(scale, pointer) { + if (this.constants.zoomable == true) { + var scaleOld = this._getScale(); + if (scale < 0.00001) { + scale = 0.00001; + } + if (scale > 10) { + scale = 10; + } - // this edge becomes part of the dynamicEdges of the cluster node - parentNode.dynamicEdges.push(edge); - }; + var preScaleDragPointer = null; + if (this.drag !== undefined) { + if (this.drag.dragging == true) { + preScaleDragPointer = this.DOMtoCanvas(this.drag.pointer); + } + } + // + this.frame.canvas.clientHeight / 2 + var translation = this._getTranslation(); + var scaleFrac = scale / scaleOld; + var tx = (1 - scaleFrac) * pointer.x + translation.x * scaleFrac; + var ty = (1 - scaleFrac) * pointer.y + translation.y * scaleFrac; + this.areaCenter = {"x" : this._XconvertDOMtoCanvas(pointer.x), + "y" : this._YconvertDOMtoCanvas(pointer.y)}; - /** - * This function connects an edge that was connected to a cluster node back to the child node. - * - * @param parentNode | Node object - * @param childNode | Node object - * @private - */ - exports._connectEdgeBackToChild = function(parentNode, childNode) { - if (parentNode.reroutedEdges.hasOwnProperty(childNode.id)) { - for (var i = 0; i < parentNode.reroutedEdges[childNode.id].length; i++) { - var edge = parentNode.reroutedEdges[childNode.id][i]; - if (edge.originalFromId[edge.originalFromId.length-1] == childNode.id) { - edge.originalFromId.pop(); - edge.fromId = childNode.id; - edge.from = childNode; - } - else { - edge.originalToId.pop(); - edge.toId = childNode.id; - edge.to = childNode; - } + this._setScale(scale); + this._setTranslation(tx, ty); + this.updateClustersDefault(); - // append this edge to the list of edges connecting to the childnode - childNode.dynamicEdges.push(edge); + if (preScaleDragPointer != null) { + var postScaleDragPointer = this.canvasToDOM(preScaleDragPointer); + this.drag.pointer.x = postScaleDragPointer.x; + this.drag.pointer.y = postScaleDragPointer.y; + } - // remove the edge from the parent object - for (var j = 0; j < parentNode.dynamicEdges.length; j++) { - if (parentNode.dynamicEdges[j].id == edge.id) { - parentNode.dynamicEdges.splice(j,1); - break; - } - } + this._redraw(); + + if (scaleOld < scale) { + this.emit("zoom", {direction:"+"}); } - // remove the entry from the rerouted edges - delete parentNode.reroutedEdges[childNode.id]; + else { + this.emit("zoom", {direction:"-"}); + } + + return scale; } }; /** - * When loops are clustered, an edge can be both in the rerouted array and the contained array. - * This function is called last to verify that all edges in dynamicEdges are in fact connected to the - * parentNode - * - * @param parentNode | Node object + * Event handler for mouse wheel event, used to zoom the timeline + * See http://adomas.org/javascript-mouse-wheel/ + * https://github.com/EightMedia/hammer.js/issues/256 + * @param {MouseEvent} event * @private */ - exports._validateEdges = function(parentNode) { - for (var i = 0; i < parentNode.dynamicEdges.length; i++) { - var edge = parentNode.dynamicEdges[i]; - if (parentNode.id != edge.toId && parentNode.id != edge.fromId) { - parentNode.dynamicEdges.splice(i,1); - } + Network.prototype._onMouseWheel = function(event) { + // retrieve delta + var delta = 0; + if (event.wheelDelta) { /* IE/Opera. */ + delta = event.wheelDelta/120; + } else if (event.detail) { /* Mozilla case. */ + // In Mozilla, sign of delta is different than in IE. + // Also, delta is multiple of 3. + delta = -event.detail/3; } - }; + // If delta is nonzero, handle it. + // Basically, delta is now positive if wheel was scrolled up, + // and negative, if wheel was scrolled down. + if (delta) { - /** - * This function released the contained edges back into the global domain and puts them back into the - * dynamic edges of both parent and child. - * - * @param {Node} parentNode | - * @param {Node} childNode | - * @private - */ - exports._releaseContainedEdges = function(parentNode, childNode) { - for (var i = 0; i < parentNode.containedEdges[childNode.id].length; i++) { - var edge = parentNode.containedEdges[childNode.id][i]; + // calculate the new scale + var scale = this._getScale(); + var zoom = delta / 10; + if (delta < 0) { + zoom = zoom / (1 - zoom); + } + scale *= (1 + zoom); - // put the edge back in the global edges object - this.edges[edge.id] = edge; + // calculate the pointer location + var gesture = hammerUtil.fakeGesture(this, event); + var pointer = this._getPointer(gesture.center); - // put the edge back in the dynamic edges of the child and parent - childNode.dynamicEdges.push(edge); - parentNode.dynamicEdges.push(edge); + // apply the new scale + this._zoom(scale, pointer); } - // remove the entry from the contained edges - delete parentNode.containedEdges[childNode.id]; + // Prevent default actions caused by mouse wheel. + event.preventDefault(); }; + /** + * Mouse move handler for checking whether the title moves over a node with a title. + * @param {Event} event + * @private + */ + Network.prototype._onMouseMoveTitle = function (event) { + var gesture = hammerUtil.fakeGesture(this, event); + var pointer = this._getPointer(gesture.center); + + // check if the previously selected node is still selected + if (this.popupObj) { + this._checkHidePopup(pointer); + } + // if we bind the keyboard to the div, we have to highlight it to use it. This highlights it on mouse over + if (this.constants.keyboard.bindToWindow == false && this.constants.keyboard.enabled == true) { + this.frame.focus(); + } - // ------------------- UTILITY FUNCTIONS ---------------------------- // + // start a timeout that will check if the mouse is positioned above + // an element + var me = this; + var checkShow = function() { + me._checkShowPopup(pointer); + }; + if (this.popupTimer) { + clearInterval(this.popupTimer); // stop any running calculationTimer + } + if (!this.drag.dragging) { + this.popupTimer = setTimeout(checkShow, this.constants.tooltip.delay); + } - /** - * This updates the node labels for all nodes (for debugging purposes) - */ - exports.updateLabels = function() { - var nodeId; - // update node labels - for (nodeId in this.nodes) { - if (this.nodes.hasOwnProperty(nodeId)) { - var node = this.nodes[nodeId]; - if (node.clusterSize > 1) { - node.label = "[".concat(String(node.clusterSize),"]"); + /** + * Adding hover highlights + */ + if (this.constants.hover == true) { + // removing all hover highlights + for (var edgeId in this.hoverObj.edges) { + if (this.hoverObj.edges.hasOwnProperty(edgeId)) { + this.hoverObj.edges[edgeId].hover = false; + delete this.hoverObj.edges[edgeId]; } } - } - // update node labels - for (nodeId in this.nodes) { - if (this.nodes.hasOwnProperty(nodeId)) { - node = this.nodes[nodeId]; - if (node.clusterSize == 1) { - if (node.originalLabel !== undefined) { - node.label = node.originalLabel; - } - else { - node.label = String(node.id); + // adding hover highlights + var obj = this._getNodeAt(pointer); + if (obj == null) { + obj = this._getEdgeAt(pointer); + } + if (obj != null) { + this._hoverObject(obj); + } + + // removing all node hover highlights except for the selected one. + for (var nodeId in this.hoverObj.nodes) { + if (this.hoverObj.nodes.hasOwnProperty(nodeId)) { + if (obj instanceof Node && obj.id != nodeId || obj instanceof Edge || obj == null) { + this._blurObject(this.hoverObj.nodes[nodeId]); + delete this.hoverObj.nodes[nodeId]; } } } + this.redraw(); } - - // /* Debug Override */ - // for (nodeId in this.nodes) { - // if (this.nodes.hasOwnProperty(nodeId)) { - // node = this.nodes[nodeId]; - // node.label = String(node.level); - // } - // } - }; - /** - * We want to keep the cluster level distribution rather small. This means we do not want unclustered nodes - * if the rest of the nodes are already a few cluster levels in. - * To fix this we use this function. It determines the min and max cluster level and sends nodes that have not - * clustered enough to the clusterToSmallestNeighbours function. + * Check if there is an element on the given position in the network + * (a node or edge). If so, and if this element has a title, + * show a popup window with its title. + * + * @param {{x:Number, y:Number}} pointer + * @private */ - exports.normalizeClusterLevels = function() { - var maxLevel = 0; - var minLevel = 1e9; - var clusterLevel = 0; - var nodeId; + Network.prototype._checkShowPopup = function (pointer) { + var obj = { + left: this._XconvertDOMtoCanvas(pointer.x), + top: this._YconvertDOMtoCanvas(pointer.y), + right: this._XconvertDOMtoCanvas(pointer.x), + bottom: this._YconvertDOMtoCanvas(pointer.y) + }; - // we loop over all nodes in the list - for (nodeId in this.nodes) { - if (this.nodes.hasOwnProperty(nodeId)) { - clusterLevel = this.nodes[nodeId].clusterSessions.length; - if (maxLevel < clusterLevel) {maxLevel = clusterLevel;} - if (minLevel > clusterLevel) {minLevel = clusterLevel;} + var id; + var lastPopupNode = this.popupObj; + var nodeUnderCursor = false; + + if (this.popupObj == undefined) { + // search the nodes for overlap, select the top one in case of multiple nodes + var nodes = this.nodes; + var overlappingNodes = []; + for (id in nodes) { + if (nodes.hasOwnProperty(id)) { + var node = nodes[id]; + if (node.isOverlappingWith(obj)) { + if (node.getTitle() !== undefined) { + overlappingNodes.push(id); + } + } + } + } + + if (overlappingNodes.length > 0) { + // if there are overlapping nodes, select the last one, this is the + // one which is drawn on top of the others + this.popupObj = this.nodes[overlappingNodes[overlappingNodes.length - 1]]; + // if you hover over a node, the title of the edge is not supposed to be shown. + nodeUnderCursor = true; } } - if (maxLevel - minLevel > this.constants.clustering.clusterLevelDifference) { - var amountOfNodes = this.nodeIndices.length; - var targetLevel = maxLevel - this.constants.clustering.clusterLevelDifference; - // we loop over all nodes in the list - for (nodeId in this.nodes) { - if (this.nodes.hasOwnProperty(nodeId)) { - if (this.nodes[nodeId].clusterSessions.length < targetLevel) { - this._clusterToSmallestNeighbour(this.nodes[nodeId]); + if (this.popupObj === undefined && nodeUnderCursor == false) { + // search the edges for overlap + var edges = this.edges; + var overlappingEdges = []; + for (id in edges) { + if (edges.hasOwnProperty(id)) { + var edge = edges[id]; + if (edge.connected && (edge.getTitle() !== undefined) && + edge.isOverlappingWith(obj)) { + overlappingEdges.push(id); } } } - this._updateNodeIndexList(); - this._updateDynamicEdges(); - // if a cluster was formed, we increase the clusterSession - if (this.nodeIndices.length != amountOfNodes) { - this.clusterSession += 1; + + if (overlappingEdges.length > 0) { + this.popupObj = this.edges[overlappingEdges[overlappingEdges.length - 1]]; } } - }; - + if (this.popupObj) { + // show popup message window + if (this.popupObj != lastPopupNode) { + var me = this; + if (!me.popup) { + me.popup = new Popup(me.frame, me.constants.tooltip); + } - /** - * This function determines if the cluster we want to decluster is in the active area - * this means around the zoom center - * - * @param {Node} node - * @returns {boolean} - * @private - */ - exports._nodeInActiveArea = function(node) { - return ( - Math.abs(node.x - this.areaCenter.x) <= this.constants.clustering.activeAreaBoxSize/this.scale - && - Math.abs(node.y - this.areaCenter.y) <= this.constants.clustering.activeAreaBoxSize/this.scale - ) + // adjust a small offset such that the mouse cursor is located in the + // bottom left location of the popup, and you can easily move over the + // popup area + me.popup.setPosition(pointer.x - 3, pointer.y - 3); + me.popup.setText(me.popupObj.getTitle()); + me.popup.show(); + } + } + else { + if (this.popup) { + this.popup.hide(); + } + } }; /** - * This is an adaptation of the original repositioning function. This is called if the system is clustered initially - * It puts large clusters away from the center and randomizes the order. - * + * Check if the popup must be hided, which is the case when the mouse is no + * longer hovering on the object + * @param {{x:Number, y:Number}} pointer + * @private */ - exports.repositionNodes = function() { - for (var i = 0; i < this.nodeIndices.length; i++) { - var node = this.nodes[this.nodeIndices[i]]; - if ((node.xFixed == false || node.yFixed == false)) { - var radius = 10 * 0.1*this.nodeIndices.length * Math.min(100,node.options.mass); - var angle = 2 * Math.PI * Math.random(); - if (node.xFixed == false) {node.x = radius * Math.cos(angle);} - if (node.yFixed == false) {node.y = radius * Math.sin(angle);} - this._repositionBezierNodes(node); + Network.prototype._checkHidePopup = function (pointer) { + if (!this.popupObj || !this._getNodeAt(pointer) ) { + this.popupObj = undefined; + if (this.popup) { + this.popup.hide(); } } }; /** - * We determine how many connections denote an important hub. - * We take the mean + 2*std as the important hub size. (Assuming a normal distribution of data, ~2.2%) - * - * @private + * Set a new size for the network + * @param {string} width Width in pixels or percentage (for example '800px' + * or '50%') + * @param {string} height Height in pixels or percentage (for example '400px' + * or '30%') */ - exports._getHubSize = function() { - var average = 0; - var averageSquared = 0; - var hubCounter = 0; - var largestHub = 0; - - for (var i = 0; i < this.nodeIndices.length; i++) { + Network.prototype.setSize = function(width, height) { + var emitEvent = false; + var oldWidth = this.frame.canvas.width; + var oldHeight = this.frame.canvas.height; + if (width != this.constants.width || height != this.constants.height || this.frame.style.width != width || this.frame.style.height != height) { + this.frame.style.width = width; + this.frame.style.height = height; - var node = this.nodes[this.nodeIndices[i]]; - if (node.dynamicEdgesLength > largestHub) { - largestHub = node.dynamicEdgesLength; - } - average += node.dynamicEdgesLength; - averageSquared += Math.pow(node.dynamicEdgesLength,2); - hubCounter += 1; - } - average = average / hubCounter; - averageSquared = averageSquared / hubCounter; + this.frame.canvas.style.width = '100%'; + this.frame.canvas.style.height = '100%'; - var variance = averageSquared - Math.pow(average,2); + this.frame.canvas.width = this.frame.canvas.clientWidth * this.pixelRatio; + this.frame.canvas.height = this.frame.canvas.clientHeight * this.pixelRatio; - var standardDeviation = Math.sqrt(variance); + this.constants.width = width; + this.constants.height = height; - this.hubThreshold = Math.floor(average + 2*standardDeviation); + emitEvent = true; + } + else { + // this would adapt the width of the canvas to the width from 100% if and only if + // there is a change. - // always have at least one to cluster - if (this.hubThreshold > largestHub) { - this.hubThreshold = largestHub; + if (this.frame.canvas.width != this.frame.canvas.clientWidth * this.pixelRatio) { + this.frame.canvas.width = this.frame.canvas.clientWidth * this.pixelRatio; + emitEvent = true; + } + if (this.frame.canvas.height != this.frame.canvas.clientHeight * this.pixelRatio) { + this.frame.canvas.height = this.frame.canvas.clientHeight * this.pixelRatio; + emitEvent = true; + } } - // console.log("average",average,"averageSQ",averageSquared,"var",variance,"std",standardDeviation); - // console.log("hubThreshold:",this.hubThreshold); - }; - - - /** - * We reduce the amount of "extension nodes" or chains. These are not quickly clustered with the outliers and hubs methods - * with this amount we can cluster specifically on these chains. - * - * @param {Number} fraction | between 0 and 1, the percentage of chains to reduce - * @private - */ - exports._reduceAmountOfChains = function(fraction) { - this.hubThreshold = 2; - var reduceAmount = Math.floor(this.nodeIndices.length * fraction); - for (var nodeId in this.nodes) { - if (this.nodes.hasOwnProperty(nodeId)) { - if (this.nodes[nodeId].dynamicEdgesLength == 2 && this.nodes[nodeId].dynamicEdges.length >= 2) { - if (reduceAmount > 0) { - this._formClusterFromHub(this.nodes[nodeId],true,true,1); - reduceAmount -= 1; - } - } - } + if (emitEvent == true) { + this.emit('resize', {width:this.frame.canvas.width * this.pixelRatio,height:this.frame.canvas.height * this.pixelRatio, oldWidth: oldWidth * this.pixelRatio, oldHeight: oldHeight * this.pixelRatio}); } }; /** - * We get the amount of "extension nodes" or chains. These are not quickly clustered with the outliers and hubs methods - * with this amount we can cluster specifically on these chains. - * + * Set a data set with nodes for the network + * @param {Array | DataSet | DataView} nodes The data containing the nodes. * @private */ - exports._getChainFraction = function() { - var chains = 0; - var total = 0; - for (var nodeId in this.nodes) { - if (this.nodes.hasOwnProperty(nodeId)) { - if (this.nodes[nodeId].dynamicEdgesLength == 2 && this.nodes[nodeId].dynamicEdges.length >= 2) { - chains += 1; - } - total += 1; - } - } - return chains/total; - }; + Network.prototype._setNodes = function(nodes) { + var oldNodesData = this.nodesData; + if (nodes instanceof DataSet || nodes instanceof DataView) { + this.nodesData = nodes; + } + else if (Array.isArray(nodes)) { + this.nodesData = new DataSet(); + this.nodesData.add(nodes); + } + else if (!nodes) { + this.nodesData = new DataSet(); + } + else { + throw new TypeError('Array or DataSet expected'); + } -/***/ }, -/* 58 */ -/***/ function(module, exports, __webpack_require__) { + if (oldNodesData) { + // unsubscribe from old dataset + util.forEach(this.nodesListeners, function (callback, event) { + oldNodesData.off(event, callback); + }); + } - var util = __webpack_require__(1); - var Node = __webpack_require__(40); + // remove drawn nodes + this.nodes = {}; - /** - * Creation of the SectorMixin var. - * - * This contains all the functions the Network object can use to employ the sector system. - * The sector system is always used by Network, though the benefits only apply to the use of clustering. - * If clustering is not used, there is no overhead except for a duplicate object with references to nodes and edges. - */ + if (this.nodesData) { + // subscribe to new dataset + var me = this; + util.forEach(this.nodesListeners, function (callback, event) { + me.nodesData.on(event, callback); + }); - /** - * This function is only called by the setData function of the Network object. - * This loads the global references into the active sector. This initializes the sector. - * - * @private - */ - exports._putDataInSector = function() { - this.sectors["active"][this._sector()].nodes = this.nodes; - this.sectors["active"][this._sector()].edges = this.edges; - this.sectors["active"][this._sector()].nodeIndices = this.nodeIndices; + // draw all new nodes + var ids = this.nodesData.getIds(); + this._addNodes(ids); + } + this._updateSelection(); }; - /** - * /** - * This function sets the global references to nodes, edges and nodeIndices back to - * those of the supplied (active) sector. If a type is defined, do the specific type - * - * @param {String} sectorId - * @param {String} [sectorType] | "active" or "frozen" + * Add nodes + * @param {Number[] | String[]} ids * @private */ - exports._switchToSector = function(sectorId, sectorType) { - if (sectorType === undefined || sectorType == "active") { - this._switchToActiveSector(sectorId); - } - else { - this._switchToFrozenSector(sectorId); + Network.prototype._addNodes = function(ids) { + var id; + for (var i = 0, len = ids.length; i < len; i++) { + id = ids[i]; + var data = this.nodesData.get(id); + var node = new Node(data, this.images, this.groups, this.constants); + this.nodes[id] = node; // note: this may replace an existing node + if ((node.xFixed == false || node.yFixed == false) && (node.x === null || node.y === null)) { + var radius = 10 * 0.1*ids.length + 10; + var angle = 2 * Math.PI * Math.random(); + if (node.xFixed == false) {node.x = radius * Math.cos(angle);} + if (node.yFixed == false) {node.y = radius * Math.sin(angle);} + } + this.moving = true; } - }; - - /** - * This function sets the global references to nodes, edges and nodeIndices back to - * those of the supplied active sector. - * - * @param sectorId - * @private - */ - exports._switchToActiveSector = function(sectorId) { - this.nodeIndices = this.sectors["active"][sectorId]["nodeIndices"]; - this.nodes = this.sectors["active"][sectorId]["nodes"]; - this.edges = this.sectors["active"][sectorId]["edges"]; + this._updateNodeIndexList(); + if (this.constants.hierarchicalLayout.enabled == true && this.initializing == false) { + this._resetLevels(); + this._setupHierarchicalLayout(); + } + this._updateCalculationNodes(); + this._reconnectEdges(); + this._updateValueRange(this.nodes); + this.updateLabels(); }; - /** - * This function sets the global references to nodes, edges and nodeIndices back to - * those of the supplied active sector. - * + * Update existing nodes, or create them when not yet existing + * @param {Number[] | String[]} ids * @private */ - exports._switchToSupportSector = function() { - this.nodeIndices = this.sectors["support"]["nodeIndices"]; - this.nodes = this.sectors["support"]["nodes"]; - this.edges = this.sectors["support"]["edges"]; + Network.prototype._updateNodes = function(ids,changedData) { + var nodes = this.nodes; + for (var i = 0, len = ids.length; i < len; i++) { + var id = ids[i]; + var node = nodes[id]; + var data = changedData[i]; + if (node) { + // update node + node.setProperties(data, this.constants); + } + else { + // create node + node = new Node(properties, this.images, this.groups, this.constants); + nodes[id] = node; + } + } + this.moving = true; + if (this.constants.hierarchicalLayout.enabled == true && this.initializing == false) { + this._resetLevels(); + this._setupHierarchicalLayout(); + } + this._updateNodeIndexList(); + this._updateValueRange(nodes); }; - /** - * This function sets the global references to nodes, edges and nodeIndices back to - * those of the supplied frozen sector. - * - * @param sectorId + * Remove existing nodes. If nodes do not exist, the method will just ignore it. + * @param {Number[] | String[]} ids * @private */ - exports._switchToFrozenSector = function(sectorId) { - this.nodeIndices = this.sectors["frozen"][sectorId]["nodeIndices"]; - this.nodes = this.sectors["frozen"][sectorId]["nodes"]; - this.edges = this.sectors["frozen"][sectorId]["edges"]; + Network.prototype._removeNodes = function(ids) { + var nodes = this.nodes; + for (var i = 0, len = ids.length; i < len; i++) { + var id = ids[i]; + delete nodes[id]; + } + this._updateNodeIndexList(); + if (this.constants.hierarchicalLayout.enabled == true && this.initializing == false) { + this._resetLevels(); + this._setupHierarchicalLayout(); + } + this._updateCalculationNodes(); + this._reconnectEdges(); + this._updateSelection(); + this._updateValueRange(nodes); }; - /** - * This function sets the global references to nodes, edges and nodeIndices back to - * those of the currently active sector. - * + * Load edges by reading the data table + * @param {Array | DataSet | DataView} edges The data containing the edges. * @private - */ - exports._loadLatestSector = function() { - this._switchToSector(this._sector()); - }; - - - /** - * This function returns the currently active sector Id - * - * @returns {String} * @private */ - exports._sector = function() { - return this.activeSector[this.activeSector.length-1]; - }; - + Network.prototype._setEdges = function(edges) { + var oldEdgesData = this.edgesData; - /** - * This function returns the previously active sector Id - * - * @returns {String} - * @private - */ - exports._previousSector = function() { - if (this.activeSector.length > 1) { - return this.activeSector[this.activeSector.length-2]; + if (edges instanceof DataSet || edges instanceof DataView) { + this.edgesData = edges; + } + else if (Array.isArray(edges)) { + this.edgesData = new DataSet(); + this.edgesData.add(edges); + } + else if (!edges) { + this.edgesData = new DataSet(); } else { - throw new TypeError('there are not enough sectors in the this.activeSector array.'); + throw new TypeError('Array or DataSet expected'); + } + + if (oldEdgesData) { + // unsubscribe from old dataset + util.forEach(this.edgesListeners, function (callback, event) { + oldEdgesData.off(event, callback); + }); } - }; + // remove drawn edges + this.edges = {}; - /** - * We add the active sector at the end of the this.activeSector array - * This ensures it is the currently active sector returned by _sector() and it reaches the top - * of the activeSector stack. When we reverse our steps we move from the end to the beginning of this stack. - * - * @param newId - * @private - */ - exports._setActiveSector = function(newId) { - this.activeSector.push(newId); - }; + if (this.edgesData) { + // subscribe to new dataset + var me = this; + util.forEach(this.edgesListeners, function (callback, event) { + me.edgesData.on(event, callback); + }); + // draw all new nodes + var ids = this.edgesData.getIds(); + this._addEdges(ids); + } - /** - * We remove the currently active sector id from the active sector stack. This happens when - * we reactivate the previously active sector - * - * @private - */ - exports._forgetLastSector = function() { - this.activeSector.pop(); + this._reconnectEdges(); }; - /** - * This function creates a new active sector with the supplied newId. This newId - * is the expanding node id. - * - * @param {String} newId | Id of the new active sector + * Add edges + * @param {Number[] | String[]} ids * @private */ - exports._createNewSector = function(newId) { - // create the new sector - this.sectors["active"][newId] = {"nodes":{}, - "edges":{}, - "nodeIndices":[], - "formationScale": this.scale, - "drawingNode": undefined}; + Network.prototype._addEdges = function (ids) { + var edges = this.edges, + edgesData = this.edgesData; - // create the new sector render node. This gives visual feedback that you are in a new sector. - this.sectors["active"][newId]['drawingNode'] = new Node( - {id:newId, - color: { - background: "#eaefef", - border: "495c5e" - } - },{},{},this.constants); - this.sectors["active"][newId]['drawingNode'].clusterSize = 2; - }; + for (var i = 0, len = ids.length; i < len; i++) { + var id = ids[i]; + var oldEdge = edges[id]; + if (oldEdge) { + oldEdge.disconnect(); + } - /** - * This function removes the currently active sector. This is called when we create a new - * active sector. - * - * @param {String} sectorId | Id of the active sector that will be removed - * @private - */ - exports._deleteActiveSector = function(sectorId) { - delete this.sectors["active"][sectorId]; + var data = edgesData.get(id, {"showInternalIds" : true}); + edges[id] = new Edge(data, this, this.constants); + } + this.moving = true; + this._updateValueRange(edges); + this._createBezierNodes(); + this._updateCalculationNodes(); + if (this.constants.hierarchicalLayout.enabled == true && this.initializing == false) { + this._resetLevels(); + this._setupHierarchicalLayout(); + } }; - /** - * This function removes the currently active sector. This is called when we reactivate - * the previously active sector. - * - * @param {String} sectorId | Id of the active sector that will be removed + * Update existing edges, or create them when not yet existing + * @param {Number[] | String[]} ids * @private */ - exports._deleteFrozenSector = function(sectorId) { - delete this.sectors["frozen"][sectorId]; - }; + Network.prototype._updateEdges = function (ids) { + var edges = this.edges, + edgesData = this.edgesData; + for (var i = 0, len = ids.length; i < len; i++) { + var id = ids[i]; + + var data = edgesData.get(id); + var edge = edges[id]; + if (edge) { + // update edge + edge.disconnect(); + edge.setProperties(data, this.constants); + edge.connect(); + } + else { + // create edge + edge = new Edge(data, this, this.constants); + this.edges[id] = edge; + } + } + this._createBezierNodes(); + if (this.constants.hierarchicalLayout.enabled == true && this.initializing == false) { + this._resetLevels(); + this._setupHierarchicalLayout(); + } + this.moving = true; + this._updateValueRange(edges); + }; /** - * Freezing an active sector means moving it from the "active" object to the "frozen" object. - * We copy the references, then delete the active entree. - * - * @param sectorId + * Remove existing edges. Non existing ids will be ignored + * @param {Number[] | String[]} ids * @private */ - exports._freezeSector = function(sectorId) { - // we move the set references from the active to the frozen stack. - this.sectors["frozen"][sectorId] = this.sectors["active"][sectorId]; + Network.prototype._removeEdges = function (ids) { + var edges = this.edges; + for (var i = 0, len = ids.length; i < len; i++) { + var id = ids[i]; + var edge = edges[id]; + if (edge) { + if (edge.via != null) { + delete this.sectors['support']['nodes'][edge.via.id]; + } + edge.disconnect(); + delete edges[id]; + } + } - // we have moved the sector data into the frozen set, we now remove it from the active set - this._deleteActiveSector(sectorId); + this.moving = true; + this._updateValueRange(edges); + if (this.constants.hierarchicalLayout.enabled == true && this.initializing == false) { + this._resetLevels(); + this._setupHierarchicalLayout(); + } + this._updateCalculationNodes(); }; - /** - * This is the reverse operation of _freezeSector. Activating means moving the sector from the "frozen" - * object to the "active" object. - * - * @param sectorId + * Reconnect all edges * @private */ - exports._activateSector = function(sectorId) { - // we move the set references from the frozen to the active stack. - this.sectors["active"][sectorId] = this.sectors["frozen"][sectorId]; + Network.prototype._reconnectEdges = function() { + var id, + nodes = this.nodes, + edges = this.edges; + for (id in nodes) { + if (nodes.hasOwnProperty(id)) { + nodes[id].edges = []; + nodes[id].dynamicEdges = []; + } + } - // we have moved the sector data into the active set, we now remove it from the frozen stack - this._deleteFrozenSector(sectorId); + for (id in edges) { + if (edges.hasOwnProperty(id)) { + var edge = edges[id]; + edge.from = null; + edge.to = null; + edge.connect(); + } + } }; - /** - * This function merges the data from the currently active sector with a frozen sector. This is used - * in the process of reverting back to the previously active sector. - * The data that is placed in the frozen (the previously active) sector is the node that has been removed from it - * upon the creation of a new active sector. - * - * @param sectorId + * Update the values of all object in the given array according to the current + * value range of the objects in the array. + * @param {Object} obj An object containing a set of Edges or Nodes + * The objects must have a method getValue() and + * setValueRange(min, max). * @private */ - exports._mergeThisWithFrozen = function(sectorId) { - // copy all nodes - for (var nodeId in this.nodes) { - if (this.nodes.hasOwnProperty(nodeId)) { - this.sectors["frozen"][sectorId]["nodes"][nodeId] = this.nodes[nodeId]; - } - } + Network.prototype._updateValueRange = function(obj) { + var id; - // copy all edges (if not fully clustered, else there are no edges) - for (var edgeId in this.edges) { - if (this.edges.hasOwnProperty(edgeId)) { - this.sectors["frozen"][sectorId]["edges"][edgeId] = this.edges[edgeId]; + // determine the range of the objects + var valueMin = undefined; + var valueMax = undefined; + for (id in obj) { + if (obj.hasOwnProperty(id)) { + var value = obj[id].getValue(); + if (value !== undefined) { + valueMin = (valueMin === undefined) ? value : Math.min(value, valueMin); + valueMax = (valueMax === undefined) ? value : Math.max(value, valueMax); + } } } - // merge the nodeIndices - for (var i = 0; i < this.nodeIndices.length; i++) { - this.sectors["frozen"][sectorId]["nodeIndices"].push(this.nodeIndices[i]); + // adjust the range of all objects + if (valueMin !== undefined && valueMax !== undefined) { + for (id in obj) { + if (obj.hasOwnProperty(id)) { + obj[id].setValueRange(valueMin, valueMax); + } + } } }; - /** - * This clusters the sector to one cluster. It was a single cluster before this process started so - * we revert to that state. The clusterToFit function with a maximum size of 1 node does this. - * - * @private + * Redraw the network with the current data + * chart will be resized too. */ - exports._collapseThisToSingleCluster = function() { - this.clusterToFit(1,false); + Network.prototype.redraw = function() { + this.setSize(this.constants.width, this.constants.height); + this._redraw(); }; - /** - * We create a new active sector from the node that we want to open. - * - * @param node + * Redraw the network with the current data + * @param hidden | used to get the first estimate of the node sizes. only the nodes are drawn after which they are quickly drawn over. * @private */ - exports._addSector = function(node) { - // this is the currently active sector - var sector = this._sector(); + Network.prototype._redraw = function(hidden) { + var ctx = this.frame.canvas.getContext('2d'); - // // this should allow me to select nodes from a frozen set. - // if (this.sectors['active'][sector]["nodes"].hasOwnProperty(node.id)) { - // console.log("the node is part of the active sector"); - // } - // else { - // console.log("I dont know what the fuck happened!!"); - // } - - // when we switch to a new sector, we remove the node that will be expanded from the current nodes list. - delete this.nodes[node.id]; - - var unqiueIdentifier = util.randomUUID(); - - // we fully freeze the currently active sector - this._freezeSector(sector); - - // we create a new active sector. This sector has the Id of the node to ensure uniqueness - this._createNewSector(unqiueIdentifier); - - // we add the active sector to the sectors array to be able to revert these steps later on - this._setActiveSector(unqiueIdentifier); - - // we redirect the global references to the new sector's references. this._sector() now returns unqiueIdentifier - this._switchToSector(this._sector()); - - // finally we add the node we removed from our previous active sector to the new active sector - this.nodes[node.id] = node; - }; - - - /** - * We close the sector that is currently open and revert back to the one before. - * If the active sector is the "default" sector, nothing happens. - * - * @private - */ - exports._collapseSector = function() { - // the currently active sector - var sector = this._sector(); + ctx.setTransform(this.pixelRatio, 0, 0, this.pixelRatio, 0, 0); - // we cannot collapse the default sector - if (sector != "default") { - if ((this.nodeIndices.length == 1) || - (this.sectors["active"][sector]["drawingNode"].width*this.scale < this.constants.clustering.screenSizeThreshold * this.frame.canvas.clientWidth) || - (this.sectors["active"][sector]["drawingNode"].height*this.scale < this.constants.clustering.screenSizeThreshold * this.frame.canvas.clientHeight)) { - var previousSector = this._previousSector(); + // clear the canvas + var w = this.frame.canvas.width * this.pixelRatio; + var h = this.frame.canvas.height * this.pixelRatio; + ctx.clearRect(0, 0, w, h); - // we collapse the sector back to a single cluster - this._collapseThisToSingleCluster(); + // set scaling and translation + ctx.save(); + ctx.translate(this.translation.x, this.translation.y); + ctx.scale(this.scale, this.scale); - // we move the remaining nodes, edges and nodeIndices to the previous sector. - // This previous sector is the one we will reactivate - this._mergeThisWithFrozen(previousSector); + this.canvasTopLeft = { + "x": this._XconvertDOMtoCanvas(0), + "y": this._YconvertDOMtoCanvas(0) + }; + this.canvasBottomRight = { + "x": this._XconvertDOMtoCanvas(this.frame.canvas.clientWidth * this.pixelRatio), + "y": this._YconvertDOMtoCanvas(this.frame.canvas.clientHeight * this.pixelRatio) + }; - // the previously active (frozen) sector now has all the data from the currently active sector. - // we can now delete the active sector. - this._deleteActiveSector(sector); + if (!(hidden == true)) { + this._doInAllSectors("_drawAllSectorNodes", ctx); + if (this.drag.dragging == false || this.drag.dragging === undefined || this.constants.hideEdgesOnDrag == false) { + this._doInAllSectors("_drawEdges", ctx); + } + } - // we activate the previously active (and currently frozen) sector. - this._activateSector(previousSector); + if (this.drag.dragging == false || this.drag.dragging === undefined || this.constants.hideNodesOnDrag == false) { + this._doInAllSectors("_drawNodes",ctx,false); + } - // we load the references from the newly active sector into the global references - this._switchToSector(previousSector); + if (!(hidden == true)) { + if (this.controlNodesActive == true) { + this._doInAllSectors("_drawControlNodes", ctx); + } + } - // we forget the previously active sector because we reverted to the one before - this._forgetLastSector(); + // this._doInSupportSector("_drawNodes",ctx,true); + // this._drawTree(ctx,"#F00F0F"); - // finally, we update the node index list. - this._updateNodeIndexList(); + // restore original scaling and translation + ctx.restore(); - // we refresh the list with calulation nodes and calculation node indices. - this._updateCalculationNodes(); - } + if (hidden == true) { + ctx.clearRect(0, 0, w, h); } }; - /** - * This runs a function in all active sectors. This is used in _redraw() and the _initializeForceCalculation(). - * - * @param {String} runFunction | This is the NAME of a function we want to call in all active sectors - * | we dont pass the function itself because then the "this" is the window object - * | instead of the Network object - * @param {*} [argument] | Optional: arguments to pass to the runFunction + * Set the translation of the network + * @param {Number} offsetX Horizontal offset + * @param {Number} offsetY Vertical offset * @private */ - exports._doInAllActiveSectors = function(runFunction,argument) { - var returnValues = []; - if (argument === undefined) { - for (var sector in this.sectors["active"]) { - if (this.sectors["active"].hasOwnProperty(sector)) { - // switch the global references to those of this sector - this._switchToActiveSector(sector); - returnValues.push( this[runFunction]() ); - } - } + Network.prototype._setTranslation = function(offsetX, offsetY) { + if (this.translation === undefined) { + this.translation = { + x: 0, + y: 0 + }; } - else { - for (var sector in this.sectors["active"]) { - if (this.sectors["active"].hasOwnProperty(sector)) { - // switch the global references to those of this sector - this._switchToActiveSector(sector); - var args = Array.prototype.splice.call(arguments, 1); - if (args.length > 1) { - returnValues.push( this[runFunction](args[0],args[1]) ); - } - else { - returnValues.push( this[runFunction](argument) ); - } - } - } + + if (offsetX !== undefined) { + this.translation.x = offsetX; + } + if (offsetY !== undefined) { + this.translation.y = offsetY; } - // we revert the global references back to our active sector - this._loadLatestSector(); - return returnValues; - }; + this.emit('viewChanged'); + }; /** - * This runs a function in all active sectors. This is used in _redraw() and the _initializeForceCalculation(). - * - * @param {String} runFunction | This is the NAME of a function we want to call in all active sectors - * | we dont pass the function itself because then the "this" is the window object - * | instead of the Network object - * @param {*} [argument] | Optional: arguments to pass to the runFunction + * Get the translation of the network + * @return {Object} translation An object with parameters x and y, both a number * @private */ - exports._doInSupportSector = function(runFunction,argument) { - var returnValues = false; - if (argument === undefined) { - this._switchToSupportSector(); - returnValues = this[runFunction](); - } - else { - this._switchToSupportSector(); - var args = Array.prototype.splice.call(arguments, 1); - if (args.length > 1) { - returnValues = this[runFunction](args[0],args[1]); - } - else { - returnValues = this[runFunction](argument); - } - } - // we revert the global references back to our active sector - this._loadLatestSector(); - return returnValues; + Network.prototype._getTranslation = function() { + return { + x: this.translation.x, + y: this.translation.y + }; }; - /** - * This runs a function in all frozen sectors. This is used in the _redraw(). - * - * @param {String} runFunction | This is the NAME of a function we want to call in all active sectors - * | we don't pass the function itself because then the "this" is the window object - * | instead of the Network object - * @param {*} [argument] | Optional: arguments to pass to the runFunction + * Scale the network + * @param {Number} scale Scaling factor 1.0 is unscaled * @private */ - exports._doInAllFrozenSectors = function(runFunction,argument) { - if (argument === undefined) { - for (var sector in this.sectors["frozen"]) { - if (this.sectors["frozen"].hasOwnProperty(sector)) { - // switch the global references to those of this sector - this._switchToFrozenSector(sector); - this[runFunction](); - } - } - } - else { - for (var sector in this.sectors["frozen"]) { - if (this.sectors["frozen"].hasOwnProperty(sector)) { - // switch the global references to those of this sector - this._switchToFrozenSector(sector); - var args = Array.prototype.splice.call(arguments, 1); - if (args.length > 1) { - this[runFunction](args[0],args[1]); - } - else { - this[runFunction](argument); - } - } - } - } - this._loadLatestSector(); + Network.prototype._setScale = function(scale) { + this.scale = scale; }; - /** - * This runs a function in all sectors. This is used in the _redraw(). - * - * @param {String} runFunction | This is the NAME of a function we want to call in all active sectors - * | we don't pass the function itself because then the "this" is the window object - * | instead of the Network object - * @param {*} [argument] | Optional: arguments to pass to the runFunction + * Get the current scale of the network + * @return {Number} scale Scaling factor 1.0 is unscaled * @private */ - exports._doInAllSectors = function(runFunction,argument) { - var args = Array.prototype.splice.call(arguments, 1); - if (argument === undefined) { - this._doInAllActiveSectors(runFunction); - this._doInAllFrozenSectors(runFunction); - } - else { - if (args.length > 1) { - this._doInAllActiveSectors(runFunction,args[0],args[1]); - this._doInAllFrozenSectors(runFunction,args[0],args[1]); - } - else { - this._doInAllActiveSectors(runFunction,argument); - this._doInAllFrozenSectors(runFunction,argument); - } - } + Network.prototype._getScale = function() { + return this.scale; }; - /** - * This clears the nodeIndices list. We cannot use this.nodeIndices = [] because we would break the link with the - * active sector. Thus we clear the nodeIndices in the active sector, then reconnect the this.nodeIndices to it. - * + * Convert the X coordinate in DOM-space (coordinate point in browser relative to the container div) to + * the X coordinate in canvas-space (the simulation sandbox, which the camera looks upon) + * @param {number} x + * @returns {number} * @private */ - exports._clearNodeIndexList = function() { - var sector = this._sector(); - this.sectors["active"][sector]["nodeIndices"] = []; - this.nodeIndices = this.sectors["active"][sector]["nodeIndices"]; + Network.prototype._XconvertDOMtoCanvas = function(x) { + return (x - this.translation.x) / this.scale; }; - /** - * Draw the encompassing sector node - * - * @param ctx - * @param sectorType + * Convert the X coordinate in canvas-space (the simulation sandbox, which the camera looks upon) to + * the X coordinate in DOM-space (coordinate point in browser relative to the container div) + * @param {number} x + * @returns {number} * @private */ - exports._drawSectorNodes = function(ctx,sectorType) { - var minY = 1e9, maxY = -1e9, minX = 1e9, maxX = -1e9, node; - for (var sector in this.sectors[sectorType]) { - if (this.sectors[sectorType].hasOwnProperty(sector)) { - if (this.sectors[sectorType][sector]["drawingNode"] !== undefined) { - - this._switchToSector(sector,sectorType); - - minY = 1e9; maxY = -1e9; minX = 1e9; maxX = -1e9; - for (var nodeId in this.nodes) { - if (this.nodes.hasOwnProperty(nodeId)) { - node = this.nodes[nodeId]; - node.resize(ctx); - if (minX > node.x - 0.5 * node.width) {minX = node.x - 0.5 * node.width;} - if (maxX < node.x + 0.5 * node.width) {maxX = node.x + 0.5 * node.width;} - if (minY > node.y - 0.5 * node.height) {minY = node.y - 0.5 * node.height;} - if (maxY < node.y + 0.5 * node.height) {maxY = node.y + 0.5 * node.height;} - } - } - node = this.sectors[sectorType][sector]["drawingNode"]; - node.x = 0.5 * (maxX + minX); - node.y = 0.5 * (maxY + minY); - node.width = 2 * (node.x - minX); - node.height = 2 * (node.y - minY); - node.options.radius = Math.sqrt(Math.pow(0.5*node.width,2) + Math.pow(0.5*node.height,2)); - node.setScale(this.scale); - node._drawCircle(ctx); - } - } - } - }; - - exports._drawAllSectorNodes = function(ctx) { - this._drawSectorNodes(ctx,"frozen"); - this._drawSectorNodes(ctx,"active"); - this._loadLatestSector(); + Network.prototype._XconvertCanvasToDOM = function(x) { + return x * this.scale + this.translation.x; }; - -/***/ }, -/* 59 */ -/***/ function(module, exports, __webpack_require__) { - - var Node = __webpack_require__(40); - /** - * This function can be called from the _doInAllSectors function - * - * @param object - * @param overlappingNodes + * Convert the Y coordinate in DOM-space (coordinate point in browser relative to the container div) to + * the Y coordinate in canvas-space (the simulation sandbox, which the camera looks upon) + * @param {number} y + * @returns {number} * @private */ - exports._getNodesOverlappingWith = function(object, overlappingNodes) { - var nodes = this.nodes; - for (var nodeId in nodes) { - if (nodes.hasOwnProperty(nodeId)) { - if (nodes[nodeId].isOverlappingWith(object)) { - overlappingNodes.push(nodeId); - } - } - } + Network.prototype._YconvertDOMtoCanvas = function(y) { + return (y - this.translation.y) / this.scale; }; /** - * retrieve all nodes overlapping with given object - * @param {Object} object An object with parameters left, top, right, bottom - * @return {Number[]} An array with id's of the overlapping nodes + * Convert the Y coordinate in canvas-space (the simulation sandbox, which the camera looks upon) to + * the Y coordinate in DOM-space (coordinate point in browser relative to the container div) + * @param {number} y + * @returns {number} * @private */ - exports._getAllNodesOverlappingWith = function (object) { - var overlappingNodes = []; - this._doInAllActiveSectors("_getNodesOverlappingWith",object,overlappingNodes); - return overlappingNodes; + Network.prototype._YconvertCanvasToDOM = function(y) { + return y * this.scale + this.translation.y ; }; /** - * Return a position object in canvasspace from a single point in screenspace * - * @param pointer - * @returns {{left: number, top: number, right: number, bottom: number}} - * @private + * @param {object} pos = {x: number, y: number} + * @returns {{x: number, y: number}} + * @constructor */ - exports._pointerToPositionObject = function(pointer) { - var x = this._XconvertDOMtoCanvas(pointer.x); - var y = this._YconvertDOMtoCanvas(pointer.y); - - return { - left: x, - top: y, - right: x, - bottom: y - }; + Network.prototype.canvasToDOM = function (pos) { + return {x: this._XconvertCanvasToDOM(pos.x), y: this._YconvertCanvasToDOM(pos.y)}; }; - /** - * Get the top node at the a specific point (like a click) * - * @param {{x: Number, y: Number}} pointer - * @return {Node | null} node + * @param {object} pos = {x: number, y: number} + * @returns {{x: number, y: number}} + * @constructor + */ + Network.prototype.DOMtoCanvas = function (pos) { + return {x: this._XconvertDOMtoCanvas(pos.x), y: this._YconvertDOMtoCanvas(pos.y)}; + }; + + /** + * Redraw all nodes + * The 2d context of a HTML canvas can be retrieved by canvas.getContext('2d'); + * @param {CanvasRenderingContext2D} ctx + * @param {Boolean} [alwaysShow] * @private */ - exports._getNodeAt = function (pointer) { - // we first check if this is an navigation controls element - var positionObject = this._pointerToPositionObject(pointer); - var overlappingNodes = this._getAllNodesOverlappingWith(positionObject); + Network.prototype._drawNodes = function(ctx,alwaysShow) { + if (alwaysShow === undefined) { + alwaysShow = false; + } - // if there are overlapping nodes, select the last one, this is the - // one which is drawn on top of the others - if (overlappingNodes.length > 0) { - return this.nodes[overlappingNodes[overlappingNodes.length - 1]]; + // first draw the unselected nodes + var nodes = this.nodes; + var selected = []; + + for (var id in nodes) { + if (nodes.hasOwnProperty(id)) { + nodes[id].setScaleAndPos(this.scale,this.canvasTopLeft,this.canvasBottomRight); + if (nodes[id].isSelected()) { + selected.push(id); + } + else { + if (nodes[id].inArea() || alwaysShow) { + nodes[id].draw(ctx); + } + } + } } - else { - return null; + + // draw the selected nodes on top + for (var s = 0, sMax = selected.length; s < sMax; s++) { + if (nodes[selected[s]].inArea() || alwaysShow) { + nodes[selected[s]].draw(ctx); + } } }; - /** - * retrieve all edges overlapping with given object, selector is around center - * @param {Object} object An object with parameters left, top, right, bottom - * @return {Number[]} An array with id's of the overlapping nodes + * Redraw all edges + * The 2d context of a HTML canvas can be retrieved by canvas.getContext('2d'); + * @param {CanvasRenderingContext2D} ctx * @private */ - exports._getEdgesOverlappingWith = function (object, overlappingEdges) { + Network.prototype._drawEdges = function(ctx) { var edges = this.edges; - for (var edgeId in edges) { - if (edges.hasOwnProperty(edgeId)) { - if (edges[edgeId].isOverlappingWith(object)) { - overlappingEdges.push(edgeId); + for (var id in edges) { + if (edges.hasOwnProperty(id)) { + var edge = edges[id]; + edge.setScale(this.scale); + if (edge.connected) { + edges[id].draw(ctx); } } } }; - /** - * retrieve all nodes overlapping with given object - * @param {Object} object An object with parameters left, top, right, bottom - * @return {Number[]} An array with id's of the overlapping nodes + * Redraw all edges + * The 2d context of a HTML canvas can be retrieved by canvas.getContext('2d'); + * @param {CanvasRenderingContext2D} ctx * @private */ - exports._getAllEdgesOverlappingWith = function (object) { - var overlappingEdges = []; - this._doInAllActiveSectors("_getEdgesOverlappingWith",object,overlappingEdges); - return overlappingEdges; + Network.prototype._drawControlNodes = function(ctx) { + var edges = this.edges; + for (var id in edges) { + if (edges.hasOwnProperty(id)) { + edges[id]._drawControlNodes(ctx); + } + } }; /** - * Place holder. To implement change the _getNodeAt to a _getObjectAt. Have the _getObjectAt call - * _getNodeAt and _getEdgesAt, then priortize the selection to user preferences. - * - * @param pointer - * @returns {null} + * Find a stable position for all nodes * @private */ - exports._getEdgeAt = function(pointer) { - var positionObject = this._pointerToPositionObject(pointer); - var overlappingEdges = this._getAllEdgesOverlappingWith(positionObject); + Network.prototype._stabilize = function() { + if (this.constants.freezeForStabilization == true) { + this._freezeDefinedNodes(); + } - if (overlappingEdges.length > 0) { - return this.edges[overlappingEdges[overlappingEdges.length - 1]]; + // find stable position + var count = 0; + while (this.moving && count < this.constants.stabilizationIterations) { + this._physicsTick(); + count++; } - else { - return null; + + if (this.constants.zoomExtentOnStabilize == true) { + this.zoomExtent(undefined, false, true); } - }; + if (this.constants.freezeForStabilization == true) { + this._restoreFrozenNodes(); + } + }; /** - * Add object to the selection array. + * When initializing and stabilizing, we can freeze nodes with a predefined position. This greatly speeds up stabilization + * because only the supportnodes for the smoothCurves have to settle. * - * @param obj * @private */ - exports._addToSelection = function(obj) { - if (obj instanceof Node) { - this.selectionObj.nodes[obj.id] = obj; - } - else { - this.selectionObj.edges[obj.id] = obj; + Network.prototype._freezeDefinedNodes = function() { + var nodes = this.nodes; + for (var id in nodes) { + if (nodes.hasOwnProperty(id)) { + if (nodes[id].x != null && nodes[id].y != null) { + nodes[id].fixedData.x = nodes[id].xFixed; + nodes[id].fixedData.y = nodes[id].yFixed; + nodes[id].xFixed = true; + nodes[id].yFixed = true; + } + } } }; /** - * Add object to the selection array. + * Unfreezes the nodes that have been frozen by _freezeDefinedNodes. * - * @param obj * @private */ - exports._addToHover = function(obj) { - if (obj instanceof Node) { - this.hoverObj.nodes[obj.id] = obj; - } - else { - this.hoverObj.edges[obj.id] = obj; + Network.prototype._restoreFrozenNodes = function() { + var nodes = this.nodes; + for (var id in nodes) { + if (nodes.hasOwnProperty(id)) { + if (nodes[id].fixedData.x != null) { + nodes[id].xFixed = nodes[id].fixedData.x; + nodes[id].yFixed = nodes[id].fixedData.y; + } + } } }; /** - * Remove a single option from selection. - * - * @param {Object} obj + * Check if any of the nodes is still moving + * @param {number} vmin the minimum velocity considered as 'moving' + * @return {boolean} true if moving, false if non of the nodes is moving * @private */ - exports._removeFromSelection = function(obj) { - if (obj instanceof Node) { - delete this.selectionObj.nodes[obj.id]; - } - else { - delete this.selectionObj.edges[obj.id]; + Network.prototype._isMoving = function(vmin) { + var nodes = this.nodes; + for (var id in nodes) { + if (nodes.hasOwnProperty(id) && nodes[id].isMoving(vmin)) { + return true; + } } + return false; }; + /** - * Unselect all. The selectionObj is useful for this. + * /** + * Perform one discrete step for all nodes * - * @param {Boolean} [doNotTrigger] | ignore trigger * @private */ - exports._unselectAll = function(doNotTrigger) { - if (doNotTrigger === undefined) { - doNotTrigger = false; - } - for(var nodeId in this.selectionObj.nodes) { - if(this.selectionObj.nodes.hasOwnProperty(nodeId)) { - this.selectionObj.nodes[nodeId].unselect(); + Network.prototype._discreteStepNodes = function() { + var interval = this.physicsDiscreteStepsize; + var nodes = this.nodes; + var nodeId; + var nodesPresent = false; + + if (this.constants.maxVelocity > 0) { + for (nodeId in nodes) { + if (nodes.hasOwnProperty(nodeId)) { + nodes[nodeId].discreteStepLimited(interval, this.constants.maxVelocity); + nodesPresent = true; + } } } - for(var edgeId in this.selectionObj.edges) { - if(this.selectionObj.edges.hasOwnProperty(edgeId)) { - this.selectionObj.edges[edgeId].unselect(); + else { + for (nodeId in nodes) { + if (nodes.hasOwnProperty(nodeId)) { + nodes[nodeId].discreteStep(interval); + nodesPresent = true; + } } } - this.selectionObj = {nodes:{},edges:{}}; - - if (doNotTrigger == false) { - this.emit('select', this.getSelection()); + if (nodesPresent == true) { + var vminCorrected = this.constants.minVelocity / Math.max(this.scale,0.05); + if (vminCorrected > 0.5*this.constants.maxVelocity) { + return true; + } + else { + return this._isMoving(vminCorrected); + } } + return false; }; - /** - * Unselect all clusters. The selectionObj is useful for this. - * - * @param {Boolean} [doNotTrigger] | ignore trigger - * @private - */ - exports._unselectClusters = function(doNotTrigger) { - if (doNotTrigger === undefined) { - doNotTrigger = false; - } - for (var nodeId in this.selectionObj.nodes) { - if (this.selectionObj.nodes.hasOwnProperty(nodeId)) { - if (this.selectionObj.nodes[nodeId].clusterSize > 1) { - this.selectionObj.nodes[nodeId].unselect(); - this._removeFromSelection(this.selectionObj.nodes[nodeId]); - } + Network.prototype._revertPhysicsState = function() { + var nodes = this.nodes; + for (var nodeId in nodes) { + if (nodes.hasOwnProperty(nodeId)) { + nodes[nodeId].revertPosition(); } } + } - if (doNotTrigger == false) { - this.emit('select', this.getSelection()); + Network.prototype._revertPhysicsTick = function() { + this._doInAllActiveSectors("_revertPhysicsState"); + if (this.constants.smoothCurves.enabled == true && this.constants.smoothCurves.dynamic == true) { + this._doInSupportSector("_revertPhysicsState"); } - }; - + } /** - * return the number of selected nodes + * A single simulation step (or "tick") in the physics simulation * - * @returns {number} * @private */ - exports._getSelectedNodeCount = function() { - var count = 0; - for (var nodeId in this.selectionObj.nodes) { - if (this.selectionObj.nodes.hasOwnProperty(nodeId)) { - count += 1; + Network.prototype._physicsTick = function() { + if (!this.freezeSimulation) { + if (this.moving == true) { + var mainMovingStatus = false; + var supportMovingStatus = false; + + this._doInAllActiveSectors("_initializeForceCalculation"); + var mainMoving = this._doInAllActiveSectors("_discreteStepNodes"); + if (this.constants.smoothCurves.enabled == true && this.constants.smoothCurves.dynamic == true) { + supportMovingStatus = this._doInSupportSector("_discreteStepNodes"); + } + + // gather movement data from all sectors, if one moves, we are NOT stabilzied + for (var i = 0; i < mainMoving.length; i++) {mainMovingStatus = mainMoving[0] || mainMovingStatus;} + + // determine if the network has stabilzied + this.moving = mainMovingStatus || supportMovingStatus; + + if (this.moving == false) { + this._revertPhysicsTick(); + } + else { + // this is here to ensure that there is no start event when the network is already stable. + if (this.startedStabilization == false) { + this.emit("startStabilization"); + this.startedStabilization = true; + } + } + + this.stabilizationIterations++; } } - return count; }; + /** - * return the selected node + * This function runs one step of the animation. It calls an x amount of physics ticks and one render tick. + * It reschedules itself at the beginning of the function * - * @returns {number} * @private */ - exports._getSelectedNode = function() { - for (var nodeId in this.selectionObj.nodes) { - if (this.selectionObj.nodes.hasOwnProperty(nodeId)) { - return this.selectionObj.nodes[nodeId]; + Network.prototype._animationStep = function() { + // reset the timer so a new scheduled animation step can be set + this.timer = undefined; + + // handle the keyboad movement + this._handleNavigation(); + + // check if the physics have settled + if (this.moving == true) { + var startTime = Date.now(); + this._physicsTick(); + var physicsTime = Date.now() - startTime; + + // run double speed if it is a little graph + if ((this.renderTimestep - this.renderTime > 2 * physicsTime || this.runDoubleSpeed == true) && this.moving == true) { + this._physicsTick(); + + // this makes sure there is no jitter. The decision is taken once to run it at double speed. + if (this.renderTime != 0) { + this.runDoubleSpeed = true + } } } - return null; + + var renderStartTime = Date.now(); + this._redraw(); + this.renderTime = Date.now() - renderStartTime; + + // this schedules a new animation step + this.start(); }; + if (typeof window !== 'undefined') { + window.requestAnimationFrame = window.requestAnimationFrame || window.mozRequestAnimationFrame || + window.webkitRequestAnimationFrame || window.msRequestAnimationFrame; + } + /** - * return the selected edge - * - * @returns {number} - * @private + * Schedule a animation step with the refreshrate interval. */ - exports._getSelectedEdge = function() { - for (var edgeId in this.selectionObj.edges) { - if (this.selectionObj.edges.hasOwnProperty(edgeId)) { - return this.selectionObj.edges[edgeId]; + Network.prototype.start = function() { + if (this.moving == true || this.xIncrement != 0 || this.yIncrement != 0 || this.zoomIncrement != 0 || this.animating == true) { + if (!this.timer) { + if (this.requiresTimeout == true) { + this.timer = window.setTimeout(this._animationStep.bind(this), this.renderTimestep); // wait this.renderTimeStep milliseconds and perform the animation step function + } + else { + this.timer = window.requestAnimationFrame(this._animationStep.bind(this)); // wait this.renderTimeStep milliseconds and perform the animation step function + } + } + } + else { + this._redraw(); + // this check is to ensure that the network does not emit these events if it was already stabilized and setOptions is called (setting moving to true and calling start()) + if (this.stabilizationIterations > 1) { + // trigger the "stabilized" event. + // The event is triggered on the next tick, to prevent the case that + // it is fired while initializing the Network, in which case you would not + // be able to catch it + var me = this; + var params = { + iterations: me.stabilizationIterations + }; + this.stabilizationIterations = 0; + this.startedStabilization = false; + setTimeout(function () { + me.emit("stabilized", params); + }, 0); + } + else { + this.stabilizationIterations = 0; } } - return null; }; /** - * return the number of selected edges + * Move the network according to the keyboard presses. * - * @returns {number} * @private */ - exports._getSelectedEdgeCount = function() { - var count = 0; - for (var edgeId in this.selectionObj.edges) { - if (this.selectionObj.edges.hasOwnProperty(edgeId)) { - count += 1; - } + Network.prototype._handleNavigation = function() { + if (this.xIncrement != 0 || this.yIncrement != 0) { + var translation = this._getTranslation(); + this._setTranslation(translation.x+this.xIncrement, translation.y+this.yIncrement); + } + if (this.zoomIncrement != 0) { + var center = { + x: this.frame.canvas.clientWidth / 2, + y: this.frame.canvas.clientHeight / 2 + }; + this._zoom(this.scale*(1 + this.zoomIncrement), center); } - return count; }; /** - * return the number of selected objects. - * - * @returns {number} - * @private + * Freeze the _animationStep */ - exports._getSelectedObjectCount = function() { - var count = 0; - for(var nodeId in this.selectionObj.nodes) { - if(this.selectionObj.nodes.hasOwnProperty(nodeId)) { - count += 1; - } + Network.prototype.toggleFreeze = function() { + if (this.freezeSimulation == false) { + this.freezeSimulation = true; } - for(var edgeId in this.selectionObj.edges) { - if(this.selectionObj.edges.hasOwnProperty(edgeId)) { - count += 1; - } + else { + this.freezeSimulation = false; + this.start(); } - return count; }; + /** - * Check if anything is selected + * This function cleans the support nodes if they are not needed and adds them when they are. * - * @returns {boolean} + * @param {boolean} [disableStart] * @private */ - exports._selectionIsEmpty = function() { - for(var nodeId in this.selectionObj.nodes) { - if(this.selectionObj.nodes.hasOwnProperty(nodeId)) { - return false; + Network.prototype._configureSmoothCurves = function(disableStart) { + if (disableStart === undefined) { + disableStart = true; + } + if (this.constants.smoothCurves.enabled == true && this.constants.smoothCurves.dynamic == true) { + this._createBezierNodes(); + // cleanup unused support nodes + for (var nodeId in this.sectors['support']['nodes']) { + if (this.sectors['support']['nodes'].hasOwnProperty(nodeId)) { + if (this.edges[this.sectors['support']['nodes'][nodeId].parentEdgeId] === undefined) { + delete this.sectors['support']['nodes'][nodeId]; + } + } } } - for(var edgeId in this.selectionObj.edges) { - if(this.selectionObj.edges.hasOwnProperty(edgeId)) { - return false; + else { + // delete the support nodes + this.sectors['support']['nodes'] = {}; + for (var edgeId in this.edges) { + if (this.edges.hasOwnProperty(edgeId)) { + this.edges[edgeId].via = null; + } } } - return true; + + + this._updateCalculationNodes(); + if (!disableStart) { + this.moving = true; + this.start(); + } }; /** - * check if one of the selected nodes is a cluster. + * 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. * - * @returns {boolean} * @private */ - exports._clusterInSelection = function() { - for(var nodeId in this.selectionObj.nodes) { - if(this.selectionObj.nodes.hasOwnProperty(nodeId)) { - if (this.selectionObj.nodes[nodeId].clusterSize > 1) { - return true; + Network.prototype._createBezierNodes = function() { + if (this.constants.smoothCurves.enabled == true && this.constants.smoothCurves.dynamic == true) { + for (var edgeId in this.edges) { + if (this.edges.hasOwnProperty(edgeId)) { + var edge = this.edges[edgeId]; + if (edge.via == null) { + var nodeId = "edgeId:".concat(edge.id); + this.sectors['support']['nodes'][nodeId] = new Node( + {id:nodeId, + mass:1, + shape:'circle', + image:"", + internalMultiplier:1 + },{},{},this.constants); + edge.via = this.sectors['support']['nodes'][nodeId]; + edge.via.parentEdgeId = edge.id; + edge.positionBezierNode(); + } } } } - return false; }; /** - * select the edges connected to the node that is being selected + * load the functions that load the mixins into the prototype. * - * @param {Node} node * @private */ - exports._selectConnectedEdges = function(node) { - for (var i = 0; i < node.dynamicEdges.length; i++) { - var edge = node.dynamicEdges[i]; - edge.select(); - this._addToSelection(edge); + Network.prototype._initializeMixinLoaders = function () { + for (var mixin in MixinLoader) { + if (MixinLoader.hasOwnProperty(mixin)) { + Network.prototype[mixin] = MixinLoader[mixin]; + } } }; /** - * select the edges connected to the node that is being selected - * - * @param {Node} node - * @private + * Load the XY positions of the nodes into the dataset. */ - exports._hoverConnectedEdges = function(node) { - for (var i = 0; i < node.dynamicEdges.length; i++) { - var edge = node.dynamicEdges[i]; - edge.hover = true; - this._addToHover(edge); - } + Network.prototype.storePosition = function() { + console.log("storePosition is depricated: use .storePositions() from now on.") + this.storePositions(); }; - /** - * unselect the edges connected to the node that is being selected - * - * @param {Node} node - * @private + * Load the XY positions of the nodes into the dataset. */ - exports._unselectConnectedEdges = function(node) { - for (var i = 0; i < node.dynamicEdges.length; i++) { - var edge = node.dynamicEdges[i]; - edge.unselect(); - this._removeFromSelection(edge); + Network.prototype.storePositions = function() { + var dataArray = []; + for (var nodeId in this.nodes) { + if (this.nodes.hasOwnProperty(nodeId)) { + var node = this.nodes[nodeId]; + var allowedToMoveX = !this.nodes.xFixed; + var allowedToMoveY = !this.nodes.yFixed; + if (this.nodesData._data[nodeId].x != Math.round(node.x) || this.nodesData._data[nodeId].y != Math.round(node.y)) { + dataArray.push({id:nodeId,x:Math.round(node.x),y:Math.round(node.y),allowedToMoveX:allowedToMoveX,allowedToMoveY:allowedToMoveY}); + } + } } + this.nodesData.update(dataArray); }; - - - /** - * This is called when someone clicks on a node. either select or deselect it. - * If there is an existing selection and we don't want to append to it, clear the existing selection - * - * @param {Node || Edge} object - * @param {Boolean} append - * @param {Boolean} [doNotTrigger] | ignore trigger - * @private + * Return the positions of the nodes. */ - exports._selectObject = function(object, append, doNotTrigger, highlightEdges, overrideSelectable) { - if (doNotTrigger === undefined) { - doNotTrigger = false; - } - if (highlightEdges === undefined) { - highlightEdges = true; - } - - if (this._selectionIsEmpty() == false && append == false && this.forceAppendSelection == false) { - this._unselectAll(true); - } - - // selectable allows the object to be selected. Override can be used if needed to bypass this. - if (object.selected == false && (this.constants.selectable == true || overrideSelectable)) { - object.select(); - this._addToSelection(object); - if (object instanceof Node && this.blockConnectingEdgeSelection == false && highlightEdges == true) { - this._selectConnectedEdges(object); + Network.prototype.getPositions = function(ids) { + var dataArray = {}; + if (ids !== undefined) { + if (Array.isArray(ids) == true) { + for (var i = 0; i < ids.length; i++) { + if (this.nodes[ids[i]] !== undefined) { + var node = this.nodes[ids[i]]; + dataArray[ids[i]] = {x: Math.round(node.x), y: Math.round(node.y)}; + } + } + } + else { + if (this.nodes[ids] !== undefined) { + var node = this.nodes[ids]; + dataArray[ids] = {x: Math.round(node.x), y: Math.round(node.y)}; + } } - } - // do not select the object if selectable is false, only add it to selection to allow drag to work - else if (object.selected == false) { - this._addToSelection(object); - doNotTrigger = true; } else { - object.unselect(); - this._removeFromSelection(object); - } - - if (doNotTrigger == false) { - this.emit('select', this.getSelection()); + for (var nodeId in this.nodes) { + if (this.nodes.hasOwnProperty(nodeId)) { + var node = this.nodes[nodeId]; + dataArray[nodeId] = {x: Math.round(node.x), y: Math.round(node.y)}; + } + } } + return dataArray; }; - /** - * This is called when someone clicks on a node. either select or deselect it. - * If there is an existing selection and we don't want to append to it, clear the existing selection - * - * @param {Node || Edge} object - * @private - */ - exports._blurObject = function(object) { - if (object.hover == true) { - object.hover = false; - this.emit("blurNode",{node:object.id}); - } - }; /** - * This is called when someone clicks on a node. either select or deselect it. - * If there is an existing selection and we don't want to append to it, clear the existing selection + * Center a node in view. * - * @param {Node || Edge} object - * @private + * @param {Number} nodeId + * @param {Number} [options] */ - exports._hoverObject = function(object) { - if (object.hover == false) { - object.hover = true; - this._addToHover(object); - if (object instanceof Node) { - this.emit("hoverNode",{node:object.id}); + Network.prototype.focusOnNode = function (nodeId, options) { + if (this.nodes.hasOwnProperty(nodeId)) { + if (options === undefined) { + options = {}; } + var nodePosition = {x: this.nodes[nodeId].x, y: this.nodes[nodeId].y}; + options.position = nodePosition; + options.lockedOnNode = nodeId; + + this.moveTo(options) } - if (object instanceof Node) { - this._hoverConnectedEdges(object); + else { + console.log("This nodeId cannot be found."); } }; - /** - * handles the selection part of the touch, only for navigation controls elements; - * Touch is triggered before tap, also before hold. Hold triggers after a while. - * This is the most responsive solution * - * @param {Object} pointer - * @private + * @param {Object} options | options.offset = {x:Number, y:Number} // offset from the center in DOM pixels + * | options.scale = Number // scale to move to + * | options.position = {x:Number, y:Number} // position to move to + * | options.animation = {duration:Number, easingFunction:String} || Boolean // position to move to */ - exports._handleTouch = function(pointer) { - }; - - - /** - * handles the selection part of the tap; - * - * @param {Object} pointer - * @private + Network.prototype.moveTo = function (options) { + if (options === undefined) { + options = {}; + return; + } + if (options.offset === undefined) {options.offset = {x: 0, y: 0}; } + if (options.offset.x === undefined) {options.offset.x = 0; } + if (options.offset.y === undefined) {options.offset.y = 0; } + if (options.scale === undefined) {options.scale = this._getScale(); } + if (options.position === undefined) {options.position = this._getTranslation();} + if (options.animation === undefined) {options.animation = {duration:0}; } + if (options.animation === false ) {options.animation = {duration:0}; } + if (options.animation === true ) {options.animation = {}; } + if (options.animation.duration === undefined) {options.animation.duration = 1000; } // default duration + if (options.animation.easingFunction === undefined) {options.animation.easingFunction = "easeInOutQuad"; } // default easing function + + this.animateView(options); + }; + + /** + * + * @param {Object} options | options.offset = {x:Number, y:Number} // offset from the center in DOM pixels + * | options.time = Number // animation time in milliseconds + * | options.scale = Number // scale to animate to + * | options.position = {x:Number, y:Number} // position to animate to + * | options.easingFunction = String // linear, easeInQuad, easeOutQuad, easeInOutQuad, + * // easeInCubic, easeOutCubic, easeInOutCubic, + * // easeInQuart, easeOutQuart, easeInOutQuart, + * // easeInQuint, easeOutQuint, easeInOutQuint */ - exports._handleTap = function(pointer) { - var node = this._getNodeAt(pointer); - if (node != null) { - this._selectObject(node, false); + Network.prototype.animateView = function (options) { + if (options === undefined) { + options = {}; + return; } - else { - var edge = this._getEdgeAt(pointer); - if (edge != null) { - this._selectObject(edge, false); + + // release if something focussed on the node + this.releaseNode(); + if (options.locked == true) { + this.lockedOnNodeId = options.lockedOnNode; + this.lockedOnNodeOffset = options.offset; + } + + // forcefully complete the old animation if it was still running + if (this.easingTime != 0) { + this._transitionRedraw(1); // by setting easingtime to 1, we finish the animation. + } + + this.sourceScale = this._getScale(); + this.sourceTranslation = this._getTranslation(); + this.targetScale = options.scale; + + // set the scale so the viewCenter is based on the correct zoom level. This is overridden in the transitionRedraw + // but at least then we'll have the target transition + this._setScale(this.targetScale); + var viewCenter = this.DOMtoCanvas({x: 0.5 * this.frame.canvas.clientWidth, y: 0.5 * this.frame.canvas.clientHeight}); + var distanceFromCenter = { // offset from view, distance view has to change by these x and y to center the node + x: viewCenter.x - options.position.x, + y: viewCenter.y - options.position.y + }; + this.targetTranslation = { + x: this.sourceTranslation.x + distanceFromCenter.x * this.targetScale + options.offset.x, + y: this.sourceTranslation.y + distanceFromCenter.y * this.targetScale + options.offset.y + }; + + // if the time is set to 0, don't do an animation + if (options.animation.duration == 0) { + if (this.lockedOnNodeId != null) { + this._classicRedraw = this._redraw; + this._redraw = this._lockedRedraw; } else { - this._unselectAll(); + this._setScale(this.targetScale); + this._setTranslation(this.targetTranslation.x, this.targetTranslation.y); + this._redraw(); } } - var properties = this.getSelection(); - properties['pointer'] = { - DOM: {x: pointer.x, y: pointer.y}, - canvas: {x: this._XconvertDOMtoCanvas(pointer.x), y: this._YconvertDOMtoCanvas(pointer.y)} + else { + this.animating = true; + this.animationSpeed = 1 / (this.renderRefreshRate * options.animation.duration * 0.001) || 1 / this.renderRefreshRate; + this.animationEasingFunction = options.animation.easingFunction; + this._classicRedraw = this._redraw; + this._redraw = this._transitionRedraw; + this._redraw(); + this.start(); } - this.emit("click", properties); - this._redraw(); }; - /** - * handles the selection part of the double tap and opens a cluster if needed - * - * @param {Object} pointer + * used to animate smoothly by hijacking the redraw function. * @private */ - exports._handleDoubleTap = function(pointer) { - var node = this._getNodeAt(pointer); - if (node != null && node !== undefined) { - // we reset the areaCenter here so the opening of the node will occur - this.areaCenter = {"x" : this._XconvertDOMtoCanvas(pointer.x), - "y" : this._YconvertDOMtoCanvas(pointer.y)}; - this.openCluster(node); - } - var properties = this.getSelection(); - properties['pointer'] = { - DOM: {x: pointer.x, y: pointer.y}, - canvas: {x: this._XconvertDOMtoCanvas(pointer.x), y: this._YconvertDOMtoCanvas(pointer.y)} - } - this.emit("doubleClick", properties); - }; + Network.prototype._lockedRedraw = function () { + var nodePosition = {x: this.nodes[this.lockedOnNodeId].x, y: this.nodes[this.lockedOnNodeId].y}; + var viewCenter = this.DOMtoCanvas({x: 0.5 * this.frame.canvas.clientWidth, y: 0.5 * this.frame.canvas.clientHeight}); + var distanceFromCenter = { // offset from view, distance view has to change by these x and y to center the node + x: viewCenter.x - nodePosition.x, + y: viewCenter.y - nodePosition.y + }; + var sourceTranslation = this._getTranslation(); + var targetTranslation = { + x: sourceTranslation.x + distanceFromCenter.x * this.scale + this.lockedOnNodeOffset.x, + y: sourceTranslation.y + distanceFromCenter.y * this.scale + this.lockedOnNodeOffset.y + }; + + this._setTranslation(targetTranslation.x,targetTranslation.y); + this._classicRedraw(); + } + Network.prototype.releaseNode = function () { + if (this.lockedOnNodeId != null) { + this._redraw = this._classicRedraw; + this.lockedOnNodeId = null; + this.lockedOnNodeOffset = null; + } + } /** - * Handle the onHold selection part * - * @param pointer + * @param easingTime * @private */ - exports._handleOnHold = function(pointer) { - var node = this._getNodeAt(pointer); - if (node != null) { - this._selectObject(node,true); - } - else { - var edge = this._getEdgeAt(pointer); - if (edge != null) { - this._selectObject(edge,true); + Network.prototype._transitionRedraw = function (easingTime) { + this.easingTime = easingTime || this.easingTime + this.animationSpeed; + this.easingTime += this.animationSpeed; + + var progress = util.easingFunctions[this.animationEasingFunction](this.easingTime); + + this._setScale(this.sourceScale + (this.targetScale - this.sourceScale) * progress); + this._setTranslation( + this.sourceTranslation.x + (this.targetTranslation.x - this.sourceTranslation.x) * progress, + this.sourceTranslation.y + (this.targetTranslation.y - this.sourceTranslation.y) * progress + ); + + this._classicRedraw(); + + // cleanup + if (this.easingTime >= 1.0) { + this.animating = false; + this.easingTime = 0; + if (this.lockedOnNodeId != null) { + this._redraw = this._lockedRedraw; + } + else { + this._redraw = this._classicRedraw; } + this.emit("animationFinished"); } - this._redraw(); }; - - /** - * handle the onRelease event. These functions are here for the navigation controls module - * and data manipulation module. - * - * @private - */ - exports._handleOnRelease = function(pointer) { - this._manipulationReleaseOverload(pointer); - this._navigationReleaseOverload(pointer); + Network.prototype._classicRedraw = function () { + // placeholder function to be overloaded by animations; }; - exports._manipulationReleaseOverload = function (pointer) {}; - exports._navigationReleaseOverload = function (pointer) {}; - /** - * - * retrieve the currently selected objects - * @return {{nodes: Array., edges: Array.}} selection + * Returns true when the Network is active. + * @returns {boolean} */ - exports.getSelection = function() { - var nodeIds = this.getSelectedNodes(); - var edgeIds = this.getSelectedEdges(); - return {nodes:nodeIds, edges:edgeIds}; + Network.prototype.isActive = function () { + return !this.activator || this.activator.active; }; - /** - * - * retrieve the currently selected nodes - * @return {String[]} selection An array with the ids of the - * selected nodes. - */ - exports.getSelectedNodes = function() { - var idArray = []; - if (this.constants.selectable == true) { - for (var nodeId in this.selectionObj.nodes) { - if (this.selectionObj.nodes.hasOwnProperty(nodeId)) { - idArray.push(nodeId); - } - } - } - return idArray - }; /** - * - * retrieve the currently selected edges - * @return {Array} selection An array with the ids of the - * selected nodes. + * Sets the scale + * @returns {Number} */ - exports.getSelectedEdges = function() { - var idArray = []; - if (this.constants.selectable == true) { - for (var edgeId in this.selectionObj.edges) { - if (this.selectionObj.edges.hasOwnProperty(edgeId)) { - idArray.push(edgeId); - } - } - } - return idArray; + Network.prototype.setScale = function () { + return this._setScale(); }; /** - * select zero or more nodes DEPRICATED - * @param {Number[] | String[]} selection An array with the ids of the - * selected nodes. + * Returns the scale + * @returns {Number} */ - exports.setSelection = function() { - console.log("setSelection is deprecated. Please use selectNodes instead.") + Network.prototype.getScale = function () { + return this._getScale(); }; /** - * select zero or more nodes with the option to highlight edges - * @param {Number[] | String[]} selection An array with the ids of the - * selected nodes. - * @param {boolean} [highlightEdges] + * Returns the scale + * @returns {Number} */ - exports.selectNodes = function(selection, highlightEdges) { - var i, iMax, id; + Network.prototype.getCenterCoordinates = function () { + return this.DOMtoCanvas({x: 0.5 * this.frame.canvas.clientWidth, y: 0.5 * this.frame.canvas.clientHeight}); + }; - if (!selection || (selection.length == undefined)) - throw 'Selection must be an array with ids'; - // first unselect any selected node - this._unselectAll(true); + Network.prototype.getBoundingBox = function(nodeId) { + if (this.nodes[nodeId] !== undefined) { + return this.nodes[nodeId].boundingBox; + } + } - for (i = 0, iMax = selection.length; i < iMax; i++) { - id = selection[i]; + module.exports = Network; - var node = this.nodes[id]; - if (!node) { - throw new RangeError('Node with id "' + id + '" not found'); - } - this._selectObject(node,true,true,highlightEdges,true); - } - this.redraw(); - }; +/***/ }, +/* 52 */ +/***/ function(module, exports, __webpack_require__) { /** - * select zero or more edges - * @param {Number[] | String[]} selection An array with the ids of the - * selected nodes. + * Parse a text source containing data in DOT language into a JSON object. + * The object contains two lists: one with nodes and one with edges. + * + * DOT language reference: http://www.graphviz.org/doc/info/lang.html + * + * @param {String} data Text containing a graph in DOT-notation + * @return {Object} graph An object containing two parameters: + * {Object[]} nodes + * {Object[]} edges */ - exports.selectEdges = function(selection) { - var i, iMax, id; - - if (!selection || (selection.length == undefined)) - throw 'Selection must be an array with ids'; + function parseDOT (data) { + dot = data; + return parseGraph(); + } - // first unselect any selected node - this._unselectAll(true); + // token types enumeration + var TOKENTYPE = { + NULL : 0, + DELIMITER : 1, + IDENTIFIER: 2, + UNKNOWN : 3 + }; - for (i = 0, iMax = selection.length; i < iMax; i++) { - id = selection[i]; + // map with all delimiters + var DELIMITERS = { + '{': true, + '}': true, + '[': true, + ']': true, + ';': true, + '=': true, + ',': true, - var edge = this.edges[id]; - if (!edge) { - throw new RangeError('Edge with id "' + id + '" not found'); - } - this._selectObject(edge,true,true,false,true); - } - this.redraw(); + '->': true, + '--': true }; + var dot = ''; // current dot file + var index = 0; // current index in dot file + var c = ''; // current token character in expr + var token = ''; // current token + var tokenType = TOKENTYPE.NULL; // type of the token + /** - * Validate the selection: remove ids of nodes which no longer exist - * @private + * Get the first character from the dot file. + * The character is stored into the char c. If the end of the dot file is + * reached, the function puts an empty string in c. */ - exports._updateSelection = function () { - for(var nodeId in this.selectionObj.nodes) { - if(this.selectionObj.nodes.hasOwnProperty(nodeId)) { - if (!this.nodes.hasOwnProperty(nodeId)) { - delete this.selectionObj.nodes[nodeId]; - } - } - } - for(var edgeId in this.selectionObj.edges) { - if(this.selectionObj.edges.hasOwnProperty(edgeId)) { - if (!this.edges.hasOwnProperty(edgeId)) { - delete this.selectionObj.edges[edgeId]; - } - } - } - }; - - -/***/ }, -/* 60 */ -/***/ function(module, exports, __webpack_require__) { + function first() { + index = 0; + c = dot.charAt(0); + } - var util = __webpack_require__(1); - var Node = __webpack_require__(40); - var Edge = __webpack_require__(37); + /** + * Get the next character from the dot file. + * The character is stored into the char c. If the end of the dot file is + * reached, the function puts an empty string in c. + */ + function next() { + index++; + c = dot.charAt(index); + } /** - * clears the toolbar div element of children - * - * @private + * Preview the next character from the dot file. + * @return {String} cNext */ - exports._clearManipulatorBar = function() { - this._recursiveDOMDelete(this.manipulationDiv); - this.manipulationDOM = {}; + function nextPreview() { + return dot.charAt(index + 1); + } - this._manipulationReleaseOverload = function () {}; - delete this.sectors['support']['nodes']['targetNode']; - delete this.sectors['support']['nodes']['targetViaNode']; - this.controlNodesActive = false; - this.freezeSimulation = false; - }; + /** + * Test whether given character is alphabetic or numeric + * @param {String} c + * @return {Boolean} isAlphaNumeric + */ + var regexAlphaNumeric = /[a-zA-Z_0-9.:#]/; + function isAlphaNumeric(c) { + return regexAlphaNumeric.test(c); + } /** - * Manipulation UI temporarily overloads certain functions to extend or replace them. To be able to restore - * these functions to their original functionality, we saved them in this.cachedFunctions. - * This function restores these functions to their original function. - * - * @private + * Merge all properties of object b into object b + * @param {Object} a + * @param {Object} b + * @return {Object} a */ - exports._restoreOverloadedFunctions = function() { - for (var functionName in this.cachedFunctions) { - if (this.cachedFunctions.hasOwnProperty(functionName)) { - this[functionName] = this.cachedFunctions[functionName]; - delete this.cachedFunctions[functionName]; + function merge (a, b) { + if (!a) { + a = {}; + } + + if (b) { + for (var name in b) { + if (b.hasOwnProperty(name)) { + a[name] = b[name]; + } } } - }; + return a; + } /** - * Enable or disable edit-mode. + * Set a value in an object, where the provided parameter name can be a + * path with nested parameters. For example: * - * @private + * var obj = {a: 2}; + * setValue(obj, 'b.c', 3); // obj = {a: 2, b: {c: 3}} + * + * @param {Object} obj + * @param {String} path A parameter name or dot-separated parameter path, + * like "color.highlight.border". + * @param {*} value */ - exports._toggleEditMode = function() { - this.editMode = !this.editMode; - var toolbar = this.manipulationDiv; - var closeDiv = this.closeDiv; - var editModeDiv = this.editModeDiv; - if (this.editMode == true) { - toolbar.style.display="block"; - closeDiv.style.display="block"; - editModeDiv.style.display="none"; - closeDiv.onclick = this._toggleEditMode.bind(this); - } - else { - toolbar.style.display="none"; - closeDiv.style.display="none"; - editModeDiv.style.display="block"; - closeDiv.onclick = null; + function setValue(obj, path, value) { + var keys = path.split('.'); + var o = obj; + while (keys.length) { + var key = keys.shift(); + if (keys.length) { + // this isn't the end point + if (!o[key]) { + o[key] = {}; + } + o = o[key]; + } + else { + // this is the end point + o[key] = value; + } } - this._createManipulatorBar() - }; + } /** - * main function, creates the main toolbar. Removes functions bound to the select event. Binds all the buttons of the toolbar. - * - * @private + * Add a node to a graph object. If there is already a node with + * the same id, their attributes will be merged. + * @param {Object} graph + * @param {Object} node */ - exports._createManipulatorBar = function() { - // remove bound functions - if (this.boundFunction) { - this.off('select', this.boundFunction); - } + function addNode(graph, node) { + var i, len; + var current = null; - var locale = this.constants.locales[this.constants.locale]; + // find root graph (in case of subgraph) + var graphs = [graph]; // list with all graphs from current graph to root graph + var root = graph; + while (root.parent) { + graphs.push(root.parent); + root = root.parent; + } - if (this.edgeBeingEdited !== undefined) { - this.edgeBeingEdited._disableControlNodes(); - this.edgeBeingEdited = undefined; - this.selectedControlNode = null; - this.controlNodesActive = false; - this._redraw(); + // find existing node (at root level) by its id + if (root.nodes) { + for (i = 0, len = root.nodes.length; i < len; i++) { + if (node.id === root.nodes[i].id) { + current = root.nodes[i]; + break; + } + } } - // restore overloaded functions - this._restoreOverloadedFunctions(); - - // resume calculation - this.freezeSimulation = false; - - // reset global variables - this.blockConnectingEdgeSelection = false; - this.forceAppendSelection = false; - this.manipulationDOM = {}; - - if (this.editMode == true) { - while (this.manipulationDiv.hasChildNodes()) { - this.manipulationDiv.removeChild(this.manipulationDiv.firstChild); + if (!current) { + // this is a new node + current = { + id: node.id + }; + if (graph.node) { + // clone default attributes + current.attr = merge(current.attr, graph.node); } + } - this.manipulationDOM['addNodeSpan'] = document.createElement('span'); - this.manipulationDOM['addNodeSpan'].className = 'network-manipulationUI add'; - this.manipulationDOM['addNodeLabelSpan'] = document.createElement('span'); - this.manipulationDOM['addNodeLabelSpan'].className = 'network-manipulationLabel'; - this.manipulationDOM['addNodeLabelSpan'].innerHTML = locale['addNode']; - this.manipulationDOM['addNodeSpan'].appendChild(this.manipulationDOM['addNodeLabelSpan']); + // add node to this (sub)graph and all its parent graphs + for (i = graphs.length - 1; i >= 0; i--) { + var g = graphs[i]; - this.manipulationDOM['seperatorLineDiv1'] = document.createElement('div'); - this.manipulationDOM['seperatorLineDiv1'].className = 'network-seperatorLine'; + if (!g.nodes) { + g.nodes = []; + } + if (g.nodes.indexOf(current) == -1) { + g.nodes.push(current); + } + } - this.manipulationDOM['addEdgeSpan'] = document.createElement('span'); - this.manipulationDOM['addEdgeSpan'].className = 'network-manipulationUI connect'; - this.manipulationDOM['addEdgeLabelSpan'] = document.createElement('span'); - this.manipulationDOM['addEdgeLabelSpan'].className = 'network-manipulationLabel'; - this.manipulationDOM['addEdgeLabelSpan'].innerHTML = locale['addEdge']; - this.manipulationDOM['addEdgeSpan'].appendChild(this.manipulationDOM['addEdgeLabelSpan']); + // merge attributes + if (node.attr) { + current.attr = merge(current.attr, node.attr); + } + } - this.manipulationDiv.appendChild(this.manipulationDOM['addNodeSpan']); - this.manipulationDiv.appendChild(this.manipulationDOM['seperatorLineDiv1']); - this.manipulationDiv.appendChild(this.manipulationDOM['addEdgeSpan']); + /** + * Add an edge to a graph object + * @param {Object} graph + * @param {Object} edge + */ + function addEdge(graph, edge) { + if (!graph.edges) { + graph.edges = []; + } + graph.edges.push(edge); + if (graph.edge) { + var attr = merge({}, graph.edge); // clone default attributes + edge.attr = merge(attr, edge.attr); // merge attributes + } + } - if (this._getSelectedNodeCount() == 1 && this.triggerFunctions.edit) { - this.manipulationDOM['seperatorLineDiv2'] = document.createElement('div'); - this.manipulationDOM['seperatorLineDiv2'].className = 'network-seperatorLine'; + /** + * Create an edge to a graph object + * @param {Object} graph + * @param {String | Number | Object} from + * @param {String | Number | Object} to + * @param {String} type + * @param {Object | null} attr + * @return {Object} edge + */ + function createEdge(graph, from, to, type, attr) { + var edge = { + from: from, + to: to, + type: type + }; - this.manipulationDOM['editNodeSpan'] = document.createElement('span'); - this.manipulationDOM['editNodeSpan'].className = 'network-manipulationUI edit'; - this.manipulationDOM['editNodeLabelSpan'] = document.createElement('span'); - this.manipulationDOM['editNodeLabelSpan'].className = 'network-manipulationLabel'; - this.manipulationDOM['editNodeLabelSpan'].innerHTML = locale['editNode']; - this.manipulationDOM['editNodeSpan'].appendChild(this.manipulationDOM['editNodeLabelSpan']); + if (graph.edge) { + edge.attr = merge({}, graph.edge); // clone default attributes + } + edge.attr = merge(edge.attr || {}, attr); // merge attributes - this.manipulationDiv.appendChild(this.manipulationDOM['seperatorLineDiv2']); - this.manipulationDiv.appendChild(this.manipulationDOM['editNodeSpan']); - } - else if (this._getSelectedEdgeCount() == 1 && this._getSelectedNodeCount() == 0) { - this.manipulationDOM['seperatorLineDiv3'] = document.createElement('div'); - this.manipulationDOM['seperatorLineDiv3'].className = 'network-seperatorLine'; + return edge; + } - this.manipulationDOM['editEdgeSpan'] = document.createElement('span'); - this.manipulationDOM['editEdgeSpan'].className = 'network-manipulationUI edit'; - this.manipulationDOM['editEdgeLabelSpan'] = document.createElement('span'); - this.manipulationDOM['editEdgeLabelSpan'].className = 'network-manipulationLabel'; - this.manipulationDOM['editEdgeLabelSpan'].innerHTML = locale['editEdge']; - this.manipulationDOM['editEdgeSpan'].appendChild(this.manipulationDOM['editEdgeLabelSpan']); + /** + * Get next token in the current dot file. + * The token and token type are available as token and tokenType + */ + function getToken() { + tokenType = TOKENTYPE.NULL; + token = ''; - this.manipulationDiv.appendChild(this.manipulationDOM['seperatorLineDiv3']); - this.manipulationDiv.appendChild(this.manipulationDOM['editEdgeSpan']); - } - if (this._selectionIsEmpty() == false) { - this.manipulationDOM['seperatorLineDiv4'] = document.createElement('div'); - this.manipulationDOM['seperatorLineDiv4'].className = 'network-seperatorLine'; + // skip over whitespaces + while (c == ' ' || c == '\t' || c == '\n' || c == '\r') { // space, tab, enter + next(); + } - this.manipulationDOM['deleteSpan'] = document.createElement('span'); - this.manipulationDOM['deleteSpan'].className = 'network-manipulationUI delete'; - this.manipulationDOM['deleteLabelSpan'] = document.createElement('span'); - this.manipulationDOM['deleteLabelSpan'].className = 'network-manipulationLabel'; - this.manipulationDOM['deleteLabelSpan'].innerHTML = locale['del']; - this.manipulationDOM['deleteSpan'].appendChild(this.manipulationDOM['deleteLabelSpan']); + do { + var isComment = false; - this.manipulationDiv.appendChild(this.manipulationDOM['seperatorLineDiv4']); - this.manipulationDiv.appendChild(this.manipulationDOM['deleteSpan']); + // skip comment + if (c == '#') { + // find the previous non-space character + var i = index - 1; + while (dot.charAt(i) == ' ' || dot.charAt(i) == '\t') { + i--; + } + if (dot.charAt(i) == '\n' || dot.charAt(i) == '') { + // the # is at the start of a line, this is indeed a line comment + while (c != '' && c != '\n') { + next(); + } + isComment = true; + } } - - - // bind the icons - this.manipulationDOM['addNodeSpan'].onclick = this._createAddNodeToolbar.bind(this); - this.manipulationDOM['addEdgeSpan'].onclick = this._createAddEdgeToolbar.bind(this); - if (this._getSelectedNodeCount() == 1 && this.triggerFunctions.edit) { - this.manipulationDOM['editNodeSpan'].onclick = this._editNode.bind(this); + if (c == '/' && nextPreview() == '/') { + // skip line comment + while (c != '' && c != '\n') { + next(); + } + isComment = true; } - else if (this._getSelectedEdgeCount() == 1 && this._getSelectedNodeCount() == 0) { - this.manipulationDOM['editEdgeSpan'].onclick = this._createEditEdgeToolbar.bind(this); + if (c == '/' && nextPreview() == '*') { + // skip block comment + while (c != '') { + if (c == '*' && nextPreview() == '/') { + // end of block comment found. skip these last two characters + next(); + next(); + break; + } + else { + next(); + } + } + isComment = true; } - if (this._selectionIsEmpty() == false) { - this.manipulationDOM['deleteSpan'].onclick = this._deleteSelected.bind(this); + + // skip over whitespaces + while (c == ' ' || c == '\t' || c == '\n' || c == '\r') { // space, tab, enter + next(); } - this.closeDiv.onclick = this._toggleEditMode.bind(this); + } + while (isComment); - var me = this; - this.boundFunction = me._createManipulatorBar; - this.on('select', this.boundFunction); + // check for end of dot file + if (c == '') { + // token is still empty + tokenType = TOKENTYPE.DELIMITER; + return; } - else { - while (this.editModeDiv.hasChildNodes()) { - this.editModeDiv.removeChild(this.editModeDiv.firstChild); - } - this.manipulationDOM['editModeSpan'] = document.createElement('span'); - this.manipulationDOM['editModeSpan'].className = 'network-manipulationUI edit editmode'; - this.manipulationDOM['editModeLabelSpan'] = document.createElement('span'); - this.manipulationDOM['editModeLabelSpan'].className = 'network-manipulationLabel'; - this.manipulationDOM['editModeLabelSpan'].innerHTML = locale['edit']; - this.manipulationDOM['editModeSpan'].appendChild(this.manipulationDOM['editModeLabelSpan']); + // check for delimiters consisting of 2 characters + var c2 = c + nextPreview(); + if (DELIMITERS[c2]) { + tokenType = TOKENTYPE.DELIMITER; + token = c2; + next(); + next(); + return; + } - this.editModeDiv.appendChild(this.manipulationDOM['editModeSpan']); + // check for delimiters consisting of 1 character + if (DELIMITERS[c]) { + tokenType = TOKENTYPE.DELIMITER; + token = c; + next(); + return; + } - this.manipulationDOM['editModeSpan'].onclick = this._toggleEditMode.bind(this); + // check for an identifier (number or string) + // TODO: more precise parsing of numbers/strings (and the port separator ':') + if (isAlphaNumeric(c) || c == '-') { + token += c; + next(); + + while (isAlphaNumeric(c)) { + token += c; + next(); + } + if (token == 'false') { + token = false; // convert to boolean + } + else if (token == 'true') { + token = true; // convert to boolean + } + else if (!isNaN(Number(token))) { + token = Number(token); // convert to number + } + tokenType = TOKENTYPE.IDENTIFIER; + return; } - }; + // check for a string enclosed by double quotes + if (c == '"') { + next(); + while (c != '' && (c != '"' || (c == '"' && nextPreview() == '"'))) { + token += c; + if (c == '"') { // skip the escape character + next(); + } + next(); + } + if (c != '"') { + throw newSyntaxError('End of string " expected'); + } + next(); + tokenType = TOKENTYPE.IDENTIFIER; + return; + } + // something unknown is found, wrong characters, a syntax error + tokenType = TOKENTYPE.UNKNOWN; + while (c != '') { + token += c; + next(); + } + throw new SyntaxError('Syntax error in part "' + chop(token, 30) + '"'); + } /** - * Create the toolbar for adding Nodes - * - * @private + * Parse a graph. + * @returns {Object} graph */ - exports._createAddNodeToolbar = function() { - // clear the toolbar - this._clearManipulatorBar(); - if (this.boundFunction) { - this.off('select', this.boundFunction); + function parseGraph() { + var graph = {}; + + first(); + getToken(); + + // optional strict keyword + if (token == 'strict') { + graph.strict = true; + getToken(); } - var locale = this.constants.locales[this.constants.locale]; + // graph or digraph keyword + if (token == 'graph' || token == 'digraph') { + graph.type = token; + getToken(); + } - this.manipulationDOM = {}; - this.manipulationDOM['backSpan'] = document.createElement('span'); - this.manipulationDOM['backSpan'].className = 'network-manipulationUI back'; - this.manipulationDOM['backLabelSpan'] = document.createElement('span'); - this.manipulationDOM['backLabelSpan'].className = 'network-manipulationLabel'; - this.manipulationDOM['backLabelSpan'].innerHTML = locale['back']; - this.manipulationDOM['backSpan'].appendChild(this.manipulationDOM['backLabelSpan']); + // optional graph id + if (tokenType == TOKENTYPE.IDENTIFIER) { + graph.id = token; + getToken(); + } - this.manipulationDOM['seperatorLineDiv1'] = document.createElement('div'); - this.manipulationDOM['seperatorLineDiv1'].className = 'network-seperatorLine'; + // open angle bracket + if (token != '{') { + throw newSyntaxError('Angle bracket { expected'); + } + getToken(); - this.manipulationDOM['descriptionSpan'] = document.createElement('span'); - this.manipulationDOM['descriptionSpan'].className = 'network-manipulationUI none'; - this.manipulationDOM['descriptionLabelSpan'] = document.createElement('span'); - this.manipulationDOM['descriptionLabelSpan'].className = 'network-manipulationLabel'; - this.manipulationDOM['descriptionLabelSpan'].innerHTML = locale['addDescription']; - this.manipulationDOM['descriptionSpan'].appendChild(this.manipulationDOM['descriptionLabelSpan']); + // statements + parseStatements(graph); - this.manipulationDiv.appendChild(this.manipulationDOM['backSpan']); - this.manipulationDiv.appendChild(this.manipulationDOM['seperatorLineDiv1']); - this.manipulationDiv.appendChild(this.manipulationDOM['descriptionSpan']); + // close angle bracket + if (token != '}') { + throw newSyntaxError('Angle bracket } expected'); + } + getToken(); - // bind the icon - this.manipulationDOM['backSpan'].onclick = this._createManipulatorBar.bind(this); + // end of file + if (token !== '') { + throw newSyntaxError('End of file expected'); + } + getToken(); - // we use the boundFunction so we can reference it when we unbind it from the "select" event. - var me = this; - this.boundFunction = me._addNode; - this.on('select', this.boundFunction); - }; + // remove temporary default properties + delete graph.node; + delete graph.edge; + delete graph.graph; + return graph; + } /** - * create the toolbar to connect nodes - * - * @private + * Parse a list with statements. + * @param {Object} graph */ - exports._createAddEdgeToolbar = function() { - // clear the toolbar - this._clearManipulatorBar(); - this._unselectAll(true); - this.freezeSimulation = true; - - if (this.boundFunction) { - this.off('select', this.boundFunction); + function parseStatements (graph) { + while (token !== '' && token != '}') { + parseStatement(graph); + if (token == ';') { + getToken(); + } } + } - var locale = this.constants.locales[this.constants.locale]; - - this._unselectAll(); - this.forceAppendSelection = false; - this.blockConnectingEdgeSelection = true; - - this.manipulationDOM = {}; - this.manipulationDOM['backSpan'] = document.createElement('span'); - this.manipulationDOM['backSpan'].className = 'network-manipulationUI back'; - this.manipulationDOM['backLabelSpan'] = document.createElement('span'); - this.manipulationDOM['backLabelSpan'].className = 'network-manipulationLabel'; - this.manipulationDOM['backLabelSpan'].innerHTML = locale['back']; - this.manipulationDOM['backSpan'].appendChild(this.manipulationDOM['backLabelSpan']); - - this.manipulationDOM['seperatorLineDiv1'] = document.createElement('div'); - this.manipulationDOM['seperatorLineDiv1'].className = 'network-seperatorLine'; - - this.manipulationDOM['descriptionSpan'] = document.createElement('span'); - this.manipulationDOM['descriptionSpan'].className = 'network-manipulationUI none'; - this.manipulationDOM['descriptionLabelSpan'] = document.createElement('span'); - this.manipulationDOM['descriptionLabelSpan'].className = 'network-manipulationLabel'; - this.manipulationDOM['descriptionLabelSpan'].innerHTML = locale['edgeDescription']; - this.manipulationDOM['descriptionSpan'].appendChild(this.manipulationDOM['descriptionLabelSpan']); - - this.manipulationDiv.appendChild(this.manipulationDOM['backSpan']); - this.manipulationDiv.appendChild(this.manipulationDOM['seperatorLineDiv1']); - this.manipulationDiv.appendChild(this.manipulationDOM['descriptionSpan']); + /** + * Parse a single statement. Can be a an attribute statement, node + * statement, a series of node statements and edge statements, or a + * parameter. + * @param {Object} graph + */ + function parseStatement(graph) { + // parse subgraph + var subgraph = parseSubgraph(graph); + if (subgraph) { + // edge statements + parseEdge(graph, subgraph); - // bind the icon - this.manipulationDOM['backSpan'].onclick = this._createManipulatorBar.bind(this); + return; + } - // we use the boundFunction so we can reference it when we unbind it from the "select" event. - var me = this; - this.boundFunction = me._handleConnect; - this.on('select', this.boundFunction); + // parse an attribute statement + var attr = parseAttributeStatement(graph); + if (attr) { + return; + } - // temporarily overload functions - this.cachedFunctions["_handleTouch"] = this._handleTouch; - this.cachedFunctions["_manipulationReleaseOverload"] = this._manipulationReleaseOverload; - this.cachedFunctions["_handleDragStart"] = this._handleDragStart; - this.cachedFunctions["_handleDragEnd"] = this._handleDragEnd; - this.cachedFunctions["_handleOnHold"] = this._handleOnHold; - this._handleTouch = this._handleConnect; - this._manipulationReleaseOverload = function () {}; - this._handleOnHold = function () {}; - this._handleDragStart = function () {}; - this._handleDragEnd = this._finishConnect; + // parse node + if (tokenType != TOKENTYPE.IDENTIFIER) { + throw newSyntaxError('Identifier expected'); + } + var id = token; // id can be a string or a number + getToken(); - // redraw to show the unselect - this._redraw(); - }; + if (token == '=') { + // id statement + getToken(); + if (tokenType != TOKENTYPE.IDENTIFIER) { + throw newSyntaxError('Identifier expected'); + } + graph[id] = token; + getToken(); + // TODO: implement comma separated list with "a_list: ID=ID [','] [a_list] " + } + else { + parseNodeStatement(graph, id); + } + } /** - * create the toolbar to edit edges - * - * @private + * Parse a subgraph + * @param {Object} graph parent graph object + * @return {Object | null} subgraph */ - exports._createEditEdgeToolbar = function() { - // clear the toolbar - this._clearManipulatorBar(); - this.controlNodesActive = true; + function parseSubgraph (graph) { + var subgraph = null; - if (this.boundFunction) { - this.off('select', this.boundFunction); + // optional subgraph keyword + if (token == 'subgraph') { + subgraph = {}; + subgraph.type = 'subgraph'; + getToken(); + + // optional graph id + if (tokenType == TOKENTYPE.IDENTIFIER) { + subgraph.id = token; + getToken(); + } } - this.edgeBeingEdited = this._getSelectedEdge(); - this.edgeBeingEdited._enableControlNodes(); + // open angle bracket + if (token == '{') { + getToken(); - var locale = this.constants.locales[this.constants.locale]; + if (!subgraph) { + subgraph = {}; + } + subgraph.parent = graph; + subgraph.node = graph.node; + subgraph.edge = graph.edge; + subgraph.graph = graph.graph; - this.manipulationDOM = {}; - this.manipulationDOM['backSpan'] = document.createElement('span'); - this.manipulationDOM['backSpan'].className = 'network-manipulationUI back'; - this.manipulationDOM['backLabelSpan'] = document.createElement('span'); - this.manipulationDOM['backLabelSpan'].className = 'network-manipulationLabel'; - this.manipulationDOM['backLabelSpan'].innerHTML = locale['back']; - this.manipulationDOM['backSpan'].appendChild(this.manipulationDOM['backLabelSpan']); + // statements + parseStatements(subgraph); - this.manipulationDOM['seperatorLineDiv1'] = document.createElement('div'); - this.manipulationDOM['seperatorLineDiv1'].className = 'network-seperatorLine'; + // close angle bracket + if (token != '}') { + throw newSyntaxError('Angle bracket } expected'); + } + getToken(); - this.manipulationDOM['descriptionSpan'] = document.createElement('span'); - this.manipulationDOM['descriptionSpan'].className = 'network-manipulationUI none'; - this.manipulationDOM['descriptionLabelSpan'] = document.createElement('span'); - this.manipulationDOM['descriptionLabelSpan'].className = 'network-manipulationLabel'; - this.manipulationDOM['descriptionLabelSpan'].innerHTML = locale['editEdgeDescription']; - this.manipulationDOM['descriptionSpan'].appendChild(this.manipulationDOM['descriptionLabelSpan']); + // remove temporary default properties + delete subgraph.node; + delete subgraph.edge; + delete subgraph.graph; + delete subgraph.parent; - this.manipulationDiv.appendChild(this.manipulationDOM['backSpan']); - this.manipulationDiv.appendChild(this.manipulationDOM['seperatorLineDiv1']); - this.manipulationDiv.appendChild(this.manipulationDOM['descriptionSpan']); + // register at the parent graph + if (!graph.subgraphs) { + graph.subgraphs = []; + } + graph.subgraphs.push(subgraph); + } - // bind the icon - this.manipulationDOM['backSpan'].onclick = this._createManipulatorBar.bind(this); + return subgraph; + } - // temporarily overload functions - this.cachedFunctions["_handleTouch"] = this._handleTouch; - this.cachedFunctions["_manipulationReleaseOverload"] = this._manipulationReleaseOverload; - this.cachedFunctions["_handleTap"] = this._handleTap; - this.cachedFunctions["_handleDragStart"] = this._handleDragStart; - this.cachedFunctions["_handleOnDrag"] = this._handleOnDrag; - this._handleTouch = this._selectControlNode; - this._handleTap = function () {}; - this._handleOnDrag = this._controlNodeDrag; - this._handleDragStart = function () {} - this._manipulationReleaseOverload = this._releaseControlNode; + /** + * parse an attribute statement like "node [shape=circle fontSize=16]". + * Available keywords are 'node', 'edge', 'graph'. + * The previous list with default attributes will be replaced + * @param {Object} graph + * @returns {String | null} keyword Returns the name of the parsed attribute + * (node, edge, graph), or null if nothing + * is parsed. + */ + function parseAttributeStatement (graph) { + // attribute statements + if (token == 'node') { + getToken(); - // redraw to show the unselect - this._redraw(); - }; + // node attributes + graph.node = parseAttributeList(); + return 'node'; + } + else if (token == 'edge') { + getToken(); + // edge attributes + graph.edge = parseAttributeList(); + return 'edge'; + } + else if (token == 'graph') { + getToken(); - /** - * the function bound to the selection event. It checks if you want to connect a cluster and changes the description - * to walk the user through the process. - * - * @private - */ - exports._selectControlNode = function(pointer) { - this.edgeBeingEdited.controlNodes.from.unselect(); - this.edgeBeingEdited.controlNodes.to.unselect(); - this.selectedControlNode = this.edgeBeingEdited._getSelectedControlNode(this._XconvertDOMtoCanvas(pointer.x),this._YconvertDOMtoCanvas(pointer.y)); - if (this.selectedControlNode !== null) { - this.selectedControlNode.select(); - this.freezeSimulation = true; + // graph attributes + graph.graph = parseAttributeList(); + return 'graph'; } - this._redraw(); - }; + return null; + } /** - * the function bound to the selection event. It checks if you want to connect a cluster and changes the description - * to walk the user through the process. - * - * @private + * parse a node statement + * @param {Object} graph + * @param {String | Number} id */ - exports._controlNodeDrag = function(event) { - var pointer = this._getPointer(event.gesture.center); - if (this.selectedControlNode !== null && this.selectedControlNode !== undefined) { - this.selectedControlNode.x = this._XconvertDOMtoCanvas(pointer.x); - this.selectedControlNode.y = this._YconvertDOMtoCanvas(pointer.y); + function parseNodeStatement(graph, id) { + // node statement + var node = { + id: id + }; + var attr = parseAttributeList(); + if (attr) { + node.attr = attr; } - this._redraw(); - }; + addNode(graph, node); + // edge statements + parseEdge(graph, id); + } /** - * - * @param pointer - * @private + * Parse an edge or a series of edges + * @param {Object} graph + * @param {String | Number} from Id of the from node */ - exports._releaseControlNode = function(pointer) { - var newNode = this._getNodeAt(pointer); - if (newNode !== null) { - if (this.edgeBeingEdited.controlNodes.from.selected == true) { - this.edgeBeingEdited._restoreControlNodes(); - this._editEdge(newNode.id, this.edgeBeingEdited.to.id); - this.edgeBeingEdited.controlNodes.from.unselect(); + function parseEdge(graph, from) { + while (token == '->' || token == '--') { + var to; + var type = token; + getToken(); + + var subgraph = parseSubgraph(graph); + if (subgraph) { + to = subgraph; } - if (this.edgeBeingEdited.controlNodes.to.selected == true) { - this.edgeBeingEdited._restoreControlNodes(); - this._editEdge(this.edgeBeingEdited.from.id, newNode.id); - this.edgeBeingEdited.controlNodes.to.unselect(); + else { + if (tokenType != TOKENTYPE.IDENTIFIER) { + throw newSyntaxError('Identifier or subgraph expected'); + } + to = token; + addNode(graph, { + id: to + }); + getToken(); } + + // parse edge attributes + var attr = parseAttributeList(); + + // create edge + var edge = createEdge(graph, from, to, type, attr); + addEdge(graph, edge); + + from = to; } - else { - this.edgeBeingEdited._restoreControlNodes(); - } - this.freezeSimulation = false; - this._redraw(); - }; + } /** - * the function bound to the selection event. It checks if you want to connect a cluster and changes the description - * to walk the user through the process. - * - * @private + * Parse a set with attributes, + * for example [label="1.000", shape=solid] + * @return {Object | null} attr */ - exports._handleConnect = function(pointer) { - if (this._getSelectedNodeCount() == 0) { - var node = this._getNodeAt(pointer); + function parseAttributeList() { + var attr = null; - if (node != null) { - if (node.clusterSize > 1) { - alert(this.constants.locales[this.constants.locale]['createEdgeError']) + while (token == '[') { + getToken(); + attr = {}; + while (token !== '' && token != ']') { + if (tokenType != TOKENTYPE.IDENTIFIER) { + throw newSyntaxError('Attribute name expected'); } - else { - this._selectObject(node,false); - var supportNodes = this.sectors['support']['nodes']; - - // create a node the temporary line can look at - supportNodes['targetNode'] = new Node({id:'targetNode'},{},{},this.constants); - var targetNode = supportNodes['targetNode']; - targetNode.x = node.x; - targetNode.y = node.y; + var name = token; - // create a temporary edge - this.edges['connectionEdge'] = new Edge({id:"connectionEdge",from:node.id,to:targetNode.id}, this, this.constants); - var connectionEdge = this.edges['connectionEdge']; - connectionEdge.from = node; - connectionEdge.connected = true; - connectionEdge.options.smoothCurves = {enabled: true, - dynamic: false, - type: "continuous", - roundness: 0.5 - }; - connectionEdge.selected = true; - connectionEdge.to = targetNode; + getToken(); + if (token != '=') { + throw newSyntaxError('Equal sign = expected'); + } + getToken(); - this.cachedFunctions["_handleOnDrag"] = this._handleOnDrag; - this._handleOnDrag = function(event) { - var pointer = this._getPointer(event.gesture.center); - var connectionEdge = this.edges['connectionEdge']; - connectionEdge.to.x = this._XconvertDOMtoCanvas(pointer.x); - connectionEdge.to.y = this._YconvertDOMtoCanvas(pointer.y); - }; + if (tokenType != TOKENTYPE.IDENTIFIER) { + throw newSyntaxError('Attribute value expected'); + } + var value = token; + setValue(attr, name, value); // name can be a path - this.moving = true; - this.start(); + getToken(); + if (token ==',') { + getToken(); } } - } - }; - - exports._finishConnect = function(event) { - if (this._getSelectedNodeCount() == 1) { - var pointer = this._getPointer(event.gesture.center); - // restore the drag function - this._handleOnDrag = this.cachedFunctions["_handleOnDrag"]; - delete this.cachedFunctions["_handleOnDrag"]; - - // remember the edge id - var connectFromId = this.edges['connectionEdge'].fromId; - - // remove the temporary nodes and edge - delete this.edges['connectionEdge']; - delete this.sectors['support']['nodes']['targetNode']; - delete this.sectors['support']['nodes']['targetViaNode']; - var node = this._getNodeAt(pointer); - if (node != null) { - if (node.clusterSize > 1) { - alert(this.constants.locales[this.constants.locale]["createEdgeError"]) - } - else { - this._createEdge(connectFromId,node.id); - this._createManipulatorBar(); - } + if (token != ']') { + throw newSyntaxError('Bracket ] expected'); } - this._unselectAll(); + getToken(); } - }; + return attr; + } /** - * Adds a node on the specified location + * Create a syntax error with extra information on current token and index. + * @param {String} message + * @returns {SyntaxError} err */ - exports._addNode = function() { - if (this._selectionIsEmpty() && this.editMode == true) { - var positionObject = this._pointerToPositionObject(this.pointerPosition); - var defaultData = {id:util.randomUUID(),x:positionObject.left,y:positionObject.top,label:"new",allowedToMoveX:true,allowedToMoveY:true}; - if (this.triggerFunctions.add) { - if (this.triggerFunctions.add.length == 2) { - var me = this; - this.triggerFunctions.add(defaultData, function(finalizedData) { - me.nodesData.add(finalizedData); - me._createManipulatorBar(); - me.moving = true; - me.start(); - }); - } - else { - throw new Error('The function for add does not support two arguments (data,callback)'); - this._createManipulatorBar(); - this.moving = true; - this.start(); - } - } - else { - this.nodesData.add(defaultData); - this._createManipulatorBar(); - this.moving = true; - this.start(); - } - } - }; - + function newSyntaxError(message) { + return new SyntaxError(message + ', got "' + chop(token, 30) + '" (char ' + index + ')'); + } /** - * connect two nodes with a new edge. - * - * @private + * Chop off text after a maximum length + * @param {String} text + * @param {Number} maxLength + * @returns {String} */ - exports._createEdge = function(sourceNodeId,targetNodeId) { - if (this.editMode == true) { - var defaultData = {from:sourceNodeId, to:targetNodeId}; - if (this.triggerFunctions.connect) { - if (this.triggerFunctions.connect.length == 2) { - var me = this; - this.triggerFunctions.connect(defaultData, function(finalizedData) { - me.edgesData.add(finalizedData); - me.moving = true; - me.start(); - }); - } - else { - throw new Error('The function for connect does not support two arguments (data,callback)'); - this.moving = true; - this.start(); - } - } - else { - this.edgesData.add(defaultData); - this.moving = true; - this.start(); - } - } - }; + function chop (text, maxLength) { + return (text.length <= maxLength) ? text : (text.substr(0, 27) + '...'); + } /** - * connect two nodes with a new edge. - * - * @private + * Execute a function fn for each pair of elements in two arrays + * @param {Array | *} array1 + * @param {Array | *} array2 + * @param {function} fn */ - exports._editEdge = function(sourceNodeId,targetNodeId) { - if (this.editMode == true) { - var defaultData = {id: this.edgeBeingEdited.id, from:sourceNodeId, to:targetNodeId}; - if (this.triggerFunctions.editEdge) { - if (this.triggerFunctions.editEdge.length == 2) { - var me = this; - this.triggerFunctions.editEdge(defaultData, function(finalizedData) { - me.edgesData.update(finalizedData); - me.moving = true; - me.start(); + function forEach2(array1, array2, fn) { + if (Array.isArray(array1)) { + array1.forEach(function (elem1) { + if (Array.isArray(array2)) { + array2.forEach(function (elem2) { + fn(elem1, elem2); }); } else { - throw new Error('The function for edit does not support two arguments (data, callback)'); - this.moving = true; - this.start(); + fn(elem1, array2); } - } - else { - this.edgesData.update(defaultData); - this.moving = true; - this.start(); - } + }); } - }; - - /** - * Create the toolbar to edit the selected node. The label and the color can be changed. Other colors are derived from the chosen color. - * - * @private - */ - exports._editNode = function() { - if (this.triggerFunctions.edit && this.editMode == true) { - var node = this._getSelectedNode(); - var data = {id:node.id, - label: node.label, - group: node.options.group, - shape: node.options.shape, - color: { - background:node.options.color.background, - border:node.options.color.border, - highlight: { - background:node.options.color.highlight.background, - border:node.options.color.highlight.border - } - }}; - if (this.triggerFunctions.edit.length == 2) { - var me = this; - this.triggerFunctions.edit(data, function (finalizedData) { - me.nodesData.update(finalizedData); - me._createManipulatorBar(); - me.moving = true; - me.start(); + else { + if (Array.isArray(array2)) { + array2.forEach(function (elem2) { + fn(array1, elem2); }); } else { - throw new Error('The function for edit does not support two arguments (data, callback)'); + fn(array1, array2); } } - else { - throw new Error('No edit function has been bound to this button'); - } - }; + } + /** + * Convert a string containing a graph in DOT language into a map containing + * with nodes and edges in the format of graph. + * @param {String} data Text containing a graph in DOT-notation + * @return {Object} graphData + */ + function DOTToGraph (data) { + // parse the DOT file + var dotData = parseDOT(data); + var graphData = { + nodes: [], + edges: [], + options: {} + }; + // copy the nodes + if (dotData.nodes) { + dotData.nodes.forEach(function (dotNode) { + var graphNode = { + id: dotNode.id, + label: String(dotNode.label || dotNode.id) + }; + merge(graphNode, dotNode.attr); + if (graphNode.image) { + graphNode.shape = 'image'; + } + graphData.nodes.push(graphNode); + }); + } + // copy the edges + if (dotData.edges) { + /** + * Convert an edge in DOT format to an edge with VisGraph format + * @param {Object} dotEdge + * @returns {Object} graphEdge + */ + var convertEdge = function (dotEdge) { + var graphEdge = { + from: dotEdge.from, + to: dotEdge.to + }; + merge(graphEdge, dotEdge.attr); + graphEdge.style = (dotEdge.type == '->') ? 'arrow' : 'line'; + return graphEdge; + } - /** - * delete everything in the selection - * - * @private - */ - exports._deleteSelected = function() { - if (!this._selectionIsEmpty() && this.editMode == true) { - if (!this._clusterInSelection()) { - var selectedNodes = this.getSelectedNodes(); - var selectedEdges = this.getSelectedEdges(); - if (this.triggerFunctions.del) { - var me = this; - var data = {nodes: selectedNodes, edges: selectedEdges}; - if (this.triggerFunctions.del.length == 2) { - this.triggerFunctions.del(data, function (finalizedData) { - me.edgesData.remove(finalizedData.edges); - me.nodesData.remove(finalizedData.nodes); - me._unselectAll(); - me.moving = true; - me.start(); - }); - } - else { - throw new Error('The function for delete does not support two arguments (data, callback)') + dotData.edges.forEach(function (dotEdge) { + var from, to; + if (dotEdge.from instanceof Object) { + from = dotEdge.from.nodes; + } + else { + from = { + id: dotEdge.from } } + + if (dotEdge.to instanceof Object) { + to = dotEdge.to.nodes; + } else { - this.edgesData.remove(selectedEdges); - this.nodesData.remove(selectedNodes); - this._unselectAll(); - this.moving = true; - this.start(); + to = { + id: dotEdge.to + } } - } - else { - alert(this.constants.locales[this.constants.locale]["deleteClusterError"]); - } - } - }; + if (dotEdge.from instanceof Object && dotEdge.from.edges) { + dotEdge.from.edges.forEach(function (subEdge) { + var graphEdge = convertEdge(subEdge); + graphData.edges.push(graphEdge); + }); + } -/***/ }, -/* 61 */ -/***/ function(module, exports, __webpack_require__) { + forEach2(from, to, function (from, to) { + var subEdge = createEdge(graphData, from.id, to.id, dotEdge.type, dotEdge.attr); + var graphEdge = convertEdge(subEdge); + graphData.edges.push(graphEdge); + }); - var util = __webpack_require__(1); - var Hammer = __webpack_require__(45); - - exports._cleanNavigation = function() { - // clean hammer bindings - if (this.navigationHammers.existing.length != 0) { - for (var i = 0; i < this.navigationHammers.existing.length; i++) { - this.navigationHammers.existing[i].dispose(); - } - this.navigationHammers.existing = []; + if (dotEdge.to instanceof Object && dotEdge.to.edges) { + dotEdge.to.edges.forEach(function (subEdge) { + var graphEdge = convertEdge(subEdge); + graphData.edges.push(graphEdge); + }); + } + }); } - this._navigationReleaseOverload = function () {}; - - // clean up previous navigation items - if (this.navigationDivs && this.navigationDivs['wrapper'] && this.navigationDivs['wrapper'].parentNode) { - this.navigationDivs['wrapper'].parentNode.removeChild(this.navigationDivs['wrapper']); + // copy the options + if (dotData.attr) { + graphData.options = dotData.attr; } - }; - /** - * 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 - */ - exports._loadNavigationElements = function() { - this._cleanNavigation(); + return graphData; + } - this.navigationDivs = {}; - var navigationDivs = ['up','down','left','right','zoomIn','zoomOut','zoomExtends']; - var navigationDivActions = ['_moveUp','_moveDown','_moveLeft','_moveRight','_zoomIn','_zoomOut','_zoomExtent']; + // exports + exports.parseDOT = parseDOT; + exports.DOTToGraph = DOTToGraph; - this.navigationDivs['wrapper'] = document.createElement('div'); - this.frame.appendChild(this.navigationDivs['wrapper']); - for (var i = 0; i < navigationDivs.length; i++) { - this.navigationDivs[navigationDivs[i]] = document.createElement('div'); - this.navigationDivs[navigationDivs[i]].className = 'network-navigation ' + navigationDivs[i]; - this.navigationDivs['wrapper'].appendChild(this.navigationDivs[navigationDivs[i]]); +/***/ }, +/* 53 */ +/***/ function(module, exports, __webpack_require__) { - var hammer = Hammer(this.navigationDivs[navigationDivs[i]], {prevent_default: true}); - hammer.on('touch', this[navigationDivActions[i]].bind(this)); - this.navigationHammers._new.push(hammer); + + function parseGephi(gephiJSON, options) { + var edges = []; + var nodes = []; + this.options = { + edges: { + inheritColor: true + }, + nodes: { + allowedToMove: false, + parseColor: false + } + }; + + if (options !== undefined) { + this.options.nodes['allowedToMove'] = options.allowedToMove | false; + this.options.nodes['parseColor'] = options.parseColor | false; + this.options.edges['inheritColor'] = options.inheritColor | true; } - this._navigationReleaseOverload = this._stopMovement; + var gEdges = gephiJSON.edges; + var gNodes = gephiJSON.nodes; + for (var i = 0; i < gEdges.length; i++) { + var edge = {}; + var gEdge = gEdges[i]; + edge['id'] = gEdge.id; + edge['from'] = gEdge.source; + edge['to'] = gEdge.target; + edge['attributes'] = gEdge.attributes; + // edge['value'] = gEdge.attributes !== undefined ? gEdge.attributes.Weight : undefined; + // edge['width'] = edge['value'] !== undefined ? undefined : edgegEdge.size; + edge['color'] = gEdge.color; + edge['inheritColor'] = edge['color'] !== undefined ? false : this.options.inheritColor; + edges.push(edge); + } - this.navigationHammers.existing = this.navigationHammers._new; - }; + for (var i = 0; i < gNodes.length; i++) { + var node = {}; + var gNode = gNodes[i]; + node['id'] = gNode.id; + node['attributes'] = gNode.attributes; + node['x'] = gNode.x; + node['y'] = gNode.y; + node['label'] = gNode.label; + if (this.options.nodes.parseColor == true) { + node['color'] = gNode.color; + } + else { + node['color'] = gNode.color !== undefined ? {background:gNode.color, border:gNode.color} : undefined; + } + node['radius'] = gNode.size; + node['allowedToMoveX'] = this.options.nodes.allowedToMove; + node['allowedToMoveY'] = this.options.nodes.allowedToMove; + nodes.push(node); + } + return {nodes:nodes, edges:edges}; + } - /** - * this stops all movement induced by the navigation buttons - * - * @private - */ - exports._zoomExtent = function(event) { - this.zoomExtent({duration:700}); - event.stopPropagation(); - }; + exports.parseGephi = parseGephi; - /** - * this stops all movement induced by the navigation buttons - * - * @private - */ - exports._stopMovement = function() { - this._xStopMoving(); - this._yStopMoving(); - this._stopZoom(); - }; +/***/ }, +/* 54 */ +/***/ function(module, exports, __webpack_require__) { + var util = __webpack_require__(1); /** - * move the screen up - * By using the increments, instead of adding a fixed number to the translation, we keep fluent and - * instant movement. The onKeypress event triggers immediately, then pauses, then triggers frequently - * To avoid this behaviour, we do the translation in the start loop. - * - * @private + * @class Groups + * This class can store groups and properties specific for groups. */ - exports._moveUp = function(event) { - this.yIncrement = this.constants.keyboard.speed.y; - this.start(); // if there is no node movement, the calculation wont be done - event.preventDefault(); - }; + function Groups() { + this.clear(); + this.defaultIndex = 0; + } /** - * move the screen down - * @private + * default constants for group colors */ - exports._moveDown = function(event) { - this.yIncrement = -this.constants.keyboard.speed.y; - this.start(); // if there is no node movement, the calculation wont be done - event.preventDefault(); - }; + Groups.DEFAULT = [ + {border: "#2B7CE9", background: "#97C2FC", highlight: {border: "#2B7CE9", background: "#D2E5FF"}, hover: {border: "#2B7CE9", background: "#D2E5FF"}}, // blue + {border: "#FFA500", background: "#FFFF00", highlight: {border: "#FFA500", background: "#FFFFA3"}, hover: {border: "#FFA500", background: "#FFFFA3"}}, // yellow + {border: "#FA0A10", background: "#FB7E81", highlight: {border: "#FA0A10", background: "#FFAFB1"}, hover: {border: "#FA0A10", background: "#FFAFB1"}}, // red + {border: "#41A906", background: "#7BE141", highlight: {border: "#41A906", background: "#A1EC76"}, hover: {border: "#41A906", background: "#A1EC76"}}, // green + {border: "#E129F0", background: "#EB7DF4", highlight: {border: "#E129F0", background: "#F0B3F5"}, hover: {border: "#E129F0", background: "#F0B3F5"}}, // magenta + {border: "#7C29F0", background: "#AD85E4", highlight: {border: "#7C29F0", background: "#D3BDF0"}, hover: {border: "#7C29F0", background: "#D3BDF0"}}, // purple + {border: "#C37F00", background: "#FFA807", highlight: {border: "#C37F00", background: "#FFCA66"}, hover: {border: "#C37F00", background: "#FFCA66"}}, // orange + {border: "#4220FB", background: "#6E6EFD", highlight: {border: "#4220FB", background: "#9B9BFD"}, hover: {border: "#4220FB", background: "#9B9BFD"}}, // darkblue + {border: "#FD5A77", background: "#FFC0CB", highlight: {border: "#FD5A77", background: "#FFD1D9"}, hover: {border: "#FD5A77", background: "#FFD1D9"}}, // pink + {border: "#4AD63A", background: "#C2FABC", highlight: {border: "#4AD63A", background: "#E6FFE3"}, hover: {border: "#4AD63A", background: "#E6FFE3"}} // mint + ]; /** - * move the screen left - * @private + * Clear all groups */ - exports._moveLeft = function(event) { - this.xIncrement = this.constants.keyboard.speed.x; - this.start(); // if there is no node movement, the calculation wont be done - event.preventDefault(); + Groups.prototype.clear = function () { + this.groups = {}; + this.groups.length = function() + { + var i = 0; + for ( var p in this ) { + if (this.hasOwnProperty(p)) { + i++; + } + } + return i; + } }; /** - * move the screen right - * @private + * get group properties of a groupname. If groupname is not found, a new group + * is added. + * @param {*} groupname Can be a number, string, Date, etc. + * @return {Object} group The created group, containing all group properties */ - exports._moveRight = function(event) { - this.xIncrement = -this.constants.keyboard.speed.y; - this.start(); // if there is no node movement, the calculation wont be done - event.preventDefault(); - }; - + Groups.prototype.get = function (groupname) { + var group = this.groups[groupname]; + if (group == undefined) { + // create new group + var index = this.defaultIndex % Groups.DEFAULT.length; + this.defaultIndex++; + group = {}; + group.color = Groups.DEFAULT[index]; + this.groups[groupname] = group; + } - /** - * Zoom in, using the same method as the movement. - * @private - */ - exports._zoomIn = function(event) { - this.zoomIncrement = this.constants.keyboard.speed.zoom; - this.start(); // if there is no node movement, the calculation wont be done - event.preventDefault(); + return group; }; - /** - * Zoom out - * @private + * Add a custom group style + * @param {String} groupname + * @param {Object} style An object containing borderColor, + * backgroundColor, etc. + * @return {Object} group The created group object */ - exports._zoomOut = function(event) { - this.zoomIncrement = -this.constants.keyboard.speed.zoom; - this.start(); // if there is no node movement, the calculation wont be done - event.preventDefault(); + Groups.prototype.add = function (groupname, style) { + this.groups[groupname] = style; + return style; }; + module.exports = Groups; - /** - * Stop zooming and unhighlight the zoom controls - * @private - */ - exports._stopZoom = function(event) { - this.zoomIncrement = 0; - event && event.preventDefault(); - }; +/***/ }, +/* 55 */ +/***/ function(module, exports, __webpack_require__) { /** - * Stop moving in the Y direction and unHighlight the up and down - * @private + * @class Images + * This class loads images and keeps them stored. */ - exports._yStopMoving = function(event) { - this.yIncrement = 0; - event && event.preventDefault(); - }; - + function Images() { + this.images = {}; + this.imageBroken = {}; + this.callback = undefined; + } /** - * Stop moving in the X direction and unHighlight left and right. - * @private + * Set an onload callback function. This will be called each time an image + * is loaded + * @param {function} callback */ - exports._xStopMoving = function(event) { - this.xIncrement = 0; - event && event.preventDefault(); - }; - - -/***/ }, -/* 62 */ -/***/ function(module, exports, __webpack_require__) { - - exports._resetLevels = function() { - for (var nodeId in this.nodes) { - if (this.nodes.hasOwnProperty(nodeId)) { - var node = this.nodes[nodeId]; - if (node.preassignedLevel == false) { - node.level = -1; - node.hierarchyEnumerated = false; - } - } - } + Images.prototype.setOnloadCallback = function(callback) { + this.callback = callback; }; /** - * This is the main function to layout the nodes in a hierarchical way. - * It checks if the node details are supplied correctly * - * @private + * @param {string} url Url of the image + * @param {string} url Url of an image to use if the url image is not found + * @return {Image} img The image object */ - exports._setupHierarchicalLayout = function() { - if (this.constants.hierarchicalLayout.enabled == true && this.nodeIndices.length > 0) { - // get the size of the largest hubs and check if the user has defined a level for a node. - var hubsize = 0; - var node, nodeId; - var definedLevel = false; - var undefinedLevel = false; - - for (nodeId in this.nodes) { - if (this.nodes.hasOwnProperty(nodeId)) { - node = this.nodes[nodeId]; - if (node.level != -1) { - definedLevel = true; - } - else { - undefinedLevel = true; - } - if (hubsize < node.edges.length) { - hubsize = node.edges.length; - } + Images.prototype.load = function(url, brokenUrl) { + var img = this.images[url]; // make a pointer + if (img === undefined) { + // create the image + var me = this; + img = new Image(); + img.onload = function () { + // IE11 fix -- thanks dponch! + if (this.width == 0) { + document.body.appendChild(this); + this.width = this.offsetWidth; + this.height = this.offsetHeight; + document.body.removeChild(this); } - } - // if the user defined some levels but not all, alert and run without hierarchical layout - if (undefinedLevel == true && definedLevel == true) { - throw new Error("To use the hierarchical layout, nodes require either no predefined levels or levels have to be defined for all nodes."); - this.zoomExtent(undefined,true,this.constants.clustering.enabled); - if (!this.constants.clustering.enabled) { - this.start(); + if (me.callback) { + me.images[url] = img; + me.callback(this); } - } - else { - // setup the system to use hierarchical method. - this._changeConstants(); + }; - // define levels if undefined by the users. Based on hubsize - if (undefinedLevel == true) { - if (this.constants.hierarchicalLayout.layout == "hubsize") { - this._determineLevels(hubsize); - } - else { - this._determineLevelsDirected(false); + img.onerror = function () { + if (brokenUrl === undefined) { + console.error("Could not load image:", url); + delete this.src; + if (me.callback) { + me.callback(this); } - } - // check the distribution of the nodes per level. - var distribution = this._getDistribution(); - - // place the nodes on the canvas. This also stablilizes the system. - this._placeNodesByHierarchy(distribution); - - // start the simulation. - this.start(); - } - } - }; - - - /** - * This function places the nodes on the canvas based on the hierarchial distribution. - * - * @param {Object} distribution | obtained by the function this._getDistribution() - * @private - */ - exports._placeNodesByHierarchy = function(distribution) { - var nodeId, node; - - // start placing all the level 0 nodes first. Then recursively position their branches. - for (var level in distribution) { - if (distribution.hasOwnProperty(level)) { - - for (nodeId in distribution[level].nodes) { - if (distribution[level].nodes.hasOwnProperty(nodeId)) { - node = distribution[level].nodes[nodeId]; - if (this.constants.hierarchicalLayout.direction == "UD" || this.constants.hierarchicalLayout.direction == "DU") { - if (node.xFixed) { - node.x = distribution[level].minPos; - node.xFixed = false; - - distribution[level].minPos += distribution[level].nodeSpacing; + else { + if (me.imageBroken[url] === true) { + if (this.src == brokenUrl) { + console.error("Could not load brokenImage:", brokenUrl); + delete this.src; + if (me.callback) { + me.callback(this); } } else { - if (node.yFixed) { - node.y = distribution[level].minPos; - node.yFixed = false; - - distribution[level].minPos += distribution[level].nodeSpacing; - } + console.error("Could not load image:", url); + this.src = brokenUrl; } - this._placeBranchNodes(node.edges,node.id,distribution,node.level); + } + else { + console.error("Could not load image:", url); + this.src = brokenUrl; + me.imageBroken[url] = true; } } - } + }; + + img.src = url; } - // stabilize the system after positioning. This function calls zoomExtent. - this._stabilize(); + return img; }; + module.exports = Images; - /** - * This function get the distribution of levels based on hubsize - * - * @returns {Object} - * @private - */ - exports._getDistribution = function() { - var distribution = {}; - var nodeId, node, level; - - // we fix Y because the hierarchy is vertical, we fix X so we do not give a node an x position for a second time. - // the fix of X is removed after the x value has been set. - for (nodeId in this.nodes) { - if (this.nodes.hasOwnProperty(nodeId)) { - node = this.nodes[nodeId]; - node.xFixed = true; - node.yFixed = true; - if (this.constants.hierarchicalLayout.direction == "UD" || this.constants.hierarchicalLayout.direction == "DU") { - node.y = this.constants.hierarchicalLayout.levelSeparation*node.level; - } - else { - node.x = this.constants.hierarchicalLayout.levelSeparation*node.level; - } - if (distribution[node.level] === undefined) { - distribution[node.level] = {amount: 0, nodes: {}, minPos:0, nodeSpacing:0}; - } - distribution[node.level].amount += 1; - distribution[node.level].nodes[nodeId] = node; - } - } - - // determine the largest amount of nodes of all levels - var maxCount = 0; - for (level in distribution) { - if (distribution.hasOwnProperty(level)) { - if (maxCount < distribution[level].amount) { - maxCount = distribution[level].amount; - } - } - } - - // set the initial position and spacing of each nodes accordingly - for (level in distribution) { - if (distribution.hasOwnProperty(level)) { - distribution[level].nodeSpacing = (maxCount + 1) * this.constants.hierarchicalLayout.nodeSpacing; - distribution[level].nodeSpacing /= (distribution[level].amount + 1); - distribution[level].minPos = distribution[level].nodeSpacing - (0.5 * (distribution[level].amount + 1) * distribution[level].nodeSpacing); - } - } - return distribution; - }; +/***/ }, +/* 56 */ +/***/ function(module, exports, __webpack_require__) { + var util = __webpack_require__(1); /** - * this function allocates nodes in levels based on the recursive branching from the largest hubs. + * @class Node + * A node. A node can be connected to other nodes via one or multiple edges. + * @param {object} properties An object containing properties for the node. All + * properties 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" + * {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 properties + * @param {Object} constants An object with default values for + * example for the color * - * @param hubsize - * @private */ - exports._determineLevels = function(hubsize) { - var nodeId, node; + function Node(properties, imagelist, grouplist, networkConstants) { + var constants = util.selectiveBridgeObject(['nodes'],networkConstants); + this.options = constants.nodes; - // determine hubs - for (nodeId in this.nodes) { - if (this.nodes.hasOwnProperty(nodeId)) { - node = this.nodes[nodeId]; - if (node.edges.length == hubsize) { - node.level = 0; - } - } - } + this.selected = false; + this.hover = false; - // branch from hubs - for (nodeId in this.nodes) { - if (this.nodes.hasOwnProperty(nodeId)) { - node = this.nodes[nodeId]; - if (node.level == 0) { - this._setLevel(1,node.edges,node.id); - } - } - } - }; + this.edges = []; // all edges connected to this node + this.dynamicEdges = []; + this.reroutedEdges = {}; + + this.fontDrawThreshold = 3; + + // set defaults for the properties + this.id = undefined; + this.allowedToMoveX = false; + this.allowedToMoveY = false; + this.xFixed = false; + this.yFixed = false; + this.horizontalAlignLeft = true; // these are for the navigation controls + this.verticalAlignTop = true; // these are for the navigation controls + this.baseRadiusValue = networkConstants.nodes.radius; + this.radiusFixed = false; + this.level = -1; + this.preassignedLevel = false; + this.hierarchyEnumerated = false; + this.labelDimensions = {top:0, left:0, width:0, height:0, yLine:0}; // could be cached + this.boundingBox = {top:0, left:0, right:0, bottom:0}; + + this.imagelist = imagelist; + this.grouplist = grouplist; + + // physics properties + this.fx = 0.0; // external force x + this.fy = 0.0; // external force y + this.vx = 0.0; // velocity x + this.vy = 0.0; // velocity y + this.x = null; + this.y = null; + + // used for reverting to previous position on stabilization + this.previousState = {vx:0,vy:0,x:0,y:0}; + + this.damping = networkConstants.physics.damping; // written every time gravity is calculated + this.fixedData = {x:null,y:null}; + + this.setProperties(properties, constants); + + // creating the variables for clustering + this.resetCluster(); + this.dynamicEdgesLength = 0; + this.clusterSession = 0; + this.clusterSizeWidthFactor = networkConstants.clustering.nodeScaling.width; + this.clusterSizeHeightFactor = networkConstants.clustering.nodeScaling.height; + this.clusterSizeRadiusFactor = networkConstants.clustering.nodeScaling.radius; + this.maxNodeSizeIncrements = networkConstants.clustering.maxNodeSizeIncrements; + this.growthIndicator = 0; + // variables to tell the node about the network. + this.networkScaleInv = 1; + this.networkScale = 1; + this.canvasTopLeft = {"x": -300, "y": -300}; + this.canvasBottomRight = {"x": 300, "y": 300}; + this.parentEdgeId = null; + } /** - * this function allocates nodes in levels based on the direction of the edges - * - * @param hubsize - * @private + * Revert the position and velocity of the previous step. */ - exports._determineLevelsDirected = function() { - var nodeId, node, firstNode; - var minLevel = 10000; + Node.prototype.revertPosition = function() { + this.x = this.previousState.x; + this.y = this.previousState.y; + this.vx = this.previousState.vx; + this.vy = this.previousState.vy; + } - // set first node to source - firstNode = this.nodes[this.nodeIndices[0]]; - firstNode.level = minLevel; - this._setLevelDirected(minLevel,firstNode.edges,firstNode.id); - // get the minimum level - for (nodeId in this.nodes) { - if (this.nodes.hasOwnProperty(nodeId)) { - node = this.nodes[nodeId]; - minLevel = node.level < minLevel ? node.level : minLevel; - } + /** + * (re)setting the clustering variables and objects + */ + Node.prototype.resetCluster = function() { + // clustering variables + this.formationScale = undefined; // this is used to determine when to open the cluster + this.clusterSize = 1; // this signifies the total amount of nodes in this cluster + this.containedNodes = {}; + this.containedEdges = {}; + this.clusterSessions = []; + }; + + /** + * Attach a edge to the node + * @param {Edge} edge + */ + Node.prototype.attachEdge = function(edge) { + if (this.edges.indexOf(edge) == -1) { + this.edges.push(edge); + } + if (this.dynamicEdges.indexOf(edge) == -1) { + this.dynamicEdges.push(edge); } + this.dynamicEdgesLength = this.dynamicEdges.length; + }; - // subtract the minimum from the set so we have a range starting from 0 - for (nodeId in this.nodes) { - if (this.nodes.hasOwnProperty(nodeId)) { - node = this.nodes[nodeId]; - node.level -= minLevel; - } + /** + * Detach a edge from the node + * @param {Edge} edge + */ + Node.prototype.detachEdge = function(edge) { + var index = this.edges.indexOf(edge); + if (index != -1) { + this.edges.splice(index, 1); } + index = this.dynamicEdges.indexOf(edge); + if (index != -1) { + this.dynamicEdges.splice(index, 1); + } + this.dynamicEdgesLength = this.dynamicEdges.length; }; /** - * Since hierarchical layout does not support: - * - smooth curves (based on the physics), - * - clustering (based on dynamic node counts) - * - * We disable both features so there will be no problems. - * - * @private + * Set or overwrite properties for the node + * @param {Object} properties an object with properties + * @param {Object} constants and object with default, global properties */ - exports._changeConstants = function() { - this.constants.clustering.enabled = false; - this.constants.physics.barnesHut.enabled = false; - this.constants.physics.hierarchicalRepulsion.enabled = true; - this._loadSelectedForceSolver(); - if (this.constants.smoothCurves.enabled == true) { - this.constants.smoothCurves.dynamic = false; + Node.prototype.setProperties = function(properties, constants) { + if (!properties) { + return; } - this._configureSmoothCurves(); - var config = this.constants.hierarchicalLayout; - config.levelSeparation = Math.abs(config.levelSeparation); - if (config.direction == "RL" || config.direction == "DU") { - config.levelSeparation *= -1; + var fields = ['borderWidth','borderWidthSelected','shape','image','brokenImage','radius','fontColor', + 'fontSize','fontFace','fontFill','fontStrokeWidth','fontStrokeColor','group','mass' + ]; + util.selectiveDeepExtend(fields, this.options, properties); + + // basic properties + if (properties.id !== undefined) {this.id = properties.id;} + if (properties.label !== undefined) {this.label = properties.label; this.originalLabel = properties.label;} + if (properties.title !== undefined) {this.title = properties.title;} + if (properties.x !== undefined) {this.x = properties.x;} + if (properties.y !== undefined) {this.y = properties.y;} + if (properties.value !== undefined) {this.value = properties.value;} + if (properties.level !== undefined) {this.level = properties.level; this.preassignedLevel = true;} + + // navigation controls properties + if (properties.horizontalAlignLeft !== undefined) {this.horizontalAlignLeft = properties.horizontalAlignLeft;} + if (properties.verticalAlignTop !== undefined) {this.verticalAlignTop = properties.verticalAlignTop;} + if (properties.triggerFunction !== undefined) {this.triggerFunction = properties.triggerFunction;} + + if (this.id === undefined) { + throw "Node must have an id"; } - if (config.direction == "RL" || config.direction == "LR") { - if (this.constants.smoothCurves.enabled == true) { - this.constants.smoothCurves.type = "vertical"; - } + // copy group properties + if (typeof properties.group === 'number' || (typeof properties.group === 'string' && properties.group != '')) { + var groupObj = this.grouplist.get(properties.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); } - else { - if (this.constants.smoothCurves.enabled == true) { - this.constants.smoothCurves.type = "horizontal"; + // individual shape properties + if (properties.radius !== undefined) {this.baseRadiusValue = this.options.radius;} + if (properties.color !== undefined) {this.options.color = util.parseColor(properties.color);} + + 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"; } } + + if (properties.allowedToMoveX !== undefined) { + this.xFixed = !properties.allowedToMoveX; + this.allowedToMoveX = properties.allowedToMoveX; + } + else if (properties.x !== undefined && this.allowedToMoveX == false) { + this.xFixed = true; + } + + + if (properties.allowedToMoveY !== undefined) { + this.yFixed = !properties.allowedToMoveY; + this.allowedToMoveY = properties.allowedToMoveY; + } + else if (properties.y !== undefined && this.allowedToMoveY == false) { + this.yFixed = true; + } + + this.radiusFixed = this.radiusFixed || (properties.radius !== undefined); + + if (this.options.shape === 'image' || this.options.shape === 'circularImage') { + this.options.radiusMin = constants.nodes.widthMin; + this.options.radiusMax = constants.nodes.widthMax; + } + + // choose draw method depending on the shape + switch (this.options.shape) { + case 'database': this.draw = this._drawDatabase; this.resize = this._resizeDatabase; break; + case 'box': this.draw = this._drawBox; this.resize = this._resizeBox; break; + case 'circle': this.draw = this._drawCircle; this.resize = this._resizeCircle; break; + case 'ellipse': this.draw = this._drawEllipse; this.resize = this._resizeEllipse; break; + // TODO: add diamond shape + case 'image': this.draw = this._drawImage; this.resize = this._resizeImage; break; + case 'circularImage': this.draw = this._drawCircularImage; this.resize = this._resizeCircularImage; break; + case 'text': this.draw = this._drawText; this.resize = this._resizeText; break; + case 'dot': this.draw = this._drawDot; this.resize = this._resizeShape; break; + case 'square': this.draw = this._drawSquare; this.resize = this._resizeShape; break; + case 'triangle': this.draw = this._drawTriangle; this.resize = this._resizeShape; break; + case 'triangleDown': this.draw = this._drawTriangleDown; this.resize = this._resizeShape; break; + case 'star': this.draw = this._drawStar; this.resize = this._resizeShape; break; + default: this.draw = this._drawEllipse; this.resize = this._resizeEllipse; break; + } + // reset the size of the node, this can be changed + this._reset(); + }; + /** + * select this node + */ + Node.prototype.select = function() { + this.selected = true; + this._reset(); + }; /** - * This is a recursively called function to enumerate the branches from the largest hubs and place the nodes - * on a X position that ensures there will be no overlap. - * - * @param edges - * @param parentId - * @param distribution - * @param parentLevel - * @private + * unselect this node */ - exports._placeBranchNodes = function(edges, parentId, distribution, parentLevel) { - for (var i = 0; i < edges.length; i++) { - var childNode = null; - if (edges[i].toId == parentId) { - childNode = edges[i].from; - } - else { - childNode = edges[i].to; - } + Node.prototype.unselect = function() { + this.selected = false; + this._reset(); + }; - // if a node is conneceted to another node on the same level (or higher (means lower level))!, this is not handled here. - var nodeMoved = false; - if (this.constants.hierarchicalLayout.direction == "UD" || this.constants.hierarchicalLayout.direction == "DU") { - if (childNode.xFixed && childNode.level > parentLevel) { - childNode.xFixed = false; - childNode.x = distribution[childNode.level].minPos; - nodeMoved = true; - } - } - else { - if (childNode.yFixed && childNode.level > parentLevel) { - childNode.yFixed = false; - childNode.y = distribution[childNode.level].minPos; - nodeMoved = true; - } - } - if (nodeMoved == true) { - distribution[childNode.level].minPos += distribution[childNode.level].nodeSpacing; - if (childNode.edges.length > 1) { - this._placeBranchNodes(childNode.edges,childNode.id,distribution,childNode.level); - } - } - } + /** + * Reset the calculated size of the node, forces it to recalculate its size + */ + Node.prototype.clearSizeCache = function() { + this._reset(); }; - /** - * this function is called recursively to enumerate the barnches of the largest hubs and give each node a level. - * - * @param level - * @param edges - * @param parentId + * Reset the calculated size of the node, forces it to recalculate its size * @private */ - exports._setLevel = function(level, edges, parentId) { - for (var i = 0; i < edges.length; i++) { - var childNode = null; - if (edges[i].toId == parentId) { - childNode = edges[i].from; - } - else { - childNode = edges[i].to; - } - if (childNode.level == -1 || childNode.level > level) { - childNode.level = level; - if (childNode.edges.length > 1) { - this._setLevel(level+1, childNode.edges, childNode.id); - } - } - } + Node.prototype._reset = function() { + this.width = undefined; + this.height = undefined; }; + /** + * get the title of this node. + * @return {string} title The title of the node, or undefined when no title + * has been set. + */ + Node.prototype.getTitle = function() { + return typeof this.title === "function" ? this.title() : this.title; + }; /** - * this function is called recursively to enumerate the branched of the first node and give each node a level based on edge direction - * - * @param level - * @param edges - * @param parentId - * @private + * 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 */ - exports._setLevelDirected = function(level, edges, parentId) { - this.nodes[parentId].hierarchyEnumerated = true; - var childNode, direction; - for (var i = 0; i < edges.length; i++) { - direction = 1; - if (edges[i].toId == parentId) { - childNode = edges[i].from; - direction = -1; - } - else { - childNode = edges[i].to; - } - if (childNode.level == -1) { - childNode.level = level + direction; - } + Node.prototype.distanceToBorder = function (ctx, angle) { + var borderWidth = 1; + + if (!this.width) { + this.resize(ctx); } - for (var i = 0; i < edges.length; i++) { - if (edges[i].toId == parentId) {childNode = edges[i].from;} - else {childNode = edges[i].to;} + switch (this.options.shape) { + case 'circle': + case 'dot': + return this.options.radius+ borderWidth; + + case 'ellipse': + 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); + + // TODO: implement distanceToBorder for database + // TODO: implement distanceToBorder for triangle + // TODO: implement distanceToBorder for triangleDown + + case 'box': + case 'image': + case 'text': + default: + if (this.width) { + return Math.min( + Math.abs(this.width / 2 / Math.cos(angle)), + Math.abs(this.height / 2 / Math.sin(angle))) + borderWidth; + // TODO: reckon with border radius too in case of box + } + else { + return 0; + } - if (childNode.edges.length > 1 && childNode.hierarchyEnumerated === false) { - this._setLevelDirected(childNode.level, childNode.edges, childNode.id); - } } + // TODO: implement calculation of distance to border for all shapes }; + /** + * Set forces acting on the node + * @param {number} fx Force in horizontal direction + * @param {number} fy Force in vertical direction + */ + Node.prototype._setForce = function(fx, fy) { + this.fx = fx; + this.fy = fy; + }; /** - * Unfix nodes - * + * Add forces acting on the node + * @param {number} fx Force in horizontal direction + * @param {number} fy Force in vertical direction * @private */ - exports._restoreNodes = function() { - for (var nodeId in this.nodes) { - if (this.nodes.hasOwnProperty(nodeId)) { - this.nodes[nodeId].xFixed = false; - this.nodes[nodeId].yFixed = false; - } - } + Node.prototype._addForce = function(fx, fy) { + this.fx += fx; + this.fy += fy; }; - -/***/ }, -/* 63 */ -/***/ 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. + * Store the state before the next step */ + Node.prototype.storeState = function() { + this.previousState.x = this.x; + this.previousState.y = this.y; + this.previousState.vx = this.vx; + this.previousState.vy = this.vy; + } - // 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(); + /** + * Perform one discrete step for the node + * @param {number} interval Time interval in seconds + */ + Node.prototype.discreteStep = function(interval) { + this.storeState(); + if (!this.xFixed) { + var dx = this.damping * this.vx; // damping force + var ax = (this.fx - dx) / this.options.mass; // acceleration + this.vx += ax * interval; // velocity + this.x += this.vx * interval; // position + } + else { + this.fx = 0; + this.vx = 0; } - }(this, function () { - function keycharm(options) { - var preventDefault = options && options.preventDefault || false; + if (!this.yFixed) { + var dy = this.damping * this.vy; // damping force + var ay = (this.fy - dy) / this.options.mass; // acceleration + this.vy += ay * interval; // velocity + this.y += this.vy * interval; // position + } + else { + this.fy = 0; + this.vy = 0; + } + }; - var container = options && options.container || window; - var _exportFunctions = {}; - var _bound = {keydown:{}, keyup:{}}; - var _keys = {}; - var i; - // a - z - for (i = 97; i <= 122; i++) {_keys[String.fromCharCode(i)] = {code:65 + (i - 97), shift: false};} - // A - Z - for (i = 65; i <= 90; i++) {_keys[String.fromCharCode(i)] = {code:i, shift: true};} - // 0 - 9 - for (i = 0; i <= 9; i++) {_keys['' + i] = {code:48 + i, shift: false};} - // F1 - F12 - for (i = 1; i <= 12; i++) {_keys['F' + i] = {code:111 + i, shift: false};} - // num0 - num9 - for (i = 0; i <= 9; i++) {_keys['num' + i] = {code:96 + i, shift: false};} - - // numpad misc - _keys['num*'] = {code:106, shift: false}; - _keys['num+'] = {code:107, shift: false}; - _keys['num-'] = {code:109, shift: false}; - _keys['num/'] = {code:111, shift: false}; - _keys['num.'] = {code:110, shift: false}; - // arrows - _keys['left'] = {code:37, shift: false}; - _keys['up'] = {code:38, shift: false}; - _keys['right'] = {code:39, shift: false}; - _keys['down'] = {code:40, shift: false}; - // extra keys - _keys['space'] = {code:32, shift: false}; - _keys['enter'] = {code:13, shift: false}; - _keys['shift'] = {code:16, shift: undefined}; - _keys['esc'] = {code:27, shift: false}; - _keys['backspace'] = {code:8, shift: false}; - _keys['tab'] = {code:9, shift: false}; - _keys['ctrl'] = {code:17, shift: false}; - _keys['alt'] = {code:18, shift: false}; - _keys['delete'] = {code:46, shift: false}; - _keys['pageup'] = {code:33, shift: false}; - _keys['pagedown'] = {code:34, shift: false}; - // symbols - _keys['='] = {code:187, shift: false}; - _keys['-'] = {code:189, shift: false}; - _keys[']'] = {code:221, shift: false}; - _keys['['] = {code:219, shift: false}; - - - - var down = function(event) {handleEvent(event,'keydown');}; - var up = function(event) {handleEvent(event,'keyup');}; - - // handle the actualy bound key with the event - var handleEvent = function(event,type) { - if (_bound[type][event.keyCode] !== undefined) { - var bound = _bound[type][event.keyCode]; - for (var i = 0; i < bound.length; i++) { - if (bound[i].shift === undefined) { - bound[i].fn(event); - } - else if (bound[i].shift == true && event.shiftKey == true) { - bound[i].fn(event); - } - else if (bound[i].shift == false && event.shiftKey == false) { - bound[i].fn(event); - } - } - - if (preventDefault == true) { - event.preventDefault(); - } - } - }; - - // bind a key to a callback - _exportFunctions.bind = function(key, callback, type) { - if (type === undefined) { - type = 'keydown'; - } - if (_keys[key] === undefined) { - throw new Error("unsupported key: " + key); - } - if (_bound[type][_keys[key].code] === undefined) { - _bound[type][_keys[key].code] = []; - } - _bound[type][_keys[key].code].push({fn:callback, shift:_keys[key].shift}); - }; - - - // bind all keys to a call back (demo purposes) - _exportFunctions.bindAll = function(callback, type) { - if (type === undefined) { - type = 'keydown'; - } - for (var key in _keys) { - if (_keys.hasOwnProperty(key)) { - _exportFunctions.bind(key,callback,type); - } - } - }; - - // get the key label from an event - _exportFunctions.getKey = function(event) { - for (var key in _keys) { - if (_keys.hasOwnProperty(key)) { - if (event.shiftKey == true && _keys[key].shift == true && event.keyCode == _keys[key].code) { - return key; - } - else if (event.shiftKey == false && _keys[key].shift == false && event.keyCode == _keys[key].code) { - return key; - } - else if (event.keyCode == _keys[key].code && key == 'shift') { - return key; - } - } - } - return "unknown key, currently not supported"; - }; - - // unbind either a specific callback from a key or all of them (by leaving callback undefined) - _exportFunctions.unbind = function(key, callback, type) { - if (type === undefined) { - type = 'keydown'; - } - if (_keys[key] === undefined) { - throw new Error("unsupported key: " + key); - } - if (callback !== undefined) { - var newBindings = []; - var bound = _bound[type][_keys[key].code]; - if (bound !== undefined) { - for (var i = 0; i < bound.length; i++) { - if (!(bound[i].fn == callback && bound[i].shift == _keys[key].shift)) { - newBindings.push(_bound[type][_keys[key].code][i]); - } - } - } - _bound[type][_keys[key].code] = newBindings; - } - else { - _bound[type][_keys[key].code] = []; - } - }; - - // reset all bound variables. - _exportFunctions.reset = function() { - _bound = {keydown:{}, keyup:{}}; - }; - - // unbind all listeners and reset all variables. - _exportFunctions.destroy = function() { - _bound = {keydown:{}, keyup:{}}; - container.removeEventListener('keydown', down, true); - container.removeEventListener('keyup', up, true); - }; - - // create listeners. - container.addEventListener('keydown',down,true); - container.addEventListener('keyup',up,true); - - // return the public functions. - return _exportFunctions; - } - - return keycharm; - })); - - - - -/***/ }, -/* 64 */ -/***/ function(module, exports, __webpack_require__) { - - var __WEBPACK_AMD_DEFINE_RESULT__;/*! Hammer.JS - v1.1.3 - 2014-05-20 - * http://eightmedia.github.io/hammer.js - * - * Copyright (c) 2014 Jorik Tangelder ; - * Licensed under the MIT license */ - - (function(window, undefined) { - 'use strict'; /** - * @main - * @module hammer - * - * @class Hammer - * @static + * Perform one discrete step for the node + * @param {number} interval Time interval in seconds + * @param {number} maxVelocity The speed limit imposed on the velocity */ + Node.prototype.discreteStepLimited = function(interval, maxVelocity) { + this.storeState(); + if (!this.xFixed) { + var dx = this.damping * this.vx; // damping force + var ax = (this.fx - dx) / this.options.mass; // acceleration + this.vx += ax * interval; // velocity + this.vx = (Math.abs(this.vx) > maxVelocity) ? ((this.vx > 0) ? maxVelocity : -maxVelocity) : this.vx; + this.x += this.vx * interval; // position + } + else { + this.fx = 0; + this.vx = 0; + } - /** - * Hammer, use this to create instances - * ```` - * var hammertime = new Hammer(myElement); - * ```` - * - * @method Hammer - * @param {HTMLElement} element - * @param {Object} [options={}] - * @return {Hammer.Instance} - */ - var Hammer = function Hammer(element, options) { - return new Hammer.Instance(element, options || {}); + if (!this.yFixed) { + var dy = this.damping * this.vy; // damping force + var ay = (this.fy - dy) / this.options.mass; // acceleration + this.vy += ay * interval; // velocity + this.vy = (Math.abs(this.vy) > maxVelocity) ? ((this.vy > 0) ? maxVelocity : -maxVelocity) : this.vy; + this.y += this.vy * interval; // position + } + else { + this.fy = 0; + this.vy = 0; + } }; /** - * version, as defined in package.json - * the value will be set at each build - * @property VERSION - * @final - * @type {String} - */ - Hammer.VERSION = '1.1.3'; - - /** - * default settings. - * more settings are defined per gesture at `/gestures`. Each gesture can be disabled/enabled - * by setting it's name (like `swipe`) to false. - * You can set the defaults for all instances by changing this object before creating an instance. - * @example - * ```` - * Hammer.defaults.drag = false; - * Hammer.defaults.behavior.touchAction = 'pan-y'; - * delete Hammer.defaults.behavior.userSelect; - * ```` - * @property defaults - * @type {Object} + * Check if this node has a fixed x and y position + * @return {boolean} true if fixed, false if not */ - Hammer.defaults = { - /** - * this setting object adds styles and attributes to the element to prevent the browser from doing - * its native behavior. The css properties are auto prefixed for the browsers when needed. - * @property defaults.behavior - * @type {Object} - */ - behavior: { - /** - * Disables text selection to improve the dragging gesture. When the value is `none` it also sets - * `onselectstart=false` for IE on the element. Mainly for desktop browsers. - * @property defaults.behavior.userSelect - * @type {String} - * @default 'none' - */ - userSelect: 'none', - - /** - * Specifies whether and how a given region can be manipulated by the user (for instance, by panning or zooming). - * Used by Chrome 35> and IE10>. By default this makes the element blocking any touch event. - * @property defaults.behavior.touchAction - * @type {String} - * @default: 'pan-y' - */ - touchAction: 'pan-y', - - /** - * Disables the default callout shown when you touch and hold a touch target. - * On iOS, when you touch and hold a touch target such as a link, Safari displays - * a callout containing information about the link. This property allows you to disable that callout. - * @property defaults.behavior.touchCallout - * @type {String} - * @default 'none' - */ - touchCallout: 'none', - - /** - * Specifies whether zooming is enabled. Used by IE10> - * @property defaults.behavior.contentZooming - * @type {String} - * @default 'none' - */ - contentZooming: 'none', - - /** - * Specifies that an entire element should be draggable instead of its contents. - * Mainly for desktop browsers. - * @property defaults.behavior.userDrag - * @type {String} - * @default 'none' - */ - userDrag: 'none', - - /** - * Overrides the highlight color shown when the user taps a link or a JavaScript - * clickable element in Safari on iPhone. This property obeys the alpha value, if specified. - * - * If you don't specify an alpha value, Safari on iPhone applies a default alpha value - * to the color. To disable tap highlighting, set the alpha value to 0 (invisible). - * If you set the alpha value to 1.0 (opaque), the element is not visible when tapped. - * @property defaults.behavior.tapHighlightColor - * @type {String} - * @default 'rgba(0,0,0,0)' - */ - tapHighlightColor: 'rgba(0,0,0,0)' - } + Node.prototype.isFixed = function() { + return (this.xFixed && this.yFixed); }; /** - * hammer document where the base events are added at - * @property DOCUMENT - * @type {HTMLElement} - * @default window.document - */ - Hammer.DOCUMENT = document; - - /** - * detect support for pointer events - * @property HAS_POINTEREVENTS - * @type {Boolean} - */ - Hammer.HAS_POINTEREVENTS = navigator.pointerEnabled || navigator.msPointerEnabled; - - /** - * detect support for touch events - * @property HAS_TOUCHEVENTS - * @type {Boolean} + * Check if this node is moving + * @param {number} vmin the minimum velocity considered as "moving" + * @return {boolean} true if moving, false if it has no velocity */ - Hammer.HAS_TOUCHEVENTS = ('ontouchstart' in window); + Node.prototype.isMoving = function(vmin) { + var velocity = Math.sqrt(Math.pow(this.vx,2) + Math.pow(this.vy,2)); + // this.velocity = Math.sqrt(Math.pow(this.vx,2) + Math.pow(this.vy,2)) + return (velocity > vmin); + }; /** - * detect mobile browsers - * @property IS_MOBILE - * @type {Boolean} + * check if this node is selecte + * @return {boolean} selected True if node is selected, else false */ - Hammer.IS_MOBILE = /mobile|tablet|ip(ad|hone|od)|android|silk/i.test(navigator.userAgent); + Node.prototype.isSelected = function() { + return this.selected; + }; /** - * detect if we want to support mouseevents at all - * @property NO_MOUSEEVENTS - * @type {Boolean} + * Retrieve the value of the node. Can be undefined + * @return {Number} value */ - Hammer.NO_MOUSEEVENTS = (Hammer.HAS_TOUCHEVENTS && Hammer.IS_MOBILE) || Hammer.HAS_POINTEREVENTS; + Node.prototype.getValue = function() { + return this.value; + }; /** - * interval in which Hammer recalculates current velocity/direction/angle in ms - * @property CALCULATE_INTERVAL - * @type {Number} - * @default 25 + * Calculate the distance from the nodes location to the given location (x,y) + * @param {Number} x + * @param {Number} y + * @return {Number} value */ - Hammer.CALCULATE_INTERVAL = 25; + Node.prototype.getDistance = function(x, y) { + var dx = this.x - x, + dy = this.y - y; + return Math.sqrt(dx * dx + dy * dy); + }; - /** - * eventtypes per touchevent (start, move, end) are filled by `Event.determineEventTypes` on `setup` - * the object contains the DOM event names per type (`EVENT_START`, `EVENT_MOVE`, `EVENT_END`) - * @property EVENT_TYPES - * @private - * @writeOnce - * @type {Object} - */ - var EVENT_TYPES = {}; /** - * direction strings, for safe comparisons - * @property DIRECTION_DOWN|LEFT|UP|RIGHT - * @final - * @type {String} - * @default 'down' 'left' 'up' 'right' + * Adjust the value range of the node. The node will adjust it's radius + * based on its value. + * @param {Number} min + * @param {Number} max */ - var DIRECTION_DOWN = Hammer.DIRECTION_DOWN = 'down'; - var DIRECTION_LEFT = Hammer.DIRECTION_LEFT = 'left'; - var DIRECTION_UP = Hammer.DIRECTION_UP = 'up'; - var DIRECTION_RIGHT = Hammer.DIRECTION_RIGHT = 'right'; + Node.prototype.setValueRange = function(min, max) { + if (!this.radiusFixed && this.value !== undefined) { + if (max == min) { + this.options.radius= (this.options.radiusMin + this.options.radiusMax) / 2; + } + else { + var scale = (this.options.radiusMax - this.options.radiusMin) / (max - min); + this.options.radius= (this.value - min) * scale + this.options.radiusMin; + } + } + this.baseRadiusValue = this.options.radius; + }; /** - * pointertype strings, for safe comparisons - * @property POINTER_MOUSE|TOUCH|PEN - * @final - * @type {String} - * @default 'mouse' 'touch' 'pen' + * Draw this node in the given canvas + * The 2d context of a HTML canvas can be retrieved by canvas.getContext("2d"); + * @param {CanvasRenderingContext2D} ctx */ - var POINTER_MOUSE = Hammer.POINTER_MOUSE = 'mouse'; - var POINTER_TOUCH = Hammer.POINTER_TOUCH = 'touch'; - var POINTER_PEN = Hammer.POINTER_PEN = 'pen'; + Node.prototype.draw = function(ctx) { + throw "Draw method not initialized for node"; + }; /** - * eventtypes - * @property EVENT_START|MOVE|END|RELEASE|TOUCH - * @final - * @type {String} - * @default 'start' 'change' 'move' 'end' 'release' 'touch' + * 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 */ - var EVENT_START = Hammer.EVENT_START = 'start'; - var EVENT_MOVE = Hammer.EVENT_MOVE = 'move'; - var EVENT_END = Hammer.EVENT_END = 'end'; - var EVENT_RELEASE = Hammer.EVENT_RELEASE = 'release'; - var EVENT_TOUCH = Hammer.EVENT_TOUCH = 'touch'; + Node.prototype.resize = function(ctx) { + throw "Resize method not initialized for node"; + }; /** - * if the window events are set... - * @property READY - * @writeOnce - * @type {Boolean} - * @default false + * 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 */ - Hammer.READY = false; + Node.prototype.isOverlappingWith = function(obj) { + return (this.left < obj.right && + this.left + this.width > obj.left && + this.top < obj.bottom && + this.top + this.height > obj.top); + }; - /** - * plugins namespace - * @property plugins - * @type {Object} - */ - Hammer.plugins = Hammer.plugins || {}; + Node.prototype._resizeImage = function (ctx) { + // TODO: pre calculate the image size - /** - * gestures namespace - * see `/gestures` for the definitions - * @property gestures - * @type {Object} - */ - Hammer.gestures = Hammer.gestures || {}; + if (!this.width || !this.height) { // undefined or 0 + var width, height; + if (this.value) { + this.options.radius= this.baseRadiusValue; + var scale = this.imageObj.height / this.imageObj.width; + if (scale !== undefined) { + width = this.options.radius|| this.imageObj.width; + height = this.options.radius* scale || this.imageObj.height; + } + else { + width = 0; + height = 0; + } + } + else { + width = this.imageObj.width; + height = this.imageObj.height; + } + this.width = width; + this.height = height; - /** - * setup events to detect gestures on the document - * this function is called when creating an new instance - * @private - */ - function setup() { - if(Hammer.READY) { - return; + this.growthIndicator = 0; + if (this.width > 0 && this.height > 0) { + this.width += Math.min(this.clusterSize - 1, this.maxNodeSizeIncrements) * this.clusterSizeWidthFactor; + this.height += Math.min(this.clusterSize - 1, this.maxNodeSizeIncrements) * this.clusterSizeHeightFactor; + this.options.radius+= Math.min(this.clusterSize - 1, this.maxNodeSizeIncrements) * this.clusterSizeRadiusFactor; + this.growthIndicator = this.width - width; } + } + }; - // find what eventtypes we add listeners to - Event.determineEventTypes(); + Node.prototype._drawImageAtPosition = function (ctx) { + if (this.imageObj.width != 0 ) { + // draw the shade + if (this.clusterSize > 1) { + var lineWidth = ((this.clusterSize > 1) ? 10 : 0.0); + lineWidth *= this.networkScaleInv; + lineWidth = Math.min(0.2 * this.width,lineWidth); - // Register all gestures inside Hammer.gestures - Utils.each(Hammer.gestures, function(gesture) { - Detection.register(gesture); - }); + ctx.globalAlpha = 0.5; + ctx.drawImage(this.imageObj, this.left - lineWidth, this.top - lineWidth, this.width + 2*lineWidth, this.height + 2*lineWidth); + } - // Add touch events on the document - Event.onTouch(Hammer.DOCUMENT, EVENT_MOVE, Detection.detect); - Event.onTouch(Hammer.DOCUMENT, EVENT_END, Detection.detect); + // draw the image + ctx.globalAlpha = 1.0; + ctx.drawImage(this.imageObj, this.left, this.top, this.width, this.height); + } + }; - // Hammer is ready...! - Hammer.READY = true; - } + Node.prototype._drawImageLabel = function (ctx) { + var yLabel; + var offset = 0; + + if (this.height){ + offset = this.height / 2; + var labelDimensions = this.getTextSize(ctx); + + if (labelDimensions.lineCount >= 1){ + offset += labelDimensions.height / 2; + offset += 3; + } + } + + yLabel = this.y + offset; - /** - * @module hammer - * - * @class Utils - * @static - */ - var Utils = Hammer.utils = { - /** - * extend method, could also be used for cloning when `dest` is an empty object. - * changes the dest object - * @method extend - * @param {Object} dest - * @param {Object} src - * @param {Boolean} [merge=false] do a merge - * @return {Object} dest - */ - extend: function extend(dest, src, merge) { - for(var key in src) { - if(!src.hasOwnProperty(key) || (dest[key] !== undefined && merge)) { - continue; - } - dest[key] = src[key]; - } - return dest; - }, + this._label(ctx, this.label, this.x, yLabel, undefined); + }; - /** - * simple addEventListener wrapper - * @method on - * @param {HTMLElement} element - * @param {String} type - * @param {Function} handler - */ - on: function on(element, type, handler) { - element.addEventListener(type, handler, false); - }, + Node.prototype._drawImage = function (ctx) { + this._resizeImage(ctx); + this.left = this.x - this.width / 2; + this.top = this.y - this.height / 2; - /** - * simple removeEventListener wrapper - * @method off - * @param {HTMLElement} element - * @param {String} type - * @param {Function} handler - */ - off: function off(element, type, handler) { - element.removeEventListener(type, handler, false); - }, + this._drawImageAtPosition(ctx); - /** - * forEach over arrays and objects - * @method each - * @param {Object|Array} obj - * @param {Function} iterator - * @param {any} iterator.item - * @param {Number} iterator.index - * @param {Object|Array} iterator.obj the source object - * @param {Object} context value to use as `this` in the iterator - */ - each: function each(obj, iterator, context) { - var i, len; + this.boundingBox.top = this.top; + this.boundingBox.left = this.left; + this.boundingBox.right = this.left + this.width; + this.boundingBox.bottom = this.top + this.height; - // native forEach on arrays - if('forEach' in obj) { - obj.forEach(iterator, context); - // arrays - } else if(obj.length !== undefined) { - for(i = 0, len = obj.length; i < len; i++) { - if(iterator.call(context, obj[i], i, obj) === false) { - return; - } - } - // objects - } else { - for(i in obj) { - if(obj.hasOwnProperty(i) && - iterator.call(context, obj[i], i, obj) === false) { - return; - } - } - } - }, + this._drawImageLabel(ctx); + this.boundingBox.left = Math.min(this.boundingBox.left, this.labelDimensions.left); + this.boundingBox.right = Math.max(this.boundingBox.right, this.labelDimensions.left + this.labelDimensions.width); + this.boundingBox.bottom = Math.max(this.boundingBox.bottom, this.boundingBox.bottom + this.labelDimensions.height); + }; - /** - * find if a string contains the string using indexOf - * @method inStr - * @param {String} src - * @param {String} find - * @return {Boolean} found - */ - inStr: function inStr(src, find) { - return src.indexOf(find) > -1; - }, + Node.prototype._resizeCircularImage = function (ctx) { + if(!this.imageObj.src || !this.imageObj.width || !this.imageObj.height){ + if (!this.width) { + var diameter = this.options.radius * 2; + this.width = diameter; + this.height = diameter; - /** - * find if a array contains the object using indexOf or a simple polyfill - * @method inArray - * @param {String} src - * @param {String} find - * @return {Boolean|Number} false when not found, or the index - */ - inArray: function inArray(src, find) { - if(src.indexOf) { - var index = src.indexOf(find); - return (index === -1) ? false : index; - } else { - for(var i = 0, len = src.length; i < len; i++) { - if(src[i] === find) { - return i; - } - } - return false; - } - }, + // scaling used for clustering + //this.width += Math.min(this.clusterSize - 1, this.maxNodeSizeIncrements) * 0.5 * this.clusterSizeWidthFactor; + //this.height += Math.min(this.clusterSize - 1, this.maxNodeSizeIncrements) * 0.5 * this.clusterSizeHeightFactor; + this.options.radius += Math.min(this.clusterSize - 1, this.maxNodeSizeIncrements) * 0.5 * this.clusterSizeRadiusFactor; + this.growthIndicator = this.options.radius- 0.5*diameter; + this._swapToImageResizeWhenImageLoaded = true; + } + } + else { + if (this._swapToImageResizeWhenImageLoaded) { + this.width = 0; + this.height = 0; + delete this._swapToImageResizeWhenImageLoaded; + } + this._resizeImage(ctx); + } - /** - * convert an array-like object (`arguments`, `touchlist`) to an array - * @method toArray - * @param {Object} obj - * @return {Array} - */ - toArray: function toArray(obj) { - return Array.prototype.slice.call(obj, 0); - }, + }; - /** - * find if a node is in the given parent - * @method hasParent - * @param {HTMLElement} node - * @param {HTMLElement} parent - * @return {Boolean} found - */ - hasParent: function hasParent(node, parent) { - while(node) { - if(node == parent) { - return true; - } - node = node.parentNode; - } - return false; - }, + Node.prototype._drawCircularImage = function (ctx) { + this._resizeCircularImage(ctx); - /** - * get the center of all the touches - * @method getCenter - * @param {Array} touches - * @return {Object} center contains `pageX`, `pageY`, `clientX` and `clientY` properties - */ - getCenter: function getCenter(touches) { - var pageX = [], - pageY = [], - clientX = [], - clientY = [], - min = Math.min, - max = Math.max; + this.left = this.x - this.width / 2; + this.top = this.y - this.height / 2; + + var centerX = this.left + (this.width / 2); + var centerY = this.top + (this.height / 2); + var radius = Math.abs(this.height / 2); - // no need to loop when only one touch - if(touches.length === 1) { - return { - pageX: touches[0].pageX, - pageY: touches[0].pageY, - clientX: touches[0].clientX, - clientY: touches[0].clientY - }; - } + this._drawRawCircle(ctx, centerX, centerY, radius); - Utils.each(touches, function(touch) { - pageX.push(touch.pageX); - pageY.push(touch.pageY); - clientX.push(touch.clientX); - clientY.push(touch.clientY); - }); + ctx.save(); + ctx.circle(this.x, this.y, radius); + ctx.stroke(); + ctx.clip(); - return { - pageX: (min.apply(Math, pageX) + max.apply(Math, pageX)) / 2, - pageY: (min.apply(Math, pageY) + max.apply(Math, pageY)) / 2, - clientX: (min.apply(Math, clientX) + max.apply(Math, clientX)) / 2, - clientY: (min.apply(Math, clientY) + max.apply(Math, clientY)) / 2 - }; - }, + this._drawImageAtPosition(ctx); - /** - * calculate the velocity between two points. unit is in px per ms. - * @method getVelocity - * @param {Number} deltaTime - * @param {Number} deltaX - * @param {Number} deltaY - * @return {Object} velocity `x` and `y` - */ - getVelocity: function getVelocity(deltaTime, deltaX, deltaY) { - return { - x: Math.abs(deltaX / deltaTime) || 0, - y: Math.abs(deltaY / deltaTime) || 0 - }; - }, + ctx.restore(); - /** - * calculate the angle between two coordinates - * @method getAngle - * @param {Touch} touch1 - * @param {Touch} touch2 - * @return {Number} angle - */ - getAngle: function getAngle(touch1, touch2) { - var x = touch2.clientX - touch1.clientX, - y = touch2.clientY - touch1.clientY; + this.boundingBox.top = this.y - this.options.radius; + this.boundingBox.left = this.x - this.options.radius; + this.boundingBox.right = this.x + this.options.radius; + this.boundingBox.bottom = this.y + this.options.radius; - return Math.atan2(y, x) * 180 / Math.PI; - }, + this._drawImageLabel(ctx); + + this.boundingBox.left = Math.min(this.boundingBox.left, this.labelDimensions.left); + this.boundingBox.right = Math.max(this.boundingBox.right, this.labelDimensions.left + this.labelDimensions.width); + this.boundingBox.bottom = Math.max(this.boundingBox.bottom, this.boundingBox.bottom + this.labelDimensions.height); + }; - /** - * do a small comparision to get the direction between two touches. - * @method getDirection - * @param {Touch} touch1 - * @param {Touch} touch2 - * @return {String} direction matches `DIRECTION_LEFT|RIGHT|UP|DOWN` - */ - getDirection: function getDirection(touch1, touch2) { - var x = Math.abs(touch1.clientX - touch2.clientX), - y = Math.abs(touch1.clientY - touch2.clientY); + Node.prototype._resizeBox = function (ctx) { + if (!this.width) { + var margin = 5; + var textSize = this.getTextSize(ctx); + this.width = textSize.width + 2 * margin; + this.height = textSize.height + 2 * margin; - if(x >= y) { - return touch1.clientX - touch2.clientX > 0 ? DIRECTION_LEFT : DIRECTION_RIGHT; - } - return touch1.clientY - touch2.clientY > 0 ? DIRECTION_UP : DIRECTION_DOWN; - }, + this.width += Math.min(this.clusterSize - 1, this.maxNodeSizeIncrements) * 0.5 * this.clusterSizeWidthFactor; + this.height += Math.min(this.clusterSize - 1, this.maxNodeSizeIncrements) * 0.5 * this.clusterSizeHeightFactor; + this.growthIndicator = this.width - (textSize.width + 2 * margin); + // this.options.radius+= Math.min(this.clusterSize - 1, this.maxNodeSizeIncrements) * 0.5 * this.clusterSizeRadiusFactor; - /** - * calculate the distance between two touches - * @method getDistance - * @param {Touch}touch1 - * @param {Touch} touch2 - * @return {Number} distance - */ - getDistance: function getDistance(touch1, touch2) { - var x = touch2.clientX - touch1.clientX, - y = touch2.clientY - touch1.clientY; + } + }; - return Math.sqrt((x * x) + (y * y)); - }, + Node.prototype._drawBox = function (ctx) { + this._resizeBox(ctx); - /** - * calculate the scale factor between two touchLists - * no scale is 1, and goes down to 0 when pinched together, and bigger when pinched out - * @method getScale - * @param {Array} start array of touches - * @param {Array} end array of touches - * @return {Number} scale - */ - getScale: function getScale(start, end) { - // need two fingers... - if(start.length >= 2 && end.length >= 2) { - return this.getDistance(end[0], end[1]) / this.getDistance(start[0], start[1]); - } - return 1; - }, + this.left = this.x - this.width / 2; + this.top = this.y - this.height / 2; - /** - * calculate the rotation degrees between two touchLists - * @method getRotation - * @param {Array} start array of touches - * @param {Array} end array of touches - * @return {Number} rotation - */ - getRotation: function getRotation(start, end) { - // need two fingers - if(start.length >= 2 && end.length >= 2) { - return this.getAngle(end[1], end[0]) - this.getAngle(start[1], start[0]); - } - return 0; - }, + var clusterLineWidth = 2.5; + var borderWidth = this.options.borderWidth; + var selectionLineWidth = this.options.borderWidthSelected || 2 * this.options.borderWidth; - /** - * find out if the direction is vertical * - * @method isVertical - * @param {String} direction matches `DIRECTION_UP|DOWN` - * @return {Boolean} is_vertical - */ - isVertical: function isVertical(direction) { - return direction == DIRECTION_UP || direction == DIRECTION_DOWN; - }, + ctx.strokeStyle = this.selected ? this.options.color.highlight.border : this.hover ? this.options.color.hover.border : this.options.color.border; - /** - * set css properties with their prefixes - * @param {HTMLElement} element - * @param {String} prop - * @param {String} value - * @param {Boolean} [toggle=true] - * @return {Boolean} - */ - setPrefixedCss: function setPrefixedCss(element, prop, value, toggle) { - var prefixes = ['', 'Webkit', 'Moz', 'O', 'ms']; - prop = Utils.toCamelCase(prop); + // draw the outer border + if (this.clusterSize > 1) { + ctx.lineWidth = (this.selected ? selectionLineWidth : borderWidth) + ((this.clusterSize > 1) ? clusterLineWidth : 0.0); + ctx.lineWidth *= this.networkScaleInv; + ctx.lineWidth = Math.min(this.width,ctx.lineWidth); - for(var i = 0; i < prefixes.length; i++) { - var p = prop; - // prefixes - if(prefixes[i]) { - p = prefixes[i] + p.slice(0, 1).toUpperCase() + p.slice(1); - } + ctx.roundRect(this.left-2*ctx.lineWidth, this.top-2*ctx.lineWidth, this.width+4*ctx.lineWidth, this.height+4*ctx.lineWidth, this.options.radius); + ctx.stroke(); + } + ctx.lineWidth = (this.selected ? selectionLineWidth : borderWidth) + ((this.clusterSize > 1) ? clusterLineWidth : 0.0); + ctx.lineWidth *= this.networkScaleInv; + ctx.lineWidth = Math.min(this.width,ctx.lineWidth); - // test the style - if(p in element.style) { - element.style[p] = (toggle == null || toggle) && value || ''; - break; - } - } - }, + ctx.fillStyle = this.selected ? this.options.color.highlight.background : this.hover ? this.options.color.hover.background : this.options.color.background; - /** - * toggle browser default behavior by setting css properties. - * `userSelect='none'` also sets `element.onselectstart` to false - * `userDrag='none'` also sets `element.ondragstart` to false - * - * @method toggleBehavior - * @param {HtmlElement} element - * @param {Object} props - * @param {Boolean} [toggle=true] - */ - toggleBehavior: function toggleBehavior(element, props, toggle) { - if(!props || !element || !element.style) { - return; - } + ctx.roundRect(this.left, this.top, this.width, this.height, this.options.radius); + ctx.fill(); + ctx.stroke(); - // set the css properties - Utils.each(props, function(value, prop) { - Utils.setPrefixedCss(element, prop, value, toggle); - }); + this.boundingBox.top = this.top; + this.boundingBox.left = this.left; + this.boundingBox.right = this.left + this.width; + this.boundingBox.bottom = this.top + this.height; - var falseFn = toggle && function() { - return false; - }; + this._label(ctx, this.label, this.x, this.y); + }; - // also the disable onselectstart - if(props.userSelect == 'none') { - element.onselectstart = falseFn; - } - // and disable ondragstart - if(props.userDrag == 'none') { - element.ondragstart = falseFn; - } - }, - /** - * convert a string with underscores to camelCase - * so prevent_default becomes preventDefault - * @param {String} str - * @return {String} camelCaseStr - */ - toCamelCase: function toCamelCase(str) { - return str.replace(/[_-]([a-z])/g, function(s) { - return s[1].toUpperCase(); - }); - } + Node.prototype._resizeDatabase = function (ctx) { + if (!this.width) { + var margin = 5; + var textSize = this.getTextSize(ctx); + var size = textSize.width + 2 * margin; + this.width = size; + this.height = size; + + // scaling used for clustering + this.width += Math.min(this.clusterSize - 1, this.maxNodeSizeIncrements) * this.clusterSizeWidthFactor; + this.height += Math.min(this.clusterSize - 1, this.maxNodeSizeIncrements) * this.clusterSizeHeightFactor; + this.options.radius+= Math.min(this.clusterSize - 1, this.maxNodeSizeIncrements) * this.clusterSizeRadiusFactor; + this.growthIndicator = this.width - size; + } }; + Node.prototype._drawDatabase = function (ctx) { + this._resizeDatabase(ctx); + this.left = this.x - this.width / 2; + this.top = this.y - this.height / 2; - /** - * @module hammer - */ - /** - * @class Event - * @static - */ - var Event = Hammer.event = { - /** - * when touch events have been fired, this is true - * this is used to stop mouse events - * @property prevent_mouseevents - * @private - * @type {Boolean} - */ - preventMouseEvents: false, + var clusterLineWidth = 2.5; + var borderWidth = this.options.borderWidth; + var selectionLineWidth = this.options.borderWidthSelected || 2 * this.options.borderWidth; - /** - * if EVENT_START has been fired - * @property started - * @private - * @type {Boolean} - */ - started: false, + ctx.strokeStyle = this.selected ? this.options.color.highlight.border : this.hover ? this.options.color.hover.border : this.options.color.border; - /** - * when the mouse is hold down, this is true - * @property should_detect - * @private - * @type {Boolean} - */ - shouldDetect: false, + // draw the outer border + if (this.clusterSize > 1) { + ctx.lineWidth = (this.selected ? selectionLineWidth : borderWidth) + ((this.clusterSize > 1) ? clusterLineWidth : 0.0); + ctx.lineWidth *= this.networkScaleInv; + ctx.lineWidth = Math.min(this.width,ctx.lineWidth); - /** - * simple event binder with a hook and support for multiple types - * @method on - * @param {HTMLElement} element - * @param {String} type - * @param {Function} handler - * @param {Function} [hook] - * @param {Object} hook.type - */ - on: function on(element, type, handler, hook) { - var types = type.split(' '); - Utils.each(types, function(type) { - Utils.on(element, type, handler); - hook && hook(type); - }); - }, - - /** - * simple event unbinder with a hook and support for multiple types - * @method off - * @param {HTMLElement} element - * @param {String} type - * @param {Function} handler - * @param {Function} [hook] - * @param {Object} hook.type - */ - off: function off(element, type, handler, hook) { - var types = type.split(' '); - Utils.each(types, function(type) { - Utils.off(element, type, handler); - hook && hook(type); - }); - }, - - /** - * the core touch event handler. - * this finds out if we should to detect gestures - * @method onTouch - * @param {HTMLElement} element - * @param {String} eventType matches `EVENT_START|MOVE|END` - * @param {Function} handler - * @return onTouchHandler {Function} the core event handler - */ - onTouch: function onTouch(element, eventType, handler) { - var self = this; + ctx.database(this.x - this.width/2 - 2*ctx.lineWidth, this.y - this.height*0.5 - 2*ctx.lineWidth, this.width + 4*ctx.lineWidth, this.height + 4*ctx.lineWidth); + ctx.stroke(); + } + ctx.lineWidth = (this.selected ? selectionLineWidth : borderWidth) + ((this.clusterSize > 1) ? clusterLineWidth : 0.0); + ctx.lineWidth *= this.networkScaleInv; + ctx.lineWidth = Math.min(this.width,ctx.lineWidth); - var onTouchHandler = function onTouchHandler(ev) { - var srcType = ev.type.toLowerCase(), - isPointer = Hammer.HAS_POINTEREVENTS, - isMouse = Utils.inStr(srcType, 'mouse'), - triggerType; + ctx.fillStyle = this.selected ? this.options.color.highlight.background : this.hover ? this.options.color.hover.background : this.options.color.background; + ctx.database(this.x - this.width/2, this.y - this.height*0.5, this.width, this.height); + ctx.fill(); + ctx.stroke(); - // if we are in a mouseevent, but there has been a touchevent triggered in this session - // we want to do nothing. simply break out of the event. - if(isMouse && self.preventMouseEvents) { - return; + this.boundingBox.top = this.top; + this.boundingBox.left = this.left; + this.boundingBox.right = this.left + this.width; + this.boundingBox.bottom = this.top + this.height; - // mousebutton must be down - } else if(isMouse && eventType == EVENT_START && ev.button === 0) { - self.preventMouseEvents = false; - self.shouldDetect = true; - } else if(isPointer && eventType == EVENT_START) { - self.shouldDetect = (ev.buttons === 1 || PointerEvent.matchType(POINTER_TOUCH, ev)); - // just a valid start event, but no mouse - } else if(!isMouse && eventType == EVENT_START) { - self.preventMouseEvents = true; - self.shouldDetect = true; - } + this._label(ctx, this.label, this.x, this.y); + }; - // update the pointer event before entering the detection - if(isPointer && eventType != EVENT_END) { - PointerEvent.updatePointer(eventType, ev); - } - // we are in a touch/down state, so allowed detection of gestures - if(self.shouldDetect) { - triggerType = self.doDetect.call(self, ev, eventType, element, handler); - } + Node.prototype._resizeCircle = function (ctx) { + if (!this.width) { + var margin = 5; + var textSize = this.getTextSize(ctx); + var diameter = Math.max(textSize.width, textSize.height) + 2 * margin; + this.options.radius = diameter / 2; - // ...and we are done with the detection - // so reset everything to start each detection totally fresh - if(triggerType == EVENT_END) { - self.preventMouseEvents = false; - self.shouldDetect = false; - PointerEvent.reset(); - // update the pointerevent object after the detection - } + this.width = diameter; + this.height = diameter; - if(isPointer && eventType == EVENT_END) { - PointerEvent.updatePointer(eventType, ev); - } - }; + // scaling used for clustering + // this.width += Math.min(this.clusterSize - 1, this.maxNodeSizeIncrements) * 0.5 * this.clusterSizeWidthFactor; + // this.height += Math.min(this.clusterSize - 1, this.maxNodeSizeIncrements) * 0.5 * this.clusterSizeHeightFactor; + this.options.radius += Math.min(this.clusterSize - 1, this.maxNodeSizeIncrements) * 0.5 * this.clusterSizeRadiusFactor; + this.growthIndicator = this.options.radius- 0.5*diameter; + } + }; - this.on(element, EVENT_TYPES[eventType], onTouchHandler); - return onTouchHandler; - }, + Node.prototype._drawRawCircle = function (ctx, x, y, radius) { + var clusterLineWidth = 2.5; + var borderWidth = this.options.borderWidth; + var selectionLineWidth = this.options.borderWidthSelected || 2 * this.options.borderWidth; + + ctx.strokeStyle = this.selected ? this.options.color.highlight.border : this.hover ? this.options.color.hover.border : this.options.color.border; - /** - * the core detection method - * this finds out what hammer-touch-events to trigger - * @method doDetect - * @param {Object} ev - * @param {String} eventType matches `EVENT_START|MOVE|END` - * @param {HTMLElement} element - * @param {Function} handler - * @return {String} triggerType matches `EVENT_START|MOVE|END` - */ - doDetect: function doDetect(ev, eventType, element, handler) { - var touchList = this.getTouchList(ev, eventType); - var touchListLength = touchList.length; - var triggerType = eventType; - var triggerChange = touchList.trigger; // used by fakeMultitouch plugin - var changedLength = touchListLength; + // draw the outer border + if (this.clusterSize > 1) { + ctx.lineWidth = (this.selected ? selectionLineWidth : borderWidth) + ((this.clusterSize > 1) ? clusterLineWidth : 0.0); + ctx.lineWidth *= this.networkScaleInv; + ctx.lineWidth = Math.min(this.width,ctx.lineWidth); - // at each touchstart-like event we want also want to trigger a TOUCH event... - if(eventType == EVENT_START) { - triggerChange = EVENT_TOUCH; - // ...the same for a touchend-like event - } else if(eventType == EVENT_END) { - triggerChange = EVENT_RELEASE; + ctx.circle(x, y, radius+2*ctx.lineWidth); + ctx.stroke(); + } + ctx.lineWidth = (this.selected ? selectionLineWidth : borderWidth) + ((this.clusterSize > 1) ? clusterLineWidth : 0.0); + ctx.lineWidth *= this.networkScaleInv; + ctx.lineWidth = Math.min(this.width,ctx.lineWidth); - // keep track of how many touches have been removed - changedLength = touchList.length - ((ev.changedTouches) ? ev.changedTouches.length : 1); - } + ctx.fillStyle = this.selected ? this.options.color.highlight.background : this.hover ? this.options.color.hover.background : this.options.color.background; + ctx.circle(this.x, this.y, radius); + ctx.fill(); + ctx.stroke(); + }; - // after there are still touches on the screen, - // we just want to trigger a MOVE event. so change the START or END to a MOVE - // but only after detection has been started, the first time we actualy want a START - if(changedLength > 0 && this.started) { - triggerType = EVENT_MOVE; - } + Node.prototype._drawCircle = function (ctx) { + this._resizeCircle(ctx); + this.left = this.x - this.width / 2; + this.top = this.y - this.height / 2; - // detection has been started, we keep track of this, see above - this.started = true; + this._drawRawCircle(ctx, this.x, this.y, this.options.radius); - // generate some event data, some basic information - var evData = this.collectEventData(element, triggerType, touchList, ev); + this.boundingBox.top = this.y - this.options.radius; + this.boundingBox.left = this.x - this.options.radius; + this.boundingBox.right = this.x + this.options.radius; + this.boundingBox.bottom = this.y + this.options.radius; - // trigger the triggerType event before the change (TOUCH, RELEASE) events - // but the END event should be at last - if(eventType != EVENT_END) { - handler.call(Detection, evData); - } + this._label(ctx, this.label, this.x, this.y); + }; - // trigger a change (TOUCH, RELEASE) event, this means the length of the touches changed - if(triggerChange) { - evData.changedLength = changedLength; - evData.eventType = triggerChange; + Node.prototype._resizeEllipse = function (ctx) { + if (!this.width) { + var textSize = this.getTextSize(ctx); - handler.call(Detection, evData); + this.width = textSize.width * 1.5; + this.height = textSize.height * 2; + if (this.width < this.height) { + this.width = this.height; + } + var defaultSize = this.width; - evData.eventType = triggerType; - delete evData.changedLength; - } + // scaling used for clustering + this.width += Math.min(this.clusterSize - 1, this.maxNodeSizeIncrements) * this.clusterSizeWidthFactor; + this.height += Math.min(this.clusterSize - 1, this.maxNodeSizeIncrements) * this.clusterSizeHeightFactor; + this.options.radius += Math.min(this.clusterSize - 1, this.maxNodeSizeIncrements) * this.clusterSizeRadiusFactor; + this.growthIndicator = this.width - defaultSize; + } + }; - // trigger the END event - if(triggerType == EVENT_END) { - handler.call(Detection, evData); + Node.prototype._drawEllipse = function (ctx) { + this._resizeEllipse(ctx); + this.left = this.x - this.width / 2; + this.top = this.y - this.height / 2; - // ...and we are done with the detection - // so reset everything to start each detection totally fresh - this.started = false; - } + var clusterLineWidth = 2.5; + var borderWidth = this.options.borderWidth; + var selectionLineWidth = this.options.borderWidthSelected || 2 * this.options.borderWidth; - return triggerType; - }, + ctx.strokeStyle = this.selected ? this.options.color.highlight.border : this.hover ? this.options.color.hover.border : this.options.color.border; - /** - * we have different events for each device/browser - * determine what we need and set them in the EVENT_TYPES constant - * the `onTouch` method is bind to these properties. - * @method determineEventTypes - * @return {Object} events - */ - determineEventTypes: function determineEventTypes() { - var types; - if(Hammer.HAS_POINTEREVENTS) { - if(window.PointerEvent) { - types = [ - 'pointerdown', - 'pointermove', - 'pointerup pointercancel lostpointercapture' - ]; - } else { - types = [ - 'MSPointerDown', - 'MSPointerMove', - 'MSPointerUp MSPointerCancel MSLostPointerCapture' - ]; - } - } else if(Hammer.NO_MOUSEEVENTS) { - types = [ - 'touchstart', - 'touchmove', - 'touchend touchcancel' - ]; - } else { - types = [ - 'touchstart mousedown', - 'touchmove mousemove', - 'touchend touchcancel mouseup' - ]; - } + // draw the outer border + if (this.clusterSize > 1) { + ctx.lineWidth = (this.selected ? selectionLineWidth : borderWidth) + ((this.clusterSize > 1) ? clusterLineWidth : 0.0); + ctx.lineWidth *= this.networkScaleInv; + ctx.lineWidth = Math.min(this.width,ctx.lineWidth); - EVENT_TYPES[EVENT_START] = types[0]; - EVENT_TYPES[EVENT_MOVE] = types[1]; - EVENT_TYPES[EVENT_END] = types[2]; - return EVENT_TYPES; - }, + ctx.ellipse(this.left-2*ctx.lineWidth, this.top-2*ctx.lineWidth, this.width+4*ctx.lineWidth, this.height+4*ctx.lineWidth); + ctx.stroke(); + } + ctx.lineWidth = (this.selected ? selectionLineWidth : borderWidth) + ((this.clusterSize > 1) ? clusterLineWidth : 0.0); + ctx.lineWidth *= this.networkScaleInv; + ctx.lineWidth = Math.min(this.width,ctx.lineWidth); - /** - * create touchList depending on the event - * @method getTouchList - * @param {Object} ev - * @param {String} eventType - * @return {Array} touches - */ - getTouchList: function getTouchList(ev, eventType) { - // get the fake pointerEvent touchlist - if(Hammer.HAS_POINTEREVENTS) { - return PointerEvent.getTouchList(); - } + ctx.fillStyle = this.selected ? this.options.color.highlight.background : this.hover ? this.options.color.hover.background : this.options.color.background; - // get the touchlist - if(ev.touches) { - if(eventType == EVENT_MOVE) { - return ev.touches; - } + ctx.ellipse(this.left, this.top, this.width, this.height); + ctx.fill(); + ctx.stroke(); - var identifiers = []; - var concat = [].concat(Utils.toArray(ev.touches), Utils.toArray(ev.changedTouches)); - var touchList = []; + this.boundingBox.top = this.top; + this.boundingBox.left = this.left; + this.boundingBox.right = this.left + this.width; + this.boundingBox.bottom = this.top + this.height; - Utils.each(concat, function(touch) { - if(Utils.inArray(identifiers, touch.identifier) === false) { - touchList.push(touch); - } - identifiers.push(touch.identifier); - }); + this._label(ctx, this.label, this.x, this.y); + }; - return touchList; - } + Node.prototype._drawDot = function (ctx) { + this._drawShape(ctx, 'circle'); + }; - // make fake touchList from mouse position - ev.identifier = 1; - return [ev]; - }, + Node.prototype._drawTriangle = function (ctx) { + this._drawShape(ctx, 'triangle'); + }; - /** - * collect basic event data - * @method collectEventData - * @param {HTMLElement} element - * @param {String} eventType matches `EVENT_START|MOVE|END` - * @param {Array} touches - * @param {Object} ev - * @return {Object} ev - */ - collectEventData: function collectEventData(element, eventType, touches, ev) { - // find out pointerType - var pointerType = POINTER_TOUCH; - if(Utils.inStr(ev.type, 'mouse') || PointerEvent.matchType(POINTER_MOUSE, ev)) { - pointerType = POINTER_MOUSE; - } else if(PointerEvent.matchType(POINTER_PEN, ev)) { - pointerType = POINTER_PEN; - } + Node.prototype._drawTriangleDown = function (ctx) { + this._drawShape(ctx, 'triangleDown'); + }; - return { - center: Utils.getCenter(touches), - timeStamp: Date.now(), - target: ev.target, - touches: touches, - eventType: eventType, - pointerType: pointerType, - srcEvent: ev, + Node.prototype._drawSquare = function (ctx) { + this._drawShape(ctx, 'square'); + }; - /** - * prevent the browser default actions - * mostly used to disable scrolling of the browser - */ - preventDefault: function() { - var srcEvent = this.srcEvent; - srcEvent.preventManipulation && srcEvent.preventManipulation(); - srcEvent.preventDefault && srcEvent.preventDefault(); - }, + Node.prototype._drawStar = function (ctx) { + this._drawShape(ctx, 'star'); + }; - /** - * stop bubbling the event up to its parents - */ - stopPropagation: function() { - this.srcEvent.stopPropagation(); - }, + Node.prototype._resizeShape = function (ctx) { + if (!this.width) { + this.options.radius= this.baseRadiusValue; + var size = 2 * this.options.radius; + this.width = size; + this.height = size; - /** - * immediately stop gesture detection - * might be useful after a swipe was detected - * @return {*} - */ - stopDetect: function() { - return Detection.stopDetect(); - } - }; - } + // scaling used for clustering + this.width += Math.min(this.clusterSize - 1, this.maxNodeSizeIncrements) * this.clusterSizeWidthFactor; + this.height += Math.min(this.clusterSize - 1, this.maxNodeSizeIncrements) * this.clusterSizeHeightFactor; + this.options.radius+= Math.min(this.clusterSize - 1, this.maxNodeSizeIncrements) * 0.5 * this.clusterSizeRadiusFactor; + this.growthIndicator = this.width - size; + } }; + Node.prototype._drawShape = function (ctx, shape) { + this._resizeShape(ctx); - /** - * @module hammer - * - * @class PointerEvent - * @static - */ - var PointerEvent = Hammer.PointerEvent = { - /** - * holds all pointers, by `identifier` - * @property pointers - * @type {Object} - */ - pointers: {}, + this.left = this.x - this.width / 2; + this.top = this.y - this.height / 2; - /** - * get the pointers as an array - * @method getTouchList - * @return {Array} touchlist - */ - getTouchList: function getTouchList() { - var touchlist = []; - // we can use forEach since pointerEvents only is in IE10 - Utils.each(this.pointers, function(pointer) { - touchlist.push(pointer); - }); - return touchlist; - }, + var clusterLineWidth = 2.5; + var borderWidth = this.options.borderWidth; + var selectionLineWidth = this.options.borderWidthSelected || 2 * this.options.borderWidth; + var radiusMultiplier = 2; - /** - * update the position of a pointer - * @method updatePointer - * @param {String} eventType matches `EVENT_START|MOVE|END` - * @param {Object} pointerEvent - */ - updatePointer: function updatePointer(eventType, pointerEvent) { - if(eventType == EVENT_END || (eventType != EVENT_END && pointerEvent.buttons !== 1)) { - delete this.pointers[pointerEvent.pointerId]; - } else { - pointerEvent.identifier = pointerEvent.pointerId; - this.pointers[pointerEvent.pointerId] = pointerEvent; - } - }, + // choose draw method depending on the shape + switch (shape) { + case 'dot': radiusMultiplier = 2; break; + case 'square': radiusMultiplier = 2; break; + case 'triangle': radiusMultiplier = 3; break; + case 'triangleDown': radiusMultiplier = 3; break; + case 'star': radiusMultiplier = 4; break; + } - /** - * check if ev matches pointertype - * @method matchType - * @param {String} pointerType matches `POINTER_MOUSE|TOUCH|PEN` - * @param {PointerEvent} ev - */ - matchType: function matchType(pointerType, ev) { - if(!ev.pointerType) { - return false; - } + ctx.strokeStyle = this.selected ? this.options.color.highlight.border : this.hover ? this.options.color.hover.border : this.options.color.border; + // draw the outer border + if (this.clusterSize > 1) { + ctx.lineWidth = (this.selected ? selectionLineWidth : borderWidth) + ((this.clusterSize > 1) ? clusterLineWidth : 0.0); + ctx.lineWidth *= this.networkScaleInv; + ctx.lineWidth = Math.min(this.width,ctx.lineWidth); - var pt = ev.pointerType, - types = {}; + ctx[shape](this.x, this.y, this.options.radius+ radiusMultiplier * ctx.lineWidth); + ctx.stroke(); + } + ctx.lineWidth = (this.selected ? selectionLineWidth : borderWidth) + ((this.clusterSize > 1) ? clusterLineWidth : 0.0); + ctx.lineWidth *= this.networkScaleInv; + ctx.lineWidth = Math.min(this.width,ctx.lineWidth); - types[POINTER_MOUSE] = (pt === (ev.MSPOINTER_TYPE_MOUSE || POINTER_MOUSE)); - types[POINTER_TOUCH] = (pt === (ev.MSPOINTER_TYPE_TOUCH || POINTER_TOUCH)); - types[POINTER_PEN] = (pt === (ev.MSPOINTER_TYPE_PEN || POINTER_PEN)); - return types[pointerType]; - }, + ctx.fillStyle = this.selected ? this.options.color.highlight.background : this.hover ? this.options.color.hover.background : this.options.color.background; + ctx[shape](this.x, this.y, this.options.radius); + ctx.fill(); + ctx.stroke(); - /** - * reset the stored pointers - * @method reset - */ - reset: function resetList() { - this.pointers = {}; - } + this.boundingBox.top = this.y - this.options.radius; + this.boundingBox.left = this.x - this.options.radius; + this.boundingBox.right = this.x + this.options.radius; + this.boundingBox.bottom = this.y + this.options.radius; + + if (this.label) { + this._label(ctx, this.label, this.x, this.y + this.height / 2, undefined, 'hanging',true); + this.boundingBox.left = Math.min(this.boundingBox.left, this.labelDimensions.left); + this.boundingBox.right = Math.max(this.boundingBox.right, this.labelDimensions.left + this.labelDimensions.width); + this.boundingBox.bottom = Math.max(this.boundingBox.bottom, this.boundingBox.bottom + this.labelDimensions.height); + } }; + Node.prototype._resizeText = function (ctx) { + if (!this.width) { + var margin = 5; + var textSize = this.getTextSize(ctx); + this.width = textSize.width + 2 * margin; + this.height = textSize.height + 2 * margin; - /** - * @module hammer - * - * @class Detection - * @static - */ - var Detection = Hammer.detection = { - // contains all registred Hammer.gestures in the correct order - gestures: [], + // scaling used for clustering + this.width += Math.min(this.clusterSize - 1, this.maxNodeSizeIncrements) * this.clusterSizeWidthFactor; + this.height += Math.min(this.clusterSize - 1, this.maxNodeSizeIncrements) * this.clusterSizeHeightFactor; + this.options.radius+= Math.min(this.clusterSize - 1, this.maxNodeSizeIncrements) * this.clusterSizeRadiusFactor; + this.growthIndicator = this.width - (textSize.width + 2 * margin); + } + }; - // data of the current Hammer.gesture detection session - current: null, + Node.prototype._drawText = function (ctx) { + this._resizeText(ctx); + this.left = this.x - this.width / 2; + this.top = this.y - this.height / 2; - // the previous Hammer.gesture session data - // is a full clone of the previous gesture.current object - previous: null, + this._label(ctx, this.label, this.x, this.y); - // when this becomes true, no gestures are fired - stopped: false, + this.boundingBox.top = this.top; + this.boundingBox.left = this.left; + this.boundingBox.right = this.left + this.width; + this.boundingBox.bottom = this.top + this.height; + }; - /** - * start Hammer.gesture detection - * @method startDetect - * @param {Hammer.Instance} inst - * @param {Object} eventData - */ - startDetect: function startDetect(inst, eventData) { - // already busy with a Hammer.gesture detection on an element - if(this.current) { - return; - } - this.stopped = false; + Node.prototype._label = function (ctx, text, x, y, align, baseline, labelUnderNode) { + if (text && Number(this.options.fontSize) * this.networkScale > this.fontDrawThreshold) { + ctx.font = (this.selected ? "bold " : "") + this.options.fontSize + "px " + this.options.fontFace; - // holds current session - this.current = { - inst: inst, // reference to HammerInstance we're working for - startEvent: Utils.extend({}, eventData), // start eventData for distances, timing etc - lastEvent: false, // last eventData - lastCalcEvent: false, // last eventData for calculations. - futureCalcEvent: false, // last eventData for calculations. - lastCalcData: {}, // last lastCalcData - name: '' // current gesture we're in/detected, can be 'tap', 'hold' etc - }; + var lines = text.split('\n'); + var lineCount = lines.length; + var fontSize = Number(this.options.fontSize); + var yLine = y + (1 - lineCount) / 2 * fontSize; + if (labelUnderNode == true) { + yLine = y + (1 - lineCount) / (2 * fontSize); + } - this.detect(eventData); - }, + // font fill from edges now for nodes! + var 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; + } + var height = this.options.fontSize * lineCount; + var left = x - width / 2; + var top = y - height / 2; + if (baseline == "hanging") { + top += 0.5 * fontSize; + top += 4; // distance from node, required because we use hanging. Hanging has less difference between browsers + yLine += 4; // distance from node + } + this.labelDimensions = {top:top,left:left,width:width,height:height,yLine:yLine}; - /** - * Hammer.gesture detection - * @method detect - * @param {Object} eventData - * @return {any} - */ - detect: function detect(eventData) { - if(!this.current || this.stopped) { - return; - } + // create the fontfill background + if (this.options.fontFill !== undefined && this.options.fontFill !== null && this.options.fontFill !== "none") { + ctx.fillStyle = this.options.fontFill; + ctx.fillRect(left, top, width, height); + } - // extend event data with calculations about scale, distance etc - eventData = this.extendEventData(eventData); + // draw text + ctx.fillStyle = this.options.fontColor || "black"; + ctx.textAlign = align || "center"; + ctx.textBaseline = baseline || "middle"; + if (this.options.fontStrokeWidth > 0){ + ctx.lineWidth = this.options.fontStrokeWidth; + ctx.strokeStyle = this.options.fontStrokeColor; + ctx.lineJoin = 'round'; + } + for (var i = 0; i < lineCount; i++) { + if(this.options.fontStrokeWidth){ + ctx.strokeText(lines[i], x, yLine); + } + ctx.fillText(lines[i], x, yLine); + yLine += fontSize; + } + } + }; - // hammer instance and instance options - var inst = this.current.inst, - instOptions = inst.options; - // call Hammer.gesture handlers - Utils.each(this.gestures, function triggerGesture(gesture) { - // only when the instance options have enabled this gesture - if(!this.stopped && inst.enabled && instOptions[gesture.name]) { - gesture.handler.call(gesture, eventData, inst); - } - }, this); + Node.prototype.getTextSize = function(ctx) { + if (this.label !== undefined) { + ctx.font = (this.selected ? "bold " : "") + this.options.fontSize + "px " + this.options.fontFace; - // store as previous event event - if(this.current) { - this.current.lastEvent = eventData; - } + var lines = this.label.split('\n'), + height = (Number(this.options.fontSize) + 4) * lines.length, + width = 0; - if(eventData.eventType == EVENT_END) { - this.stopDetect(); - } + for (var i = 0, iMax = lines.length; i < iMax; i++) { + width = Math.max(width, ctx.measureText(lines[i]).width); + } - return eventData; - }, + return {"width": width, "height": height, lineCount: lines.length}; + } + else { + return {"width": 0, "height": 0, lineCount: 0}; + } + }; - /** - * clear the Hammer.gesture vars - * this is called on endDetect, but can also be used when a final Hammer.gesture has been detected - * to stop other Hammer.gestures from being fired - * @method stopDetect - */ - stopDetect: function stopDetect() { - // clone current data to the store as the previous gesture - // used for the double tap gesture, since this is an other gesture detect session - this.previous = Utils.extend({}, this.current); + /** + * this is used to determine if a node is visible at all. this is used to determine when it needs to be drawn. + * there is a safety margin of 0.3 * width; + * + * @returns {boolean} + */ + Node.prototype.inArea = function() { + if (this.width !== undefined) { + return (this.x + this.width *this.networkScaleInv >= this.canvasTopLeft.x && + this.x - this.width *this.networkScaleInv < this.canvasBottomRight.x && + this.y + this.height*this.networkScaleInv >= this.canvasTopLeft.y && + this.y - this.height*this.networkScaleInv < this.canvasBottomRight.y); + } + else { + return true; + } + }; - // reset the current - this.current = null; - this.stopped = true; - }, + /** + * checks if the core of the node is in the display area, this is used for opening clusters around zoom + * @returns {boolean} + */ + Node.prototype.inView = function() { + return (this.x >= this.canvasTopLeft.x && + this.x < this.canvasBottomRight.x && + this.y >= this.canvasTopLeft.y && + this.y < this.canvasBottomRight.y); + }; - /** - * calculate velocity, angle and direction - * @method getVelocityData - * @param {Object} ev - * @param {Object} center - * @param {Number} deltaTime - * @param {Number} deltaX - * @param {Number} deltaY - */ - getCalculatedData: function getCalculatedData(ev, center, deltaTime, deltaX, deltaY) { - var cur = this.current, - recalc = false, - calcEv = cur.lastCalcEvent, - calcData = cur.lastCalcData; + /** + * This allows the zoom level of the network to influence the rendering + * We store the inverted scale and the coordinates of the top left, and bottom right points of the canvas + * + * @param scale + * @param canvasTopLeft + * @param canvasBottomRight + */ + Node.prototype.setScaleAndPos = function(scale,canvasTopLeft,canvasBottomRight) { + this.networkScaleInv = 1.0/scale; + this.networkScale = scale; + this.canvasTopLeft = canvasTopLeft; + this.canvasBottomRight = canvasBottomRight; + }; - if(calcEv && ev.timeStamp - calcEv.timeStamp > Hammer.CALCULATE_INTERVAL) { - center = calcEv.center; - deltaTime = ev.timeStamp - calcEv.timeStamp; - deltaX = ev.center.clientX - calcEv.center.clientX; - deltaY = ev.center.clientY - calcEv.center.clientY; - recalc = true; - } - if(ev.eventType == EVENT_TOUCH || ev.eventType == EVENT_RELEASE) { - cur.futureCalcEvent = ev; - } + /** + * This allows the zoom level of the network to influence the rendering + * + * @param scale + */ + Node.prototype.setScale = function(scale) { + this.networkScaleInv = 1.0/scale; + this.networkScale = scale; + }; - if(!cur.lastCalcEvent || recalc) { - calcData.velocity = Utils.getVelocity(deltaTime, deltaX, deltaY); - calcData.angle = Utils.getAngle(center, ev.center); - calcData.direction = Utils.getDirection(center, ev.center); - cur.lastCalcEvent = cur.futureCalcEvent || ev; - cur.futureCalcEvent = ev; - } - ev.velocityX = calcData.velocity.x; - ev.velocityY = calcData.velocity.y; - ev.interimAngle = calcData.angle; - ev.interimDirection = calcData.direction; - }, + /** + * set the velocity at 0. Is called when this node is contained in another during clustering + */ + Node.prototype.clearVelocity = function() { + this.vx = 0; + this.vy = 0; + }; - /** - * extend eventData for Hammer.gestures - * @method extendEventData - * @param {Object} ev - * @return {Object} ev - */ - extendEventData: function extendEventData(ev) { - var cur = this.current, - startEv = cur.startEvent, - lastEv = cur.lastEvent || startEv; - // update the start touchlist to calculate the scale/rotation - if(ev.eventType == EVENT_TOUCH || ev.eventType == EVENT_RELEASE) { - startEv.touches = []; - Utils.each(ev.touches, function(touch) { - startEv.touches.push({ - clientX: touch.clientX, - clientY: touch.clientY - }); - }); - } + /** + * Basic preservation of (kinectic) energy + * + * @param massBeforeClustering + */ + Node.prototype.updateVelocity = function(massBeforeClustering) { + var energyBefore = this.vx * this.vx * massBeforeClustering; + //this.vx = (this.vx < 0) ? -Math.sqrt(energyBefore/this.options.mass) : Math.sqrt(energyBefore/this.options.mass); + this.vx = Math.sqrt(energyBefore/this.options.mass); + energyBefore = this.vy * this.vy * massBeforeClustering; + //this.vy = (this.vy < 0) ? -Math.sqrt(energyBefore/this.options.mass) : Math.sqrt(energyBefore/this.options.mass); + this.vy = Math.sqrt(energyBefore/this.options.mass); + }; - var deltaTime = ev.timeStamp - startEv.timeStamp, - deltaX = ev.center.clientX - startEv.center.clientX, - deltaY = ev.center.clientY - startEv.center.clientY; + module.exports = Node; - this.getCalculatedData(ev, lastEv.center, deltaTime, deltaX, deltaY); - Utils.extend(ev, { - startEvent: startEv, +/***/ }, +/* 57 */ +/***/ function(module, exports, __webpack_require__) { - deltaTime: deltaTime, - deltaX: deltaX, - deltaY: deltaY, + var util = __webpack_require__(1); + var Node = __webpack_require__(56); - distance: Utils.getDistance(startEv.center, ev.center), - angle: Utils.getAngle(startEv.center, ev.center), - direction: Utils.getDirection(startEv.center, ev.center), - scale: Utils.getScale(startEv.touches, ev.touches), - rotation: Utils.getRotation(startEv.touches, ev.touches) - }); + /** + * @class Edge + * + * A edge connects two nodes + * @param {Object} properties Object with properties. Must contain + * At least properties from and to. + * Available properties: 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 + */ + function Edge (properties, network, networkConstants) { + if (!network) { + throw "No network provided"; + } + var fields = ['edges','physics']; + var constants = util.selectiveBridgeObject(fields,networkConstants); + this.options = constants.edges; + this.physics = constants.physics; + this.options['smoothCurves'] = networkConstants['smoothCurves']; - return ev; - }, - /** - * register new gesture - * @method register - * @param {Object} gesture object, see `gestures/` for documentation - * @return {Array} gestures - */ - register: function register(gesture) { - // add an enable gesture options if there is no given - var options = gesture.defaults || {}; - if(options[gesture.name] === undefined) { - options[gesture.name] = true; - } + this.network = network; - // extend Hammer default options with the Hammer.gesture options - Utils.extend(Hammer.defaults, options, true); + // initialize variables + this.id = undefined; + this.fromId = undefined; + this.toId = undefined; + this.title = undefined; + this.widthSelected = this.options.width * this.options.widthSelectionMultiplier; + this.value = undefined; + this.selected = false; + this.hover = false; + this.labelDimensions = {top:0,left:0,width:0,height:0,yLine:0}; // could be cached + this.dirtyLabel = true; - // set its index - gesture.index = gesture.index || 1000; + this.from = null; // a node + this.to = null; // a node + this.via = null; // a temp node - // add Hammer.gesture to the list - this.gestures.push(gesture); + this.fromBackup = null; // used to clean up after reconnect + this.toBackup = null;; // used to clean up after reconnect - // sort the list by index - this.gestures.sort(function(a, b) { - if(a.index < b.index) { - return -1; - } - if(a.index > b.index) { - return 1; - } - return 0; - }); + // we use this to be able to reconnect the edge to a cluster if its node is put into a cluster + // by storing the original information we can revert to the original connection when the cluser is opened. + this.originalFromId = []; + this.originalToId = []; - return this.gestures; - } - }; + this.connected = false; + this.widthFixed = false; + this.lengthFixed = false; - /** - * @module hammer - */ + this.setProperties(properties); + + this.controlNodesEnabled = false; + this.controlNodes = {from:null, to:null, positions:{}}; + this.connectedNode = null; + } /** - * create new hammer instance - * all methods should return the instance itself, so it is chainable. - * - * @class Instance - * @constructor - * @param {HTMLElement} element - * @param {Object} [options={}] options are merged with `Hammer.defaults` - * @return {Hammer.Instance} + * Set or overwrite properties for the edge + * @param {Object} properties an object with properties + * @param {Object} constants and object with default, global properties */ - Hammer.Instance = function(element, options) { - var self = this; - - // setup HammerJS window events and register all gestures - // this also sets up the default options - setup(); + Edge.prototype.setProperties = function(properties) { + if (!properties) { + return; + } - /** - * @property element - * @type {HTMLElement} - */ - this.element = element; + var fields = ['style','fontSize','fontFace','fontColor','fontFill','fontStrokeWidth','fontStrokeColor','width', + 'widthSelectionMultiplier','hoverWidth','arrowScaleFactor','dash','inheritColor','labelAlignment' + ]; + util.selectiveDeepExtend(fields, this.options, properties); - /** - * @property enabled - * @type {Boolean} - * @protected - */ - this.enabled = true; + if (properties.from !== undefined) {this.fromId = properties.from;} + if (properties.to !== undefined) {this.toId = properties.to;} - /** - * options, merged with the defaults - * options with an _ are converted to camelCase - * @property options - * @type {Object} - */ - Utils.each(options, function(value, name) { - delete options[name]; - options[Utils.toCamelCase(name)] = value; - }); + if (properties.id !== undefined) {this.id = properties.id;} + if (properties.label !== undefined) {this.label = properties.label; this.dirtyLabel = true;} - this.options = Utils.extend(Utils.extend({}, Hammer.defaults), options || {}); + if (properties.title !== undefined) {this.title = properties.title;} + if (properties.value !== undefined) {this.value = properties.value;} + if (properties.length !== undefined) {this.physics.springLength = properties.length;} - // add some css to the element to prevent the browser from doing its native behavoir - if(this.options.behavior) { - Utils.toggleBehavior(this.element, this.options.behavior, true); + if (properties.color !== undefined) { + this.options.inheritColor = false; + if (util.isString(properties.color)) { + this.options.color.color = properties.color; + this.options.color.highlight = properties.color; + } + else { + if (properties.color.color !== undefined) {this.options.color.color = properties.color.color;} + if (properties.color.highlight !== undefined) {this.options.color.highlight = properties.color.highlight;} + if (properties.color.hover !== undefined) {this.options.color.hover = properties.color.hover;} } + } - /** - * event start handler on the element to start the detection - * @property eventStartHandler - * @type {Object} - */ - this.eventStartHandler = Event.onTouch(element, EVENT_START, function(ev) { - if(self.enabled && ev.eventType == EVENT_START) { - Detection.startDetect(self, ev); - } else if(ev.eventType == EVENT_TOUCH) { - Detection.detect(ev); - } - }); + // A node is connected when it has a from and to node. + this.connect(); - /** - * keep a list of user event handlers which needs to be removed when calling 'dispose' - * @property eventHandlers - * @type {Array} - */ - this.eventHandlers = []; - }; + this.widthFixed = this.widthFixed || (properties.width !== undefined); + this.lengthFixed = this.lengthFixed || (properties.length !== undefined); - Hammer.Instance.prototype = { - /** - * bind events to the instance - * @method on - * @chainable - * @param {String} gestures multiple gestures by splitting with a space - * @param {Function} handler - * @param {Object} handler.ev event object - */ - on: function onEvent(gestures, handler) { - var self = this; - Event.on(self.element, gestures, handler, function(type) { - self.eventHandlers.push({ gesture: type, handler: handler }); - }); - return self; - }, + this.widthSelected = this.options.width* this.options.widthSelectionMultiplier; - /** - * unbind events to the instance - * @method off - * @chainable - * @param {String} gestures - * @param {Function} handler - */ - off: function offEvent(gestures, handler) { - var self = this; - - Event.off(self.element, gestures, handler, function(type) { - var index = Utils.inArray({ gesture: type, handler: handler }); - if(index !== false) { - self.eventHandlers.splice(index, 1); - } - }); - return self; - }, - - /** - * trigger gesture event - * @method trigger - * @chainable - * @param {String} gesture - * @param {Object} [eventData] - */ - trigger: function triggerEvent(gesture, eventData) { - // optional - if(!eventData) { - eventData = {}; - } - - // create DOM event - var event = Hammer.DOCUMENT.createEvent('Event'); - event.initEvent(gesture, true, true); - event.gesture = eventData; - - // trigger on the target if it is in the instance element, - // this is for event delegation tricks - var element = this.element; - if(Utils.hasParent(eventData.target, element)) { - element = eventData.target; - } - - element.dispatchEvent(event); - return this; - }, - - /** - * enable of disable hammer.js detection - * @method enable - * @chainable - * @param {Boolean} state - */ - enable: function enable(state) { - this.enabled = state; - return this; - }, - - /** - * dispose this hammer instance - * @method dispose - * @return {Null} - */ - dispose: function dispose() { - var i, eh; - - // undo all changes made by stop_browser_behavior - Utils.toggleBehavior(this.element, this.options.behavior, false); - - // unbind all custom event handlers - for(i = -1; (eh = this.eventHandlers[++i]);) { - Utils.off(this.element, eh.gesture, eh.handler); - } + // set draw method based on style + switch (this.options.style) { + case 'line': this.draw = this._drawLine; break; + case 'arrow': this.draw = this._drawArrow; break; + case 'arrow-center': this.draw = this._drawArrowCenter; break; + case 'dash-line': this.draw = this._drawDashLine; break; + default: this.draw = this._drawLine; break; + } + + }; - this.eventHandlers = []; + /** + * Connect an edge to its nodes + */ + Edge.prototype.connect = function () { + this.disconnect(); - // unbind the start event listener - Event.off(this.element, EVENT_TYPES[EVENT_START], this.eventStartHandler); + this.from = this.network.nodes[this.fromId] || null; + this.to = this.network.nodes[this.toId] || null; + this.connected = (this.from && this.to); - return null; + if (this.connected) { + this.from.attachEdge(this); + this.to.attachEdge(this); + } + else { + if (this.from) { + this.from.detachEdge(this); + } + if (this.to) { + this.to.detachEdge(this); } + } }; - - /** - * @module gestures - */ - /** - * Move with x fingers (default 1) around on the page. - * Preventing the default browser behavior is a good way to improve feel and working. - * ```` - * hammertime.on("drag", function(ev) { - * console.log(ev); - * ev.gesture.preventDefault(); - * }); - * ```` - * - * @class Drag - * @static - */ - /** - * @event drag - * @param {Object} ev - */ - /** - * @event dragstart - * @param {Object} ev - */ /** - * @event dragend - * @param {Object} ev + * Disconnect an edge from its nodes */ + Edge.prototype.disconnect = function () { + if (this.from) { + this.from.detachEdge(this); + this.from = null; + } + if (this.to) { + this.to.detachEdge(this); + this.to = null; + } + + this.connected = false; + }; + /** - * @event drapleft - * @param {Object} ev + * get the title of this edge. + * @return {string} title The title of the edge, or undefined when no title + * has been set. */ + Edge.prototype.getTitle = function() { + return typeof this.title === "function" ? this.title() : this.title; + }; + + /** - * @event dragright - * @param {Object} ev + * Retrieve the value of the edge. Can be undefined + * @return {Number} value */ + Edge.prototype.getValue = function() { + return this.value; + }; + /** - * @event dragup - * @param {Object} ev + * Adjust the value range of the edge. The edge will adjust it's width + * based on its value. + * @param {Number} min + * @param {Number} max */ + Edge.prototype.setValueRange = function(min, max) { + if (!this.widthFixed && this.value !== undefined) { + var scale = (this.options.widthMax - this.options.widthMin) / (max - min); + this.options.width= (this.value - min) * scale + this.options.widthMin; + this.widthSelected = this.options.width* this.options.widthSelectionMultiplier; + } + }; + /** - * @event dragdown - * @param {Object} ev + * 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 */ + Edge.prototype.draw = function(ctx) { + throw "Method draw not initialized in edge"; + }; /** - * @param {String} name + * 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 */ - (function(name) { - var triggered = false; - - function dragGesture(ev, inst) { - var cur = Detection.current; - - // max touches - if(inst.options.dragMaxTouches > 0 && - ev.touches.length > inst.options.dragMaxTouches) { - return; - } - - switch(ev.eventType) { - case EVENT_START: - triggered = false; - break; - - case EVENT_MOVE: - // when the distance we moved is too small we skip this gesture - // or we can be already in dragging - if(ev.distance < inst.options.dragMinDistance && - cur.name != name) { - return; - } - - var startCenter = cur.startEvent.center; - - // we are dragging! - if(cur.name != name) { - cur.name = name; - if(inst.options.dragDistanceCorrection && ev.distance > 0) { - // When a drag is triggered, set the event center to dragMinDistance pixels from the original event center. - // Without this correction, the dragged distance would jumpstart at dragMinDistance pixels instead of at 0. - // It might be useful to save the original start point somewhere - var factor = Math.abs(inst.options.dragMinDistance / ev.distance); - startCenter.pageX += ev.deltaX * factor; - startCenter.pageY += ev.deltaY * factor; - startCenter.clientX += ev.deltaX * factor; - startCenter.clientY += ev.deltaY * factor; - - // recalculate event data using new start point - ev = Detection.extendEventData(ev); - } - } + Edge.prototype.isOverlappingWith = function(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; - // lock drag to axis? - if(cur.lastEvent.dragLockToAxis || - ( inst.options.dragLockToAxis && - inst.options.dragLockMinDistance <= ev.distance - )) { - ev.dragLockToAxis = true; - } + var dist = this._getDistanceToEdge(xFrom, yFrom, xTo, yTo, xObj, yObj); - // keep direction on the axis that the drag gesture started on - var lastDirection = cur.lastEvent.direction; - if(ev.dragLockToAxis && lastDirection !== ev.direction) { - if(Utils.isVertical(lastDirection)) { - ev.direction = (ev.deltaY < 0) ? DIRECTION_UP : DIRECTION_DOWN; - } else { - ev.direction = (ev.deltaX < 0) ? DIRECTION_LEFT : DIRECTION_RIGHT; - } - } + return (dist < distMax); + } + else { + return false + } + }; - // first time, trigger dragstart event - if(!triggered) { - inst.trigger(name + 'start', ev); - triggered = true; - } + Edge.prototype._getColor = function() { + var colorObj = this.options.color; + if (this.options.inheritColor == "to") { + colorObj = { + highlight: this.to.options.color.highlight.border, + hover: this.to.options.color.hover.border, + color: this.to.options.color.border + }; + } + else if (this.options.inheritColor == "from" || this.options.inheritColor == true) { + colorObj = { + highlight: this.from.options.color.highlight.border, + hover: this.from.options.color.hover.border, + color: this.from.options.color.border + }; + } - // trigger events - inst.trigger(name, ev); - inst.trigger(name + ev.direction, ev); + if (this.selected == true) {return colorObj.highlight;} + else if (this.hover == true) {return colorObj.hover;} + else {return colorObj.color;} + }; - var isVertical = Utils.isVertical(ev.direction); - // block the browser events - if((inst.options.dragBlockVertical && isVertical) || - (inst.options.dragBlockHorizontal && !isVertical)) { - ev.preventDefault(); - } - break; + /** + * Redraw a edge as a line + * Draw this edge in the given canvas + * The 2d context of a HTML canvas can be retrieved by canvas.getContext("2d"); + * @param {CanvasRenderingContext2D} ctx + * @private + */ + Edge.prototype._drawLine = function(ctx) { + // set style + ctx.strokeStyle = this._getColor(); + ctx.lineWidth = this._getLineWidth(); - case EVENT_RELEASE: - if(triggered && ev.changedLength <= inst.options.dragMaxTouches) { - inst.trigger(name + 'end', ev); - triggered = false; - } - break; + if (this.from != this.to) { + // draw line + var via = this._line(ctx); - case EVENT_END: - triggered = false; - break; - } + // draw label + var point; + if (this.label) { + if (this.options.smoothCurves.enabled == true && via != null) { + var midpointX = 0.5*(0.5*(this.from.x + via.x) + 0.5*(this.to.x + via.x)); + var midpointY = 0.5*(0.5*(this.from.y + via.y) + 0.5*(this.to.y + via.y)); + point = {x:midpointX, y:midpointY}; + } + else { + point = this._pointOnLine(0.5); + } + this._label(ctx, this.label, point.x, point.y); } - - Hammer.gestures.Drag = { - name: name, - index: 50, - handler: dragGesture, - defaults: { - /** - * minimal movement that have to be made before the drag event gets triggered - * @property dragMinDistance - * @type {Number} - * @default 10 - */ - dragMinDistance: 10, - - /** - * Set dragDistanceCorrection to true to make the starting point of the drag - * be calculated from where the drag was triggered, not from where the touch started. - * Useful to avoid a jerk-starting drag, which can make fine-adjustments - * through dragging difficult, and be visually unappealing. - * @property dragDistanceCorrection - * @type {Boolean} - * @default true - */ - dragDistanceCorrection: true, - - /** - * set 0 for unlimited, but this can conflict with transform - * @property dragMaxTouches - * @type {Number} - * @default 1 - */ - dragMaxTouches: 1, - - /** - * prevent default browser behavior when dragging occurs - * be careful with it, it makes the element a blocking element - * when you are using the drag gesture, it is a good practice to set this true - * @property dragBlockHorizontal - * @type {Boolean} - * @default false - */ - dragBlockHorizontal: false, - - /** - * same as `dragBlockHorizontal`, but for vertical movement - * @property dragBlockVertical - * @type {Boolean} - * @default false - */ - dragBlockVertical: false, - - /** - * dragLockToAxis keeps the drag gesture on the axis that it started on, - * It disallows vertical directions if the initial direction was horizontal, and vice versa. - * @property dragLockToAxis - * @type {Boolean} - * @default false - */ - dragLockToAxis: false, - - /** - * drag lock only kicks in when distance > dragLockMinDistance - * This way, locking occurs only when the distance has become large enough to reliably determine the direction - * @property dragLockMinDistance - * @type {Number} - * @default 25 - */ - dragLockMinDistance: 25 - } - }; - })('drag'); - - /** - * @module gestures - */ - /** - * trigger a simple gesture event, so you can do anything in your handler. - * only usable if you know what your doing... - * - * @class Gesture - * @static - */ - /** - * @event gesture - * @param {Object} ev - */ - Hammer.gestures.Gesture = { - name: 'gesture', - index: 1337, - handler: function releaseGesture(ev, inst) { - inst.trigger(this.name, ev); + } + else { + var x, y; + var radius = this.physics.springLength / 4; + var node = this.from; + if (!node.width) { + node.resize(ctx); + } + if (node.width > node.height) { + x = node.x + node.width / 2; + y = node.y - radius; + } + else { + x = node.x + radius; + y = node.y - node.height / 2; } + this._circle(ctx, x, y, radius); + point = this._pointOnCircle(x, y, radius, 0.5); + this._label(ctx, this.label, point.x, point.y); + } }; /** - * @module gestures - */ - /** - * Touch stays at the same place for x time - * - * @class Hold - * @static - */ - /** - * @event hold - * @param {Object} ev - */ - - /** - * @param {String} name + * Get the line width of the edge. Depends on width and whether one of the + * connected nodes is selected. + * @return {Number} width + * @private */ - (function(name) { - var timer; - - function holdGesture(ev, inst) { - var options = inst.options, - current = Detection.current; - - switch(ev.eventType) { - case EVENT_START: - clearTimeout(timer); - - // set the gesture so we can check in the timeout if it still is - current.name = name; - - // set timer and if after the timeout it still is hold, - // we trigger the hold event - timer = setTimeout(function() { - if(current && current.name == name) { - inst.trigger(name, ev); - } - }, options.holdTimeout); - break; - - case EVENT_MOVE: - if(ev.distance > options.holdThreshold) { - clearTimeout(timer); - } - break; - - case EVENT_RELEASE: - clearTimeout(timer); - break; - } + Edge.prototype._getLineWidth = function() { + if (this.selected == true) { + return Math.max(Math.min(this.widthSelected, this.options.widthMax), 0.3*this.networkScaleInv); + } + else { + if (this.hover == true) { + return Math.max(Math.min(this.options.hoverWidth, this.options.widthMax), 0.3*this.networkScaleInv); } + else { + return Math.max(this.options.width, 0.3*this.networkScaleInv); + } + } + }; - Hammer.gestures.Hold = { - name: name, - index: 10, - defaults: { - /** - * @property holdTimeout - * @type {Number} - * @default 500 - */ - holdTimeout: 500, - - /** - * movement allowed while holding - * @property holdThreshold - * @type {Number} - * @default 2 - */ - holdThreshold: 2 - }, - handler: holdGesture - }; - })('hold'); + Edge.prototype._getViaCoordinates = function () { + if (this.options.smoothCurves.dynamic == true && this.options.smoothCurves.enabled == true ) { + return this.via; + } + else if (this.options.smoothCurves.enabled == false) { + return {x:0,y:0}; + } + else { + var xVia = null; + var yVia = null; + var factor = this.options.smoothCurves.roundness; + var type = this.options.smoothCurves.type; - /** - * @module gestures - */ - /** - * when a touch is being released from the page - * - * @class Release - * @static - */ - /** - * @event release - * @param {Object} ev - */ - Hammer.gestures.Release = { - name: 'release', - index: Infinity, - handler: function releaseGesture(ev, inst) { - if(ev.eventType == EVENT_RELEASE) { - inst.trigger(this.name, ev); + var dx = Math.abs(this.from.x - this.to.x); + var dy = Math.abs(this.from.y - this.to.y); + if (type == 'discrete' || type == 'diagonalCross') { + if (Math.abs(this.from.x - this.to.x) < Math.abs(this.from.y - this.to.y)) { + if (this.from.y > this.to.y) { + if (this.from.x < this.to.x) { + xVia = this.from.x + factor * dy; + yVia = this.from.y - factor * dy; + } + else if (this.from.x > this.to.x) { + xVia = this.from.x - factor * dy; + yVia = this.from.y - factor * dy; + } + } + else if (this.from.y < this.to.y) { + if (this.from.x < this.to.x) { + xVia = this.from.x + factor * dy; + yVia = this.from.y + factor * dy; + } + else if (this.from.x > this.to.x) { + xVia = this.from.x - factor * dy; + yVia = this.from.y + factor * dy; + } + } + if (type == "discrete") { + xVia = dx < factor * dy ? this.from.x : xVia; + } + } + else if (Math.abs(this.from.x - this.to.x) > Math.abs(this.from.y - this.to.y)) { + if (this.from.y > this.to.y) { + if (this.from.x < this.to.x) { + xVia = this.from.x + factor * dx; + yVia = this.from.y - factor * dx; + } + else if (this.from.x > this.to.x) { + xVia = this.from.x - factor * dx; + yVia = this.from.y - factor * dx; + } + } + else if (this.from.y < this.to.y) { + if (this.from.x < this.to.x) { + xVia = this.from.x + factor * dx; + yVia = this.from.y + factor * dx; + } + else if (this.from.x > this.to.x) { + xVia = this.from.x - factor * dx; + yVia = this.from.y + factor * dx; + } + } + if (type == "discrete") { + yVia = dy < factor * dx ? this.from.y : yVia; + } + } + } + else if (type == "straightCross") { + if (Math.abs(this.from.x - this.to.x) < Math.abs(this.from.y - this.to.y)) { // up - down + xVia = this.from.x; + if (this.from.y < this.to.y) { + yVia = this.to.y - (1 - factor) * dy; + } + else { + yVia = this.to.y + (1 - factor) * dy; + } + } + else if (Math.abs(this.from.x - this.to.x) > Math.abs(this.from.y - this.to.y)) { // left - right + if (this.from.x < this.to.x) { + xVia = this.to.x - (1 - factor) * dx; + } + else { + xVia = this.to.x + (1 - factor) * dx; + } + yVia = this.from.y; + } + } + else if (type == 'horizontal') { + if (this.from.x < this.to.x) { + xVia = this.to.x - (1 - factor) * dx; + } + else { + xVia = this.to.x + (1 - factor) * dx; + } + yVia = this.from.y; + } + else if (type == 'vertical') { + xVia = this.from.x; + if (this.from.y < this.to.y) { + yVia = this.to.y - (1 - factor) * dy; + } + else { + yVia = this.to.y + (1 - factor) * dy; + } + } + else { // continuous + if (Math.abs(this.from.x - this.to.x) < Math.abs(this.from.y - this.to.y)) { + if (this.from.y > this.to.y) { + if (this.from.x < this.to.x) { + // console.log(1) + xVia = this.from.x + factor * dy; + yVia = this.from.y - factor * dy; + xVia = this.to.x < xVia ? this.to.x : xVia; + } + else if (this.from.x > this.to.x) { + // console.log(2) + xVia = this.from.x - factor * dy; + yVia = this.from.y - factor * dy; + xVia = this.to.x > xVia ? this.to.x : xVia; + } + } + else if (this.from.y < this.to.y) { + if (this.from.x < this.to.x) { + // console.log(3) + xVia = this.from.x + factor * dy; + yVia = this.from.y + factor * dy; + xVia = this.to.x < xVia ? this.to.x : xVia; + } + else if (this.from.x > this.to.x) { + // console.log(4, this.from.x, this.to.x) + xVia = this.from.x - factor * dy; + yVia = this.from.y + factor * dy; + xVia = this.to.x > xVia ? this.to.x : xVia; + } + } + } + else if (Math.abs(this.from.x - this.to.x) > Math.abs(this.from.y - this.to.y)) { + if (this.from.y > this.to.y) { + if (this.from.x < this.to.x) { + // console.log(5) + xVia = this.from.x + factor * dx; + yVia = this.from.y - factor * dx; + yVia = this.to.y > yVia ? this.to.y : yVia; + } + else if (this.from.x > this.to.x) { + // console.log(6) + xVia = this.from.x - factor * dx; + yVia = this.from.y - factor * dx; + yVia = this.to.y > yVia ? this.to.y : yVia; + } + } + else if (this.from.y < this.to.y) { + if (this.from.x < this.to.x) { + // console.log(7) + xVia = this.from.x + factor * dx; + yVia = this.from.y + factor * dx; + yVia = this.to.y < yVia ? this.to.y : yVia; + } + else if (this.from.x > this.to.x) { + // console.log(8) + xVia = this.from.x - factor * dx; + yVia = this.from.y + factor * dx; + yVia = this.to.y < yVia ? this.to.y : yVia; + } } + } } + + + return {x: xVia, y: yVia}; + } }; /** - * @module gestures - */ - /** - * triggers swipe events when the end velocity is above the threshold - * for best usage, set `preventDefault` (on the drag gesture) to `true` - * ```` - * hammertime.on("dragleft swipeleft", function(ev) { - * console.log(ev); - * ev.gesture.preventDefault(); - * }); - * ```` - * - * @class Swipe - * @static - */ - /** - * @event swipe - * @param {Object} ev - */ - /** - * @event swipeleft - * @param {Object} ev - */ - /** - * @event swiperight - * @param {Object} ev + * Draw a line between two nodes + * @param {CanvasRenderingContext2D} ctx + * @private */ + Edge.prototype._line = function (ctx) { + // draw a straight line + ctx.beginPath(); + ctx.moveTo(this.from.x, this.from.y); + if (this.options.smoothCurves.enabled == true) { + if (this.options.smoothCurves.dynamic == false) { + var via = this._getViaCoordinates(); + if (via.x == null) { + ctx.lineTo(this.to.x, this.to.y); + ctx.stroke(); + return null; + } + else { + // this.via.x = via.x; + // this.via.y = via.y; + ctx.quadraticCurveTo(via.x,via.y,this.to.x, this.to.y); + ctx.stroke(); + return via; + } + } + else { + ctx.quadraticCurveTo(this.via.x,this.via.y,this.to.x, this.to.y); + ctx.stroke(); + return this.via; + } + } + else { + ctx.lineTo(this.to.x, this.to.y); + ctx.stroke(); + return null; + } + }; + /** - * @event swipeup - * @param {Object} ev + * Draw a line from a node to itself, a circle + * @param {CanvasRenderingContext2D} ctx + * @param {Number} x + * @param {Number} y + * @param {Number} radius + * @private */ + Edge.prototype._circle = function (ctx, x, y, radius) { + // draw a circle + ctx.beginPath(); + ctx.arc(x, y, radius, 0, 2 * Math.PI, false); + ctx.stroke(); + }; + /** - * @event swipedown - * @param {Object} ev + * Draw label with white background and with the middle at (x, y) + * @param {CanvasRenderingContext2D} ctx + * @param {String} text + * @param {Number} x + * @param {Number} y + * @private */ - Hammer.gestures.Swipe = { - name: 'swipe', - index: 40, - defaults: { - /** - * @property swipeMinTouches - * @type {Number} - * @default 1 - */ - swipeMinTouches: 1, - - /** - * @property swipeMaxTouches - * @type {Number} - * @default 1 - */ - swipeMaxTouches: 1, + Edge.prototype._label = function (ctx, text, x, y) { + if (text) { + ctx.font = ((this.from.selected || this.to.selected) ? "bold " : "") + + this.options.fontSize + "px " + this.options.fontFace; + var yLine; - /** - * horizontal swipe velocity - * @property swipeVelocityX - * @type {Number} - * @default 0.6 - */ - swipeVelocityX: 0.6, + if (this.dirtyLabel == true) { + var lines = String(text).split('\n'); + var lineCount = lines.length; + var fontSize = Number(this.options.fontSize); + yLine = y + (1 - lineCount) / 2 * fontSize; - /** - * vertical swipe velocity - * @property swipeVelocityY - * @type {Number} - * @default 0.6 - */ - swipeVelocityY: 0.6 - }, + var 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; + } + var height = this.options.fontSize * lineCount; + var left = x - width / 2; + var top = y - height / 2; - handler: function swipeGesture(ev, inst) { - if(ev.eventType == EVENT_RELEASE) { - var touches = ev.touches.length, - options = inst.options; + // cache + this.labelDimensions = {top:top,left:left,width:width,height:height,yLine:yLine}; + } - // max touches - if(touches < options.swipeMinTouches || - touches > options.swipeMaxTouches) { - return; - } + var yLine = this.labelDimensions.yLine; + + ctx.save(); + + if (this.options.labelAlignment != "horizontal"){ + ctx.translate(x, yLine); + this._rotateForLabelAlignment(ctx); + x = 0; + yLine = 0; + } - // when the distance we moved is too small we skip this gesture - // or we can be already in dragging - if(ev.velocityX > options.swipeVelocityX || - ev.velocityY > options.swipeVelocityY) { - // trigger swipe events - inst.trigger(this.name, ev); - inst.trigger(this.name + ev.direction, ev); - } - } - } + + this._drawLabelRect(ctx); + this._drawLabelText(ctx,x,yLine, lines, lineCount, fontSize); + + ctx.restore(); + } }; /** - * @module gestures - */ - /** - * Single tap and a double tap on a place - * - * @class Tap - * @static + * Rotates the canvas so the text is most readable + * @param {CanvasRenderingContext2D} ctx + * @private */ + Edge.prototype._rotateForLabelAlignment = function(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); + }; + /** - * @event tap - * @param {Object} ev + * Draws the label rectangle + * @param {CanvasRenderingContext2D} ctx + * @param {String} labelAlignment + * @private */ + Edge.prototype._drawLabelRect = function(ctx) { + if (this.options.fontFill !== undefined && this.options.fontFill !== null && this.options.fontFill !== "none") { + ctx.fillStyle = this.options.fontFill; + + var lineMargin = 2; + + if (this.options.labelAlignment == 'line-center') { + ctx.fillRect(-this.labelDimensions.width * 0.5, -this.labelDimensions.height * 0.5, this.labelDimensions.width, this.labelDimensions.height); + } + else if (this.options.labelAlignment == 'line-above') { + ctx.fillRect(-this.labelDimensions.width * 0.5, -(this.labelDimensions.height + lineMargin), this.labelDimensions.width, this.labelDimensions.height); + } + else if (this.options.labelAlignment == 'line-below') { + ctx.fillRect(-this.labelDimensions.width * 0.5, lineMargin, this.labelDimensions.width, this.labelDimensions.height); + } + else { + ctx.fillRect(this.labelDimensions.left, this.labelDimensions.top, this.labelDimensions.width, this.labelDimensions.height); + } + } + }; + /** - * @event doubletap - * @param {Object} ev + * Draws the label text + * @param {CanvasRenderingContext2D} ctx + * @param {Number} x + * @param {Number} yLine + * @param {Array} lines + * @param {Number} lineCount + * @param {Number} fontSize + * @private */ + Edge.prototype._drawLabelText = function(ctx, x, yLine, lines, lineCount, fontSize) { + // draw text + ctx.fillStyle = this.options.fontColor || "black"; + ctx.textAlign = "center"; + + // check for label alignment + if (this.options.labelAlignment != 'horizontal') { + var lineMargin = 2; + if (this.options.labelAlignment == 'line-above') { + ctx.textBaseline = "alphabetic"; + yLine -= 2 * lineMargin; // distance from edge, required because we use alphabetic. Alphabetic has less difference between browsers + } + else if (this.options.labelAlignment == 'line-below') { + 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 = "middle"; + } + + // check for strokeWidth + if (this.options.fontStrokeWidth > 0){ + ctx.lineWidth = this.options.fontStrokeWidth; + ctx.strokeStyle = this.options.fontStrokeColor; + ctx.lineJoin = 'round'; + } + for (var i = 0; i < lineCount; i++) { + if(this.options.fontStrokeWidth > 0){ + ctx.strokeText(lines[i], x, yLine); + } + ctx.fillText(lines[i], x, yLine); + yLine += fontSize; + } + }; /** - * @param {String} name + * Redraw a edge as a dashed line + * Draw this edge in the given canvas + * @author David Jordan + * @date 2012-08-08 + * The 2d context of a HTML canvas can be retrieved by canvas.getContext("2d"); + * @param {CanvasRenderingContext2D} ctx + * @private */ - (function(name) { - var hasMoved = false; + Edge.prototype._drawDashLine = function(ctx) { + // set style + ctx.strokeStyle = this._getColor(); + ctx.lineWidth = this._getLineWidth(); - function tapGesture(ev, inst) { - var options = inst.options, - current = Detection.current, - prev = Detection.previous, - sincePrev, - didDoubleTap; + var via = null; + // only firefox and chrome support this method, else we use the legacy one. + if (ctx.setLineDash !== undefined) { + ctx.save(); + // configure the dash pattern + var pattern = [0]; + if (this.options.dash.length !== undefined && this.options.dash.gap !== undefined) { + pattern = [this.options.dash.length,this.options.dash.gap]; + } + else { + pattern = [5,5]; + } - switch(ev.eventType) { - case EVENT_START: - hasMoved = false; - break; + // set dash settings for chrome or firefox + ctx.setLineDash(pattern); + ctx.lineDashOffset = 0; - case EVENT_MOVE: - hasMoved = hasMoved || (ev.distance > options.tapMaxDistance); - break; - - case EVENT_END: - if(!Utils.inStr(ev.srcEvent.type, 'cancel') && ev.deltaTime < options.tapMaxTime && !hasMoved) { - // previous gesture, for the double tap since these are two different gesture detections - sincePrev = prev && prev.lastEvent && ev.timeStamp - prev.lastEvent.timeStamp; - didDoubleTap = false; - - // check if double tap - if(prev && prev.name == name && - (sincePrev && sincePrev < options.doubleTapInterval) && - ev.distance < options.doubleTapDistance) { - inst.trigger('doubletap', ev); - didDoubleTap = true; - } + // draw the line + via = this._line(ctx); - // do a single tap - if(!didDoubleTap || options.tapAlways) { - current.name = name; - inst.trigger(current.name, ev); - } - } - break; - } + // restore the dash settings. + ctx.setLineDash([0]); + ctx.lineDashOffset = 0; + ctx.restore(); + } + else { // unsupporting smooth lines + // draw dashed line + ctx.beginPath(); + ctx.lineCap = 'round'; + if (this.options.dash.altLength !== undefined) //If an alt dash value has been set add to the array this value + { + ctx.dashedLine(this.from.x,this.from.y,this.to.x,this.to.y, + [this.options.dash.length,this.options.dash.gap,this.options.dash.altLength,this.options.dash.gap]); } + else if (this.options.dash.length !== undefined && this.options.dash.gap !== undefined) //If a dash and gap value has been set add to the array this value + { + ctx.dashedLine(this.from.x,this.from.y,this.to.x,this.to.y, + [this.options.dash.length,this.options.dash.gap]); + } + else //If all else fails draw a line + { + ctx.moveTo(this.from.x, this.from.y); + ctx.lineTo(this.to.x, this.to.y); + } + ctx.stroke(); + } - Hammer.gestures.Tap = { - name: name, - index: 100, - handler: tapGesture, - defaults: { - /** - * max time of a tap, this is for the slow tappers - * @property tapMaxTime - * @type {Number} - * @default 250 - */ - tapMaxTime: 250, - - /** - * max distance of movement of a tap, this is for the slow tappers - * @property tapMaxDistance - * @type {Number} - * @default 10 - */ - tapMaxDistance: 10, - - /** - * always trigger the `tap` event, even while double-tapping - * @property tapAlways - * @type {Boolean} - * @default true - */ - tapAlways: true, - - /** - * max distance between two taps - * @property doubleTapDistance - * @type {Number} - * @default 20 - */ - doubleTapDistance: 20, - - /** - * max time between two taps - * @property doubleTapInterval - * @type {Number} - * @default 300 - */ - doubleTapInterval: 300 - } - }; - })('tap'); - - /** - * @module gestures - */ - /** - * when a touch is being touched at the page - * - * @class Touch - * @static - */ - /** - * @event touch - * @param {Object} ev - */ - Hammer.gestures.Touch = { - name: 'touch', - index: -Infinity, - defaults: { - /** - * call preventDefault at touchstart, and makes the element blocking by disabling the scrolling of the page, - * but it improves gestures like transforming and dragging. - * be careful with using this, it can be very annoying for users to be stuck on the page - * @property preventDefault - * @type {Boolean} - * @default false - */ - preventDefault: false, - - /** - * disable mouse events, so only touch (or pen!) input triggers events - * @property preventMouse - * @type {Boolean} - * @default false - */ - preventMouse: false - }, - handler: function touchGesture(ev, inst) { - if(inst.options.preventMouse && ev.pointerType == POINTER_MOUSE) { - ev.stopDetect(); - return; - } - - if(inst.options.preventDefault) { - ev.preventDefault(); - } - - if(ev.eventType == EVENT_TOUCH) { - inst.trigger('touch', ev); - } + // draw label + if (this.label) { + var point; + if (this.options.smoothCurves.enabled == true && via != null) { + var midpointX = 0.5*(0.5*(this.from.x + via.x) + 0.5*(this.to.x + via.x)); + var midpointY = 0.5*(0.5*(this.from.y + via.y) + 0.5*(this.to.y + via.y)); + point = {x:midpointX, y:midpointY}; + } + else { + point = this._pointOnLine(0.5); } + this._label(ctx, this.label, point.x, point.y); + } }; /** - * @module gestures - */ - /** - * User want to scale or rotate with 2 fingers - * Preventing the default browser behavior is a good way to improve feel and working. This can be done with the - * `preventDefault` option. - * - * @class Transform - * @static - */ - /** - * @event transform - * @param {Object} ev - */ - /** - * @event transformstart - * @param {Object} ev - */ - /** - * @event transformend - * @param {Object} ev - */ - /** - * @event pinchin - * @param {Object} ev - */ - /** - * @event pinchout - * @param {Object} ev + * Get a point on a line + * @param {Number} percentage. Value between 0 (line start) and 1 (line end) + * @return {Object} point + * @private */ + Edge.prototype._pointOnLine = function (percentage) { + return { + x: (1 - percentage) * this.from.x + percentage * this.to.x, + y: (1 - percentage) * this.from.y + percentage * this.to.y + } + }; + /** - * @event rotate - * @param {Object} ev + * 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 */ + Edge.prototype._pointOnCircle = function (x, y, radius, percentage) { + var angle = (percentage - 3/8) * 2 * Math.PI; + return { + x: x + radius * Math.cos(angle), + y: y - radius * Math.sin(angle) + } + }; /** - * @param {String} name + * Redraw a edge as a line with an arrow halfway the line + * Draw this edge in the given canvas + * The 2d context of a HTML canvas can be retrieved by canvas.getContext("2d"); + * @param {CanvasRenderingContext2D} ctx + * @private */ - (function(name) { - var triggered = false; - - function transformGesture(ev, inst) { - switch(ev.eventType) { - case EVENT_START: - triggered = false; - break; - - case EVENT_MOVE: - // at least multitouch - if(ev.touches.length < 2) { - return; - } - - var scaleThreshold = Math.abs(1 - ev.scale); - var rotationThreshold = Math.abs(ev.rotation); - - // when the distance we moved is too small we skip this gesture - // or we can be already in dragging - if(scaleThreshold < inst.options.transformMinScale && - rotationThreshold < inst.options.transformMinRotation) { - return; - } + Edge.prototype._drawArrowCenter = function(ctx) { + var point; + // set style + ctx.strokeStyle = this._getColor(); + ctx.fillStyle = ctx.strokeStyle; + ctx.lineWidth = this._getLineWidth(); - // we are transforming! - Detection.current.name = name; + if (this.from != this.to) { + // draw line + var via = this._line(ctx); - // first time, trigger dragstart event - if(!triggered) { - inst.trigger(name + 'start', ev); - triggered = true; - } + var angle = Math.atan2((this.to.y - this.from.y), (this.to.x - this.from.x)); + var length = (10 + 5 * this.options.width) * this.options.arrowScaleFactor; + // draw an arrow halfway the line + if (this.options.smoothCurves.enabled == true && via != null) { + var midpointX = 0.5*(0.5*(this.from.x + via.x) + 0.5*(this.to.x + via.x)); + var midpointY = 0.5*(0.5*(this.from.y + via.y) + 0.5*(this.to.y + via.y)); + point = {x:midpointX, y:midpointY}; + } + else { + point = this._pointOnLine(0.5); + } - inst.trigger(name, ev); // basic transform event + ctx.arrow(point.x, point.y, angle, length); + ctx.fill(); + ctx.stroke(); - // trigger rotate event - if(rotationThreshold > inst.options.transformMinRotation) { - inst.trigger('rotate', ev); - } + // draw label + if (this.label) { + this._label(ctx, this.label, point.x, point.y); + } + } + else { + // draw circle + var x, y; + var radius = 0.25 * Math.max(100,this.physics.springLength); + var node = this.from; + if (!node.width) { + node.resize(ctx); + } + if (node.width > node.height) { + x = node.x + node.width * 0.5; + y = node.y - radius; + } + else { + x = node.x + radius; + y = node.y - node.height * 0.5; + } + this._circle(ctx, x, y, radius); - // trigger pinch event - if(scaleThreshold > inst.options.transformMinScale) { - inst.trigger('pinch', ev); - inst.trigger('pinch' + (ev.scale < 1 ? 'in' : 'out'), ev); - } - break; + // draw all arrows + var angle = 0.2 * Math.PI; + var length = (10 + 5 * this.options.width) * this.options.arrowScaleFactor; + point = this._pointOnCircle(x, y, radius, 0.5); + ctx.arrow(point.x, point.y, angle, length); + ctx.fill(); + ctx.stroke(); - case EVENT_RELEASE: - if(triggered && ev.changedLength < 2) { - inst.trigger(name + 'end', ev); - triggered = false; - } - break; - } + // draw label + if (this.label) { + point = this._pointOnCircle(x, y, radius, 0.5); + this._label(ctx, this.label, point.x, point.y); } + } + }; - Hammer.gestures.Transform = { - name: name, - index: 45, - defaults: { - /** - * minimal scale factor, no scale is 1, zoomin is to 0 and zoomout until higher then 1 - * @property transformMinScale - * @type {Number} - * @default 0.01 - */ - transformMinScale: 0.01, + Edge.prototype._pointOnBezier = function(t) { + var via = this._getViaCoordinates(); - /** - * rotation in degrees - * @property transformMinRotation - * @type {Number} - * @default 1 - */ - transformMinRotation: 1 - }, + var x = Math.pow(1-t,2)*this.from.x + (2*t*(1 - t))*via.x + Math.pow(t,2)*this.to.x; + var y = Math.pow(1-t,2)*this.from.y + (2*t*(1 - t))*via.y + Math.pow(t,2)*this.to.y; - handler: transformGesture - }; - })('transform'); + return {x:x,y:y}; + } /** - * @module hammer + * This function uses binary search to look for the point where the bezier curve crosses the border of the node. + * + * @param from + * @param ctx + * @returns {*} + * @private */ + Edge.prototype._findBorderPosition = function(from,ctx) { + var maxIterations = 10; + var iteration = 0; + var low = 0; + var high = 1; + var pos,angle,distanceToBorder, distanceToNodes, difference; + var threshold = 0.2; + var node = this.to; + if (from == true) { + node = this.from; + } - // AMD export - if(true) { - !(__WEBPACK_AMD_DEFINE_RESULT__ = function() { - return Hammer; - }.call(exports, __webpack_require__, exports, module), __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__)); - // commonjs export - } else if(typeof module !== 'undefined' && module.exports) { - module.exports = Hammer; - // browser export - } else { - window.Hammer = Hammer; - } - - })(window); - -/***/ }, -/* 65 */ -/***/ function(module, exports, __webpack_require__) { - - var __WEBPACK_AMD_DEFINE_RESULT__;/* WEBPACK VAR INJECTION */(function(global, module) {//! moment.js - //! version : 2.9.0 - //! authors : Tim Wood, Iskren Chernev, Moment.js contributors - //! license : MIT - //! momentjs.com + while (low <= high && iteration < maxIterations) { + var middle = (low + high) * 0.5; - (function (undefined) { - /************************************ - Constants - ************************************/ + pos = this._pointOnBezier(middle); + angle = Math.atan2((node.y - pos.y), (node.x - pos.x)); + distanceToBorder = node.distanceToBorder(ctx,angle); + distanceToNodes = Math.sqrt(Math.pow(pos.x-node.x,2) + Math.pow(pos.y-node.y,2)); + difference = distanceToBorder - distanceToNodes; + if (Math.abs(difference) < threshold) { + break; // found + } + else if (difference < 0) { // distance to nodes is larger than distance to border --> t needs to be bigger if we're looking at the to node. + if (from == false) { + low = middle; + } + else { + high = middle; + } + } + else { + if (from == false) { + high = middle; + } + else { + low = middle; + } + } - var moment, - VERSION = '2.9.0', - // the global-scope this is NOT the global object in Node.js - globalScope = (typeof global !== 'undefined' && (typeof window === 'undefined' || window === global.window)) ? global : this, - oldGlobalMoment, - round = Math.round, - hasOwnProperty = Object.prototype.hasOwnProperty, - i, + iteration++; + } + pos.t = middle; - YEAR = 0, - MONTH = 1, - DATE = 2, - HOUR = 3, - MINUTE = 4, - SECOND = 5, - MILLISECOND = 6, + return pos; + }; - // internal storage for locale config files - locales = {}, + /** + * Redraw a edge as a line with an arrow + * Draw this edge in the given canvas + * The 2d context of a HTML canvas can be retrieved by canvas.getContext("2d"); + * @param {CanvasRenderingContext2D} ctx + * @private + */ + Edge.prototype._drawArrow = function(ctx) { + // set style + ctx.strokeStyle = this._getColor(); + ctx.fillStyle = ctx.strokeStyle; + ctx.lineWidth = this._getLineWidth(); - // extra moment internal properties (plugins register props here) - momentProperties = [], + // set vars + var angle, length, arrowPos; - // check for nodeJS - hasModule = (typeof module !== 'undefined' && module && module.exports), + // if not connected to itself + if (this.from != this.to) { + // draw line + this._line(ctx); - // ASP.NET json date format regex - aspNetJsonRegex = /^\/?Date\((\-?\d+)/i, - aspNetTimeSpanJsonRegex = /(\-)?(?:(\d*)\.)?(\d+)\:(\d+)(?:\:(\d+)\.?(\d{3})?)?/, + // draw arrow head + if (this.options.smoothCurves.enabled == true) { + var via = this._getViaCoordinates(); + arrowPos = this._findBorderPosition(false, ctx); + var guidePos = this._pointOnBezier(Math.max(0.0, arrowPos.t - 0.1)) + angle = Math.atan2((arrowPos.y - guidePos.y), (arrowPos.x - guidePos.x)); + } + else { + angle = Math.atan2((this.to.y - this.from.y), (this.to.x - this.from.x)); + var dx = (this.to.x - this.from.x); + var dy = (this.to.y - this.from.y); + var edgeSegmentLength = Math.sqrt(dx * dx + dy * dy); + var toBorderDist = this.to.distanceToBorder(ctx, angle); + var toBorderPoint = (edgeSegmentLength - toBorderDist) / edgeSegmentLength; - // from http://docs.closure-library.googlecode.com/git/closure_goog_date_date.js.source.html - // somewhat more in line with 4.4.3.2 2004 spec, but allows decimal anywhere - isoDurationRegex = /^(-)?P(?:(?:([0-9,.]*)Y)?(?:([0-9,.]*)M)?(?:([0-9,.]*)D)?(?:T(?:([0-9,.]*)H)?(?:([0-9,.]*)M)?(?:([0-9,.]*)S)?)?|([0-9,.]*)W)$/, + arrowPos = {}; + arrowPos.x = (1 - toBorderPoint) * this.from.x + toBorderPoint * this.to.x; + arrowPos.y = (1 - toBorderPoint) * this.from.y + toBorderPoint * this.to.y; + } - // format tokens - formattingTokens = /(\[[^\[]*\])|(\\)?(Mo|MM?M?M?|Do|DDDo|DD?D?D?|ddd?d?|do?|w[o|w]?|W[o|W]?|Q|YYYYYY|YYYYY|YYYY|YY|gg(ggg?)?|GG(GGG?)?|e|E|a|A|hh?|HH?|mm?|ss?|S{1,4}|x|X|zz?|ZZ?|.)/g, - localFormattingTokens = /(\[[^\[]*\])|(\\)?(LTS|LT|LL?L?L?|l{1,4})/g, + // draw arrow at the end of the line + length = (10 + 5 * this.options.width) * this.options.arrowScaleFactor; + ctx.arrow(arrowPos.x,arrowPos.y, angle, length); + ctx.fill(); + ctx.stroke(); - // parsing token regexes - parseTokenOneOrTwoDigits = /\d\d?/, // 0 - 99 - parseTokenOneToThreeDigits = /\d{1,3}/, // 0 - 999 - parseTokenOneToFourDigits = /\d{1,4}/, // 0 - 9999 - parseTokenOneToSixDigits = /[+\-]?\d{1,6}/, // -999,999 - 999,999 - parseTokenDigits = /\d+/, // nonzero number of digits - parseTokenWord = /[0-9]*['a-z\u00A0-\u05FF\u0700-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]+|[\u0600-\u06FF\/]+(\s*?[\u0600-\u06FF]+){1,2}/i, // any word (or two) characters or numbers including two/three word month in arabic. - parseTokenTimezone = /Z|[\+\-]\d\d:?\d\d/gi, // +00:00 -00:00 +0000 -0000 or Z - parseTokenT = /T/i, // T (ISO separator) - parseTokenOffsetMs = /[\+\-]?\d+/, // 1234567890123 - parseTokenTimestampMs = /[\+\-]?\d+(\.\d{1,3})?/, // 123456789 123456789.123 + // draw label + if (this.label) { + var point; + if (this.options.smoothCurves.enabled == true && via != null) { + point = this._pointOnBezier(0.5); + } + else { + point = this._pointOnLine(0.5); + } + this._label(ctx, this.label, point.x, point.y); + } + } + else { + // draw circle + var node = this.from; + var x, y, arrow; + var radius = 0.25 * Math.max(100,this.physics.springLength); + if (!node.width) { + node.resize(ctx); + } + if (node.width > node.height) { + x = node.x + node.width * 0.5; + y = node.y - radius; + arrow = { + x: x, + y: node.y, + angle: 0.9 * Math.PI + }; + } + else { + x = node.x + radius; + y = node.y - node.height * 0.5; + arrow = { + x: node.x, + y: y, + angle: 0.6 * Math.PI + }; + } + ctx.beginPath(); + // TODO: similarly, for a line without arrows, draw to the border of the nodes instead of the center + ctx.arc(x, y, radius, 0, 2 * Math.PI, false); + ctx.stroke(); + + // draw all arrows + var length = (10 + 5 * this.options.width) * this.options.arrowScaleFactor; + ctx.arrow(arrow.x, arrow.y, arrow.angle, length); + ctx.fill(); + ctx.stroke(); + + // draw label + if (this.label) { + point = this._pointOnCircle(x, y, radius, 0.5); + this._label(ctx, this.label, point.x, point.y); + } + } + }; + + /** + * Calculate the distance between a point (x3,y3) and a line segment from + * (x1,y1) to (x2,y2). + * http://stackoverflow.com/questions/849211/shortest-distancae-between-a-point-and-a-line-segment + * @param {number} x1 + * @param {number} y1 + * @param {number} x2 + * @param {number} y2 + * @param {number} x3 + * @param {number} y3 + * @private + */ + Edge.prototype._getDistanceToEdge = function (x1,y1, x2,y2, x3,y3) { // x3,y3 is the point + var returnValue = 0; + if (this.from != this.to) { + if (this.options.smoothCurves.enabled == true) { + var xVia, yVia; + if (this.options.smoothCurves.enabled == true && this.options.smoothCurves.dynamic == true) { + xVia = this.via.x; + yVia = this.via.y; + } + else { + var via = this._getViaCoordinates(); + xVia = via.x; + yVia = via.y; + } + var minDistance = 1e9; + var distance; + var i,t,x,y, lastX, lastY; + for (i = 0; i < 10; i++) { + t = 0.1*i; + x = Math.pow(1-t,2)*x1 + (2*t*(1 - t))*xVia + Math.pow(t,2)*x2; + y = Math.pow(1-t,2)*y1 + (2*t*(1 - t))*yVia + Math.pow(t,2)*y2; + if (i > 0) { + distance = this._getDistanceToLine(lastX,lastY,x,y, x3,y3); + minDistance = distance < minDistance ? distance : minDistance; + } + lastX = x; lastY = y; + } + returnValue = minDistance; + } + else { + returnValue = this._getDistanceToLine(x1,y1,x2,y2,x3,y3); + } + } + else { + var x, y, dx, dy; + var radius = 0.25 * this.physics.springLength; + var node = this.from; + if (node.width > node.height) { + x = node.x + 0.5 * node.width; + y = node.y - radius; + } + else { + x = node.x + radius; + y = node.y - 0.5 * node.height; + } + dx = x - x3; + dy = y - y3; + returnValue = Math.abs(Math.sqrt(dx*dx + dy*dy) - radius); + } + + if (this.labelDimensions.left < x3 && + this.labelDimensions.left + this.labelDimensions.width > x3 && + this.labelDimensions.top < y3 && + this.labelDimensions.top + this.labelDimensions.height > y3) { + return 0; + } + else { + return returnValue; + } + }; + + Edge.prototype._getDistanceToLine = function(x1,y1,x2,y2,x3,y3) { + var px = x2-x1, + py = y2-y1, + something = px*px + py*py, + u = ((x3 - x1) * px + (y3 - y1) * py) / something; + + if (u > 1) { + u = 1; + } + else if (u < 0) { + u = 0; + } + + var x = x1 + u * px, + y = y1 + u * py, + dx = x - x3, + dy = y - y3; + + //# Note: If the actual distance does not matter, + //# if you only want to compare what this function + //# returns to other results of this function, you + //# can just return the squared distance instead + //# (i.e. remove the sqrt) to gain a little performance + + return Math.sqrt(dx*dx + dy*dy); + }; + + /** + * This allows the zoom level of the network to influence the rendering + * + * @param scale + */ + Edge.prototype.setScale = function(scale) { + this.networkScaleInv = 1.0/scale; + }; + + + Edge.prototype.select = function() { + this.selected = true; + }; + + Edge.prototype.unselect = function() { + this.selected = false; + }; + + Edge.prototype.positionBezierNode = function() { + if (this.via !== null && this.from !== null && this.to !== null) { + 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 !== null) { + this.via.x = 0; + this.via.y = 0; + } + }; + + /** + * This function draws the control nodes for the manipulator. + * In order to enable this, only set the this.controlNodesEnabled to true. + * @param ctx + */ + Edge.prototype._drawControlNodes = function(ctx) { + if (this.controlNodesEnabled == true) { + if (this.controlNodes.from === null && this.controlNodes.to === null) { + var nodeIdFrom = "edgeIdFrom:".concat(this.id); + var nodeIdTo = "edgeIdTo:".concat(this.id); + var constants = { + nodes:{group:'', radius:7, borderWidth:2, borderWidthSelected: 2}, + physics:{damping:0}, + clustering: {maxNodeSizeIncrements: 0 ,nodeScaling: {width:0, height: 0, radius:0}} + }; + this.controlNodes.from = new Node( + {id:nodeIdFrom, + shape:'dot', + color:{background:'#ff0000', border:'#3c3c3c', highlight: {background:'#07f968'}} + },{},{},constants); + this.controlNodes.to = new Node( + {id:nodeIdTo, + shape:'dot', + color:{background:'#ff0000', border:'#3c3c3c', highlight: {background:'#07f968'}} + },{},{},constants); + } + + this.controlNodes.positions = {}; + if (this.controlNodes.from.selected == false) { + this.controlNodes.positions.from = this.getControlNodeFromPosition(ctx); + this.controlNodes.from.x = this.controlNodes.positions.from.x; + this.controlNodes.from.y = this.controlNodes.positions.from.y; + } + if (this.controlNodes.to.selected == false) { + this.controlNodes.positions.to = this.getControlNodeToPosition(ctx); + this.controlNodes.to.x = this.controlNodes.positions.to.x; + this.controlNodes.to.y = this.controlNodes.positions.to.y; + } + + this.controlNodes.from.draw(ctx); + this.controlNodes.to.draw(ctx); + } + else { + this.controlNodes = {from:null, to:null, positions:{}}; + } + }; + + /** + * Enable control nodes. + * @private + */ + Edge.prototype._enableControlNodes = function() { + this.fromBackup = this.from; + this.toBackup = this.to; + this.controlNodesEnabled = true; + }; + + /** + * disable control nodes and remove from dynamicEdges from old node + * @private + */ + Edge.prototype._disableControlNodes = function() { + this.fromId = this.from.id; + this.toId = this.to.id; + if (this.fromId != this.fromBackup.id) { // from was changed, remove edge from old 'from' node dynamic edges + this.fromBackup.detachEdge(this); + } + else if (this.toId != this.toBackup.id) { // to was changed, remove edge from old 'to' node dynamic edges + this.toBackup.detachEdge(this); + } + + this.fromBackup = null; + this.toBackup = null; + this.controlNodesEnabled = false; + }; + + + /** + * This checks if one of the control nodes is selected and if so, returns the control node object. Else it returns null. + * @param x + * @param y + * @returns {null} + * @private + */ + Edge.prototype._getSelectedControlNode = function(x,y) { + var positions = this.controlNodes.positions; + var fromDistance = Math.sqrt(Math.pow(x - positions.from.x,2) + Math.pow(y - positions.from.y,2)); + var toDistance = Math.sqrt(Math.pow(x - positions.to.x ,2) + Math.pow(y - positions.to.y ,2)); + + if (fromDistance < 15) { + this.connectedNode = this.from; + this.from = this.controlNodes.from; + return this.controlNodes.from; + } + else if (toDistance < 15) { + this.connectedNode = this.to; + this.to = this.controlNodes.to; + return this.controlNodes.to; + } + else { + return null; + } + }; + + + /** + * this resets the control nodes to their original position. + * @private + */ + Edge.prototype._restoreControlNodes = function() { + if (this.controlNodes.from.selected == true) { + this.from = this.connectedNode; + this.connectedNode = null; + this.controlNodes.from.unselect(); + } + else if (this.controlNodes.to.selected == true) { + this.to = this.connectedNode; + this.connectedNode = null; + this.controlNodes.to.unselect(); + } + }; + + /** + * this calculates the position of the control nodes on the edges of the parent nodes. + * + * @param ctx + * @returns {x: *, y: *} + */ + Edge.prototype.getControlNodeFromPosition = function(ctx) { + // draw arrow head + var controlnodeFromPos; + if (this.options.smoothCurves.enabled == true) { + controlnodeFromPos = this._findBorderPosition(true, ctx); + } + else { + var angle = Math.atan2((this.to.y - this.from.y), (this.to.x - this.from.x)); + var dx = (this.to.x - this.from.x); + var dy = (this.to.y - this.from.y); + var edgeSegmentLength = Math.sqrt(dx * dx + dy * dy); + + var fromBorderDist = this.from.distanceToBorder(ctx, angle + Math.PI); + var fromBorderPoint = (edgeSegmentLength - fromBorderDist) / edgeSegmentLength; + controlnodeFromPos = {}; + controlnodeFromPos.x = (fromBorderPoint) * this.from.x + (1 - fromBorderPoint) * this.to.x; + controlnodeFromPos.y = (fromBorderPoint) * this.from.y + (1 - fromBorderPoint) * this.to.y; + } + + return controlnodeFromPos; + }; + + /** + * this calculates the position of the control nodes on the edges of the parent nodes. + * + * @param ctx + * @returns {{from: {x: number, y: number}, to: {x: *, y: *}}} + */ + Edge.prototype.getControlNodeToPosition = function(ctx) { + // draw arrow head + var controlnodeFromPos,controlnodeToPos; + if (this.options.smoothCurves.enabled == true) { + controlnodeToPos = this._findBorderPosition(false, ctx); + } + else { + var angle = Math.atan2((this.to.y - this.from.y), (this.to.x - this.from.x)); + var dx = (this.to.x - this.from.x); + var dy = (this.to.y - this.from.y); + var edgeSegmentLength = Math.sqrt(dx * dx + dy * dy); + var toBorderDist = this.to.distanceToBorder(ctx, angle); + var toBorderPoint = (edgeSegmentLength - toBorderDist) / edgeSegmentLength; + + controlnodeToPos = {}; + controlnodeToPos.x = (1 - toBorderPoint) * this.from.x + toBorderPoint * this.to.x; + controlnodeToPos.y = (1 - toBorderPoint) * this.from.y + toBorderPoint * this.to.y; + } + + return controlnodeToPos; + }; + + module.exports = Edge; + +/***/ }, +/* 58 */ +/***/ function(module, exports, __webpack_require__) { + + /** + * 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 Popup(container, x, y, text, style) { + if (container) { + this.container = container; + } + else { + this.container = document.body; + } + + // x, y and text are optional, see if a style object was passed in their place + if (style === undefined) { + if (typeof x === "object") { + style = x; + x = undefined; + } else if (typeof text === "object") { + style = text; + text = undefined; + } else { + // for backwards compatibility, in case clients other than Network are creating Popup directly + style = { + fontColor: 'black', + fontSize: 14, // px + fontFace: 'verdana', + color: { + border: '#666', + background: '#FFFFC6' + } + } + } + } + + this.x = 0; + this.y = 0; + this.padding = 5; + + if (x !== undefined && y !== undefined ) { + this.setPosition(x, y); + } + if (text !== undefined) { + this.setText(text); + } + + // create the frame + this.frame = document.createElement("div"); + var styleAttr = this.frame.style; + styleAttr.position = "absolute"; + styleAttr.visibility = "hidden"; + styleAttr.border = "1px solid " + style.color.border; + styleAttr.color = style.fontColor; + styleAttr.fontSize = style.fontSize + "px"; + styleAttr.fontFamily = style.fontFace; + styleAttr.padding = this.padding + "px"; + styleAttr.backgroundColor = style.color.background; + styleAttr.borderRadius = "3px"; + styleAttr.MozBorderRadius = "3px"; + styleAttr.WebkitBorderRadius = "3px"; + styleAttr.boxShadow = "3px 3px 10px rgba(128, 128, 128, 0.5)"; + styleAttr.whiteSpace = "nowrap"; + this.container.appendChild(this.frame); + } + + /** + * @param {number} x Horizontal position of the popup window + * @param {number} y Vertical position of the popup window + */ + Popup.prototype.setPosition = function(x, y) { + this.x = parseInt(x); + this.y = parseInt(y); + }; + + /** + * Set the content for the popup window. This can be HTML code or text. + * @param {string | Element} content + */ + Popup.prototype.setText = function(content) { + if (content instanceof Element) { + this.frame.innerHTML = ''; + this.frame.appendChild(content); + } + else { + this.frame.innerHTML = content; // string containing text or HTML + } + }; + + /** + * Show the popup window + * @param {boolean} show Optional. Show or hide the window + */ + Popup.prototype.show = function (show) { + if (show === undefined) { + show = true; + } + + if (show) { + 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"; + } + else { + this.hide(); + } + }; + + /** + * Hide the popup window + */ + Popup.prototype.hide = function () { + this.frame.style.visibility = "hidden"; + }; + + module.exports = Popup; + + +/***/ }, +/* 59 */ +/***/ function(module, exports, __webpack_require__) { + + var PhysicsMixin = __webpack_require__(60); + var ClusterMixin = __webpack_require__(64); + var SectorsMixin = __webpack_require__(65); + var SelectionMixin = __webpack_require__(66); + var ManipulationMixin = __webpack_require__(67); + var NavigationMixin = __webpack_require__(68); + var HierarchicalLayoutMixin = __webpack_require__(69); + + /** + * Load a mixin into the network object + * + * @param {Object} sourceVariable | this object has to contain functions. + * @private + */ + exports._loadMixin = function (sourceVariable) { + for (var mixinFunction in sourceVariable) { + if (sourceVariable.hasOwnProperty(mixinFunction)) { + this[mixinFunction] = sourceVariable[mixinFunction]; + } + } + }; + + + /** + * removes a mixin from the network object. + * + * @param {Object} sourceVariable | this object has to contain functions. + * @private + */ + exports._clearMixin = function (sourceVariable) { + for (var mixinFunction in sourceVariable) { + if (sourceVariable.hasOwnProperty(mixinFunction)) { + this[mixinFunction] = undefined; + } + } + }; + + + /** + * Mixin the physics system and initialize the parameters required. + * + * @private + */ + exports._loadPhysicsSystem = function () { + this._loadMixin(PhysicsMixin); + this._loadSelectedForceSolver(); + if (this.constants.configurePhysics == true) { + this._loadPhysicsConfiguration(); + } + else { + this._cleanupPhysicsConfiguration(); + } + }; + + + /** + * Mixin the cluster system and initialize the parameters required. + * + * @private + */ + exports._loadClusterSystem = function () { + this.clusterSession = 0; + this.hubThreshold = 5; + this._loadMixin(ClusterMixin); + }; + + + /** + * Mixin the sector system and initialize the parameters required + * + * @private + */ + exports._loadSectorSystem = function () { + this.sectors = {}; + this.activeSector = ["default"]; + this.sectors["active"] = {}; + this.sectors["active"]["default"] = {"nodes": {}, + "edges": {}, + "nodeIndices": [], + "formationScale": 1.0, + "drawingNode": undefined }; + this.sectors["frozen"] = {}; + this.sectors["support"] = {"nodes": {}, + "edges": {}, + "nodeIndices": [], + "formationScale": 1.0, + "drawingNode": undefined }; + + this.nodeIndices = this.sectors["active"]["default"]["nodeIndices"]; // the node indices list is used to speed up the computation of the repulsion fields + + this._loadMixin(SectorsMixin); + }; + + + /** + * Mixin the selection system and initialize the parameters required + * + * @private + */ + exports._loadSelectionSystem = function () { + this.selectionObj = {nodes: {}, edges: {}}; + + this._loadMixin(SelectionMixin); + }; + + + /** + * Mixin the navigationUI (User Interface) system and initialize the parameters required + * + * @private + */ + exports._loadManipulationSystem = function () { + // reset global variables -- these are used by the selection of nodes and edges. + this.blockConnectingEdgeSelection = false; + this.forceAppendSelection = false; + + if (this.constants.dataManipulation.enabled == true) { + // load the manipulator HTML elements. All styling done in css. + if (this.manipulationDiv === undefined) { + this.manipulationDiv = document.createElement('div'); + this.manipulationDiv.className = 'network-manipulationDiv'; + if (this.editMode == true) { + this.manipulationDiv.style.display = "block"; + } + else { + this.manipulationDiv.style.display = "none"; + } + this.frame.appendChild(this.manipulationDiv); + } + + if (this.editModeDiv === undefined) { + this.editModeDiv = document.createElement('div'); + this.editModeDiv.className = 'network-manipulation-editMode'; + if (this.editMode == true) { + this.editModeDiv.style.display = "none"; + } + else { + this.editModeDiv.style.display = "block"; + } + this.frame.appendChild(this.editModeDiv); + } + + if (this.closeDiv === undefined) { + this.closeDiv = document.createElement('div'); + this.closeDiv.className = 'network-manipulation-closeDiv'; + this.closeDiv.style.display = this.manipulationDiv.style.display; + this.frame.appendChild(this.closeDiv); + } + + // load the manipulation functions + this._loadMixin(ManipulationMixin); + + // create the manipulator toolbar + this._createManipulatorBar(); + } + else { + if (this.manipulationDiv !== undefined) { + // removes all the bindings and overloads + this._createManipulatorBar(); + + // remove the manipulation divs + this.frame.removeChild(this.manipulationDiv); + this.frame.removeChild(this.editModeDiv); + this.frame.removeChild(this.closeDiv); + + this.manipulationDiv = undefined; + this.editModeDiv = undefined; + this.closeDiv = undefined; + // remove the mixin functions + this._clearMixin(ManipulationMixin); + } + } + }; + + + /** + * Mixin the navigation (User Interface) system and initialize the parameters required + * + * @private + */ + exports._loadNavigationControls = function () { + this._loadMixin(NavigationMixin); + // the clean function removes the button divs, this is done to remove the bindings. + this._cleanNavigation(); + if (this.constants.navigation.enabled == true) { + this._loadNavigationElements(); + } + }; + + + /** + * Mixin the hierarchical layout system. + * + * @private + */ + exports._loadHierarchySystem = function () { + this._loadMixin(HierarchicalLayoutMixin); + }; + + +/***/ }, +/* 60 */ +/***/ function(module, exports, __webpack_require__) { + + var util = __webpack_require__(1); + var RepulsionMixin = __webpack_require__(61); + var HierarchialRepulsionMixin = __webpack_require__(62); + var BarnesHutMixin = __webpack_require__(63); + + /** + * Toggling barnes Hut calculation on and off. + * + * @private + */ + exports._toggleBarnesHut = function () { + this.constants.physics.barnesHut.enabled = !this.constants.physics.barnesHut.enabled; + this._loadSelectedForceSolver(); + this.moving = true; + this.start(); + }; + + + /** + * This loads the node force solver based on the barnes hut or repulsion algorithm + * + * @private + */ + exports._loadSelectedForceSolver = function () { + // this overloads the this._calculateNodeForces + if (this.constants.physics.barnesHut.enabled == true) { + this._clearMixin(RepulsionMixin); + this._clearMixin(HierarchialRepulsionMixin); + + this.constants.physics.centralGravity = this.constants.physics.barnesHut.centralGravity; + this.constants.physics.springLength = this.constants.physics.barnesHut.springLength; + this.constants.physics.springConstant = this.constants.physics.barnesHut.springConstant; + this.constants.physics.damping = this.constants.physics.barnesHut.damping; + + this._loadMixin(BarnesHutMixin); + } + else if (this.constants.physics.hierarchicalRepulsion.enabled == true) { + this._clearMixin(BarnesHutMixin); + this._clearMixin(RepulsionMixin); + + this.constants.physics.centralGravity = this.constants.physics.hierarchicalRepulsion.centralGravity; + this.constants.physics.springLength = this.constants.physics.hierarchicalRepulsion.springLength; + this.constants.physics.springConstant = this.constants.physics.hierarchicalRepulsion.springConstant; + this.constants.physics.damping = this.constants.physics.hierarchicalRepulsion.damping; + + this._loadMixin(HierarchialRepulsionMixin); + } + else { + this._clearMixin(BarnesHutMixin); + this._clearMixin(HierarchialRepulsionMixin); + this.barnesHutTree = undefined; + + this.constants.physics.centralGravity = this.constants.physics.repulsion.centralGravity; + this.constants.physics.springLength = this.constants.physics.repulsion.springLength; + this.constants.physics.springConstant = this.constants.physics.repulsion.springConstant; + this.constants.physics.damping = this.constants.physics.repulsion.damping; + + this._loadMixin(RepulsionMixin); + } + }; + + /** + * Before calculating the forces, we check if we need to cluster to keep up performance and we check + * if there is more than one node. If it is just one node, we dont calculate anything. + * + * @private + */ + exports._initializeForceCalculation = function () { + // stop calculation if there is only one node + if (this.nodeIndices.length == 1) { + this.nodes[this.nodeIndices[0]]._setForce(0, 0); + } + else { + // if there are too many nodes on screen, we cluster without repositioning + if (this.nodeIndices.length > this.constants.clustering.clusterThreshold && this.constants.clustering.enabled == true) { + this.clusterToFit(this.constants.clustering.reduceToNodes, false); + } + + // we now start the force calculation + this._calculateForces(); + } + }; + + + /** + * Calculate the external forces acting on the nodes + * Forces are caused by: edges, repulsing forces between nodes, gravity + * @private + */ + exports._calculateForces = function () { + // Gravity is required to keep separated groups from floating off + // the forces are reset to zero in this loop by using _setForce instead + // of _addForce + + this._calculateGravitationalForces(); + this._calculateNodeForces(); + + if (this.constants.physics.springConstant > 0) { + if (this.constants.smoothCurves.enabled == true && this.constants.smoothCurves.dynamic == true) { + this._calculateSpringForcesWithSupport(); + } + else { + if (this.constants.physics.hierarchicalRepulsion.enabled == true) { + this._calculateHierarchicalSpringForces(); + } + else { + this._calculateSpringForces(); + } + } + } + }; + + + /** + * Smooth curves are created by adding invisible nodes in the center of the edges. These nodes are also + * handled in the calculateForces function. We then use a quadratic curve with the center node as control. + * This function joins the datanodes and invisible (called support) nodes into one object. + * We do this so we do not contaminate this.nodes with the support nodes. + * + * @private + */ + exports._updateCalculationNodes = function () { + if (this.constants.smoothCurves.enabled == true && this.constants.smoothCurves.dynamic == true) { + this.calculationNodes = {}; + this.calculationNodeIndices = []; + + for (var nodeId in this.nodes) { + if (this.nodes.hasOwnProperty(nodeId)) { + this.calculationNodes[nodeId] = this.nodes[nodeId]; + } + } + var supportNodes = this.sectors['support']['nodes']; + for (var supportNodeId in supportNodes) { + if (supportNodes.hasOwnProperty(supportNodeId)) { + if (this.edges.hasOwnProperty(supportNodes[supportNodeId].parentEdgeId)) { + this.calculationNodes[supportNodeId] = supportNodes[supportNodeId]; + } + else { + supportNodes[supportNodeId]._setForce(0, 0); + } + } + } + + for (var idx in this.calculationNodes) { + if (this.calculationNodes.hasOwnProperty(idx)) { + this.calculationNodeIndices.push(idx); + } + } + } + else { + this.calculationNodes = this.nodes; + this.calculationNodeIndices = this.nodeIndices; + } + }; + + + /** + * this function applies the central gravity effect to keep groups from floating off + * + * @private + */ + exports._calculateGravitationalForces = function () { + var dx, dy, distance, node, i; + var nodes = this.calculationNodes; + var gravity = this.constants.physics.centralGravity; + var gravityForce = 0; + + for (i = 0; i < this.calculationNodeIndices.length; i++) { + node = nodes[this.calculationNodeIndices[i]]; + node.damping = this.constants.physics.damping; // possibly add function to alter damping properties of clusters. + // gravity does not apply when we are in a pocket sector + if (this._sector() == "default" && gravity != 0) { + dx = -node.x; + dy = -node.y; + distance = Math.sqrt(dx * dx + dy * dy); + + gravityForce = (distance == 0) ? 0 : (gravity / distance); + node.fx = dx * gravityForce; + node.fy = dy * gravityForce; + } + else { + node.fx = 0; + node.fy = 0; + } + } + }; + + + + + /** + * this function calculates the effects of the springs in the case of unsmooth curves. + * + * @private + */ + exports._calculateSpringForces = function () { + var edgeLength, edge, edgeId; + var dx, dy, fx, fy, springForce, distance; + var edges = this.edges; + + // forces caused by the edges, modelled as springs + for (edgeId in edges) { + if (edges.hasOwnProperty(edgeId)) { + edge = edges[edgeId]; + if (edge.connected) { + // only calculate forces if nodes are in the same sector + if (this.nodes.hasOwnProperty(edge.toId) && this.nodes.hasOwnProperty(edge.fromId)) { + edgeLength = edge.physics.springLength; + // this implies that the edges between big clusters are longer + edgeLength += (edge.to.clusterSize + edge.from.clusterSize - 2) * this.constants.clustering.edgeGrowth; + + dx = (edge.from.x - edge.to.x); + dy = (edge.from.y - edge.to.y); + distance = Math.sqrt(dx * dx + dy * dy); + + if (distance == 0) { + distance = 0.01; + } + + // the 1/distance is so the fx and fy can be calculated without sine or cosine. + springForce = this.constants.physics.springConstant * (edgeLength - distance) / distance; + + fx = dx * springForce; + fy = dy * springForce; + + edge.from.fx += fx; + edge.from.fy += fy; + edge.to.fx -= fx; + edge.to.fy -= fy; + } + } + } + } + }; + + + + + /** + * This function calculates the springforces on the nodes, accounting for the support nodes. + * + * @private + */ + exports._calculateSpringForcesWithSupport = function () { + var edgeLength, edge, edgeId, combinedClusterSize; + var edges = this.edges; + + // forces caused by the edges, modelled as springs + for (edgeId in edges) { + if (edges.hasOwnProperty(edgeId)) { + edge = edges[edgeId]; + if (edge.connected) { + // only calculate forces if nodes are in the same sector + if (this.nodes.hasOwnProperty(edge.toId) && this.nodes.hasOwnProperty(edge.fromId)) { + if (edge.via != null) { + var node1 = edge.to; + var node2 = edge.via; + var node3 = edge.from; + + edgeLength = edge.physics.springLength; + + combinedClusterSize = node1.clusterSize + node3.clusterSize - 2; + + // this implies that the edges between big clusters are longer + edgeLength += combinedClusterSize * this.constants.clustering.edgeGrowth; + this._calculateSpringForce(node1, node2, 0.5 * edgeLength); + this._calculateSpringForce(node2, node3, 0.5 * edgeLength); + } + } + } + } + } + }; + + + /** + * This is the code actually performing the calculation for the function above. It is split out to avoid repetition. + * + * @param node1 + * @param node2 + * @param edgeLength + * @private + */ + exports._calculateSpringForce = function (node1, node2, edgeLength) { + var dx, dy, fx, fy, springForce, distance; + + dx = (node1.x - node2.x); + dy = (node1.y - node2.y); + distance = Math.sqrt(dx * dx + dy * dy); + + if (distance == 0) { + distance = 0.01; + } + + // the 1/distance is so the fx and fy can be calculated without sine or cosine. + springForce = this.constants.physics.springConstant * (edgeLength - distance) / distance; + + fx = dx * springForce; + fy = dy * springForce; + + node1.fx += fx; + node1.fy += fy; + node2.fx -= fx; + node2.fy -= fy; + }; + + + exports._cleanupPhysicsConfiguration = function() { + if (this.physicsConfiguration !== undefined) { + while (this.physicsConfiguration.hasChildNodes()) { + this.physicsConfiguration.removeChild(this.physicsConfiguration.firstChild); + } + + this.physicsConfiguration.parentNode.removeChild(this.physicsConfiguration); + this.physicsConfiguration = undefined; + } + } + + /** + * Load the HTML for the physics config and bind it + * @private + */ + exports._loadPhysicsConfiguration = function () { + if (this.physicsConfiguration === undefined) { + this.backupConstants = {}; + util.deepExtend(this.backupConstants,this.constants); + + var hierarchicalLayoutDirections = ["LR", "RL", "UD", "DU"]; + this.physicsConfiguration = document.createElement('div'); + this.physicsConfiguration.className = "PhysicsConfiguration"; + this.physicsConfiguration.innerHTML = '' + + '' + + '' + + '' + + '' + + '' + + '' + + '
Simulation Mode:
Barnes HutRepulsionHierarchical
' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '
Options:
' + this.containerElement.parentElement.insertBefore(this.physicsConfiguration, this.containerElement); + this.optionsDiv = document.createElement("div"); + this.optionsDiv.style.fontSize = "14px"; + this.optionsDiv.style.fontFamily = "verdana"; + this.containerElement.parentElement.insertBefore(this.optionsDiv, this.containerElement); + + var rangeElement; + rangeElement = document.getElementById('graph_BH_gc'); + rangeElement.onchange = showValueOfRange.bind(this, 'graph_BH_gc', -1, "physics_barnesHut_gravitationalConstant"); + rangeElement = document.getElementById('graph_BH_cg'); + rangeElement.onchange = showValueOfRange.bind(this, 'graph_BH_cg', 1, "physics_centralGravity"); + rangeElement = document.getElementById('graph_BH_sc'); + rangeElement.onchange = showValueOfRange.bind(this, 'graph_BH_sc', 1, "physics_springConstant"); + rangeElement = document.getElementById('graph_BH_sl'); + rangeElement.onchange = showValueOfRange.bind(this, 'graph_BH_sl', 1, "physics_springLength"); + rangeElement = document.getElementById('graph_BH_damp'); + rangeElement.onchange = showValueOfRange.bind(this, 'graph_BH_damp', 1, "physics_damping"); + + rangeElement = document.getElementById('graph_R_nd'); + rangeElement.onchange = showValueOfRange.bind(this, 'graph_R_nd', 1, "physics_repulsion_nodeDistance"); + rangeElement = document.getElementById('graph_R_cg'); + rangeElement.onchange = showValueOfRange.bind(this, 'graph_R_cg', 1, "physics_centralGravity"); + rangeElement = document.getElementById('graph_R_sc'); + rangeElement.onchange = showValueOfRange.bind(this, 'graph_R_sc', 1, "physics_springConstant"); + rangeElement = document.getElementById('graph_R_sl'); + rangeElement.onchange = showValueOfRange.bind(this, 'graph_R_sl', 1, "physics_springLength"); + rangeElement = document.getElementById('graph_R_damp'); + rangeElement.onchange = showValueOfRange.bind(this, 'graph_R_damp', 1, "physics_damping"); + + rangeElement = document.getElementById('graph_H_nd'); + rangeElement.onchange = showValueOfRange.bind(this, 'graph_H_nd', 1, "physics_hierarchicalRepulsion_nodeDistance"); + rangeElement = document.getElementById('graph_H_cg'); + rangeElement.onchange = showValueOfRange.bind(this, 'graph_H_cg', 1, "physics_centralGravity"); + rangeElement = document.getElementById('graph_H_sc'); + rangeElement.onchange = showValueOfRange.bind(this, 'graph_H_sc', 1, "physics_springConstant"); + rangeElement = document.getElementById('graph_H_sl'); + rangeElement.onchange = showValueOfRange.bind(this, 'graph_H_sl', 1, "physics_springLength"); + rangeElement = document.getElementById('graph_H_damp'); + rangeElement.onchange = showValueOfRange.bind(this, 'graph_H_damp', 1, "physics_damping"); + rangeElement = document.getElementById('graph_H_direction'); + rangeElement.onchange = showValueOfRange.bind(this, 'graph_H_direction', hierarchicalLayoutDirections, "hierarchicalLayout_direction"); + rangeElement = document.getElementById('graph_H_levsep'); + rangeElement.onchange = showValueOfRange.bind(this, 'graph_H_levsep', 1, "hierarchicalLayout_levelSeparation"); + rangeElement = document.getElementById('graph_H_nspac'); + rangeElement.onchange = showValueOfRange.bind(this, 'graph_H_nspac', 1, "hierarchicalLayout_nodeSpacing"); + + var radioButton1 = document.getElementById("graph_physicsMethod1"); + var radioButton2 = document.getElementById("graph_physicsMethod2"); + var radioButton3 = document.getElementById("graph_physicsMethod3"); + radioButton2.checked = true; + if (this.constants.physics.barnesHut.enabled) { + radioButton1.checked = true; + } + if (this.constants.hierarchicalLayout.enabled) { + radioButton3.checked = true; + } - //strict parsing regexes - parseTokenOneDigit = /\d/, // 0 - 9 - parseTokenTwoDigits = /\d\d/, // 00 - 99 - parseTokenThreeDigits = /\d{3}/, // 000 - 999 - parseTokenFourDigits = /\d{4}/, // 0000 - 9999 - parseTokenSixDigits = /[+-]?\d{6}/, // -999,999 - 999,999 - parseTokenSignedNumber = /[+-]?\d+/, // -inf - inf + var graph_toggleSmooth = document.getElementById("graph_toggleSmooth"); + var graph_repositionNodes = document.getElementById("graph_repositionNodes"); + var graph_generateOptions = document.getElementById("graph_generateOptions"); - // iso 8601 regex - // 0000-00-00 0000-W00 or 0000-W00-0 + T + 00 or 00:00 or 00:00:00 or 00:00:00.000 + +00:00 or +0000 or +00) - isoRegex = /^\s*(?:[+-]\d{6}|\d{4})-(?:(\d\d-\d\d)|(W\d\d$)|(W\d\d-\d)|(\d\d\d))((T| )(\d\d(:\d\d(:\d\d(\.\d+)?)?)?)?([\+\-]\d\d(?::?\d\d)?|\s*Z)?)?$/, + graph_toggleSmooth.onclick = graphToggleSmoothCurves.bind(this); + graph_repositionNodes.onclick = graphRepositionNodes.bind(this); + graph_generateOptions.onclick = graphGenerateOptions.bind(this); + if (this.constants.smoothCurves == true && this.constants.dynamicSmoothCurves == false) { + graph_toggleSmooth.style.background = "#A4FF56"; + } + else { + graph_toggleSmooth.style.background = "#FF8532"; + } - isoFormat = 'YYYY-MM-DDTHH:mm:ssZ', - isoDates = [ - ['YYYYYY-MM-DD', /[+-]\d{6}-\d{2}-\d{2}/], - ['YYYY-MM-DD', /\d{4}-\d{2}-\d{2}/], - ['GGGG-[W]WW-E', /\d{4}-W\d{2}-\d/], - ['GGGG-[W]WW', /\d{4}-W\d{2}/], - ['YYYY-DDD', /\d{4}-\d{3}/] - ], + switchConfigurations.apply(this); - // iso time formats and regexes - isoTimes = [ - ['HH:mm:ss.SSSS', /(T| )\d\d:\d\d:\d\d\.\d+/], - ['HH:mm:ss', /(T| )\d\d:\d\d:\d\d/], - ['HH:mm', /(T| )\d\d:\d\d/], - ['HH', /(T| )\d\d/] - ], + radioButton1.onchange = switchConfigurations.bind(this); + radioButton2.onchange = switchConfigurations.bind(this); + radioButton3.onchange = switchConfigurations.bind(this); + } + }; - // timezone chunker '+10:00' > ['10', '00'] or '-1530' > ['-', '15', '30'] - parseTimezoneChunker = /([\+\-]|\d\d)/gi, + /** + * This overwrites the this.constants. + * + * @param constantsVariableName + * @param value + * @private + */ + exports._overWriteGraphConstants = function (constantsVariableName, value) { + var nameArray = constantsVariableName.split("_"); + if (nameArray.length == 1) { + this.constants[nameArray[0]] = value; + } + else if (nameArray.length == 2) { + this.constants[nameArray[0]][nameArray[1]] = value; + } + else if (nameArray.length == 3) { + this.constants[nameArray[0]][nameArray[1]][nameArray[2]] = value; + } + }; - // getter and setter names - proxyGettersAndSetters = 'Date|Hours|Minutes|Seconds|Milliseconds'.split('|'), - unitMillisecondFactors = { - 'Milliseconds' : 1, - 'Seconds' : 1e3, - 'Minutes' : 6e4, - 'Hours' : 36e5, - 'Days' : 864e5, - 'Months' : 2592e6, - 'Years' : 31536e6 - }, - unitAliases = { - ms : 'millisecond', - s : 'second', - m : 'minute', - h : 'hour', - d : 'day', - D : 'date', - w : 'week', - W : 'isoWeek', - M : 'month', - Q : 'quarter', - y : 'year', - DDD : 'dayOfYear', - e : 'weekday', - E : 'isoWeekday', - gg: 'weekYear', - GG: 'isoWeekYear' - }, + /** + * this function is bound to the toggle smooth curves button. That is also why it is not in the prototype. + */ + function graphToggleSmoothCurves () { + this.constants.smoothCurves.enabled = !this.constants.smoothCurves.enabled; + var graph_toggleSmooth = document.getElementById("graph_toggleSmooth"); + if (this.constants.smoothCurves.enabled == true) {graph_toggleSmooth.style.background = "#A4FF56";} + else {graph_toggleSmooth.style.background = "#FF8532";} - camelFunctions = { - dayofyear : 'dayOfYear', - isoweekday : 'isoWeekday', - isoweek : 'isoWeek', - weekyear : 'weekYear', - isoweekyear : 'isoWeekYear' - }, + this._configureSmoothCurves(false); + } - // format function strings - formatFunctions = {}, + /** + * this function is used to scramble the nodes + * + */ + function graphRepositionNodes () { + for (var nodeId in this.calculationNodes) { + if (this.calculationNodes.hasOwnProperty(nodeId)) { + this.calculationNodes[nodeId].vx = 0; this.calculationNodes[nodeId].vy = 0; + this.calculationNodes[nodeId].fx = 0; this.calculationNodes[nodeId].fy = 0; + } + } + if (this.constants.hierarchicalLayout.enabled == true) { + this._setupHierarchicalLayout(); + showValueOfRange.call(this, 'graph_H_nd', 1, "physics_hierarchicalRepulsion_nodeDistance"); + showValueOfRange.call(this, 'graph_H_cg', 1, "physics_centralGravity"); + showValueOfRange.call(this, 'graph_H_sc', 1, "physics_springConstant"); + showValueOfRange.call(this, 'graph_H_sl', 1, "physics_springLength"); + showValueOfRange.call(this, 'graph_H_damp', 1, "physics_damping"); + } + else { + this.repositionNodes(); + } + this.moving = true; + this.start(); + } - // default relative time thresholds - relativeTimeThresholds = { - s: 45, // seconds to minute - m: 45, // minutes to hour - h: 22, // hours to day - d: 26, // days to month - M: 11 // months to year - }, + /** + * this is used to generate an options file from the playing with physics system. + */ + function graphGenerateOptions () { + var options = "No options are required, default values used."; + var optionsSpecific = []; + var radioButton1 = document.getElementById("graph_physicsMethod1"); + var radioButton2 = document.getElementById("graph_physicsMethod2"); + if (radioButton1.checked == true) { + if (this.constants.physics.barnesHut.gravitationalConstant != this.backupConstants.physics.barnesHut.gravitationalConstant) {optionsSpecific.push("gravitationalConstant: " + this.constants.physics.barnesHut.gravitationalConstant);} + if (this.constants.physics.centralGravity != this.backupConstants.physics.barnesHut.centralGravity) {optionsSpecific.push("centralGravity: " + this.constants.physics.centralGravity);} + if (this.constants.physics.springLength != this.backupConstants.physics.barnesHut.springLength) {optionsSpecific.push("springLength: " + this.constants.physics.springLength);} + if (this.constants.physics.springConstant != this.backupConstants.physics.barnesHut.springConstant) {optionsSpecific.push("springConstant: " + this.constants.physics.springConstant);} + if (this.constants.physics.damping != this.backupConstants.physics.barnesHut.damping) {optionsSpecific.push("damping: " + this.constants.physics.damping);} + if (optionsSpecific.length != 0) { + options = "var options = {"; + options += "physics: {barnesHut: {"; + for (var i = 0; i < optionsSpecific.length; i++) { + options += optionsSpecific[i]; + if (i < optionsSpecific.length - 1) { + options += ", " + } + } + options += '}}' + } + if (this.constants.smoothCurves.enabled != this.backupConstants.smoothCurves.enabled) { + if (optionsSpecific.length == 0) {options = "var options = {";} + else {options += ", "} + options += "smoothCurves: " + this.constants.smoothCurves.enabled; + } + if (options != "No options are required, default values used.") { + options += '};' + } + } + else if (radioButton2.checked == true) { + options = "var options = {"; + options += "physics: {barnesHut: {enabled: false}"; + if (this.constants.physics.repulsion.nodeDistance != this.backupConstants.physics.repulsion.nodeDistance) {optionsSpecific.push("nodeDistance: " + this.constants.physics.repulsion.nodeDistance);} + if (this.constants.physics.centralGravity != this.backupConstants.physics.repulsion.centralGravity) {optionsSpecific.push("centralGravity: " + this.constants.physics.centralGravity);} + if (this.constants.physics.springLength != this.backupConstants.physics.repulsion.springLength) {optionsSpecific.push("springLength: " + this.constants.physics.springLength);} + if (this.constants.physics.springConstant != this.backupConstants.physics.repulsion.springConstant) {optionsSpecific.push("springConstant: " + this.constants.physics.springConstant);} + if (this.constants.physics.damping != this.backupConstants.physics.repulsion.damping) {optionsSpecific.push("damping: " + this.constants.physics.damping);} + if (optionsSpecific.length != 0) { + options += ", repulsion: {"; + for (var i = 0; i < optionsSpecific.length; i++) { + options += optionsSpecific[i]; + if (i < optionsSpecific.length - 1) { + options += ", " + } + } + options += '}}' + } + if (optionsSpecific.length == 0) {options += "}"} + if (this.constants.smoothCurves != this.backupConstants.smoothCurves) { + options += ", smoothCurves: " + this.constants.smoothCurves; + } + options += '};' + } + else { + options = "var options = {"; + if (this.constants.physics.hierarchicalRepulsion.nodeDistance != this.backupConstants.physics.hierarchicalRepulsion.nodeDistance) {optionsSpecific.push("nodeDistance: " + this.constants.physics.hierarchicalRepulsion.nodeDistance);} + if (this.constants.physics.centralGravity != this.backupConstants.physics.hierarchicalRepulsion.centralGravity) {optionsSpecific.push("centralGravity: " + this.constants.physics.centralGravity);} + if (this.constants.physics.springLength != this.backupConstants.physics.hierarchicalRepulsion.springLength) {optionsSpecific.push("springLength: " + this.constants.physics.springLength);} + if (this.constants.physics.springConstant != this.backupConstants.physics.hierarchicalRepulsion.springConstant) {optionsSpecific.push("springConstant: " + this.constants.physics.springConstant);} + if (this.constants.physics.damping != this.backupConstants.physics.hierarchicalRepulsion.damping) {optionsSpecific.push("damping: " + this.constants.physics.damping);} + if (optionsSpecific.length != 0) { + options += "physics: {hierarchicalRepulsion: {"; + for (var i = 0; i < optionsSpecific.length; i++) { + options += optionsSpecific[i]; + if (i < optionsSpecific.length - 1) { + options += ", "; + } + } + options += '}},'; + } + options += 'hierarchicalLayout: {'; + optionsSpecific = []; + if (this.constants.hierarchicalLayout.direction != this.backupConstants.hierarchicalLayout.direction) {optionsSpecific.push("direction: " + this.constants.hierarchicalLayout.direction);} + if (Math.abs(this.constants.hierarchicalLayout.levelSeparation) != this.backupConstants.hierarchicalLayout.levelSeparation) {optionsSpecific.push("levelSeparation: " + this.constants.hierarchicalLayout.levelSeparation);} + if (this.constants.hierarchicalLayout.nodeSpacing != this.backupConstants.hierarchicalLayout.nodeSpacing) {optionsSpecific.push("nodeSpacing: " + this.constants.hierarchicalLayout.nodeSpacing);} + if (optionsSpecific.length != 0) { + for (var i = 0; i < optionsSpecific.length; i++) { + options += optionsSpecific[i]; + if (i < optionsSpecific.length - 1) { + options += ", " + } + } + options += '}' + } + else { + options += "enabled:true}"; + } + options += '};' + } - // tokens to ordinalize and pad - ordinalizeTokens = 'DDD w W M D d'.split(' '), - paddedTokens = 'M D H h m s w W'.split(' '), - formatTokenFunctions = { - M : function () { - return this.month() + 1; - }, - MMM : function (format) { - return this.localeData().monthsShort(this, format); - }, - MMMM : function (format) { - return this.localeData().months(this, format); - }, - D : function () { - return this.date(); - }, - DDD : function () { - return this.dayOfYear(); - }, - d : function () { - return this.day(); - }, - dd : function (format) { - return this.localeData().weekdaysMin(this, format); - }, - ddd : function (format) { - return this.localeData().weekdaysShort(this, format); - }, - dddd : function (format) { - return this.localeData().weekdays(this, format); - }, - w : function () { - return this.week(); - }, - W : function () { - return this.isoWeek(); - }, - YY : function () { - return leftZeroFill(this.year() % 100, 2); - }, - YYYY : function () { - return leftZeroFill(this.year(), 4); - }, - YYYYY : function () { - return leftZeroFill(this.year(), 5); - }, - YYYYYY : function () { - var y = this.year(), sign = y >= 0 ? '+' : '-'; - return sign + leftZeroFill(Math.abs(y), 6); - }, - gg : function () { - return leftZeroFill(this.weekYear() % 100, 2); - }, - gggg : function () { - return leftZeroFill(this.weekYear(), 4); - }, - ggggg : function () { - return leftZeroFill(this.weekYear(), 5); - }, - GG : function () { - return leftZeroFill(this.isoWeekYear() % 100, 2); - }, - GGGG : function () { - return leftZeroFill(this.isoWeekYear(), 4); - }, - GGGGG : function () { - return leftZeroFill(this.isoWeekYear(), 5); - }, - e : function () { - return this.weekday(); - }, - E : function () { - return this.isoWeekday(); - }, - a : function () { - return this.localeData().meridiem(this.hours(), this.minutes(), true); - }, - A : function () { - return this.localeData().meridiem(this.hours(), this.minutes(), false); - }, - H : function () { - return this.hours(); - }, - h : function () { - return this.hours() % 12 || 12; - }, - m : function () { - return this.minutes(); - }, - s : function () { - return this.seconds(); - }, - S : function () { - return toInt(this.milliseconds() / 100); - }, - SS : function () { - return leftZeroFill(toInt(this.milliseconds() / 10), 2); - }, - SSS : function () { - return leftZeroFill(this.milliseconds(), 3); - }, - SSSS : function () { - return leftZeroFill(this.milliseconds(), 3); - }, - Z : function () { - var a = this.utcOffset(), - b = '+'; - if (a < 0) { - a = -a; - b = '-'; - } - return b + leftZeroFill(toInt(a / 60), 2) + ':' + leftZeroFill(toInt(a) % 60, 2); - }, - ZZ : function () { - var a = this.utcOffset(), - b = '+'; - if (a < 0) { - a = -a; - b = '-'; - } - return b + leftZeroFill(toInt(a / 60), 2) + leftZeroFill(toInt(a) % 60, 2); - }, - z : function () { - return this.zoneAbbr(); - }, - zz : function () { - return this.zoneName(); - }, - x : function () { - return this.valueOf(); - }, - X : function () { - return this.unix(); - }, - Q : function () { - return this.quarter(); - } - }, + this.optionsDiv.innerHTML = options; + } - deprecations = {}, + /** + * this is used to switch between barnesHut, repulsion and hierarchical. + * + */ + function switchConfigurations () { + var ids = ["graph_BH_table", "graph_R_table", "graph_H_table"]; + var radioButton = document.querySelector('input[name="graph_physicsMethod"]:checked').value; + var tableId = "graph_" + radioButton + "_table"; + var table = document.getElementById(tableId); + table.style.display = "block"; + for (var i = 0; i < ids.length; i++) { + if (ids[i] != tableId) { + table = document.getElementById(ids[i]); + table.style.display = "none"; + } + } + this._restoreNodes(); + if (radioButton == "R") { + this.constants.hierarchicalLayout.enabled = false; + this.constants.physics.hierarchicalRepulsion.enabled = false; + this.constants.physics.barnesHut.enabled = false; + } + else if (radioButton == "H") { + if (this.constants.hierarchicalLayout.enabled == false) { + this.constants.hierarchicalLayout.enabled = true; + this.constants.physics.hierarchicalRepulsion.enabled = true; + this.constants.physics.barnesHut.enabled = false; + this.constants.smoothCurves.enabled = false; + this._setupHierarchicalLayout(); + } + } + else { + this.constants.hierarchicalLayout.enabled = false; + this.constants.physics.hierarchicalRepulsion.enabled = false; + this.constants.physics.barnesHut.enabled = true; + } + this._loadSelectedForceSolver(); + var graph_toggleSmooth = document.getElementById("graph_toggleSmooth"); + if (this.constants.smoothCurves.enabled == true) {graph_toggleSmooth.style.background = "#A4FF56";} + else {graph_toggleSmooth.style.background = "#FF8532";} + this.moving = true; + this.start(); + } - lists = ['months', 'monthsShort', 'weekdays', 'weekdaysShort', 'weekdaysMin'], - updateInProgress = false; + /** + * this generates the ranges depending on the iniital values. + * + * @param id + * @param map + * @param constantsVariableName + */ + function showValueOfRange (id,map,constantsVariableName) { + var valueId = id + "_value"; + var rangeValue = document.getElementById(id).value; - // Pick the first defined of two or three arguments. dfl comes from - // default. - function dfl(a, b, c) { - switch (arguments.length) { - case 2: return a != null ? a : b; - case 3: return a != null ? a : b != null ? b : c; - default: throw new Error('Implement me'); - } - } + if (Array.isArray(map)) { + document.getElementById(valueId).value = map[parseInt(rangeValue)]; + this._overWriteGraphConstants(constantsVariableName,map[parseInt(rangeValue)]); + } + else { + document.getElementById(valueId).value = parseInt(map) * parseFloat(rangeValue); + this._overWriteGraphConstants(constantsVariableName, parseInt(map) * parseFloat(rangeValue)); + } - function hasOwnProp(a, b) { - return hasOwnProperty.call(a, b); - } + if (constantsVariableName == "hierarchicalLayout_direction" || + constantsVariableName == "hierarchicalLayout_levelSeparation" || + constantsVariableName == "hierarchicalLayout_nodeSpacing") { + this._setupHierarchicalLayout(); + } + this.moving = true; + this.start(); + } - function defaultParsingFlags() { - // We need to deep clone this object, and es5 standard is not very - // helpful. - return { - empty : false, - unusedTokens : [], - unusedInput : [], - overflow : -2, - charsLeftOver : 0, - nullInput : false, - invalidMonth : null, - invalidFormat : false, - userInvalidated : false, - iso: false - }; - } - function printMsg(msg) { - if (moment.suppressDeprecationWarnings === false && - typeof console !== 'undefined' && console.warn) { - console.warn('Deprecation warning: ' + msg); - } - } - function deprecate(msg, fn) { - var firstTime = true; - return extend(function () { - if (firstTime) { - printMsg(msg); - firstTime = false; - } - return fn.apply(this, arguments); - }, fn); - } - function deprecateSimple(name, msg) { - if (!deprecations[name]) { - printMsg(msg); - deprecations[name] = true; +/***/ }, +/* 61 */ +/***/ function(module, exports, __webpack_require__) { + + /** + * Calculate the forces the nodes apply on each other based on a repulsion field. + * This field is linearly approximated. + * + * @private + */ + exports._calculateNodeForces = function () { + var dx, dy, angle, distance, fx, fy, combinedClusterSize, + repulsingForce, node1, node2, i, j; + + var nodes = this.calculationNodes; + var nodeIndices = this.calculationNodeIndices; + + // approximation constants + var a_base = -2 / 3; + var b = 4 / 3; + + // repulsing forces between nodes + var nodeDistance = this.constants.physics.repulsion.nodeDistance; + var minimumDistance = 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]]; + combinedClusterSize = node1.clusterSize + node2.clusterSize - 2; + + dx = node2.x - node1.x; + dy = node2.y - node1.y; + distance = Math.sqrt(dx * dx + dy * dy); + + // same condition as BarnesHut, making sure nodes are never 100% overlapping. + if (distance == 0) { + distance = 0.1*Math.random(); + dx = distance; + } + + minimumDistance = (combinedClusterSize == 0) ? nodeDistance : (nodeDistance * (1 + combinedClusterSize * this.constants.clustering.distanceAmplification)); + var a = a_base / minimumDistance; + if (distance < 2 * minimumDistance) { + if (distance < 0.5 * minimumDistance) { + repulsingForce = 1.0; + } + else { + repulsingForce = a * distance + b; // linear approx of 1 / (1 + Math.exp((distance / minimumDistance - 1) * steepness)) } - } - function padToken(func, count) { - return function (a) { - return leftZeroFill(func.call(this, a), count); - }; - } - function ordinalizeToken(func, period) { - return function (a) { - return this.localeData().ordinal(func.call(this, a), period); - }; + // amplify the repulsion for clusters. + repulsingForce *= (combinedClusterSize == 0) ? 1 : 1 + combinedClusterSize * this.constants.clustering.forceAmplification; + repulsingForce = repulsingForce / Math.max(distance,0.01*minimumDistance); + + fx = dx * repulsingForce; + fy = dy * repulsingForce; + node1.fx -= fx; + node1.fy -= fy; + node2.fx += fx; + node2.fy += fy; + + } } + } + }; - function monthDiff(a, b) { - // difference in months - var wholeMonthDiff = ((b.year() - a.year()) * 12) + (b.month() - a.month()), - // b is in (anchor - 1 month, anchor + 1 month) - anchor = a.clone().add(wholeMonthDiff, 'months'), - anchor2, adjust; - if (b - anchor < 0) { - anchor2 = a.clone().add(wholeMonthDiff - 1, 'months'); - // linear across the month - adjust = (b - anchor) / (anchor - anchor2); - } else { - anchor2 = a.clone().add(wholeMonthDiff + 1, 'months'); - // linear across the month - adjust = (b - anchor) / (anchor2 - anchor); - } +/***/ }, +/* 62 */ +/***/ function(module, exports, __webpack_require__) { - return -(wholeMonthDiff + adjust); - } + /** + * Calculate the forces the nodes apply on eachother based on a repulsion field. + * This field is linearly approximated. + * + * @private + */ + exports._calculateNodeForces = function () { + var dx, dy, distance, fx, fy, + repulsingForce, node1, node2, i, j; - while (ordinalizeTokens.length) { - i = ordinalizeTokens.pop(); - formatTokenFunctions[i + 'o'] = ordinalizeToken(formatTokenFunctions[i], i); - } - while (paddedTokens.length) { - i = paddedTokens.pop(); - formatTokenFunctions[i + i] = padToken(formatTokenFunctions[i], 2); - } - formatTokenFunctions.DDDD = padToken(formatTokenFunctions.DDD, 3); + var nodes = this.calculationNodes; + var nodeIndices = this.calculationNodeIndices; + // repulsing forces between nodes + var nodeDistance = this.constants.physics.hierarchicalRepulsion.nodeDistance; - function meridiemFixWrap(locale, hour, meridiem) { - var isPm; + // 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]]; - if (meridiem == null) { - // nothing to do - return hour; - } - if (locale.meridiemHour != null) { - return locale.meridiemHour(hour, meridiem); - } else if (locale.isPM != null) { - // Fallback - isPm = locale.isPM(meridiem); - if (isPm && hour < 12) { - hour += 12; - } - if (!isPm && hour === 12) { - hour = 0; - } - return hour; - } else { - // thie is not supposed to happen - return hour; - } - } + // nodes only affect nodes on their level + if (node1.level == node2.level) { - /************************************ - Constructors - ************************************/ + dx = node2.x - node1.x; + dy = node2.y - node1.y; + distance = Math.sqrt(dx * dx + dy * dy); - function Locale() { - } - // Moment prototype object - function Moment(config, skipOverflow) { - if (skipOverflow !== false) { - checkOverflow(config); + var steepness = 0.05; + if (distance < nodeDistance) { + repulsingForce = -Math.pow(steepness*distance,2) + Math.pow(steepness*nodeDistance,2); } - copyConfig(this, config); - this._d = new Date(+config._d); - // Prevent infinite loop in case updateOffset creates new moment - // objects. - if (updateInProgress === false) { - updateInProgress = true; - moment.updateOffset(this); - updateInProgress = false; + else { + repulsingForce = 0; } - } - - // Duration Constructor - function Duration(duration) { - var normalizedInput = normalizeObjectUnits(duration), - years = normalizedInput.year || 0, - quarters = normalizedInput.quarter || 0, - months = normalizedInput.month || 0, - weeks = normalizedInput.week || 0, - days = normalizedInput.day || 0, - hours = normalizedInput.hour || 0, - minutes = normalizedInput.minute || 0, - seconds = normalizedInput.second || 0, - milliseconds = normalizedInput.millisecond || 0; + // normalize force with + if (distance == 0) { + distance = 0.01; + } + else { + repulsingForce = repulsingForce / distance; + } + fx = dx * repulsingForce; + fy = dy * repulsingForce; - // representation for dateAddRemove - this._milliseconds = +milliseconds + - seconds * 1e3 + // 1000 - minutes * 6e4 + // 1000 * 60 - hours * 36e5; // 1000 * 60 * 60 - // Because of dateAddRemove treats 24 hours as different from a - // day when working around DST, we need to store them separately - this._days = +days + - weeks * 7; - // It is impossible translate months into days without knowing - // which months you are are talking about, so we have to store - // it separately. - this._months = +months + - quarters * 3 + - years * 12; + node1.fx -= fx; + node1.fy -= fy; + node2.fx += fx; + node2.fy += fy; + } + } + } + }; - this._data = {}; - this._locale = moment.localeData(); + /** + * this function calculates the effects of the springs in the case of unsmooth curves. + * + * @private + */ + exports._calculateHierarchicalSpringForces = function () { + var edgeLength, edge, edgeId; + var dx, dy, fx, fy, springForce, distance; + var edges = this.edges; - this._bubble(); - } + var nodes = this.calculationNodes; + var nodeIndices = this.calculationNodeIndices; - /************************************ - Helpers - ************************************/ + for (var i = 0; i < nodeIndices.length; i++) { + var node1 = nodes[nodeIndices[i]]; + node1.springFx = 0; + node1.springFy = 0; + } - function extend(a, b) { - for (var i in b) { - if (hasOwnProp(b, i)) { - a[i] = b[i]; - } - } - if (hasOwnProp(b, 'toString')) { - a.toString = b.toString; - } + // forces caused by the edges, modelled as springs + for (edgeId in edges) { + if (edges.hasOwnProperty(edgeId)) { + edge = edges[edgeId]; + if (edge.connected) { + // only calculate forces if nodes are in the same sector + if (this.nodes.hasOwnProperty(edge.toId) && this.nodes.hasOwnProperty(edge.fromId)) { + edgeLength = edge.physics.springLength; + // this implies that the edges between big clusters are longer + edgeLength += (edge.to.clusterSize + edge.from.clusterSize - 2) * this.constants.clustering.edgeGrowth; - if (hasOwnProp(b, 'valueOf')) { - a.valueOf = b.valueOf; - } + dx = (edge.from.x - edge.to.x); + dy = (edge.from.y - edge.to.y); + distance = Math.sqrt(dx * dx + dy * dy); - return a; - } + if (distance == 0) { + distance = 0.01; + } - function copyConfig(to, from) { - var i, prop, val; + // the 1/distance is so the fx and fy can be calculated without sine or cosine. + springForce = this.constants.physics.springConstant * (edgeLength - distance) / distance; - if (typeof from._isAMomentObject !== 'undefined') { - to._isAMomentObject = from._isAMomentObject; - } - if (typeof from._i !== 'undefined') { - to._i = from._i; - } - if (typeof from._f !== 'undefined') { - to._f = from._f; - } - if (typeof from._l !== 'undefined') { - to._l = from._l; - } - if (typeof from._strict !== 'undefined') { - to._strict = from._strict; - } - if (typeof from._tzm !== 'undefined') { - to._tzm = from._tzm; - } - if (typeof from._isUTC !== 'undefined') { - to._isUTC = from._isUTC; - } - if (typeof from._offset !== 'undefined') { - to._offset = from._offset; - } - if (typeof from._pf !== 'undefined') { - to._pf = from._pf; - } - if (typeof from._locale !== 'undefined') { - to._locale = from._locale; - } + fx = dx * springForce; + fy = dy * springForce; - if (momentProperties.length > 0) { - for (i in momentProperties) { - prop = momentProperties[i]; - val = from[prop]; - if (typeof val !== 'undefined') { - to[prop] = val; - } - } - } - return to; - } - function absRound(number) { - if (number < 0) { - return Math.ceil(number); - } else { - return Math.floor(number); + if (edge.to.level != edge.from.level) { + edge.to.springFx -= fx; + edge.to.springFy -= fy; + edge.from.springFx += fx; + edge.from.springFy += fy; + } + else { + var factor = 0.5; + edge.to.fx -= factor*fx; + edge.to.fy -= factor*fy; + edge.from.fx += factor*fx; + edge.from.fy += factor*fy; + } } + } } + } - // left zero fill a number - // see http://jsperf.com/left-zero-filling for performance comparison - function leftZeroFill(number, targetLength, forceSign) { - var output = '' + Math.abs(number), - sign = number >= 0; + // normalize spring forces + var springForce = 1; + var springFx, springFy; + for (i = 0; i < nodeIndices.length; i++) { + var node = nodes[nodeIndices[i]]; + springFx = Math.min(springForce,Math.max(-springForce,node.springFx)); + springFy = Math.min(springForce,Math.max(-springForce,node.springFy)); - while (output.length < targetLength) { - output = '0' + output; - } - return (sign ? (forceSign ? '+' : '') : '-') + output; - } + node.fx += springFx; + node.fy += springFy; + } - function positiveMomentsDifference(base, other) { - var res = {milliseconds: 0, months: 0}; + // retain energy balance + var totalFx = 0; + var totalFy = 0; + for (i = 0; i < nodeIndices.length; i++) { + var node = nodes[nodeIndices[i]]; + totalFx += node.fx; + totalFy += node.fy; + } + var correctionFx = totalFx / nodeIndices.length; + var correctionFy = totalFy / nodeIndices.length; - res.months = other.month() - base.month() + - (other.year() - base.year()) * 12; - if (base.clone().add(res.months, 'M').isAfter(other)) { - --res.months; - } + for (i = 0; i < nodeIndices.length; i++) { + var node = nodes[nodeIndices[i]]; + node.fx -= correctionFx; + node.fy -= correctionFy; + } - res.milliseconds = +other - +(base.clone().add(res.months, 'M')); + }; - return res; - } +/***/ }, +/* 63 */ +/***/ function(module, exports, __webpack_require__) { - function momentsDifference(base, other) { - var res; - other = makeAs(other, base); - if (base.isBefore(other)) { - res = positiveMomentsDifference(base, other); - } else { - res = positiveMomentsDifference(other, base); - res.milliseconds = -res.milliseconds; - res.months = -res.months; - } + /** + * 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 + */ + exports._calculateNodeForces = function() { + if (this.constants.physics.barnesHut.gravitationalConstant != 0) { + var node; + var nodes = this.calculationNodes; + var nodeIndices = this.calculationNodeIndices; + var nodeCount = nodeIndices.length; - return res; - } + this._formBarnesHutTree(nodes,nodeIndices); - // TODO: remove 'name' arg after deprecation is removed - function createAdder(direction, name) { - return function (val, period) { - var dur, tmp; - //invert the arguments, but complain about it - if (period !== null && !isNaN(+period)) { - deprecateSimple(name, 'moment().' + name + '(period, number) is deprecated. Please use moment().' + name + '(number, period).'); - tmp = val; val = period; period = tmp; - } + var barnesHutTree = this.barnesHutTree; - val = typeof val === 'string' ? +val : val; - dur = moment.duration(val, period); - addOrSubtractDurationFromMoment(this, dur, direction); - return this; - }; + // 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 BarnesHut 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); + } } + } + }; - function addOrSubtractDurationFromMoment(mom, duration, isAdding, updateOffset) { - var milliseconds = duration._milliseconds, - days = duration._days, - months = duration._months; - updateOffset = updateOffset == null ? true : updateOffset; - if (milliseconds) { - mom._d.setTime(+mom._d + milliseconds * isAdding); - } - if (days) { - rawSetter(mom, 'Date', rawGetter(mom, 'Date') + days * isAdding); - } - if (months) { - rawMonthSetter(mom, rawGetter(mom, 'Month') + months * isAdding); - } - if (updateOffset) { - moment.updateOffset(mom, days || months); - } - } + /** + * 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 + */ + exports._getForceContribution = function(parentBranch,node) { + // we get no force contribution from an empty region + if (parentBranch.childrenCount > 0) { + var dx,dy,distance; - // check if is an array - function isArray(input) { - return Object.prototype.toString.call(input) === '[object Array]'; - } + // 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); - function isDate(input) { - return Object.prototype.toString.call(input) === '[object Date]' || - input instanceof Date; + // BarnesHut condition + // original condition : s/d < thetaInverted = passed === d/s > 1/theta = passed + // calcSize = 1/s --> d * 1/s > 1/theta = passed + if (distance * parentBranch.calcSize > this.constants.physics.barnesHut.thetaInverted) { + // duplicate code to reduce function calls to speed up program + if (distance == 0) { + distance = 0.1*Math.random(); + dx = distance; + } + var gravityForce = this.constants.physics.barnesHut.gravitationalConstant * parentBranch.mass * node.options.mass / (distance * distance * distance); + var fx = dx * gravityForce; + var fy = dy * gravityForce; + node.fx += fx; + node.fy += fy; } - - // compare two arrays, return the number of differences - function compareArrays(array1, array2, dontConvert) { - var len = Math.min(array1.length, array2.length), - lengthDiff = Math.abs(array1.length - array2.length), - diffs = 0, - i; - for (i = 0; i < len; i++) { - if ((dontConvert && array1[i] !== array2[i]) || - (!dontConvert && toInt(array1[i]) !== toInt(array2[i]))) { - diffs++; - } + 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 + // duplicate code to reduce function calls to speed up program + if (distance == 0) { + distance = 0.5*Math.random(); + dx = distance; + } + var gravityForce = this.constants.physics.barnesHut.gravitationalConstant * parentBranch.mass * node.options.mass / (distance * distance * distance); + var fx = dx * gravityForce; + var fy = dy * gravityForce; + node.fx += fx; + node.fy += fy; } - return diffs + lengthDiff; + } } + } + }; - function normalizeUnits(units) { - if (units) { - var lowered = units.toLowerCase().replace(/(.)s$/, '$1'); - units = unitAliases[units] || camelFunctions[lowered] || lowered; - } - return units; + /** + * This function constructs the barnesHut tree recursively. It creates the root, splits it and starts placing the nodes. + * + * @param nodes + * @param nodeIndices + * @private + */ + exports._formBarnesHutTree = function(nodes,nodeIndices) { + var node; + var nodeCount = nodeIndices.length; + + var minX = Number.MAX_VALUE, + minY = Number.MAX_VALUE, + maxX =-Number.MAX_VALUE, + maxY =-Number.MAX_VALUE; + + // get the range of the nodes + for (var i = 0; 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 - function normalizeObjectUnits(inputObject) { - var normalizedInput = {}, - normalizedProp, - prop; - for (prop in inputObject) { - if (hasOwnProp(inputObject, prop)) { - normalizedProp = normalizeUnits(prop); - if (normalizedProp) { - normalizedInput[normalizedProp] = inputObject[prop]; - } - } - } + var minimumTreeSize = 1e-5; + var rootSize = Math.max(minimumTreeSize,Math.abs(maxX - minX)); + var halfRootSize = 0.5 * rootSize; + var centerX = 0.5 * (minX + maxX), centerY = 0.5 * (minY + maxY); - return normalizedInput; + // 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); - function makeList(field) { - var count, setter; + // place the nodes one by one recursively + for (i = 0; i < nodeCount; i++) { + node = nodes[nodeIndices[i]]; + if (node.options.mass > 0) { + this._placeInTree(barnesHutTree.root,node); + } + } - if (field.indexOf('week') === 0) { - count = 7; - setter = 'day'; - } - else if (field.indexOf('month') === 0) { - count = 12; - setter = 'month'; - } - else { - return; - } + // make global + this.barnesHutTree = barnesHutTree + }; - moment[field] = function (format, index) { - var i, getter, - method = moment._locale[field], - results = []; - if (typeof format === 'number') { - index = format; - format = undefined; - } + /** + * this updates the mass of a branch. this is increased by adding a node. + * + * @param parentBranch + * @param node + * @private + */ + exports._updateBranchMass = function(parentBranch, node) { + var totalMass = parentBranch.mass + node.options.mass; + var totalMassInv = 1/totalMass; - getter = function (i) { - var m = moment().utc().set(setter, i); - return method.call(moment._locale, m, format || ''); - }; + parentBranch.centerOfMass.x = parentBranch.centerOfMass.x * parentBranch.mass + node.x * node.options.mass; + parentBranch.centerOfMass.x *= totalMassInv; - if (index != null) { - return getter(index); - } - else { - for (i = 0; i < count; i++) { - results.push(getter(i)); - } - return results; - } - }; - } + parentBranch.centerOfMass.y = parentBranch.centerOfMass.y * parentBranch.mass + node.y * node.options.mass; + parentBranch.centerOfMass.y *= totalMassInv; - function toInt(argumentForCoercion) { - var coercedNumber = +argumentForCoercion, - value = 0; + parentBranch.mass = totalMass; + var biggestSize = Math.max(Math.max(node.height,node.radius),node.width); + parentBranch.maxWidth = (parentBranch.maxWidth < biggestSize) ? biggestSize : parentBranch.maxWidth; - if (coercedNumber !== 0 && isFinite(coercedNumber)) { - if (coercedNumber >= 0) { - value = Math.floor(coercedNumber); - } else { - value = Math.ceil(coercedNumber); - } - } + }; - return value; - } - function daysInMonth(year, month) { - return new Date(Date.UTC(year, month + 1, 0)).getUTCDate(); - } + /** + * determine in which branch the node will be placed. + * + * @param parentBranch + * @param node + * @param skipMassUpdate + * @private + */ + exports._placeInTree = function(parentBranch,node,skipMassUpdate) { + if (skipMassUpdate != true || skipMassUpdate === undefined) { + // update the mass of the branch. + this._updateBranchMass(parentBranch,node); + } - function weeksInYear(year, dow, doy) { - return weekOfYear(moment([year, 11, 31 + dow - doy]), dow, doy).week; + 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"); } - - function daysInYear(year) { - return isLeapYear(year) ? 366 : 365; + else { // in SW + this._placeInRegion(parentBranch,node,"SW"); } - - function isLeapYear(year) { - return (year % 4 === 0 && year % 100 !== 0) || year % 400 === 0; + } + 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"); + } + } + }; - function checkOverflow(m) { - var overflow; - if (m._a && m._pf.overflow === -2) { - overflow = - m._a[MONTH] < 0 || m._a[MONTH] > 11 ? MONTH : - m._a[DATE] < 1 || m._a[DATE] > daysInMonth(m._a[YEAR], m._a[MONTH]) ? DATE : - m._a[HOUR] < 0 || m._a[HOUR] > 24 || - (m._a[HOUR] === 24 && (m._a[MINUTE] !== 0 || - m._a[SECOND] !== 0 || - m._a[MILLISECOND] !== 0)) ? HOUR : - m._a[MINUTE] < 0 || m._a[MINUTE] > 59 ? MINUTE : - m._a[SECOND] < 0 || m._a[SECOND] > 59 ? SECOND : - m._a[MILLISECOND] < 0 || m._a[MILLISECOND] > 999 ? MILLISECOND : - -1; - if (m._pf._overflowDayOfYear && (overflow < YEAR || overflow > DATE)) { - overflow = DATE; - } + /** + * actually place the node in a region (or branch) + * + * @param parentBranch + * @param node + * @param region + * @private + */ + exports._placeInRegion = function(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; + } + }; - m._pf.overflow = overflow; - } - } - function isValid(m) { - if (m._isValid == null) { - m._isValid = !isNaN(m._d.getTime()) && - m._pf.overflow < 0 && - !m._pf.empty && - !m._pf.invalidMonth && - !m._pf.nullInput && - !m._pf.invalidFormat && - !m._pf.userInvalidated; + /** + * 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 + */ + exports._splitBranch = function(parentBranch) { + // if the branch is shaded with a node, replace the node in the new subset. + var containedNode = null; + if (parentBranch.childrenCount == 1) { + containedNode = parentBranch.children.data; + parentBranch.mass = 0; parentBranch.centerOfMass.x = 0; parentBranch.centerOfMass.y = 0; + } + parentBranch.childrenCount = 4; + parentBranch.children.data = null; + this._insertRegion(parentBranch,"NW"); + this._insertRegion(parentBranch,"NE"); + this._insertRegion(parentBranch,"SW"); + this._insertRegion(parentBranch,"SE"); - if (m._strict) { - m._isValid = m._isValid && - m._pf.charsLeftOver === 0 && - m._pf.unusedTokens.length === 0 && - m._pf.bigHour === undefined; - } - } - return m._isValid; - } + if (containedNode != null) { + this._placeInTree(parentBranch,containedNode); + } + }; - function normalizeLocale(key) { - return key ? key.toLowerCase().replace('_', '-') : key; - } - // pick the locale from the array - // try ['en-au', 'en-gb'] as 'en-au', 'en-gb', 'en', as in move through the list trying each - // substring from most specific to least, but move to the next array item if it's a more specific variant than the current root - function chooseLocale(names) { - var i = 0, j, next, locale, split; + /** + * 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 + */ + exports._insertRegion = function(parentBranch, region) { + var minX,maxX,minY,maxY; + 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; + } - while (i < names.length) { - split = normalizeLocale(names[i]).split('-'); - j = split.length; - next = normalizeLocale(names[i + 1]); - next = next ? next.split('-') : null; - while (j > 0) { - locale = loadLocale(split.slice(0, j).join('-')); - if (locale) { - return locale; - } - if (next && next.length >= j && compareArrays(split, next, true) >= j - 1) { - //the next array item is better than a shallower substring of this one - break; - } - j--; - } - i++; - } - return null; - } - function loadLocale(name) { - var oldLocale = null; - if (!locales[name] && hasModule) { - try { - oldLocale = moment.locale(); - !(function webpackMissingModule() { var e = new Error("Cannot find module \"./locale\""); e.code = 'MODULE_NOT_FOUND'; throw e; }()); - // because defineLocale currently also sets the global locale, we want to undo that for lazy loaded locales - moment.locale(oldLocale); - } catch (e) { } - } - return locales[name]; - } + 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 + }; + }; - // Return a moment from input, that is local/utc/utcOffset equivalent to - // model. - function makeAs(input, model) { - var res, diff; - if (model._isUTC) { - res = model.clone(); - diff = (moment.isMoment(input) || isDate(input) ? - +input : +moment(input)) - (+res); - // Use low-level api, because this fn is low-level api. - res._d.setTime(+res._d + diff); - moment.updateOffset(res, false); - return res; - } else { - return moment(input).local(); - } - } - /************************************ - Locale - ************************************/ + /** + * This function is for debugging purposed, it draws the tree. + * + * @param ctx + * @param color + * @private + */ + exports._drawTree = function(ctx,color) { + if (this.barnesHutTree !== undefined) { + ctx.lineWidth = 1; - extend(Locale.prototype, { + this._drawBranch(this.barnesHutTree.root,ctx,color); + } + }; - set : function (config) { - var prop, i; - for (i in config) { - prop = config[i]; - if (typeof prop === 'function') { - this[i] = prop; - } else { - this['_' + i] = prop; - } - } - // Lenient ordinal parsing accepts just a number in addition to - // number + (possibly) stuff coming from _ordinalParseLenient. - this._ordinalParseLenient = new RegExp(this._ordinalParse.source + '|' + /\d{1,2}/.source); - }, - _months : 'January_February_March_April_May_June_July_August_September_October_November_December'.split('_'), - months : function (m) { - return this._months[m.month()]; - }, + /** + * This function is for debugging purposes. It draws the branches recursively. + * + * @param branch + * @param ctx + * @param color + * @private + */ + exports._drawBranch = function(branch,ctx,color) { + if (color === undefined) { + color = "#FF0000"; + } - _monthsShort : 'Jan_Feb_Mar_Apr_May_Jun_Jul_Aug_Sep_Oct_Nov_Dec'.split('_'), - monthsShort : function (m) { - return this._monthsShort[m.month()]; - }, + 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(); - monthsParse : function (monthName, format, strict) { - var i, mom, regex; + ctx.beginPath(); + ctx.moveTo(branch.range.maxX,branch.range.minY); + ctx.lineTo(branch.range.maxX,branch.range.maxY); + ctx.stroke(); - if (!this._monthsParse) { - this._monthsParse = []; - this._longMonthsParse = []; - this._shortMonthsParse = []; - } + ctx.beginPath(); + ctx.moveTo(branch.range.maxX,branch.range.maxY); + ctx.lineTo(branch.range.minX,branch.range.maxY); + ctx.stroke(); - for (i = 0; i < 12; i++) { - // make the regex if we don't have it already - mom = moment.utc([2000, i]); - if (strict && !this._longMonthsParse[i]) { - this._longMonthsParse[i] = new RegExp('^' + this.months(mom, '').replace('.', '') + '$', 'i'); - this._shortMonthsParse[i] = new RegExp('^' + this.monthsShort(mom, '').replace('.', '') + '$', 'i'); - } - if (!strict && !this._monthsParse[i]) { - regex = '^' + this.months(mom, '') + '|^' + this.monthsShort(mom, ''); - this._monthsParse[i] = new RegExp(regex.replace('.', ''), 'i'); - } - // test the regex - if (strict && format === 'MMMM' && this._longMonthsParse[i].test(monthName)) { - return i; - } else if (strict && format === 'MMM' && this._shortMonthsParse[i].test(monthName)) { - return i; - } else if (!strict && this._monthsParse[i].test(monthName)) { - return i; - } - } - }, + 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(); + } + */ + }; + + +/***/ }, +/* 64 */ +/***/ function(module, exports, __webpack_require__) { + + /** + * Creation of the ClusterMixin var. + * + * This contains all the functions the Network object can use to employ clustering + */ - _weekdays : 'Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday'.split('_'), - weekdays : function (m) { - return this._weekdays[m.day()]; - }, + /** + * This is only called in the constructor of the network object + * + */ + exports.startWithClustering = function() { + // cluster if the data set is big + this.clusterToFit(this.constants.clustering.initialMaxNodes, true); - _weekdaysShort : 'Sun_Mon_Tue_Wed_Thu_Fri_Sat'.split('_'), - weekdaysShort : function (m) { - return this._weekdaysShort[m.day()]; - }, + // updates the lables after clustering + this.updateLabels(); - _weekdaysMin : 'Su_Mo_Tu_We_Th_Fr_Sa'.split('_'), - weekdaysMin : function (m) { - return this._weekdaysMin[m.day()]; - }, + // this is called here because if clusterin is disabled, the start and stabilize are called in + // the setData function. + if (this.stabilize) { + this._stabilize(); + } + this.start(); + }; - weekdaysParse : function (weekdayName) { - var i, mom, regex; + /** + * This function clusters until the initialMaxNodes has been reached + * + * @param {Number} maxNumberOfNodes + * @param {Boolean} reposition + */ + exports.clusterToFit = function(maxNumberOfNodes, reposition) { + var numberOfNodes = this.nodeIndices.length; - if (!this._weekdaysParse) { - this._weekdaysParse = []; - } + var maxLevels = 2; + var level = 0; - for (i = 0; i < 7; i++) { - // make the regex if we don't have it already - if (!this._weekdaysParse[i]) { - mom = moment([2000, 1]).day(i); - regex = '^' + this.weekdays(mom, '') + '|^' + this.weekdaysShort(mom, '') + '|^' + this.weekdaysMin(mom, ''); - this._weekdaysParse[i] = new RegExp(regex.replace('.', ''), 'i'); - } - // test the regex - if (this._weekdaysParse[i].test(weekdayName)) { - return i; - } - } - }, + // we first cluster the hubs, then we pull in the outliers, repeat + while (numberOfNodes > maxNumberOfNodes && level < maxLevels) { + console.log("Performing clustering:", level, numberOfNodes, this.clusterSession); + if (level % 3 == 0.0) { + //this.forceAggregateHubs(true); + //this.normalizeClusterLevels(); + } + else { + //this.increaseClusterLevel(); // this also includes a cluster normalization + } + //this.forceAggregateHubs(true); + numberOfNodes = this.nodeIndices.length; + level += 1; + } + console.log("finished") - _longDateFormat : { - LTS : 'h:mm:ss A', - LT : 'h:mm A', - L : 'MM/DD/YYYY', - LL : 'MMMM D, YYYY', - LLL : 'MMMM D, YYYY LT', - LLLL : 'dddd, MMMM D, YYYY LT' - }, - longDateFormat : function (key) { - var output = this._longDateFormat[key]; - if (!output && this._longDateFormat[key.toUpperCase()]) { - output = this._longDateFormat[key.toUpperCase()].replace(/MMMM|MM|DD|dddd/g, function (val) { - return val.slice(1); - }); - this._longDateFormat[key] = output; - } - return output; - }, + // after the clustering we reposition the nodes to reduce the initial chaos + if (level > 0 && reposition == true) { + this.repositionNodes(); + } + this._updateCalculationNodes(); + }; - isPM : function (input) { - // IE8 Quirks Mode & IE7 Standards Mode do not allow accessing strings like arrays - // Using charAt should be more compatible. - return ((input + '').toLowerCase().charAt(0) === 'p'); - }, + /** + * This function can be called to open up a specific cluster. It is only called by + * It will unpack the cluster back one level. + * + * @param node | Node object: cluster to open. + */ + exports.openCluster = function(node) { + var isMovingBeforeClustering = this.moving; + if (node.clusterSize > this.constants.clustering.sectorThreshold && this._nodeInActiveArea(node) && + !(this._sector() == "default" && this.nodeIndices.length == 1)) { + // this loads a new sector, loads the nodes and edges and nodeIndices of it. + this._addSector(node); + var level = 0; - _meridiemParse : /[ap]\.?m?\.?/i, - meridiem : function (hours, minutes, isLower) { - if (hours > 11) { - return isLower ? 'pm' : 'PM'; - } else { - return isLower ? 'am' : 'AM'; - } - }, + // we decluster until we reach a decent number of nodes + while ((this.nodeIndices.length < this.constants.clustering.initialMaxNodes) && (level < 10)) { + this.decreaseClusterLevel(); + level += 1; + } + } + else { + this._expandClusterNode(node,false,true); - _calendar : { - sameDay : '[Today at] LT', - nextDay : '[Tomorrow at] LT', - nextWeek : 'dddd [at] LT', - lastDay : '[Yesterday at] LT', - lastWeek : '[Last] dddd [at] LT', - sameElse : 'L' - }, - calendar : function (key, mom, now) { - var output = this._calendar[key]; - return typeof output === 'function' ? output.apply(mom, [now]) : output; - }, + // update the index list, dynamic edges and labels + this._updateNodeIndexList(); + this._updateDynamicEdges(); + this._updateCalculationNodes(); + this.updateLabels(); + } - _relativeTime : { - future : 'in %s', - past : '%s ago', - s : 'a few seconds', - m : 'a minute', - mm : '%d minutes', - h : 'an hour', - hh : '%d hours', - d : 'a day', - dd : '%d days', - M : 'a month', - MM : '%d months', - y : 'a year', - yy : '%d years' - }, + // if the simulation was settled, we restart the simulation if a cluster has been formed or expanded + if (this.moving != isMovingBeforeClustering) { + this.start(); + } + }; - relativeTime : function (number, withoutSuffix, string, isFuture) { - var output = this._relativeTime[string]; - return (typeof output === 'function') ? - output(number, withoutSuffix, string, isFuture) : - output.replace(/%d/i, number); - }, - pastFuture : function (diff, output) { - var format = this._relativeTime[diff > 0 ? 'future' : 'past']; - return typeof format === 'function' ? format(output) : format.replace(/%s/i, output); - }, + /** + * This calls the updateClustes with default arguments + */ + exports.updateClustersDefault = function() { + if (this.constants.clustering.enabled == true && this.constants.clustering.clusterByZoom == true) { + this.updateClusters(0,false,false); + } + }; - ordinal : function (number) { - return this._ordinal.replace('%d', number); - }, - _ordinal : '%d', - _ordinalParse : /\d{1,2}/, - preparse : function (string) { - return string; - }, + /** + * This function can be called to increase the cluster level. This means that the nodes with only one edge connection will + * be clustered with their connected node. This can be repeated as many times as needed. + * This can be called externally (by a keybind for instance) to reduce the complexity of big datasets. + */ + exports.increaseClusterLevel = function() { + this.updateClusters(-1,false,true); + }; - postformat : function (string) { - return string; - }, - week : function (mom) { - return weekOfYear(mom, this._week.dow, this._week.doy).week; - }, + /** + * This function can be called to decrease the cluster level. This means that the nodes with only one edge connection will + * be unpacked if they are a cluster. This can be repeated as many times as needed. + * This can be called externally (by a key-bind for instance) to look into clusters without zooming. + */ + exports.decreaseClusterLevel = function() { + this.updateClusters(1,false,true); + }; - _week : { - dow : 0, // Sunday is the first day of the week. - doy : 6 // The week that contains Jan 1st is the first week of the year. - }, - firstDayOfWeek : function () { - return this._week.dow; - }, + /** + * This is the main clustering function. It clusters and declusters on zoom or forced + * This function clusters on zoom, it can be called with a predefined zoom direction + * If out, check if we can form clusters, if in, check if we can open clusters. + * This function is only called from _zoom() + * + * @param {Number} zoomDirection | -1 / 0 / +1 for zoomOut / determineByZoom / zoomIn + * @param {Boolean} recursive | enabled or disable recursive calling of the opening of clusters + * @param {Boolean} force | enabled or disable forcing + * @param {Boolean} doNotStart | if true do not call start + * + */ + exports.updateClusters = function(zoomDirection,recursive,force,doNotStart) { + var isMovingBeforeClustering = this.moving; + var amountOfNodes = this.nodeIndices.length; - firstDayOfYear : function () { - return this._week.doy; - }, + // on zoom out collapse the sector if the scale is at the level the sector was made + if (this.previousScale > this.scale && zoomDirection == 0) { + this._collapseSector(); + } - _invalidDate: 'Invalid date', - invalidDate: function () { - return this._invalidDate; - } - }); + // check if we zoom in or out + if (this.previousScale > this.scale || zoomDirection == -1) { // zoom out + // forming clusters when forced pulls outliers in. When not forced, the edge length of the + // outer nodes determines if it is being clustered + this._formClusters(force); + } + else if (this.previousScale < this.scale || zoomDirection == 1) { // zoom in + if (force == true) { + // _openClusters checks for each node if the formationScale of the cluster is smaller than + // the current scale and if so, declusters. When forced, all clusters are reduced by one step + this._openClusters(recursive,force); + } + else { + // if a cluster takes up a set percentage of the active window + this._openClustersBySize(); + } + } + this._updateNodeIndexList(); - /************************************ - Formatting - ************************************/ + // if a cluster was NOT formed and the user zoomed out, we try clustering by hubs + if (this.nodeIndices.length == amountOfNodes && (this.previousScale > this.scale || zoomDirection == -1)) { + this._aggregateHubs(force); + this._updateNodeIndexList(); + } + // we now reduce chains. + if (this.previousScale > this.scale || zoomDirection == -1) { // zoom out + this.handleChains(); + this._updateNodeIndexList(); + } - function removeFormattingTokens(input) { - if (input.match(/\[[\s\S]/)) { - return input.replace(/^\[|\]$/g, ''); - } - return input.replace(/\\/g, ''); - } + this.previousScale = this.scale; - function makeFormatFunction(format) { - var array = format.match(formattingTokens), i, length; + // rest of the update the index list, dynamic edges and labels + this._updateDynamicEdges(); + this.updateLabels(); - for (i = 0, length = array.length; i < length; i++) { - if (formatTokenFunctions[array[i]]) { - array[i] = formatTokenFunctions[array[i]]; - } else { - array[i] = removeFormattingTokens(array[i]); - } - } + // if a cluster was formed, we increase the clusterSession + if (this.nodeIndices.length < amountOfNodes) { // this means a clustering operation has taken place + this.clusterSession += 1; + // if clusters have been made, we normalize the cluster level + this.normalizeClusterLevels(); + } - return function (mom) { - var output = ''; - for (i = 0; i < length; i++) { - output += array[i] instanceof Function ? array[i].call(mom, format) : array[i]; - } - return output; - }; + if (doNotStart == false || doNotStart === undefined) { + // if the simulation was settled, we restart the simulation if a cluster has been formed or expanded + if (this.moving != isMovingBeforeClustering) { + this.start(); } + } - // format date using native date object - function formatMoment(m, format) { - if (!m.isValid()) { - return m.localeData().invalidDate(); - } + this._updateCalculationNodes(); + }; - format = expandFormat(format, m.localeData()); + /** + * This function handles the chains. It is called on every updateClusters(). + */ + exports.handleChains = function() { + // after clustering we check how many chains there are + var chainPercentage = this._getChainFraction(); + if (chainPercentage > this.constants.clustering.chainThreshold) { + this._reduceAmountOfChains(1 - this.constants.clustering.chainThreshold / chainPercentage) - if (!formatFunctions[format]) { - formatFunctions[format] = makeFormatFunction(format); - } + } + }; - return formatFunctions[format](m); - } + /** + * this functions starts clustering by hubs + * The minimum hub threshold is set globally + * + * @private + */ + exports._aggregateHubs = function(force) { + this._getHubSize(); + this._formClustersByHub(force,false); + }; - function expandFormat(format, locale) { - var i = 5; - function replaceLongDateFormatTokens(input) { - return locale.longDateFormat(input) || input; - } + /** + * This function forces hubs to form. + * + */ + exports.forceAggregateHubs = function(doNotStart) { + var isMovingBeforeClustering = this.moving; + var amountOfNodes = this.nodeIndices.length; - localFormattingTokens.lastIndex = 0; - while (i >= 0 && localFormattingTokens.test(format)) { - format = format.replace(localFormattingTokens, replaceLongDateFormatTokens); - localFormattingTokens.lastIndex = 0; - i -= 1; - } + this._aggregateHubs(true); - return format; + // update the index list, dynamic edges and labels + this._updateNodeIndexList(); + this._updateCalculationNodes(); + this._updateDynamicEdges(); + this.updateLabels(); + + // if a cluster was formed, we increase the clusterSession + if (this.nodeIndices.length != amountOfNodes) { + this.clusterSession += 1; + } + + if (doNotStart == false || doNotStart === undefined) { + // if the simulation was settled, we restart the simulation if a cluster has been formed or expanded + if (this.moving != isMovingBeforeClustering) { + this.start(); } + } + }; + /** + * If a cluster takes up more than a set percentage of the screen, open the cluster + * + * @private + */ + exports._openClustersBySize = function() { + for (var nodeId in this.nodes) { + if (this.nodes.hasOwnProperty(nodeId)) { + var node = this.nodes[nodeId]; + if (node.inView() == true) { + if ((node.width*this.scale > this.constants.clustering.screenSizeThreshold * this.frame.canvas.clientWidth) || + (node.height*this.scale > this.constants.clustering.screenSizeThreshold * this.frame.canvas.clientHeight)) { + this.openCluster(node); + } + } + } + } + }; - /************************************ - Parsing - ************************************/ + /** + * This function loops over all nodes in the nodeIndices list. For each node it checks if it is a cluster and if it + * has to be opened based on the current zoom level. + * + * @private + */ + exports._openClusters = function(recursive,force) { + for (var i = 0; i < this.nodeIndices.length; i++) { + var node = this.nodes[this.nodeIndices[i]]; + this._expandClusterNode(node,recursive,force); + this._updateCalculationNodes(); + } + }; - // get the regex to find the next token - function getParseRegexForToken(token, config) { - var a, strict = config._strict; - switch (token) { - case 'Q': - return parseTokenOneDigit; - case 'DDDD': - return parseTokenThreeDigits; - case 'YYYY': - case 'GGGG': - case 'gggg': - return strict ? parseTokenFourDigits : parseTokenOneToFourDigits; - case 'Y': - case 'G': - case 'g': - return parseTokenSignedNumber; - case 'YYYYYY': - case 'YYYYY': - case 'GGGGG': - case 'ggggg': - return strict ? parseTokenSixDigits : parseTokenOneToSixDigits; - case 'S': - if (strict) { - return parseTokenOneDigit; - } - /* falls through */ - case 'SS': - if (strict) { - return parseTokenTwoDigits; - } - /* falls through */ - case 'SSS': - if (strict) { - return parseTokenThreeDigits; - } - /* falls through */ - case 'DDD': - return parseTokenOneToThreeDigits; - case 'MMM': - case 'MMMM': - case 'dd': - case 'ddd': - case 'dddd': - return parseTokenWord; - case 'a': - case 'A': - return config._locale._meridiemParse; - case 'x': - return parseTokenOffsetMs; - case 'X': - return parseTokenTimestampMs; - case 'Z': - case 'ZZ': - return parseTokenTimezone; - case 'T': - return parseTokenT; - case 'SSSS': - return parseTokenDigits; - case 'MM': - case 'DD': - case 'YY': - case 'GG': - case 'gg': - case 'HH': - case 'hh': - case 'mm': - case 'ss': - case 'ww': - case 'WW': - return strict ? parseTokenTwoDigits : parseTokenOneOrTwoDigits; - case 'M': - case 'D': - case 'd': - case 'H': - case 'h': - case 'm': - case 's': - case 'w': - case 'W': - case 'e': - case 'E': - return parseTokenOneOrTwoDigits; - case 'Do': - return strict ? config._locale._ordinalParse : config._locale._ordinalParseLenient; - default : - a = new RegExp(regexpEscape(unescapeFormat(token.replace('\\', '')), 'i')); - return a; - } + /** + * This function checks if a node has to be opened. This is done by checking the zoom level. + * If the node contains child nodes, this function is recursively called on the child nodes as well. + * This recursive behaviour is optional and can be set by the recursive argument. + * + * @param {Node} parentNode | to check for cluster and expand + * @param {Boolean} recursive | enabled or disable recursive calling + * @param {Boolean} force | enabled or disable forcing + * @param {Boolean} [openAll] | This will recursively force all nodes in the parent to be released + * @private + */ + exports._expandClusterNode = function(parentNode, recursive, force, openAll) { + // first check if node is a cluster + if (parentNode.clusterSize > 1) { + // this means that on a double tap event or a zoom event, the cluster fully unpacks if it is smaller than 20 + if (parentNode.clusterSize < this.constants.clustering.sectorThreshold) { + openAll = true; } + recursive = openAll ? true : recursive; - function utcOffsetFromString(string) { - string = string || ''; - var possibleTzMatches = (string.match(parseTokenTimezone) || []), - tzChunk = possibleTzMatches[possibleTzMatches.length - 1] || [], - parts = (tzChunk + '').match(parseTimezoneChunker) || ['-', 0, 0], - minutes = +(parts[1] * 60) + toInt(parts[2]); + // if the last child has been added on a smaller scale than current scale decluster + if (parentNode.formationScale < this.scale || force == true) { + // we will check if any of the contained child nodes should be removed from the cluster + for (var containedNodeId in parentNode.containedNodes) { + if (parentNode.containedNodes.hasOwnProperty(containedNodeId)) { + var childNode = parentNode.containedNodes[containedNodeId]; - return parts[0] === '+' ? minutes : -minutes; + // force expand will expand the largest cluster size clusters. Since we cluster from outside in, we assume that + // the largest cluster is the one that comes from outside + if (force == true) { + if (childNode.clusterSession == parentNode.clusterSessions[parentNode.clusterSessions.length-1] + || openAll) { + this._expelChildFromParent(parentNode,containedNodeId,recursive,force,openAll); + } + } + else { + if (this._nodeInActiveArea(parentNode)) { + this._expelChildFromParent(parentNode,containedNodeId,recursive,force,openAll); + } + } + } + } } + } + }; - // function to convert string input to date - function addTimeToArrayFromToken(token, input, config) { - var a, datePartArray = config._a; + /** + * ONLY CALLED FROM _expandClusterNode + * + * This function will expel a child_node from a parent_node. This is to de-cluster the node. This function will remove + * the child node from the parent contained_node object and put it back into the global nodes object. + * The same holds for the edge that was connected to the child node. It is moved back into the global edges object. + * + * @param {Node} parentNode | the parent node + * @param {String} containedNodeId | child_node id as it is contained in the containedNodes object of the parent node + * @param {Boolean} recursive | This will also check if the child needs to be expanded. + * With force and recursive both true, the entire cluster is unpacked + * @param {Boolean} force | This will disregard the zoom level and will expel this child from the parent + * @param {Boolean} openAll | This will recursively force all nodes in the parent to be released + * @private + */ + exports._expelChildFromParent = function(parentNode, containedNodeId, recursive, force, openAll) { + var childNode = parentNode.containedNodes[containedNodeId] - switch (token) { - // QUARTER - case 'Q': - if (input != null) { - datePartArray[MONTH] = (toInt(input) - 1) * 3; - } - break; - // MONTH - case 'M' : // fall through to MM - case 'MM' : - if (input != null) { - datePartArray[MONTH] = toInt(input) - 1; - } - break; - case 'MMM' : // fall through to MMMM - case 'MMMM' : - a = config._locale.monthsParse(input, token, config._strict); - // if we didn't find a month name, mark the date as invalid. - if (a != null) { - datePartArray[MONTH] = a; - } else { - config._pf.invalidMonth = input; - } - break; - // DAY OF MONTH - case 'D' : // fall through to DD - case 'DD' : - if (input != null) { - datePartArray[DATE] = toInt(input); - } - break; - case 'Do' : - if (input != null) { - datePartArray[DATE] = toInt(parseInt( - input.match(/\d{1,2}/)[0], 10)); - } - break; - // DAY OF YEAR - case 'DDD' : // fall through to DDDD - case 'DDDD' : - if (input != null) { - config._dayOfYear = toInt(input); - } + // if child node has been added on smaller scale than current, kick out + if (childNode.formationScale < this.scale || force == true) { + // unselect all selected items + this._unselectAll(); - break; - // YEAR - case 'YY' : - datePartArray[YEAR] = moment.parseTwoDigitYear(input); - break; - case 'YYYY' : - case 'YYYYY' : - case 'YYYYYY' : - datePartArray[YEAR] = toInt(input); - break; - // AM / PM - case 'a' : // fall through to A - case 'A' : - config._meridiem = input; - // config._isPm = config._locale.isPM(input); - break; - // HOUR - case 'h' : // fall through to hh - case 'hh' : - config._pf.bigHour = true; - /* falls through */ - case 'H' : // fall through to HH - case 'HH' : - datePartArray[HOUR] = toInt(input); - break; - // MINUTE - case 'm' : // fall through to mm - case 'mm' : - datePartArray[MINUTE] = toInt(input); - break; - // SECOND - case 's' : // fall through to ss - case 'ss' : - datePartArray[SECOND] = toInt(input); - break; - // MILLISECOND - case 'S' : - case 'SS' : - case 'SSS' : - case 'SSSS' : - datePartArray[MILLISECOND] = toInt(('0.' + input) * 1000); - break; - // UNIX OFFSET (MILLISECONDS) - case 'x': - config._d = new Date(toInt(input)); - break; - // UNIX TIMESTAMP WITH MS - case 'X': - config._d = new Date(parseFloat(input) * 1000); - break; - // TIMEZONE - case 'Z' : // fall through to ZZ - case 'ZZ' : - config._useUTC = true; - config._tzm = utcOffsetFromString(input); - break; - // WEEKDAY - human - case 'dd': - case 'ddd': - case 'dddd': - a = config._locale.weekdaysParse(input); - // if we didn't get a weekday name, mark the date as invalid - if (a != null) { - config._w = config._w || {}; - config._w['d'] = a; - } else { - config._pf.invalidWeekday = input; - } - break; - // WEEK, WEEK DAY - numeric - case 'w': - case 'ww': - case 'W': - case 'WW': - case 'd': - case 'e': - case 'E': - token = token.substr(0, 1); - /* falls through */ - case 'gggg': - case 'GGGG': - case 'GGGGG': - token = token.substr(0, 2); - if (input) { - config._w = config._w || {}; - config._w[token] = toInt(input); - } - break; - case 'gg': - case 'GG': - config._w = config._w || {}; - config._w[token] = moment.parseTwoDigitYear(input); + // put the child node back in the global nodes object + this.nodes[containedNodeId] = childNode; + + // release the contained edges from this childNode back into the global edges + this._releaseContainedEdges(parentNode,childNode); + + // reconnect rerouted edges to the childNode + this._connectEdgeBackToChild(parentNode,childNode); + + // validate all edges in dynamicEdges + this._validateEdges(parentNode); + + // undo the changes from the clustering operation on the parent node + parentNode.options.mass -= childNode.options.mass; + parentNode.clusterSize -= childNode.clusterSize; + parentNode.options.fontSize = Math.min(this.constants.clustering.maxFontSize, this.constants.nodes.fontSize + this.constants.clustering.fontSizeMultiplier*(parentNode.clusterSize-1)); + parentNode.dynamicEdgesLength = parentNode.dynamicEdges.length; + + // place the child node near the parent, not at the exact same location to avoid chaos in the system + childNode.x = parentNode.x + parentNode.growthIndicator * (0.5 - Math.random()); + childNode.y = parentNode.y + parentNode.growthIndicator * (0.5 - Math.random()); + + // remove node from the list + delete parentNode.containedNodes[containedNodeId]; + + // check if there are other childs with this clusterSession in the parent. + var othersPresent = false; + for (var childNodeId in parentNode.containedNodes) { + if (parentNode.containedNodes.hasOwnProperty(childNodeId)) { + if (parentNode.containedNodes[childNodeId].clusterSession == childNode.clusterSession) { + othersPresent = true; + break; } + } + } + // if there are no others, remove the cluster session from the list + if (othersPresent == false) { + parentNode.clusterSessions.pop(); } - function dayOfYearFromWeekInfo(config) { - var w, weekYear, week, weekday, dow, doy, temp; + this._repositionBezierNodes(childNode); + // this._repositionBezierNodes(parentNode); - w = config._w; - if (w.GG != null || w.W != null || w.E != null) { - dow = 1; - doy = 4; + // remove the clusterSession from the child node + childNode.clusterSession = 0; - // TODO: We need to take the current isoWeekYear, but that depends on - // how we interpret now (local, utc, fixed offset). So create - // a now version of current config (take local/utc/offset flags, and - // create now). - weekYear = dfl(w.GG, config._a[YEAR], weekOfYear(moment(), 1, 4).year); - week = dfl(w.W, 1); - weekday = dfl(w.E, 1); - } else { - dow = config._locale._week.dow; - doy = config._locale._week.doy; + // recalculate the size of the node on the next time the node is rendered + parentNode.clearSizeCache(); - weekYear = dfl(w.gg, config._a[YEAR], weekOfYear(moment(), dow, doy).year); - week = dfl(w.w, 1); + // restart the simulation to reorganise all nodes + this.moving = true; + } - if (w.d != null) { - // weekday -- low day numbers are considered next week - weekday = w.d; - if (weekday < dow) { - ++week; - } - } else if (w.e != null) { - // local weekday -- counting starts from begining of week - weekday = w.e + dow; - } else { - // default to begining of week - weekday = dow; - } - } - temp = dayOfYearFromWeeks(weekYear, week, weekday, doy, dow); + // check if a further expansion step is possible if recursivity is enabled + if (recursive == true) { + this._expandClusterNode(childNode,recursive,force,openAll); + } + }; - config._a[YEAR] = temp.year; - config._dayOfYear = temp.dayOfYear; - } - // convert an array to a date. - // the array should mirror the parameters below - // note: all values past the year are optional and will default to the lowest possible value. - // [year, month, day , hour, minute, second, millisecond] - function dateFromConfig(config) { - var i, date, input = [], currentDate, yearToUse; + /** + * position the bezier nodes at the center of the edges + * + * @param node + * @private + */ + exports._repositionBezierNodes = function(node) { + for (var i = 0; i < node.dynamicEdges.length; i++) { + node.dynamicEdges[i].positionBezierNode(); + } + }; - if (config._d) { - return; - } - currentDate = currentDateArray(config); + /** + * This function checks if any nodes at the end of their trees have edges below a threshold length + * This function is called only from updateClusters() + * forceLevelCollapse ignores the length of the edge and collapses one level + * This means that a node with only one edge will be clustered with its connected node + * + * @private + * @param {Boolean} force + */ + exports._formClusters = function(force) { + if (force == false) { + this._formClustersByZoom(); + } + else { + this._forceClustersByZoom(); + } + }; - //compute day of the year from weeks and weekdays - if (config._w && config._a[DATE] == null && config._a[MONTH] == null) { - dayOfYearFromWeekInfo(config); - } - //if the day of the year is set, figure out what it is - if (config._dayOfYear) { - yearToUse = dfl(config._a[YEAR], currentDate[YEAR]); + /** + * This function handles the clustering by zooming out, this is based on a minimum edge distance + * + * @private + */ + exports._formClustersByZoom = function() { + var dx,dy,length, + minLength = this.constants.clustering.clusterEdgeThreshold/this.scale; - if (config._dayOfYear > daysInYear(yearToUse)) { - config._pf._overflowDayOfYear = true; - } + // check if any edges are shorter than minLength and start the clustering + // the clustering favours the node with the larger mass + for (var edgeId in this.edges) { + if (this.edges.hasOwnProperty(edgeId)) { + var edge = this.edges[edgeId]; + if (edge.connected) { + if (edge.toId != edge.fromId) { + dx = (edge.to.x - edge.from.x); + dy = (edge.to.y - edge.from.y); + length = Math.sqrt(dx * dx + dy * dy); - date = makeUTCDate(yearToUse, 0, config._dayOfYear); - config._a[MONTH] = date.getUTCMonth(); - config._a[DATE] = date.getUTCDate(); - } - // Default to current date. - // * if no year, month, day of month are given, default to today - // * if day of month is given, default month and year - // * if month is given, default only year - // * if year is given, don't default anything - for (i = 0; i < 3 && config._a[i] == null; ++i) { - config._a[i] = input[i] = currentDate[i]; - } + if (length < minLength) { + // first check which node is larger + var parentNode = edge.from; + var childNode = edge.to; + if (edge.to.options.mass > edge.from.options.mass) { + parentNode = edge.to; + childNode = edge.from; + } - // Zero out whatever was not defaulted, including time - for (; i < 7; i++) { - config._a[i] = input[i] = (config._a[i] == null) ? (i === 2 ? 1 : 0) : config._a[i]; + if (childNode.dynamicEdgesLength == 1) { + this._addToCluster(parentNode,childNode,false); + } + else if (parentNode.dynamicEdgesLength == 1) { + this._addToCluster(childNode,parentNode,false); + } + } } + } + } + } + }; - // Check for 24:00:00.000 - if (config._a[HOUR] === 24 && - config._a[MINUTE] === 0 && - config._a[SECOND] === 0 && - config._a[MILLISECOND] === 0) { - config._nextDay = true; - config._a[HOUR] = 0; - } + /** + * This function forces the network to cluster all nodes with only one connecting edge to their + * connected node. + * + * @private + */ + exports._forceClustersByZoom = function() { + for (var nodeId in this.nodes) { + // another node could have absorbed this child. + if (this.nodes.hasOwnProperty(nodeId)) { + var childNode = this.nodes[nodeId]; - config._d = (config._useUTC ? makeUTCDate : makeDate).apply(null, input); - // Apply timezone offset from input. The actual utcOffset can be changed - // with parseZone. - if (config._tzm != null) { - config._d.setUTCMinutes(config._d.getUTCMinutes() - config._tzm); - } + // the edges can be swallowed by another decrease - if (config._nextDay) { - config._a[HOUR] = 24; + if (childNode.dynamicEdgesLength == 1 && childNode.dynamicEdges.length != 0) { + var edge = childNode.dynamicEdges[0]; + var parentNode = (edge.toId == childNode.id) ? this.nodes[edge.fromId] : this.nodes[edge.toId]; + // group to the largest node + if (childNode.id != parentNode.id) { + if (parentNode.options.mass > childNode.options.mass) { + this._addToCluster(parentNode,childNode,true); + } + else { + this._addToCluster(childNode,parentNode,true); + } } + } } + } + }; - function dateFromObject(config) { - var normalizedInput; - if (config._d) { - return; - } + /** + * To keep the nodes of roughly equal size we normalize the cluster levels. + * This function clusters a node to its smallest connected neighbour. + * + * @param node + * @private + */ + exports._clusterToSmallestNeighbour = function(node) { + var smallestNeighbour = -1; + var smallestNeighbourNode = null; + for (var i = 0; i < node.dynamicEdges.length; i++) { + if (node.dynamicEdges[i] !== undefined) { + var neighbour = null; + if (node.dynamicEdges[i].fromId != node.id) { + neighbour = node.dynamicEdges[i].from; + } + else if (node.dynamicEdges[i].toId != node.id) { + neighbour = node.dynamicEdges[i].to; + } - normalizedInput = normalizeObjectUnits(config._i); - config._a = [ - normalizedInput.year, - normalizedInput.month, - normalizedInput.day || normalizedInput.date, - normalizedInput.hour, - normalizedInput.minute, - normalizedInput.second, - normalizedInput.millisecond - ]; - dateFromConfig(config); + if (neighbour != null && smallestNeighbour > neighbour.clusterSessions.length) { + smallestNeighbour = neighbour.clusterSessions.length; + smallestNeighbourNode = neighbour; + } } + } - function currentDateArray(config) { - var now = new Date(); - if (config._useUTC) { - return [ - now.getUTCFullYear(), - now.getUTCMonth(), - now.getUTCDate() - ]; - } else { - return [now.getFullYear(), now.getMonth(), now.getDate()]; - } + if (neighbour != null && this.nodes[neighbour.id] !== undefined) { + this._addToCluster(neighbour, node, true); + } + }; + + + /** + * This function forms clusters from hubs, it loops over all nodes + * + * @param {Boolean} force | Disregard zoom level + * @param {Boolean} onlyEqual | This only clusters a hub with a specific number of edges + * @private + */ + exports._formClustersByHub = function(force, onlyEqual) { + // we loop over all nodes in the list + for (var nodeId in this.nodes) { + // we check if it is still available since it can be used by the clustering in this loop + if (this.nodes.hasOwnProperty(nodeId)) { + this._formClusterFromHub(this.nodes[nodeId],force,onlyEqual); } + } + }; + + /** + * This function forms a cluster from a specific preselected hub node + * + * @param {Node} hubNode | the node we will cluster as a hub + * @param {Boolean} force | Disregard zoom level + * @param {Boolean} onlyEqual | This only clusters a hub with a specific number of edges + * @param {Number} [absorptionSizeOffset] | + * @private + */ + exports._formClusterFromHub = function(hubNode, force, onlyEqual, absorptionSizeOffset) { + if (absorptionSizeOffset === undefined) { + absorptionSizeOffset = 0; + } - // date from string and format string - function makeDateFromStringAndFormat(config) { - if (config._f === moment.ISO_8601) { - parseISO(config); - return; - } + if (hubNode.dynamicEdgesLength < 0) { + console.error(hubNode.dynamicEdgesLength, this.hubThreshold, onlyEqual) + } + // we decide if the node is a hub + if ((hubNode.dynamicEdgesLength >= this.hubThreshold && onlyEqual == false) || + (hubNode.dynamicEdgesLength == this.hubThreshold && onlyEqual == true)) { - config._a = []; - config._pf.empty = true; + // initialize variables + var dx,dy,length; + var minLength = this.constants.clustering.clusterEdgeThreshold/this.scale; + var allowCluster = false; - // This array is used to make a Date, either with `new Date` or `Date.UTC` - var string = '' + config._i, - i, parsedInput, tokens, token, skipped, - stringLength = string.length, - totalParsedInputLength = 0; + // we create a list of edges because the dynamicEdges change over the course of this loop + var edgesIdarray = []; + var amountOfInitialEdges = hubNode.dynamicEdges.length; + for (var j = 0; j < amountOfInitialEdges; j++) { + edgesIdarray.push(hubNode.dynamicEdges[j].id); + } - tokens = expandFormat(config._f, config._locale).match(formattingTokens) || []; + // if the hub clustering is not forced, we check if one of the edges connected + // to a cluster is small enough based on the constants.clustering.clusterEdgeThreshold + if (force == false) { + allowCluster = false; + for (j = 0; j < amountOfInitialEdges; j++) { + var edge = this.edges[edgesIdarray[j]]; + if (edge !== undefined) { + if (edge.connected) { + if (edge.toId != edge.fromId) { + dx = (edge.to.x - edge.from.x); + dy = (edge.to.y - edge.from.y); + length = Math.sqrt(dx * dx + dy * dy); - for (i = 0; i < tokens.length; i++) { - token = tokens[i]; - parsedInput = (string.match(getParseRegexForToken(token, config)) || [])[0]; - if (parsedInput) { - skipped = string.substr(0, string.indexOf(parsedInput)); - if (skipped.length > 0) { - config._pf.unusedInput.push(skipped); - } - string = string.slice(string.indexOf(parsedInput) + parsedInput.length); - totalParsedInputLength += parsedInput.length; - } - // don't parse if it's not a known token - if (formatTokenFunctions[token]) { - if (parsedInput) { - config._pf.empty = false; - } - else { - config._pf.unusedTokens.push(token); - } - addTimeToArrayFromToken(token, parsedInput, config); - } - else if (config._strict && !parsedInput) { - config._pf.unusedTokens.push(token); + if (length < minLength) { + allowCluster = true; + break; + } } + } } + } + } - // add remaining unparsed input length to the string - config._pf.charsLeftOver = stringLength - totalParsedInputLength; - if (string.length > 0) { - config._pf.unusedInput.push(string); + // start the clustering if allowed + if ((!force && allowCluster) || force) { + var children = []; + var childrenIds = {}; + // we loop over all edges INITIALLY connected to this hub to get a list of the childNodes + for (j = 0; j < amountOfInitialEdges; j++) { + edge = this.edges[edgesIdarray[j]]; + var childNode = this.nodes[(edge.fromId == hubNode.id) ? edge.toId : edge.fromId]; + if (childrenIds[childNode.id] === undefined) { + childrenIds[childNode.id] = true; + children.push(childNode); } + } - // clear _12h flag if hour is <= 12 - if (config._pf.bigHour === true && config._a[HOUR] <= 12) { - config._pf.bigHour = undefined; + for (j = 0; j < children.length; j++) { + var childNode = children[j]; + // we do not want hubs to merge with other hubs nor do we want to cluster itself. + if ((childNode.dynamicEdges.length <= (this.hubThreshold + absorptionSizeOffset)) && + (childNode.id != hubNode.id)) { + this._addToCluster(hubNode,childNode,force); } - // handle meridiem - config._a[HOUR] = meridiemFixWrap(config._locale, config._a[HOUR], - config._meridiem); - dateFromConfig(config); - checkOverflow(config); + } } + } + }; - function unescapeFormat(s) { - return s.replace(/\\(\[)|\\(\])|\[([^\]\[]*)\]|\\(.)/g, function (matched, p1, p2, p3, p4) { - return p1 || p2 || p3 || p4; - }); - } - // Code from http://stackoverflow.com/questions/3561493/is-there-a-regexp-escape-function-in-javascript - function regexpEscape(s) { - return s.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&'); + + /** + * This function adds the child node to the parent node, creating a cluster if it is not already. + * + * @param {Node} parentNode | this is the node that will house the child node + * @param {Node} childNode | this node will be deleted from the global this.nodes and stored in the parent node + * @param {Boolean} force | true will only update the remainingEdges at the very end of the clustering, ensuring single level collapse + * @private + */ + exports._addToCluster = function(parentNode, childNode, force) { + // join child node in the parent node + parentNode.containedNodes[childNode.id] = childNode; + //console.log(parentNode.id, childNode.id) + // manage all the edges connected to the child and parent nodes + for (var i = 0; i < childNode.dynamicEdges.length; i++) { + var edge = childNode.dynamicEdges[i]; + if (edge.toId == parentNode.id || edge.fromId == parentNode.id) { // edge connected to parentNode + //console.log("COLLECT",parentNode.id, childNode.id, edge.toId, edge.fromId) + this._addToContainedEdges(parentNode,childNode,edge); + } + else { + //console.log("REWIRE",parentNode.id, childNode.id, edge.toId, edge.fromId) + this._connectEdgeToCluster(parentNode,childNode,edge); } + } + // a contained node has no dynamic edges. + childNode.dynamicEdges = []; - // date from string and array of format strings - function makeDateFromStringAndArray(config) { - var tempConfig, - bestMoment, + // remove circular edges from clusters + this._containCircularEdgesFromNode(parentNode,childNode); - scoreToBeat, - i, - currentScore; - if (config._f.length === 0) { - config._pf.invalidFormat = true; - config._d = new Date(NaN); - return; - } + // remove the childNode from the global nodes object + delete this.nodes[childNode.id]; - for (i = 0; i < config._f.length; i++) { - currentScore = 0; - tempConfig = copyConfig({}, config); - if (config._useUTC != null) { - tempConfig._useUTC = config._useUTC; - } - tempConfig._pf = defaultParsingFlags(); - tempConfig._f = config._f[i]; - makeDateFromStringAndFormat(tempConfig); + // update the properties of the child and parent + var massBefore = parentNode.options.mass; + childNode.clusterSession = this.clusterSession; + parentNode.options.mass += childNode.options.mass; + parentNode.clusterSize += childNode.clusterSize; + parentNode.options.fontSize = Math.min(this.constants.clustering.maxFontSize, this.constants.nodes.fontSize + this.constants.clustering.fontSizeMultiplier*parentNode.clusterSize); - if (!isValid(tempConfig)) { - continue; - } + // keep track of the clustersessions so we can open the cluster up as it has been formed. + if (parentNode.clusterSessions[parentNode.clusterSessions.length - 1] != this.clusterSession) { + parentNode.clusterSessions.push(this.clusterSession); + } - // if there is any input that was not parsed add a penalty for that format - currentScore += tempConfig._pf.charsLeftOver; + // forced clusters only open from screen size and double tap + if (force == true) { + parentNode.formationScale = 0; + } + else { + parentNode.formationScale = this.scale; // The latest child has been added on this scale + } - //or tokens - currentScore += tempConfig._pf.unusedTokens.length * 10; + // recalculate the size of the node on the next time the node is rendered + parentNode.clearSizeCache(); - tempConfig._pf.score = currentScore; + // set the pop-out scale for the childnode + parentNode.containedNodes[childNode.id].formationScale = parentNode.formationScale; - if (scoreToBeat == null || currentScore < scoreToBeat) { - scoreToBeat = currentScore; - bestMoment = tempConfig; - } - } + // nullify the movement velocity of the child, this is to avoid hectic behaviour + childNode.clearVelocity(); - extend(config, bestMoment || tempConfig); - } + // the mass has altered, preservation of energy dictates the velocity to be updated + parentNode.updateVelocity(massBefore); - // date from iso format - function parseISO(config) { - var i, l, - string = config._i, - match = isoRegex.exec(string); + // restart the simulation to reorganise all nodes + this.moving = true; + }; - if (match) { - config._pf.iso = true; - for (i = 0, l = isoDates.length; i < l; i++) { - if (isoDates[i][1].exec(string)) { - // match[5] should be 'T' or undefined - config._f = isoDates[i][0] + (match[6] || ' '); - break; - } - } - for (i = 0, l = isoTimes.length; i < l; i++) { - if (isoTimes[i][1].exec(string)) { - config._f += isoTimes[i][0]; - break; - } - } - if (string.match(parseTokenTimezone)) { - config._f += 'Z'; - } - makeDateFromStringAndFormat(config); - } else { - config._isValid = false; - } - } - // date from iso format or fallback - function makeDateFromString(config) { - parseISO(config); - if (config._isValid === false) { - delete config._isValid; - moment.createFromInputFallback(config); - } - } + /** + * This function will apply the changes made to the remainingEdges during the formation of the clusters. + * This is a seperate function to allow for level-wise collapsing of the node barnesHutTree. + * It has to be called if a level is collapsed. It is called by _formClusters(). + * @private + */ + exports._updateDynamicEdges = function() { + for (var i = 0; i < this.nodeIndices.length; i++) { + var node = this.nodes[this.nodeIndices[i]]; + node.dynamicEdgesLength = node.dynamicEdges.length; - function map(arr, fn) { - var res = [], i; - for (i = 0; i < arr.length; ++i) { - res.push(fn(arr[i], i)); + // this corrects for multiple edges pointing at the same other node + var correction = 0; + if (node.dynamicEdgesLength > 1) { + for (var j = 0; j < node.dynamicEdgesLength - 1; j++) { + var edgeToId = node.dynamicEdges[j].toId; + var edgeFromId = node.dynamicEdges[j].fromId; + for (var k = j+1; k < node.dynamicEdgesLength; k++) { + if ((node.dynamicEdges[k].toId == edgeToId && node.dynamicEdges[k].fromId == edgeFromId) || + (node.dynamicEdges[k].fromId == edgeToId && node.dynamicEdges[k].toId == edgeFromId)) { + correction += 1; + } } - return res; + } } - - function makeDateFromInput(config) { - var input = config._i, matched; - if (input === undefined) { - config._d = new Date(); - } else if (isDate(input)) { - config._d = new Date(+input); - } else if ((matched = aspNetJsonRegex.exec(input)) !== null) { - config._d = new Date(+matched[1]); - } else if (typeof input === 'string') { - makeDateFromString(config); - } else if (isArray(input)) { - config._a = map(input.slice(0), function (obj) { - return parseInt(obj, 10); - }); - dateFromConfig(config); - } else if (typeof(input) === 'object') { - dateFromObject(config); - } else if (typeof(input) === 'number') { - // from milliseconds - config._d = new Date(input); - } else { - moment.createFromInputFallback(config); - } + if (node.dynamicEdgesLength < correction) { + console.error("overshoot", node.dynamicEdgesLength, correction) } - function makeDate(y, m, d, h, M, s, ms) { - //can't just apply() to create a date: - //http://stackoverflow.com/questions/181348/instantiating-a-javascript-object-by-calling-prototype-constructor-apply - var date = new Date(y, m, d, h, M, s, ms); + node.dynamicEdgesLength -= correction; + } + }; - //the date constructor doesn't accept years < 1970 - if (y < 1970) { - date.setFullYear(y); - } - return date; + + /** + * This adds an edge from the childNode to the contained edges of the parent node + * + * @param parentNode | Node object + * @param childNode | Node object + * @param edge | Edge object + * @private + */ + exports._addToContainedEdges = function(parentNode, childNode, edge) { + // create an array object if it does not yet exist for this childNode + if (!(parentNode.containedEdges.hasOwnProperty(childNode.id))) { + parentNode.containedEdges[childNode.id] = [] + } + // add this edge to the list + parentNode.containedEdges[childNode.id].push(edge); + + // remove the edge from the global edges object + delete this.edges[edge.id]; + + // remove the edge from the parent object + for (var i = 0; i < parentNode.dynamicEdges.length; i++) { + if (parentNode.dynamicEdges[i].id == edge.id) { + parentNode.dynamicEdges.splice(i,1); + break; } + } + }; - function makeUTCDate(y) { - var date = new Date(Date.UTC.apply(null, arguments)); - if (y < 1970) { - date.setUTCFullYear(y); - } - return date; + /** + * This function connects an edge that was connected to a child node to the parent node. + * It keeps track of which nodes it has been connected to with the originalId array. + * + * @param {Node} parentNode | Node object + * @param {Node} childNode | Node object + * @param {Edge} edge | Edge object + * @private + */ + exports._connectEdgeToCluster = function(parentNode, childNode, edge) { + // handle circular edges + if (edge.toId == edge.fromId) { + this._addToContainedEdges(parentNode, childNode, edge); + } + else { + if (edge.toId == childNode.id) { // edge connected to other node on the "to" side + edge.originalToId.push(childNode.id); + edge.to = parentNode; + edge.toId = parentNode.id; } + else { // edge connected to other node with the "from" side - function parseWeekday(input, locale) { - if (typeof input === 'string') { - if (!isNaN(input)) { - input = parseInt(input, 10); - } - else { - input = locale.weekdaysParse(input); - if (typeof input !== 'number') { - return null; - } - } - } - return input; + edge.originalFromId.push(childNode.id); + edge.from = parentNode; + edge.fromId = parentNode.id; } - /************************************ - Relative Time - ************************************/ + this._addToReroutedEdges(parentNode,childNode,edge); + } + }; - // helper function for moment.fn.from, moment.fn.fromNow, and moment.duration.fn.humanize - function substituteTimeAgo(string, number, withoutSuffix, isFuture, locale) { - return locale.relativeTime(number || 1, !!withoutSuffix, string, isFuture); + /** + * If a node is connected to itself, a circular edge is drawn. When clustering we want to contain + * these edges inside of the cluster. + * + * @param parentNode + * @param childNode + * @private + */ + exports._containCircularEdgesFromNode = function(parentNode, childNode) { + // manage all the edges connected to the child and parent nodes + for (var i = 0; i < parentNode.dynamicEdges.length; i++) { + var edge = parentNode.dynamicEdges[i]; + // handle circular edges + if (edge.toId == edge.fromId) { + this._addToContainedEdges(parentNode, childNode, edge); } + } + }; - function relativeTime(posNegDuration, withoutSuffix, locale) { - var duration = moment.duration(posNegDuration).abs(), - seconds = round(duration.as('s')), - minutes = round(duration.as('m')), - hours = round(duration.as('h')), - days = round(duration.as('d')), - months = round(duration.as('M')), - years = round(duration.as('y')), - - args = seconds < relativeTimeThresholds.s && ['s', seconds] || - minutes === 1 && ['m'] || - minutes < relativeTimeThresholds.m && ['mm', minutes] || - hours === 1 && ['h'] || - hours < relativeTimeThresholds.h && ['hh', hours] || - days === 1 && ['d'] || - days < relativeTimeThresholds.d && ['dd', days] || - months === 1 && ['M'] || - months < relativeTimeThresholds.M && ['MM', months] || - years === 1 && ['y'] || ['yy', years]; - args[2] = withoutSuffix; - args[3] = +posNegDuration > 0; - args[4] = locale; - return substituteTimeAgo.apply({}, args); - } + /** + * This adds an edge from the childNode to the rerouted edges of the parent node + * + * @param parentNode | Node object + * @param childNode | Node object + * @param edge | Edge object + * @private + */ + exports._addToReroutedEdges = function(parentNode, childNode, edge) { + // create an array object if it does not yet exist for this childNode + // we store the edge in the rerouted edges so we can restore it when the cluster pops open + if (!(parentNode.reroutedEdges.hasOwnProperty(childNode.id))) { + parentNode.reroutedEdges[childNode.id] = []; + } + parentNode.reroutedEdges[childNode.id].push(edge); + // this edge becomes part of the dynamicEdges of the cluster node + parentNode.dynamicEdges.push(edge); + }; - /************************************ - Week of Year - ************************************/ - // firstDayOfWeek 0 = sun, 6 = sat - // the day of the week that starts the week - // (usually sunday or monday) - // firstDayOfWeekOfYear 0 = sun, 6 = sat - // the first week is the week that contains the first - // of this day of the week - // (eg. ISO weeks use thursday (4)) - function weekOfYear(mom, firstDayOfWeek, firstDayOfWeekOfYear) { - var end = firstDayOfWeekOfYear - firstDayOfWeek, - daysToDayOfWeek = firstDayOfWeekOfYear - mom.day(), - adjustedMoment; + /** + * This function connects an edge that was connected to a cluster node back to the child node. + * + * @param parentNode | Node object + * @param childNode | Node object + * @private + */ + exports._connectEdgeBackToChild = function(parentNode, childNode) { + if (parentNode.reroutedEdges.hasOwnProperty(childNode.id)) { + for (var i = 0; i < parentNode.reroutedEdges[childNode.id].length; i++) { + var edge = parentNode.reroutedEdges[childNode.id][i]; + if (edge.originalFromId[edge.originalFromId.length-1] == childNode.id) { + edge.originalFromId.pop(); + edge.fromId = childNode.id; + edge.from = childNode; + } + else { + edge.originalToId.pop(); + edge.toId = childNode.id; + edge.to = childNode; + } + // append this edge to the list of edges connecting to the childnode + childNode.dynamicEdges.push(edge); - if (daysToDayOfWeek > end) { - daysToDayOfWeek -= 7; + // remove the edge from the parent object + for (var j = 0; j < parentNode.dynamicEdges.length; j++) { + if (parentNode.dynamicEdges[j].id == edge.id) { + parentNode.dynamicEdges.splice(j,1); + break; } + } + } + // remove the entry from the rerouted edges + delete parentNode.reroutedEdges[childNode.id]; + } + }; - if (daysToDayOfWeek < end - 7) { - daysToDayOfWeek += 7; - } - adjustedMoment = moment(mom).add(daysToDayOfWeek, 'd'); - return { - week: Math.ceil(adjustedMoment.dayOfYear() / 7), - year: adjustedMoment.year() - }; + /** + * When loops are clustered, an edge can be both in the rerouted array and the contained array. + * This function is called last to verify that all edges in dynamicEdges are in fact connected to the + * parentNode + * + * @param parentNode | Node object + * @private + */ + exports._validateEdges = function(parentNode) { + var dynamicEdges = [] + for (var i = 0; i < parentNode.dynamicEdges.length; i++) { + var edge = parentNode.dynamicEdges[i]; + if (parentNode.id == edge.toId || parentNode.id == edge.fromId) { + dynamicEdges.push(edge); } + } + parentNode.dynamicEdges = dynamicEdges; + }; - //http://en.wikipedia.org/wiki/ISO_week_date#Calculating_a_date_given_the_year.2C_week_number_and_weekday - function dayOfYearFromWeeks(year, week, weekday, firstDayOfWeekOfYear, firstDayOfWeek) { - var d = makeUTCDate(year, 0, 1).getUTCDay(), daysToAdd, dayOfYear; - d = d === 0 ? 7 : d; - weekday = weekday != null ? weekday : firstDayOfWeek; - daysToAdd = firstDayOfWeek - d + (d > firstDayOfWeekOfYear ? 7 : 0) - (d < firstDayOfWeek ? 7 : 0); - dayOfYear = 7 * (week - 1) + (weekday - firstDayOfWeek) + daysToAdd + 1; + /** + * This function released the contained edges back into the global domain and puts them back into the + * dynamic edges of both parent and child. + * + * @param {Node} parentNode | + * @param {Node} childNode | + * @private + */ + exports._releaseContainedEdges = function(parentNode, childNode) { + for (var i = 0; i < parentNode.containedEdges[childNode.id].length; i++) { + var edge = parentNode.containedEdges[childNode.id][i]; + + // put the edge back in the global edges object + this.edges[edge.id] = edge; + + // put the edge back in the dynamic edges of the child and parent + childNode.dynamicEdges.push(edge); + parentNode.dynamicEdges.push(edge); + } + // remove the entry from the contained edges + delete parentNode.containedEdges[childNode.id]; - return { - year: dayOfYear > 0 ? year : year - 1, - dayOfYear: dayOfYear > 0 ? dayOfYear : daysInYear(year - 1) + dayOfYear - }; - } + }; - /************************************ - Top Level Functions - ************************************/ - function makeMoment(config) { - var input = config._i, - format = config._f, - res; - config._locale = config._locale || moment.localeData(config._l); - if (input === null || (format === undefined && input === '')) { - return moment.invalid({nullInput: true}); - } + // ------------------- UTILITY FUNCTIONS ---------------------------- // - if (typeof input === 'string') { - config._i = input = config._locale.preparse(input); - } - if (moment.isMoment(input)) { - return new Moment(input, true); - } else if (format) { - if (isArray(format)) { - makeDateFromStringAndArray(config); - } else { - makeDateFromStringAndFormat(config); - } - } else { - makeDateFromInput(config); - } + /** + * This updates the node labels for all nodes (for debugging purposes) + */ + exports.updateLabels = function() { + var nodeId; + // update node labels + for (nodeId in this.nodes) { + if (this.nodes.hasOwnProperty(nodeId)) { + var node = this.nodes[nodeId]; + if (node.clusterSize > 1) { + node.label = "[".concat(String(node.clusterSize),"]"); + } + } + } - res = new Moment(config); - if (res._nextDay) { - // Adding is smart enough around DST - res.add(1, 'd'); - res._nextDay = undefined; + // update node labels + for (nodeId in this.nodes) { + if (this.nodes.hasOwnProperty(nodeId)) { + node = this.nodes[nodeId]; + if (node.clusterSize == 1) { + if (node.originalLabel !== undefined) { + node.label = node.originalLabel; } - - return res; + else { + node.label = String(node.id); + } + } } + } - moment = function (input, format, locale, strict) { - var c; + // /* Debug Override */ + // for (nodeId in this.nodes) { + // if (this.nodes.hasOwnProperty(nodeId)) { + // node = this.nodes[nodeId]; + // node.label = String(node.level); + // } + // } - if (typeof(locale) === 'boolean') { - strict = locale; - locale = undefined; - } - // object construction must be done this way. - // https://github.com/moment/moment/issues/1423 - c = {}; - c._isAMomentObject = true; - c._i = input; - c._f = format; - c._l = locale; - c._strict = strict; - c._isUTC = false; - c._pf = defaultParsingFlags(); + }; - return makeMoment(c); - }; - moment.suppressDeprecationWarnings = false; + /** + * We want to keep the cluster level distribution rather small. This means we do not want unclustered nodes + * if the rest of the nodes are already a few cluster levels in. + * To fix this we use this function. It determines the min and max cluster level and sends nodes that have not + * clustered enough to the clusterToSmallestNeighbours function. + */ + exports.normalizeClusterLevels = function() { + var maxLevel = 0; + var minLevel = 1e9; + var clusterLevel = 0; + var nodeId; - moment.createFromInputFallback = deprecate( - 'moment construction falls back to js Date. This is ' + - 'discouraged and will be removed in upcoming major ' + - 'release. Please refer to ' + - 'https://github.com/moment/moment/issues/1407 for more info.', - function (config) { - config._d = new Date(config._i + (config._useUTC ? ' UTC' : '')); - } - ); + // we loop over all nodes in the list + for (nodeId in this.nodes) { + if (this.nodes.hasOwnProperty(nodeId)) { + clusterLevel = this.nodes[nodeId].clusterSessions.length; + if (maxLevel < clusterLevel) {maxLevel = clusterLevel;} + if (minLevel > clusterLevel) {minLevel = clusterLevel;} + } + } - // Pick a moment m from moments so that m[fn](other) is true for all - // other. This relies on the function fn to be transitive. - // - // moments should either be an array of moment objects or an array, whose - // first element is an array of moment objects. - function pickBy(fn, moments) { - var res, i; - if (moments.length === 1 && isArray(moments[0])) { - moments = moments[0]; - } - if (!moments.length) { - return moment(); - } - res = moments[0]; - for (i = 1; i < moments.length; ++i) { - if (moments[i][fn](res)) { - res = moments[i]; - } + if (maxLevel - minLevel > this.constants.clustering.clusterLevelDifference) { + var amountOfNodes = this.nodeIndices.length; + var targetLevel = maxLevel - this.constants.clustering.clusterLevelDifference; + // we loop over all nodes in the list + for (nodeId in this.nodes) { + if (this.nodes.hasOwnProperty(nodeId)) { + if (this.nodes[nodeId].clusterSessions.length < targetLevel) { + this._clusterToSmallestNeighbour(this.nodes[nodeId]); } - return res; + } + } + this._updateNodeIndexList(); + this._updateDynamicEdges(); + // if a cluster was formed, we increase the clusterSession + if (this.nodeIndices.length != amountOfNodes) { + this.clusterSession += 1; } + } + }; - moment.min = function () { - var args = [].slice.call(arguments, 0); - return pickBy('isBefore', args); - }; - moment.max = function () { - var args = [].slice.call(arguments, 0); + /** + * This function determines if the cluster we want to decluster is in the active area + * this means around the zoom center + * + * @param {Node} node + * @returns {boolean} + * @private + */ + exports._nodeInActiveArea = function(node) { + return ( + Math.abs(node.x - this.areaCenter.x) <= this.constants.clustering.activeAreaBoxSize/this.scale + && + Math.abs(node.y - this.areaCenter.y) <= this.constants.clustering.activeAreaBoxSize/this.scale + ) + }; - return pickBy('isAfter', args); - }; - // creating with utc - moment.utc = function (input, format, locale, strict) { - var c; + /** + * This is an adaptation of the original repositioning function. This is called if the system is clustered initially + * It puts large clusters away from the center and randomizes the order. + * + */ + exports.repositionNodes = function() { + for (var i = 0; i < this.nodeIndices.length; i++) { + var node = this.nodes[this.nodeIndices[i]]; + if ((node.xFixed == false || node.yFixed == false)) { + var radius = 10 * 0.1*this.nodeIndices.length * Math.min(100,node.options.mass); + var angle = 2 * Math.PI * Math.random(); + if (node.xFixed == false) {node.x = radius * Math.cos(angle);} + if (node.yFixed == false) {node.y = radius * Math.sin(angle);} + this._repositionBezierNodes(node); + } + } + }; - if (typeof(locale) === 'boolean') { - strict = locale; - locale = undefined; - } - // object construction must be done this way. - // https://github.com/moment/moment/issues/1423 - c = {}; - c._isAMomentObject = true; - c._useUTC = true; - c._isUTC = true; - c._l = locale; - c._i = input; - c._f = format; - c._strict = strict; - c._pf = defaultParsingFlags(); - return makeMoment(c).utc(); - }; + /** + * We determine how many connections denote an important hub. + * We take the mean + 2*std as the important hub size. (Assuming a normal distribution of data, ~2.2%) + * + * @private + */ + exports._getHubSize = function() { + var average = 0; + var averageSquared = 0; + var hubCounter = 0; + var largestHub = 0; - // creating with unix timestamp (in seconds) - moment.unix = function (input) { - return moment(input * 1000); - }; + for (var i = 0; i < this.nodeIndices.length; i++) { - // duration - moment.duration = function (input, key) { - var duration = input, - // matching against regexp is expensive, do it on demand - match = null, - sign, - ret, - parseIso, - diffRes; + var node = this.nodes[this.nodeIndices[i]]; + if (node.dynamicEdgesLength > largestHub) { + largestHub = node.dynamicEdgesLength; + } + average += node.dynamicEdgesLength; + averageSquared += Math.pow(node.dynamicEdgesLength,2); + hubCounter += 1; + } + average = average / hubCounter; + averageSquared = averageSquared / hubCounter; - if (moment.isDuration(input)) { - duration = { - ms: input._milliseconds, - d: input._days, - M: input._months - }; - } else if (typeof input === 'number') { - duration = {}; - if (key) { - duration[key] = input; - } else { - duration.milliseconds = input; - } - } else if (!!(match = aspNetTimeSpanJsonRegex.exec(input))) { - sign = (match[1] === '-') ? -1 : 1; - duration = { - y: 0, - d: toInt(match[DATE]) * sign, - h: toInt(match[HOUR]) * sign, - m: toInt(match[MINUTE]) * sign, - s: toInt(match[SECOND]) * sign, - ms: toInt(match[MILLISECOND]) * sign - }; - } else if (!!(match = isoDurationRegex.exec(input))) { - sign = (match[1] === '-') ? -1 : 1; - parseIso = function (inp) { - // We'd normally use ~~inp for this, but unfortunately it also - // converts floats to ints. - // inp may be undefined, so careful calling replace on it. - var res = inp && parseFloat(inp.replace(',', '.')); - // apply sign while we're at it - return (isNaN(res) ? 0 : res) * sign; - }; - duration = { - y: parseIso(match[2]), - M: parseIso(match[3]), - d: parseIso(match[4]), - h: parseIso(match[5]), - m: parseIso(match[6]), - s: parseIso(match[7]), - w: parseIso(match[8]) - }; - } else if (duration == null) {// checks for null or undefined - duration = {}; - } else if (typeof duration === 'object' && - ('from' in duration || 'to' in duration)) { - diffRes = momentsDifference(moment(duration.from), moment(duration.to)); + var variance = averageSquared - Math.pow(average,2); - duration = {}; - duration.ms = diffRes.milliseconds; - duration.M = diffRes.months; - } + var standardDeviation = Math.sqrt(variance); - ret = new Duration(duration); + this.hubThreshold = Math.floor(average + 2*standardDeviation); - if (moment.isDuration(input) && hasOwnProp(input, '_locale')) { - ret._locale = input._locale; - } + // always have at least one to cluster + if (this.hubThreshold > largestHub) { + this.hubThreshold = largestHub; + } - return ret; - }; + // console.log("average",average,"averageSQ",averageSquared,"var",variance,"std",standardDeviation); + // console.log("hubThreshold:",this.hubThreshold); + }; - // version number - moment.version = VERSION; - // default format - moment.defaultFormat = isoFormat; + /** + * We reduce the amount of "extension nodes" or chains. These are not quickly clustered with the outliers and hubs methods + * with this amount we can cluster specifically on these chains. + * + * @param {Number} fraction | between 0 and 1, the percentage of chains to reduce + * @private + */ + exports._reduceAmountOfChains = function(fraction) { + this.hubThreshold = 2; + var reduceAmount = Math.floor(this.nodeIndices.length * fraction); + for (var nodeId in this.nodes) { + if (this.nodes.hasOwnProperty(nodeId)) { + if (this.nodes[nodeId].dynamicEdgesLength == 2 && this.nodes[nodeId].dynamicEdges.length >= 2) { + if (reduceAmount > 0) { + this._formClusterFromHub(this.nodes[nodeId],true,true,1); + reduceAmount -= 1; + } + } + } + } + }; - // constant that refers to the ISO standard - moment.ISO_8601 = function () {}; + /** + * We get the amount of "extension nodes" or chains. These are not quickly clustered with the outliers and hubs methods + * with this amount we can cluster specifically on these chains. + * + * @private + */ + exports._getChainFraction = function() { + var chains = 0; + var total = 0; + for (var nodeId in this.nodes) { + if (this.nodes.hasOwnProperty(nodeId)) { + if (this.nodes[nodeId].dynamicEdgesLength == 2 && this.nodes[nodeId].dynamicEdges.length >= 2) { + chains += 1; + } + total += 1; + } + } + return chains/total; + }; - // Plugins that add properties should also add the key here (null value), - // so we can properly clone ourselves. - moment.momentProperties = momentProperties; - // This function will be called whenever a moment is mutated. - // It is intended to keep the offset in sync with the timezone. - moment.updateOffset = function () {}; +/***/ }, +/* 65 */ +/***/ function(module, exports, __webpack_require__) { - // This function allows you to set a threshold for relative time strings - moment.relativeTimeThreshold = function (threshold, limit) { - if (relativeTimeThresholds[threshold] === undefined) { - return false; - } - if (limit === undefined) { - return relativeTimeThresholds[threshold]; - } - relativeTimeThresholds[threshold] = limit; - return true; - }; + var util = __webpack_require__(1); + var Node = __webpack_require__(56); - moment.lang = deprecate( - 'moment.lang is deprecated. Use moment.locale instead.', - function (key, value) { - return moment.locale(key, value); - } - ); + /** + * Creation of the SectorMixin var. + * + * This contains all the functions the Network object can use to employ the sector system. + * The sector system is always used by Network, though the benefits only apply to the use of clustering. + * If clustering is not used, there is no overhead except for a duplicate object with references to nodes and edges. + */ - // This function will load locale and then set the global locale. If - // no arguments are passed in, it will simply return the current global - // locale key. - moment.locale = function (key, values) { - var data; - if (key) { - if (typeof(values) !== 'undefined') { - data = moment.defineLocale(key, values); - } - else { - data = moment.localeData(key); - } + /** + * This function is only called by the setData function of the Network object. + * This loads the global references into the active sector. This initializes the sector. + * + * @private + */ + exports._putDataInSector = function() { + this.sectors["active"][this._sector()].nodes = this.nodes; + this.sectors["active"][this._sector()].edges = this.edges; + this.sectors["active"][this._sector()].nodeIndices = this.nodeIndices; + }; - if (data) { - moment.duration._locale = moment._locale = data; - } - } - return moment._locale._abbr; - }; + /** + * /** + * This function sets the global references to nodes, edges and nodeIndices back to + * those of the supplied (active) sector. If a type is defined, do the specific type + * + * @param {String} sectorId + * @param {String} [sectorType] | "active" or "frozen" + * @private + */ + exports._switchToSector = function(sectorId, sectorType) { + if (sectorType === undefined || sectorType == "active") { + this._switchToActiveSector(sectorId); + } + else { + this._switchToFrozenSector(sectorId); + } + }; - moment.defineLocale = function (name, values) { - if (values !== null) { - values.abbr = name; - if (!locales[name]) { - locales[name] = new Locale(); - } - locales[name].set(values); - // backwards compat for now: also set the locale - moment.locale(name); + /** + * This function sets the global references to nodes, edges and nodeIndices back to + * those of the supplied active sector. + * + * @param sectorId + * @private + */ + exports._switchToActiveSector = function(sectorId) { + this.nodeIndices = this.sectors["active"][sectorId]["nodeIndices"]; + this.nodes = this.sectors["active"][sectorId]["nodes"]; + this.edges = this.sectors["active"][sectorId]["edges"]; + }; - return locales[name]; - } else { - // useful for testing - delete locales[name]; - return null; - } - }; - moment.langData = deprecate( - 'moment.langData is deprecated. Use moment.localeData instead.', - function (key) { - return moment.localeData(key); - } - ); + /** + * This function sets the global references to nodes, edges and nodeIndices back to + * those of the supplied active sector. + * + * @private + */ + exports._switchToSupportSector = function() { + this.nodeIndices = this.sectors["support"]["nodeIndices"]; + this.nodes = this.sectors["support"]["nodes"]; + this.edges = this.sectors["support"]["edges"]; + }; + + + /** + * This function sets the global references to nodes, edges and nodeIndices back to + * those of the supplied frozen sector. + * + * @param sectorId + * @private + */ + exports._switchToFrozenSector = function(sectorId) { + this.nodeIndices = this.sectors["frozen"][sectorId]["nodeIndices"]; + this.nodes = this.sectors["frozen"][sectorId]["nodes"]; + this.edges = this.sectors["frozen"][sectorId]["edges"]; + }; - // returns locale data - moment.localeData = function (key) { - var locale; - if (key && key._locale && key._locale._abbr) { - key = key._locale._abbr; - } + /** + * This function sets the global references to nodes, edges and nodeIndices back to + * those of the currently active sector. + * + * @private + */ + exports._loadLatestSector = function() { + this._switchToSector(this._sector()); + }; - if (!key) { - return moment._locale; - } - if (!isArray(key)) { - //short-circuit everything else - locale = loadLocale(key); - if (locale) { - return locale; - } - key = [key]; - } + /** + * This function returns the currently active sector Id + * + * @returns {String} + * @private + */ + exports._sector = function() { + return this.activeSector[this.activeSector.length-1]; + }; - return chooseLocale(key); - }; - // compare moment object - moment.isMoment = function (obj) { - return obj instanceof Moment || - (obj != null && hasOwnProp(obj, '_isAMomentObject')); - }; + /** + * This function returns the previously active sector Id + * + * @returns {String} + * @private + */ + exports._previousSector = function() { + if (this.activeSector.length > 1) { + return this.activeSector[this.activeSector.length-2]; + } + else { + throw new TypeError('there are not enough sectors in the this.activeSector array.'); + } + }; - // for typechecking Duration objects - moment.isDuration = function (obj) { - return obj instanceof Duration; - }; - for (i = lists.length - 1; i >= 0; --i) { - makeList(lists[i]); - } + /** + * We add the active sector at the end of the this.activeSector array + * This ensures it is the currently active sector returned by _sector() and it reaches the top + * of the activeSector stack. When we reverse our steps we move from the end to the beginning of this stack. + * + * @param newId + * @private + */ + exports._setActiveSector = function(newId) { + this.activeSector.push(newId); + }; - moment.normalizeUnits = function (units) { - return normalizeUnits(units); - }; - moment.invalid = function (flags) { - var m = moment.utc(NaN); - if (flags != null) { - extend(m._pf, flags); - } - else { - m._pf.userInvalidated = true; - } + /** + * We remove the currently active sector id from the active sector stack. This happens when + * we reactivate the previously active sector + * + * @private + */ + exports._forgetLastSector = function() { + this.activeSector.pop(); + }; - return m; - }; - moment.parseZone = function () { - return moment.apply(null, arguments).parseZone(); - }; + /** + * This function creates a new active sector with the supplied newId. This newId + * is the expanding node id. + * + * @param {String} newId | Id of the new active sector + * @private + */ + exports._createNewSector = function(newId) { + // create the new sector + this.sectors["active"][newId] = {"nodes":{}, + "edges":{}, + "nodeIndices":[], + "formationScale": this.scale, + "drawingNode": undefined}; - moment.parseTwoDigitYear = function (input) { - return toInt(input) + (toInt(input) > 68 ? 1900 : 2000); - }; + // create the new sector render node. This gives visual feedback that you are in a new sector. + this.sectors["active"][newId]['drawingNode'] = new Node( + {id:newId, + color: { + background: "#eaefef", + border: "495c5e" + } + },{},{},this.constants); + this.sectors["active"][newId]['drawingNode'].clusterSize = 2; + }; - moment.isDate = isDate; - /************************************ - Moment Prototype - ************************************/ + /** + * This function removes the currently active sector. This is called when we create a new + * active sector. + * + * @param {String} sectorId | Id of the active sector that will be removed + * @private + */ + exports._deleteActiveSector = function(sectorId) { + delete this.sectors["active"][sectorId]; + }; - extend(moment.fn = Moment.prototype, { + /** + * This function removes the currently active sector. This is called when we reactivate + * the previously active sector. + * + * @param {String} sectorId | Id of the active sector that will be removed + * @private + */ + exports._deleteFrozenSector = function(sectorId) { + delete this.sectors["frozen"][sectorId]; + }; - clone : function () { - return moment(this); - }, - valueOf : function () { - return +this._d - ((this._offset || 0) * 60000); - }, + /** + * Freezing an active sector means moving it from the "active" object to the "frozen" object. + * We copy the references, then delete the active entree. + * + * @param sectorId + * @private + */ + exports._freezeSector = function(sectorId) { + // we move the set references from the active to the frozen stack. + this.sectors["frozen"][sectorId] = this.sectors["active"][sectorId]; - unix : function () { - return Math.floor(+this / 1000); - }, + // we have moved the sector data into the frozen set, we now remove it from the active set + this._deleteActiveSector(sectorId); + }; - toString : function () { - return this.clone().locale('en').format('ddd MMM DD YYYY HH:mm:ss [GMT]ZZ'); - }, - toDate : function () { - return this._offset ? new Date(+this) : this._d; - }, + /** + * This is the reverse operation of _freezeSector. Activating means moving the sector from the "frozen" + * object to the "active" object. + * + * @param sectorId + * @private + */ + exports._activateSector = function(sectorId) { + // we move the set references from the frozen to the active stack. + this.sectors["active"][sectorId] = this.sectors["frozen"][sectorId]; - toISOString : function () { - var m = moment(this).utc(); - if (0 < m.year() && m.year() <= 9999) { - if ('function' === typeof Date.prototype.toISOString) { - // native implementation is ~50x faster, use it when we can - return this.toDate().toISOString(); - } else { - return formatMoment(m, 'YYYY-MM-DD[T]HH:mm:ss.SSS[Z]'); - } - } else { - return formatMoment(m, 'YYYYYY-MM-DD[T]HH:mm:ss.SSS[Z]'); - } - }, + // we have moved the sector data into the active set, we now remove it from the frozen stack + this._deleteFrozenSector(sectorId); + }; - toArray : function () { - var m = this; - return [ - m.year(), - m.month(), - m.date(), - m.hours(), - m.minutes(), - m.seconds(), - m.milliseconds() - ]; - }, - isValid : function () { - return isValid(this); - }, + /** + * This function merges the data from the currently active sector with a frozen sector. This is used + * in the process of reverting back to the previously active sector. + * The data that is placed in the frozen (the previously active) sector is the node that has been removed from it + * upon the creation of a new active sector. + * + * @param sectorId + * @private + */ + exports._mergeThisWithFrozen = function(sectorId) { + // copy all nodes + for (var nodeId in this.nodes) { + if (this.nodes.hasOwnProperty(nodeId)) { + this.sectors["frozen"][sectorId]["nodes"][nodeId] = this.nodes[nodeId]; + } + } - isDSTShifted : function () { - if (this._a) { - return this.isValid() && compareArrays(this._a, (this._isUTC ? moment.utc(this._a) : moment(this._a)).toArray()) > 0; - } + // copy all edges (if not fully clustered, else there are no edges) + for (var edgeId in this.edges) { + if (this.edges.hasOwnProperty(edgeId)) { + this.sectors["frozen"][sectorId]["edges"][edgeId] = this.edges[edgeId]; + } + } - return false; - }, + // merge the nodeIndices + for (var i = 0; i < this.nodeIndices.length; i++) { + this.sectors["frozen"][sectorId]["nodeIndices"].push(this.nodeIndices[i]); + } + }; - parsingFlags : function () { - return extend({}, this._pf); - }, - invalidAt: function () { - return this._pf.overflow; - }, + /** + * This clusters the sector to one cluster. It was a single cluster before this process started so + * we revert to that state. The clusterToFit function with a maximum size of 1 node does this. + * + * @private + */ + exports._collapseThisToSingleCluster = function() { + this.clusterToFit(1,false); + }; - utc : function (keepLocalTime) { - return this.utcOffset(0, keepLocalTime); - }, - local : function (keepLocalTime) { - if (this._isUTC) { - this.utcOffset(0, keepLocalTime); - this._isUTC = false; + /** + * We create a new active sector from the node that we want to open. + * + * @param node + * @private + */ + exports._addSector = function(node) { + // this is the currently active sector + var sector = this._sector(); - if (keepLocalTime) { - this.subtract(this._dateUtcOffset(), 'm'); - } - } - return this; - }, + // // this should allow me to select nodes from a frozen set. + // if (this.sectors['active'][sector]["nodes"].hasOwnProperty(node.id)) { + // console.log("the node is part of the active sector"); + // } + // else { + // console.log("I dont know what the fuck happened!!"); + // } - format : function (inputString) { - var output = formatMoment(this, inputString || moment.defaultFormat); - return this.localeData().postformat(output); - }, + // when we switch to a new sector, we remove the node that will be expanded from the current nodes list. + delete this.nodes[node.id]; - add : createAdder(1, 'add'), + var unqiueIdentifier = util.randomUUID(); - subtract : createAdder(-1, 'subtract'), + // we fully freeze the currently active sector + this._freezeSector(sector); - diff : function (input, units, asFloat) { - var that = makeAs(input, this), - zoneDiff = (that.utcOffset() - this.utcOffset()) * 6e4, - anchor, diff, output, daysAdjust; + // we create a new active sector. This sector has the Id of the node to ensure uniqueness + this._createNewSector(unqiueIdentifier); - units = normalizeUnits(units); + // we add the active sector to the sectors array to be able to revert these steps later on + this._setActiveSector(unqiueIdentifier); - if (units === 'year' || units === 'month' || units === 'quarter') { - output = monthDiff(this, that); - if (units === 'quarter') { - output = output / 3; - } else if (units === 'year') { - output = output / 12; - } - } else { - diff = this - that; - output = units === 'second' ? diff / 1e3 : // 1000 - units === 'minute' ? diff / 6e4 : // 1000 * 60 - units === 'hour' ? diff / 36e5 : // 1000 * 60 * 60 - units === 'day' ? (diff - zoneDiff) / 864e5 : // 1000 * 60 * 60 * 24, negate dst - units === 'week' ? (diff - zoneDiff) / 6048e5 : // 1000 * 60 * 60 * 24 * 7, negate dst - diff; - } - return asFloat ? output : absRound(output); - }, + // we redirect the global references to the new sector's references. this._sector() now returns unqiueIdentifier + this._switchToSector(this._sector()); - from : function (time, withoutSuffix) { - return moment.duration({to: this, from: time}).locale(this.locale()).humanize(!withoutSuffix); - }, + // finally we add the node we removed from our previous active sector to the new active sector + this.nodes[node.id] = node; + }; - fromNow : function (withoutSuffix) { - return this.from(moment(), withoutSuffix); - }, - calendar : function (time) { - // We want to compare the start of today, vs this. - // Getting start-of-today depends on whether we're locat/utc/offset - // or not. - var now = time || moment(), - sod = makeAs(now, this).startOf('day'), - diff = this.diff(sod, 'days', true), - format = diff < -6 ? 'sameElse' : - diff < -1 ? 'lastWeek' : - diff < 0 ? 'lastDay' : - diff < 1 ? 'sameDay' : - diff < 2 ? 'nextDay' : - diff < 7 ? 'nextWeek' : 'sameElse'; - return this.format(this.localeData().calendar(format, this, moment(now))); - }, + /** + * We close the sector that is currently open and revert back to the one before. + * If the active sector is the "default" sector, nothing happens. + * + * @private + */ + exports._collapseSector = function() { + // the currently active sector + var sector = this._sector(); - isLeapYear : function () { - return isLeapYear(this.year()); - }, + // we cannot collapse the default sector + if (sector != "default") { + if ((this.nodeIndices.length == 1) || + (this.sectors["active"][sector]["drawingNode"].width*this.scale < this.constants.clustering.screenSizeThreshold * this.frame.canvas.clientWidth) || + (this.sectors["active"][sector]["drawingNode"].height*this.scale < this.constants.clustering.screenSizeThreshold * this.frame.canvas.clientHeight)) { + var previousSector = this._previousSector(); - isDST : function () { - return (this.utcOffset() > this.clone().month(0).utcOffset() || - this.utcOffset() > this.clone().month(5).utcOffset()); - }, + // we collapse the sector back to a single cluster + this._collapseThisToSingleCluster(); - day : function (input) { - var day = this._isUTC ? this._d.getUTCDay() : this._d.getDay(); - if (input != null) { - input = parseWeekday(input, this.localeData()); - return this.add(input - day, 'd'); - } else { - return day; - } - }, + // we move the remaining nodes, edges and nodeIndices to the previous sector. + // This previous sector is the one we will reactivate + this._mergeThisWithFrozen(previousSector); - month : makeAccessor('Month', true), + // the previously active (frozen) sector now has all the data from the currently active sector. + // we can now delete the active sector. + this._deleteActiveSector(sector); - startOf : function (units) { - units = normalizeUnits(units); - // the following switch intentionally omits break keywords - // to utilize falling through the cases. - switch (units) { - case 'year': - this.month(0); - /* falls through */ - case 'quarter': - case 'month': - this.date(1); - /* falls through */ - case 'week': - case 'isoWeek': - case 'day': - this.hours(0); - /* falls through */ - case 'hour': - this.minutes(0); - /* falls through */ - case 'minute': - this.seconds(0); - /* falls through */ - case 'second': - this.milliseconds(0); - /* falls through */ - } + // we activate the previously active (and currently frozen) sector. + this._activateSector(previousSector); - // weeks are a special case - if (units === 'week') { - this.weekday(0); - } else if (units === 'isoWeek') { - this.isoWeekday(1); - } + // we load the references from the newly active sector into the global references + this._switchToSector(previousSector); - // quarters are also special - if (units === 'quarter') { - this.month(Math.floor(this.month() / 3) * 3); - } + // we forget the previously active sector because we reverted to the one before + this._forgetLastSector(); - return this; - }, + // finally, we update the node index list. + this._updateNodeIndexList(); - endOf: function (units) { - units = normalizeUnits(units); - if (units === undefined || units === 'millisecond') { - return this; - } - return this.startOf(units).add(1, (units === 'isoWeek' ? 'week' : units)).subtract(1, 'ms'); - }, + // we refresh the list with calulation nodes and calculation node indices. + this._updateCalculationNodes(); + } + } + }; - isAfter: function (input, units) { - var inputMs; - units = normalizeUnits(typeof units !== 'undefined' ? units : 'millisecond'); - if (units === 'millisecond') { - input = moment.isMoment(input) ? input : moment(input); - return +this > +input; - } else { - inputMs = moment.isMoment(input) ? +input : +moment(input); - return inputMs < +this.clone().startOf(units); - } - }, - isBefore: function (input, units) { - var inputMs; - units = normalizeUnits(typeof units !== 'undefined' ? units : 'millisecond'); - if (units === 'millisecond') { - input = moment.isMoment(input) ? input : moment(input); - return +this < +input; - } else { - inputMs = moment.isMoment(input) ? +input : +moment(input); - return +this.clone().endOf(units) < inputMs; - } - }, + /** + * This runs a function in all active sectors. This is used in _redraw() and the _initializeForceCalculation(). + * + * @param {String} runFunction | This is the NAME of a function we want to call in all active sectors + * | we dont pass the function itself because then the "this" is the window object + * | instead of the Network object + * @param {*} [argument] | Optional: arguments to pass to the runFunction + * @private + */ + exports._doInAllActiveSectors = function(runFunction,argument) { + var returnValues = []; + if (argument === undefined) { + for (var sector in this.sectors["active"]) { + if (this.sectors["active"].hasOwnProperty(sector)) { + // switch the global references to those of this sector + this._switchToActiveSector(sector); + returnValues.push( this[runFunction]() ); + } + } + } + else { + for (var sector in this.sectors["active"]) { + if (this.sectors["active"].hasOwnProperty(sector)) { + // switch the global references to those of this sector + this._switchToActiveSector(sector); + var args = Array.prototype.splice.call(arguments, 1); + if (args.length > 1) { + returnValues.push( this[runFunction](args[0],args[1]) ); + } + else { + returnValues.push( this[runFunction](argument) ); + } + } + } + } + // we revert the global references back to our active sector + this._loadLatestSector(); + return returnValues; + }; + - isBetween: function (from, to, units) { - return this.isAfter(from, units) && this.isBefore(to, units); - }, + /** + * This runs a function in all active sectors. This is used in _redraw() and the _initializeForceCalculation(). + * + * @param {String} runFunction | This is the NAME of a function we want to call in all active sectors + * | we dont pass the function itself because then the "this" is the window object + * | instead of the Network object + * @param {*} [argument] | Optional: arguments to pass to the runFunction + * @private + */ + exports._doInSupportSector = function(runFunction,argument) { + var returnValues = false; + if (argument === undefined) { + this._switchToSupportSector(); + returnValues = this[runFunction](); + } + else { + this._switchToSupportSector(); + var args = Array.prototype.splice.call(arguments, 1); + if (args.length > 1) { + returnValues = this[runFunction](args[0],args[1]); + } + else { + returnValues = this[runFunction](argument); + } + } + // we revert the global references back to our active sector + this._loadLatestSector(); + return returnValues; + }; - isSame: function (input, units) { - var inputMs; - units = normalizeUnits(units || 'millisecond'); - if (units === 'millisecond') { - input = moment.isMoment(input) ? input : moment(input); - return +this === +input; - } else { - inputMs = +moment(input); - return +(this.clone().startOf(units)) <= inputMs && inputMs <= +(this.clone().endOf(units)); - } - }, - min: deprecate( - 'moment().min is deprecated, use moment.min instead. https://github.com/moment/moment/issues/1548', - function (other) { - other = moment.apply(null, arguments); - return other < this ? this : other; - } - ), + /** + * This runs a function in all frozen sectors. This is used in the _redraw(). + * + * @param {String} runFunction | This is the NAME of a function we want to call in all active sectors + * | we don't pass the function itself because then the "this" is the window object + * | instead of the Network object + * @param {*} [argument] | Optional: arguments to pass to the runFunction + * @private + */ + exports._doInAllFrozenSectors = function(runFunction,argument) { + if (argument === undefined) { + for (var sector in this.sectors["frozen"]) { + if (this.sectors["frozen"].hasOwnProperty(sector)) { + // switch the global references to those of this sector + this._switchToFrozenSector(sector); + this[runFunction](); + } + } + } + else { + for (var sector in this.sectors["frozen"]) { + if (this.sectors["frozen"].hasOwnProperty(sector)) { + // switch the global references to those of this sector + this._switchToFrozenSector(sector); + var args = Array.prototype.splice.call(arguments, 1); + if (args.length > 1) { + this[runFunction](args[0],args[1]); + } + else { + this[runFunction](argument); + } + } + } + } + this._loadLatestSector(); + }; - max: deprecate( - 'moment().max is deprecated, use moment.max instead. https://github.com/moment/moment/issues/1548', - function (other) { - other = moment.apply(null, arguments); - return other > this ? this : other; - } - ), - zone : deprecate( - 'moment().zone is deprecated, use moment().utcOffset instead. ' + - 'https://github.com/moment/moment/issues/1779', - function (input, keepLocalTime) { - if (input != null) { - if (typeof input !== 'string') { - input = -input; - } + /** + * This runs a function in all sectors. This is used in the _redraw(). + * + * @param {String} runFunction | This is the NAME of a function we want to call in all active sectors + * | we don't pass the function itself because then the "this" is the window object + * | instead of the Network object + * @param {*} [argument] | Optional: arguments to pass to the runFunction + * @private + */ + exports._doInAllSectors = function(runFunction,argument) { + var args = Array.prototype.splice.call(arguments, 1); + if (argument === undefined) { + this._doInAllActiveSectors(runFunction); + this._doInAllFrozenSectors(runFunction); + } + else { + if (args.length > 1) { + this._doInAllActiveSectors(runFunction,args[0],args[1]); + this._doInAllFrozenSectors(runFunction,args[0],args[1]); + } + else { + this._doInAllActiveSectors(runFunction,argument); + this._doInAllFrozenSectors(runFunction,argument); + } + } + }; - this.utcOffset(input, keepLocalTime); - return this; - } else { - return -this.utcOffset(); - } - } - ), + /** + * This clears the nodeIndices list. We cannot use this.nodeIndices = [] because we would break the link with the + * active sector. Thus we clear the nodeIndices in the active sector, then reconnect the this.nodeIndices to it. + * + * @private + */ + exports._clearNodeIndexList = function() { + var sector = this._sector(); + this.sectors["active"][sector]["nodeIndices"] = []; + this.nodeIndices = this.sectors["active"][sector]["nodeIndices"]; + }; - // keepLocalTime = true means only change the timezone, without - // affecting the local hour. So 5:31:26 +0300 --[utcOffset(2, true)]--> - // 5:31:26 +0200 It is possible that 5:31:26 doesn't exist with offset - // +0200, so we adjust the time as needed, to be valid. - // - // Keeping the time actually adds/subtracts (one hour) - // from the actual represented time. That is why we call updateOffset - // a second time. In case it wants us to change the offset again - // _changeInProgress == true case, then we have to adjust, because - // there is no such time in the given timezone. - utcOffset : function (input, keepLocalTime) { - var offset = this._offset || 0, - localAdjust; - if (input != null) { - if (typeof input === 'string') { - input = utcOffsetFromString(input); - } - if (Math.abs(input) < 16) { - input = input * 60; - } - if (!this._isUTC && keepLocalTime) { - localAdjust = this._dateUtcOffset(); - } - this._offset = input; - this._isUTC = true; - if (localAdjust != null) { - this.add(localAdjust, 'm'); - } - if (offset !== input) { - if (!keepLocalTime || this._changeInProgress) { - addOrSubtractDurationFromMoment(this, - moment.duration(input - offset, 'm'), 1, false); - } else if (!this._changeInProgress) { - this._changeInProgress = true; - moment.updateOffset(this, true); - this._changeInProgress = null; - } - } - return this; - } else { - return this._isUTC ? offset : this._dateUtcOffset(); - } - }, + /** + * Draw the encompassing sector node + * + * @param ctx + * @param sectorType + * @private + */ + exports._drawSectorNodes = function(ctx,sectorType) { + var minY = 1e9, maxY = -1e9, minX = 1e9, maxX = -1e9, node; + for (var sector in this.sectors[sectorType]) { + if (this.sectors[sectorType].hasOwnProperty(sector)) { + if (this.sectors[sectorType][sector]["drawingNode"] !== undefined) { - isLocal : function () { - return !this._isUTC; - }, + this._switchToSector(sector,sectorType); - isUtcOffset : function () { - return this._isUTC; - }, + minY = 1e9; maxY = -1e9; minX = 1e9; maxX = -1e9; + for (var nodeId in this.nodes) { + if (this.nodes.hasOwnProperty(nodeId)) { + node = this.nodes[nodeId]; + node.resize(ctx); + if (minX > node.x - 0.5 * node.width) {minX = node.x - 0.5 * node.width;} + if (maxX < node.x + 0.5 * node.width) {maxX = node.x + 0.5 * node.width;} + if (minY > node.y - 0.5 * node.height) {minY = node.y - 0.5 * node.height;} + if (maxY < node.y + 0.5 * node.height) {maxY = node.y + 0.5 * node.height;} + } + } + node = this.sectors[sectorType][sector]["drawingNode"]; + node.x = 0.5 * (maxX + minX); + node.y = 0.5 * (maxY + minY); + node.width = 2 * (node.x - minX); + node.height = 2 * (node.y - minY); + node.options.radius = Math.sqrt(Math.pow(0.5*node.width,2) + Math.pow(0.5*node.height,2)); + node.setScale(this.scale); + node._drawCircle(ctx); + } + } + } + }; - isUtc : function () { - return this._isUTC && this._offset === 0; - }, + exports._drawAllSectorNodes = function(ctx) { + this._drawSectorNodes(ctx,"frozen"); + this._drawSectorNodes(ctx,"active"); + this._loadLatestSector(); + }; - zoneAbbr : function () { - return this._isUTC ? 'UTC' : ''; - }, - zoneName : function () { - return this._isUTC ? 'Coordinated Universal Time' : ''; - }, +/***/ }, +/* 66 */ +/***/ function(module, exports, __webpack_require__) { - parseZone : function () { - if (this._tzm) { - this.utcOffset(this._tzm); - } else if (typeof this._i === 'string') { - this.utcOffset(utcOffsetFromString(this._i)); - } - return this; - }, + var Node = __webpack_require__(56); - hasAlignedHourOffset : function (input) { - if (!input) { - input = 0; - } - else { - input = moment(input).utcOffset(); - } + /** + * This function can be called from the _doInAllSectors function + * + * @param object + * @param overlappingNodes + * @private + */ + exports._getNodesOverlappingWith = function(object, overlappingNodes) { + var nodes = this.nodes; + for (var nodeId in nodes) { + if (nodes.hasOwnProperty(nodeId)) { + if (nodes[nodeId].isOverlappingWith(object)) { + overlappingNodes.push(nodeId); + } + } + } + }; - return (this.utcOffset() - input) % 60 === 0; - }, + /** + * retrieve all nodes overlapping with given object + * @param {Object} object An object with parameters left, top, right, bottom + * @return {Number[]} An array with id's of the overlapping nodes + * @private + */ + exports._getAllNodesOverlappingWith = function (object) { + var overlappingNodes = []; + this._doInAllActiveSectors("_getNodesOverlappingWith",object,overlappingNodes); + return overlappingNodes; + }; - daysInMonth : function () { - return daysInMonth(this.year(), this.month()); - }, - dayOfYear : function (input) { - var dayOfYear = round((moment(this).startOf('day') - moment(this).startOf('year')) / 864e5) + 1; - return input == null ? dayOfYear : this.add((input - dayOfYear), 'd'); - }, + /** + * Return a position object in canvasspace from a single point in screenspace + * + * @param pointer + * @returns {{left: number, top: number, right: number, bottom: number}} + * @private + */ + exports._pointerToPositionObject = function(pointer) { + var x = this._XconvertDOMtoCanvas(pointer.x); + var y = this._YconvertDOMtoCanvas(pointer.y); - quarter : function (input) { - return input == null ? Math.ceil((this.month() + 1) / 3) : this.month((input - 1) * 3 + this.month() % 3); - }, + return { + left: x, + top: y, + right: x, + bottom: y + }; + }; - weekYear : function (input) { - var year = weekOfYear(this, this.localeData()._week.dow, this.localeData()._week.doy).year; - return input == null ? year : this.add((input - year), 'y'); - }, - isoWeekYear : function (input) { - var year = weekOfYear(this, 1, 4).year; - return input == null ? year : this.add((input - year), 'y'); - }, + /** + * Get the top node at the a specific point (like a click) + * + * @param {{x: Number, y: Number}} pointer + * @return {Node | null} node + * @private + */ + exports._getNodeAt = function (pointer) { + // we first check if this is an navigation controls element + var positionObject = this._pointerToPositionObject(pointer); + var overlappingNodes = this._getAllNodesOverlappingWith(positionObject); - week : function (input) { - var week = this.localeData().week(this); - return input == null ? week : this.add((input - week) * 7, 'd'); - }, + // if there are overlapping nodes, select the last one, this is the + // one which is drawn on top of the others + if (overlappingNodes.length > 0) { + return this.nodes[overlappingNodes[overlappingNodes.length - 1]]; + } + else { + return null; + } + }; - isoWeek : function (input) { - var week = weekOfYear(this, 1, 4).week; - return input == null ? week : this.add((input - week) * 7, 'd'); - }, - weekday : function (input) { - var weekday = (this.day() + 7 - this.localeData()._week.dow) % 7; - return input == null ? weekday : this.add(input - weekday, 'd'); - }, + /** + * retrieve all edges overlapping with given object, selector is around center + * @param {Object} object An object with parameters left, top, right, bottom + * @return {Number[]} An array with id's of the overlapping nodes + * @private + */ + exports._getEdgesOverlappingWith = function (object, overlappingEdges) { + var edges = this.edges; + for (var edgeId in edges) { + if (edges.hasOwnProperty(edgeId)) { + if (edges[edgeId].isOverlappingWith(object)) { + overlappingEdges.push(edgeId); + } + } + } + }; - isoWeekday : function (input) { - // behaves the same as moment#day except - // as a getter, returns 7 instead of 0 (1-7 range instead of 0-6) - // as a setter, sunday should belong to the previous week. - return input == null ? this.day() || 7 : this.day(this.day() % 7 ? input : input - 7); - }, - isoWeeksInYear : function () { - return weeksInYear(this.year(), 1, 4); - }, + /** + * retrieve all nodes overlapping with given object + * @param {Object} object An object with parameters left, top, right, bottom + * @return {Number[]} An array with id's of the overlapping nodes + * @private + */ + exports._getAllEdgesOverlappingWith = function (object) { + var overlappingEdges = []; + this._doInAllActiveSectors("_getEdgesOverlappingWith",object,overlappingEdges); + return overlappingEdges; + }; - weeksInYear : function () { - var weekInfo = this.localeData()._week; - return weeksInYear(this.year(), weekInfo.dow, weekInfo.doy); - }, + /** + * Place holder. To implement change the _getNodeAt to a _getObjectAt. Have the _getObjectAt call + * _getNodeAt and _getEdgesAt, then priortize the selection to user preferences. + * + * @param pointer + * @returns {null} + * @private + */ + exports._getEdgeAt = function(pointer) { + var positionObject = this._pointerToPositionObject(pointer); + var overlappingEdges = this._getAllEdgesOverlappingWith(positionObject); - get : function (units) { - units = normalizeUnits(units); - return this[units](); - }, + if (overlappingEdges.length > 0) { + return this.edges[overlappingEdges[overlappingEdges.length - 1]]; + } + else { + return null; + } + }; - set : function (units, value) { - var unit; - if (typeof units === 'object') { - for (unit in units) { - this.set(unit, units[unit]); - } - } - else { - units = normalizeUnits(units); - if (typeof this[units] === 'function') { - this[units](value); - } - } - return this; - }, - // If passed a locale key, it will set the locale for this - // instance. Otherwise, it will return the locale configuration - // variables for this instance. - locale : function (key) { - var newLocaleData; + /** + * Add object to the selection array. + * + * @param obj + * @private + */ + exports._addToSelection = function(obj) { + if (obj instanceof Node) { + this.selectionObj.nodes[obj.id] = obj; + } + else { + this.selectionObj.edges[obj.id] = obj; + } + }; - if (key === undefined) { - return this._locale._abbr; - } else { - newLocaleData = moment.localeData(key); - if (newLocaleData != null) { - this._locale = newLocaleData; - } - return this; - } - }, + /** + * Add object to the selection array. + * + * @param obj + * @private + */ + exports._addToHover = function(obj) { + if (obj instanceof Node) { + this.hoverObj.nodes[obj.id] = obj; + } + else { + this.hoverObj.edges[obj.id] = obj; + } + }; - lang : deprecate( - 'moment().lang() is deprecated. Instead, use moment().localeData() to get the language configuration. Use moment().locale() to change languages.', - function (key) { - if (key === undefined) { - return this.localeData(); - } else { - return this.locale(key); - } - } - ), - localeData : function () { - return this._locale; - }, + /** + * Remove a single option from selection. + * + * @param {Object} obj + * @private + */ + exports._removeFromSelection = function(obj) { + if (obj instanceof Node) { + delete this.selectionObj.nodes[obj.id]; + } + else { + delete this.selectionObj.edges[obj.id]; + } + }; - _dateUtcOffset : function () { - // On Firefox.24 Date#getTimezoneOffset returns a floating point. - // https://github.com/moment/moment/pull/1871 - return -Math.round(this._d.getTimezoneOffset() / 15) * 15; - } + /** + * Unselect all. The selectionObj is useful for this. + * + * @param {Boolean} [doNotTrigger] | ignore trigger + * @private + */ + exports._unselectAll = function(doNotTrigger) { + if (doNotTrigger === undefined) { + doNotTrigger = false; + } + for(var nodeId in this.selectionObj.nodes) { + if(this.selectionObj.nodes.hasOwnProperty(nodeId)) { + this.selectionObj.nodes[nodeId].unselect(); + } + } + for(var edgeId in this.selectionObj.edges) { + if(this.selectionObj.edges.hasOwnProperty(edgeId)) { + this.selectionObj.edges[edgeId].unselect(); + } + } - }); + this.selectionObj = {nodes:{},edges:{}}; - function rawMonthSetter(mom, value) { - var dayOfMonth; + if (doNotTrigger == false) { + this.emit('select', this.getSelection()); + } + }; - // TODO: Move this out of here! - if (typeof value === 'string') { - value = mom.localeData().monthsParse(value); - // TODO: Another silent failure? - if (typeof value !== 'number') { - return mom; - } - } + /** + * Unselect all clusters. The selectionObj is useful for this. + * + * @param {Boolean} [doNotTrigger] | ignore trigger + * @private + */ + exports._unselectClusters = function(doNotTrigger) { + if (doNotTrigger === undefined) { + doNotTrigger = false; + } - dayOfMonth = Math.min(mom.date(), - daysInMonth(mom.year(), value)); - mom._d['set' + (mom._isUTC ? 'UTC' : '') + 'Month'](value, dayOfMonth); - return mom; + for (var nodeId in this.selectionObj.nodes) { + if (this.selectionObj.nodes.hasOwnProperty(nodeId)) { + if (this.selectionObj.nodes[nodeId].clusterSize > 1) { + this.selectionObj.nodes[nodeId].unselect(); + this._removeFromSelection(this.selectionObj.nodes[nodeId]); + } } + } - function rawGetter(mom, unit) { - return mom._d['get' + (mom._isUTC ? 'UTC' : '') + unit](); - } + if (doNotTrigger == false) { + this.emit('select', this.getSelection()); + } + }; - function rawSetter(mom, unit, value) { - if (unit === 'Month') { - return rawMonthSetter(mom, value); - } else { - return mom._d['set' + (mom._isUTC ? 'UTC' : '') + unit](value); - } - } - function makeAccessor(unit, keepTime) { - return function (value) { - if (value != null) { - rawSetter(this, unit, value); - moment.updateOffset(this, keepTime); - return this; - } else { - return rawGetter(this, unit); - } - }; + /** + * return the number of selected nodes + * + * @returns {number} + * @private + */ + exports._getSelectedNodeCount = function() { + var count = 0; + for (var nodeId in this.selectionObj.nodes) { + if (this.selectionObj.nodes.hasOwnProperty(nodeId)) { + count += 1; } + } + return count; + }; - moment.fn.millisecond = moment.fn.milliseconds = makeAccessor('Milliseconds', false); - moment.fn.second = moment.fn.seconds = makeAccessor('Seconds', false); - moment.fn.minute = moment.fn.minutes = makeAccessor('Minutes', false); - // Setting the hour should keep the time, because the user explicitly - // specified which hour he wants. So trying to maintain the same hour (in - // a new timezone) makes sense. Adding/subtracting hours does not follow - // this rule. - moment.fn.hour = moment.fn.hours = makeAccessor('Hours', true); - // moment.fn.month is defined separately - moment.fn.date = makeAccessor('Date', true); - moment.fn.dates = deprecate('dates accessor is deprecated. Use date instead.', makeAccessor('Date', true)); - moment.fn.year = makeAccessor('FullYear', true); - moment.fn.years = deprecate('years accessor is deprecated. Use year instead.', makeAccessor('FullYear', true)); - - // add plural methods - moment.fn.days = moment.fn.day; - moment.fn.months = moment.fn.month; - moment.fn.weeks = moment.fn.week; - moment.fn.isoWeeks = moment.fn.isoWeek; - moment.fn.quarters = moment.fn.quarter; + /** + * return the selected node + * + * @returns {number} + * @private + */ + exports._getSelectedNode = function() { + for (var nodeId in this.selectionObj.nodes) { + if (this.selectionObj.nodes.hasOwnProperty(nodeId)) { + return this.selectionObj.nodes[nodeId]; + } + } + return null; + }; - // add aliased format methods - moment.fn.toJSON = moment.fn.toISOString; + /** + * return the selected edge + * + * @returns {number} + * @private + */ + exports._getSelectedEdge = function() { + for (var edgeId in this.selectionObj.edges) { + if (this.selectionObj.edges.hasOwnProperty(edgeId)) { + return this.selectionObj.edges[edgeId]; + } + } + return null; + }; - // alias isUtc for dev-friendliness - moment.fn.isUTC = moment.fn.isUtc; - /************************************ - Duration Prototype - ************************************/ + /** + * return the number of selected edges + * + * @returns {number} + * @private + */ + exports._getSelectedEdgeCount = function() { + var count = 0; + for (var edgeId in this.selectionObj.edges) { + if (this.selectionObj.edges.hasOwnProperty(edgeId)) { + count += 1; + } + } + return count; + }; - function daysToYears (days) { - // 400 years have 146097 days (taking into account leap year rules) - return days * 400 / 146097; + /** + * return the number of selected objects. + * + * @returns {number} + * @private + */ + exports._getSelectedObjectCount = function() { + var count = 0; + for(var nodeId in this.selectionObj.nodes) { + if(this.selectionObj.nodes.hasOwnProperty(nodeId)) { + count += 1; } - - function yearsToDays (years) { - // years * 365 + absRound(years / 4) - - // absRound(years / 100) + absRound(years / 400); - return years * 146097 / 400; + } + for(var edgeId in this.selectionObj.edges) { + if(this.selectionObj.edges.hasOwnProperty(edgeId)) { + count += 1; } + } + return count; + }; - extend(moment.duration.fn = Duration.prototype, { - - _bubble : function () { - var milliseconds = this._milliseconds, - days = this._days, - months = this._months, - data = this._data, - seconds, minutes, hours, years = 0; + /** + * Check if anything is selected + * + * @returns {boolean} + * @private + */ + exports._selectionIsEmpty = function() { + for(var nodeId in this.selectionObj.nodes) { + if(this.selectionObj.nodes.hasOwnProperty(nodeId)) { + return false; + } + } + for(var edgeId in this.selectionObj.edges) { + if(this.selectionObj.edges.hasOwnProperty(edgeId)) { + return false; + } + } + return true; + }; - // The following code bubbles up values, see the tests for - // examples of what that means. - data.milliseconds = milliseconds % 1000; - seconds = absRound(milliseconds / 1000); - data.seconds = seconds % 60; + /** + * check if one of the selected nodes is a cluster. + * + * @returns {boolean} + * @private + */ + exports._clusterInSelection = function() { + for(var nodeId in this.selectionObj.nodes) { + if(this.selectionObj.nodes.hasOwnProperty(nodeId)) { + if (this.selectionObj.nodes[nodeId].clusterSize > 1) { + return true; + } + } + } + return false; + }; - minutes = absRound(seconds / 60); - data.minutes = minutes % 60; + /** + * select the edges connected to the node that is being selected + * + * @param {Node} node + * @private + */ + exports._selectConnectedEdges = function(node) { + for (var i = 0; i < node.dynamicEdges.length; i++) { + var edge = node.dynamicEdges[i]; + edge.select(); + this._addToSelection(edge); + } + }; - hours = absRound(minutes / 60); - data.hours = hours % 24; + /** + * select the edges connected to the node that is being selected + * + * @param {Node} node + * @private + */ + exports._hoverConnectedEdges = function(node) { + for (var i = 0; i < node.dynamicEdges.length; i++) { + var edge = node.dynamicEdges[i]; + edge.hover = true; + this._addToHover(edge); + } + }; - days += absRound(hours / 24); - // Accurately convert days to years, assume start from year 0. - years = absRound(daysToYears(days)); - days -= absRound(yearsToDays(years)); + /** + * unselect the edges connected to the node that is being selected + * + * @param {Node} node + * @private + */ + exports._unselectConnectedEdges = function(node) { + for (var i = 0; i < node.dynamicEdges.length; i++) { + var edge = node.dynamicEdges[i]; + edge.unselect(); + this._removeFromSelection(edge); + } + }; - // 30 days to a month - // TODO (iskren): Use anchor date (like 1st Jan) to compute this. - months += absRound(days / 30); - days %= 30; - // 12 months -> 1 year - years += absRound(months / 12); - months %= 12; - data.days = days; - data.months = months; - data.years = years; - }, - abs : function () { - this._milliseconds = Math.abs(this._milliseconds); - this._days = Math.abs(this._days); - this._months = Math.abs(this._months); + /** + * This is called when someone clicks on a node. either select or deselect it. + * If there is an existing selection and we don't want to append to it, clear the existing selection + * + * @param {Node || Edge} object + * @param {Boolean} append + * @param {Boolean} [doNotTrigger] | ignore trigger + * @private + */ + exports._selectObject = function(object, append, doNotTrigger, highlightEdges, overrideSelectable) { + if (doNotTrigger === undefined) { + doNotTrigger = false; + } + if (highlightEdges === undefined) { + highlightEdges = true; + } - this._data.milliseconds = Math.abs(this._data.milliseconds); - this._data.seconds = Math.abs(this._data.seconds); - this._data.minutes = Math.abs(this._data.minutes); - this._data.hours = Math.abs(this._data.hours); - this._data.months = Math.abs(this._data.months); - this._data.years = Math.abs(this._data.years); + if (this._selectionIsEmpty() == false && append == false && this.forceAppendSelection == false) { + this._unselectAll(true); + } - return this; - }, + // selectable allows the object to be selected. Override can be used if needed to bypass this. + if (object.selected == false && (this.constants.selectable == true || overrideSelectable)) { + object.select(); + this._addToSelection(object); + if (object instanceof Node && this.blockConnectingEdgeSelection == false && highlightEdges == true) { + this._selectConnectedEdges(object); + } + } + // do not select the object if selectable is false, only add it to selection to allow drag to work + else if (object.selected == false) { + this._addToSelection(object); + doNotTrigger = true; + } + else { + object.unselect(); + this._removeFromSelection(object); + } - weeks : function () { - return absRound(this.days() / 7); - }, + if (doNotTrigger == false) { + this.emit('select', this.getSelection()); + } + }; - valueOf : function () { - return this._milliseconds + - this._days * 864e5 + - (this._months % 12) * 2592e6 + - toInt(this._months / 12) * 31536e6; - }, - humanize : function (withSuffix) { - var output = relativeTime(this, !withSuffix, this.localeData()); + /** + * This is called when someone clicks on a node. either select or deselect it. + * If there is an existing selection and we don't want to append to it, clear the existing selection + * + * @param {Node || Edge} object + * @private + */ + exports._blurObject = function(object) { + if (object.hover == true) { + object.hover = false; + this.emit("blurNode",{node:object.id}); + } + }; - if (withSuffix) { - output = this.localeData().pastFuture(+this, output); - } + /** + * This is called when someone clicks on a node. either select or deselect it. + * If there is an existing selection and we don't want to append to it, clear the existing selection + * + * @param {Node || Edge} object + * @private + */ + exports._hoverObject = function(object) { + if (object.hover == false) { + object.hover = true; + this._addToHover(object); + if (object instanceof Node) { + this.emit("hoverNode",{node:object.id}); + } + } + if (object instanceof Node) { + this._hoverConnectedEdges(object); + } + }; - return this.localeData().postformat(output); - }, - add : function (input, val) { - // supports only 2.0-style add(1, 's') or add(moment) - var dur = moment.duration(input, val); + /** + * handles the selection part of the touch, only for navigation controls elements; + * Touch is triggered before tap, also before hold. Hold triggers after a while. + * This is the most responsive solution + * + * @param {Object} pointer + * @private + */ + exports._handleTouch = function(pointer) { + }; - this._milliseconds += dur._milliseconds; - this._days += dur._days; - this._months += dur._months; - this._bubble(); + /** + * handles the selection part of the tap; + * + * @param {Object} pointer + * @private + */ + exports._handleTap = function(pointer) { + var node = this._getNodeAt(pointer); + if (node != null) { + this._selectObject(node, false); + } + else { + var edge = this._getEdgeAt(pointer); + if (edge != null) { + this._selectObject(edge, false); + } + else { + this._unselectAll(); + } + } + var properties = this.getSelection(); + properties['pointer'] = { + DOM: {x: pointer.x, y: pointer.y}, + canvas: {x: this._XconvertDOMtoCanvas(pointer.x), y: this._YconvertDOMtoCanvas(pointer.y)} + } + this.emit("click", properties); + this._redraw(); + }; - return this; - }, - subtract : function (input, val) { - var dur = moment.duration(input, val); + /** + * handles the selection part of the double tap and opens a cluster if needed + * + * @param {Object} pointer + * @private + */ + exports._handleDoubleTap = function(pointer) { + var node = this._getNodeAt(pointer); + if (node != null && node !== undefined) { + // we reset the areaCenter here so the opening of the node will occur + this.areaCenter = {"x" : this._XconvertDOMtoCanvas(pointer.x), + "y" : this._YconvertDOMtoCanvas(pointer.y)}; + this.openCluster(node); + } + var properties = this.getSelection(); + properties['pointer'] = { + DOM: {x: pointer.x, y: pointer.y}, + canvas: {x: this._XconvertDOMtoCanvas(pointer.x), y: this._YconvertDOMtoCanvas(pointer.y)} + } + this.emit("doubleClick", properties); + }; - this._milliseconds -= dur._milliseconds; - this._days -= dur._days; - this._months -= dur._months; - this._bubble(); + /** + * Handle the onHold selection part + * + * @param pointer + * @private + */ + exports._handleOnHold = function(pointer) { + var node = this._getNodeAt(pointer); + if (node != null) { + this._selectObject(node,true); + } + else { + var edge = this._getEdgeAt(pointer); + if (edge != null) { + this._selectObject(edge,true); + } + } + this._redraw(); + }; - return this; - }, - get : function (units) { - units = normalizeUnits(units); - return this[units.toLowerCase() + 's'](); - }, + /** + * handle the onRelease event. These functions are here for the navigation controls module + * and data manipulation module. + * + * @private + */ + exports._handleOnRelease = function(pointer) { + this._manipulationReleaseOverload(pointer); + this._navigationReleaseOverload(pointer); + }; - as : function (units) { - var days, months; - units = normalizeUnits(units); + exports._manipulationReleaseOverload = function (pointer) {}; + exports._navigationReleaseOverload = function (pointer) {}; - if (units === 'month' || units === 'year') { - days = this._days + this._milliseconds / 864e5; - months = this._months + daysToYears(days) * 12; - return units === 'month' ? months : months / 12; - } else { - // handle milliseconds separately because of floating point math errors (issue #1867) - days = this._days + Math.round(yearsToDays(this._months / 12)); - switch (units) { - case 'week': return days / 7 + this._milliseconds / 6048e5; - case 'day': return days + this._milliseconds / 864e5; - case 'hour': return days * 24 + this._milliseconds / 36e5; - case 'minute': return days * 24 * 60 + this._milliseconds / 6e4; - case 'second': return days * 24 * 60 * 60 + this._milliseconds / 1000; - // Math.floor prevents floating point math errors here - case 'millisecond': return Math.floor(days * 24 * 60 * 60 * 1000) + this._milliseconds; - default: throw new Error('Unknown unit ' + units); - } - } - }, + /** + * + * retrieve the currently selected objects + * @return {{nodes: Array., edges: Array.}} selection + */ + exports.getSelection = function() { + var nodeIds = this.getSelectedNodes(); + var edgeIds = this.getSelectedEdges(); + return {nodes:nodeIds, edges:edgeIds}; + }; - lang : moment.fn.lang, - locale : moment.fn.locale, + /** + * + * retrieve the currently selected nodes + * @return {String[]} selection An array with the ids of the + * selected nodes. + */ + exports.getSelectedNodes = function() { + var idArray = []; + if (this.constants.selectable == true) { + for (var nodeId in this.selectionObj.nodes) { + if (this.selectionObj.nodes.hasOwnProperty(nodeId)) { + idArray.push(nodeId); + } + } + } + return idArray + }; - toIsoString : deprecate( - 'toIsoString() is deprecated. Please use toISOString() instead ' + - '(notice the capitals)', - function () { - return this.toISOString(); - } - ), + /** + * + * retrieve the currently selected edges + * @return {Array} selection An array with the ids of the + * selected nodes. + */ + exports.getSelectedEdges = function() { + var idArray = []; + if (this.constants.selectable == true) { + for (var edgeId in this.selectionObj.edges) { + if (this.selectionObj.edges.hasOwnProperty(edgeId)) { + idArray.push(edgeId); + } + } + } + return idArray; + }; - toISOString : function () { - // inspired by https://github.com/dordille/moment-isoduration/blob/master/moment.isoduration.js - var years = Math.abs(this.years()), - months = Math.abs(this.months()), - days = Math.abs(this.days()), - hours = Math.abs(this.hours()), - minutes = Math.abs(this.minutes()), - seconds = Math.abs(this.seconds() + this.milliseconds() / 1000); - if (!this.asSeconds()) { - // this is the same as C#'s (Noda) and python (isodate)... - // but not other JS (goog.date) - return 'P0D'; - } + /** + * select zero or more nodes DEPRICATED + * @param {Number[] | String[]} selection An array with the ids of the + * selected nodes. + */ + exports.setSelection = function() { + console.log("setSelection is deprecated. Please use selectNodes instead.") + }; - return (this.asSeconds() < 0 ? '-' : '') + - 'P' + - (years ? years + 'Y' : '') + - (months ? months + 'M' : '') + - (days ? days + 'D' : '') + - ((hours || minutes || seconds) ? 'T' : '') + - (hours ? hours + 'H' : '') + - (minutes ? minutes + 'M' : '') + - (seconds ? seconds + 'S' : ''); - }, - localeData : function () { - return this._locale; - }, + /** + * select zero or more nodes with the option to highlight edges + * @param {Number[] | String[]} selection An array with the ids of the + * selected nodes. + * @param {boolean} [highlightEdges] + */ + exports.selectNodes = function(selection, highlightEdges) { + var i, iMax, id; - toJSON : function () { - return this.toISOString(); - } - }); + if (!selection || (selection.length == undefined)) + throw 'Selection must be an array with ids'; - moment.duration.fn.toString = moment.duration.fn.toISOString; + // first unselect any selected node + this._unselectAll(true); - function makeDurationGetter(name) { - moment.duration.fn[name] = function () { - return this._data[name]; - }; - } + for (i = 0, iMax = selection.length; i < iMax; i++) { + id = selection[i]; - for (i in unitMillisecondFactors) { - if (hasOwnProp(unitMillisecondFactors, i)) { - makeDurationGetter(i.toLowerCase()); - } + var node = this.nodes[id]; + if (!node) { + throw new RangeError('Node with id "' + id + '" not found'); } + this._selectObject(node,true,true,highlightEdges,true); + } + this.redraw(); + }; - moment.duration.fn.asMilliseconds = function () { - return this.as('ms'); - }; - moment.duration.fn.asSeconds = function () { - return this.as('s'); - }; - moment.duration.fn.asMinutes = function () { - return this.as('m'); - }; - moment.duration.fn.asHours = function () { - return this.as('h'); - }; - moment.duration.fn.asDays = function () { - return this.as('d'); - }; - moment.duration.fn.asWeeks = function () { - return this.as('weeks'); - }; - moment.duration.fn.asMonths = function () { - return this.as('M'); - }; - moment.duration.fn.asYears = function () { - return this.as('y'); - }; - - /************************************ - Default Locale - ************************************/ + /** + * select zero or more edges + * @param {Number[] | String[]} selection An array with the ids of the + * selected nodes. + */ + exports.selectEdges = function(selection) { + var i, iMax, id; - // Set default locale, other locale will inherit from English. - moment.locale('en', { - ordinalParse: /\d{1,2}(th|st|nd|rd)/, - ordinal : function (number) { - var b = number % 10, - output = (toInt(number % 100 / 10) === 1) ? 'th' : - (b === 1) ? 'st' : - (b === 2) ? 'nd' : - (b === 3) ? 'rd' : 'th'; - return number + output; - } - }); + if (!selection || (selection.length == undefined)) + throw 'Selection must be an array with ids'; - /* EMBED_LOCALES */ + // first unselect any selected node + this._unselectAll(true); - /************************************ - Exposing Moment - ************************************/ + for (i = 0, iMax = selection.length; i < iMax; i++) { + id = selection[i]; - function makeGlobal(shouldDeprecate) { - /*global ender:false */ - if (typeof ender !== 'undefined') { - return; - } - oldGlobalMoment = globalScope.moment; - if (shouldDeprecate) { - globalScope.moment = deprecate( - 'Accessing Moment through the global scope is ' + - 'deprecated, and will be removed in an upcoming ' + - 'release.', - moment); - } else { - globalScope.moment = moment; - } + var edge = this.edges[id]; + if (!edge) { + throw new RangeError('Edge with id "' + id + '" not found'); } + this._selectObject(edge,true,true,false,true); + } + this.redraw(); + }; - // CommonJS module is defined - if (hasModule) { - module.exports = moment; - } else if (true) { - !(__WEBPACK_AMD_DEFINE_RESULT__ = function (require, exports, module) { - if (module.config && module.config() && module.config().noGlobal === true) { - // release the global variable - globalScope.moment = oldGlobalMoment; - } - - return moment; - }.call(exports, __webpack_require__, exports, module), __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__)); - makeGlobal(true); - } else { - makeGlobal(); + /** + * Validate the selection: remove ids of nodes which no longer exist + * @private + */ + exports._updateSelection = function () { + for(var nodeId in this.selectionObj.nodes) { + if(this.selectionObj.nodes.hasOwnProperty(nodeId)) { + if (!this.nodes.hasOwnProperty(nodeId)) { + delete this.selectionObj.nodes[nodeId]; + } } - }).call(this); - - /* WEBPACK VAR INJECTION */}.call(exports, (function() { return this; }()), __webpack_require__(71)(module))) + } + for(var edgeId in this.selectionObj.edges) { + if(this.selectionObj.edges.hasOwnProperty(edgeId)) { + if (!this.edges.hasOwnProperty(edgeId)) { + delete this.selectionObj.edges[edgeId]; + } + } + } + }; + /***/ }, -/* 66 */ +/* 67 */ /***/ function(module, exports, __webpack_require__) { var util = __webpack_require__(1); - var RepulsionMixin = __webpack_require__(68); - var HierarchialRepulsionMixin = __webpack_require__(69); - var BarnesHutMixin = __webpack_require__(70); + var Node = __webpack_require__(56); + var Edge = __webpack_require__(57); /** - * Toggling barnes Hut calculation on and off. + * clears the toolbar div element of children * * @private */ - exports._toggleBarnesHut = function () { - this.constants.physics.barnesHut.enabled = !this.constants.physics.barnesHut.enabled; - this._loadSelectedForceSolver(); - this.moving = true; - this.start(); - }; + exports._clearManipulatorBar = function() { + this._recursiveDOMDelete(this.manipulationDiv); + this.manipulationDOM = {}; + this._manipulationReleaseOverload = function () {}; + delete this.sectors['support']['nodes']['targetNode']; + delete this.sectors['support']['nodes']['targetViaNode']; + this.controlNodesActive = false; + this.freezeSimulation = false; + }; /** - * This loads the node force solver based on the barnes hut or repulsion algorithm + * Manipulation UI temporarily overloads certain functions to extend or replace them. To be able to restore + * these functions to their original functionality, we saved them in this.cachedFunctions. + * This function restores these functions to their original function. * * @private */ - exports._loadSelectedForceSolver = function () { - // this overloads the this._calculateNodeForces - if (this.constants.physics.barnesHut.enabled == true) { - this._clearMixin(RepulsionMixin); - this._clearMixin(HierarchialRepulsionMixin); - - this.constants.physics.centralGravity = this.constants.physics.barnesHut.centralGravity; - this.constants.physics.springLength = this.constants.physics.barnesHut.springLength; - this.constants.physics.springConstant = this.constants.physics.barnesHut.springConstant; - this.constants.physics.damping = this.constants.physics.barnesHut.damping; - - this._loadMixin(BarnesHutMixin); + exports._restoreOverloadedFunctions = function() { + for (var functionName in this.cachedFunctions) { + if (this.cachedFunctions.hasOwnProperty(functionName)) { + this[functionName] = this.cachedFunctions[functionName]; + delete this.cachedFunctions[functionName]; + } } - else if (this.constants.physics.hierarchicalRepulsion.enabled == true) { - this._clearMixin(BarnesHutMixin); - this._clearMixin(RepulsionMixin); - - this.constants.physics.centralGravity = this.constants.physics.hierarchicalRepulsion.centralGravity; - this.constants.physics.springLength = this.constants.physics.hierarchicalRepulsion.springLength; - this.constants.physics.springConstant = this.constants.physics.hierarchicalRepulsion.springConstant; - this.constants.physics.damping = this.constants.physics.hierarchicalRepulsion.damping; + }; - this._loadMixin(HierarchialRepulsionMixin); + /** + * Enable or disable edit-mode. + * + * @private + */ + exports._toggleEditMode = function() { + this.editMode = !this.editMode; + var toolbar = this.manipulationDiv; + var closeDiv = this.closeDiv; + var editModeDiv = this.editModeDiv; + if (this.editMode == true) { + toolbar.style.display="block"; + closeDiv.style.display="block"; + editModeDiv.style.display="none"; + closeDiv.onclick = this._toggleEditMode.bind(this); } else { - this._clearMixin(BarnesHutMixin); - this._clearMixin(HierarchialRepulsionMixin); - this.barnesHutTree = undefined; - - this.constants.physics.centralGravity = this.constants.physics.repulsion.centralGravity; - this.constants.physics.springLength = this.constants.physics.repulsion.springLength; - this.constants.physics.springConstant = this.constants.physics.repulsion.springConstant; - this.constants.physics.damping = this.constants.physics.repulsion.damping; - - this._loadMixin(RepulsionMixin); + toolbar.style.display="none"; + closeDiv.style.display="none"; + editModeDiv.style.display="block"; + closeDiv.onclick = null; } + this._createManipulatorBar() }; /** - * Before calculating the forces, we check if we need to cluster to keep up performance and we check - * if there is more than one node. If it is just one node, we dont calculate anything. + * main function, creates the main toolbar. Removes functions bound to the select event. Binds all the buttons of the toolbar. * * @private */ - exports._initializeForceCalculation = function () { - // stop calculation if there is only one node - if (this.nodeIndices.length == 1) { - this.nodes[this.nodeIndices[0]]._setForce(0, 0); + exports._createManipulatorBar = function() { + // remove bound functions + if (this.boundFunction) { + this.off('select', this.boundFunction); } - else { - // if there are too many nodes on screen, we cluster without repositioning - if (this.nodeIndices.length > this.constants.clustering.clusterThreshold && this.constants.clustering.enabled == true) { - this.clusterToFit(this.constants.clustering.reduceToNodes, false); - } - // we now start the force calculation - this._calculateForces(); + var locale = this.constants.locales[this.constants.locale]; + + if (this.edgeBeingEdited !== undefined) { + this.edgeBeingEdited._disableControlNodes(); + this.edgeBeingEdited = undefined; + this.selectedControlNode = null; + this.controlNodesActive = false; + this._redraw(); } - }; + // restore overloaded functions + this._restoreOverloadedFunctions(); - /** - * Calculate the external forces acting on the nodes - * Forces are caused by: edges, repulsing forces between nodes, gravity - * @private - */ - exports._calculateForces = function () { - // Gravity is required to keep separated groups from floating off - // the forces are reset to zero in this loop by using _setForce instead - // of _addForce + // resume calculation + this.freezeSimulation = false; - this._calculateGravitationalForces(); - this._calculateNodeForces(); + // reset global variables + this.blockConnectingEdgeSelection = false; + this.forceAppendSelection = false; + this.manipulationDOM = {}; - if (this.constants.physics.springConstant > 0) { - if (this.constants.smoothCurves.enabled == true && this.constants.smoothCurves.dynamic == true) { - this._calculateSpringForcesWithSupport(); - } - else { - if (this.constants.physics.hierarchicalRepulsion.enabled == true) { - this._calculateHierarchicalSpringForces(); - } - else { - this._calculateSpringForces(); - } + if (this.editMode == true) { + while (this.manipulationDiv.hasChildNodes()) { + this.manipulationDiv.removeChild(this.manipulationDiv.firstChild); } - } - }; + this.manipulationDOM['addNodeSpan'] = document.createElement('span'); + this.manipulationDOM['addNodeSpan'].className = 'network-manipulationUI add'; + this.manipulationDOM['addNodeLabelSpan'] = document.createElement('span'); + this.manipulationDOM['addNodeLabelSpan'].className = 'network-manipulationLabel'; + this.manipulationDOM['addNodeLabelSpan'].innerHTML = locale['addNode']; + this.manipulationDOM['addNodeSpan'].appendChild(this.manipulationDOM['addNodeLabelSpan']); + + this.manipulationDOM['seperatorLineDiv1'] = document.createElement('div'); + this.manipulationDOM['seperatorLineDiv1'].className = 'network-seperatorLine'; - /** - * Smooth curves are created by adding invisible nodes in the center of the edges. These nodes are also - * handled in the calculateForces function. We then use a quadratic curve with the center node as control. - * This function joins the datanodes and invisible (called support) nodes into one object. - * We do this so we do not contaminate this.nodes with the support nodes. - * - * @private - */ - exports._updateCalculationNodes = function () { - if (this.constants.smoothCurves.enabled == true && this.constants.smoothCurves.dynamic == true) { - this.calculationNodes = {}; - this.calculationNodeIndices = []; + this.manipulationDOM['addEdgeSpan'] = document.createElement('span'); + this.manipulationDOM['addEdgeSpan'].className = 'network-manipulationUI connect'; + this.manipulationDOM['addEdgeLabelSpan'] = document.createElement('span'); + this.manipulationDOM['addEdgeLabelSpan'].className = 'network-manipulationLabel'; + this.manipulationDOM['addEdgeLabelSpan'].innerHTML = locale['addEdge']; + this.manipulationDOM['addEdgeSpan'].appendChild(this.manipulationDOM['addEdgeLabelSpan']); - for (var nodeId in this.nodes) { - if (this.nodes.hasOwnProperty(nodeId)) { - this.calculationNodes[nodeId] = this.nodes[nodeId]; - } + this.manipulationDiv.appendChild(this.manipulationDOM['addNodeSpan']); + this.manipulationDiv.appendChild(this.manipulationDOM['seperatorLineDiv1']); + this.manipulationDiv.appendChild(this.manipulationDOM['addEdgeSpan']); + + if (this._getSelectedNodeCount() == 1 && this.triggerFunctions.edit) { + this.manipulationDOM['seperatorLineDiv2'] = document.createElement('div'); + this.manipulationDOM['seperatorLineDiv2'].className = 'network-seperatorLine'; + + this.manipulationDOM['editNodeSpan'] = document.createElement('span'); + this.manipulationDOM['editNodeSpan'].className = 'network-manipulationUI edit'; + this.manipulationDOM['editNodeLabelSpan'] = document.createElement('span'); + this.manipulationDOM['editNodeLabelSpan'].className = 'network-manipulationLabel'; + this.manipulationDOM['editNodeLabelSpan'].innerHTML = locale['editNode']; + this.manipulationDOM['editNodeSpan'].appendChild(this.manipulationDOM['editNodeLabelSpan']); + + this.manipulationDiv.appendChild(this.manipulationDOM['seperatorLineDiv2']); + this.manipulationDiv.appendChild(this.manipulationDOM['editNodeSpan']); } - var supportNodes = this.sectors['support']['nodes']; - for (var supportNodeId in supportNodes) { - if (supportNodes.hasOwnProperty(supportNodeId)) { - if (this.edges.hasOwnProperty(supportNodes[supportNodeId].parentEdgeId)) { - this.calculationNodes[supportNodeId] = supportNodes[supportNodeId]; - } - else { - supportNodes[supportNodeId]._setForce(0, 0); - } - } + else if (this._getSelectedEdgeCount() == 1 && this._getSelectedNodeCount() == 0) { + this.manipulationDOM['seperatorLineDiv3'] = document.createElement('div'); + this.manipulationDOM['seperatorLineDiv3'].className = 'network-seperatorLine'; + + this.manipulationDOM['editEdgeSpan'] = document.createElement('span'); + this.manipulationDOM['editEdgeSpan'].className = 'network-manipulationUI edit'; + this.manipulationDOM['editEdgeLabelSpan'] = document.createElement('span'); + this.manipulationDOM['editEdgeLabelSpan'].className = 'network-manipulationLabel'; + this.manipulationDOM['editEdgeLabelSpan'].innerHTML = locale['editEdge']; + this.manipulationDOM['editEdgeSpan'].appendChild(this.manipulationDOM['editEdgeLabelSpan']); + + this.manipulationDiv.appendChild(this.manipulationDOM['seperatorLineDiv3']); + this.manipulationDiv.appendChild(this.manipulationDOM['editEdgeSpan']); } + if (this._selectionIsEmpty() == false) { + this.manipulationDOM['seperatorLineDiv4'] = document.createElement('div'); + this.manipulationDOM['seperatorLineDiv4'].className = 'network-seperatorLine'; - for (var idx in this.calculationNodes) { - if (this.calculationNodes.hasOwnProperty(idx)) { - this.calculationNodeIndices.push(idx); - } + this.manipulationDOM['deleteSpan'] = document.createElement('span'); + this.manipulationDOM['deleteSpan'].className = 'network-manipulationUI delete'; + this.manipulationDOM['deleteLabelSpan'] = document.createElement('span'); + this.manipulationDOM['deleteLabelSpan'].className = 'network-manipulationLabel'; + this.manipulationDOM['deleteLabelSpan'].innerHTML = locale['del']; + this.manipulationDOM['deleteSpan'].appendChild(this.manipulationDOM['deleteLabelSpan']); + + this.manipulationDiv.appendChild(this.manipulationDOM['seperatorLineDiv4']); + this.manipulationDiv.appendChild(this.manipulationDOM['deleteSpan']); + } + + + // bind the icons + this.manipulationDOM['addNodeSpan'].onclick = this._createAddNodeToolbar.bind(this); + this.manipulationDOM['addEdgeSpan'].onclick = this._createAddEdgeToolbar.bind(this); + if (this._getSelectedNodeCount() == 1 && this.triggerFunctions.edit) { + this.manipulationDOM['editNodeSpan'].onclick = this._editNode.bind(this); + } + else if (this._getSelectedEdgeCount() == 1 && this._getSelectedNodeCount() == 0) { + this.manipulationDOM['editEdgeSpan'].onclick = this._createEditEdgeToolbar.bind(this); } + if (this._selectionIsEmpty() == false) { + this.manipulationDOM['deleteSpan'].onclick = this._deleteSelected.bind(this); + } + this.closeDiv.onclick = this._toggleEditMode.bind(this); + + var me = this; + this.boundFunction = me._createManipulatorBar; + this.on('select', this.boundFunction); } else { - this.calculationNodes = this.nodes; - this.calculationNodeIndices = this.nodeIndices; + while (this.editModeDiv.hasChildNodes()) { + this.editModeDiv.removeChild(this.editModeDiv.firstChild); + } + + this.manipulationDOM['editModeSpan'] = document.createElement('span'); + this.manipulationDOM['editModeSpan'].className = 'network-manipulationUI edit editmode'; + this.manipulationDOM['editModeLabelSpan'] = document.createElement('span'); + this.manipulationDOM['editModeLabelSpan'].className = 'network-manipulationLabel'; + this.manipulationDOM['editModeLabelSpan'].innerHTML = locale['edit']; + this.manipulationDOM['editModeSpan'].appendChild(this.manipulationDOM['editModeLabelSpan']); + + this.editModeDiv.appendChild(this.manipulationDOM['editModeSpan']); + + this.manipulationDOM['editModeSpan'].onclick = this._toggleEditMode.bind(this); } }; + /** - * this function applies the central gravity effect to keep groups from floating off + * Create the toolbar for adding Nodes * * @private */ - exports._calculateGravitationalForces = function () { - var dx, dy, distance, node, i; - var nodes = this.calculationNodes; - var gravity = this.constants.physics.centralGravity; - var gravityForce = 0; + exports._createAddNodeToolbar = function() { + // clear the toolbar + this._clearManipulatorBar(); + if (this.boundFunction) { + this.off('select', this.boundFunction); + } - for (i = 0; i < this.calculationNodeIndices.length; i++) { - node = nodes[this.calculationNodeIndices[i]]; - node.damping = this.constants.physics.damping; // possibly add function to alter damping properties of clusters. - // gravity does not apply when we are in a pocket sector - if (this._sector() == "default" && gravity != 0) { - dx = -node.x; - dy = -node.y; - distance = Math.sqrt(dx * dx + dy * dy); + var locale = this.constants.locales[this.constants.locale]; - gravityForce = (distance == 0) ? 0 : (gravity / distance); - node.fx = dx * gravityForce; - node.fy = dy * gravityForce; - } - else { - node.fx = 0; - node.fy = 0; - } - } - }; + this.manipulationDOM = {}; + this.manipulationDOM['backSpan'] = document.createElement('span'); + this.manipulationDOM['backSpan'].className = 'network-manipulationUI back'; + this.manipulationDOM['backLabelSpan'] = document.createElement('span'); + this.manipulationDOM['backLabelSpan'].className = 'network-manipulationLabel'; + this.manipulationDOM['backLabelSpan'].innerHTML = locale['back']; + this.manipulationDOM['backSpan'].appendChild(this.manipulationDOM['backLabelSpan']); + + this.manipulationDOM['seperatorLineDiv1'] = document.createElement('div'); + this.manipulationDOM['seperatorLineDiv1'].className = 'network-seperatorLine'; + + this.manipulationDOM['descriptionSpan'] = document.createElement('span'); + this.manipulationDOM['descriptionSpan'].className = 'network-manipulationUI none'; + this.manipulationDOM['descriptionLabelSpan'] = document.createElement('span'); + this.manipulationDOM['descriptionLabelSpan'].className = 'network-manipulationLabel'; + this.manipulationDOM['descriptionLabelSpan'].innerHTML = locale['addDescription']; + this.manipulationDOM['descriptionSpan'].appendChild(this.manipulationDOM['descriptionLabelSpan']); + + this.manipulationDiv.appendChild(this.manipulationDOM['backSpan']); + this.manipulationDiv.appendChild(this.manipulationDOM['seperatorLineDiv1']); + this.manipulationDiv.appendChild(this.manipulationDOM['descriptionSpan']); + // bind the icon + this.manipulationDOM['backSpan'].onclick = this._createManipulatorBar.bind(this); + // we use the boundFunction so we can reference it when we unbind it from the "select" event. + var me = this; + this.boundFunction = me._addNode; + this.on('select', this.boundFunction); + }; /** - * this function calculates the effects of the springs in the case of unsmooth curves. + * create the toolbar to connect nodes * * @private */ - exports._calculateSpringForces = function () { - var edgeLength, edge, edgeId; - var dx, dy, fx, fy, springForce, distance; - var edges = this.edges; + exports._createAddEdgeToolbar = function() { + // clear the toolbar + this._clearManipulatorBar(); + this._unselectAll(true); + this.freezeSimulation = true; - // forces caused by the edges, modelled as springs - for (edgeId in edges) { - if (edges.hasOwnProperty(edgeId)) { - edge = edges[edgeId]; - if (edge.connected) { - // only calculate forces if nodes are in the same sector - if (this.nodes.hasOwnProperty(edge.toId) && this.nodes.hasOwnProperty(edge.fromId)) { - edgeLength = edge.physics.springLength; - // this implies that the edges between big clusters are longer - edgeLength += (edge.to.clusterSize + edge.from.clusterSize - 2) * this.constants.clustering.edgeGrowth; + if (this.boundFunction) { + this.off('select', this.boundFunction); + } - dx = (edge.from.x - edge.to.x); - dy = (edge.from.y - edge.to.y); - distance = Math.sqrt(dx * dx + dy * dy); + var locale = this.constants.locales[this.constants.locale]; - if (distance == 0) { - distance = 0.01; - } + this._unselectAll(); + this.forceAppendSelection = false; + this.blockConnectingEdgeSelection = true; - // the 1/distance is so the fx and fy can be calculated without sine or cosine. - springForce = this.constants.physics.springConstant * (edgeLength - distance) / distance; + this.manipulationDOM = {}; + this.manipulationDOM['backSpan'] = document.createElement('span'); + this.manipulationDOM['backSpan'].className = 'network-manipulationUI back'; + this.manipulationDOM['backLabelSpan'] = document.createElement('span'); + this.manipulationDOM['backLabelSpan'].className = 'network-manipulationLabel'; + this.manipulationDOM['backLabelSpan'].innerHTML = locale['back']; + this.manipulationDOM['backSpan'].appendChild(this.manipulationDOM['backLabelSpan']); - fx = dx * springForce; - fy = dy * springForce; + this.manipulationDOM['seperatorLineDiv1'] = document.createElement('div'); + this.manipulationDOM['seperatorLineDiv1'].className = 'network-seperatorLine'; - edge.from.fx += fx; - edge.from.fy += fy; - edge.to.fx -= fx; - edge.to.fy -= fy; - } - } - } - } - }; + this.manipulationDOM['descriptionSpan'] = document.createElement('span'); + this.manipulationDOM['descriptionSpan'].className = 'network-manipulationUI none'; + this.manipulationDOM['descriptionLabelSpan'] = document.createElement('span'); + this.manipulationDOM['descriptionLabelSpan'].className = 'network-manipulationLabel'; + this.manipulationDOM['descriptionLabelSpan'].innerHTML = locale['edgeDescription']; + this.manipulationDOM['descriptionSpan'].appendChild(this.manipulationDOM['descriptionLabelSpan']); + + this.manipulationDiv.appendChild(this.manipulationDOM['backSpan']); + this.manipulationDiv.appendChild(this.manipulationDOM['seperatorLineDiv1']); + this.manipulationDiv.appendChild(this.manipulationDOM['descriptionSpan']); + + // bind the icon + this.manipulationDOM['backSpan'].onclick = this._createManipulatorBar.bind(this); + // we use the boundFunction so we can reference it when we unbind it from the "select" event. + var me = this; + this.boundFunction = me._handleConnect; + this.on('select', this.boundFunction); + // temporarily overload functions + this.cachedFunctions["_handleTouch"] = this._handleTouch; + this.cachedFunctions["_manipulationReleaseOverload"] = this._manipulationReleaseOverload; + this.cachedFunctions["_handleDragStart"] = this._handleDragStart; + this.cachedFunctions["_handleDragEnd"] = this._handleDragEnd; + this.cachedFunctions["_handleOnHold"] = this._handleOnHold; + this._handleTouch = this._handleConnect; + this._manipulationReleaseOverload = function () {}; + this._handleOnHold = function () {}; + this._handleDragStart = function () {}; + this._handleDragEnd = this._finishConnect; + // redraw to show the unselect + this._redraw(); + }; /** - * This function calculates the springforces on the nodes, accounting for the support nodes. + * create the toolbar to edit edges * * @private */ - exports._calculateSpringForcesWithSupport = function () { - var edgeLength, edge, edgeId, combinedClusterSize; - var edges = this.edges; + exports._createEditEdgeToolbar = function() { + // clear the toolbar + this._clearManipulatorBar(); + this.controlNodesActive = true; - // forces caused by the edges, modelled as springs - for (edgeId in edges) { - if (edges.hasOwnProperty(edgeId)) { - edge = edges[edgeId]; - if (edge.connected) { - // only calculate forces if nodes are in the same sector - if (this.nodes.hasOwnProperty(edge.toId) && this.nodes.hasOwnProperty(edge.fromId)) { - if (edge.via != null) { - var node1 = edge.to; - var node2 = edge.via; - var node3 = edge.from; + if (this.boundFunction) { + this.off('select', this.boundFunction); + } - edgeLength = edge.physics.springLength; + this.edgeBeingEdited = this._getSelectedEdge(); + this.edgeBeingEdited._enableControlNodes(); - combinedClusterSize = node1.clusterSize + node3.clusterSize - 2; + var locale = this.constants.locales[this.constants.locale]; - // this implies that the edges between big clusters are longer - edgeLength += combinedClusterSize * this.constants.clustering.edgeGrowth; - this._calculateSpringForce(node1, node2, 0.5 * edgeLength); - this._calculateSpringForce(node2, node3, 0.5 * edgeLength); - } - } - } - } - } + this.manipulationDOM = {}; + this.manipulationDOM['backSpan'] = document.createElement('span'); + this.manipulationDOM['backSpan'].className = 'network-manipulationUI back'; + this.manipulationDOM['backLabelSpan'] = document.createElement('span'); + this.manipulationDOM['backLabelSpan'].className = 'network-manipulationLabel'; + this.manipulationDOM['backLabelSpan'].innerHTML = locale['back']; + this.manipulationDOM['backSpan'].appendChild(this.manipulationDOM['backLabelSpan']); + + this.manipulationDOM['seperatorLineDiv1'] = document.createElement('div'); + this.manipulationDOM['seperatorLineDiv1'].className = 'network-seperatorLine'; + + this.manipulationDOM['descriptionSpan'] = document.createElement('span'); + this.manipulationDOM['descriptionSpan'].className = 'network-manipulationUI none'; + this.manipulationDOM['descriptionLabelSpan'] = document.createElement('span'); + this.manipulationDOM['descriptionLabelSpan'].className = 'network-manipulationLabel'; + this.manipulationDOM['descriptionLabelSpan'].innerHTML = locale['editEdgeDescription']; + this.manipulationDOM['descriptionSpan'].appendChild(this.manipulationDOM['descriptionLabelSpan']); + + this.manipulationDiv.appendChild(this.manipulationDOM['backSpan']); + this.manipulationDiv.appendChild(this.manipulationDOM['seperatorLineDiv1']); + this.manipulationDiv.appendChild(this.manipulationDOM['descriptionSpan']); + + // bind the icon + this.manipulationDOM['backSpan'].onclick = this._createManipulatorBar.bind(this); + + // temporarily overload functions + this.cachedFunctions["_handleTouch"] = this._handleTouch; + this.cachedFunctions["_manipulationReleaseOverload"] = this._manipulationReleaseOverload; + this.cachedFunctions["_handleTap"] = this._handleTap; + this.cachedFunctions["_handleDragStart"] = this._handleDragStart; + this.cachedFunctions["_handleOnDrag"] = this._handleOnDrag; + this._handleTouch = this._selectControlNode; + this._handleTap = function () {}; + this._handleOnDrag = this._controlNodeDrag; + this._handleDragStart = function () {} + this._manipulationReleaseOverload = this._releaseControlNode; + + // redraw to show the unselect + this._redraw(); }; /** - * This is the code actually performing the calculation for the function above. It is split out to avoid repetition. + * the function bound to the selection event. It checks if you want to connect a cluster and changes the description + * to walk the user through the process. * - * @param node1 - * @param node2 - * @param edgeLength * @private */ - exports._calculateSpringForce = function (node1, node2, edgeLength) { - var dx, dy, fx, fy, springForce, distance; - - dx = (node1.x - node2.x); - dy = (node1.y - node2.y); - distance = Math.sqrt(dx * dx + dy * dy); - - if (distance == 0) { - distance = 0.01; + exports._selectControlNode = function(pointer) { + this.edgeBeingEdited.controlNodes.from.unselect(); + this.edgeBeingEdited.controlNodes.to.unselect(); + this.selectedControlNode = this.edgeBeingEdited._getSelectedControlNode(this._XconvertDOMtoCanvas(pointer.x),this._YconvertDOMtoCanvas(pointer.y)); + if (this.selectedControlNode !== null) { + this.selectedControlNode.select(); + this.freezeSimulation = true; } + this._redraw(); + }; - // the 1/distance is so the fx and fy can be calculated without sine or cosine. - springForce = this.constants.physics.springConstant * (edgeLength - distance) / distance; - - fx = dx * springForce; - fy = dy * springForce; - node1.fx += fx; - node1.fy += fy; - node2.fx -= fx; - node2.fy -= fy; + /** + * the function bound to the selection event. It checks if you want to connect a cluster and changes the description + * to walk the user through the process. + * + * @private + */ + exports._controlNodeDrag = function(event) { + var pointer = this._getPointer(event.gesture.center); + if (this.selectedControlNode !== null && this.selectedControlNode !== undefined) { + this.selectedControlNode.x = this._XconvertDOMtoCanvas(pointer.x); + this.selectedControlNode.y = this._YconvertDOMtoCanvas(pointer.y); + } + this._redraw(); }; - exports._cleanupPhysicsConfiguration = function() { - if (this.physicsConfiguration !== undefined) { - while (this.physicsConfiguration.hasChildNodes()) { - this.physicsConfiguration.removeChild(this.physicsConfiguration.firstChild); + /** + * + * @param pointer + * @private + */ + exports._releaseControlNode = function(pointer) { + var newNode = this._getNodeAt(pointer); + if (newNode !== null) { + if (this.edgeBeingEdited.controlNodes.from.selected == true) { + this.edgeBeingEdited._restoreControlNodes(); + this._editEdge(newNode.id, this.edgeBeingEdited.to.id); + this.edgeBeingEdited.controlNodes.from.unselect(); + } + if (this.edgeBeingEdited.controlNodes.to.selected == true) { + this.edgeBeingEdited._restoreControlNodes(); + this._editEdge(this.edgeBeingEdited.from.id, newNode.id); + this.edgeBeingEdited.controlNodes.to.unselect(); } - - this.physicsConfiguration.parentNode.removeChild(this.physicsConfiguration); - this.physicsConfiguration = undefined; } - } + else { + this.edgeBeingEdited._restoreControlNodes(); + } + this.freezeSimulation = false; + this._redraw(); + }; /** - * Load the HTML for the physics config and bind it + * the function bound to the selection event. It checks if you want to connect a cluster and changes the description + * to walk the user through the process. + * * @private */ - exports._loadPhysicsConfiguration = function () { - if (this.physicsConfiguration === undefined) { - this.backupConstants = {}; - util.deepExtend(this.backupConstants,this.constants); + exports._handleConnect = function(pointer) { + if (this._getSelectedNodeCount() == 0) { + var node = this._getNodeAt(pointer); - var hierarchicalLayoutDirections = ["LR", "RL", "UD", "DU"]; - this.physicsConfiguration = document.createElement('div'); - this.physicsConfiguration.className = "PhysicsConfiguration"; - this.physicsConfiguration.innerHTML = '' + - '' + - '' + - '' + - '' + - '' + - '' + - '
Simulation Mode:
Barnes HutRepulsionHierarchical
' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '
Options:
' - this.containerElement.parentElement.insertBefore(this.physicsConfiguration, this.containerElement); - this.optionsDiv = document.createElement("div"); - this.optionsDiv.style.fontSize = "14px"; - this.optionsDiv.style.fontFamily = "verdana"; - this.containerElement.parentElement.insertBefore(this.optionsDiv, this.containerElement); + if (node != null) { + if (node.clusterSize > 1) { + alert(this.constants.locales[this.constants.locale]['createEdgeError']) + } + else { + this._selectObject(node,false); + var supportNodes = this.sectors['support']['nodes']; - var rangeElement; - rangeElement = document.getElementById('graph_BH_gc'); - rangeElement.onchange = showValueOfRange.bind(this, 'graph_BH_gc', -1, "physics_barnesHut_gravitationalConstant"); - rangeElement = document.getElementById('graph_BH_cg'); - rangeElement.onchange = showValueOfRange.bind(this, 'graph_BH_cg', 1, "physics_centralGravity"); - rangeElement = document.getElementById('graph_BH_sc'); - rangeElement.onchange = showValueOfRange.bind(this, 'graph_BH_sc', 1, "physics_springConstant"); - rangeElement = document.getElementById('graph_BH_sl'); - rangeElement.onchange = showValueOfRange.bind(this, 'graph_BH_sl', 1, "physics_springLength"); - rangeElement = document.getElementById('graph_BH_damp'); - rangeElement.onchange = showValueOfRange.bind(this, 'graph_BH_damp', 1, "physics_damping"); + // create a node the temporary line can look at + supportNodes['targetNode'] = new Node({id:'targetNode'},{},{},this.constants); + var targetNode = supportNodes['targetNode']; + targetNode.x = node.x; + targetNode.y = node.y; - rangeElement = document.getElementById('graph_R_nd'); - rangeElement.onchange = showValueOfRange.bind(this, 'graph_R_nd', 1, "physics_repulsion_nodeDistance"); - rangeElement = document.getElementById('graph_R_cg'); - rangeElement.onchange = showValueOfRange.bind(this, 'graph_R_cg', 1, "physics_centralGravity"); - rangeElement = document.getElementById('graph_R_sc'); - rangeElement.onchange = showValueOfRange.bind(this, 'graph_R_sc', 1, "physics_springConstant"); - rangeElement = document.getElementById('graph_R_sl'); - rangeElement.onchange = showValueOfRange.bind(this, 'graph_R_sl', 1, "physics_springLength"); - rangeElement = document.getElementById('graph_R_damp'); - rangeElement.onchange = showValueOfRange.bind(this, 'graph_R_damp', 1, "physics_damping"); + // create a temporary edge + this.edges['connectionEdge'] = new Edge({id:"connectionEdge",from:node.id,to:targetNode.id}, this, this.constants); + var connectionEdge = this.edges['connectionEdge']; + connectionEdge.from = node; + connectionEdge.connected = true; + connectionEdge.options.smoothCurves = {enabled: true, + dynamic: false, + type: "continuous", + roundness: 0.5 + }; + connectionEdge.selected = true; + connectionEdge.to = targetNode; - rangeElement = document.getElementById('graph_H_nd'); - rangeElement.onchange = showValueOfRange.bind(this, 'graph_H_nd', 1, "physics_hierarchicalRepulsion_nodeDistance"); - rangeElement = document.getElementById('graph_H_cg'); - rangeElement.onchange = showValueOfRange.bind(this, 'graph_H_cg', 1, "physics_centralGravity"); - rangeElement = document.getElementById('graph_H_sc'); - rangeElement.onchange = showValueOfRange.bind(this, 'graph_H_sc', 1, "physics_springConstant"); - rangeElement = document.getElementById('graph_H_sl'); - rangeElement.onchange = showValueOfRange.bind(this, 'graph_H_sl', 1, "physics_springLength"); - rangeElement = document.getElementById('graph_H_damp'); - rangeElement.onchange = showValueOfRange.bind(this, 'graph_H_damp', 1, "physics_damping"); - rangeElement = document.getElementById('graph_H_direction'); - rangeElement.onchange = showValueOfRange.bind(this, 'graph_H_direction', hierarchicalLayoutDirections, "hierarchicalLayout_direction"); - rangeElement = document.getElementById('graph_H_levsep'); - rangeElement.onchange = showValueOfRange.bind(this, 'graph_H_levsep', 1, "hierarchicalLayout_levelSeparation"); - rangeElement = document.getElementById('graph_H_nspac'); - rangeElement.onchange = showValueOfRange.bind(this, 'graph_H_nspac', 1, "hierarchicalLayout_nodeSpacing"); + this.cachedFunctions["_handleOnDrag"] = this._handleOnDrag; + this._handleOnDrag = function(event) { + var pointer = this._getPointer(event.gesture.center); + var connectionEdge = this.edges['connectionEdge']; + connectionEdge.to.x = this._XconvertDOMtoCanvas(pointer.x); + connectionEdge.to.y = this._YconvertDOMtoCanvas(pointer.y); + }; - var radioButton1 = document.getElementById("graph_physicsMethod1"); - var radioButton2 = document.getElementById("graph_physicsMethod2"); - var radioButton3 = document.getElementById("graph_physicsMethod3"); - radioButton2.checked = true; - if (this.constants.physics.barnesHut.enabled) { - radioButton1.checked = true; + this.moving = true; + this.start(); + } } - if (this.constants.hierarchicalLayout.enabled) { - radioButton3.checked = true; + } + }; + + exports._finishConnect = function(event) { + if (this._getSelectedNodeCount() == 1) { + var pointer = this._getPointer(event.gesture.center); + // restore the drag function + this._handleOnDrag = this.cachedFunctions["_handleOnDrag"]; + delete this.cachedFunctions["_handleOnDrag"]; + + // remember the edge id + var connectFromId = this.edges['connectionEdge'].fromId; + + // remove the temporary nodes and edge + delete this.edges['connectionEdge']; + delete this.sectors['support']['nodes']['targetNode']; + delete this.sectors['support']['nodes']['targetViaNode']; + + var node = this._getNodeAt(pointer); + if (node != null) { + if (node.clusterSize > 1) { + alert(this.constants.locales[this.constants.locale]["createEdgeError"]) + } + else { + this._createEdge(connectFromId,node.id); + this._createManipulatorBar(); + } } + this._unselectAll(); + } + }; - var graph_toggleSmooth = document.getElementById("graph_toggleSmooth"); - var graph_repositionNodes = document.getElementById("graph_repositionNodes"); - var graph_generateOptions = document.getElementById("graph_generateOptions"); - graph_toggleSmooth.onclick = graphToggleSmoothCurves.bind(this); - graph_repositionNodes.onclick = graphRepositionNodes.bind(this); - graph_generateOptions.onclick = graphGenerateOptions.bind(this); - if (this.constants.smoothCurves == true && this.constants.dynamicSmoothCurves == false) { - graph_toggleSmooth.style.background = "#A4FF56"; + /** + * Adds a node on the specified location + */ + exports._addNode = function() { + if (this._selectionIsEmpty() && this.editMode == true) { + var positionObject = this._pointerToPositionObject(this.pointerPosition); + var defaultData = {id:util.randomUUID(),x:positionObject.left,y:positionObject.top,label:"new",allowedToMoveX:true,allowedToMoveY:true}; + if (this.triggerFunctions.add) { + if (this.triggerFunctions.add.length == 2) { + var me = this; + this.triggerFunctions.add(defaultData, function(finalizedData) { + me.nodesData.add(finalizedData); + me._createManipulatorBar(); + me.moving = true; + me.start(); + }); + } + else { + throw new Error('The function for add does not support two arguments (data,callback)'); + this._createManipulatorBar(); + this.moving = true; + this.start(); + } } else { - graph_toggleSmooth.style.background = "#FF8532"; + this.nodesData.add(defaultData); + this._createManipulatorBar(); + this.moving = true; + this.start(); } - - - switchConfigurations.apply(this); - - radioButton1.onchange = switchConfigurations.bind(this); - radioButton2.onchange = switchConfigurations.bind(this); - radioButton3.onchange = switchConfigurations.bind(this); } }; + /** - * This overwrites the this.constants. + * connect two nodes with a new edge. * - * @param constantsVariableName - * @param value * @private */ - exports._overWriteGraphConstants = function (constantsVariableName, value) { - var nameArray = constantsVariableName.split("_"); - if (nameArray.length == 1) { - this.constants[nameArray[0]] = value; - } - else if (nameArray.length == 2) { - this.constants[nameArray[0]][nameArray[1]] = value; - } - else if (nameArray.length == 3) { - this.constants[nameArray[0]][nameArray[1]][nameArray[2]] = value; + exports._createEdge = function(sourceNodeId,targetNodeId) { + if (this.editMode == true) { + var defaultData = {from:sourceNodeId, to:targetNodeId}; + if (this.triggerFunctions.connect) { + if (this.triggerFunctions.connect.length == 2) { + var me = this; + this.triggerFunctions.connect(defaultData, function(finalizedData) { + me.edgesData.add(finalizedData); + me.moving = true; + me.start(); + }); + } + else { + throw new Error('The function for connect does not support two arguments (data,callback)'); + this.moving = true; + this.start(); + } + } + else { + this.edgesData.add(defaultData); + this.moving = true; + this.start(); + } } }; - /** - * this function is bound to the toggle smooth curves button. That is also why it is not in the prototype. + * connect two nodes with a new edge. + * + * @private */ - function graphToggleSmoothCurves () { - this.constants.smoothCurves.enabled = !this.constants.smoothCurves.enabled; - var graph_toggleSmooth = document.getElementById("graph_toggleSmooth"); - if (this.constants.smoothCurves.enabled == true) {graph_toggleSmooth.style.background = "#A4FF56";} - else {graph_toggleSmooth.style.background = "#FF8532";} - - this._configureSmoothCurves(false); - } + exports._editEdge = function(sourceNodeId,targetNodeId) { + if (this.editMode == true) { + var defaultData = {id: this.edgeBeingEdited.id, from:sourceNodeId, to:targetNodeId}; + if (this.triggerFunctions.editEdge) { + if (this.triggerFunctions.editEdge.length == 2) { + var me = this; + this.triggerFunctions.editEdge(defaultData, function(finalizedData) { + me.edgesData.update(finalizedData); + me.moving = true; + me.start(); + }); + } + else { + throw new Error('The function for edit does not support two arguments (data, callback)'); + this.moving = true; + this.start(); + } + } + else { + this.edgesData.update(defaultData); + this.moving = true; + this.start(); + } + } + }; /** - * this function is used to scramble the nodes + * Create the toolbar to edit the selected node. The label and the color can be changed. Other colors are derived from the chosen color. * + * @private */ - function graphRepositionNodes () { - for (var nodeId in this.calculationNodes) { - if (this.calculationNodes.hasOwnProperty(nodeId)) { - this.calculationNodes[nodeId].vx = 0; this.calculationNodes[nodeId].vy = 0; - this.calculationNodes[nodeId].fx = 0; this.calculationNodes[nodeId].fy = 0; + exports._editNode = function() { + if (this.triggerFunctions.edit && this.editMode == true) { + var node = this._getSelectedNode(); + var data = {id:node.id, + label: node.label, + group: node.options.group, + shape: node.options.shape, + color: { + background:node.options.color.background, + border:node.options.color.border, + highlight: { + background:node.options.color.highlight.background, + border:node.options.color.highlight.border + } + }}; + if (this.triggerFunctions.edit.length == 2) { + var me = this; + this.triggerFunctions.edit(data, function (finalizedData) { + me.nodesData.update(finalizedData); + me._createManipulatorBar(); + me.moving = true; + me.start(); + }); + } + else { + throw new Error('The function for edit does not support two arguments (data, callback)'); } - } - if (this.constants.hierarchicalLayout.enabled == true) { - this._setupHierarchicalLayout(); - showValueOfRange.call(this, 'graph_H_nd', 1, "physics_hierarchicalRepulsion_nodeDistance"); - showValueOfRange.call(this, 'graph_H_cg', 1, "physics_centralGravity"); - showValueOfRange.call(this, 'graph_H_sc', 1, "physics_springConstant"); - showValueOfRange.call(this, 'graph_H_sl', 1, "physics_springLength"); - showValueOfRange.call(this, 'graph_H_damp', 1, "physics_damping"); } else { - this.repositionNodes(); + throw new Error('No edit function has been bound to this button'); } - this.moving = true; - this.start(); - } + }; + + + /** - * this is used to generate an options file from the playing with physics system. + * delete everything in the selection + * + * @private */ - function graphGenerateOptions () { - var options = "No options are required, default values used."; - var optionsSpecific = []; - var radioButton1 = document.getElementById("graph_physicsMethod1"); - var radioButton2 = document.getElementById("graph_physicsMethod2"); - if (radioButton1.checked == true) { - if (this.constants.physics.barnesHut.gravitationalConstant != this.backupConstants.physics.barnesHut.gravitationalConstant) {optionsSpecific.push("gravitationalConstant: " + this.constants.physics.barnesHut.gravitationalConstant);} - if (this.constants.physics.centralGravity != this.backupConstants.physics.barnesHut.centralGravity) {optionsSpecific.push("centralGravity: " + this.constants.physics.centralGravity);} - if (this.constants.physics.springLength != this.backupConstants.physics.barnesHut.springLength) {optionsSpecific.push("springLength: " + this.constants.physics.springLength);} - if (this.constants.physics.springConstant != this.backupConstants.physics.barnesHut.springConstant) {optionsSpecific.push("springConstant: " + this.constants.physics.springConstant);} - if (this.constants.physics.damping != this.backupConstants.physics.barnesHut.damping) {optionsSpecific.push("damping: " + this.constants.physics.damping);} - if (optionsSpecific.length != 0) { - options = "var options = {"; - options += "physics: {barnesHut: {"; - for (var i = 0; i < optionsSpecific.length; i++) { - options += optionsSpecific[i]; - if (i < optionsSpecific.length - 1) { - options += ", " - } - } - options += '}}' - } - if (this.constants.smoothCurves.enabled != this.backupConstants.smoothCurves.enabled) { - if (optionsSpecific.length == 0) {options = "var options = {";} - else {options += ", "} - options += "smoothCurves: " + this.constants.smoothCurves.enabled; - } - if (options != "No options are required, default values used.") { - options += '};' - } - } - else if (radioButton2.checked == true) { - options = "var options = {"; - options += "physics: {barnesHut: {enabled: false}"; - if (this.constants.physics.repulsion.nodeDistance != this.backupConstants.physics.repulsion.nodeDistance) {optionsSpecific.push("nodeDistance: " + this.constants.physics.repulsion.nodeDistance);} - if (this.constants.physics.centralGravity != this.backupConstants.physics.repulsion.centralGravity) {optionsSpecific.push("centralGravity: " + this.constants.physics.centralGravity);} - if (this.constants.physics.springLength != this.backupConstants.physics.repulsion.springLength) {optionsSpecific.push("springLength: " + this.constants.physics.springLength);} - if (this.constants.physics.springConstant != this.backupConstants.physics.repulsion.springConstant) {optionsSpecific.push("springConstant: " + this.constants.physics.springConstant);} - if (this.constants.physics.damping != this.backupConstants.physics.repulsion.damping) {optionsSpecific.push("damping: " + this.constants.physics.damping);} - if (optionsSpecific.length != 0) { - options += ", repulsion: {"; - for (var i = 0; i < optionsSpecific.length; i++) { - options += optionsSpecific[i]; - if (i < optionsSpecific.length - 1) { - options += ", " + exports._deleteSelected = function() { + if (!this._selectionIsEmpty() && this.editMode == true) { + if (!this._clusterInSelection()) { + var selectedNodes = this.getSelectedNodes(); + var selectedEdges = this.getSelectedEdges(); + if (this.triggerFunctions.del) { + var me = this; + var data = {nodes: selectedNodes, edges: selectedEdges}; + if (this.triggerFunctions.del.length == 2) { + this.triggerFunctions.del(data, function (finalizedData) { + me.edgesData.remove(finalizedData.edges); + me.nodesData.remove(finalizedData.nodes); + me._unselectAll(); + me.moving = true; + me.start(); + }); } - } - options += '}}' - } - if (optionsSpecific.length == 0) {options += "}"} - if (this.constants.smoothCurves != this.backupConstants.smoothCurves) { - options += ", smoothCurves: " + this.constants.smoothCurves; - } - options += '};' - } - else { - options = "var options = {"; - if (this.constants.physics.hierarchicalRepulsion.nodeDistance != this.backupConstants.physics.hierarchicalRepulsion.nodeDistance) {optionsSpecific.push("nodeDistance: " + this.constants.physics.hierarchicalRepulsion.nodeDistance);} - if (this.constants.physics.centralGravity != this.backupConstants.physics.hierarchicalRepulsion.centralGravity) {optionsSpecific.push("centralGravity: " + this.constants.physics.centralGravity);} - if (this.constants.physics.springLength != this.backupConstants.physics.hierarchicalRepulsion.springLength) {optionsSpecific.push("springLength: " + this.constants.physics.springLength);} - if (this.constants.physics.springConstant != this.backupConstants.physics.hierarchicalRepulsion.springConstant) {optionsSpecific.push("springConstant: " + this.constants.physics.springConstant);} - if (this.constants.physics.damping != this.backupConstants.physics.hierarchicalRepulsion.damping) {optionsSpecific.push("damping: " + this.constants.physics.damping);} - if (optionsSpecific.length != 0) { - options += "physics: {hierarchicalRepulsion: {"; - for (var i = 0; i < optionsSpecific.length; i++) { - options += optionsSpecific[i]; - if (i < optionsSpecific.length - 1) { - options += ", "; + else { + throw new Error('The function for delete does not support two arguments (data, callback)') } } - options += '}},'; - } - options += 'hierarchicalLayout: {'; - optionsSpecific = []; - if (this.constants.hierarchicalLayout.direction != this.backupConstants.hierarchicalLayout.direction) {optionsSpecific.push("direction: " + this.constants.hierarchicalLayout.direction);} - if (Math.abs(this.constants.hierarchicalLayout.levelSeparation) != this.backupConstants.hierarchicalLayout.levelSeparation) {optionsSpecific.push("levelSeparation: " + this.constants.hierarchicalLayout.levelSeparation);} - if (this.constants.hierarchicalLayout.nodeSpacing != this.backupConstants.hierarchicalLayout.nodeSpacing) {optionsSpecific.push("nodeSpacing: " + this.constants.hierarchicalLayout.nodeSpacing);} - if (optionsSpecific.length != 0) { - for (var i = 0; i < optionsSpecific.length; i++) { - options += optionsSpecific[i]; - if (i < optionsSpecific.length - 1) { - options += ", " - } + else { + this.edgesData.remove(selectedEdges); + this.nodesData.remove(selectedNodes); + this._unselectAll(); + this.moving = true; + this.start(); } - options += '}' } else { - options += "enabled:true}"; + alert(this.constants.locales[this.constants.locale]["deleteClusterError"]); } - options += '};' } + }; - this.optionsDiv.innerHTML = options; - } +/***/ }, +/* 68 */ +/***/ function(module, exports, __webpack_require__) { - /** - * this is used to switch between barnesHut, repulsion and hierarchical. - * - */ - function switchConfigurations () { - var ids = ["graph_BH_table", "graph_R_table", "graph_H_table"]; - var radioButton = document.querySelector('input[name="graph_physicsMethod"]:checked').value; - var tableId = "graph_" + radioButton + "_table"; - var table = document.getElementById(tableId); - table.style.display = "block"; - for (var i = 0; i < ids.length; i++) { - if (ids[i] != tableId) { - table = document.getElementById(ids[i]); - table.style.display = "none"; - } - } - this._restoreNodes(); - if (radioButton == "R") { - this.constants.hierarchicalLayout.enabled = false; - this.constants.physics.hierarchicalRepulsion.enabled = false; - this.constants.physics.barnesHut.enabled = false; - } - else if (radioButton == "H") { - if (this.constants.hierarchicalLayout.enabled == false) { - this.constants.hierarchicalLayout.enabled = true; - this.constants.physics.hierarchicalRepulsion.enabled = true; - this.constants.physics.barnesHut.enabled = false; - this.constants.smoothCurves.enabled = false; - this._setupHierarchicalLayout(); + var util = __webpack_require__(1); + var Hammer = __webpack_require__(19); + + exports._cleanNavigation = function() { + // clean hammer bindings + if (this.navigationHammers.existing.length != 0) { + for (var i = 0; i < this.navigationHammers.existing.length; i++) { + this.navigationHammers.existing[i].dispose(); } + this.navigationHammers.existing = []; } - else { - this.constants.hierarchicalLayout.enabled = false; - this.constants.physics.hierarchicalRepulsion.enabled = false; - this.constants.physics.barnesHut.enabled = true; - } - this._loadSelectedForceSolver(); - var graph_toggleSmooth = document.getElementById("graph_toggleSmooth"); - if (this.constants.smoothCurves.enabled == true) {graph_toggleSmooth.style.background = "#A4FF56";} - else {graph_toggleSmooth.style.background = "#FF8532";} - this.moving = true; - this.start(); - } + this._navigationReleaseOverload = function () {}; + + // clean up previous navigation items + if (this.navigationDivs && this.navigationDivs['wrapper'] && this.navigationDivs['wrapper'].parentNode) { + this.navigationDivs['wrapper'].parentNode.removeChild(this.navigationDivs['wrapper']); + } + }; /** - * this generates the ranges depending on the iniital values. + * 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. * - * @param id - * @param map - * @param constantsVariableName + * @private */ - function showValueOfRange (id,map,constantsVariableName) { - var valueId = id + "_value"; - var rangeValue = document.getElementById(id).value; + exports._loadNavigationElements = function() { + this._cleanNavigation(); - if (Array.isArray(map)) { - document.getElementById(valueId).value = map[parseInt(rangeValue)]; - this._overWriteGraphConstants(constantsVariableName,map[parseInt(rangeValue)]); - } - else { - document.getElementById(valueId).value = parseInt(map) * parseFloat(rangeValue); - this._overWriteGraphConstants(constantsVariableName, parseInt(map) * parseFloat(rangeValue)); - } + this.navigationDivs = {}; + var navigationDivs = ['up','down','left','right','zoomIn','zoomOut','zoomExtends']; + var navigationDivActions = ['_moveUp','_moveDown','_moveLeft','_moveRight','_zoomIn','_zoomOut','_zoomExtent']; - if (constantsVariableName == "hierarchicalLayout_direction" || - constantsVariableName == "hierarchicalLayout_levelSeparation" || - constantsVariableName == "hierarchicalLayout_nodeSpacing") { - this._setupHierarchicalLayout(); - } - this.moving = true; - this.start(); - } + this.navigationDivs['wrapper'] = document.createElement('div'); + this.frame.appendChild(this.navigationDivs['wrapper']); + + for (var i = 0; i < navigationDivs.length; i++) { + this.navigationDivs[navigationDivs[i]] = document.createElement('div'); + this.navigationDivs[navigationDivs[i]].className = 'network-navigation ' + navigationDivs[i]; + this.navigationDivs['wrapper'].appendChild(this.navigationDivs[navigationDivs[i]]); + var hammer = Hammer(this.navigationDivs[navigationDivs[i]], {prevent_default: true}); + hammer.on('touch', this[navigationDivActions[i]].bind(this)); + this.navigationHammers._new.push(hammer); + } + this._navigationReleaseOverload = this._stopMovement; + this.navigationHammers.existing = this.navigationHammers._new; + }; -/***/ }, -/* 67 */ -/***/ function(module, exports, __webpack_require__) { - function webpackContext(req) { - throw new Error("Cannot find module '" + req + "'."); - } - webpackContext.keys = function() { return []; }; - webpackContext.resolve = webpackContext; - module.exports = webpackContext; - webpackContext.id = 67; + /** + * this stops all movement induced by the navigation buttons + * + * @private + */ + exports._zoomExtent = function(event) { + this.zoomExtent({duration:700}); + event.stopPropagation(); + }; + /** + * this stops all movement induced by the navigation buttons + * + * @private + */ + exports._stopMovement = function() { + this._xStopMoving(); + this._yStopMoving(); + this._stopZoom(); + }; -/***/ }, -/* 68 */ -/***/ function(module, exports, __webpack_require__) { /** - * Calculate the forces the nodes apply on each other based on a repulsion field. - * This field is linearly approximated. + * move the screen up + * By using the increments, instead of adding a fixed number to the translation, we keep fluent and + * instant movement. The onKeypress event triggers immediately, then pauses, then triggers frequently + * To avoid this behaviour, we do the translation in the start loop. * * @private */ - exports._calculateNodeForces = function () { - var dx, dy, angle, distance, fx, fy, combinedClusterSize, - repulsingForce, node1, node2, i, j; + exports._moveUp = function(event) { + this.yIncrement = this.constants.keyboard.speed.y; + this.start(); // if there is no node movement, the calculation wont be done + event.preventDefault(); + }; - var nodes = this.calculationNodes; - var nodeIndices = this.calculationNodeIndices; - // approximation constants - var a_base = -2 / 3; - var b = 4 / 3; + /** + * move the screen down + * @private + */ + exports._moveDown = function(event) { + this.yIncrement = -this.constants.keyboard.speed.y; + this.start(); // if there is no node movement, the calculation wont be done + event.preventDefault(); + }; - // repulsing forces between nodes - var nodeDistance = this.constants.physics.repulsion.nodeDistance; - var minimumDistance = 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]]; - combinedClusterSize = node1.clusterSize + node2.clusterSize - 2; + /** + * move the screen left + * @private + */ + exports._moveLeft = function(event) { + this.xIncrement = this.constants.keyboard.speed.x; + this.start(); // if there is no node movement, the calculation wont be done + event.preventDefault(); + }; + + + /** + * move the screen right + * @private + */ + exports._moveRight = function(event) { + this.xIncrement = -this.constants.keyboard.speed.y; + this.start(); // if there is no node movement, the calculation wont be done + event.preventDefault(); + }; + + + /** + * Zoom in, using the same method as the movement. + * @private + */ + exports._zoomIn = function(event) { + this.zoomIncrement = this.constants.keyboard.speed.zoom; + this.start(); // if there is no node movement, the calculation wont be done + event.preventDefault(); + }; + + + /** + * Zoom out + * @private + */ + exports._zoomOut = function(event) { + this.zoomIncrement = -this.constants.keyboard.speed.zoom; + this.start(); // if there is no node movement, the calculation wont be done + event.preventDefault(); + }; - dx = node2.x - node1.x; - dy = node2.y - node1.y; - distance = Math.sqrt(dx * dx + dy * dy); - // same condition as BarnesHut, making sure nodes are never 100% overlapping. - if (distance == 0) { - distance = 0.1*Math.random(); - dx = distance; - } + /** + * Stop zooming and unhighlight the zoom controls + * @private + */ + exports._stopZoom = function(event) { + this.zoomIncrement = 0; + event && event.preventDefault(); + }; - minimumDistance = (combinedClusterSize == 0) ? nodeDistance : (nodeDistance * (1 + combinedClusterSize * this.constants.clustering.distanceAmplification)); - var a = a_base / minimumDistance; - if (distance < 2 * minimumDistance) { - if (distance < 0.5 * minimumDistance) { - repulsingForce = 1.0; - } - else { - repulsingForce = a * distance + b; // linear approx of 1 / (1 + Math.exp((distance / minimumDistance - 1) * steepness)) - } - // amplify the repulsion for clusters. - repulsingForce *= (combinedClusterSize == 0) ? 1 : 1 + combinedClusterSize * this.constants.clustering.forceAmplification; - repulsingForce = repulsingForce / Math.max(distance,0.01*minimumDistance); + /** + * Stop moving in the Y direction and unHighlight the up and down + * @private + */ + exports._yStopMoving = function(event) { + this.yIncrement = 0; + event && event.preventDefault(); + }; - fx = dx * repulsingForce; - fy = dy * repulsingForce; - node1.fx -= fx; - node1.fy -= fy; - node2.fx += fx; - node2.fy += fy; - } - } - } + /** + * Stop moving in the X direction and unHighlight left and right. + * @private + */ + exports._xStopMoving = function(event) { + this.xIncrement = 0; + event && event.preventDefault(); }; @@ -34108,579 +34037,676 @@ return /******/ (function(modules) { // webpackBootstrap /* 69 */ /***/ function(module, exports, __webpack_require__) { + exports._resetLevels = function() { + for (var nodeId in this.nodes) { + if (this.nodes.hasOwnProperty(nodeId)) { + var node = this.nodes[nodeId]; + if (node.preassignedLevel == false) { + node.level = -1; + node.hierarchyEnumerated = false; + } + } + } + }; + /** - * Calculate the forces the nodes apply on eachother based on a repulsion field. - * This field is linearly approximated. + * This is the main function to layout the nodes in a hierarchical way. + * It checks if the node details are supplied correctly * * @private */ - exports._calculateNodeForces = function () { - var dx, dy, distance, fx, fy, - repulsingForce, node1, node2, i, j; - - var nodes = this.calculationNodes; - var nodeIndices = this.calculationNodeIndices; - - // repulsing forces between nodes - var nodeDistance = this.constants.physics.hierarchicalRepulsion.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) { + exports._setupHierarchicalLayout = function() { + if (this.constants.hierarchicalLayout.enabled == true && this.nodeIndices.length > 0) { + // get the size of the largest hubs and check if the user has defined a level for a node. + var hubsize = 0; + var node, nodeId; + var definedLevel = false; + var undefinedLevel = false; - dx = node2.x - node1.x; - dy = node2.y - node1.y; - distance = Math.sqrt(dx * dx + dy * dy); + for (nodeId in this.nodes) { + if (this.nodes.hasOwnProperty(nodeId)) { + node = this.nodes[nodeId]; + if (node.level != -1) { + definedLevel = true; + } + else { + undefinedLevel = true; + } + if (hubsize < node.edges.length) { + hubsize = node.edges.length; + } + } + } + // if the user defined some levels but not all, alert and run without hierarchical layout + if (undefinedLevel == true && definedLevel == true) { + throw new Error("To use the hierarchical layout, nodes require either no predefined levels or levels have to be defined for all nodes."); + this.zoomExtent(undefined,true,this.constants.clustering.enabled); + if (!this.constants.clustering.enabled) { + this.start(); + } + } + else { + // setup the system to use hierarchical method. + this._changeConstants(); - var steepness = 0.05; - if (distance < nodeDistance) { - repulsingForce = -Math.pow(steepness*distance,2) + Math.pow(steepness*nodeDistance,2); + // define levels if undefined by the users. Based on hubsize + if (undefinedLevel == true) { + if (this.constants.hierarchicalLayout.layout == "hubsize") { + this._determineLevels(hubsize); } else { - repulsingForce = 0; + this._determineLevelsDirected(false); } - // normalize force with - if (distance == 0) { - distance = 0.01; - } - else { - repulsingForce = repulsingForce / distance; - } - fx = dx * repulsingForce; - fy = dy * repulsingForce; - node1.fx -= fx; - node1.fy -= fy; - node2.fx += fx; - node2.fy += fy; } + // check the distribution of the nodes per level. + var distribution = this._getDistribution(); + + // place the nodes on the canvas. This also stablilizes the system. + this._placeNodesByHierarchy(distribution); + + // start the simulation. + this.start(); } } }; /** - * this function calculates the effects of the springs in the case of unsmooth curves. + * This function places the nodes on the canvas based on the hierarchial distribution. * + * @param {Object} distribution | obtained by the function this._getDistribution() * @private */ - exports._calculateHierarchicalSpringForces = function () { - var edgeLength, edge, edgeId; - var dx, dy, fx, fy, springForce, distance; - var edges = this.edges; - - var nodes = this.calculationNodes; - var nodeIndices = this.calculationNodeIndices; - - - for (var i = 0; i < nodeIndices.length; i++) { - var node1 = nodes[nodeIndices[i]]; - node1.springFx = 0; - node1.springFy = 0; - } - - - // forces caused by the edges, modelled as springs - for (edgeId in edges) { - if (edges.hasOwnProperty(edgeId)) { - edge = edges[edgeId]; - if (edge.connected) { - // only calculate forces if nodes are in the same sector - if (this.nodes.hasOwnProperty(edge.toId) && this.nodes.hasOwnProperty(edge.fromId)) { - edgeLength = edge.physics.springLength; - // this implies that the edges between big clusters are longer - edgeLength += (edge.to.clusterSize + edge.from.clusterSize - 2) * this.constants.clustering.edgeGrowth; - - dx = (edge.from.x - edge.to.x); - dy = (edge.from.y - edge.to.y); - distance = Math.sqrt(dx * dx + dy * dy); - - if (distance == 0) { - distance = 0.01; - } - - // the 1/distance is so the fx and fy can be calculated without sine or cosine. - springForce = this.constants.physics.springConstant * (edgeLength - distance) / distance; - - fx = dx * springForce; - fy = dy * springForce; + exports._placeNodesByHierarchy = function(distribution) { + var nodeId, node; + // start placing all the level 0 nodes first. Then recursively position their branches. + for (var level in distribution) { + if (distribution.hasOwnProperty(level)) { + for (nodeId in distribution[level].nodes) { + if (distribution[level].nodes.hasOwnProperty(nodeId)) { + node = distribution[level].nodes[nodeId]; + if (this.constants.hierarchicalLayout.direction == "UD" || this.constants.hierarchicalLayout.direction == "DU") { + if (node.xFixed) { + node.x = distribution[level].minPos; + node.xFixed = false; - if (edge.to.level != edge.from.level) { - edge.to.springFx -= fx; - edge.to.springFy -= fy; - edge.from.springFx += fx; - edge.from.springFy += fy; + distribution[level].minPos += distribution[level].nodeSpacing; + } } else { - var factor = 0.5; - edge.to.fx -= factor*fx; - edge.to.fy -= factor*fy; - edge.from.fx += factor*fx; - edge.from.fy += factor*fy; + if (node.yFixed) { + node.y = distribution[level].minPos; + node.yFixed = false; + + distribution[level].minPos += distribution[level].nodeSpacing; + } } + this._placeBranchNodes(node.edges,node.id,distribution,node.level); } } } } - // normalize spring forces - var springForce = 1; - var springFx, springFy; - for (i = 0; i < nodeIndices.length; i++) { - var node = nodes[nodeIndices[i]]; - springFx = Math.min(springForce,Math.max(-springForce,node.springFx)); - springFy = Math.min(springForce,Math.max(-springForce,node.springFy)); - - node.fx += springFx; - node.fy += springFy; - } - - // retain energy balance - var totalFx = 0; - var totalFy = 0; - for (i = 0; i < nodeIndices.length; i++) { - var node = nodes[nodeIndices[i]]; - totalFx += node.fx; - totalFy += node.fy; - } - var correctionFx = totalFx / nodeIndices.length; - var correctionFy = totalFy / nodeIndices.length; - - for (i = 0; i < nodeIndices.length; i++) { - var node = nodes[nodeIndices[i]]; - node.fx -= correctionFx; - node.fy -= correctionFy; - } - + // stabilize the system after positioning. This function calls zoomExtent. + this._stabilize(); }; -/***/ }, -/* 70 */ -/***/ function(module, exports, __webpack_require__) { /** - * 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. + * This function get the distribution of levels based on hubsize * + * @returns {Object} * @private */ - exports._calculateNodeForces = function() { - if (this.constants.physics.barnesHut.gravitationalConstant != 0) { - var node; - var nodes = this.calculationNodes; - var nodeIndices = this.calculationNodeIndices; - var nodeCount = nodeIndices.length; - - this._formBarnesHutTree(nodes,nodeIndices); + exports._getDistribution = function() { + var distribution = {}; + var nodeId, node, level; - var barnesHutTree = this.barnesHutTree; + // we fix Y because the hierarchy is vertical, we fix X so we do not give a node an x position for a second time. + // the fix of X is removed after the x value has been set. + for (nodeId in this.nodes) { + if (this.nodes.hasOwnProperty(nodeId)) { + node = this.nodes[nodeId]; + node.xFixed = true; + node.yFixed = true; + if (this.constants.hierarchicalLayout.direction == "UD" || this.constants.hierarchicalLayout.direction == "DU") { + node.y = this.constants.hierarchicalLayout.levelSeparation*node.level; + } + else { + node.x = this.constants.hierarchicalLayout.levelSeparation*node.level; + } + if (distribution[node.level] === undefined) { + distribution[node.level] = {amount: 0, nodes: {}, minPos:0, nodeSpacing:0}; + } + distribution[node.level].amount += 1; + distribution[node.level].nodes[nodeId] = node; + } + } - // 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 BarnesHut 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); + // determine the largest amount of nodes of all levels + var maxCount = 0; + for (level in distribution) { + if (distribution.hasOwnProperty(level)) { + if (maxCount < distribution[level].amount) { + maxCount = distribution[level].amount; } } } + + // set the initial position and spacing of each nodes accordingly + for (level in distribution) { + if (distribution.hasOwnProperty(level)) { + distribution[level].nodeSpacing = (maxCount + 1) * this.constants.hierarchicalLayout.nodeSpacing; + distribution[level].nodeSpacing /= (distribution[level].amount + 1); + distribution[level].minPos = distribution[level].nodeSpacing - (0.5 * (distribution[level].amount + 1) * distribution[level].nodeSpacing); + } + } + + return distribution; }; /** - * 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. + * this function allocates nodes in levels based on the recursive branching from the largest hubs. * - * @param parentBranch - * @param node + * @param hubsize * @private */ - exports._getForceContribution = function(parentBranch,node) { - // we get no force contribution from an empty region - if (parentBranch.childrenCount > 0) { - var dx,dy,distance; - - // 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); + exports._determineLevels = function(hubsize) { + var nodeId, node; - // BarnesHut condition - // original condition : s/d < thetaInverted = passed === d/s > 1/theta = passed - // calcSize = 1/s --> d * 1/s > 1/theta = passed - if (distance * parentBranch.calcSize > this.constants.physics.barnesHut.thetaInverted) { - // duplicate code to reduce function calls to speed up program - if (distance == 0) { - distance = 0.1*Math.random(); - dx = distance; + // determine hubs + for (nodeId in this.nodes) { + if (this.nodes.hasOwnProperty(nodeId)) { + node = this.nodes[nodeId]; + if (node.edges.length == hubsize) { + node.level = 0; } - var gravityForce = this.constants.physics.barnesHut.gravitationalConstant * parentBranch.mass * node.options.mass / (distance * distance * distance); - var fx = dx * gravityForce; - var fy = dy * gravityForce; - node.fx += fx; - node.fy += fy; } - 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 - // duplicate code to reduce function calls to speed up program - if (distance == 0) { - distance = 0.5*Math.random(); - dx = distance; - } - var gravityForce = this.constants.physics.barnesHut.gravitationalConstant * parentBranch.mass * node.options.mass / (distance * distance * distance); - var fx = dx * gravityForce; - var fy = dy * gravityForce; - node.fx += fx; - node.fy += fy; - } + } + + // branch from hubs + for (nodeId in this.nodes) { + if (this.nodes.hasOwnProperty(nodeId)) { + node = this.nodes[nodeId]; + if (node.level == 0) { + this._setLevel(1,node.edges,node.id); } } } }; + + /** - * This function constructs the barnesHut tree recursively. It creates the root, splits it and starts placing the nodes. + * this function allocates nodes in levels based on the direction of the edges * - * @param nodes - * @param nodeIndices + * @param hubsize * @private */ - exports._formBarnesHutTree = function(nodes,nodeIndices) { - var node; - var nodeCount = nodeIndices.length; + exports._determineLevelsDirected = function() { + var nodeId, node, firstNode; + var minLevel = 10000; - var minX = Number.MAX_VALUE, - minY = Number.MAX_VALUE, - maxX =-Number.MAX_VALUE, - maxY =-Number.MAX_VALUE; + // set first node to source + firstNode = this.nodes[this.nodeIndices[0]]; + firstNode.level = minLevel; + this._setLevelDirected(minLevel,firstNode.edges,firstNode.id); - // get the range of the nodes - for (var i = 0; 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; } + // get the minimum level + for (nodeId in this.nodes) { + if (this.nodes.hasOwnProperty(nodeId)) { + node = this.nodes[nodeId]; + minLevel = node.level < minLevel ? node.level : minLevel; } } - // make the range a square - var sizeDiff = Math.abs(maxX - minX) - Math.abs(maxY - minY); // difference between X and Y - if (sizeDiff > 0) {minY -= 0.5 * sizeDiff; maxY += 0.5 * sizeDiff;} // xSize > ySize - else {minX += 0.5 * sizeDiff; maxX -= 0.5 * sizeDiff;} // xSize < ySize - - - var minimumTreeSize = 1e-5; - var rootSize = Math.max(minimumTreeSize,Math.abs(maxX - minX)); - var halfRootSize = 0.5 * rootSize; - var centerX = 0.5 * (minX + maxX), centerY = 0.5 * (minY + maxY); - - // construct the barnesHutTree - var barnesHutTree = { - root:{ - centerOfMass: {x:0, y:0}, - mass:0, - range: { - minX: centerX-halfRootSize,maxX:centerX+halfRootSize, - minY: centerY-halfRootSize,maxY:centerY+halfRootSize - }, - size: rootSize, - calcSize: 1 / rootSize, - children: { data:null}, - maxWidth: 0, - level: 0, - childrenCount: 4 - } - }; - this._splitBranch(barnesHutTree.root); - // place the nodes one by one recursively - for (i = 0; i < nodeCount; i++) { - node = nodes[nodeIndices[i]]; - if (node.options.mass > 0) { - this._placeInTree(barnesHutTree.root,node); + // subtract the minimum from the set so we have a range starting from 0 + for (nodeId in this.nodes) { + if (this.nodes.hasOwnProperty(nodeId)) { + node = this.nodes[nodeId]; + node.level -= minLevel; } } - - // make global - this.barnesHutTree = barnesHutTree }; /** - * this updates the mass of a branch. this is increased by adding a node. + * Since hierarchical layout does not support: + * - smooth curves (based on the physics), + * - clustering (based on dynamic node counts) + * + * We disable both features so there will be no problems. * - * @param parentBranch - * @param node * @private */ - exports._updateBranchMass = function(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; + exports._changeConstants = function() { + this.constants.clustering.enabled = false; + this.constants.physics.barnesHut.enabled = false; + this.constants.physics.hierarchicalRepulsion.enabled = true; + this._loadSelectedForceSolver(); + if (this.constants.smoothCurves.enabled == true) { + this.constants.smoothCurves.dynamic = false; + } + this._configureSmoothCurves(); - parentBranch.mass = totalMass; - var biggestSize = Math.max(Math.max(node.height,node.radius),node.width); - parentBranch.maxWidth = (parentBranch.maxWidth < biggestSize) ? biggestSize : parentBranch.maxWidth; + var config = this.constants.hierarchicalLayout; + config.levelSeparation = Math.abs(config.levelSeparation); + if (config.direction == "RL" || config.direction == "DU") { + config.levelSeparation *= -1; + } + if (config.direction == "RL" || config.direction == "LR") { + if (this.constants.smoothCurves.enabled == true) { + this.constants.smoothCurves.type = "vertical"; + } + } + else { + if (this.constants.smoothCurves.enabled == true) { + this.constants.smoothCurves.type = "horizontal"; + } + } }; /** - * determine in which branch the node will be placed. + * This is a recursively called function to enumerate the branches from the largest hubs and place the nodes + * on a X position that ensures there will be no overlap. * - * @param parentBranch - * @param node - * @param skipMassUpdate + * @param edges + * @param parentId + * @param distribution + * @param parentLevel * @private */ - exports._placeInTree = function(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"); + exports._placeBranchNodes = function(edges, parentId, distribution, parentLevel) { + for (var i = 0; i < edges.length; i++) { + var childNode = null; + if (edges[i].toId == parentId) { + childNode = edges[i].from; } - else { // in SW - this._placeInRegion(parentBranch,node,"SW"); + else { + childNode = edges[i].to; } - } - else { // in NE or SE - if (parentBranch.children.NW.range.maxY > node.y) { // in NE - this._placeInRegion(parentBranch,node,"NE"); + + // if a node is conneceted to another node on the same level (or higher (means lower level))!, this is not handled here. + var nodeMoved = false; + if (this.constants.hierarchicalLayout.direction == "UD" || this.constants.hierarchicalLayout.direction == "DU") { + if (childNode.xFixed && childNode.level > parentLevel) { + childNode.xFixed = false; + childNode.x = distribution[childNode.level].minPos; + nodeMoved = true; + } } - else { // in SE - this._placeInRegion(parentBranch,node,"SE"); + else { + if (childNode.yFixed && childNode.level > parentLevel) { + childNode.yFixed = false; + childNode.y = distribution[childNode.level].minPos; + nodeMoved = true; + } + } + + if (nodeMoved == true) { + distribution[childNode.level].minPos += distribution[childNode.level].nodeSpacing; + if (childNode.edges.length > 1) { + this._placeBranchNodes(childNode.edges,childNode.id,distribution,childNode.level); + } } } }; /** - * actually place the node in a region (or branch) + * this function is called recursively to enumerate the barnches of the largest hubs and give each node a level. * - * @param parentBranch - * @param node - * @param region + * @param level + * @param edges + * @param parentId * @private */ - exports._placeInRegion = function(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); + exports._setLevel = function(level, edges, parentId) { + for (var i = 0; i < edges.length; i++) { + var childNode = null; + if (edges[i].toId == parentId) { + childNode = edges[i].from; + } + else { + childNode = edges[i].to; + } + if (childNode.level == -1 || childNode.level > level) { + childNode.level = level; + if (childNode.edges.length > 1) { + this._setLevel(level+1, childNode.edges, childNode.id); } - break; - case 4: // place in branch - this._placeInTree(parentBranch.children[region],node); - break; + } } }; /** - * this function splits a branch into 4 sub branches. If the branch contained a node, we place it in the subbranch - * after the split is complete. + * this function is called recursively to enumerate the branched of the first node and give each node a level based on edge direction * - * @param parentBranch + * @param level + * @param edges + * @param parentId * @private */ - exports._splitBranch = function(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; + exports._setLevelDirected = function(level, edges, parentId) { + this.nodes[parentId].hierarchyEnumerated = true; + var childNode, direction; + for (var i = 0; i < edges.length; i++) { + direction = 1; + if (edges[i].toId == parentId) { + childNode = edges[i].from; + direction = -1; + } + else { + childNode = edges[i].to; + } + if (childNode.level == -1) { + childNode.level = level + direction; + } } - parentBranch.childrenCount = 4; - parentBranch.children.data = null; - this._insertRegion(parentBranch,"NW"); - this._insertRegion(parentBranch,"NE"); - this._insertRegion(parentBranch,"SW"); - this._insertRegion(parentBranch,"SE"); - if (containedNode != null) { - this._placeInTree(parentBranch,containedNode); + for (var i = 0; i < edges.length; i++) { + if (edges[i].toId == parentId) {childNode = edges[i].from;} + else {childNode = edges[i].to;} + + if (childNode.edges.length > 1 && childNode.hierarchyEnumerated === false) { + this._setLevelDirected(childNode.level, childNode.edges, childNode.id); + } } }; /** - * This function subdivides the region into four new segments. - * Specifically, this inserts a single new segment. - * It fills the children section of the parentBranch + * Unfix nodes * - * @param parentBranch - * @param region - * @param parentRange * @private */ - exports._insertRegion = function(parentBranch, region) { - var minX,maxX,minY,maxY; - 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; + exports._restoreNodes = function() { + for (var nodeId in this.nodes) { + if (this.nodes.hasOwnProperty(nodeId)) { + this.nodes[nodeId].xFixed = false; + this.nodes[nodeId].yFixed = 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 - }; +/***/ }, +/* 70 */ +/***/ function(module, exports, __webpack_require__) { + + // English + exports['en'] = { + edit: 'Edit', + del: 'Delete selected', + back: 'Back', + addNode: 'Add Node', + addEdge: 'Add Edge', + editNode: 'Edit Node', + editEdge: 'Edit Edge', + addDescription: 'Click in an empty space to place a new node.', + edgeDescription: 'Click on a node and drag the edge to another node to connect them.', + editEdgeDescription: 'Click on the control points and drag them to a node to connect to it.', + createEdgeError: 'Cannot link edges to a cluster.', + deleteClusterError: 'Clusters cannot be deleted.' + }; + exports['en_EN'] = exports['en']; + exports['en_US'] = exports['en']; + + // Dutch + exports['nl'] = { + edit: 'Wijzigen', + del: 'Selectie verwijderen', + back: 'Terug', + addNode: 'Node toevoegen', + addEdge: 'Link toevoegen', + editNode: 'Node wijzigen', + editEdge: 'Link wijzigen', + addDescription: 'Klik op een leeg gebied om een nieuwe node te maken.', + edgeDescription: 'Klik op een node en sleep de link naar een andere node om ze te verbinden.', + editEdgeDescription: 'Klik op de verbindingspunten en sleep ze naar een node om daarmee te verbinden.', + createEdgeError: 'Kan geen link maken naar een cluster.', + deleteClusterError: 'Clusters kunnen niet worden verwijderd.' }; + exports['nl_NL'] = exports['nl']; + exports['nl_BE'] = exports['nl']; + +/***/ }, +/* 71 */ +/***/ function(module, exports, __webpack_require__) { /** - * This function is for debugging purposed, it draws the tree. - * - * @param ctx - * @param color - * @private + * Canvas shapes used by Network */ - exports._drawTree = function(ctx,color) { - if (this.barnesHutTree !== undefined) { + if (typeof CanvasRenderingContext2D !== 'undefined') { - ctx.lineWidth = 1; + /** + * Draw a circle shape + */ + CanvasRenderingContext2D.prototype.circle = function(x, y, r) { + this.beginPath(); + this.arc(x, y, r, 0, 2*Math.PI, false); + }; - this._drawBranch(this.barnesHutTree.root,ctx,color); - } - }; + /** + * Draw a square shape + * @param {Number} x horizontal center + * @param {Number} y vertical center + * @param {Number} r size, width and height of the square + */ + CanvasRenderingContext2D.prototype.square = function(x, y, r) { + this.beginPath(); + this.rect(x - r, y - r, r * 2, r * 2); + }; + /** + * Draw a triangle shape + * @param {Number} x horizontal center + * @param {Number} y vertical center + * @param {Number} r radius, half the length of the sides of the triangle + */ + CanvasRenderingContext2D.prototype.triangle = function(x, y, r) { + // http://en.wikipedia.org/wiki/Equilateral_triangle + this.beginPath(); - /** - * This function is for debugging purposes. It draws the branches recursively. - * - * @param branch - * @param ctx - * @param color - * @private - */ - exports._drawBranch = function(branch,ctx,color) { - if (color === undefined) { - color = "#FF0000"; - } + var s = r * 2; + var s2 = s / 2; + var ir = Math.sqrt(3) / 6 * s; // radius of inner circle + var h = Math.sqrt(s * s - s2 * s2); // height - 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(); + this.moveTo(x, y - (h - ir)); + this.lineTo(x + s2, y + ir); + this.lineTo(x - s2, y + ir); + this.lineTo(x, y - (h - ir)); + this.closePath(); + }; - ctx.beginPath(); - ctx.moveTo(branch.range.maxX,branch.range.minY); - ctx.lineTo(branch.range.maxX,branch.range.maxY); - ctx.stroke(); + /** + * Draw a triangle shape in downward orientation + * @param {Number} x horizontal center + * @param {Number} y vertical center + * @param {Number} r radius + */ + CanvasRenderingContext2D.prototype.triangleDown = function(x, y, r) { + // http://en.wikipedia.org/wiki/Equilateral_triangle + this.beginPath(); - ctx.beginPath(); - ctx.moveTo(branch.range.maxX,branch.range.maxY); - ctx.lineTo(branch.range.minX,branch.range.maxY); - ctx.stroke(); + var s = r * 2; + var s2 = s / 2; + var ir = Math.sqrt(3) / 6 * s; // radius of inner circle + var h = Math.sqrt(s * s - s2 * s2); // height - ctx.beginPath(); - ctx.moveTo(branch.range.minX,branch.range.maxY); - ctx.lineTo(branch.range.minX,branch.range.minY); - ctx.stroke(); + this.moveTo(x, y + (h - ir)); + this.lineTo(x + s2, y - ir); + this.lineTo(x - s2, y - ir); + this.lineTo(x, y + (h - ir)); + this.closePath(); + }; - /* - if (branch.mass > 0) { - ctx.circle(branch.centerOfMass.x, branch.centerOfMass.y, 3*branch.mass); - ctx.stroke(); - } + /** + * Draw a star shape, a star with 5 points + * @param {Number} x horizontal center + * @param {Number} y vertical center + * @param {Number} r radius, half the length of the sides of the triangle */ - }; + CanvasRenderingContext2D.prototype.star = function(x, y, r) { + // http://www.html5canvastutorials.com/labs/html5-canvas-star-spinner/ + this.beginPath(); + for (var n = 0; n < 10; n++) { + var radius = (n % 2 === 0) ? r * 1.3 : r * 0.5; + this.lineTo( + x + radius * Math.sin(n * 2 * Math.PI / 10), + y - radius * Math.cos(n * 2 * Math.PI / 10) + ); + } -/***/ }, -/* 71 */ -/***/ function(module, exports, __webpack_require__) { + this.closePath(); + }; - module.exports = function(module) { - if(!module.webpackPolyfill) { - module.deprecate = function() {}; - module.paths = []; - // module.parent = undefined by default - module.children = []; - module.webpackPolyfill = 1; - } - return module; + /** + * http://stackoverflow.com/questions/1255512/how-to-draw-a-rounded-rectangle-on-html-canvas + */ + CanvasRenderingContext2D.prototype.roundRect = function(x, y, w, h, r) { + var r2d = Math.PI/180; + if( w - ( 2 * r ) < 0 ) { r = ( w / 2 ); } //ensure that the radius isn't too large for x + if( h - ( 2 * r ) < 0 ) { r = ( h / 2 ); } //ensure that the radius isn't too large for y + this.beginPath(); + this.moveTo(x+r,y); + this.lineTo(x+w-r,y); + this.arc(x+w-r,y+r,r,r2d*270,r2d*360,false); + this.lineTo(x+w,y+h-r); + this.arc(x+w-r,y+h-r,r,0,r2d*90,false); + this.lineTo(x+r,y+h); + this.arc(x+r,y+h-r,r,r2d*90,r2d*180,false); + this.lineTo(x,y+r); + this.arc(x+r,y+r,r,r2d*180,r2d*270,false); + }; + + /** + * http://stackoverflow.com/questions/2172798/how-to-draw-an-oval-in-html5-canvas + */ + CanvasRenderingContext2D.prototype.ellipse = function(x, y, w, h) { + var kappa = .5522848, + ox = (w / 2) * kappa, // control point offset horizontal + oy = (h / 2) * kappa, // control point offset vertical + xe = x + w, // x-end + ye = y + h, // y-end + xm = x + w / 2, // x-middle + ym = y + h / 2; // y-middle + + this.beginPath(); + this.moveTo(x, ym); + this.bezierCurveTo(x, ym - oy, xm - ox, y, xm, y); + this.bezierCurveTo(xm + ox, y, xe, ym - oy, xe, ym); + this.bezierCurveTo(xe, ym + oy, xm + ox, ye, xm, ye); + this.bezierCurveTo(xm - ox, ye, x, ym + oy, x, ym); + }; + + + + /** + * http://stackoverflow.com/questions/2172798/how-to-draw-an-oval-in-html5-canvas + */ + CanvasRenderingContext2D.prototype.database = function(x, y, w, h) { + var f = 1/3; + var wEllipse = w; + var hEllipse = h * f; + + var kappa = .5522848, + ox = (wEllipse / 2) * kappa, // control point offset horizontal + oy = (hEllipse / 2) * kappa, // control point offset vertical + xe = x + wEllipse, // x-end + ye = y + hEllipse, // y-end + xm = x + wEllipse / 2, // x-middle + ym = y + hEllipse / 2, // y-middle + ymb = y + (h - hEllipse/2), // y-midlle, bottom ellipse + yeb = y + h; // y-end, bottom ellipse + + this.beginPath(); + this.moveTo(xe, ym); + + this.bezierCurveTo(xe, ym + oy, xm + ox, ye, xm, ye); + this.bezierCurveTo(xm - ox, ye, x, ym + oy, x, ym); + + this.bezierCurveTo(x, ym - oy, xm - ox, y, xm, y); + this.bezierCurveTo(xm + ox, y, xe, ym - oy, xe, ym); + + this.lineTo(xe, ymb); + + this.bezierCurveTo(xe, ymb + oy, xm + ox, yeb, xm, yeb); + this.bezierCurveTo(xm - ox, yeb, x, ymb + oy, x, ymb); + + this.lineTo(x, ym); + }; + + + /** + * Draw an arrow point (no line) + */ + CanvasRenderingContext2D.prototype.arrow = function(x, y, angle, length) { + // tail + var xt = x - length * Math.cos(angle); + var yt = y - length * Math.sin(angle); + + // inner tail + // TODO: allow to customize different shapes + var xi = x - length * 0.9 * Math.cos(angle); + var yi = y - length * 0.9 * Math.sin(angle); + + // left + var xl = xt + length / 3 * Math.cos(angle + 0.5 * Math.PI); + var yl = yt + length / 3 * Math.sin(angle + 0.5 * Math.PI); + + // right + var xr = xt + length / 3 * Math.cos(angle - 0.5 * Math.PI); + var yr = yt + length / 3 * Math.sin(angle - 0.5 * Math.PI); + + this.beginPath(); + this.moveTo(x, y); + this.lineTo(xl, yl); + this.lineTo(xi, yi); + this.lineTo(xr, yr); + this.closePath(); + }; + + /** + * Sets up the dashedLine functionality for drawing + * Original code came from http://stackoverflow.com/questions/4576724/dotted-stroke-in-canvas + * @author David Jordan + * @date 2012-08-08 + */ + CanvasRenderingContext2D.prototype.dashedLine = function(x,y,x2,y2,dashArray){ + if (!dashArray) dashArray=[10,5]; + if (dashLength==0) dashLength = 0.001; // Hack for Safari + var dashCount = dashArray.length; + this.moveTo(x, y); + var dx = (x2-x), dy = (y2-y); + var slope = dy/dx; + var distRemaining = Math.sqrt( dx*dx + dy*dy ); + var dashIndex=0, draw=true; + while (distRemaining>=0.1){ + var dashLength = dashArray[dashIndex++%dashCount]; + if (dashLength > distRemaining) dashLength = distRemaining; + var xStep = Math.sqrt( dashLength*dashLength / (1 + slope*slope) ); + if (dx<0) xStep = -xStep; + x += xStep; + y += slope*xStep; + this[draw ? 'lineTo' : 'moveTo'](x,y); + distRemaining -= dashLength; + draw = !draw; + } + }; + + // TODO: add diamond shape } diff --git a/lib/network/Network.js b/lib/network/Network.js index 15caa519..38934b76 100644 --- a/lib/network/Network.js +++ b/lib/network/Network.js @@ -164,7 +164,8 @@ function Network (container, data, options) { radius: 1}, // (px PNiC) | growth of the radius per node in cluster. maxNodeSizeIncrements: 600, // (# increments) | max growth of the width per node in cluster. activeAreaBoxSize: 80, // (px) | box area around the curser where clusters are popped open. - clusterLevelDifference: 2 + clusterLevelDifference: 2, + clusterByZoom: true // enable clustering through zooming in and out }, navigation: { enabled: false @@ -873,7 +874,10 @@ Network.prototype._createKeyBinds = function() { this.keycharm.bind("pagedown",this._zoomOut.bind(me),"keydown"); this.keycharm.bind("pagedown",this._stopZoom.bind(me), "keyup"); } - + this.keycharm.bind("1",this.increaseClusterLevel.bind(me), "keydown"); + this.keycharm.bind("2",this.decreaseClusterLevel.bind(me), "keydown"); + this.keycharm.bind("3",this.forceAggregateHubs.bind(me,true),"keydown"); + this.keycharm.bind("4",this.normalizeClusterLevels.bind(me), "keydown"); if (this.constants.dataManipulation.enabled == true) { this.keycharm.bind("esc",this._createManipulatorBar.bind(me)); this.keycharm.bind("delete",this._deleteSelected.bind(me)); diff --git a/lib/network/mixins/ClusterMixin.js b/lib/network/mixins/ClusterMixin.js index 16b6d5f1..3c40745b 100644 --- a/lib/network/mixins/ClusterMixin.js +++ b/lib/network/mixins/ClusterMixin.js @@ -32,22 +32,24 @@ exports.startWithClustering = function() { exports.clusterToFit = function(maxNumberOfNodes, reposition) { var numberOfNodes = this.nodeIndices.length; - var maxLevels = 50; + var maxLevels = 2; var level = 0; // we first cluster the hubs, then we pull in the outliers, repeat while (numberOfNodes > maxNumberOfNodes && level < maxLevels) { - if (level % 3 == 0) { - this.forceAggregateHubs(true); - this.normalizeClusterLevels(); + console.log("Performing clustering:", level, numberOfNodes, this.clusterSession); + if (level % 3 == 0.0) { + //this.forceAggregateHubs(true); + //this.normalizeClusterLevels(); } else { - this.increaseClusterLevel(); // this also includes a cluster normalization + //this.increaseClusterLevel(); // this also includes a cluster normalization } - + //this.forceAggregateHubs(true); numberOfNodes = this.nodeIndices.length; level += 1; } + console.log("finished") // after the clustering we reposition the nodes to reduce the initial chaos if (level > 0 && reposition == true) { @@ -98,7 +100,7 @@ exports.openCluster = function(node) { * This calls the updateClustes with default arguments */ exports.updateClustersDefault = function() { - if (this.constants.clustering.enabled == true) { + if (this.constants.clustering.enabled == true && this.constants.clustering.clusterByZoom == true) { this.updateClusters(0,false,false); } }; @@ -224,7 +226,7 @@ exports._aggregateHubs = function(force) { /** - * This function is fired by keypress. It forces hubs to form. + * This function forces hubs to form. * */ exports.forceAggregateHubs = function(doNotStart) { @@ -235,6 +237,7 @@ exports.forceAggregateHubs = function(doNotStart) { // update the index list, dynamic edges and labels this._updateNodeIndexList(); + this._updateCalculationNodes(); this._updateDynamicEdges(); this.updateLabels(); @@ -347,7 +350,7 @@ exports._expandClusterNode = function(parentNode, recursive, force, openAll) { * @private */ exports._expelChildFromParent = function(parentNode, containedNodeId, recursive, force, openAll) { - var childNode = parentNode.containedNodes[containedNodeId]; + var childNode = parentNode.containedNodes[containedNodeId] // if child node has been added on smaller scale than current, kick out if (childNode.formationScale < this.scale || force == true) { @@ -502,10 +505,10 @@ exports._forceClustersByZoom = function() { var childNode = this.nodes[nodeId]; // the edges can be swallowed by another decrease + if (childNode.dynamicEdgesLength == 1 && childNode.dynamicEdges.length != 0) { var edge = childNode.dynamicEdges[0]; var parentNode = (edge.toId == childNode.id) ? this.nodes[edge.fromId] : this.nodes[edge.toId]; - // group to the largest node if (childNode.id != parentNode.id) { if (parentNode.options.mass > childNode.options.mass) { @@ -585,9 +588,14 @@ exports._formClusterFromHub = function(hubNode, force, onlyEqual, absorptionSize if (absorptionSizeOffset === undefined) { absorptionSizeOffset = 0; } + + if (hubNode.dynamicEdgesLength < 0) { + console.error(hubNode.dynamicEdgesLength, this.hubThreshold, onlyEqual) + } // we decide if the node is a hub if ((hubNode.dynamicEdgesLength >= this.hubThreshold && onlyEqual == false) || (hubNode.dynamicEdgesLength == this.hubThreshold && onlyEqual == true)) { + // initialize variables var dx,dy,length; var minLength = this.constants.clustering.clusterEdgeThreshold/this.scale; @@ -600,7 +608,7 @@ exports._formClusterFromHub = function(hubNode, force, onlyEqual, absorptionSize edgesIdarray.push(hubNode.dynamicEdges[j].id); } - // if the hub clustering is not forces, we check if one of the edges connected + // if the hub clustering is not forced, we check if one of the edges connected // to a cluster is small enough based on the constants.clustering.clusterEdgeThreshold if (force == false) { allowCluster = false; @@ -625,17 +633,24 @@ exports._formClusterFromHub = function(hubNode, force, onlyEqual, absorptionSize // start the clustering if allowed if ((!force && allowCluster) || force) { - // we loop over all edges INITIALLY connected to this hub + var children = []; + var childrenIds = {}; + // we loop over all edges INITIALLY connected to this hub to get a list of the childNodes for (j = 0; j < amountOfInitialEdges; j++) { edge = this.edges[edgesIdarray[j]]; - // the edge can be clustered by this function in a previous loop - if (edge !== undefined) { - var childNode = this.nodes[(edge.fromId == hubNode.id) ? edge.toId : edge.fromId]; - // we do not want hubs to merge with other hubs nor do we want to cluster itself. - if ((childNode.dynamicEdges.length <= (this.hubThreshold + absorptionSizeOffset)) && - (childNode.id != hubNode.id)) { - this._addToCluster(hubNode,childNode,force); - } + var childNode = this.nodes[(edge.fromId == hubNode.id) ? edge.toId : edge.fromId]; + if (childrenIds[childNode.id] === undefined) { + childrenIds[childNode.id] = true; + children.push(childNode); + } + } + + for (j = 0; j < children.length; j++) { + var childNode = children[j]; + // we do not want hubs to merge with other hubs nor do we want to cluster itself. + if ((childNode.dynamicEdges.length <= (this.hubThreshold + absorptionSizeOffset)) && + (childNode.id != hubNode.id)) { + this._addToCluster(hubNode,childNode,force); } } } @@ -655,14 +670,16 @@ exports._formClusterFromHub = function(hubNode, force, onlyEqual, absorptionSize exports._addToCluster = function(parentNode, childNode, force) { // join child node in the parent node parentNode.containedNodes[childNode.id] = childNode; - + //console.log(parentNode.id, childNode.id) // manage all the edges connected to the child and parent nodes for (var i = 0; i < childNode.dynamicEdges.length; i++) { var edge = childNode.dynamicEdges[i]; if (edge.toId == parentNode.id || edge.fromId == parentNode.id) { // edge connected to parentNode + //console.log("COLLECT",parentNode.id, childNode.id, edge.toId, edge.fromId) this._addToContainedEdges(parentNode,childNode,edge); } else { + //console.log("REWIRE",parentNode.id, childNode.id, edge.toId, edge.fromId) this._connectEdgeToCluster(parentNode,childNode,edge); } } @@ -690,7 +707,6 @@ exports._addToCluster = function(parentNode, childNode, force) { // forced clusters only open from screen size and double tap if (force == true) { - // parentNode.formationScale = Math.pow(1 - (1.0/11.0),this.clusterSession+3); parentNode.formationScale = 0; } else { @@ -739,6 +755,10 @@ exports._updateDynamicEdges = function() { } } } + if (node.dynamicEdgesLength < correction) { + console.error("overshoot", node.dynamicEdgesLength, correction) + } + node.dynamicEdgesLength -= correction; } }; @@ -894,12 +914,14 @@ exports._connectEdgeBackToChild = function(parentNode, childNode) { * @private */ exports._validateEdges = function(parentNode) { + var dynamicEdges = [] for (var i = 0; i < parentNode.dynamicEdges.length; i++) { var edge = parentNode.dynamicEdges[i]; - if (parentNode.id != edge.toId && parentNode.id != edge.fromId) { - parentNode.dynamicEdges.splice(i,1); + if (parentNode.id == edge.toId || parentNode.id == edge.fromId) { + dynamicEdges.push(edge); } } + parentNode.dynamicEdges = dynamicEdges; }; From c4d357dd85a4d097c4fb622141d4a6ec5ba2ea07 Mon Sep 17 00:00:00 2001 From: jos Date: Thu, 5 Feb 2015 14:04:49 +0100 Subject: [PATCH 5/7] Fixed #339: Added a method `refresh()` to the `DataView`, to update filter results --- HISTORY.md | 1 + docs/dataview.html | 16 +++++++++++++ lib/DataView.js | 42 ++++++++++++++++++++++++++++++++++ test/DataView.test.js | 53 +++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 112 insertions(+) diff --git a/HISTORY.md b/HISTORY.md index 74cf8c49..cfb0404a 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -13,6 +13,7 @@ http://visjs.org - Added property `length` holding the total number of items to the `DataSet` and `DataView`. +- Added a method `refresh()` to the `DataView`, to update filter results. ### Timeline diff --git a/docs/dataview.html b/docs/dataview.html index 159ad720..316da77a 100644 --- a/docs/dataview.html +++ b/docs/dataview.html @@ -219,6 +219,22 @@ var data = new vis.DataView(dataset, options) + + refresh() + none + + Refresh the filter results of a DataView. Useful when the filter function contains dynamic properties, like: + +
var data = new vis.DataSet(...);
+var view = new vis.DataView(data, {
+  filter: function (item) {
+    return item.value > threshold;
+  }
+});
+ In this example, threshold is an external parameter. When the value of threshold changes, the DataView must be notified that the filter results may have changed by calling DataView.refresh(). + + + setDataSet(data) diff --git a/lib/DataView.js b/lib/DataView.js index c5fbdfb8..cc4e04bd 100644 --- a/lib/DataView.js +++ b/lib/DataView.js @@ -79,6 +79,48 @@ DataView.prototype.setData = function (data) { } }; +/** + * Refresh the DataView. Useful when the DataView has a filter function + * containing a variable parameter. + */ +DataView.prototype.refresh = function () { + var id; + var ids = this._data.getIds({filter: this._options && this._options.filter}); + var newIds = {}; + var added = []; + var removed = []; + + // check for additions + for (var i = 0; i < ids.length; i++) { + id = ids[i]; + newIds[id] = true; + if (!this._ids[id]) { + added.push(id); + this._ids[id] = true; + this.length++; + } + } + + // check for removals + for (id in this._ids) { + if (this._ids.hasOwnProperty(id)) { + if (!newIds[id]) { + removed.push(id); + delete this._ids[id]; + this.length--; + } + } + } + + // trigger events + if (added.length) { + this._trigger('add', {items: added}); + } + if (removed.length) { + this._trigger('remove', {items: removed}); + } +}; + /** * Get data from the data view * diff --git a/test/DataView.test.js b/test/DataView.test.js index 43110ba2..bb8ff1f9 100644 --- a/test/DataView.test.js +++ b/test/DataView.test.js @@ -94,4 +94,57 @@ describe('DataView', function () { assert.equal(group2.length, 0); }); + + it('should refresh a DataView with filter', function () { + var data = new DataSet([ + {id:1, value:2}, + {id:2, value:4}, + {id:3, value:7} + ]); + + var threshold = 5; + + // create a view. The view has a filter with a dynamic property `threshold` + var view = new DataView(data, { + filter: function (item) { + return item.value < threshold; + } + }); + + var added, updated, removed; + view.on('add', function (event, props) {added = added.concat(props.items)}); + view.on('update', function (event, props) {updated = updated.concat(props.items)}); + view.on('remove', function (event, props) {removed = removed.concat(props.items)}); + + assert.deepEqual(view.get(), [ + {id:1, value:2}, + {id:2, value:4} + ]); + + // change the threshold to 3 + added = []; + updated = []; + removed = []; + threshold = 3; + view.refresh(); + assert.deepEqual(view.get(), [{id:1, value:2}]); + assert.deepEqual(added, []); + assert.deepEqual(updated, []); + assert.deepEqual(removed, [2]); + + // change threshold to 8 + added = []; + updated = []; + removed = []; + threshold = 8; + view.refresh(); + assert.deepEqual(view.get(), [ + {id:1, value:2}, + {id:2, value:4}, + {id:3, value:7} + ]); + assert.deepEqual(added, [2, 3]); + assert.deepEqual(updated, []); + assert.deepEqual(removed, []); + }) }); From b778649725103c8908a64af89f3a3e14dce63e82 Mon Sep 17 00:00:00 2001 From: jos Date: Thu, 5 Feb 2015 14:33:13 +0100 Subject: [PATCH 6/7] Fixed #531: a bug in the `DataSet` returning an empty object instead of `null` when no item was found when using both a filter and specifying fields. --- HISTORY.md | 4 +- dist/vis.js | 47185 +++++++++++++++++++++++---------------------- dist/vis.map | 2 +- dist/vis.min.css | 2 +- dist/vis.min.js | 30 +- lib/DataSet.js | 8 +- 6 files changed, 23642 insertions(+), 23589 deletions(-) diff --git a/HISTORY.md b/HISTORY.md index cfb0404a..2bee89bc 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -9,11 +9,13 @@ http://visjs.org - Added option bindToWindow (default true) to choose whether the keyboard binds are global or to the network div. - Improved images handling so broken images are shown on all references of images that are broken. -### DataSet +### DataSet/DataView - Added property `length` holding the total number of items to the `DataSet` and `DataView`. - Added a method `refresh()` to the `DataView`, to update filter results. +- Fixed a bug in the `DataSet` returning an empty object instead of `null` when + no item was found when using both a filter and specifying fields. ### Timeline diff --git a/dist/vis.js b/dist/vis.js index 2ffd080f..59f20156 100644 --- a/dist/vis.js +++ b/dist/vis.js @@ -5,7 +5,7 @@ * A dynamic, browser-based visualization library. * * @version 3.9.2-SNAPSHOT - * @date 2015-02-02 + * @date 2015-02-05 * * @license * Copyright (C) 2011-2014 Almende B.V, http://almende.com @@ -83,67 +83,67 @@ return /******/ (function(modules) { // webpackBootstrap // utils exports.util = __webpack_require__(1); - exports.DOMutil = __webpack_require__(6); + exports.DOMutil = __webpack_require__(2); // data - exports.DataSet = __webpack_require__(7); - exports.DataView = __webpack_require__(9); - exports.Queue = __webpack_require__(8); + exports.DataSet = __webpack_require__(3); + exports.DataView = __webpack_require__(4); + exports.Queue = __webpack_require__(5); // Graph3d - exports.Graph3d = __webpack_require__(10); + exports.Graph3d = __webpack_require__(6); exports.graph3d = { - Camera: __webpack_require__(14), - Filter: __webpack_require__(15), - Point2d: __webpack_require__(13), - Point3d: __webpack_require__(12), - Slider: __webpack_require__(16), - StepNumber: __webpack_require__(17) + Camera: __webpack_require__(7), + Filter: __webpack_require__(8), + Point2d: __webpack_require__(9), + Point3d: __webpack_require__(10), + Slider: __webpack_require__(11), + StepNumber: __webpack_require__(12) }; // Timeline - exports.Timeline = __webpack_require__(18); - exports.Graph2d = __webpack_require__(42); + exports.Timeline = __webpack_require__(13); + exports.Graph2d = __webpack_require__(14); exports.timeline = { - DateUtil: __webpack_require__(24), - DataStep: __webpack_require__(45), - Range: __webpack_require__(21), - stack: __webpack_require__(28), - TimeStep: __webpack_require__(38), + DateUtil: __webpack_require__(15), + DataStep: __webpack_require__(16), + Range: __webpack_require__(17), + stack: __webpack_require__(18), + TimeStep: __webpack_require__(19), components: { items: { - Item: __webpack_require__(30), - BackgroundItem: __webpack_require__(34), - BoxItem: __webpack_require__(32), - PointItem: __webpack_require__(33), - RangeItem: __webpack_require__(29) + Item: __webpack_require__(20), + BackgroundItem: __webpack_require__(21), + BoxItem: __webpack_require__(22), + PointItem: __webpack_require__(23), + RangeItem: __webpack_require__(24) }, - Component: __webpack_require__(23), - CurrentTime: __webpack_require__(39), - CustomTime: __webpack_require__(41), - DataAxis: __webpack_require__(44), - GraphGroup: __webpack_require__(46), - Group: __webpack_require__(27), + Component: __webpack_require__(25), + CurrentTime: __webpack_require__(26), + CustomTime: __webpack_require__(27), + DataAxis: __webpack_require__(28), + GraphGroup: __webpack_require__(29), + Group: __webpack_require__(30), BackgroundGroup: __webpack_require__(31), - ItemSet: __webpack_require__(26), - Legend: __webpack_require__(50), - LineGraph: __webpack_require__(43), - TimeAxis: __webpack_require__(37) + ItemSet: __webpack_require__(32), + Legend: __webpack_require__(33), + LineGraph: __webpack_require__(34), + TimeAxis: __webpack_require__(35) } }; // Network - exports.Network = __webpack_require__(51); + exports.Network = __webpack_require__(36); exports.network = { - Edge: __webpack_require__(57), - Groups: __webpack_require__(54), - Images: __webpack_require__(55), - Node: __webpack_require__(56), - Popup: __webpack_require__(58), - dotparser: __webpack_require__(52), - gephiParser: __webpack_require__(53) + Edge: __webpack_require__(37), + Groups: __webpack_require__(38), + Images: __webpack_require__(39), + Node: __webpack_require__(40), + Popup: __webpack_require__(41), + dotparser: __webpack_require__(42), + gephiParser: __webpack_require__(43) }; // Deprecated since v3.0.0 @@ -152,8 +152,8 @@ return /******/ (function(modules) { // webpackBootstrap }; // bundled external libraries - exports.moment = __webpack_require__(2); - exports.hammer = __webpack_require__(19); + exports.moment = __webpack_require__(44); + exports.hammer = __webpack_require__(45); /***/ }, @@ -164,7 +164,7 @@ return /******/ (function(modules) { // webpackBootstrap // first check if moment.js is already loaded in the browser window, if so, // use this instance. Else, load via commonjs. - var moment = __webpack_require__(2); + var moment = __webpack_require__(44); /** * Test whether given object is a number @@ -1396,12879 +1396,10902 @@ return /******/ (function(modules) { // webpackBootstrap /* 2 */ /***/ function(module, exports, __webpack_require__) { - // first check if moment.js is already loaded in the browser window, if so, - // use this instance. Else, load via commonjs. - module.exports = (typeof window !== 'undefined') && window['moment'] || __webpack_require__(3); + // DOM utility methods + + /** + * this prepares the JSON container for allocating SVG elements + * @param JSONcontainer + * @private + */ + exports.prepareElements = function(JSONcontainer) { + // cleanup the redundant svgElements; + for (var elementType in JSONcontainer) { + if (JSONcontainer.hasOwnProperty(elementType)) { + JSONcontainer[elementType].redundant = JSONcontainer[elementType].used; + JSONcontainer[elementType].used = []; + } + } + }; + + /** + * this cleans up all the unused SVG elements. By asking for the parentNode, we only need to supply the JSON container from + * which to remove the redundant elements. + * + * @param JSONcontainer + * @private + */ + exports.cleanupElements = function(JSONcontainer) { + // cleanup the redundant svgElements; + for (var elementType in JSONcontainer) { + if (JSONcontainer.hasOwnProperty(elementType)) { + if (JSONcontainer[elementType].redundant) { + for (var i = 0; i < JSONcontainer[elementType].redundant.length; i++) { + JSONcontainer[elementType].redundant[i].parentNode.removeChild(JSONcontainer[elementType].redundant[i]); + } + JSONcontainer[elementType].redundant = []; + } + } + } + }; + + /** + * Allocate or generate an SVG element if needed. Store a reference to it in the JSON container and draw it in the svgContainer + * the JSON container and the SVG container have to be supplied so other svg containers (like the legend) can use this. + * + * @param elementType + * @param JSONcontainer + * @param svgContainer + * @returns {*} + * @private + */ + exports.getSVGElement = function (elementType, JSONcontainer, svgContainer) { + var element; + // allocate SVG element, if it doesnt yet exist, create one. + if (JSONcontainer.hasOwnProperty(elementType)) { // this element has been created before + // check if there is an redundant element + if (JSONcontainer[elementType].redundant.length > 0) { + element = JSONcontainer[elementType].redundant[0]; + JSONcontainer[elementType].redundant.shift(); + } + else { + // create a new element and add it to the SVG + element = document.createElementNS('http://www.w3.org/2000/svg', elementType); + svgContainer.appendChild(element); + } + } + else { + // create a new element and add it to the SVG, also create a new object in the svgElements to keep track of it. + element = document.createElementNS('http://www.w3.org/2000/svg', elementType); + JSONcontainer[elementType] = {used: [], redundant: []}; + svgContainer.appendChild(element); + } + JSONcontainer[elementType].used.push(element); + return element; + }; + + + /** + * Allocate or generate an SVG element if needed. Store a reference to it in the JSON container and draw it in the svgContainer + * the JSON container and the SVG container have to be supplied so other svg containers (like the legend) can use this. + * + * @param elementType + * @param JSONcontainer + * @param DOMContainer + * @returns {*} + * @private + */ + exports.getDOMElement = function (elementType, JSONcontainer, DOMContainer, insertBefore) { + var element; + // allocate DOM element, if it doesnt yet exist, create one. + if (JSONcontainer.hasOwnProperty(elementType)) { // this element has been created before + // check if there is an redundant element + if (JSONcontainer[elementType].redundant.length > 0) { + element = JSONcontainer[elementType].redundant[0]; + JSONcontainer[elementType].redundant.shift(); + } + else { + // create a new element and add it to the SVG + element = document.createElement(elementType); + if (insertBefore !== undefined) { + DOMContainer.insertBefore(element, insertBefore); + } + else { + DOMContainer.appendChild(element); + } + } + } + else { + // create a new element and add it to the SVG, also create a new object in the svgElements to keep track of it. + element = document.createElement(elementType); + JSONcontainer[elementType] = {used: [], redundant: []}; + if (insertBefore !== undefined) { + DOMContainer.insertBefore(element, insertBefore); + } + else { + DOMContainer.appendChild(element); + } + } + JSONcontainer[elementType].used.push(element); + return element; + }; + + + + + /** + * draw a point object. this is a seperate function because it can also be called by the legend. + * The reason the JSONcontainer and the target SVG svgContainer have to be supplied is so the legend can use these functions + * as well. + * + * @param x + * @param y + * @param group + * @param JSONcontainer + * @param svgContainer + * @returns {*} + */ + exports.drawPoint = function(x, y, group, JSONcontainer, svgContainer) { + var point; + if (group.options.drawPoints.style == 'circle') { + point = exports.getSVGElement('circle',JSONcontainer,svgContainer); + point.setAttributeNS(null, "cx", x); + point.setAttributeNS(null, "cy", y); + point.setAttributeNS(null, "r", 0.5 * group.options.drawPoints.size); + } + else { + point = exports.getSVGElement('rect',JSONcontainer,svgContainer); + point.setAttributeNS(null, "x", x - 0.5*group.options.drawPoints.size); + point.setAttributeNS(null, "y", y - 0.5*group.options.drawPoints.size); + point.setAttributeNS(null, "width", group.options.drawPoints.size); + point.setAttributeNS(null, "height", group.options.drawPoints.size); + } + + if(group.options.drawPoints.styles !== undefined) { + point.setAttributeNS(null, "style", group.group.options.drawPoints.styles); + } + point.setAttributeNS(null, "class", group.className + " point"); + return point; + }; + /** + * draw a bar SVG element centered on the X coordinate + * + * @param x + * @param y + * @param className + */ + exports.drawBar = function (x, y, width, height, className, JSONcontainer, svgContainer) { + if (height != 0) { + if (height < 0) { + height *= -1; + y -= height; + } + var rect = exports.getSVGElement('rect',JSONcontainer, svgContainer); + rect.setAttributeNS(null, "x", x - 0.5 * width); + rect.setAttributeNS(null, "y", y); + rect.setAttributeNS(null, "width", width); + rect.setAttributeNS(null, "height", height); + rect.setAttributeNS(null, "class", className); + } + }; /***/ }, /* 3 */ /***/ function(module, exports, __webpack_require__) { - var __WEBPACK_AMD_DEFINE_RESULT__;/* WEBPACK VAR INJECTION */(function(global, module) {//! moment.js - //! version : 2.9.0 - //! authors : Tim Wood, Iskren Chernev, Moment.js contributors - //! license : MIT - //! momentjs.com + var util = __webpack_require__(1); + var Queue = __webpack_require__(5); - (function (undefined) { - /************************************ - Constants - ************************************/ + /** + * DataSet + * + * Usage: + * var dataSet = new DataSet({ + * fieldId: '_id', + * type: { + * // ... + * } + * }); + * + * dataSet.add(item); + * dataSet.add(data); + * dataSet.update(item); + * dataSet.update(data); + * dataSet.remove(id); + * dataSet.remove(ids); + * var data = dataSet.get(); + * var data = dataSet.get(id); + * var data = dataSet.get(ids); + * var data = dataSet.get(ids, options, data); + * dataSet.clear(); + * + * A data set can: + * - add/remove/update data + * - gives triggers upon changes in the data + * - can import/export data in various data formats + * + * @param {Array | DataTable} [data] Optional array with initial data + * @param {Object} [options] Available options: + * {String} fieldId Field name of the id in the + * items, 'id' by default. + * {Object. ['10', '00'] or '-1530' > ['-', '15', '30'] - parseTimezoneChunker = /([\+\-]|\d\d)/gi, + var subscribers = []; + if (event in this._subscribers) { + subscribers = subscribers.concat(this._subscribers[event]); + } + if ('*' in this._subscribers) { + subscribers = subscribers.concat(this._subscribers['*']); + } - // getter and setter names - proxyGettersAndSetters = 'Date|Hours|Minutes|Seconds|Milliseconds'.split('|'), - unitMillisecondFactors = { - 'Milliseconds' : 1, - 'Seconds' : 1e3, - 'Minutes' : 6e4, - 'Hours' : 36e5, - 'Days' : 864e5, - 'Months' : 2592e6, - 'Years' : 31536e6 - }, + for (var i = 0; i < subscribers.length; i++) { + var subscriber = subscribers[i]; + if (subscriber.callback) { + subscriber.callback(event, params, senderId || null); + } + } + }; - unitAliases = { - ms : 'millisecond', - s : 'second', - m : 'minute', - h : 'hour', - d : 'day', - D : 'date', - w : 'week', - W : 'isoWeek', - M : 'month', - Q : 'quarter', - y : 'year', - DDD : 'dayOfYear', - e : 'weekday', - E : 'isoWeekday', - gg: 'weekYear', - GG: 'isoWeekYear' - }, + /** + * Add data. + * Adding an item will fail when there already is an item with the same id. + * @param {Object | Array | DataTable} data + * @param {String} [senderId] Optional sender id + * @return {Array} addedIds Array with the ids of the added items + */ + DataSet.prototype.add = function (data, senderId) { + var addedIds = [], + id, + me = this; - camelFunctions = { - dayofyear : 'dayOfYear', - isoweekday : 'isoWeekday', - isoweek : 'isoWeek', - weekyear : 'weekYear', - isoweekyear : 'isoWeekYear' - }, + if (Array.isArray(data)) { + // Array + for (var i = 0, len = data.length; i < len; i++) { + id = me._addItem(data[i]); + addedIds.push(id); + } + } + else if (util.isDataTable(data)) { + // Google DataTable + var columns = this._getColumnNames(data); + for (var row = 0, rows = data.getNumberOfRows(); row < rows; row++) { + var item = {}; + for (var col = 0, cols = columns.length; col < cols; col++) { + var field = columns[col]; + item[field] = data.getValue(row, col); + } - // format function strings - formatFunctions = {}, + id = me._addItem(item); + addedIds.push(id); + } + } + else if (data instanceof Object) { + // Single item + id = me._addItem(data); + addedIds.push(id); + } + else { + throw new Error('Unknown dataType'); + } - // default relative time thresholds - relativeTimeThresholds = { - s: 45, // seconds to minute - m: 45, // minutes to hour - h: 22, // hours to day - d: 26, // days to month - M: 11 // months to year - }, - - // tokens to ordinalize and pad - ordinalizeTokens = 'DDD w W M D d'.split(' '), - paddedTokens = 'M D H h m s w W'.split(' '), - - formatTokenFunctions = { - M : function () { - return this.month() + 1; - }, - MMM : function (format) { - return this.localeData().monthsShort(this, format); - }, - MMMM : function (format) { - return this.localeData().months(this, format); - }, - D : function () { - return this.date(); - }, - DDD : function () { - return this.dayOfYear(); - }, - d : function () { - return this.day(); - }, - dd : function (format) { - return this.localeData().weekdaysMin(this, format); - }, - ddd : function (format) { - return this.localeData().weekdaysShort(this, format); - }, - dddd : function (format) { - return this.localeData().weekdays(this, format); - }, - w : function () { - return this.week(); - }, - W : function () { - return this.isoWeek(); - }, - YY : function () { - return leftZeroFill(this.year() % 100, 2); - }, - YYYY : function () { - return leftZeroFill(this.year(), 4); - }, - YYYYY : function () { - return leftZeroFill(this.year(), 5); - }, - YYYYYY : function () { - var y = this.year(), sign = y >= 0 ? '+' : '-'; - return sign + leftZeroFill(Math.abs(y), 6); - }, - gg : function () { - return leftZeroFill(this.weekYear() % 100, 2); - }, - gggg : function () { - return leftZeroFill(this.weekYear(), 4); - }, - ggggg : function () { - return leftZeroFill(this.weekYear(), 5); - }, - GG : function () { - return leftZeroFill(this.isoWeekYear() % 100, 2); - }, - GGGG : function () { - return leftZeroFill(this.isoWeekYear(), 4); - }, - GGGGG : function () { - return leftZeroFill(this.isoWeekYear(), 5); - }, - e : function () { - return this.weekday(); - }, - E : function () { - return this.isoWeekday(); - }, - a : function () { - return this.localeData().meridiem(this.hours(), this.minutes(), true); - }, - A : function () { - return this.localeData().meridiem(this.hours(), this.minutes(), false); - }, - H : function () { - return this.hours(); - }, - h : function () { - return this.hours() % 12 || 12; - }, - m : function () { - return this.minutes(); - }, - s : function () { - return this.seconds(); - }, - S : function () { - return toInt(this.milliseconds() / 100); - }, - SS : function () { - return leftZeroFill(toInt(this.milliseconds() / 10), 2); - }, - SSS : function () { - return leftZeroFill(this.milliseconds(), 3); - }, - SSSS : function () { - return leftZeroFill(this.milliseconds(), 3); - }, - Z : function () { - var a = this.utcOffset(), - b = '+'; - if (a < 0) { - a = -a; - b = '-'; - } - return b + leftZeroFill(toInt(a / 60), 2) + ':' + leftZeroFill(toInt(a) % 60, 2); - }, - ZZ : function () { - var a = this.utcOffset(), - b = '+'; - if (a < 0) { - a = -a; - b = '-'; - } - return b + leftZeroFill(toInt(a / 60), 2) + leftZeroFill(toInt(a) % 60, 2); - }, - z : function () { - return this.zoneAbbr(); - }, - zz : function () { - return this.zoneName(); - }, - x : function () { - return this.valueOf(); - }, - X : function () { - return this.unix(); - }, - Q : function () { - return this.quarter(); - } - }, - - deprecations = {}, - - lists = ['months', 'monthsShort', 'weekdays', 'weekdaysShort', 'weekdaysMin'], + if (addedIds.length) { + this._trigger('add', {items: addedIds}, senderId); + } - updateInProgress = false; + return addedIds; + }; - // Pick the first defined of two or three arguments. dfl comes from - // default. - function dfl(a, b, c) { - switch (arguments.length) { - case 2: return a != null ? a : b; - case 3: return a != null ? a : b != null ? b : c; - default: throw new Error('Implement me'); - } - } + /** + * Update existing items. When an item does not exist, it will be created + * @param {Object | Array | DataTable} data + * @param {String} [senderId] Optional sender id + * @return {Array} updatedIds The ids of the added or updated items + */ + DataSet.prototype.update = function (data, senderId) { + var addedIds = []; + var updatedIds = []; + var updatedData = []; + var me = this; + var fieldId = me._fieldId; - function hasOwnProp(a, b) { - return hasOwnProperty.call(a, b); + var addOrUpdate = function (item) { + var id = item[fieldId]; + if (me._data[id]) { + // update item + id = me._updateItem(item); + updatedIds.push(id); + updatedData.push(item); } - - function defaultParsingFlags() { - // We need to deep clone this object, and es5 standard is not very - // helpful. - return { - empty : false, - unusedTokens : [], - unusedInput : [], - overflow : -2, - charsLeftOver : 0, - nullInput : false, - invalidMonth : null, - invalidFormat : false, - userInvalidated : false, - iso: false - }; + else { + // add new item + id = me._addItem(item); + addedIds.push(id); } + }; - function printMsg(msg) { - if (moment.suppressDeprecationWarnings === false && - typeof console !== 'undefined' && console.warn) { - console.warn('Deprecation warning: ' + msg); - } + if (Array.isArray(data)) { + // Array + for (var i = 0, len = data.length; i < len; i++) { + addOrUpdate(data[i]); } + } + else if (util.isDataTable(data)) { + // Google DataTable + var columns = this._getColumnNames(data); + for (var row = 0, rows = data.getNumberOfRows(); row < rows; row++) { + var item = {}; + for (var col = 0, cols = columns.length; col < cols; col++) { + var field = columns[col]; + item[field] = data.getValue(row, col); + } - function deprecate(msg, fn) { - var firstTime = true; - return extend(function () { - if (firstTime) { - printMsg(msg); - firstTime = false; - } - return fn.apply(this, arguments); - }, fn); + addOrUpdate(item); } + } + else if (data instanceof Object) { + // Single item + addOrUpdate(data); + } + else { + throw new Error('Unknown dataType'); + } - function deprecateSimple(name, msg) { - if (!deprecations[name]) { - printMsg(msg); - deprecations[name] = true; - } - } + if (addedIds.length) { + this._trigger('add', {items: addedIds}, senderId); + } + if (updatedIds.length) { + this._trigger('update', {items: updatedIds, data: updatedData}, senderId); + } - function padToken(func, count) { - return function (a) { - return leftZeroFill(func.call(this, a), count); - }; - } - function ordinalizeToken(func, period) { - return function (a) { - return this.localeData().ordinal(func.call(this, a), period); - }; - } + return addedIds.concat(updatedIds); + }; - function monthDiff(a, b) { - // difference in months - var wholeMonthDiff = ((b.year() - a.year()) * 12) + (b.month() - a.month()), - // b is in (anchor - 1 month, anchor + 1 month) - anchor = a.clone().add(wholeMonthDiff, 'months'), - anchor2, adjust; + /** + * Get a data item or multiple items. + * + * Usage: + * + * get() + * get(options: Object) + * get(options: Object, data: Array | DataTable) + * + * get(id: Number | String) + * get(id: Number | String, options: Object) + * get(id: Number | String, options: Object, data: Array | DataTable) + * + * get(ids: Number[] | String[]) + * get(ids: Number[] | String[], options: Object) + * get(ids: Number[] | String[], options: Object, data: Array | DataTable) + * + * Where: + * + * {Number | String} id The id of an item + * {Number[] | String{}} ids An array with ids of items + * {Object} options An Object with options. Available options: + * {String} [returnType] Type of data to be + * returned. Can be 'DataTable' or 'Array' (default) + * {Object.} [type] + * {String[]} [fields] field names to be returned + * {function} [filter] filter items + * {String | function} [order] Order the items by + * a field name or custom sort function. + * {Array | DataTable} [data] If provided, items will be appended to this + * array or table. Required in case of Google + * DataTable. + * + * @throws Error + */ + DataSet.prototype.get = function (args) { + var me = this; - if (b - anchor < 0) { - anchor2 = a.clone().add(wholeMonthDiff - 1, 'months'); - // linear across the month - adjust = (b - anchor) / (anchor - anchor2); - } else { - anchor2 = a.clone().add(wholeMonthDiff + 1, 'months'); - // linear across the month - adjust = (b - anchor) / (anchor2 - anchor); - } + // parse the arguments + var id, ids, options, data; + var firstType = util.getType(arguments[0]); + if (firstType == 'String' || firstType == 'Number') { + // get(id [, options] [, data]) + id = arguments[0]; + options = arguments[1]; + data = arguments[2]; + } + else if (firstType == 'Array') { + // get(ids [, options] [, data]) + ids = arguments[0]; + options = arguments[1]; + data = arguments[2]; + } + else { + // get([, options] [, data]) + options = arguments[0]; + data = arguments[1]; + } - return -(wholeMonthDiff + adjust); - } + // determine the return type + var returnType; + if (options && options.returnType) { + var allowedValues = ["DataTable", "Array", "Object"]; + returnType = allowedValues.indexOf(options.returnType) == -1 ? "Array" : options.returnType; - while (ordinalizeTokens.length) { - i = ordinalizeTokens.pop(); - formatTokenFunctions[i + 'o'] = ordinalizeToken(formatTokenFunctions[i], i); + if (data && (returnType != util.getType(data))) { + throw new Error('Type of parameter "data" (' + util.getType(data) + ') ' + + 'does not correspond with specified options.type (' + options.type + ')'); } - while (paddedTokens.length) { - i = paddedTokens.pop(); - formatTokenFunctions[i + i] = padToken(formatTokenFunctions[i], 2); + if (returnType == 'DataTable' && !util.isDataTable(data)) { + throw new Error('Parameter "data" must be a DataTable ' + + 'when options.type is "DataTable"'); } - formatTokenFunctions.DDDD = padToken(formatTokenFunctions.DDD, 3); - + } + else if (data) { + returnType = (util.getType(data) == 'DataTable') ? 'DataTable' : 'Array'; + } + else { + returnType = 'Array'; + } - function meridiemFixWrap(locale, hour, meridiem) { - var isPm; + // build options + var type = options && options.type || this._options.type; + var filter = options && options.filter; + var items = [], item, itemId, i, len; - if (meridiem == null) { - // nothing to do - return hour; - } - if (locale.meridiemHour != null) { - return locale.meridiemHour(hour, meridiem); - } else if (locale.isPM != null) { - // Fallback - isPm = locale.isPM(meridiem); - if (isPm && hour < 12) { - hour += 12; - } - if (!isPm && hour === 12) { - hour = 0; - } - return hour; - } else { - // thie is not supposed to happen - return hour; - } + // convert items + if (id != undefined) { + // return a single item + item = me._getItem(id, type); + if (filter && !filter(item)) { + item = null; } - - /************************************ - Constructors - ************************************/ - - function Locale() { + } + else if (ids != undefined) { + // return a subset of items + for (i = 0, len = ids.length; i < len; i++) { + item = me._getItem(ids[i], type); + if (!filter || filter(item)) { + items.push(item); + } } - - // Moment prototype object - function Moment(config, skipOverflow) { - if (skipOverflow !== false) { - checkOverflow(config); - } - copyConfig(this, config); - this._d = new Date(+config._d); - // Prevent infinite loop in case updateOffset creates new moment - // objects. - if (updateInProgress === false) { - updateInProgress = true; - moment.updateOffset(this); - updateInProgress = false; + } + else { + // return all items + for (itemId in this._data) { + if (this._data.hasOwnProperty(itemId)) { + item = me._getItem(itemId, type); + if (!filter || filter(item)) { + items.push(item); } + } } + } - // Duration Constructor - function Duration(duration) { - var normalizedInput = normalizeObjectUnits(duration), - years = normalizedInput.year || 0, - quarters = normalizedInput.quarter || 0, - months = normalizedInput.month || 0, - weeks = normalizedInput.week || 0, - days = normalizedInput.day || 0, - hours = normalizedInput.hour || 0, - minutes = normalizedInput.minute || 0, - seconds = normalizedInput.second || 0, - milliseconds = normalizedInput.millisecond || 0; - - // representation for dateAddRemove - this._milliseconds = +milliseconds + - seconds * 1e3 + // 1000 - minutes * 6e4 + // 1000 * 60 - hours * 36e5; // 1000 * 60 * 60 - // Because of dateAddRemove treats 24 hours as different from a - // day when working around DST, we need to store them separately - this._days = +days + - weeks * 7; - // It is impossible translate months into days without knowing - // which months you are are talking about, so we have to store - // it separately. - this._months = +months + - quarters * 3 + - years * 12; - - this._data = {}; - - this._locale = moment.localeData(); + // order the results + if (options && options.order && id == undefined) { + this._sort(items, options.order); + } - this._bubble(); + // filter fields of the items + if (options && options.fields) { + var fields = options.fields; + if (id != undefined) { + item = this._filterFields(item, fields); } - - /************************************ - Helpers - ************************************/ - - - function extend(a, b) { - for (var i in b) { - if (hasOwnProp(b, i)) { - a[i] = b[i]; - } - } - - if (hasOwnProp(b, 'toString')) { - a.toString = b.toString; - } - - if (hasOwnProp(b, 'valueOf')) { - a.valueOf = b.valueOf; - } - - return a; + else { + for (i = 0, len = items.length; i < len; i++) { + items[i] = this._filterFields(items[i], fields); + } } + } - function copyConfig(to, from) { - var i, prop, val; - - if (typeof from._isAMomentObject !== 'undefined') { - to._isAMomentObject = from._isAMomentObject; - } - if (typeof from._i !== 'undefined') { - to._i = from._i; - } - if (typeof from._f !== 'undefined') { - to._f = from._f; - } - if (typeof from._l !== 'undefined') { - to._l = from._l; - } - if (typeof from._strict !== 'undefined') { - to._strict = from._strict; - } - if (typeof from._tzm !== 'undefined') { - to._tzm = from._tzm; - } - if (typeof from._isUTC !== 'undefined') { - to._isUTC = from._isUTC; - } - if (typeof from._offset !== 'undefined') { - to._offset = from._offset; - } - if (typeof from._pf !== 'undefined') { - to._pf = from._pf; - } - if (typeof from._locale !== 'undefined') { - to._locale = from._locale; - } - - if (momentProperties.length > 0) { - for (i in momentProperties) { - prop = momentProperties[i]; - val = from[prop]; - if (typeof val !== 'undefined') { - to[prop] = val; - } - } - } - - return to; + // return the results + if (returnType == 'DataTable') { + var columns = this._getColumnNames(data); + if (id != undefined) { + // append a single item to the data table + me._appendRow(data, columns, item); } - - function absRound(number) { - if (number < 0) { - return Math.ceil(number); - } else { - return Math.floor(number); - } + else { + // copy the items to the provided data table + for (i = 0; i < items.length; i++) { + me._appendRow(data, columns, items[i]); + } } - - // left zero fill a number - // see http://jsperf.com/left-zero-filling for performance comparison - function leftZeroFill(number, targetLength, forceSign) { - var output = '' + Math.abs(number), - sign = number >= 0; - - while (output.length < targetLength) { - output = '0' + output; + return data; + } + else if (returnType == "Object") { + var result = {}; + for (i = 0; i < items.length; i++) { + result[items[i].id] = items[i]; + } + return result; + } + else { + // return an array + if (id != undefined) { + // a single item + return item; + } + else { + // multiple items + if (data) { + // copy the items to the provided array + for (i = 0, len = items.length; i < len; i++) { + data.push(items[i]); } - return (sign ? (forceSign ? '+' : '') : '-') + output; + return data; + } + else { + // just return our array + return items; + } } + } + }; - function positiveMomentsDifference(base, other) { - var res = {milliseconds: 0, months: 0}; + /** + * Get ids of all items or from a filtered set of items. + * @param {Object} [options] An Object with options. Available options: + * {function} [filter] filter items + * {String | function} [order] Order the items by + * a field name or custom sort function. + * @return {Array} ids + */ + DataSet.prototype.getIds = function (options) { + var data = this._data, + filter = options && options.filter, + order = options && options.order, + type = options && options.type || this._options.type, + i, + len, + id, + item, + items, + ids = []; - res.months = other.month() - base.month() + - (other.year() - base.year()) * 12; - if (base.clone().add(res.months, 'M').isAfter(other)) { - --res.months; + if (filter) { + // get filtered items + if (order) { + // create ordered list + items = []; + for (id in data) { + if (data.hasOwnProperty(id)) { + item = this._getItem(id, type); + if (filter(item)) { + items.push(item); + } } + } - res.milliseconds = +other - +(base.clone().add(res.months, 'M')); + this._sort(items, order); - return res; + for (i = 0, len = items.length; i < len; i++) { + ids[i] = items[i][this._fieldId]; + } } - - function momentsDifference(base, other) { - var res; - other = makeAs(other, base); - if (base.isBefore(other)) { - res = positiveMomentsDifference(base, other); - } else { - res = positiveMomentsDifference(other, base); - res.milliseconds = -res.milliseconds; - res.months = -res.months; + else { + // create unordered list + for (id in data) { + if (data.hasOwnProperty(id)) { + item = this._getItem(id, type); + if (filter(item)) { + ids.push(item[this._fieldId]); + } } - - return res; + } } + } + else { + // get all items + if (order) { + // create an ordered list + items = []; + for (id in data) { + if (data.hasOwnProperty(id)) { + items.push(data[id]); + } + } - // TODO: remove 'name' arg after deprecation is removed - function createAdder(direction, name) { - return function (val, period) { - var dur, tmp; - //invert the arguments, but complain about it - if (period !== null && !isNaN(+period)) { - deprecateSimple(name, 'moment().' + name + '(period, number) is deprecated. Please use moment().' + name + '(number, period).'); - tmp = val; val = period; period = tmp; - } + this._sort(items, order); - val = typeof val === 'string' ? +val : val; - dur = moment.duration(val, period); - addOrSubtractDurationFromMoment(this, dur, direction); - return this; - }; + for (i = 0, len = items.length; i < len; i++) { + ids[i] = items[i][this._fieldId]; + } } - - function addOrSubtractDurationFromMoment(mom, duration, isAdding, updateOffset) { - var milliseconds = duration._milliseconds, - days = duration._days, - months = duration._months; - updateOffset = updateOffset == null ? true : updateOffset; - - if (milliseconds) { - mom._d.setTime(+mom._d + milliseconds * isAdding); - } - if (days) { - rawSetter(mom, 'Date', rawGetter(mom, 'Date') + days * isAdding); - } - if (months) { - rawMonthSetter(mom, rawGetter(mom, 'Month') + months * isAdding); - } - if (updateOffset) { - moment.updateOffset(mom, days || months); + else { + // create unordered list + for (id in data) { + if (data.hasOwnProperty(id)) { + item = data[id]; + ids.push(item[this._fieldId]); } + } } + } - // check if is an array - function isArray(input) { - return Object.prototype.toString.call(input) === '[object Array]'; - } + return ids; + }; - function isDate(input) { - return Object.prototype.toString.call(input) === '[object Date]' || - input instanceof Date; - } + /** + * Returns the DataSet itself. Is overwritten for example by the DataView, + * which returns the DataSet it is connected to instead. + */ + DataSet.prototype.getDataSet = function () { + return this; + }; - // compare two arrays, return the number of differences - function compareArrays(array1, array2, dontConvert) { - var len = Math.min(array1.length, array2.length), - lengthDiff = Math.abs(array1.length - array2.length), - diffs = 0, - i; - for (i = 0; i < len; i++) { - if ((dontConvert && array1[i] !== array2[i]) || - (!dontConvert && toInt(array1[i]) !== toInt(array2[i]))) { - diffs++; - } - } - return diffs + lengthDiff; - } + /** + * Execute a callback function for every item in the dataset. + * @param {function} callback + * @param {Object} [options] Available options: + * {Object.} [type] + * {String[]} [fields] filter fields + * {function} [filter] filter items + * {String | function} [order] Order the items by + * a field name or custom sort function. + */ + DataSet.prototype.forEach = function (callback, options) { + var filter = options && options.filter, + type = options && options.type || this._options.type, + data = this._data, + item, + id; - function normalizeUnits(units) { - if (units) { - var lowered = units.toLowerCase().replace(/(.)s$/, '$1'); - units = unitAliases[units] || camelFunctions[lowered] || lowered; + if (options && options.order) { + // execute forEach on ordered list + var items = this.get(options); + + for (var i = 0, len = items.length; i < len; i++) { + item = items[i]; + id = item[this._fieldId]; + callback(item, id); + } + } + else { + // unordered + for (id in data) { + if (data.hasOwnProperty(id)) { + item = this._getItem(id, type); + if (!filter || filter(item)) { + callback(item, id); } - return units; + } } + } + }; - function normalizeObjectUnits(inputObject) { - var normalizedInput = {}, - normalizedProp, - prop; - - for (prop in inputObject) { - if (hasOwnProp(inputObject, prop)) { - normalizedProp = normalizeUnits(prop); - if (normalizedProp) { - normalizedInput[normalizedProp] = inputObject[prop]; - } - } - } + /** + * Map every item in the dataset. + * @param {function} callback + * @param {Object} [options] Available options: + * {Object.} [type] + * {String[]} [fields] filter fields + * {function} [filter] filter items + * {String | function} [order] Order the items by + * a field name or custom sort function. + * @return {Object[]} mappedItems + */ + DataSet.prototype.map = function (callback, options) { + var filter = options && options.filter, + type = options && options.type || this._options.type, + mappedItems = [], + data = this._data, + item; - return normalizedInput; + // convert and filter items + for (var id in data) { + if (data.hasOwnProperty(id)) { + item = this._getItem(id, type); + if (!filter || filter(item)) { + mappedItems.push(callback(item, id)); + } } + } - function makeList(field) { - var count, setter; - - if (field.indexOf('week') === 0) { - count = 7; - setter = 'day'; - } - else if (field.indexOf('month') === 0) { - count = 12; - setter = 'month'; - } - else { - return; - } + // order items + if (options && options.order) { + this._sort(mappedItems, options.order); + } - moment[field] = function (format, index) { - var i, getter, - method = moment._locale[field], - results = []; + return mappedItems; + }; - if (typeof format === 'number') { - index = format; - format = undefined; - } + /** + * Filter the fields of an item + * @param {Object | null} item + * @param {String[]} fields Field names + * @return {Object | null} filteredItem or null if no item is provided + * @private + */ + DataSet.prototype._filterFields = function (item, fields) { + if (!item) { // item is null + return item; + } - getter = function (i) { - var m = moment().utc().set(setter, i); - return method.call(moment._locale, m, format || ''); - }; + var filteredItem = {}; - if (index != null) { - return getter(index); - } - else { - for (i = 0; i < count; i++) { - results.push(getter(i)); - } - return results; - } - }; + for (var field in item) { + if (item.hasOwnProperty(field) && (fields.indexOf(field) != -1)) { + filteredItem[field] = item[field]; } + } - function toInt(argumentForCoercion) { - var coercedNumber = +argumentForCoercion, - value = 0; + return filteredItem; + }; - if (coercedNumber !== 0 && isFinite(coercedNumber)) { - if (coercedNumber >= 0) { - value = Math.floor(coercedNumber); - } else { - value = Math.ceil(coercedNumber); - } - } + /** + * Sort the provided array with items + * @param {Object[]} items + * @param {String | function} order A field name or custom sort function. + * @private + */ + DataSet.prototype._sort = function (items, order) { + if (util.isString(order)) { + // order by provided field name + var name = order; // field name + items.sort(function (a, b) { + var av = a[name]; + var bv = b[name]; + return (av > bv) ? 1 : ((av < bv) ? -1 : 0); + }); + } + else if (typeof order === 'function') { + // order by sort function + items.sort(order); + } + // TODO: extend order by an Object {field:String, direction:String} + // where direction can be 'asc' or 'desc' + else { + throw new TypeError('Order must be a function or a string'); + } + }; - return value; - } + /** + * Remove an object by pointer or by id + * @param {String | Number | Object | Array} id Object or id, or an array with + * objects or ids to be removed + * @param {String} [senderId] Optional sender id + * @return {Array} removedIds + */ + DataSet.prototype.remove = function (id, senderId) { + var removedIds = [], + i, len, removedId; - function daysInMonth(year, month) { - return new Date(Date.UTC(year, month + 1, 0)).getUTCDate(); + if (Array.isArray(id)) { + for (i = 0, len = id.length; i < len; i++) { + removedId = this._remove(id[i]); + if (removedId != null) { + removedIds.push(removedId); + } } - - function weeksInYear(year, dow, doy) { - return weekOfYear(moment([year, 11, 31 + dow - doy]), dow, doy).week; + } + else { + removedId = this._remove(id); + if (removedId != null) { + removedIds.push(removedId); } + } - function daysInYear(year) { - return isLeapYear(year) ? 366 : 365; - } + if (removedIds.length) { + this._trigger('remove', {items: removedIds}, senderId); + } - function isLeapYear(year) { - return (year % 4 === 0 && year % 100 !== 0) || year % 400 === 0; + return removedIds; + }; + + /** + * Remove an item by its id + * @param {Number | String | Object} id id or item + * @returns {Number | String | null} id + * @private + */ + DataSet.prototype._remove = function (id) { + if (util.isNumber(id) || util.isString(id)) { + if (this._data[id]) { + delete this._data[id]; + this.length--; + return id; + } + } + else if (id instanceof Object) { + var itemId = id[this._fieldId]; + if (itemId && this._data[itemId]) { + delete this._data[itemId]; + this.length--; + return itemId; } + } + return null; + }; - function checkOverflow(m) { - var overflow; - if (m._a && m._pf.overflow === -2) { - overflow = - m._a[MONTH] < 0 || m._a[MONTH] > 11 ? MONTH : - m._a[DATE] < 1 || m._a[DATE] > daysInMonth(m._a[YEAR], m._a[MONTH]) ? DATE : - m._a[HOUR] < 0 || m._a[HOUR] > 24 || - (m._a[HOUR] === 24 && (m._a[MINUTE] !== 0 || - m._a[SECOND] !== 0 || - m._a[MILLISECOND] !== 0)) ? HOUR : - m._a[MINUTE] < 0 || m._a[MINUTE] > 59 ? MINUTE : - m._a[SECOND] < 0 || m._a[SECOND] > 59 ? SECOND : - m._a[MILLISECOND] < 0 || m._a[MILLISECOND] > 999 ? MILLISECOND : - -1; + /** + * Clear the data + * @param {String} [senderId] Optional sender id + * @return {Array} removedIds The ids of all removed items + */ + DataSet.prototype.clear = function (senderId) { + var ids = Object.keys(this._data); - if (m._pf._overflowDayOfYear && (overflow < YEAR || overflow > DATE)) { - overflow = DATE; - } + this._data = {}; + this.length = 0; - m._pf.overflow = overflow; - } - } + this._trigger('remove', {items: ids}, senderId); - function isValid(m) { - if (m._isValid == null) { - m._isValid = !isNaN(m._d.getTime()) && - m._pf.overflow < 0 && - !m._pf.empty && - !m._pf.invalidMonth && - !m._pf.nullInput && - !m._pf.invalidFormat && - !m._pf.userInvalidated; + return ids; + }; - if (m._strict) { - m._isValid = m._isValid && - m._pf.charsLeftOver === 0 && - m._pf.unusedTokens.length === 0 && - m._pf.bigHour === undefined; - } - } - return m._isValid; - } + /** + * Find the item with maximum value of a specified field + * @param {String} field + * @return {Object | null} item Item containing max value, or null if no items + */ + DataSet.prototype.max = function (field) { + var data = this._data, + max = null, + maxField = null; - function normalizeLocale(key) { - return key ? key.toLowerCase().replace('_', '-') : key; + for (var id in data) { + if (data.hasOwnProperty(id)) { + var item = data[id]; + var itemField = item[field]; + if (itemField != null && (!max || itemField > maxField)) { + max = item; + maxField = itemField; + } } + } - // pick the locale from the array - // try ['en-au', 'en-gb'] as 'en-au', 'en-gb', 'en', as in move through the list trying each - // substring from most specific to least, but move to the next array item if it's a more specific variant than the current root - function chooseLocale(names) { - var i = 0, j, next, locale, split; + return max; + }; - while (i < names.length) { - split = normalizeLocale(names[i]).split('-'); - j = split.length; - next = normalizeLocale(names[i + 1]); - next = next ? next.split('-') : null; - while (j > 0) { - locale = loadLocale(split.slice(0, j).join('-')); - if (locale) { - return locale; - } - if (next && next.length >= j && compareArrays(split, next, true) >= j - 1) { - //the next array item is better than a shallower substring of this one - break; - } - j--; - } - i++; - } - return null; + /** + * Find the item with minimum value of a specified field + * @param {String} field + * @return {Object | null} item Item containing max value, or null if no items + */ + DataSet.prototype.min = function (field) { + var data = this._data, + min = null, + minField = null; + + for (var id in data) { + if (data.hasOwnProperty(id)) { + var item = data[id]; + var itemField = item[field]; + if (itemField != null && (!min || itemField < minField)) { + min = item; + minField = itemField; + } } + } - function loadLocale(name) { - var oldLocale = null; - if (!locales[name] && hasModule) { - try { - oldLocale = moment.locale(); - !(function webpackMissingModule() { var e = new Error("Cannot find module \"./locale\""); e.code = 'MODULE_NOT_FOUND'; throw e; }()); - // because defineLocale currently also sets the global locale, we want to undo that for lazy loaded locales - moment.locale(oldLocale); - } catch (e) { } + return min; + }; + + /** + * Find all distinct values of a specified field + * @param {String} field + * @return {Array} values Array containing all distinct values. If data items + * do not contain the specified field are ignored. + * The returned array is unordered. + */ + DataSet.prototype.distinct = function (field) { + var data = this._data; + var values = []; + var fieldType = this._options.type && this._options.type[field] || null; + var count = 0; + var i; + + for (var prop in data) { + if (data.hasOwnProperty(prop)) { + var item = data[prop]; + var value = item[field]; + var exists = false; + for (i = 0; i < count; i++) { + if (values[i] == value) { + exists = true; + break; } - return locales[name]; + } + if (!exists && (value !== undefined)) { + values[count] = value; + count++; + } } + } - // Return a moment from input, that is local/utc/utcOffset equivalent to - // model. - function makeAs(input, model) { - var res, diff; - if (model._isUTC) { - res = model.clone(); - diff = (moment.isMoment(input) || isDate(input) ? - +input : +moment(input)) - (+res); - // Use low-level api, because this fn is low-level api. - res._d.setTime(+res._d + diff); - moment.updateOffset(res, false); - return res; - } else { - return moment(input).local(); - } + if (fieldType) { + for (i = 0; i < values.length; i++) { + values[i] = util.convert(values[i], fieldType); } + } - /************************************ - Locale - ************************************/ + return values; + }; + /** + * Add a single item. Will fail when an item with the same id already exists. + * @param {Object} item + * @return {String} id + * @private + */ + DataSet.prototype._addItem = function (item) { + var id = item[this._fieldId]; - extend(Locale.prototype, { + if (id != undefined) { + // check whether this id is already taken + if (this._data[id]) { + // item already exists + throw new Error('Cannot add item: item with id ' + id + ' already exists'); + } + } + else { + // generate an id + id = util.randomUUID(); + item[this._fieldId] = id; + } - set : function (config) { - var prop, i; - for (i in config) { - prop = config[i]; - if (typeof prop === 'function') { - this[i] = prop; - } else { - this['_' + i] = prop; - } - } - // Lenient ordinal parsing accepts just a number in addition to - // number + (possibly) stuff coming from _ordinalParseLenient. - this._ordinalParseLenient = new RegExp(this._ordinalParse.source + '|' + /\d{1,2}/.source); - }, + var d = {}; + for (var field in item) { + if (item.hasOwnProperty(field)) { + var fieldType = this._type[field]; // type may be undefined + d[field] = util.convert(item[field], fieldType); + } + } + this._data[id] = d; + this.length++; - _months : 'January_February_March_April_May_June_July_August_September_October_November_December'.split('_'), - months : function (m) { - return this._months[m.month()]; - }, + return id; + }; - _monthsShort : 'Jan_Feb_Mar_Apr_May_Jun_Jul_Aug_Sep_Oct_Nov_Dec'.split('_'), - monthsShort : function (m) { - return this._monthsShort[m.month()]; - }, + /** + * Get an item. Fields can be converted to a specific type + * @param {String} id + * @param {Object.} [types] field types to convert + * @return {Object | null} item + * @private + */ + DataSet.prototype._getItem = function (id, types) { + var field, value; - monthsParse : function (monthName, format, strict) { - var i, mom, regex; + // get the item from the dataset + var raw = this._data[id]; + if (!raw) { + return null; + } - if (!this._monthsParse) { - this._monthsParse = []; - this._longMonthsParse = []; - this._shortMonthsParse = []; - } + // convert the items field types + var converted = {}; + if (types) { + for (field in raw) { + if (raw.hasOwnProperty(field)) { + value = raw[field]; + converted[field] = util.convert(value, types[field]); + } + } + } + else { + // no field types specified, no converting needed + for (field in raw) { + if (raw.hasOwnProperty(field)) { + value = raw[field]; + converted[field] = value; + } + } + } + return converted; + }; - for (i = 0; i < 12; i++) { - // make the regex if we don't have it already - mom = moment.utc([2000, i]); - if (strict && !this._longMonthsParse[i]) { - this._longMonthsParse[i] = new RegExp('^' + this.months(mom, '').replace('.', '') + '$', 'i'); - this._shortMonthsParse[i] = new RegExp('^' + this.monthsShort(mom, '').replace('.', '') + '$', 'i'); - } - if (!strict && !this._monthsParse[i]) { - regex = '^' + this.months(mom, '') + '|^' + this.monthsShort(mom, ''); - this._monthsParse[i] = new RegExp(regex.replace('.', ''), 'i'); - } - // test the regex - if (strict && format === 'MMMM' && this._longMonthsParse[i].test(monthName)) { - return i; - } else if (strict && format === 'MMM' && this._shortMonthsParse[i].test(monthName)) { - return i; - } else if (!strict && this._monthsParse[i].test(monthName)) { - return i; - } - } - }, + /** + * Update a single item: merge with existing item. + * Will fail when the item has no id, or when there does not exist an item + * with the same id. + * @param {Object} item + * @return {String} id + * @private + */ + DataSet.prototype._updateItem = function (item) { + var id = item[this._fieldId]; + if (id == undefined) { + throw new Error('Cannot update item: item has no id (item: ' + JSON.stringify(item) + ')'); + } + var d = this._data[id]; + if (!d) { + // item doesn't exist + throw new Error('Cannot update item: no item with id ' + id + ' found'); + } - _weekdays : 'Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday'.split('_'), - weekdays : function (m) { - return this._weekdays[m.day()]; - }, + // merge with current item + for (var field in item) { + if (item.hasOwnProperty(field)) { + var fieldType = this._type[field]; // type may be undefined + d[field] = util.convert(item[field], fieldType); + } + } - _weekdaysShort : 'Sun_Mon_Tue_Wed_Thu_Fri_Sat'.split('_'), - weekdaysShort : function (m) { - return this._weekdaysShort[m.day()]; - }, + return id; + }; - _weekdaysMin : 'Su_Mo_Tu_We_Th_Fr_Sa'.split('_'), - weekdaysMin : function (m) { - return this._weekdaysMin[m.day()]; - }, + /** + * Get an array with the column names of a Google DataTable + * @param {DataTable} dataTable + * @return {String[]} columnNames + * @private + */ + DataSet.prototype._getColumnNames = function (dataTable) { + var columns = []; + for (var col = 0, cols = dataTable.getNumberOfColumns(); col < cols; col++) { + columns[col] = dataTable.getColumnId(col) || dataTable.getColumnLabel(col); + } + return columns; + }; - weekdaysParse : function (weekdayName) { - var i, mom, regex; + /** + * Append an item as a row to the dataTable + * @param dataTable + * @param columns + * @param item + * @private + */ + DataSet.prototype._appendRow = function (dataTable, columns, item) { + var row = dataTable.addRow(); - if (!this._weekdaysParse) { - this._weekdaysParse = []; - } + for (var col = 0, cols = columns.length; col < cols; col++) { + var field = columns[col]; + dataTable.setValue(row, col, item[field]); + } + }; - for (i = 0; i < 7; i++) { - // make the regex if we don't have it already - if (!this._weekdaysParse[i]) { - mom = moment([2000, 1]).day(i); - regex = '^' + this.weekdays(mom, '') + '|^' + this.weekdaysShort(mom, '') + '|^' + this.weekdaysMin(mom, ''); - this._weekdaysParse[i] = new RegExp(regex.replace('.', ''), 'i'); - } - // test the regex - if (this._weekdaysParse[i].test(weekdayName)) { - return i; - } - } - }, + module.exports = DataSet; - _longDateFormat : { - LTS : 'h:mm:ss A', - LT : 'h:mm A', - L : 'MM/DD/YYYY', - LL : 'MMMM D, YYYY', - LLL : 'MMMM D, YYYY LT', - LLLL : 'dddd, MMMM D, YYYY LT' - }, - longDateFormat : function (key) { - var output = this._longDateFormat[key]; - if (!output && this._longDateFormat[key.toUpperCase()]) { - output = this._longDateFormat[key.toUpperCase()].replace(/MMMM|MM|DD|dddd/g, function (val) { - return val.slice(1); - }); - this._longDateFormat[key] = output; - } - return output; - }, - isPM : function (input) { - // IE8 Quirks Mode & IE7 Standards Mode do not allow accessing strings like arrays - // Using charAt should be more compatible. - return ((input + '').toLowerCase().charAt(0) === 'p'); - }, +/***/ }, +/* 4 */ +/***/ function(module, exports, __webpack_require__) { - _meridiemParse : /[ap]\.?m?\.?/i, - meridiem : function (hours, minutes, isLower) { - if (hours > 11) { - return isLower ? 'pm' : 'PM'; - } else { - return isLower ? 'am' : 'AM'; - } - }, + var util = __webpack_require__(1); + var DataSet = __webpack_require__(3); + /** + * DataView + * + * a dataview offers a filtered view on a dataset or an other dataview. + * + * @param {DataSet | DataView} data + * @param {Object} [options] Available options: see method get + * + * @constructor DataView + */ + function DataView (data, options) { + this._data = null; + this._ids = {}; // ids of the items currently in memory (just contains a boolean true) + this.length = 0; // number of items in the DataView + this._options = options || {}; + this._fieldId = 'id'; // name of the field containing id + this._subscribers = {}; // event subscribers - _calendar : { - sameDay : '[Today at] LT', - nextDay : '[Tomorrow at] LT', - nextWeek : 'dddd [at] LT', - lastDay : '[Yesterday at] LT', - lastWeek : '[Last] dddd [at] LT', - sameElse : 'L' - }, - calendar : function (key, mom, now) { - var output = this._calendar[key]; - return typeof output === 'function' ? output.apply(mom, [now]) : output; - }, + var me = this; + this.listener = function () { + me._onEvent.apply(me, arguments); + }; - _relativeTime : { - future : 'in %s', - past : '%s ago', - s : 'a few seconds', - m : 'a minute', - mm : '%d minutes', - h : 'an hour', - hh : '%d hours', - d : 'a day', - dd : '%d days', - M : 'a month', - MM : '%d months', - y : 'a year', - yy : '%d years' - }, + this.setData(data); + } - relativeTime : function (number, withoutSuffix, string, isFuture) { - var output = this._relativeTime[string]; - return (typeof output === 'function') ? - output(number, withoutSuffix, string, isFuture) : - output.replace(/%d/i, number); - }, + // TODO: implement a function .config() to dynamically update things like configured filter + // and trigger changes accordingly - pastFuture : function (diff, output) { - var format = this._relativeTime[diff > 0 ? 'future' : 'past']; - return typeof format === 'function' ? format(output) : format.replace(/%s/i, output); - }, + /** + * Set a data source for the view + * @param {DataSet | DataView} data + */ + DataView.prototype.setData = function (data) { + var ids, i, len; - ordinal : function (number) { - return this._ordinal.replace('%d', number); - }, - _ordinal : '%d', - _ordinalParse : /\d{1,2}/, + if (this._data) { + // unsubscribe from current dataset + if (this._data.unsubscribe) { + this._data.unsubscribe('*', this.listener); + } - preparse : function (string) { - return string; - }, + // trigger a remove of all items in memory + ids = []; + for (var id in this._ids) { + if (this._ids.hasOwnProperty(id)) { + ids.push(id); + } + } + this._ids = {}; + this.length = 0; + this._trigger('remove', {items: ids}); + } - postformat : function (string) { - return string; - }, + this._data = data; - week : function (mom) { - return weekOfYear(mom, this._week.dow, this._week.doy).week; - }, + if (this._data) { + // update fieldId + this._fieldId = this._options.fieldId || + (this._data && this._data.options && this._data.options.fieldId) || + 'id'; - _week : { - dow : 0, // Sunday is the first day of the week. - doy : 6 // The week that contains Jan 1st is the first week of the year. - }, + // trigger an add of all added items + ids = this._data.getIds({filter: this._options && this._options.filter}); + for (i = 0, len = ids.length; i < len; i++) { + id = ids[i]; + this._ids[id] = true; + } + this.length = ids.length; + this._trigger('add', {items: ids}); - firstDayOfWeek : function () { - return this._week.dow; - }, + // subscribe to new dataset + if (this._data.on) { + this._data.on('*', this.listener); + } + } + }; - firstDayOfYear : function () { - return this._week.doy; - }, - - _invalidDate: 'Invalid date', - invalidDate: function () { - return this._invalidDate; - } - }); - - /************************************ - Formatting - ************************************/ + /** + * Refresh the DataView. Useful when the DataView has a filter function + * containing a variable parameter. + */ + DataView.prototype.refresh = function () { + var id; + var ids = this._data.getIds({filter: this._options && this._options.filter}); + var newIds = {}; + var added = []; + var removed = []; + // check for additions + for (var i = 0; i < ids.length; i++) { + id = ids[i]; + newIds[id] = true; + if (!this._ids[id]) { + added.push(id); + this._ids[id] = true; + this.length++; + } + } - function removeFormattingTokens(input) { - if (input.match(/\[[\s\S]/)) { - return input.replace(/^\[|\]$/g, ''); - } - return input.replace(/\\/g, ''); + // check for removals + for (id in this._ids) { + if (this._ids.hasOwnProperty(id)) { + if (!newIds[id]) { + removed.push(id); + delete this._ids[id]; + this.length--; + } } + } - function makeFormatFunction(format) { - var array = format.match(formattingTokens), i, length; + // trigger events + if (added.length) { + this._trigger('add', {items: added}); + } + if (removed.length) { + this._trigger('remove', {items: removed}); + } + }; - for (i = 0, length = array.length; i < length; i++) { - if (formatTokenFunctions[array[i]]) { - array[i] = formatTokenFunctions[array[i]]; - } else { - array[i] = removeFormattingTokens(array[i]); - } - } + /** + * Get data from the data view + * + * Usage: + * + * get() + * get(options: Object) + * get(options: Object, data: Array | DataTable) + * + * get(id: Number) + * get(id: Number, options: Object) + * get(id: Number, options: Object, data: Array | DataTable) + * + * get(ids: Number[]) + * get(ids: Number[], options: Object) + * get(ids: Number[], options: Object, data: Array | DataTable) + * + * Where: + * + * {Number | String} id The id of an item + * {Number[] | String{}} ids An array with ids of items + * {Object} options An Object with options. Available options: + * {String} [type] Type of data to be returned. Can + * be 'DataTable' or 'Array' (default) + * {Object.} [convert] + * {String[]} [fields] field names to be returned + * {function} [filter] filter items + * {String | function} [order] Order the items by + * a field name or custom sort function. + * {Array | DataTable} [data] If provided, items will be appended to this + * array or table. Required in case of Google + * DataTable. + * @param args + */ + DataView.prototype.get = function (args) { + var me = this; - return function (mom) { - var output = ''; - for (i = 0; i < length; i++) { - output += array[i] instanceof Function ? array[i].call(mom, format) : array[i]; - } - return output; - }; - } + // parse the arguments + var ids, options, data; + var firstType = util.getType(arguments[0]); + if (firstType == 'String' || firstType == 'Number' || firstType == 'Array') { + // get(id(s) [, options] [, data]) + ids = arguments[0]; // can be a single id or an array with ids + options = arguments[1]; + data = arguments[2]; + } + else { + // get([, options] [, data]) + options = arguments[0]; + data = arguments[1]; + } - // format date using native date object - function formatMoment(m, format) { - if (!m.isValid()) { - return m.localeData().invalidDate(); - } + // extend the options with the default options and provided options + var viewOptions = util.extend({}, this._options, options); - format = expandFormat(format, m.localeData()); + // create a combined filter method when needed + if (this._options.filter && options && options.filter) { + viewOptions.filter = function (item) { + return me._options.filter(item) && options.filter(item); + } + } - if (!formatFunctions[format]) { - formatFunctions[format] = makeFormatFunction(format); - } + // build up the call to the linked data set + var getArguments = []; + if (ids != undefined) { + getArguments.push(ids); + } + getArguments.push(viewOptions); + getArguments.push(data); - return formatFunctions[format](m); - } + return this._data && this._data.get.apply(this._data, getArguments); + }; - function expandFormat(format, locale) { - var i = 5; + /** + * Get ids of all items or from a filtered set of items. + * @param {Object} [options] An Object with options. Available options: + * {function} [filter] filter items + * {String | function} [order] Order the items by + * a field name or custom sort function. + * @return {Array} ids + */ + DataView.prototype.getIds = function (options) { + var ids; - function replaceLongDateFormatTokens(input) { - return locale.longDateFormat(input) || input; - } + if (this._data) { + var defaultFilter = this._options.filter; + var filter; - localFormattingTokens.lastIndex = 0; - while (i >= 0 && localFormattingTokens.test(format)) { - format = format.replace(localFormattingTokens, replaceLongDateFormatTokens); - localFormattingTokens.lastIndex = 0; - i -= 1; + if (options && options.filter) { + if (defaultFilter) { + filter = function (item) { + return defaultFilter(item) && options.filter(item); } - - return format; + } + else { + filter = options.filter; + } + } + else { + filter = defaultFilter; } + ids = this._data.getIds({ + filter: filter, + order: options && options.order + }); + } + else { + ids = []; + } - /************************************ - Parsing - ************************************/ + return ids; + }; + /** + * Get the DataSet to which this DataView is connected. In case there is a chain + * of multiple DataViews, the root DataSet of this chain is returned. + * @return {DataSet} dataSet + */ + DataView.prototype.getDataSet = function () { + var dataSet = this; + while (dataSet instanceof DataView) { + dataSet = dataSet._data; + } + return dataSet || null; + }; - // get the regex to find the next token - function getParseRegexForToken(token, config) { - var a, strict = config._strict; - switch (token) { - case 'Q': - return parseTokenOneDigit; - case 'DDDD': - return parseTokenThreeDigits; - case 'YYYY': - case 'GGGG': - case 'gggg': - return strict ? parseTokenFourDigits : parseTokenOneToFourDigits; - case 'Y': - case 'G': - case 'g': - return parseTokenSignedNumber; - case 'YYYYYY': - case 'YYYYY': - case 'GGGGG': - case 'ggggg': - return strict ? parseTokenSixDigits : parseTokenOneToSixDigits; - case 'S': - if (strict) { - return parseTokenOneDigit; - } - /* falls through */ - case 'SS': - if (strict) { - return parseTokenTwoDigits; - } - /* falls through */ - case 'SSS': - if (strict) { - return parseTokenThreeDigits; - } - /* falls through */ - case 'DDD': - return parseTokenOneToThreeDigits; - case 'MMM': - case 'MMMM': - case 'dd': - case 'ddd': - case 'dddd': - return parseTokenWord; - case 'a': - case 'A': - return config._locale._meridiemParse; - case 'x': - return parseTokenOffsetMs; - case 'X': - return parseTokenTimestampMs; - case 'Z': - case 'ZZ': - return parseTokenTimezone; - case 'T': - return parseTokenT; - case 'SSSS': - return parseTokenDigits; - case 'MM': - case 'DD': - case 'YY': - case 'GG': - case 'gg': - case 'HH': - case 'hh': - case 'mm': - case 'ss': - case 'ww': - case 'WW': - return strict ? parseTokenTwoDigits : parseTokenOneOrTwoDigits; - case 'M': - case 'D': - case 'd': - case 'H': - case 'h': - case 'm': - case 's': - case 'w': - case 'W': - case 'e': - case 'E': - return parseTokenOneOrTwoDigits; - case 'Do': - return strict ? config._locale._ordinalParse : config._locale._ordinalParseLenient; - default : - a = new RegExp(regexpEscape(unescapeFormat(token.replace('\\', '')), 'i')); - return a; - } - } + /** + * Event listener. Will propagate all events from the connected data set to + * the subscribers of the DataView, but will filter the items and only trigger + * when there are changes in the filtered data set. + * @param {String} event + * @param {Object | null} params + * @param {String} senderId + * @private + */ + DataView.prototype._onEvent = function (event, params, senderId) { + var i, len, id, item, + ids = params && params.items, + data = this._data, + added = [], + updated = [], + removed = []; - function utcOffsetFromString(string) { - string = string || ''; - var possibleTzMatches = (string.match(parseTokenTimezone) || []), - tzChunk = possibleTzMatches[possibleTzMatches.length - 1] || [], - parts = (tzChunk + '').match(parseTimezoneChunker) || ['-', 0, 0], - minutes = +(parts[1] * 60) + toInt(parts[2]); + if (ids && data) { + switch (event) { + case 'add': + // filter the ids of the added items + for (i = 0, len = ids.length; i < len; i++) { + id = ids[i]; + item = this.get(id); + if (item) { + this._ids[id] = true; + added.push(id); + } + } - return parts[0] === '+' ? minutes : -minutes; - } + break; - // function to convert string input to date - function addTimeToArrayFromToken(token, input, config) { - var a, datePartArray = config._a; + case 'update': + // determine the event from the views viewpoint: an updated + // item can be added, updated, or removed from this view. + for (i = 0, len = ids.length; i < len; i++) { + id = ids[i]; + item = this.get(id); - switch (token) { - // QUARTER - case 'Q': - if (input != null) { - datePartArray[MONTH] = (toInt(input) - 1) * 3; - } - break; - // MONTH - case 'M' : // fall through to MM - case 'MM' : - if (input != null) { - datePartArray[MONTH] = toInt(input) - 1; - } - break; - case 'MMM' : // fall through to MMMM - case 'MMMM' : - a = config._locale.monthsParse(input, token, config._strict); - // if we didn't find a month name, mark the date as invalid. - if (a != null) { - datePartArray[MONTH] = a; - } else { - config._pf.invalidMonth = input; + if (item) { + if (this._ids[id]) { + updated.push(id); } - break; - // DAY OF MONTH - case 'D' : // fall through to DD - case 'DD' : - if (input != null) { - datePartArray[DATE] = toInt(input); + else { + this._ids[id] = true; + added.push(id); } - break; - case 'Do' : - if (input != null) { - datePartArray[DATE] = toInt(parseInt( - input.match(/\d{1,2}/)[0], 10)); + } + else { + if (this._ids[id]) { + delete this._ids[id]; + removed.push(id); } - break; - // DAY OF YEAR - case 'DDD' : // fall through to DDDD - case 'DDDD' : - if (input != null) { - config._dayOfYear = toInt(input); + else { + // nothing interesting for me :-( } + } + } - break; - // YEAR - case 'YY' : - datePartArray[YEAR] = moment.parseTwoDigitYear(input); - break; - case 'YYYY' : - case 'YYYYY' : - case 'YYYYYY' : - datePartArray[YEAR] = toInt(input); - break; - // AM / PM - case 'a' : // fall through to A - case 'A' : - config._meridiem = input; - // config._isPm = config._locale.isPM(input); - break; - // HOUR - case 'h' : // fall through to hh - case 'hh' : - config._pf.bigHour = true; - /* falls through */ - case 'H' : // fall through to HH - case 'HH' : - datePartArray[HOUR] = toInt(input); - break; - // MINUTE - case 'm' : // fall through to mm - case 'mm' : - datePartArray[MINUTE] = toInt(input); - break; - // SECOND - case 's' : // fall through to ss - case 'ss' : - datePartArray[SECOND] = toInt(input); - break; - // MILLISECOND - case 'S' : - case 'SS' : - case 'SSS' : - case 'SSSS' : - datePartArray[MILLISECOND] = toInt(('0.' + input) * 1000); - break; - // UNIX OFFSET (MILLISECONDS) - case 'x': - config._d = new Date(toInt(input)); - break; - // UNIX TIMESTAMP WITH MS - case 'X': - config._d = new Date(parseFloat(input) * 1000); - break; - // TIMEZONE - case 'Z' : // fall through to ZZ - case 'ZZ' : - config._useUTC = true; - config._tzm = utcOffsetFromString(input); - break; - // WEEKDAY - human - case 'dd': - case 'ddd': - case 'dddd': - a = config._locale.weekdaysParse(input); - // if we didn't get a weekday name, mark the date as invalid - if (a != null) { - config._w = config._w || {}; - config._w['d'] = a; - } else { - config._pf.invalidWeekday = input; - } - break; - // WEEK, WEEK DAY - numeric - case 'w': - case 'ww': - case 'W': - case 'WW': - case 'd': - case 'e': - case 'E': - token = token.substr(0, 1); - /* falls through */ - case 'gggg': - case 'GGGG': - case 'GGGGG': - token = token.substr(0, 2); - if (input) { - config._w = config._w || {}; - config._w[token] = toInt(input); - } - break; - case 'gg': - case 'GG': - config._w = config._w || {}; - config._w[token] = moment.parseTwoDigitYear(input); + break; + + case 'remove': + // filter the ids of the removed items + for (i = 0, len = ids.length; i < len; i++) { + id = ids[i]; + if (this._ids[id]) { + delete this._ids[id]; + removed.push(id); + } } - } - function dayOfYearFromWeekInfo(config) { - var w, weekYear, week, weekday, dow, doy, temp; + break; + } - w = config._w; - if (w.GG != null || w.W != null || w.E != null) { - dow = 1; - doy = 4; + this.length += added.length - removed.length; - // TODO: We need to take the current isoWeekYear, but that depends on - // how we interpret now (local, utc, fixed offset). So create - // a now version of current config (take local/utc/offset flags, and - // create now). - weekYear = dfl(w.GG, config._a[YEAR], weekOfYear(moment(), 1, 4).year); - week = dfl(w.W, 1); - weekday = dfl(w.E, 1); - } else { - dow = config._locale._week.dow; - doy = config._locale._week.doy; + if (added.length) { + this._trigger('add', {items: added}, senderId); + } + if (updated.length) { + this._trigger('update', {items: updated}, senderId); + } + if (removed.length) { + this._trigger('remove', {items: removed}, senderId); + } + } + }; - weekYear = dfl(w.gg, config._a[YEAR], weekOfYear(moment(), dow, doy).year); - week = dfl(w.w, 1); + // copy subscription functionality from DataSet + DataView.prototype.on = DataSet.prototype.on; + DataView.prototype.off = DataSet.prototype.off; + DataView.prototype._trigger = DataSet.prototype._trigger; - if (w.d != null) { - // weekday -- low day numbers are considered next week - weekday = w.d; - if (weekday < dow) { - ++week; - } - } else if (w.e != null) { - // local weekday -- counting starts from begining of week - weekday = w.e + dow; - } else { - // default to begining of week - weekday = dow; - } - } - temp = dayOfYearFromWeeks(weekYear, week, weekday, doy, dow); - - config._a[YEAR] = temp.year; - config._dayOfYear = temp.dayOfYear; - } - - // convert an array to a date. - // the array should mirror the parameters below - // note: all values past the year are optional and will default to the lowest possible value. - // [year, month, day , hour, minute, second, millisecond] - function dateFromConfig(config) { - var i, date, input = [], currentDate, yearToUse; + // TODO: make these functions deprecated (replaced with `on` and `off` since version 0.5) + DataView.prototype.subscribe = DataView.prototype.on; + DataView.prototype.unsubscribe = DataView.prototype.off; - if (config._d) { - return; - } + module.exports = DataView; - currentDate = currentDateArray(config); +/***/ }, +/* 5 */ +/***/ function(module, exports, __webpack_require__) { - //compute day of the year from weeks and weekdays - if (config._w && config._a[DATE] == null && config._a[MONTH] == null) { - dayOfYearFromWeekInfo(config); - } + /** + * A queue + * @param {Object} options + * Available options: + * - delay: number When provided, the queue will be flushed + * automatically after an inactivity of this delay + * in milliseconds. + * Default value is null. + * - max: number When the queue exceeds the given maximum number + * of entries, the queue is flushed automatically. + * Default value of max is Infinity. + * @constructor + */ + function Queue(options) { + // options + this.delay = null; + this.max = Infinity; - //if the day of the year is set, figure out what it is - if (config._dayOfYear) { - yearToUse = dfl(config._a[YEAR], currentDate[YEAR]); + // properties + this._queue = []; + this._timeout = null; + this._extended = null; - if (config._dayOfYear > daysInYear(yearToUse)) { - config._pf._overflowDayOfYear = true; - } + this.setOptions(options); + } - date = makeUTCDate(yearToUse, 0, config._dayOfYear); - config._a[MONTH] = date.getUTCMonth(); - config._a[DATE] = date.getUTCDate(); - } + /** + * Update the configuration of the queue + * @param {Object} options + * Available options: + * - delay: number When provided, the queue will be flushed + * automatically after an inactivity of this delay + * in milliseconds. + * Default value is null. + * - max: number When the queue exceeds the given maximum number + * of entries, the queue is flushed automatically. + * Default value of max is Infinity. + * @param options + */ + Queue.prototype.setOptions = function (options) { + if (options && typeof options.delay !== 'undefined') { + this.delay = options.delay; + } + if (options && typeof options.max !== 'undefined') { + this.max = options.max; + } - // Default to current date. - // * if no year, month, day of month are given, default to today - // * if day of month is given, default month and year - // * if month is given, default only year - // * if year is given, don't default anything - for (i = 0; i < 3 && config._a[i] == null; ++i) { - config._a[i] = input[i] = currentDate[i]; - } + this._flushIfNeeded(); + }; - // Zero out whatever was not defaulted, including time - for (; i < 7; i++) { - config._a[i] = input[i] = (config._a[i] == null) ? (i === 2 ? 1 : 0) : config._a[i]; - } + /** + * Extend an object with queuing functionality. + * The object will be extended with a function flush, and the methods provided + * in options.replace will be replaced with queued ones. + * @param {Object} object + * @param {Object} options + * Available options: + * - replace: Array. + * A list with method names of the methods + * on the object to be replaced with queued ones. + * - delay: number When provided, the queue will be flushed + * automatically after an inactivity of this delay + * in milliseconds. + * Default value is null. + * - max: number When the queue exceeds the given maximum number + * of entries, the queue is flushed automatically. + * Default value of max is Infinity. + * @return {Queue} Returns the created queue + */ + Queue.extend = function (object, options) { + var queue = new Queue(options); - // Check for 24:00:00.000 - if (config._a[HOUR] === 24 && - config._a[MINUTE] === 0 && - config._a[SECOND] === 0 && - config._a[MILLISECOND] === 0) { - config._nextDay = true; - config._a[HOUR] = 0; - } + if (object.flush !== undefined) { + throw new Error('Target object already has a property flush'); + } + object.flush = function () { + queue.flush(); + }; - config._d = (config._useUTC ? makeUTCDate : makeDate).apply(null, input); - // Apply timezone offset from input. The actual utcOffset can be changed - // with parseZone. - if (config._tzm != null) { - config._d.setUTCMinutes(config._d.getUTCMinutes() - config._tzm); - } + var methods = [{ + name: 'flush', + original: undefined + }]; - if (config._nextDay) { - config._a[HOUR] = 24; - } + if (options && options.replace) { + for (var i = 0; i < options.replace.length; i++) { + var name = options.replace[i]; + methods.push({ + name: name, + original: object[name] + }); + queue.replace(object, name); } + } - function dateFromObject(config) { - var normalizedInput; + queue._extended = { + object: object, + methods: methods + }; - if (config._d) { - return; - } + return queue; + }; - normalizedInput = normalizeObjectUnits(config._i); - config._a = [ - normalizedInput.year, - normalizedInput.month, - normalizedInput.day || normalizedInput.date, - normalizedInput.hour, - normalizedInput.minute, - normalizedInput.second, - normalizedInput.millisecond - ]; + /** + * Destroy the queue. The queue will first flush all queued actions, and in + * case it has extended an object, will restore the original object. + */ + Queue.prototype.destroy = function () { + this.flush(); - dateFromConfig(config); + if (this._extended) { + var object = this._extended.object; + var methods = this._extended.methods; + for (var i = 0; i < methods.length; i++) { + var method = methods[i]; + if (method.original) { + object[method.name] = method.original; + } + else { + delete object[method.name]; + } } + this._extended = null; + } + }; - function currentDateArray(config) { - var now = new Date(); - if (config._useUTC) { - return [ - now.getUTCFullYear(), - now.getUTCMonth(), - now.getUTCDate() - ]; - } else { - return [now.getFullYear(), now.getMonth(), now.getDate()]; - } + /** + * Replace a method on an object with a queued version + * @param {Object} object Object having the method + * @param {string} method The method name + */ + Queue.prototype.replace = function(object, method) { + var me = this; + var original = object[method]; + if (!original) { + throw new Error('Method ' + method + ' undefined'); + } + + object[method] = function () { + // create an Array with the arguments + var args = []; + for (var i = 0; i < arguments.length; i++) { + args[i] = arguments[i]; } - // date from string and format string - function makeDateFromStringAndFormat(config) { - if (config._f === moment.ISO_8601) { - parseISO(config); - return; - } + // add this call to the queue + me.queue({ + args: args, + fn: original, + context: this + }); + }; + }; - config._a = []; - config._pf.empty = true; + /** + * Queue a call + * @param {function | {fn: function, args: Array} | {fn: function, args: Array, context: Object}} entry + */ + Queue.prototype.queue = function(entry) { + if (typeof entry === 'function') { + this._queue.push({fn: entry}); + } + else { + this._queue.push(entry); + } - // This array is used to make a Date, either with `new Date` or `Date.UTC` - var string = '' + config._i, - i, parsedInput, tokens, token, skipped, - stringLength = string.length, - totalParsedInputLength = 0; + this._flushIfNeeded(); + }; - tokens = expandFormat(config._f, config._locale).match(formattingTokens) || []; + /** + * Check whether the queue needs to be flushed + * @private + */ + Queue.prototype._flushIfNeeded = function () { + // flush when the maximum is exceeded. + if (this._queue.length > this.max) { + this.flush(); + } - for (i = 0; i < tokens.length; i++) { - token = tokens[i]; - parsedInput = (string.match(getParseRegexForToken(token, config)) || [])[0]; - if (parsedInput) { - skipped = string.substr(0, string.indexOf(parsedInput)); - if (skipped.length > 0) { - config._pf.unusedInput.push(skipped); - } - string = string.slice(string.indexOf(parsedInput) + parsedInput.length); - totalParsedInputLength += parsedInput.length; - } - // don't parse if it's not a known token - if (formatTokenFunctions[token]) { - if (parsedInput) { - config._pf.empty = false; - } - else { - config._pf.unusedTokens.push(token); - } - addTimeToArrayFromToken(token, parsedInput, config); - } - else if (config._strict && !parsedInput) { - config._pf.unusedTokens.push(token); - } - } + // flush after a period of inactivity when a delay is configured + clearTimeout(this._timeout); + if (this.queue.length > 0 && typeof this.delay === 'number') { + var me = this; + this._timeout = setTimeout(function () { + me.flush(); + }, this.delay); + } + }; - // add remaining unparsed input length to the string - config._pf.charsLeftOver = stringLength - totalParsedInputLength; - if (string.length > 0) { - config._pf.unusedInput.push(string); - } + /** + * Flush all queued calls + */ + Queue.prototype.flush = function () { + while (this._queue.length > 0) { + var entry = this._queue.shift(); + entry.fn.apply(entry.context || entry.fn, entry.args || []); + } + }; - // clear _12h flag if hour is <= 12 - if (config._pf.bigHour === true && config._a[HOUR] <= 12) { - config._pf.bigHour = undefined; - } - // handle meridiem - config._a[HOUR] = meridiemFixWrap(config._locale, config._a[HOUR], - config._meridiem); - dateFromConfig(config); - checkOverflow(config); - } + module.exports = Queue; - function unescapeFormat(s) { - return s.replace(/\\(\[)|\\(\])|\[([^\]\[]*)\]|\\(.)/g, function (matched, p1, p2, p3, p4) { - return p1 || p2 || p3 || p4; - }); - } - // Code from http://stackoverflow.com/questions/3561493/is-there-a-regexp-escape-function-in-javascript - function regexpEscape(s) { - return s.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&'); - } +/***/ }, +/* 6 */ +/***/ function(module, exports, __webpack_require__) { - // date from string and array of format strings - function makeDateFromStringAndArray(config) { - var tempConfig, - bestMoment, + var Emitter = __webpack_require__(56); + var DataSet = __webpack_require__(3); + var DataView = __webpack_require__(4); + var util = __webpack_require__(1); + var Point3d = __webpack_require__(10); + var Point2d = __webpack_require__(9); + var Camera = __webpack_require__(7); + var Filter = __webpack_require__(8); + var Slider = __webpack_require__(11); + var StepNumber = __webpack_require__(12); - scoreToBeat, - i, - currentScore; + /** + * @constructor Graph3d + * Graph3d displays data in 3d. + * + * Graph3d is developed in javascript as a Google Visualization Chart. + * + * @param {Element} container The DOM element in which the Graph3d will + * be created. Normally a div element. + * @param {DataSet | DataView | Array} [data] + * @param {Object} [options] + */ + function Graph3d(container, data, options) { + if (!(this instanceof Graph3d)) { + throw new SyntaxError('Constructor must be called with the new operator'); + } - if (config._f.length === 0) { - config._pf.invalidFormat = true; - config._d = new Date(NaN); - return; - } + // create variables and set default values + this.containerElement = container; + this.width = '400px'; + this.height = '400px'; + this.margin = 10; // px + this.defaultXCenter = '55%'; + this.defaultYCenter = '50%'; - for (i = 0; i < config._f.length; i++) { - currentScore = 0; - tempConfig = copyConfig({}, config); - if (config._useUTC != null) { - tempConfig._useUTC = config._useUTC; - } - tempConfig._pf = defaultParsingFlags(); - tempConfig._f = config._f[i]; - makeDateFromStringAndFormat(tempConfig); + this.xLabel = 'x'; + this.yLabel = 'y'; + this.zLabel = 'z'; - if (!isValid(tempConfig)) { - continue; - } + var passValueFn = function(v) { return v; }; + this.xValueLabel = passValueFn; + this.yValueLabel = passValueFn; + this.zValueLabel = passValueFn; + + this.filterLabel = 'time'; + this.legendLabel = 'value'; - // if there is any input that was not parsed add a penalty for that format - currentScore += tempConfig._pf.charsLeftOver; + this.style = Graph3d.STYLE.DOT; + this.showPerspective = true; + this.showGrid = true; + this.keepAspectRatio = true; + this.showShadow = false; + this.showGrayBottom = false; // TODO: this does not work correctly + this.showTooltip = false; + this.verticalRatio = 0.5; // 0.1 to 1.0, where 1.0 results in a 'cube' - //or tokens - currentScore += tempConfig._pf.unusedTokens.length * 10; + this.animationInterval = 1000; // milliseconds + this.animationPreload = false; - tempConfig._pf.score = currentScore; + this.camera = new Camera(); + this.eye = new Point3d(0, 0, -1); // TODO: set eye.z about 3/4 of the width of the window? - if (scoreToBeat == null || currentScore < scoreToBeat) { - scoreToBeat = currentScore; - bestMoment = tempConfig; - } - } + this.dataTable = null; // The original data table + this.dataPoints = null; // The table with point objects - extend(config, bestMoment || tempConfig); - } + // the column indexes + this.colX = undefined; + this.colY = undefined; + this.colZ = undefined; + this.colValue = undefined; + this.colFilter = undefined; - // date from iso format - function parseISO(config) { - var i, l, - string = config._i, - match = isoRegex.exec(string); + this.xMin = 0; + this.xStep = undefined; // auto by default + this.xMax = 1; + this.yMin = 0; + this.yStep = undefined; // auto by default + this.yMax = 1; + this.zMin = 0; + this.zStep = undefined; // auto by default + this.zMax = 1; + this.valueMin = 0; + this.valueMax = 1; + this.xBarWidth = 1; + this.yBarWidth = 1; + // TODO: customize axis range - if (match) { - config._pf.iso = true; - for (i = 0, l = isoDates.length; i < l; i++) { - if (isoDates[i][1].exec(string)) { - // match[5] should be 'T' or undefined - config._f = isoDates[i][0] + (match[6] || ' '); - break; - } - } - for (i = 0, l = isoTimes.length; i < l; i++) { - if (isoTimes[i][1].exec(string)) { - config._f += isoTimes[i][0]; - break; - } - } - if (string.match(parseTokenTimezone)) { - config._f += 'Z'; - } - makeDateFromStringAndFormat(config); - } else { - config._isValid = false; - } - } + // constants + this.colorAxis = '#4D4D4D'; + this.colorGrid = '#D3D3D3'; + this.colorDot = '#7DC1FF'; + this.colorDotBorder = '#3267D2'; - // date from iso format or fallback - function makeDateFromString(config) { - parseISO(config); - if (config._isValid === false) { - delete config._isValid; - moment.createFromInputFallback(config); - } - } + // create a frame and canvas + this.create(); - function map(arr, fn) { - var res = [], i; - for (i = 0; i < arr.length; ++i) { - res.push(fn(arr[i], i)); - } - return res; - } + // apply options (also when undefined) + this.setOptions(options); - function makeDateFromInput(config) { - var input = config._i, matched; - if (input === undefined) { - config._d = new Date(); - } else if (isDate(input)) { - config._d = new Date(+input); - } else if ((matched = aspNetJsonRegex.exec(input)) !== null) { - config._d = new Date(+matched[1]); - } else if (typeof input === 'string') { - makeDateFromString(config); - } else if (isArray(input)) { - config._a = map(input.slice(0), function (obj) { - return parseInt(obj, 10); - }); - dateFromConfig(config); - } else if (typeof(input) === 'object') { - dateFromObject(config); - } else if (typeof(input) === 'number') { - // from milliseconds - config._d = new Date(input); - } else { - moment.createFromInputFallback(config); - } - } + // apply data + if (data) { + this.setData(data); + } + } - function makeDate(y, m, d, h, M, s, ms) { - //can't just apply() to create a date: - //http://stackoverflow.com/questions/181348/instantiating-a-javascript-object-by-calling-prototype-constructor-apply - var date = new Date(y, m, d, h, M, s, ms); + // Extend Graph3d with an Emitter mixin + Emitter(Graph3d.prototype); - //the date constructor doesn't accept years < 1970 - if (y < 1970) { - date.setFullYear(y); - } - return date; - } + /** + * Calculate the scaling values, dependent on the range in x, y, and z direction + */ + Graph3d.prototype._setScale = function() { + this.scale = new Point3d(1 / (this.xMax - this.xMin), + 1 / (this.yMax - this.yMin), + 1 / (this.zMax - this.zMin)); - function makeUTCDate(y) { - var date = new Date(Date.UTC.apply(null, arguments)); - if (y < 1970) { - date.setUTCFullYear(y); - } - return date; + // keep aspect ration between x and y scale if desired + if (this.keepAspectRatio) { + if (this.scale.x < this.scale.y) { + //noinspection JSSuspiciousNameCombination + this.scale.y = this.scale.x; } - - function parseWeekday(input, locale) { - if (typeof input === 'string') { - if (!isNaN(input)) { - input = parseInt(input, 10); - } - else { - input = locale.weekdaysParse(input); - if (typeof input !== 'number') { - return null; - } - } - } - return input; + else { + //noinspection JSSuspiciousNameCombination + this.scale.x = this.scale.y; } + } - /************************************ - Relative Time - ************************************/ + // scale the vertical axis + this.scale.z *= this.verticalRatio; + // TODO: can this be automated? verticalRatio? + // determine scale for (optional) value + this.scale.value = 1 / (this.valueMax - this.valueMin); - // helper function for moment.fn.from, moment.fn.fromNow, and moment.duration.fn.humanize - function substituteTimeAgo(string, number, withoutSuffix, isFuture, locale) { - return locale.relativeTime(number || 1, !!withoutSuffix, string, isFuture); - } + // position the camera arm + var xCenter = (this.xMax + this.xMin) / 2 * this.scale.x; + var yCenter = (this.yMax + this.yMin) / 2 * this.scale.y; + var zCenter = (this.zMax + this.zMin) / 2 * this.scale.z; + this.camera.setArmLocation(xCenter, yCenter, zCenter); + }; - function relativeTime(posNegDuration, withoutSuffix, locale) { - var duration = moment.duration(posNegDuration).abs(), - seconds = round(duration.as('s')), - minutes = round(duration.as('m')), - hours = round(duration.as('h')), - days = round(duration.as('d')), - months = round(duration.as('M')), - years = round(duration.as('y')), - args = seconds < relativeTimeThresholds.s && ['s', seconds] || - minutes === 1 && ['m'] || - minutes < relativeTimeThresholds.m && ['mm', minutes] || - hours === 1 && ['h'] || - hours < relativeTimeThresholds.h && ['hh', hours] || - days === 1 && ['d'] || - days < relativeTimeThresholds.d && ['dd', days] || - months === 1 && ['M'] || - months < relativeTimeThresholds.M && ['MM', months] || - years === 1 && ['y'] || ['yy', years]; + /** + * Convert a 3D location to a 2D location on screen + * http://en.wikipedia.org/wiki/3D_projection + * @param {Point3d} point3d A 3D point with parameters x, y, z + * @return {Point2d} point2d A 2D point with parameters x, y + */ + Graph3d.prototype._convert3Dto2D = function(point3d) { + var translation = this._convertPointToTranslation(point3d); + return this._convertTranslationToScreen(translation); + }; - args[2] = withoutSuffix; - args[3] = +posNegDuration > 0; - args[4] = locale; - return substituteTimeAgo.apply({}, args); - } + /** + * Convert a 3D location its translation seen from the camera + * http://en.wikipedia.org/wiki/3D_projection + * @param {Point3d} point3d A 3D point with parameters x, y, z + * @return {Point3d} translation A 3D point with parameters x, y, z This is + * the translation of the point, seen from the + * camera + */ + Graph3d.prototype._convertPointToTranslation = function(point3d) { + var ax = point3d.x * this.scale.x, + ay = point3d.y * this.scale.y, + az = point3d.z * this.scale.z, + cx = this.camera.getCameraLocation().x, + cy = this.camera.getCameraLocation().y, + cz = this.camera.getCameraLocation().z, - /************************************ - Week of Year - ************************************/ + // calculate angles + sinTx = Math.sin(this.camera.getCameraRotation().x), + cosTx = Math.cos(this.camera.getCameraRotation().x), + sinTy = Math.sin(this.camera.getCameraRotation().y), + cosTy = Math.cos(this.camera.getCameraRotation().y), + sinTz = Math.sin(this.camera.getCameraRotation().z), + cosTz = Math.cos(this.camera.getCameraRotation().z), + // calculate translation + dx = cosTy * (sinTz * (ay - cy) + cosTz * (ax - cx)) - sinTy * (az - cz), + dy = sinTx * (cosTy * (az - cz) + sinTy * (sinTz * (ay - cy) + cosTz * (ax - cx))) + cosTx * (cosTz * (ay - cy) - sinTz * (ax-cx)), + dz = cosTx * (cosTy * (az - cz) + sinTy * (sinTz * (ay - cy) + cosTz * (ax - cx))) - sinTx * (cosTz * (ay - cy) - sinTz * (ax-cx)); - // firstDayOfWeek 0 = sun, 6 = sat - // the day of the week that starts the week - // (usually sunday or monday) - // firstDayOfWeekOfYear 0 = sun, 6 = sat - // the first week is the week that contains the first - // of this day of the week - // (eg. ISO weeks use thursday (4)) - function weekOfYear(mom, firstDayOfWeek, firstDayOfWeekOfYear) { - var end = firstDayOfWeekOfYear - firstDayOfWeek, - daysToDayOfWeek = firstDayOfWeekOfYear - mom.day(), - adjustedMoment; + return new Point3d(dx, dy, dz); + }; + /** + * Convert a translation point to a point on the screen + * @param {Point3d} translation A 3D point with parameters x, y, z This is + * the translation of the point, seen from the + * camera + * @return {Point2d} point2d A 2D point with parameters x, y + */ + Graph3d.prototype._convertTranslationToScreen = function(translation) { + var ex = this.eye.x, + ey = this.eye.y, + ez = this.eye.z, + dx = translation.x, + dy = translation.y, + dz = translation.z; - if (daysToDayOfWeek > end) { - daysToDayOfWeek -= 7; - } + // calculate position on screen from translation + var bx; + var by; + if (this.showPerspective) { + bx = (dx - ex) * (ez / dz); + by = (dy - ey) * (ez / dz); + } + else { + bx = dx * -(ez / this.camera.getArmLength()); + by = dy * -(ez / this.camera.getArmLength()); + } - if (daysToDayOfWeek < end - 7) { - daysToDayOfWeek += 7; - } + // shift and scale the point to the center of the screen + // use the width of the graph to scale both horizontally and vertically. + return new Point2d( + this.xcenter + bx * this.frame.canvas.clientWidth, + this.ycenter - by * this.frame.canvas.clientWidth); + }; - adjustedMoment = moment(mom).add(daysToDayOfWeek, 'd'); - return { - week: Math.ceil(adjustedMoment.dayOfYear() / 7), - year: adjustedMoment.year() - }; - } + /** + * Set the background styling for the graph + * @param {string | {fill: string, stroke: string, strokeWidth: string}} backgroundColor + */ + Graph3d.prototype._setBackgroundColor = function(backgroundColor) { + var fill = 'white'; + var stroke = 'gray'; + var strokeWidth = 1; - //http://en.wikipedia.org/wiki/ISO_week_date#Calculating_a_date_given_the_year.2C_week_number_and_weekday - function dayOfYearFromWeeks(year, week, weekday, firstDayOfWeekOfYear, firstDayOfWeek) { - var d = makeUTCDate(year, 0, 1).getUTCDay(), daysToAdd, dayOfYear; + if (typeof(backgroundColor) === 'string') { + fill = backgroundColor; + stroke = 'none'; + strokeWidth = 0; + } + else if (typeof(backgroundColor) === 'object') { + if (backgroundColor.fill !== undefined) fill = backgroundColor.fill; + if (backgroundColor.stroke !== undefined) stroke = backgroundColor.stroke; + if (backgroundColor.strokeWidth !== undefined) strokeWidth = backgroundColor.strokeWidth; + } + else if (backgroundColor === undefined) { + // use use defaults + } + else { + throw 'Unsupported type of backgroundColor'; + } - d = d === 0 ? 7 : d; - weekday = weekday != null ? weekday : firstDayOfWeek; - daysToAdd = firstDayOfWeek - d + (d > firstDayOfWeekOfYear ? 7 : 0) - (d < firstDayOfWeek ? 7 : 0); - dayOfYear = 7 * (week - 1) + (weekday - firstDayOfWeek) + daysToAdd + 1; + this.frame.style.backgroundColor = fill; + this.frame.style.borderColor = stroke; + this.frame.style.borderWidth = strokeWidth + 'px'; + this.frame.style.borderStyle = 'solid'; + }; - return { - year: dayOfYear > 0 ? year : year - 1, - dayOfYear: dayOfYear > 0 ? dayOfYear : daysInYear(year - 1) + dayOfYear - }; - } - /************************************ - Top Level Functions - ************************************/ + /// enumerate the available styles + Graph3d.STYLE = { + BAR: 0, + BARCOLOR: 1, + BARSIZE: 2, + DOT : 3, + DOTLINE : 4, + DOTCOLOR: 5, + DOTSIZE: 6, + GRID : 7, + LINE: 8, + SURFACE : 9 + }; - function makeMoment(config) { - var input = config._i, - format = config._f, - res; + /** + * Retrieve the style index from given styleName + * @param {string} styleName Style name such as 'dot', 'grid', 'dot-line' + * @return {Number} styleNumber Enumeration value representing the style, or -1 + * when not found + */ + Graph3d.prototype._getStyleNumber = function(styleName) { + switch (styleName) { + case 'dot': return Graph3d.STYLE.DOT; + case 'dot-line': return Graph3d.STYLE.DOTLINE; + case 'dot-color': return Graph3d.STYLE.DOTCOLOR; + case 'dot-size': return Graph3d.STYLE.DOTSIZE; + case 'line': return Graph3d.STYLE.LINE; + case 'grid': return Graph3d.STYLE.GRID; + case 'surface': return Graph3d.STYLE.SURFACE; + case 'bar': return Graph3d.STYLE.BAR; + case 'bar-color': return Graph3d.STYLE.BARCOLOR; + case 'bar-size': return Graph3d.STYLE.BARSIZE; + } - config._locale = config._locale || moment.localeData(config._l); + return -1; + }; - if (input === null || (format === undefined && input === '')) { - return moment.invalid({nullInput: true}); - } + /** + * Determine the indexes of the data columns, based on the given style and data + * @param {DataSet} data + * @param {Number} style + */ + Graph3d.prototype._determineColumnIndexes = function(data, style) { + if (this.style === Graph3d.STYLE.DOT || + this.style === Graph3d.STYLE.DOTLINE || + this.style === Graph3d.STYLE.LINE || + this.style === Graph3d.STYLE.GRID || + this.style === Graph3d.STYLE.SURFACE || + this.style === Graph3d.STYLE.BAR) { + // 3 columns expected, and optionally a 4th with filter values + this.colX = 0; + this.colY = 1; + this.colZ = 2; + this.colValue = undefined; - if (typeof input === 'string') { - config._i = input = config._locale.preparse(input); - } + if (data.getNumberOfColumns() > 3) { + this.colFilter = 3; + } + } + else if (this.style === Graph3d.STYLE.DOTCOLOR || + this.style === Graph3d.STYLE.DOTSIZE || + this.style === Graph3d.STYLE.BARCOLOR || + this.style === Graph3d.STYLE.BARSIZE) { + // 4 columns expected, and optionally a 5th with filter values + this.colX = 0; + this.colY = 1; + this.colZ = 2; + this.colValue = 3; - if (moment.isMoment(input)) { - return new Moment(input, true); - } else if (format) { - if (isArray(format)) { - makeDateFromStringAndArray(config); - } else { - makeDateFromStringAndFormat(config); - } - } else { - makeDateFromInput(config); - } + if (data.getNumberOfColumns() > 4) { + this.colFilter = 4; + } + } + else { + throw 'Unknown style "' + this.style + '"'; + } + }; - res = new Moment(config); - if (res._nextDay) { - // Adding is smart enough around DST - res.add(1, 'd'); - res._nextDay = undefined; - } + Graph3d.prototype.getNumberOfRows = function(data) { + return data.length; + } - return res; + + Graph3d.prototype.getNumberOfColumns = function(data) { + var counter = 0; + for (var column in data[0]) { + if (data[0].hasOwnProperty(column)) { + counter++; } + } + return counter; + } - moment = function (input, format, locale, strict) { - var c; - if (typeof(locale) === 'boolean') { - strict = locale; - locale = undefined; - } - // object construction must be done this way. - // https://github.com/moment/moment/issues/1423 - c = {}; - c._isAMomentObject = true; - c._i = input; - c._f = format; - c._l = locale; - c._strict = strict; - c._isUTC = false; - c._pf = defaultParsingFlags(); + Graph3d.prototype.getDistinctValues = function(data, column) { + var distinctValues = []; + for (var i = 0; i < data.length; i++) { + if (distinctValues.indexOf(data[i][column]) == -1) { + distinctValues.push(data[i][column]); + } + } + return distinctValues; + } - return makeMoment(c); - }; - moment.suppressDeprecationWarnings = false; + Graph3d.prototype.getColumnRange = function(data,column) { + var minMax = {min:data[0][column],max:data[0][column]}; + for (var i = 0; i < data.length; i++) { + if (minMax.min > data[i][column]) { minMax.min = data[i][column]; } + if (minMax.max < data[i][column]) { minMax.max = data[i][column]; } + } + return minMax; + }; - moment.createFromInputFallback = deprecate( - 'moment construction falls back to js Date. This is ' + - 'discouraged and will be removed in upcoming major ' + - 'release. Please refer to ' + - 'https://github.com/moment/moment/issues/1407 for more info.', - function (config) { - config._d = new Date(config._i + (config._useUTC ? ' UTC' : '')); - } - ); + /** + * Initialize the data from the data table. Calculate minimum and maximum values + * and column index values + * @param {Array | DataSet | DataView} rawData The data containing the items for the Graph. + * @param {Number} style Style Number + */ + Graph3d.prototype._dataInitialize = function (rawData, style) { + var me = this; - // Pick a moment m from moments so that m[fn](other) is true for all - // other. This relies on the function fn to be transitive. - // - // moments should either be an array of moment objects or an array, whose - // first element is an array of moment objects. - function pickBy(fn, moments) { - var res, i; - if (moments.length === 1 && isArray(moments[0])) { - moments = moments[0]; - } - if (!moments.length) { - return moment(); - } - res = moments[0]; - for (i = 1; i < moments.length; ++i) { - if (moments[i][fn](res)) { - res = moments[i]; - } - } - return res; - } + // unsubscribe from the dataTable + if (this.dataSet) { + this.dataSet.off('*', this._onChange); + } - moment.min = function () { - var args = [].slice.call(arguments, 0); + if (rawData === undefined) + return; - return pickBy('isBefore', args); - }; + if (Array.isArray(rawData)) { + rawData = new DataSet(rawData); + } - moment.max = function () { - var args = [].slice.call(arguments, 0); + var data; + if (rawData instanceof DataSet || rawData instanceof DataView) { + data = rawData.get(); + } + else { + throw new Error('Array, DataSet, or DataView expected'); + } - return pickBy('isAfter', args); - }; + if (data.length == 0) + return; - // creating with utc - moment.utc = function (input, format, locale, strict) { - var c; + this.dataSet = rawData; + this.dataTable = data; - if (typeof(locale) === 'boolean') { - strict = locale; - locale = undefined; - } - // object construction must be done this way. - // https://github.com/moment/moment/issues/1423 - c = {}; - c._isAMomentObject = true; - c._useUTC = true; - c._isUTC = true; - c._l = locale; - c._i = input; - c._f = format; - c._strict = strict; - c._pf = defaultParsingFlags(); + // subscribe to changes in the dataset + this._onChange = function () { + me.setData(me.dataSet); + }; + this.dataSet.on('*', this._onChange); - return makeMoment(c).utc(); - }; + // _determineColumnIndexes + // getNumberOfRows (points) + // getNumberOfColumns (x,y,z,v,t,t1,t2...) + // getDistinctValues (unique values?) + // getColumnRange - // creating with unix timestamp (in seconds) - moment.unix = function (input) { - return moment(input * 1000); - }; + // determine the location of x,y,z,value,filter columns + this.colX = 'x'; + this.colY = 'y'; + this.colZ = 'z'; + this.colValue = 'style'; + this.colFilter = 'filter'; - // duration - moment.duration = function (input, key) { - var duration = input, - // matching against regexp is expensive, do it on demand - match = null, - sign, - ret, - parseIso, - diffRes; - - if (moment.isDuration(input)) { - duration = { - ms: input._milliseconds, - d: input._days, - M: input._months - }; - } else if (typeof input === 'number') { - duration = {}; - if (key) { - duration[key] = input; - } else { - duration.milliseconds = input; - } - } else if (!!(match = aspNetTimeSpanJsonRegex.exec(input))) { - sign = (match[1] === '-') ? -1 : 1; - duration = { - y: 0, - d: toInt(match[DATE]) * sign, - h: toInt(match[HOUR]) * sign, - m: toInt(match[MINUTE]) * sign, - s: toInt(match[SECOND]) * sign, - ms: toInt(match[MILLISECOND]) * sign - }; - } else if (!!(match = isoDurationRegex.exec(input))) { - sign = (match[1] === '-') ? -1 : 1; - parseIso = function (inp) { - // We'd normally use ~~inp for this, but unfortunately it also - // converts floats to ints. - // inp may be undefined, so careful calling replace on it. - var res = inp && parseFloat(inp.replace(',', '.')); - // apply sign while we're at it - return (isNaN(res) ? 0 : res) * sign; - }; - duration = { - y: parseIso(match[2]), - M: parseIso(match[3]), - d: parseIso(match[4]), - h: parseIso(match[5]), - m: parseIso(match[6]), - s: parseIso(match[7]), - w: parseIso(match[8]) - }; - } else if (duration == null) {// checks for null or undefined - duration = {}; - } else if (typeof duration === 'object' && - ('from' in duration || 'to' in duration)) { - diffRes = momentsDifference(moment(duration.from), moment(duration.to)); - duration = {}; - duration.ms = diffRes.milliseconds; - duration.M = diffRes.months; - } - ret = new Duration(duration); + // check if a filter column is provided + if (data[0].hasOwnProperty('filter')) { + if (this.dataFilter === undefined) { + this.dataFilter = new Filter(rawData, this.colFilter, this); + this.dataFilter.setOnLoadCallback(function() {me.redraw();}); + } + } - if (moment.isDuration(input) && hasOwnProp(input, '_locale')) { - ret._locale = input._locale; - } - return ret; - }; + var withBars = this.style == Graph3d.STYLE.BAR || + this.style == Graph3d.STYLE.BARCOLOR || + this.style == Graph3d.STYLE.BARSIZE; - // version number - moment.version = VERSION; + // determine barWidth from data + if (withBars) { + if (this.defaultXBarWidth !== undefined) { + this.xBarWidth = this.defaultXBarWidth; + } + else { + var dataX = this.getDistinctValues(data,this.colX); + this.xBarWidth = (dataX[1] - dataX[0]) || 1; + } - // default format - moment.defaultFormat = isoFormat; + if (this.defaultYBarWidth !== undefined) { + this.yBarWidth = this.defaultYBarWidth; + } + else { + var dataY = this.getDistinctValues(data,this.colY); + this.yBarWidth = (dataY[1] - dataY[0]) || 1; + } + } - // constant that refers to the ISO standard - moment.ISO_8601 = function () {}; + // calculate minimums and maximums + var xRange = this.getColumnRange(data,this.colX); + if (withBars) { + xRange.min -= this.xBarWidth / 2; + xRange.max += this.xBarWidth / 2; + } + this.xMin = (this.defaultXMin !== undefined) ? this.defaultXMin : xRange.min; + this.xMax = (this.defaultXMax !== undefined) ? this.defaultXMax : xRange.max; + if (this.xMax <= this.xMin) this.xMax = this.xMin + 1; + this.xStep = (this.defaultXStep !== undefined) ? this.defaultXStep : (this.xMax-this.xMin)/5; - // Plugins that add properties should also add the key here (null value), - // so we can properly clone ourselves. - moment.momentProperties = momentProperties; + var yRange = this.getColumnRange(data,this.colY); + if (withBars) { + yRange.min -= this.yBarWidth / 2; + yRange.max += this.yBarWidth / 2; + } + this.yMin = (this.defaultYMin !== undefined) ? this.defaultYMin : yRange.min; + this.yMax = (this.defaultYMax !== undefined) ? this.defaultYMax : yRange.max; + if (this.yMax <= this.yMin) this.yMax = this.yMin + 1; + this.yStep = (this.defaultYStep !== undefined) ? this.defaultYStep : (this.yMax-this.yMin)/5; - // This function will be called whenever a moment is mutated. - // It is intended to keep the offset in sync with the timezone. - moment.updateOffset = function () {}; + var zRange = this.getColumnRange(data,this.colZ); + this.zMin = (this.defaultZMin !== undefined) ? this.defaultZMin : zRange.min; + this.zMax = (this.defaultZMax !== undefined) ? this.defaultZMax : zRange.max; + if (this.zMax <= this.zMin) this.zMax = this.zMin + 1; + this.zStep = (this.defaultZStep !== undefined) ? this.defaultZStep : (this.zMax-this.zMin)/5; - // This function allows you to set a threshold for relative time strings - moment.relativeTimeThreshold = function (threshold, limit) { - if (relativeTimeThresholds[threshold] === undefined) { - return false; - } - if (limit === undefined) { - return relativeTimeThresholds[threshold]; - } - relativeTimeThresholds[threshold] = limit; - return true; - }; + if (this.colValue !== undefined) { + var valueRange = this.getColumnRange(data,this.colValue); + this.valueMin = (this.defaultValueMin !== undefined) ? this.defaultValueMin : valueRange.min; + this.valueMax = (this.defaultValueMax !== undefined) ? this.defaultValueMax : valueRange.max; + if (this.valueMax <= this.valueMin) this.valueMax = this.valueMin + 1; + } - moment.lang = deprecate( - 'moment.lang is deprecated. Use moment.locale instead.', - function (key, value) { - return moment.locale(key, value); - } - ); + // set the scale dependent on the ranges. + this._setScale(); + }; - // This function will load locale and then set the global locale. If - // no arguments are passed in, it will simply return the current global - // locale key. - moment.locale = function (key, values) { - var data; - if (key) { - if (typeof(values) !== 'undefined') { - data = moment.defineLocale(key, values); - } - else { - data = moment.localeData(key); - } - if (data) { - moment.duration._locale = moment._locale = data; - } - } - return moment._locale._abbr; - }; + /** + * Filter the data based on the current filter + * @param {Array} data + * @return {Array} dataPoints Array with point objects which can be drawn on screen + */ + Graph3d.prototype._getDataPoints = function (data) { + // TODO: store the created matrix dataPoints in the filters instead of reloading each time + var x, y, i, z, obj, point; - moment.defineLocale = function (name, values) { - if (values !== null) { - values.abbr = name; - if (!locales[name]) { - locales[name] = new Locale(); - } - locales[name].set(values); + var dataPoints = []; - // backwards compat for now: also set the locale - moment.locale(name); + if (this.style === Graph3d.STYLE.GRID || + this.style === Graph3d.STYLE.SURFACE) { + // copy all values from the google data table to a matrix + // the provided values are supposed to form a grid of (x,y) positions - return locales[name]; - } else { - // useful for testing - delete locales[name]; - return null; - } - }; + // create two lists with all present x and y values + var dataX = []; + var dataY = []; + for (i = 0; i < this.getNumberOfRows(data); i++) { + x = data[i][this.colX] || 0; + y = data[i][this.colY] || 0; - moment.langData = deprecate( - 'moment.langData is deprecated. Use moment.localeData instead.', - function (key) { - return moment.localeData(key); - } - ); + if (dataX.indexOf(x) === -1) { + dataX.push(x); + } + if (dataY.indexOf(y) === -1) { + dataY.push(y); + } + } - // returns locale data - moment.localeData = function (key) { - var locale; + var sortNumber = function (a, b) { + return a - b; + }; + dataX.sort(sortNumber); + dataY.sort(sortNumber); - if (key && key._locale && key._locale._abbr) { - key = key._locale._abbr; - } + // create a grid, a 2d matrix, with all values. + var dataMatrix = []; // temporary data matrix + for (i = 0; i < data.length; i++) { + x = data[i][this.colX] || 0; + y = data[i][this.colY] || 0; + z = data[i][this.colZ] || 0; - if (!key) { - return moment._locale; - } + var xIndex = dataX.indexOf(x); // TODO: implement Array().indexOf() for Internet Explorer + var yIndex = dataY.indexOf(y); - if (!isArray(key)) { - //short-circuit everything else - locale = loadLocale(key); - if (locale) { - return locale; - } - key = [key]; - } + if (dataMatrix[xIndex] === undefined) { + dataMatrix[xIndex] = []; + } - return chooseLocale(key); - }; + var point3d = new Point3d(); + point3d.x = x; + point3d.y = y; + point3d.z = z; - // compare moment object - moment.isMoment = function (obj) { - return obj instanceof Moment || - (obj != null && hasOwnProp(obj, '_isAMomentObject')); - }; + obj = {}; + obj.point = point3d; + obj.trans = undefined; + obj.screen = undefined; + obj.bottom = new Point3d(x, y, this.zMin); - // for typechecking Duration objects - moment.isDuration = function (obj) { - return obj instanceof Duration; - }; + dataMatrix[xIndex][yIndex] = obj; - for (i = lists.length - 1; i >= 0; --i) { - makeList(lists[i]); + dataPoints.push(obj); } - moment.normalizeUnits = function (units) { - return normalizeUnits(units); - }; - - moment.invalid = function (flags) { - var m = moment.utc(NaN); - if (flags != null) { - extend(m._pf, flags); - } - else { - m._pf.userInvalidated = true; + // fill in the pointers to the neighbors. + for (x = 0; x < dataMatrix.length; x++) { + for (y = 0; y < dataMatrix[x].length; y++) { + if (dataMatrix[x][y]) { + dataMatrix[x][y].pointRight = (x < dataMatrix.length-1) ? dataMatrix[x+1][y] : undefined; + dataMatrix[x][y].pointTop = (y < dataMatrix[x].length-1) ? dataMatrix[x][y+1] : undefined; + dataMatrix[x][y].pointCross = + (x < dataMatrix.length-1 && y < dataMatrix[x].length-1) ? + dataMatrix[x+1][y+1] : + undefined; } + } + } + } + else { // 'dot', 'dot-line', etc. + // copy all values from the google data table to a list with Point3d objects + for (i = 0; i < data.length; i++) { + point = new Point3d(); + point.x = data[i][this.colX] || 0; + point.y = data[i][this.colY] || 0; + point.z = data[i][this.colZ] || 0; - return m; - }; - - moment.parseZone = function () { - return moment.apply(null, arguments).parseZone(); - }; - - moment.parseTwoDigitYear = function (input) { - return toInt(input) + (toInt(input) > 68 ? 1900 : 2000); - }; + if (this.colValue !== undefined) { + point.value = data[i][this.colValue] || 0; + } - moment.isDate = isDate; + obj = {}; + obj.point = point; + obj.bottom = new Point3d(point.x, point.y, this.zMin); + obj.trans = undefined; + obj.screen = undefined; - /************************************ - Moment Prototype - ************************************/ + dataPoints.push(obj); + } + } + return dataPoints; + }; - extend(moment.fn = Moment.prototype, { + /** + * Create the main frame for the Graph3d. + * This function is executed once when a Graph3d object is created. The frame + * contains a canvas, and this canvas contains all objects like the axis and + * nodes. + */ + Graph3d.prototype.create = function () { + // remove all elements from the container element. + while (this.containerElement.hasChildNodes()) { + this.containerElement.removeChild(this.containerElement.firstChild); + } - clone : function () { - return moment(this); - }, + this.frame = document.createElement('div'); + this.frame.style.position = 'relative'; + this.frame.style.overflow = 'hidden'; - valueOf : function () { - return +this._d - ((this._offset || 0) * 60000); - }, + // create the graph canvas (HTML canvas element) + this.frame.canvas = document.createElement( 'canvas' ); + this.frame.canvas.style.position = 'relative'; + this.frame.appendChild(this.frame.canvas); + //if (!this.frame.canvas.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.frame.canvas.appendChild(noCanvas); + } - unix : function () { - return Math.floor(+this / 1000); - }, + this.frame.filter = document.createElement( 'div' ); + this.frame.filter.style.position = 'absolute'; + this.frame.filter.style.bottom = '0px'; + this.frame.filter.style.left = '0px'; + this.frame.filter.style.width = '100%'; + this.frame.appendChild(this.frame.filter); - toString : function () { - return this.clone().locale('en').format('ddd MMM DD YYYY HH:mm:ss [GMT]ZZ'); - }, + // add event listeners to handle moving and zooming the contents + var me = this; + var onmousedown = function (event) {me._onMouseDown(event);}; + var ontouchstart = function (event) {me._onTouchStart(event);}; + var onmousewheel = function (event) {me._onWheel(event);}; + var ontooltip = function (event) {me._onTooltip(event);}; + // TODO: these events are never cleaned up... can give a 'memory leakage' - toDate : function () { - return this._offset ? new Date(+this) : this._d; - }, + util.addEventListener(this.frame.canvas, 'keydown', onkeydown); + util.addEventListener(this.frame.canvas, 'mousedown', onmousedown); + util.addEventListener(this.frame.canvas, 'touchstart', ontouchstart); + util.addEventListener(this.frame.canvas, 'mousewheel', onmousewheel); + util.addEventListener(this.frame.canvas, 'mousemove', ontooltip); - toISOString : function () { - var m = moment(this).utc(); - if (0 < m.year() && m.year() <= 9999) { - if ('function' === typeof Date.prototype.toISOString) { - // native implementation is ~50x faster, use it when we can - return this.toDate().toISOString(); - } else { - return formatMoment(m, 'YYYY-MM-DD[T]HH:mm:ss.SSS[Z]'); - } - } else { - return formatMoment(m, 'YYYYYY-MM-DD[T]HH:mm:ss.SSS[Z]'); - } - }, + // add the new graph to the container element + this.containerElement.appendChild(this.frame); + }; - toArray : function () { - var m = this; - return [ - m.year(), - m.month(), - m.date(), - m.hours(), - m.minutes(), - m.seconds(), - m.milliseconds() - ]; - }, - isValid : function () { - return isValid(this); - }, + /** + * Set a new size for the graph + * @param {string} width Width in pixels or percentage (for example '800px' + * or '50%') + * @param {string} height Height in pixels or percentage (for example '400px' + * or '30%') + */ + Graph3d.prototype.setSize = function(width, height) { + this.frame.style.width = width; + this.frame.style.height = height; - isDSTShifted : function () { - if (this._a) { - return this.isValid() && compareArrays(this._a, (this._isUTC ? moment.utc(this._a) : moment(this._a)).toArray()) > 0; - } + this._resizeCanvas(); + }; - return false; - }, + /** + * Resize the canvas to the current size of the frame + */ + Graph3d.prototype._resizeCanvas = function() { + this.frame.canvas.style.width = '100%'; + this.frame.canvas.style.height = '100%'; - parsingFlags : function () { - return extend({}, this._pf); - }, + this.frame.canvas.width = this.frame.canvas.clientWidth; + this.frame.canvas.height = this.frame.canvas.clientHeight; - invalidAt: function () { - return this._pf.overflow; - }, + // adjust with for margin + this.frame.filter.style.width = (this.frame.canvas.clientWidth - 2 * 10) + 'px'; + }; - utc : function (keepLocalTime) { - return this.utcOffset(0, keepLocalTime); - }, + /** + * Start animation + */ + Graph3d.prototype.animationStart = function() { + if (!this.frame.filter || !this.frame.filter.slider) + throw 'No animation available'; - local : function (keepLocalTime) { - if (this._isUTC) { - this.utcOffset(0, keepLocalTime); - this._isUTC = false; + this.frame.filter.slider.play(); + }; - if (keepLocalTime) { - this.subtract(this._dateUtcOffset(), 'm'); - } - } - return this; - }, - format : function (inputString) { - var output = formatMoment(this, inputString || moment.defaultFormat); - return this.localeData().postformat(output); - }, + /** + * Stop animation + */ + Graph3d.prototype.animationStop = function() { + if (!this.frame.filter || !this.frame.filter.slider) return; - add : createAdder(1, 'add'), + this.frame.filter.slider.stop(); + }; - subtract : createAdder(-1, 'subtract'), - diff : function (input, units, asFloat) { - var that = makeAs(input, this), - zoneDiff = (that.utcOffset() - this.utcOffset()) * 6e4, - anchor, diff, output, daysAdjust; + /** + * Resize the center position based on the current values in this.defaultXCenter + * and this.defaultYCenter (which are strings with a percentage or a value + * in pixels). The center positions are the variables this.xCenter + * and this.yCenter + */ + Graph3d.prototype._resizeCenter = function() { + // calculate the horizontal center position + if (this.defaultXCenter.charAt(this.defaultXCenter.length-1) === '%') { + this.xcenter = + parseFloat(this.defaultXCenter) / 100 * + this.frame.canvas.clientWidth; + } + else { + this.xcenter = parseFloat(this.defaultXCenter); // supposed to be in px + } - units = normalizeUnits(units); + // calculate the vertical center position + if (this.defaultYCenter.charAt(this.defaultYCenter.length-1) === '%') { + this.ycenter = + parseFloat(this.defaultYCenter) / 100 * + (this.frame.canvas.clientHeight - this.frame.filter.clientHeight); + } + else { + this.ycenter = parseFloat(this.defaultYCenter); // supposed to be in px + } + }; - if (units === 'year' || units === 'month' || units === 'quarter') { - output = monthDiff(this, that); - if (units === 'quarter') { - output = output / 3; - } else if (units === 'year') { - output = output / 12; - } - } else { - diff = this - that; - output = units === 'second' ? diff / 1e3 : // 1000 - units === 'minute' ? diff / 6e4 : // 1000 * 60 - units === 'hour' ? diff / 36e5 : // 1000 * 60 * 60 - units === 'day' ? (diff - zoneDiff) / 864e5 : // 1000 * 60 * 60 * 24, negate dst - units === 'week' ? (diff - zoneDiff) / 6048e5 : // 1000 * 60 * 60 * 24 * 7, negate dst - diff; - } - return asFloat ? output : absRound(output); - }, + /** + * Set the rotation and distance of the camera + * @param {Object} pos An object with the camera position. The object + * contains three parameters: + * - horizontal {Number} + * The horizontal rotation, between 0 and 2*PI. + * Optional, can be left undefined. + * - vertical {Number} + * The vertical rotation, between 0 and 0.5*PI + * if vertical=0.5*PI, the graph is shown from the + * top. Optional, can be left undefined. + * - distance {Number} + * The (normalized) distance of the camera to the + * center of the graph, a value between 0.71 and 5.0. + * Optional, can be left undefined. + */ + Graph3d.prototype.setCameraPosition = function(pos) { + if (pos === undefined) { + return; + } - from : function (time, withoutSuffix) { - return moment.duration({to: this, from: time}).locale(this.locale()).humanize(!withoutSuffix); - }, + if (pos.horizontal !== undefined && pos.vertical !== undefined) { + this.camera.setArmRotation(pos.horizontal, pos.vertical); + } - fromNow : function (withoutSuffix) { - return this.from(moment(), withoutSuffix); - }, + if (pos.distance !== undefined) { + this.camera.setArmLength(pos.distance); + } - calendar : function (time) { - // We want to compare the start of today, vs this. - // Getting start-of-today depends on whether we're locat/utc/offset - // or not. - var now = time || moment(), - sod = makeAs(now, this).startOf('day'), - diff = this.diff(sod, 'days', true), - format = diff < -6 ? 'sameElse' : - diff < -1 ? 'lastWeek' : - diff < 0 ? 'lastDay' : - diff < 1 ? 'sameDay' : - diff < 2 ? 'nextDay' : - diff < 7 ? 'nextWeek' : 'sameElse'; - return this.format(this.localeData().calendar(format, this, moment(now))); - }, + this.redraw(); + }; - isLeapYear : function () { - return isLeapYear(this.year()); - }, - isDST : function () { - return (this.utcOffset() > this.clone().month(0).utcOffset() || - this.utcOffset() > this.clone().month(5).utcOffset()); - }, + /** + * Retrieve the current camera rotation + * @return {object} An object with parameters horizontal, vertical, and + * distance + */ + Graph3d.prototype.getCameraPosition = function() { + var pos = this.camera.getArmRotation(); + pos.distance = this.camera.getArmLength(); + return pos; + }; - day : function (input) { - var day = this._isUTC ? this._d.getUTCDay() : this._d.getDay(); - if (input != null) { - input = parseWeekday(input, this.localeData()); - return this.add(input - day, 'd'); - } else { - return day; - } - }, + /** + * Load data into the 3D Graph + */ + Graph3d.prototype._readData = function(data) { + // read the data + this._dataInitialize(data, this.style); - month : makeAccessor('Month', true), - startOf : function (units) { - units = normalizeUnits(units); - // the following switch intentionally omits break keywords - // to utilize falling through the cases. - switch (units) { - case 'year': - this.month(0); - /* falls through */ - case 'quarter': - case 'month': - this.date(1); - /* falls through */ - case 'week': - case 'isoWeek': - case 'day': - this.hours(0); - /* falls through */ - case 'hour': - this.minutes(0); - /* falls through */ - case 'minute': - this.seconds(0); - /* falls through */ - case 'second': - this.milliseconds(0); - /* falls through */ - } + if (this.dataFilter) { + // apply filtering + this.dataPoints = this.dataFilter._getDataPoints(); + } + else { + // no filtering. load all data + this.dataPoints = this._getDataPoints(this.dataTable); + } - // weeks are a special case - if (units === 'week') { - this.weekday(0); - } else if (units === 'isoWeek') { - this.isoWeekday(1); - } + // draw the filter + this._redrawFilter(); + }; - // quarters are also special - if (units === 'quarter') { - this.month(Math.floor(this.month() / 3) * 3); - } + /** + * Replace the dataset of the Graph3d + * @param {Array | DataSet | DataView} data + */ + Graph3d.prototype.setData = function (data) { + this._readData(data); + this.redraw(); - return this; - }, + // start animation when option is true + if (this.animationAutoStart && this.dataFilter) { + this.animationStart(); + } + }; - endOf: function (units) { - units = normalizeUnits(units); - if (units === undefined || units === 'millisecond') { - return this; - } - return this.startOf(units).add(1, (units === 'isoWeek' ? 'week' : units)).subtract(1, 'ms'); - }, + /** + * Update the options. Options will be merged with current options + * @param {Object} options + */ + Graph3d.prototype.setOptions = function (options) { + var cameraPosition = undefined; - isAfter: function (input, units) { - var inputMs; - units = normalizeUnits(typeof units !== 'undefined' ? units : 'millisecond'); - if (units === 'millisecond') { - input = moment.isMoment(input) ? input : moment(input); - return +this > +input; - } else { - inputMs = moment.isMoment(input) ? +input : +moment(input); - return inputMs < +this.clone().startOf(units); - } - }, + this.animationStop(); - isBefore: function (input, units) { - var inputMs; - units = normalizeUnits(typeof units !== 'undefined' ? units : 'millisecond'); - if (units === 'millisecond') { - input = moment.isMoment(input) ? input : moment(input); - return +this < +input; - } else { - inputMs = moment.isMoment(input) ? +input : +moment(input); - return +this.clone().endOf(units) < inputMs; - } - }, + if (options !== undefined) { + // retrieve parameter values + if (options.width !== undefined) this.width = options.width; + if (options.height !== undefined) this.height = options.height; - isBetween: function (from, to, units) { - return this.isAfter(from, units) && this.isBefore(to, units); - }, + if (options.xCenter !== undefined) this.defaultXCenter = options.xCenter; + if (options.yCenter !== undefined) this.defaultYCenter = options.yCenter; - isSame: function (input, units) { - var inputMs; - units = normalizeUnits(units || 'millisecond'); - if (units === 'millisecond') { - input = moment.isMoment(input) ? input : moment(input); - return +this === +input; - } else { - inputMs = +moment(input); - return +(this.clone().startOf(units)) <= inputMs && inputMs <= +(this.clone().endOf(units)); - } - }, + if (options.filterLabel !== undefined) this.filterLabel = options.filterLabel; + if (options.legendLabel !== undefined) this.legendLabel = options.legendLabel; + if (options.xLabel !== undefined) this.xLabel = options.xLabel; + if (options.yLabel !== undefined) this.yLabel = options.yLabel; + if (options.zLabel !== undefined) this.zLabel = options.zLabel; - min: deprecate( - 'moment().min is deprecated, use moment.min instead. https://github.com/moment/moment/issues/1548', - function (other) { - other = moment.apply(null, arguments); - return other < this ? this : other; - } - ), + if (options.xValueLabel !== undefined) this.xValueLabel = options.xValueLabel; + if (options.yValueLabel !== undefined) this.yValueLabel = options.yValueLabel; + if (options.zValueLabel !== undefined) this.zValueLabel = options.zValueLabel; - max: deprecate( - 'moment().max is deprecated, use moment.max instead. https://github.com/moment/moment/issues/1548', - function (other) { - other = moment.apply(null, arguments); - return other > this ? this : other; - } - ), + if (options.style !== undefined) { + var styleNumber = this._getStyleNumber(options.style); + if (styleNumber !== -1) { + this.style = styleNumber; + } + } + if (options.showGrid !== undefined) this.showGrid = options.showGrid; + if (options.showPerspective !== undefined) this.showPerspective = options.showPerspective; + if (options.showShadow !== undefined) this.showShadow = options.showShadow; + if (options.tooltip !== undefined) this.showTooltip = options.tooltip; + if (options.showAnimationControls !== undefined) this.showAnimationControls = options.showAnimationControls; + if (options.keepAspectRatio !== undefined) this.keepAspectRatio = options.keepAspectRatio; + if (options.verticalRatio !== undefined) this.verticalRatio = options.verticalRatio; - zone : deprecate( - 'moment().zone is deprecated, use moment().utcOffset instead. ' + - 'https://github.com/moment/moment/issues/1779', - function (input, keepLocalTime) { - if (input != null) { - if (typeof input !== 'string') { - input = -input; - } + if (options.animationInterval !== undefined) this.animationInterval = options.animationInterval; + if (options.animationPreload !== undefined) this.animationPreload = options.animationPreload; + if (options.animationAutoStart !== undefined)this.animationAutoStart = options.animationAutoStart; - this.utcOffset(input, keepLocalTime); + if (options.xBarWidth !== undefined) this.defaultXBarWidth = options.xBarWidth; + if (options.yBarWidth !== undefined) this.defaultYBarWidth = options.yBarWidth; - return this; - } else { - return -this.utcOffset(); - } - } - ), + if (options.xMin !== undefined) this.defaultXMin = options.xMin; + if (options.xStep !== undefined) this.defaultXStep = options.xStep; + if (options.xMax !== undefined) this.defaultXMax = options.xMax; + if (options.yMin !== undefined) this.defaultYMin = options.yMin; + if (options.yStep !== undefined) this.defaultYStep = options.yStep; + if (options.yMax !== undefined) this.defaultYMax = options.yMax; + if (options.zMin !== undefined) this.defaultZMin = options.zMin; + if (options.zStep !== undefined) this.defaultZStep = options.zStep; + if (options.zMax !== undefined) this.defaultZMax = options.zMax; + if (options.valueMin !== undefined) this.defaultValueMin = options.valueMin; + if (options.valueMax !== undefined) this.defaultValueMax = options.valueMax; - // keepLocalTime = true means only change the timezone, without - // affecting the local hour. So 5:31:26 +0300 --[utcOffset(2, true)]--> - // 5:31:26 +0200 It is possible that 5:31:26 doesn't exist with offset - // +0200, so we adjust the time as needed, to be valid. - // - // Keeping the time actually adds/subtracts (one hour) - // from the actual represented time. That is why we call updateOffset - // a second time. In case it wants us to change the offset again - // _changeInProgress == true case, then we have to adjust, because - // there is no such time in the given timezone. - utcOffset : function (input, keepLocalTime) { - var offset = this._offset || 0, - localAdjust; - if (input != null) { - if (typeof input === 'string') { - input = utcOffsetFromString(input); - } - if (Math.abs(input) < 16) { - input = input * 60; - } - if (!this._isUTC && keepLocalTime) { - localAdjust = this._dateUtcOffset(); - } - this._offset = input; - this._isUTC = true; - if (localAdjust != null) { - this.add(localAdjust, 'm'); - } - if (offset !== input) { - if (!keepLocalTime || this._changeInProgress) { - addOrSubtractDurationFromMoment(this, - moment.duration(input - offset, 'm'), 1, false); - } else if (!this._changeInProgress) { - this._changeInProgress = true; - moment.updateOffset(this, true); - this._changeInProgress = null; - } - } + if (options.cameraPosition !== undefined) cameraPosition = options.cameraPosition; - return this; - } else { - return this._isUTC ? offset : this._dateUtcOffset(); - } - }, + if (cameraPosition !== undefined) { + this.camera.setArmRotation(cameraPosition.horizontal, cameraPosition.vertical); + this.camera.setArmLength(cameraPosition.distance); + } + else { + this.camera.setArmRotation(1.0, 0.5); + this.camera.setArmLength(1.7); + } + } - isLocal : function () { - return !this._isUTC; - }, + this._setBackgroundColor(options && options.backgroundColor); - isUtcOffset : function () { - return this._isUTC; - }, + this.setSize(this.width, this.height); - isUtc : function () { - return this._isUTC && this._offset === 0; - }, + // re-load the data + if (this.dataTable) { + this.setData(this.dataTable); + } - zoneAbbr : function () { - return this._isUTC ? 'UTC' : ''; - }, + // start animation when option is true + if (this.animationAutoStart && this.dataFilter) { + this.animationStart(); + } + }; - zoneName : function () { - return this._isUTC ? 'Coordinated Universal Time' : ''; - }, + /** + * Redraw the Graph. + */ + Graph3d.prototype.redraw = function() { + if (this.dataPoints === undefined) { + throw 'Error: graph data not initialized'; + } - parseZone : function () { - if (this._tzm) { - this.utcOffset(this._tzm); - } else if (typeof this._i === 'string') { - this.utcOffset(utcOffsetFromString(this._i)); - } - return this; - }, + this._resizeCanvas(); + this._resizeCenter(); + this._redrawSlider(); + this._redrawClear(); + this._redrawAxis(); - hasAlignedHourOffset : function (input) { - if (!input) { - input = 0; - } - else { - input = moment(input).utcOffset(); - } + if (this.style === Graph3d.STYLE.GRID || + this.style === Graph3d.STYLE.SURFACE) { + this._redrawDataGrid(); + } + else if (this.style === Graph3d.STYLE.LINE) { + this._redrawDataLine(); + } + else if (this.style === Graph3d.STYLE.BAR || + this.style === Graph3d.STYLE.BARCOLOR || + this.style === Graph3d.STYLE.BARSIZE) { + this._redrawDataBar(); + } + else { + // style is DOT, DOTLINE, DOTCOLOR, DOTSIZE + this._redrawDataDot(); + } - return (this.utcOffset() - input) % 60 === 0; - }, + this._redrawInfo(); + this._redrawLegend(); + }; - daysInMonth : function () { - return daysInMonth(this.year(), this.month()); - }, + /** + * Clear the canvas before redrawing + */ + Graph3d.prototype._redrawClear = function() { + var canvas = this.frame.canvas; + var ctx = canvas.getContext('2d'); - dayOfYear : function (input) { - var dayOfYear = round((moment(this).startOf('day') - moment(this).startOf('year')) / 864e5) + 1; - return input == null ? dayOfYear : this.add((input - dayOfYear), 'd'); - }, + ctx.clearRect(0, 0, canvas.width, canvas.height); + }; - quarter : function (input) { - return input == null ? Math.ceil((this.month() + 1) / 3) : this.month((input - 1) * 3 + this.month() % 3); - }, - weekYear : function (input) { - var year = weekOfYear(this, this.localeData()._week.dow, this.localeData()._week.doy).year; - return input == null ? year : this.add((input - year), 'y'); - }, + /** + * Redraw the legend showing the colors + */ + Graph3d.prototype._redrawLegend = function() { + var y; - isoWeekYear : function (input) { - var year = weekOfYear(this, 1, 4).year; - return input == null ? year : this.add((input - year), 'y'); - }, + if (this.style === Graph3d.STYLE.DOTCOLOR || + this.style === Graph3d.STYLE.DOTSIZE) { - week : function (input) { - var week = this.localeData().week(this); - return input == null ? week : this.add((input - week) * 7, 'd'); - }, + var dotSize = this.frame.clientWidth * 0.02; - isoWeek : function (input) { - var week = weekOfYear(this, 1, 4).week; - return input == null ? week : this.add((input - week) * 7, 'd'); - }, + var widthMin, widthMax; + if (this.style === Graph3d.STYLE.DOTSIZE) { + widthMin = dotSize / 2; // px + widthMax = dotSize / 2 + dotSize * 2; // Todo: put this in one function + } + else { + widthMin = 20; // px + widthMax = 20; // px + } - weekday : function (input) { - var weekday = (this.day() + 7 - this.localeData()._week.dow) % 7; - return input == null ? weekday : this.add(input - weekday, 'd'); - }, + var height = Math.max(this.frame.clientHeight * 0.25, 100); + var top = this.margin; + var right = this.frame.clientWidth - this.margin; + var left = right - widthMax; + var bottom = top + height; + } - isoWeekday : function (input) { - // behaves the same as moment#day except - // as a getter, returns 7 instead of 0 (1-7 range instead of 0-6) - // as a setter, sunday should belong to the previous week. - return input == null ? this.day() || 7 : this.day(this.day() % 7 ? input : input - 7); - }, + var canvas = this.frame.canvas; + var ctx = canvas.getContext('2d'); + ctx.lineWidth = 1; + ctx.font = '14px arial'; // TODO: put in options - isoWeeksInYear : function () { - return weeksInYear(this.year(), 1, 4); - }, + if (this.style === Graph3d.STYLE.DOTCOLOR) { + // draw the color bar + var ymin = 0; + var ymax = height; // Todo: make height customizable + for (y = ymin; y < ymax; y++) { + var f = (y - ymin) / (ymax - ymin); - weeksInYear : function () { - var weekInfo = this.localeData()._week; - return weeksInYear(this.year(), weekInfo.dow, weekInfo.doy); - }, + //var width = (dotSize / 2 + (1-f) * dotSize * 2); // Todo: put this in one function + var hue = f * 240; + var color = this._hsv2rgb(hue, 1, 1); - get : function (units) { - units = normalizeUnits(units); - return this[units](); - }, - - set : function (units, value) { - var unit; - if (typeof units === 'object') { - for (unit in units) { - this.set(unit, units[unit]); - } - } - else { - units = normalizeUnits(units); - if (typeof this[units] === 'function') { - this[units](value); - } - } - return this; - }, - - // If passed a locale key, it will set the locale for this - // instance. Otherwise, it will return the locale configuration - // variables for this instance. - locale : function (key) { - var newLocaleData; - - if (key === undefined) { - return this._locale._abbr; - } else { - newLocaleData = moment.localeData(key); - if (newLocaleData != null) { - this._locale = newLocaleData; - } - return this; - } - }, - - lang : deprecate( - 'moment().lang() is deprecated. Instead, use moment().localeData() to get the language configuration. Use moment().locale() to change languages.', - function (key) { - if (key === undefined) { - return this.localeData(); - } else { - return this.locale(key); - } - } - ), - - localeData : function () { - return this._locale; - }, - - _dateUtcOffset : function () { - // On Firefox.24 Date#getTimezoneOffset returns a floating point. - // https://github.com/moment/moment/pull/1871 - return -Math.round(this._d.getTimezoneOffset() / 15) * 15; - } - - }); + ctx.strokeStyle = color; + ctx.beginPath(); + ctx.moveTo(left, top + y); + ctx.lineTo(right, top + y); + ctx.stroke(); + } - function rawMonthSetter(mom, value) { - var dayOfMonth; + ctx.strokeStyle = this.colorAxis; + ctx.strokeRect(left, top, widthMax, height); + } - // TODO: Move this out of here! - if (typeof value === 'string') { - value = mom.localeData().monthsParse(value); - // TODO: Another silent failure? - if (typeof value !== 'number') { - return mom; - } - } + if (this.style === Graph3d.STYLE.DOTSIZE) { + // draw border around color bar + ctx.strokeStyle = this.colorAxis; + ctx.fillStyle = this.colorDot; + ctx.beginPath(); + ctx.moveTo(left, top); + ctx.lineTo(right, top); + ctx.lineTo(right - widthMax + widthMin, bottom); + ctx.lineTo(left, bottom); + ctx.closePath(); + ctx.fill(); + ctx.stroke(); + } - dayOfMonth = Math.min(mom.date(), - daysInMonth(mom.year(), value)); - mom._d['set' + (mom._isUTC ? 'UTC' : '') + 'Month'](value, dayOfMonth); - return mom; + if (this.style === Graph3d.STYLE.DOTCOLOR || + this.style === Graph3d.STYLE.DOTSIZE) { + // print values along the color bar + var gridLineLen = 5; // px + var step = new StepNumber(this.valueMin, this.valueMax, (this.valueMax-this.valueMin)/5, true); + step.start(); + if (step.getCurrent() < this.valueMin) { + step.next(); } + while (!step.end()) { + y = bottom - (step.getCurrent() - this.valueMin) / (this.valueMax - this.valueMin) * height; - function rawGetter(mom, unit) { - return mom._d['get' + (mom._isUTC ? 'UTC' : '') + unit](); - } + ctx.beginPath(); + ctx.moveTo(left - gridLineLen, y); + ctx.lineTo(left, y); + ctx.stroke(); - function rawSetter(mom, unit, value) { - if (unit === 'Month') { - return rawMonthSetter(mom, value); - } else { - return mom._d['set' + (mom._isUTC ? 'UTC' : '') + unit](value); - } - } + ctx.textAlign = 'right'; + ctx.textBaseline = 'middle'; + ctx.fillStyle = this.colorAxis; + ctx.fillText(step.getCurrent(), left - 2 * gridLineLen, y); - function makeAccessor(unit, keepTime) { - return function (value) { - if (value != null) { - rawSetter(this, unit, value); - moment.updateOffset(this, keepTime); - return this; - } else { - return rawGetter(this, unit); - } - }; + step.next(); } - moment.fn.millisecond = moment.fn.milliseconds = makeAccessor('Milliseconds', false); - moment.fn.second = moment.fn.seconds = makeAccessor('Seconds', false); - moment.fn.minute = moment.fn.minutes = makeAccessor('Minutes', false); - // Setting the hour should keep the time, because the user explicitly - // specified which hour he wants. So trying to maintain the same hour (in - // a new timezone) makes sense. Adding/subtracting hours does not follow - // this rule. - moment.fn.hour = moment.fn.hours = makeAccessor('Hours', true); - // moment.fn.month is defined separately - moment.fn.date = makeAccessor('Date', true); - moment.fn.dates = deprecate('dates accessor is deprecated. Use date instead.', makeAccessor('Date', true)); - moment.fn.year = makeAccessor('FullYear', true); - moment.fn.years = deprecate('years accessor is deprecated. Use year instead.', makeAccessor('FullYear', true)); + ctx.textAlign = 'right'; + ctx.textBaseline = 'top'; + var label = this.legendLabel; + ctx.fillText(label, right, bottom + this.margin); + } + }; - // add plural methods - moment.fn.days = moment.fn.day; - moment.fn.months = moment.fn.month; - moment.fn.weeks = moment.fn.week; - moment.fn.isoWeeks = moment.fn.isoWeek; - moment.fn.quarters = moment.fn.quarter; + /** + * Redraw the filter + */ + Graph3d.prototype._redrawFilter = function() { + this.frame.filter.innerHTML = ''; - // add aliased format methods - moment.fn.toJSON = moment.fn.toISOString; + if (this.dataFilter) { + var options = { + 'visible': this.showAnimationControls + }; + var slider = new Slider(this.frame.filter, options); + this.frame.filter.slider = slider; - // alias isUtc for dev-friendliness - moment.fn.isUTC = moment.fn.isUtc; + // TODO: css here is not nice here... + this.frame.filter.style.padding = '10px'; + //this.frame.filter.style.backgroundColor = '#EFEFEF'; - /************************************ - Duration Prototype - ************************************/ + slider.setValues(this.dataFilter.values); + slider.setPlayInterval(this.animationInterval); + // create an event handler + var me = this; + var onchange = function () { + var index = slider.getIndex(); - function daysToYears (days) { - // 400 years have 146097 days (taking into account leap year rules) - return days * 400 / 146097; - } + me.dataFilter.selectValue(index); + me.dataPoints = me.dataFilter._getDataPoints(); - function yearsToDays (years) { - // years * 365 + absRound(years / 4) - - // absRound(years / 100) + absRound(years / 400); - return years * 146097 / 400; - } + me.redraw(); + }; + slider.setOnChangeCallback(onchange); + } + else { + this.frame.filter.slider = undefined; + } + }; - extend(moment.duration.fn = Duration.prototype, { + /** + * Redraw the slider + */ + Graph3d.prototype._redrawSlider = function() { + if ( this.frame.filter.slider !== undefined) { + this.frame.filter.slider.redraw(); + } + }; - _bubble : function () { - var milliseconds = this._milliseconds, - days = this._days, - months = this._months, - data = this._data, - seconds, minutes, hours, years = 0; - // The following code bubbles up values, see the tests for - // examples of what that means. - data.milliseconds = milliseconds % 1000; + /** + * Redraw common information + */ + Graph3d.prototype._redrawInfo = function() { + if (this.dataFilter) { + var canvas = this.frame.canvas; + var ctx = canvas.getContext('2d'); - seconds = absRound(milliseconds / 1000); - data.seconds = seconds % 60; + ctx.font = '14px arial'; // TODO: put in options + ctx.lineStyle = 'gray'; + ctx.fillStyle = 'gray'; + ctx.textAlign = 'left'; + ctx.textBaseline = 'top'; - minutes = absRound(seconds / 60); - data.minutes = minutes % 60; + var x = this.margin; + var y = this.margin; + ctx.fillText(this.dataFilter.getLabel() + ': ' + this.dataFilter.getSelectedValue(), x, y); + } + }; - hours = absRound(minutes / 60); - data.hours = hours % 24; - days += absRound(hours / 24); + /** + * Redraw the axis + */ + Graph3d.prototype._redrawAxis = function() { + var canvas = this.frame.canvas, + ctx = canvas.getContext('2d'), + from, to, step, prettyStep, + text, xText, yText, zText, + offset, xOffset, yOffset, + xMin2d, xMax2d; - // Accurately convert days to years, assume start from year 0. - years = absRound(daysToYears(days)); - days -= absRound(yearsToDays(years)); + // TODO: get the actual rendered style of the containerElement + //ctx.font = this.containerElement.style.font; + ctx.font = 24 / this.camera.getArmLength() + 'px arial'; - // 30 days to a month - // TODO (iskren): Use anchor date (like 1st Jan) to compute this. - months += absRound(days / 30); - days %= 30; + // calculate the length for the short grid lines + var gridLenX = 0.025 / this.scale.x; + var gridLenY = 0.025 / this.scale.y; + var textMargin = 5 / this.camera.getArmLength(); // px + var armAngle = this.camera.getArmRotation().horizontal; - // 12 months -> 1 year - years += absRound(months / 12); - months %= 12; + // draw x-grid lines + ctx.lineWidth = 1; + prettyStep = (this.defaultXStep === undefined); + step = new StepNumber(this.xMin, this.xMax, this.xStep, prettyStep); + step.start(); + if (step.getCurrent() < this.xMin) { + step.next(); + } + while (!step.end()) { + var x = step.getCurrent(); - data.days = days; - data.months = months; - data.years = years; - }, + if (this.showGrid) { + from = this._convert3Dto2D(new Point3d(x, this.yMin, this.zMin)); + to = this._convert3Dto2D(new Point3d(x, this.yMax, this.zMin)); + ctx.strokeStyle = this.colorGrid; + ctx.beginPath(); + ctx.moveTo(from.x, from.y); + ctx.lineTo(to.x, to.y); + ctx.stroke(); + } + else { + from = this._convert3Dto2D(new Point3d(x, this.yMin, this.zMin)); + to = this._convert3Dto2D(new Point3d(x, this.yMin+gridLenX, this.zMin)); + ctx.strokeStyle = this.colorAxis; + ctx.beginPath(); + ctx.moveTo(from.x, from.y); + ctx.lineTo(to.x, to.y); + ctx.stroke(); - abs : function () { - this._milliseconds = Math.abs(this._milliseconds); - this._days = Math.abs(this._days); - this._months = Math.abs(this._months); + from = this._convert3Dto2D(new Point3d(x, this.yMax, this.zMin)); + to = this._convert3Dto2D(new Point3d(x, this.yMax-gridLenX, this.zMin)); + ctx.strokeStyle = this.colorAxis; + ctx.beginPath(); + ctx.moveTo(from.x, from.y); + ctx.lineTo(to.x, to.y); + ctx.stroke(); + } - this._data.milliseconds = Math.abs(this._data.milliseconds); - this._data.seconds = Math.abs(this._data.seconds); - this._data.minutes = Math.abs(this._data.minutes); - this._data.hours = Math.abs(this._data.hours); - this._data.months = Math.abs(this._data.months); - this._data.years = Math.abs(this._data.years); + yText = (Math.cos(armAngle) > 0) ? this.yMin : this.yMax; + text = this._convert3Dto2D(new Point3d(x, yText, this.zMin)); + if (Math.cos(armAngle * 2) > 0) { + ctx.textAlign = 'center'; + ctx.textBaseline = 'top'; + text.y += textMargin; + } + else if (Math.sin(armAngle * 2) < 0){ + ctx.textAlign = 'right'; + ctx.textBaseline = 'middle'; + } + else { + ctx.textAlign = 'left'; + ctx.textBaseline = 'middle'; + } + ctx.fillStyle = this.colorAxis; + ctx.fillText(' ' + this.xValueLabel(step.getCurrent()) + ' ', text.x, text.y); - return this; - }, + step.next(); + } - weeks : function () { - return absRound(this.days() / 7); - }, + // draw y-grid lines + ctx.lineWidth = 1; + prettyStep = (this.defaultYStep === undefined); + step = new StepNumber(this.yMin, this.yMax, this.yStep, prettyStep); + step.start(); + if (step.getCurrent() < this.yMin) { + step.next(); + } + while (!step.end()) { + if (this.showGrid) { + from = this._convert3Dto2D(new Point3d(this.xMin, step.getCurrent(), this.zMin)); + to = this._convert3Dto2D(new Point3d(this.xMax, step.getCurrent(), this.zMin)); + ctx.strokeStyle = this.colorGrid; + ctx.beginPath(); + ctx.moveTo(from.x, from.y); + ctx.lineTo(to.x, to.y); + ctx.stroke(); + } + else { + from = this._convert3Dto2D(new Point3d(this.xMin, step.getCurrent(), this.zMin)); + to = this._convert3Dto2D(new Point3d(this.xMin+gridLenY, step.getCurrent(), this.zMin)); + ctx.strokeStyle = this.colorAxis; + ctx.beginPath(); + ctx.moveTo(from.x, from.y); + ctx.lineTo(to.x, to.y); + ctx.stroke(); - valueOf : function () { - return this._milliseconds + - this._days * 864e5 + - (this._months % 12) * 2592e6 + - toInt(this._months / 12) * 31536e6; - }, + from = this._convert3Dto2D(new Point3d(this.xMax, step.getCurrent(), this.zMin)); + to = this._convert3Dto2D(new Point3d(this.xMax-gridLenY, step.getCurrent(), this.zMin)); + ctx.strokeStyle = this.colorAxis; + ctx.beginPath(); + ctx.moveTo(from.x, from.y); + ctx.lineTo(to.x, to.y); + ctx.stroke(); + } - humanize : function (withSuffix) { - var output = relativeTime(this, !withSuffix, this.localeData()); + xText = (Math.sin(armAngle ) > 0) ? this.xMin : this.xMax; + text = this._convert3Dto2D(new Point3d(xText, step.getCurrent(), this.zMin)); + if (Math.cos(armAngle * 2) < 0) { + ctx.textAlign = 'center'; + ctx.textBaseline = 'top'; + text.y += textMargin; + } + else if (Math.sin(armAngle * 2) > 0){ + ctx.textAlign = 'right'; + ctx.textBaseline = 'middle'; + } + else { + ctx.textAlign = 'left'; + ctx.textBaseline = 'middle'; + } + ctx.fillStyle = this.colorAxis; + ctx.fillText(' ' + this.yValueLabel(step.getCurrent()) + ' ', text.x, text.y); - if (withSuffix) { - output = this.localeData().pastFuture(+this, output); - } + step.next(); + } - return this.localeData().postformat(output); - }, + // draw z-grid lines and axis + ctx.lineWidth = 1; + prettyStep = (this.defaultZStep === undefined); + step = new StepNumber(this.zMin, this.zMax, this.zStep, prettyStep); + step.start(); + if (step.getCurrent() < this.zMin) { + step.next(); + } + xText = (Math.cos(armAngle ) > 0) ? this.xMin : this.xMax; + yText = (Math.sin(armAngle ) < 0) ? this.yMin : this.yMax; + while (!step.end()) { + // TODO: make z-grid lines really 3d? + from = this._convert3Dto2D(new Point3d(xText, yText, step.getCurrent())); + ctx.strokeStyle = this.colorAxis; + ctx.beginPath(); + ctx.moveTo(from.x, from.y); + ctx.lineTo(from.x - textMargin, from.y); + ctx.stroke(); - add : function (input, val) { - // supports only 2.0-style add(1, 's') or add(moment) - var dur = moment.duration(input, val); + ctx.textAlign = 'right'; + ctx.textBaseline = 'middle'; + ctx.fillStyle = this.colorAxis; + ctx.fillText(this.zValueLabel(step.getCurrent()) + ' ', from.x - 5, from.y); - this._milliseconds += dur._milliseconds; - this._days += dur._days; - this._months += dur._months; + step.next(); + } + ctx.lineWidth = 1; + from = this._convert3Dto2D(new Point3d(xText, yText, this.zMin)); + to = this._convert3Dto2D(new Point3d(xText, yText, this.zMax)); + ctx.strokeStyle = this.colorAxis; + ctx.beginPath(); + ctx.moveTo(from.x, from.y); + ctx.lineTo(to.x, to.y); + ctx.stroke(); - this._bubble(); + // draw x-axis + ctx.lineWidth = 1; + // line at yMin + xMin2d = this._convert3Dto2D(new Point3d(this.xMin, this.yMin, this.zMin)); + xMax2d = this._convert3Dto2D(new Point3d(this.xMax, this.yMin, this.zMin)); + ctx.strokeStyle = this.colorAxis; + ctx.beginPath(); + ctx.moveTo(xMin2d.x, xMin2d.y); + ctx.lineTo(xMax2d.x, xMax2d.y); + ctx.stroke(); + // line at ymax + xMin2d = this._convert3Dto2D(new Point3d(this.xMin, this.yMax, this.zMin)); + xMax2d = this._convert3Dto2D(new Point3d(this.xMax, this.yMax, this.zMin)); + ctx.strokeStyle = this.colorAxis; + ctx.beginPath(); + ctx.moveTo(xMin2d.x, xMin2d.y); + ctx.lineTo(xMax2d.x, xMax2d.y); + ctx.stroke(); - return this; - }, + // draw y-axis + ctx.lineWidth = 1; + // line at xMin + from = this._convert3Dto2D(new Point3d(this.xMin, this.yMin, this.zMin)); + to = this._convert3Dto2D(new Point3d(this.xMin, this.yMax, this.zMin)); + ctx.strokeStyle = this.colorAxis; + ctx.beginPath(); + ctx.moveTo(from.x, from.y); + ctx.lineTo(to.x, to.y); + ctx.stroke(); + // line at xMax + from = this._convert3Dto2D(new Point3d(this.xMax, this.yMin, this.zMin)); + to = this._convert3Dto2D(new Point3d(this.xMax, this.yMax, this.zMin)); + ctx.strokeStyle = this.colorAxis; + ctx.beginPath(); + ctx.moveTo(from.x, from.y); + ctx.lineTo(to.x, to.y); + ctx.stroke(); - subtract : function (input, val) { - var dur = moment.duration(input, val); + // draw x-label + var xLabel = this.xLabel; + if (xLabel.length > 0) { + yOffset = 0.1 / this.scale.y; + xText = (this.xMin + this.xMax) / 2; + yText = (Math.cos(armAngle) > 0) ? this.yMin - yOffset: this.yMax + yOffset; + text = this._convert3Dto2D(new Point3d(xText, yText, this.zMin)); + if (Math.cos(armAngle * 2) > 0) { + ctx.textAlign = 'center'; + ctx.textBaseline = 'top'; + } + else if (Math.sin(armAngle * 2) < 0){ + ctx.textAlign = 'right'; + ctx.textBaseline = 'middle'; + } + else { + ctx.textAlign = 'left'; + ctx.textBaseline = 'middle'; + } + ctx.fillStyle = this.colorAxis; + ctx.fillText(xLabel, text.x, text.y); + } - this._milliseconds -= dur._milliseconds; - this._days -= dur._days; - this._months -= dur._months; - - this._bubble(); + // draw y-label + var yLabel = this.yLabel; + if (yLabel.length > 0) { + xOffset = 0.1 / this.scale.x; + xText = (Math.sin(armAngle ) > 0) ? this.xMin - xOffset : this.xMax + xOffset; + yText = (this.yMin + this.yMax) / 2; + text = this._convert3Dto2D(new Point3d(xText, yText, this.zMin)); + if (Math.cos(armAngle * 2) < 0) { + ctx.textAlign = 'center'; + ctx.textBaseline = 'top'; + } + else if (Math.sin(armAngle * 2) > 0){ + ctx.textAlign = 'right'; + ctx.textBaseline = 'middle'; + } + else { + ctx.textAlign = 'left'; + ctx.textBaseline = 'middle'; + } + ctx.fillStyle = this.colorAxis; + ctx.fillText(yLabel, text.x, text.y); + } - return this; - }, + // draw z-label + var zLabel = this.zLabel; + if (zLabel.length > 0) { + offset = 30; // pixels. // TODO: relate to the max width of the values on the z axis? + xText = (Math.cos(armAngle ) > 0) ? this.xMin : this.xMax; + yText = (Math.sin(armAngle ) < 0) ? this.yMin : this.yMax; + zText = (this.zMin + this.zMax) / 2; + text = this._convert3Dto2D(new Point3d(xText, yText, zText)); + ctx.textAlign = 'right'; + ctx.textBaseline = 'middle'; + ctx.fillStyle = this.colorAxis; + ctx.fillText(zLabel, text.x - offset, text.y); + } + }; - get : function (units) { - units = normalizeUnits(units); - return this[units.toLowerCase() + 's'](); - }, + /** + * Calculate the color based on the given value. + * @param {Number} H Hue, a value be between 0 and 360 + * @param {Number} S Saturation, a value between 0 and 1 + * @param {Number} V Value, a value between 0 and 1 + */ + Graph3d.prototype._hsv2rgb = function(H, S, V) { + var R, G, B, C, Hi, X; - as : function (units) { - var days, months; - units = normalizeUnits(units); + C = V * S; + Hi = Math.floor(H/60); // hi = 0,1,2,3,4,5 + X = C * (1 - Math.abs(((H/60) % 2) - 1)); - if (units === 'month' || units === 'year') { - days = this._days + this._milliseconds / 864e5; - months = this._months + daysToYears(days) * 12; - return units === 'month' ? months : months / 12; - } else { - // handle milliseconds separately because of floating point math errors (issue #1867) - days = this._days + Math.round(yearsToDays(this._months / 12)); - switch (units) { - case 'week': return days / 7 + this._milliseconds / 6048e5; - case 'day': return days + this._milliseconds / 864e5; - case 'hour': return days * 24 + this._milliseconds / 36e5; - case 'minute': return days * 24 * 60 + this._milliseconds / 6e4; - case 'second': return days * 24 * 60 * 60 + this._milliseconds / 1000; - // Math.floor prevents floating point math errors here - case 'millisecond': return Math.floor(days * 24 * 60 * 60 * 1000) + this._milliseconds; - default: throw new Error('Unknown unit ' + units); - } - } - }, + switch (Hi) { + case 0: R = C; G = X; B = 0; break; + case 1: R = X; G = C; B = 0; break; + case 2: R = 0; G = C; B = X; break; + case 3: R = 0; G = X; B = C; break; + case 4: R = X; G = 0; B = C; break; + case 5: R = C; G = 0; B = X; break; - lang : moment.fn.lang, - locale : moment.fn.locale, + default: R = 0; G = 0; B = 0; break; + } - toIsoString : deprecate( - 'toIsoString() is deprecated. Please use toISOString() instead ' + - '(notice the capitals)', - function () { - return this.toISOString(); - } - ), + return 'RGB(' + parseInt(R*255) + ',' + parseInt(G*255) + ',' + parseInt(B*255) + ')'; + }; - toISOString : function () { - // inspired by https://github.com/dordille/moment-isoduration/blob/master/moment.isoduration.js - var years = Math.abs(this.years()), - months = Math.abs(this.months()), - days = Math.abs(this.days()), - hours = Math.abs(this.hours()), - minutes = Math.abs(this.minutes()), - seconds = Math.abs(this.seconds() + this.milliseconds() / 1000); - if (!this.asSeconds()) { - // this is the same as C#'s (Noda) and python (isodate)... - // but not other JS (goog.date) - return 'P0D'; - } + /** + * Draw all datapoints as a grid + * This function can be used when the style is 'grid' + */ + Graph3d.prototype._redrawDataGrid = function() { + var canvas = this.frame.canvas, + ctx = canvas.getContext('2d'), + point, right, top, cross, + i, + topSideVisible, fillStyle, strokeStyle, lineWidth, + h, s, v, zAvg; - return (this.asSeconds() < 0 ? '-' : '') + - 'P' + - (years ? years + 'Y' : '') + - (months ? months + 'M' : '') + - (days ? days + 'D' : '') + - ((hours || minutes || seconds) ? 'T' : '') + - (hours ? hours + 'H' : '') + - (minutes ? minutes + 'M' : '') + - (seconds ? seconds + 'S' : ''); - }, - localeData : function () { - return this._locale; - }, + if (this.dataPoints === undefined || this.dataPoints.length <= 0) + return; // TODO: throw exception? - toJSON : function () { - return this.toISOString(); - } - }); + // calculate the translations and screen position of all points + for (i = 0; i < this.dataPoints.length; i++) { + var trans = this._convertPointToTranslation(this.dataPoints[i].point); + var screen = this._convertTranslationToScreen(trans); - moment.duration.fn.toString = moment.duration.fn.toISOString; + this.dataPoints[i].trans = trans; + this.dataPoints[i].screen = screen; - function makeDurationGetter(name) { - moment.duration.fn[name] = function () { - return this._data[name]; - }; - } + // calculate the translation of the point at the bottom (needed for sorting) + var transBottom = this._convertPointToTranslation(this.dataPoints[i].bottom); + this.dataPoints[i].dist = this.showPerspective ? transBottom.length() : -transBottom.z; + } - for (i in unitMillisecondFactors) { - if (hasOwnProp(unitMillisecondFactors, i)) { - makeDurationGetter(i.toLowerCase()); - } - } + // sort the points on depth of their (x,y) position (not on z) + var sortDepth = function (a, b) { + return b.dist - a.dist; + }; + this.dataPoints.sort(sortDepth); - moment.duration.fn.asMilliseconds = function () { - return this.as('ms'); - }; - moment.duration.fn.asSeconds = function () { - return this.as('s'); - }; - moment.duration.fn.asMinutes = function () { - return this.as('m'); - }; - moment.duration.fn.asHours = function () { - return this.as('h'); - }; - moment.duration.fn.asDays = function () { - return this.as('d'); - }; - moment.duration.fn.asWeeks = function () { - return this.as('weeks'); - }; - moment.duration.fn.asMonths = function () { - return this.as('M'); - }; - moment.duration.fn.asYears = function () { - return this.as('y'); - }; + if (this.style === Graph3d.STYLE.SURFACE) { + for (i = 0; i < this.dataPoints.length; i++) { + point = this.dataPoints[i]; + right = this.dataPoints[i].pointRight; + top = this.dataPoints[i].pointTop; + cross = this.dataPoints[i].pointCross; - /************************************ - Default Locale - ************************************/ + if (point !== undefined && right !== undefined && top !== undefined && cross !== undefined) { + if (this.showGrayBottom || this.showShadow) { + // calculate the cross product of the two vectors from center + // to left and right, in order to know whether we are looking at the + // bottom or at the top side. We can also use the cross product + // for calculating light intensity + var aDiff = Point3d.subtract(cross.trans, point.trans); + var bDiff = Point3d.subtract(top.trans, right.trans); + var crossproduct = Point3d.crossProduct(aDiff, bDiff); + var len = crossproduct.length(); + // FIXME: there is a bug with determining the surface side (shadow or colored) - // Set default locale, other locale will inherit from English. - moment.locale('en', { - ordinalParse: /\d{1,2}(th|st|nd|rd)/, - ordinal : function (number) { - var b = number % 10, - output = (toInt(number % 100 / 10) === 1) ? 'th' : - (b === 1) ? 'st' : - (b === 2) ? 'nd' : - (b === 3) ? 'rd' : 'th'; - return number + output; + topSideVisible = (crossproduct.z > 0); + } + else { + topSideVisible = true; } - }); - - /* EMBED_LOCALES */ - /************************************ - Exposing Moment - ************************************/ + if (topSideVisible) { + // calculate Hue from the current value. At zMin the hue is 240, at zMax the hue is 0 + zAvg = (point.point.z + right.point.z + top.point.z + cross.point.z) / 4; + h = (1 - (zAvg - this.zMin) * this.scale.z / this.verticalRatio) * 240; + s = 1; // saturation - function makeGlobal(shouldDeprecate) { - /*global ender:false */ - if (typeof ender !== 'undefined') { - return; + if (this.showShadow) { + v = Math.min(1 + (crossproduct.x / len) / 2, 1); // value. TODO: scale + fillStyle = this._hsv2rgb(h, s, v); + strokeStyle = fillStyle; + } + else { + v = 1; + fillStyle = this._hsv2rgb(h, s, v); + strokeStyle = this.colorAxis; + } } - oldGlobalMoment = globalScope.moment; - if (shouldDeprecate) { - globalScope.moment = deprecate( - 'Accessing Moment through the global scope is ' + - 'deprecated, and will be removed in an upcoming ' + - 'release.', - moment); - } else { - globalScope.moment = moment; + else { + fillStyle = 'gray'; + strokeStyle = this.colorAxis; } + lineWidth = 0.5; + + ctx.lineWidth = lineWidth; + ctx.fillStyle = fillStyle; + ctx.strokeStyle = strokeStyle; + ctx.beginPath(); + ctx.moveTo(point.screen.x, point.screen.y); + ctx.lineTo(right.screen.x, right.screen.y); + ctx.lineTo(cross.screen.x, cross.screen.y); + ctx.lineTo(top.screen.x, top.screen.y); + ctx.closePath(); + ctx.fill(); + ctx.stroke(); + } } + } + else { // grid style + for (i = 0; i < this.dataPoints.length; i++) { + point = this.dataPoints[i]; + right = this.dataPoints[i].pointRight; + top = this.dataPoints[i].pointTop; - // CommonJS module is defined - if (hasModule) { - module.exports = moment; - } else if (true) { - !(__WEBPACK_AMD_DEFINE_RESULT__ = function (require, exports, module) { - if (module.config && module.config() && module.config().noGlobal === true) { - // release the global variable - globalScope.moment = oldGlobalMoment; - } + if (point !== undefined) { + if (this.showPerspective) { + lineWidth = 2 / -point.trans.z; + } + else { + lineWidth = 2 * -(this.eye.z / this.camera.getArmLength()); + } + } - return moment; - }.call(exports, __webpack_require__, exports, module), __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__)); - makeGlobal(true); - } else { - makeGlobal(); + if (point !== undefined && right !== undefined) { + // calculate Hue from the current value. At zMin the hue is 240, at zMax the hue is 0 + zAvg = (point.point.z + right.point.z) / 2; + h = (1 - (zAvg - this.zMin) * this.scale.z / this.verticalRatio) * 240; + + ctx.lineWidth = lineWidth; + ctx.strokeStyle = this._hsv2rgb(h, 1, 1); + ctx.beginPath(); + ctx.moveTo(point.screen.x, point.screen.y); + ctx.lineTo(right.screen.x, right.screen.y); + ctx.stroke(); + } + + if (point !== undefined && top !== undefined) { + // calculate Hue from the current value. At zMin the hue is 240, at zMax the hue is 0 + zAvg = (point.point.z + top.point.z) / 2; + h = (1 - (zAvg - this.zMin) * this.scale.z / this.verticalRatio) * 240; + + ctx.lineWidth = lineWidth; + ctx.strokeStyle = this._hsv2rgb(h, 1, 1); + ctx.beginPath(); + ctx.moveTo(point.screen.x, point.screen.y); + ctx.lineTo(top.screen.x, top.screen.y); + ctx.stroke(); + } } - }).call(this); - - /* WEBPACK VAR INJECTION */}.call(exports, (function() { return this; }()), __webpack_require__(5)(module))) + } + }; -/***/ }, -/* 4 */ -/***/ function(module, exports, __webpack_require__) { - function webpackContext(req) { - throw new Error("Cannot find module '" + req + "'."); - } - webpackContext.keys = function() { return []; }; - webpackContext.resolve = webpackContext; - module.exports = webpackContext; - webpackContext.id = 4; + /** + * Draw all datapoints as dots. + * This function can be used when the style is 'dot' or 'dot-line' + */ + Graph3d.prototype._redrawDataDot = function() { + var canvas = this.frame.canvas; + var ctx = canvas.getContext('2d'); + var i; + if (this.dataPoints === undefined || this.dataPoints.length <= 0) + return; // TODO: throw exception? -/***/ }, -/* 5 */ -/***/ function(module, exports, __webpack_require__) { + // calculate the translations of all points + for (i = 0; i < this.dataPoints.length; i++) { + var trans = this._convertPointToTranslation(this.dataPoints[i].point); + var screen = this._convertTranslationToScreen(trans); + this.dataPoints[i].trans = trans; + this.dataPoints[i].screen = screen; - module.exports = function(module) { - if(!module.webpackPolyfill) { - module.deprecate = function() {}; - module.paths = []; - // module.parent = undefined by default - module.children = []; - module.webpackPolyfill = 1; - } - return module; - } + // calculate the distance from the point at the bottom to the camera + var transBottom = this._convertPointToTranslation(this.dataPoints[i].bottom); + this.dataPoints[i].dist = this.showPerspective ? transBottom.length() : -transBottom.z; + } + // order the translated points by depth + var sortDepth = function (a, b) { + return b.dist - a.dist; + }; + this.dataPoints.sort(sortDepth); -/***/ }, -/* 6 */ -/***/ function(module, exports, __webpack_require__) { + // draw the datapoints as colored circles + var dotSize = this.frame.clientWidth * 0.02; // px + for (i = 0; i < this.dataPoints.length; i++) { + var point = this.dataPoints[i]; - // DOM utility methods + if (this.style === Graph3d.STYLE.DOTLINE) { + // draw a vertical line from the bottom to the graph value + //var from = this._convert3Dto2D(new Point3d(point.point.x, point.point.y, this.zMin)); + var from = this._convert3Dto2D(point.bottom); + ctx.lineWidth = 1; + ctx.strokeStyle = this.colorGrid; + ctx.beginPath(); + ctx.moveTo(from.x, from.y); + ctx.lineTo(point.screen.x, point.screen.y); + ctx.stroke(); + } - /** - * this prepares the JSON container for allocating SVG elements - * @param JSONcontainer - * @private - */ - exports.prepareElements = function(JSONcontainer) { - // cleanup the redundant svgElements; - for (var elementType in JSONcontainer) { - if (JSONcontainer.hasOwnProperty(elementType)) { - JSONcontainer[elementType].redundant = JSONcontainer[elementType].used; - JSONcontainer[elementType].used = []; + // calculate radius for the circle + var size; + if (this.style === Graph3d.STYLE.DOTSIZE) { + size = dotSize/2 + 2*dotSize * (point.point.value - this.valueMin) / (this.valueMax - this.valueMin); + } + else { + size = dotSize; } - } - }; - /** - * this cleans up all the unused SVG elements. By asking for the parentNode, we only need to supply the JSON container from - * which to remove the redundant elements. - * - * @param JSONcontainer - * @private - */ - exports.cleanupElements = function(JSONcontainer) { - // cleanup the redundant svgElements; - for (var elementType in JSONcontainer) { - if (JSONcontainer.hasOwnProperty(elementType)) { - if (JSONcontainer[elementType].redundant) { - for (var i = 0; i < JSONcontainer[elementType].redundant.length; i++) { - JSONcontainer[elementType].redundant[i].parentNode.removeChild(JSONcontainer[elementType].redundant[i]); - } - JSONcontainer[elementType].redundant = []; - } + var radius; + if (this.showPerspective) { + radius = size / -point.trans.z; + } + else { + radius = size * -(this.eye.z / this.camera.getArmLength()); + } + if (radius < 0) { + radius = 0; } - } - }; - /** - * Allocate or generate an SVG element if needed. Store a reference to it in the JSON container and draw it in the svgContainer - * the JSON container and the SVG container have to be supplied so other svg containers (like the legend) can use this. - * - * @param elementType - * @param JSONcontainer - * @param svgContainer - * @returns {*} - * @private - */ - exports.getSVGElement = function (elementType, JSONcontainer, svgContainer) { - var element; - // allocate SVG element, if it doesnt yet exist, create one. - if (JSONcontainer.hasOwnProperty(elementType)) { // this element has been created before - // check if there is an redundant element - if (JSONcontainer[elementType].redundant.length > 0) { - element = JSONcontainer[elementType].redundant[0]; - JSONcontainer[elementType].redundant.shift(); + var hue, color, borderColor; + if (this.style === Graph3d.STYLE.DOTCOLOR ) { + // calculate the color based on the value + hue = (1 - (point.point.value - this.valueMin) * this.scale.value) * 240; + color = this._hsv2rgb(hue, 1, 1); + borderColor = this._hsv2rgb(hue, 1, 0.8); + } + else if (this.style === Graph3d.STYLE.DOTSIZE) { + color = this.colorDot; + borderColor = this.colorDotBorder; } else { - // create a new element and add it to the SVG - element = document.createElementNS('http://www.w3.org/2000/svg', elementType); - svgContainer.appendChild(element); + // calculate Hue from the current value. At zMin the hue is 240, at zMax the hue is 0 + hue = (1 - (point.point.z - this.zMin) * this.scale.z / this.verticalRatio) * 240; + color = this._hsv2rgb(hue, 1, 1); + borderColor = this._hsv2rgb(hue, 1, 0.8); } + + // draw the circle + ctx.lineWidth = 1.0; + ctx.strokeStyle = borderColor; + ctx.fillStyle = color; + ctx.beginPath(); + ctx.arc(point.screen.x, point.screen.y, radius, 0, Math.PI*2, true); + ctx.fill(); + ctx.stroke(); } - else { - // create a new element and add it to the SVG, also create a new object in the svgElements to keep track of it. - element = document.createElementNS('http://www.w3.org/2000/svg', elementType); - JSONcontainer[elementType] = {used: [], redundant: []}; - svgContainer.appendChild(element); - } - JSONcontainer[elementType].used.push(element); - return element; }; - /** - * Allocate or generate an SVG element if needed. Store a reference to it in the JSON container and draw it in the svgContainer - * the JSON container and the SVG container have to be supplied so other svg containers (like the legend) can use this. - * - * @param elementType - * @param JSONcontainer - * @param DOMContainer - * @returns {*} - * @private + * Draw all datapoints as bars. + * This function can be used when the style is 'bar', 'bar-color', or 'bar-size' */ - exports.getDOMElement = function (elementType, JSONcontainer, DOMContainer, insertBefore) { - var element; - // allocate DOM element, if it doesnt yet exist, create one. - if (JSONcontainer.hasOwnProperty(elementType)) { // this element has been created before - // check if there is an redundant element - if (JSONcontainer[elementType].redundant.length > 0) { - element = JSONcontainer[elementType].redundant[0]; - JSONcontainer[elementType].redundant.shift(); - } - else { - // create a new element and add it to the SVG - element = document.createElement(elementType); - if (insertBefore !== undefined) { - DOMContainer.insertBefore(element, insertBefore); - } - else { - DOMContainer.appendChild(element); - } - } + Graph3d.prototype._redrawDataBar = function() { + var canvas = this.frame.canvas; + var ctx = canvas.getContext('2d'); + var i, j, surface, corners; + + if (this.dataPoints === undefined || this.dataPoints.length <= 0) + return; // TODO: throw exception? + + // calculate the translations of all points + for (i = 0; i < this.dataPoints.length; i++) { + var trans = this._convertPointToTranslation(this.dataPoints[i].point); + var screen = this._convertTranslationToScreen(trans); + this.dataPoints[i].trans = trans; + this.dataPoints[i].screen = screen; + + // calculate the distance from the point at the bottom to the camera + var transBottom = this._convertPointToTranslation(this.dataPoints[i].bottom); + this.dataPoints[i].dist = this.showPerspective ? transBottom.length() : -transBottom.z; } - else { - // create a new element and add it to the SVG, also create a new object in the svgElements to keep track of it. - element = document.createElement(elementType); - JSONcontainer[elementType] = {used: [], redundant: []}; - if (insertBefore !== undefined) { - DOMContainer.insertBefore(element, insertBefore); + + // order the translated points by depth + var sortDepth = function (a, b) { + return b.dist - a.dist; + }; + this.dataPoints.sort(sortDepth); + + // draw the datapoints as bars + var xWidth = this.xBarWidth / 2; + var yWidth = this.yBarWidth / 2; + for (i = 0; i < this.dataPoints.length; i++) { + var point = this.dataPoints[i]; + + // determine color + var hue, color, borderColor; + if (this.style === Graph3d.STYLE.BARCOLOR ) { + // calculate the color based on the value + hue = (1 - (point.point.value - this.valueMin) * this.scale.value) * 240; + color = this._hsv2rgb(hue, 1, 1); + borderColor = this._hsv2rgb(hue, 1, 0.8); + } + else if (this.style === Graph3d.STYLE.BARSIZE) { + color = this.colorDot; + borderColor = this.colorDotBorder; } else { - DOMContainer.appendChild(element); + // calculate Hue from the current value. At zMin the hue is 240, at zMax the hue is 0 + hue = (1 - (point.point.z - this.zMin) * this.scale.z / this.verticalRatio) * 240; + color = this._hsv2rgb(hue, 1, 1); + borderColor = this._hsv2rgb(hue, 1, 0.8); } - } - JSONcontainer[elementType].used.push(element); - return element; - }; + // calculate size for the bar + if (this.style === Graph3d.STYLE.BARSIZE) { + xWidth = (this.xBarWidth / 2) * ((point.point.value - this.valueMin) / (this.valueMax - this.valueMin) * 0.8 + 0.2); + yWidth = (this.yBarWidth / 2) * ((point.point.value - this.valueMin) / (this.valueMax - this.valueMin) * 0.8 + 0.2); + } + // calculate all corner points + var me = this; + var point3d = point.point; + var top = [ + {point: new Point3d(point3d.x - xWidth, point3d.y - yWidth, point3d.z)}, + {point: new Point3d(point3d.x + xWidth, point3d.y - yWidth, point3d.z)}, + {point: new Point3d(point3d.x + xWidth, point3d.y + yWidth, point3d.z)}, + {point: new Point3d(point3d.x - xWidth, point3d.y + yWidth, point3d.z)} + ]; + var bottom = [ + {point: new Point3d(point3d.x - xWidth, point3d.y - yWidth, this.zMin)}, + {point: new Point3d(point3d.x + xWidth, point3d.y - yWidth, this.zMin)}, + {point: new Point3d(point3d.x + xWidth, point3d.y + yWidth, this.zMin)}, + {point: new Point3d(point3d.x - xWidth, point3d.y + yWidth, this.zMin)} + ]; + // calculate screen location of the points + top.forEach(function (obj) { + obj.screen = me._convert3Dto2D(obj.point); + }); + bottom.forEach(function (obj) { + obj.screen = me._convert3Dto2D(obj.point); + }); - /** - * draw a point object. this is a seperate function because it can also be called by the legend. - * The reason the JSONcontainer and the target SVG svgContainer have to be supplied is so the legend can use these functions - * as well. - * - * @param x - * @param y - * @param group - * @param JSONcontainer - * @param svgContainer - * @returns {*} - */ - exports.drawPoint = function(x, y, group, JSONcontainer, svgContainer) { - var point; - if (group.options.drawPoints.style == 'circle') { - point = exports.getSVGElement('circle',JSONcontainer,svgContainer); - point.setAttributeNS(null, "cx", x); - point.setAttributeNS(null, "cy", y); - point.setAttributeNS(null, "r", 0.5 * group.options.drawPoints.size); - } - else { - point = exports.getSVGElement('rect',JSONcontainer,svgContainer); - point.setAttributeNS(null, "x", x - 0.5*group.options.drawPoints.size); - point.setAttributeNS(null, "y", y - 0.5*group.options.drawPoints.size); - point.setAttributeNS(null, "width", group.options.drawPoints.size); - point.setAttributeNS(null, "height", group.options.drawPoints.size); - } + // create five sides, calculate both corner points and center points + var surfaces = [ + {corners: top, center: Point3d.avg(bottom[0].point, bottom[2].point)}, + {corners: [top[0], top[1], bottom[1], bottom[0]], center: Point3d.avg(bottom[1].point, bottom[0].point)}, + {corners: [top[1], top[2], bottom[2], bottom[1]], center: Point3d.avg(bottom[2].point, bottom[1].point)}, + {corners: [top[2], top[3], bottom[3], bottom[2]], center: Point3d.avg(bottom[3].point, bottom[2].point)}, + {corners: [top[3], top[0], bottom[0], bottom[3]], center: Point3d.avg(bottom[0].point, bottom[3].point)} + ]; + point.surfaces = surfaces; - if(group.options.drawPoints.styles !== undefined) { - point.setAttributeNS(null, "style", group.group.options.drawPoints.styles); - } - point.setAttributeNS(null, "class", group.className + " point"); - return point; - }; + // calculate the distance of each of the surface centers to the camera + for (j = 0; j < surfaces.length; j++) { + surface = surfaces[j]; + var transCenter = this._convertPointToTranslation(surface.center); + surface.dist = this.showPerspective ? transCenter.length() : -transCenter.z; + // TODO: this dept calculation doesn't work 100% of the cases due to perspective, + // but the current solution is fast/simple and works in 99.9% of all cases + // the issue is visible in example 14, with graph.setCameraPosition({horizontal: 2.97, vertical: 0.5, distance: 0.9}) + } - /** - * draw a bar SVG element centered on the X coordinate - * - * @param x - * @param y - * @param className - */ - exports.drawBar = function (x, y, width, height, className, JSONcontainer, svgContainer) { - if (height != 0) { - if (height < 0) { - height *= -1; - y -= height; + // order the surfaces by their (translated) depth + surfaces.sort(function (a, b) { + var diff = b.dist - a.dist; + if (diff) return diff; + + // if equal depth, sort the top surface last + if (a.corners === top) return 1; + if (b.corners === top) return -1; + + // both are equal + return 0; + }); + + // draw the ordered surfaces + ctx.lineWidth = 1; + ctx.strokeStyle = borderColor; + ctx.fillStyle = color; + // NOTE: we start at j=2 instead of j=0 as we don't need to draw the two surfaces at the backside + for (j = 2; j < surfaces.length; j++) { + surface = surfaces[j]; + corners = surface.corners; + ctx.beginPath(); + ctx.moveTo(corners[3].screen.x, corners[3].screen.y); + ctx.lineTo(corners[0].screen.x, corners[0].screen.y); + ctx.lineTo(corners[1].screen.x, corners[1].screen.y); + ctx.lineTo(corners[2].screen.x, corners[2].screen.y); + ctx.lineTo(corners[3].screen.x, corners[3].screen.y); + ctx.fill(); + ctx.stroke(); } - var rect = exports.getSVGElement('rect',JSONcontainer, svgContainer); - rect.setAttributeNS(null, "x", x - 0.5 * width); - rect.setAttributeNS(null, "y", y); - rect.setAttributeNS(null, "width", width); - rect.setAttributeNS(null, "height", height); - rect.setAttributeNS(null, "class", className); } }; -/***/ }, -/* 7 */ -/***/ function(module, exports, __webpack_require__) { - - var util = __webpack_require__(1); - var Queue = __webpack_require__(8); /** - * DataSet - * - * Usage: - * var dataSet = new DataSet({ - * fieldId: '_id', - * type: { - * // ... - * } - * }); - * - * dataSet.add(item); - * dataSet.add(data); - * dataSet.update(item); - * dataSet.update(data); - * dataSet.remove(id); - * dataSet.remove(ids); - * var data = dataSet.get(); - * var data = dataSet.get(id); - * var data = dataSet.get(ids); - * var data = dataSet.get(ids, options, data); - * dataSet.clear(); - * - * A data set can: - * - add/remove/update data - * - gives triggers upon changes in the data - * - can import/export data in various data formats - * - * @param {Array | DataTable} [data] Optional array with initial data - * @param {Object} [options] Available options: - * {String} fieldId Field name of the id in the - * items, 'id' by default. - * {Object. 0) { + point = this.dataPoints[0]; - // add initial data when provided - if (data) { - this.add(data); + ctx.lineWidth = 1; // TODO: make customizable + ctx.strokeStyle = 'blue'; // TODO: make customizable + ctx.beginPath(); + ctx.moveTo(point.screen.x, point.screen.y); } - this.setOptions(options); - } - - /** - * @param {Object} [options] Available options: - * {Object} queue Queue changes to the DataSet, - * flush them all at once. - * Queue options: - * - {number} delay Delay in ms, null by default - * - {number} max Maximum number of entries in the queue, Infinity by default - * @param options - */ - DataSet.prototype.setOptions = function(options) { - if (options && options.queue !== undefined) { - if (options.queue === false) { - // delete queue if loaded - if (this._queue) { - this._queue.destroy(); - delete this._queue; - } - } - else { - // create queue and update its options - if (!this._queue) { - this._queue = Queue.extend(this, { - replace: ['add', 'update', 'remove'] - }); - } + // draw the datapoints as colored circles + for (i = 1; i < this.dataPoints.length; i++) { + point = this.dataPoints[i]; + ctx.lineTo(point.screen.x, point.screen.y); + } - if (typeof options.queue === 'object') { - this._queue.setOptions(options.queue); - } - } + // finish the line + if (this.dataPoints.length > 0) { + ctx.stroke(); } }; /** - * Subscribe to an event, add an event listener - * @param {String} event Event name. Available events: 'put', 'update', - * 'remove' - * @param {function} callback Callback method. Called with three parameters: - * {String} event - * {Object | null} params - * {String | Number} senderId + * Start a moving operation inside the provided parent element + * @param {Event} event The event that occurred (required for + * retrieving the mouse position) */ - DataSet.prototype.on = function(event, callback) { - var subscribers = this._subscribers[event]; - if (!subscribers) { - subscribers = []; - this._subscribers[event] = subscribers; + Graph3d.prototype._onMouseDown = function(event) { + event = event || window.event; + + // check if mouse is still down (may be up when focus is lost for example + // in an iframe) + if (this.leftButtonDown) { + this._onMouseUp(event); } - subscribers.push({ - callback: callback - }); - }; + // only react on left mouse button down + this.leftButtonDown = event.which ? (event.which === 1) : (event.button === 1); + if (!this.leftButtonDown && !this.touchDown) return; - // TODO: make this function deprecated (replaced with `on` since version 0.5) - DataSet.prototype.subscribe = DataSet.prototype.on; + // get mouse position (different code for IE and all other browsers) + this.startMouseX = getMouseX(event); + this.startMouseY = getMouseY(event); - /** - * Unsubscribe from an event, remove an event listener - * @param {String} event - * @param {function} callback - */ - DataSet.prototype.off = function(event, callback) { - var subscribers = this._subscribers[event]; - if (subscribers) { - this._subscribers[event] = subscribers.filter(function (listener) { - return (listener.callback != callback); - }); - } + this.startStart = new Date(this.start); + this.startEnd = new Date(this.end); + this.startArmRotation = this.camera.getArmRotation(); + + this.frame.style.cursor = 'move'; + + // add event listeners to handle moving the contents + // we store the function onmousemove and onmouseup in the graph, so we can + // remove the eventlisteners lateron in the function mouseUp() + var me = this; + this.onmousemove = function (event) {me._onMouseMove(event);}; + this.onmouseup = function (event) {me._onMouseUp(event);}; + util.addEventListener(document, 'mousemove', me.onmousemove); + util.addEventListener(document, 'mouseup', me.onmouseup); + util.preventDefault(event); }; - // TODO: make this function deprecated (replaced with `on` since version 0.5) - DataSet.prototype.unsubscribe = DataSet.prototype.off; /** - * Trigger an event - * @param {String} event - * @param {Object | null} params - * @param {String} [senderId] Optional id of the sender. - * @private + * Perform moving operating. + * This function activated from within the funcion Graph.mouseDown(). + * @param {Event} event Well, eehh, the event */ - DataSet.prototype._trigger = function (event, params, senderId) { - if (event == '*') { - throw new Error('Cannot trigger event *'); - } + Graph3d.prototype._onMouseMove = function (event) { + event = event || window.event; - var subscribers = []; - if (event in this._subscribers) { - subscribers = subscribers.concat(this._subscribers[event]); - } - if ('*' in this._subscribers) { - subscribers = subscribers.concat(this._subscribers['*']); - } + // calculate change in mouse position + var diffX = parseFloat(getMouseX(event)) - this.startMouseX; + var diffY = parseFloat(getMouseY(event)) - this.startMouseY; - for (var i = 0; i < subscribers.length; i++) { - var subscriber = subscribers[i]; - if (subscriber.callback) { - subscriber.callback(event, params, senderId || null); - } - } - }; + var horizontalNew = this.startArmRotation.horizontal + diffX / 200; + var verticalNew = this.startArmRotation.vertical + diffY / 200; - /** - * Add data. - * Adding an item will fail when there already is an item with the same id. - * @param {Object | Array | DataTable} data - * @param {String} [senderId] Optional sender id - * @return {Array} addedIds Array with the ids of the added items - */ - DataSet.prototype.add = function (data, senderId) { - var addedIds = [], - id, - me = this; + var snapAngle = 4; // degrees + var snapValue = Math.sin(snapAngle / 360 * 2 * Math.PI); - if (Array.isArray(data)) { - // Array - for (var i = 0, len = data.length; i < len; i++) { - id = me._addItem(data[i]); - addedIds.push(id); - } + // snap horizontally to nice angles at 0pi, 0.5pi, 1pi, 1.5pi, etc... + // the -0.001 is to take care that the vertical axis is always drawn at the left front corner + if (Math.abs(Math.sin(horizontalNew)) < snapValue) { + horizontalNew = Math.round((horizontalNew / Math.PI)) * Math.PI - 0.001; } - else if (util.isDataTable(data)) { - // Google DataTable - var columns = this._getColumnNames(data); - for (var row = 0, rows = data.getNumberOfRows(); row < rows; row++) { - var item = {}; - for (var col = 0, cols = columns.length; col < cols; col++) { - var field = columns[col]; - item[field] = data.getValue(row, col); - } - - id = me._addItem(item); - addedIds.push(id); - } + if (Math.abs(Math.cos(horizontalNew)) < snapValue) { + horizontalNew = (Math.round((horizontalNew/ Math.PI - 0.5)) + 0.5) * Math.PI - 0.001; } - else if (data instanceof Object) { - // Single item - id = me._addItem(data); - addedIds.push(id); + + // snap vertically to nice angles + if (Math.abs(Math.sin(verticalNew)) < snapValue) { + verticalNew = Math.round((verticalNew / Math.PI)) * Math.PI; } - else { - throw new Error('Unknown dataType'); + if (Math.abs(Math.cos(verticalNew)) < snapValue) { + verticalNew = (Math.round((verticalNew/ Math.PI - 0.5)) + 0.5) * Math.PI; } - if (addedIds.length) { - this._trigger('add', {items: addedIds}, senderId); - } + this.camera.setArmRotation(horizontalNew, verticalNew); + this.redraw(); - return addedIds; + // fire a cameraPositionChange event + var parameters = this.getCameraPosition(); + this.emit('cameraPositionChange', parameters); + + util.preventDefault(event); }; + /** - * Update existing items. When an item does not exist, it will be created - * @param {Object | Array | DataTable} data - * @param {String} [senderId] Optional sender id - * @return {Array} updatedIds The ids of the added or updated items + * Stop moving operating. + * This function activated from within the funcion Graph.mouseDown(). + * @param {event} event The event */ - DataSet.prototype.update = function (data, senderId) { - var addedIds = []; - var updatedIds = []; - var updatedData = []; - var me = this; - var fieldId = me._fieldId; - - var addOrUpdate = function (item) { - var id = item[fieldId]; - if (me._data[id]) { - // update item - id = me._updateItem(item); - updatedIds.push(id); - updatedData.push(item); - } - else { - // add new item - id = me._addItem(item); - addedIds.push(id); - } - }; - - if (Array.isArray(data)) { - // Array - for (var i = 0, len = data.length; i < len; i++) { - addOrUpdate(data[i]); - } - } - else if (util.isDataTable(data)) { - // Google DataTable - var columns = this._getColumnNames(data); - for (var row = 0, rows = data.getNumberOfRows(); row < rows; row++) { - var item = {}; - for (var col = 0, cols = columns.length; col < cols; col++) { - var field = columns[col]; - item[field] = data.getValue(row, col); - } - - addOrUpdate(item); - } - } - else if (data instanceof Object) { - // Single item - addOrUpdate(data); - } - else { - throw new Error('Unknown dataType'); - } - - if (addedIds.length) { - this._trigger('add', {items: addedIds}, senderId); - } - if (updatedIds.length) { - this._trigger('update', {items: updatedIds, data: updatedData}, senderId); - } + Graph3d.prototype._onMouseUp = function (event) { + this.frame.style.cursor = 'auto'; + this.leftButtonDown = false; - return addedIds.concat(updatedIds); + // remove event listeners here + util.removeEventListener(document, 'mousemove', this.onmousemove); + util.removeEventListener(document, 'mouseup', this.onmouseup); + util.preventDefault(event); }; /** - * Get a data item or multiple items. - * - * Usage: - * - * get() - * get(options: Object) - * get(options: Object, data: Array | DataTable) - * - * get(id: Number | String) - * get(id: Number | String, options: Object) - * get(id: Number | String, options: Object, data: Array | DataTable) - * - * get(ids: Number[] | String[]) - * get(ids: Number[] | String[], options: Object) - * get(ids: Number[] | String[], options: Object, data: Array | DataTable) - * - * Where: - * - * {Number | String} id The id of an item - * {Number[] | String{}} ids An array with ids of items - * {Object} options An Object with options. Available options: - * {String} [returnType] Type of data to be - * returned. Can be 'DataTable' or 'Array' (default) - * {Object.} [type] - * {String[]} [fields] field names to be returned - * {function} [filter] filter items - * {String | function} [order] Order the items by - * a field name or custom sort function. - * {Array | DataTable} [data] If provided, items will be appended to this - * array or table. Required in case of Google - * DataTable. - * - * @throws Error + * After having moved the mouse, a tooltip should pop up when the mouse is resting on a data point + * @param {Event} event A mouse move event */ - DataSet.prototype.get = function (args) { - var me = this; - - // parse the arguments - var id, ids, options, data; - var firstType = util.getType(arguments[0]); - if (firstType == 'String' || firstType == 'Number') { - // get(id [, options] [, data]) - id = arguments[0]; - options = arguments[1]; - data = arguments[2]; - } - else if (firstType == 'Array') { - // get(ids [, options] [, data]) - ids = arguments[0]; - options = arguments[1]; - data = arguments[2]; - } - else { - // get([, options] [, data]) - options = arguments[0]; - data = arguments[1]; - } - - // determine the return type - var returnType; - if (options && options.returnType) { - var allowedValues = ["DataTable", "Array", "Object"]; - returnType = allowedValues.indexOf(options.returnType) == -1 ? "Array" : options.returnType; + Graph3d.prototype._onTooltip = function (event) { + var delay = 300; // ms + var boundingRect = this.frame.getBoundingClientRect(); + var mouseX = getMouseX(event) - boundingRect.left; + var mouseY = getMouseY(event) - boundingRect.top; - if (data && (returnType != util.getType(data))) { - throw new Error('Type of parameter "data" (' + util.getType(data) + ') ' + - 'does not correspond with specified options.type (' + options.type + ')'); - } - if (returnType == 'DataTable' && !util.isDataTable(data)) { - throw new Error('Parameter "data" must be a DataTable ' + - 'when options.type is "DataTable"'); - } - } - else if (data) { - returnType = (util.getType(data) == 'DataTable') ? 'DataTable' : 'Array'; - } - else { - returnType = 'Array'; + if (!this.showTooltip) { + return; } - // build options - var type = options && options.type || this._options.type; - var filter = options && options.filter; - var items = [], item, itemId, i, len; - - // convert items - if (id != undefined) { - // return a single item - item = me._getItem(id, type); - if (filter && !filter(item)) { - item = null; - } - } - else if (ids != undefined) { - // return a subset of items - for (i = 0, len = ids.length; i < len; i++) { - item = me._getItem(ids[i], type); - if (!filter || filter(item)) { - items.push(item); - } - } - } - else { - // return all items - for (itemId in this._data) { - if (this._data.hasOwnProperty(itemId)) { - item = me._getItem(itemId, type); - if (!filter || filter(item)) { - items.push(item); - } - } - } + if (this.tooltipTimeout) { + clearTimeout(this.tooltipTimeout); } - // order the results - if (options && options.order && id == undefined) { - this._sort(items, options.order); + // (delayed) display of a tooltip only if no mouse button is down + if (this.leftButtonDown) { + this._hideTooltip(); + return; } - // filter fields of the items - if (options && options.fields) { - var fields = options.fields; - if (id != undefined) { - item = this._filterFields(item, fields); - } - else { - for (i = 0, len = items.length; i < len; i++) { - items[i] = this._filterFields(items[i], fields); + if (this.tooltip && this.tooltip.dataPoint) { + // tooltip is currently visible + var dataPoint = this._dataPointFromXY(mouseX, mouseY); + if (dataPoint !== this.tooltip.dataPoint) { + // datapoint changed + if (dataPoint) { + this._showTooltip(dataPoint); } - } - } - - // return the results - if (returnType == 'DataTable') { - var columns = this._getColumnNames(data); - if (id != undefined) { - // append a single item to the data table - me._appendRow(data, columns, item); - } - else { - // copy the items to the provided data table - for (i = 0; i < items.length; i++) { - me._appendRow(data, columns, items[i]); + else { + this._hideTooltip(); } } - return data; - } - else if (returnType == "Object") { - var result = {}; - for (i = 0; i < items.length; i++) { - result[items[i].id] = items[i]; - } - return result; } else { - // return an array - if (id != undefined) { - // a single item - return item; - } - else { - // multiple items - if (data) { - // copy the items to the provided array - for (i = 0, len = items.length; i < len; i++) { - data.push(items[i]); - } - return data; - } - else { - // just return our array - return items; + // tooltip is currently not visible + var me = this; + this.tooltipTimeout = setTimeout(function () { + me.tooltipTimeout = null; + + // show a tooltip if we have a data point + var dataPoint = me._dataPointFromXY(mouseX, mouseY); + if (dataPoint) { + me._showTooltip(dataPoint); } - } + }, delay); } }; /** - * Get ids of all items or from a filtered set of items. - * @param {Object} [options] An Object with options. Available options: - * {function} [filter] filter items - * {String | function} [order] Order the items by - * a field name or custom sort function. - * @return {Array} ids + * Event handler for touchstart event on mobile devices */ - DataSet.prototype.getIds = function (options) { - var data = this._data, - filter = options && options.filter, - order = options && options.order, - type = options && options.type || this._options.type, - i, - len, - id, - item, - items, - ids = []; - - if (filter) { - // get filtered items - if (order) { - // create ordered list - items = []; - for (id in data) { - if (data.hasOwnProperty(id)) { - item = this._getItem(id, type); - if (filter(item)) { - items.push(item); - } - } - } - - this._sort(items, order); - - for (i = 0, len = items.length; i < len; i++) { - ids[i] = items[i][this._fieldId]; - } - } - else { - // create unordered list - for (id in data) { - if (data.hasOwnProperty(id)) { - item = this._getItem(id, type); - if (filter(item)) { - ids.push(item[this._fieldId]); - } - } - } - } - } - else { - // get all items - if (order) { - // create an ordered list - items = []; - for (id in data) { - if (data.hasOwnProperty(id)) { - items.push(data[id]); - } - } - - this._sort(items, order); + Graph3d.prototype._onTouchStart = function(event) { + this.touchDown = true; - for (i = 0, len = items.length; i < len; i++) { - ids[i] = items[i][this._fieldId]; - } - } - else { - // create unordered list - for (id in data) { - if (data.hasOwnProperty(id)) { - item = data[id]; - ids.push(item[this._fieldId]); - } - } - } - } + var me = this; + this.ontouchmove = function (event) {me._onTouchMove(event);}; + this.ontouchend = function (event) {me._onTouchEnd(event);}; + util.addEventListener(document, 'touchmove', me.ontouchmove); + util.addEventListener(document, 'touchend', me.ontouchend); - return ids; + this._onMouseDown(event); }; /** - * Returns the DataSet itself. Is overwritten for example by the DataView, - * which returns the DataSet it is connected to instead. + * Event handler for touchmove event on mobile devices */ - DataSet.prototype.getDataSet = function () { - return this; + Graph3d.prototype._onTouchMove = function(event) { + this._onMouseMove(event); }; /** - * Execute a callback function for every item in the dataset. - * @param {function} callback - * @param {Object} [options] Available options: - * {Object.} [type] - * {String[]} [fields] filter fields - * {function} [filter] filter items - * {String | function} [order] Order the items by - * a field name or custom sort function. + * Event handler for touchend event on mobile devices */ - DataSet.prototype.forEach = function (callback, options) { - var filter = options && options.filter, - type = options && options.type || this._options.type, - data = this._data, - item, - id; + Graph3d.prototype._onTouchEnd = function(event) { + this.touchDown = false; - if (options && options.order) { - // execute forEach on ordered list - var items = this.get(options); + util.removeEventListener(document, 'touchmove', this.ontouchmove); + util.removeEventListener(document, 'touchend', this.ontouchend); - for (var i = 0, len = items.length; i < len; i++) { - item = items[i]; - id = item[this._fieldId]; - callback(item, id); - } - } - else { - // unordered - for (id in data) { - if (data.hasOwnProperty(id)) { - item = this._getItem(id, type); - if (!filter || filter(item)) { - callback(item, id); - } - } - } - } + this._onMouseUp(event); }; + /** - * Map every item in the dataset. - * @param {function} callback - * @param {Object} [options] Available options: - * {Object.} [type] - * {String[]} [fields] filter fields - * {function} [filter] filter items - * {String | function} [order] Order the items by - * a field name or custom sort function. - * @return {Object[]} mappedItems + * Event handler for mouse wheel event, used to zoom the graph + * Code from http://adomas.org/javascript-mouse-wheel/ + * @param {event} event The event */ - DataSet.prototype.map = function (callback, options) { - var filter = options && options.filter, - type = options && options.type || this._options.type, - mappedItems = [], - data = this._data, - item; + Graph3d.prototype._onWheel = function(event) { + if (!event) /* For IE. */ + event = window.event; - // convert and filter items - for (var id in data) { - if (data.hasOwnProperty(id)) { - item = this._getItem(id, type); - if (!filter || filter(item)) { - mappedItems.push(callback(item, id)); - } - } + // retrieve delta + var delta = 0; + if (event.wheelDelta) { /* IE/Opera. */ + delta = event.wheelDelta/120; + } else if (event.detail) { /* Mozilla case. */ + // In Mozilla, sign of delta is different than in IE. + // Also, delta is multiple of 3. + delta = -event.detail/3; } - // order items - if (options && options.order) { - this._sort(mappedItems, options.order); + // If delta is nonzero, handle it. + // Basically, delta is now positive if wheel was scrolled up, + // and negative, if wheel was scrolled down. + if (delta) { + var oldLength = this.camera.getArmLength(); + var newLength = oldLength * (1 - delta / 10); + + this.camera.setArmLength(newLength); + this.redraw(); + + this._hideTooltip(); } - return mappedItems; + // fire a cameraPositionChange event + var parameters = this.getCameraPosition(); + this.emit('cameraPositionChange', parameters); + + // Prevent default actions caused by mouse wheel. + // That might be ugly, but we handle scrolls somehow + // anyway, so don't bother here.. + util.preventDefault(event); }; /** - * Filter the fields of an item - * @param {Object} item - * @param {String[]} fields Field names - * @return {Object} filteredItem + * Test whether a point lies inside given 2D triangle + * @param {Point2d} point + * @param {Point2d[]} triangle + * @return {boolean} Returns true if given point lies inside or on the edge of the triangle * @private */ - DataSet.prototype._filterFields = function (item, fields) { - var filteredItem = {}; + Graph3d.prototype._insideTriangle = function (point, triangle) { + var a = triangle[0], + b = triangle[1], + c = triangle[2]; - for (var field in item) { - if (item.hasOwnProperty(field) && (fields.indexOf(field) != -1)) { - filteredItem[field] = item[field]; - } + function sign (x) { + return x > 0 ? 1 : x < 0 ? -1 : 0; } - return filteredItem; + var as = sign((b.x - a.x) * (point.y - a.y) - (b.y - a.y) * (point.x - a.x)); + var bs = sign((c.x - b.x) * (point.y - b.y) - (c.y - b.y) * (point.x - b.x)); + var cs = sign((a.x - c.x) * (point.y - c.y) - (a.y - c.y) * (point.x - c.x)); + + // each of the three signs must be either equal to each other or zero + return (as == 0 || bs == 0 || as == bs) && + (bs == 0 || cs == 0 || bs == cs) && + (as == 0 || cs == 0 || as == cs); }; /** - * Sort the provided array with items - * @param {Object[]} items - * @param {String | function} order A field name or custom sort function. + * Find a data point close to given screen position (x, y) + * @param {Number} x + * @param {Number} y + * @return {Object | null} The closest data point or null if not close to any data point * @private */ - DataSet.prototype._sort = function (items, order) { - if (util.isString(order)) { - // order by provided field name - var name = order; // field name - items.sort(function (a, b) { - var av = a[name]; - var bv = b[name]; - return (av > bv) ? 1 : ((av < bv) ? -1 : 0); - }); - } - else if (typeof order === 'function') { - // order by sort function - items.sort(order); - } - // TODO: extend order by an Object {field:String, direction:String} - // where direction can be 'asc' or 'desc' - else { - throw new TypeError('Order must be a function or a string'); - } - }; + Graph3d.prototype._dataPointFromXY = function (x, y) { + var i, + distMax = 100, // px + dataPoint = null, + closestDataPoint = null, + closestDist = null, + center = new Point2d(x, y); - /** - * Remove an object by pointer or by id - * @param {String | Number | Object | Array} id Object or id, or an array with - * objects or ids to be removed - * @param {String} [senderId] Optional sender id - * @return {Array} removedIds - */ - DataSet.prototype.remove = function (id, senderId) { - var removedIds = [], - i, len, removedId; - - if (Array.isArray(id)) { - for (i = 0, len = id.length; i < len; i++) { - removedId = this._remove(id[i]); - if (removedId != null) { - removedIds.push(removedId); + if (this.style === Graph3d.STYLE.BAR || + this.style === Graph3d.STYLE.BARCOLOR || + this.style === Graph3d.STYLE.BARSIZE) { + // the data points are ordered from far away to closest + for (i = this.dataPoints.length - 1; i >= 0; i--) { + dataPoint = this.dataPoints[i]; + var surfaces = dataPoint.surfaces; + if (surfaces) { + for (var s = surfaces.length - 1; s >= 0; s--) { + // split each surface in two triangles, and see if the center point is inside one of these + var surface = surfaces[s]; + var corners = surface.corners; + var triangle1 = [corners[0].screen, corners[1].screen, corners[2].screen]; + var triangle2 = [corners[2].screen, corners[3].screen, corners[0].screen]; + if (this._insideTriangle(center, triangle1) || + this._insideTriangle(center, triangle2)) { + // return immediately at the first hit + return dataPoint; + } + } } } } else { - removedId = this._remove(id); - if (removedId != null) { - removedIds.push(removedId); + // find the closest data point, using distance to the center of the point on 2d screen + for (i = 0; i < this.dataPoints.length; i++) { + dataPoint = this.dataPoints[i]; + var point = dataPoint.screen; + if (point) { + var distX = Math.abs(x - point.x); + var distY = Math.abs(y - point.y); + var dist = Math.sqrt(distX * distX + distY * distY); + + if ((closestDist === null || dist < closestDist) && dist < distMax) { + closestDist = dist; + closestDataPoint = dataPoint; + } + } } } - if (removedIds.length) { - this._trigger('remove', {items: removedIds}, senderId); - } - return removedIds; + return closestDataPoint; }; /** - * Remove an item by its id - * @param {Number | String | Object} id id or item - * @returns {Number | String | null} id + * Display a tooltip for given data point + * @param {Object} dataPoint * @private */ - DataSet.prototype._remove = function (id) { - if (util.isNumber(id) || util.isString(id)) { - if (this._data[id]) { - delete this._data[id]; - this.length--; - return id; - } + Graph3d.prototype._showTooltip = function (dataPoint) { + var content, line, dot; + + if (!this.tooltip) { + content = document.createElement('div'); + content.style.position = 'absolute'; + content.style.padding = '10px'; + content.style.border = '1px solid #4d4d4d'; + content.style.color = '#1a1a1a'; + content.style.background = 'rgba(255,255,255,0.7)'; + content.style.borderRadius = '2px'; + content.style.boxShadow = '5px 5px 10px rgba(128,128,128,0.5)'; + + line = document.createElement('div'); + line.style.position = 'absolute'; + line.style.height = '40px'; + line.style.width = '0'; + line.style.borderLeft = '1px solid #4d4d4d'; + + dot = document.createElement('div'); + dot.style.position = 'absolute'; + dot.style.height = '0'; + dot.style.width = '0'; + dot.style.border = '5px solid #4d4d4d'; + dot.style.borderRadius = '5px'; + + this.tooltip = { + dataPoint: null, + dom: { + content: content, + line: line, + dot: dot + } + }; } - else if (id instanceof Object) { - var itemId = id[this._fieldId]; - if (itemId && this._data[itemId]) { - delete this._data[itemId]; - this.length--; - return itemId; - } + else { + content = this.tooltip.dom.content; + line = this.tooltip.dom.line; + dot = this.tooltip.dom.dot; } - return null; - }; - /** - * Clear the data - * @param {String} [senderId] Optional sender id - * @return {Array} removedIds The ids of all removed items - */ - DataSet.prototype.clear = function (senderId) { - var ids = Object.keys(this._data); + this._hideTooltip(); - this._data = {}; - this.length = 0; + this.tooltip.dataPoint = dataPoint; + if (typeof this.showTooltip === 'function') { + content.innerHTML = this.showTooltip(dataPoint.point); + } + else { + content.innerHTML = '' + + '' + + '' + + '' + + '
x:' + dataPoint.point.x + '
y:' + dataPoint.point.y + '
z:' + dataPoint.point.z + '
'; + } - this._trigger('remove', {items: ids}, senderId); + content.style.left = '0'; + content.style.top = '0'; + this.frame.appendChild(content); + this.frame.appendChild(line); + this.frame.appendChild(dot); - return ids; + // calculate sizes + var contentWidth = content.offsetWidth; + var contentHeight = content.offsetHeight; + var lineHeight = line.offsetHeight; + var dotWidth = dot.offsetWidth; + var dotHeight = dot.offsetHeight; + + var left = dataPoint.screen.x - contentWidth / 2; + left = Math.min(Math.max(left, 10), this.frame.clientWidth - 10 - contentWidth); + + line.style.left = dataPoint.screen.x + 'px'; + line.style.top = (dataPoint.screen.y - lineHeight) + 'px'; + content.style.left = left + 'px'; + content.style.top = (dataPoint.screen.y - lineHeight - contentHeight) + 'px'; + dot.style.left = (dataPoint.screen.x - dotWidth / 2) + 'px'; + dot.style.top = (dataPoint.screen.y - dotHeight / 2) + 'px'; }; /** - * Find the item with maximum value of a specified field - * @param {String} field - * @return {Object | null} item Item containing max value, or null if no items + * Hide the tooltip when displayed + * @private */ - DataSet.prototype.max = function (field) { - var data = this._data, - max = null, - maxField = null; + Graph3d.prototype._hideTooltip = function () { + if (this.tooltip) { + this.tooltip.dataPoint = null; - for (var id in data) { - if (data.hasOwnProperty(id)) { - var item = data[id]; - var itemField = item[field]; - if (itemField != null && (!max || itemField > maxField)) { - max = item; - maxField = itemField; + for (var prop in this.tooltip.dom) { + if (this.tooltip.dom.hasOwnProperty(prop)) { + var elem = this.tooltip.dom[prop]; + if (elem && elem.parentNode) { + elem.parentNode.removeChild(elem); + } } } } - - return max; }; + /**--------------------------------------------------------------------------**/ + + /** - * Find the item with minimum value of a specified field - * @param {String} field - * @return {Object | null} item Item containing max value, or null if no items + * Get the horizontal mouse position from a mouse event + * @param {Event} event + * @return {Number} mouse x */ - DataSet.prototype.min = function (field) { - var data = this._data, - min = null, - minField = null; + function getMouseX (event) { + if ('clientX' in event) return event.clientX; + return event.targetTouches[0] && event.targetTouches[0].clientX || 0; + } - for (var id in data) { - if (data.hasOwnProperty(id)) { - var item = data[id]; - var itemField = item[field]; - if (itemField != null && (!min || itemField < minField)) { - min = item; - minField = itemField; - } - } - } + /** + * Get the vertical mouse position from a mouse event + * @param {Event} event + * @return {Number} mouse y + */ + function getMouseY (event) { + if ('clientY' in event) return event.clientY; + return event.targetTouches[0] && event.targetTouches[0].clientY || 0; + } - return min; - }; + module.exports = Graph3d; + + +/***/ }, +/* 7 */ +/***/ function(module, exports, __webpack_require__) { + + var Point3d = __webpack_require__(10); /** - * Find all distinct values of a specified field - * @param {String} field - * @return {Array} values Array containing all distinct values. If data items - * do not contain the specified field are ignored. - * The returned array is unordered. + * @class Camera + * The camera is mounted on a (virtual) camera arm. The camera arm can rotate + * The camera is always looking in the direction of the origin of the arm. + * This way, the camera always rotates around one fixed point, the location + * of the camera arm. + * + * Documentation: + * http://en.wikipedia.org/wiki/3D_projection */ - DataSet.prototype.distinct = function (field) { - var data = this._data; - var values = []; - var fieldType = this._options.type && this._options.type[field] || null; - var count = 0; - var i; + function Camera() { + this.armLocation = new Point3d(); + this.armRotation = {}; + this.armRotation.horizontal = 0; + this.armRotation.vertical = 0; + this.armLength = 1.7; - for (var prop in data) { - if (data.hasOwnProperty(prop)) { - var item = data[prop]; - var value = item[field]; - var exists = false; - for (i = 0; i < count; i++) { - if (values[i] == value) { - exists = true; - break; - } - } - if (!exists && (value !== undefined)) { - values[count] = value; - count++; - } - } - } + this.cameraLocation = new Point3d(); + this.cameraRotation = new Point3d(0.5*Math.PI, 0, 0); - if (fieldType) { - for (i = 0; i < values.length; i++) { - values[i] = util.convert(values[i], fieldType); - } - } + this.calculateCameraOrientation(); + } - return values; + /** + * Set the location (origin) of the arm + * @param {Number} x Normalized value of x + * @param {Number} y Normalized value of y + * @param {Number} z Normalized value of z + */ + Camera.prototype.setArmLocation = function(x, y, z) { + this.armLocation.x = x; + this.armLocation.y = y; + this.armLocation.z = z; + + this.calculateCameraOrientation(); }; /** - * Add a single item. Will fail when an item with the same id already exists. - * @param {Object} item - * @return {String} id - * @private + * Set the rotation of the camera arm + * @param {Number} horizontal The horizontal rotation, between 0 and 2*PI. + * Optional, can be left undefined. + * @param {Number} vertical The vertical rotation, between 0 and 0.5*PI + * if vertical=0.5*PI, the graph is shown from the + * top. Optional, can be left undefined. */ - DataSet.prototype._addItem = function (item) { - var id = item[this._fieldId]; - - if (id != undefined) { - // check whether this id is already taken - if (this._data[id]) { - // item already exists - throw new Error('Cannot add item: item with id ' + id + ' already exists'); - } - } - else { - // generate an id - id = util.randomUUID(); - item[this._fieldId] = id; + Camera.prototype.setArmRotation = function(horizontal, vertical) { + if (horizontal !== undefined) { + this.armRotation.horizontal = horizontal; } - var d = {}; - for (var field in item) { - if (item.hasOwnProperty(field)) { - var fieldType = this._type[field]; // type may be undefined - d[field] = util.convert(item[field], fieldType); - } + if (vertical !== undefined) { + this.armRotation.vertical = vertical; + if (this.armRotation.vertical < 0) this.armRotation.vertical = 0; + if (this.armRotation.vertical > 0.5*Math.PI) this.armRotation.vertical = 0.5*Math.PI; } - this._data[id] = d; - this.length++; - return id; + if (horizontal !== undefined || vertical !== undefined) { + this.calculateCameraOrientation(); + } }; /** - * Get an item. Fields can be converted to a specific type - * @param {String} id - * @param {Object.} [types] field types to convert - * @return {Object | null} item - * @private + * Retrieve the current arm rotation + * @return {object} An object with parameters horizontal and vertical */ - DataSet.prototype._getItem = function (id, types) { - var field, value; - - // get the item from the dataset - var raw = this._data[id]; - if (!raw) { - return null; - } + Camera.prototype.getArmRotation = function() { + var rot = {}; + rot.horizontal = this.armRotation.horizontal; + rot.vertical = this.armRotation.vertical; - // convert the items field types - var converted = {}; - if (types) { - for (field in raw) { - if (raw.hasOwnProperty(field)) { - value = raw[field]; - converted[field] = util.convert(value, types[field]); - } - } - } - else { - // no field types specified, no converting needed - for (field in raw) { - if (raw.hasOwnProperty(field)) { - value = raw[field]; - converted[field] = value; - } - } - } - return converted; + return rot; }; /** - * Update a single item: merge with existing item. - * Will fail when the item has no id, or when there does not exist an item - * with the same id. - * @param {Object} item - * @return {String} id - * @private + * Set the (normalized) length of the camera arm. + * @param {Number} length A length between 0.71 and 5.0 */ - DataSet.prototype._updateItem = function (item) { - var id = item[this._fieldId]; - if (id == undefined) { - throw new Error('Cannot update item: item has no id (item: ' + JSON.stringify(item) + ')'); - } - var d = this._data[id]; - if (!d) { - // item doesn't exist - throw new Error('Cannot update item: no item with id ' + id + ' found'); - } + Camera.prototype.setArmLength = function(length) { + if (length === undefined) + return; - // merge with current item - for (var field in item) { - if (item.hasOwnProperty(field)) { - var fieldType = this._type[field]; // type may be undefined - d[field] = util.convert(item[field], fieldType); - } - } + this.armLength = length; - return id; + // Radius must be larger than the corner of the graph, + // which has a distance of sqrt(0.5^2+0.5^2) = 0.71 from the center of the + // graph + if (this.armLength < 0.71) this.armLength = 0.71; + if (this.armLength > 5.0) this.armLength = 5.0; + + this.calculateCameraOrientation(); }; /** - * Get an array with the column names of a Google DataTable - * @param {DataTable} dataTable - * @return {String[]} columnNames - * @private + * Retrieve the arm length + * @return {Number} length */ - DataSet.prototype._getColumnNames = function (dataTable) { - var columns = []; - for (var col = 0, cols = dataTable.getNumberOfColumns(); col < cols; col++) { - columns[col] = dataTable.getColumnId(col) || dataTable.getColumnLabel(col); - } - return columns; + Camera.prototype.getArmLength = function() { + return this.armLength; }; /** - * Append an item as a row to the dataTable - * @param dataTable - * @param columns - * @param item - * @private + * Retrieve the camera location + * @return {Point3d} cameraLocation */ - DataSet.prototype._appendRow = function (dataTable, columns, item) { - var row = dataTable.addRow(); + Camera.prototype.getCameraLocation = function() { + return this.cameraLocation; + }; - for (var col = 0, cols = columns.length; col < cols; col++) { - var field = columns[col]; - dataTable.setValue(row, col, item[field]); - } + /** + * Retrieve the camera rotation + * @return {Point3d} cameraRotation + */ + Camera.prototype.getCameraRotation = function() { + return this.cameraRotation; }; - module.exports = DataSet; + /** + * Calculate the location and rotation of the camera based on the + * position and orientation of the camera arm + */ + Camera.prototype.calculateCameraOrientation = function() { + // calculate location of the camera + this.cameraLocation.x = this.armLocation.x - this.armLength * Math.sin(this.armRotation.horizontal) * Math.cos(this.armRotation.vertical); + this.cameraLocation.y = this.armLocation.y - this.armLength * Math.cos(this.armRotation.horizontal) * Math.cos(this.armRotation.vertical); + this.cameraLocation.z = this.armLocation.z + this.armLength * Math.sin(this.armRotation.vertical); + + // calculate rotation of the camera + this.cameraRotation.x = Math.PI/2 - this.armRotation.vertical; + this.cameraRotation.y = 0; + this.cameraRotation.z = -this.armRotation.horizontal; + }; + module.exports = Camera; /***/ }, /* 8 */ /***/ function(module, exports, __webpack_require__) { + var DataView = __webpack_require__(4); + /** - * A queue - * @param {Object} options - * Available options: - * - delay: number When provided, the queue will be flushed - * automatically after an inactivity of this delay - * in milliseconds. - * Default value is null. - * - max: number When the queue exceeds the given maximum number - * of entries, the queue is flushed automatically. - * Default value of max is Infinity. - * @constructor + * @class Filter + * + * @param {DataSet} data The google data table + * @param {Number} column The index of the column to be filtered + * @param {Graph} graph The graph */ - function Queue(options) { - // options - this.delay = null; - this.max = Infinity; + function Filter (data, column, graph) { + this.data = data; + this.column = column; + this.graph = graph; // the parent graph - // properties - this._queue = []; - this._timeout = null; - this._extended = null; + this.index = undefined; + this.value = undefined; - this.setOptions(options); - } + // read all distinct values and select the first one + this.values = graph.getDistinctValues(data.get(), this.column); - /** - * Update the configuration of the queue - * @param {Object} options - * Available options: - * - delay: number When provided, the queue will be flushed - * automatically after an inactivity of this delay - * in milliseconds. - * Default value is null. - * - max: number When the queue exceeds the given maximum number - * of entries, the queue is flushed automatically. - * Default value of max is Infinity. - * @param options - */ - Queue.prototype.setOptions = function (options) { - if (options && typeof options.delay !== 'undefined') { - this.delay = options.delay; - } - if (options && typeof options.max !== 'undefined') { - this.max = options.max; + // sort both numeric and string values correctly + this.values.sort(function (a, b) { + return a > b ? 1 : a < b ? -1 : 0; + }); + + if (this.values.length > 0) { + this.selectValue(0); } - this._flushIfNeeded(); - }; + // create an array with the filtered datapoints. this will be loaded afterwards + this.dataPoints = []; - /** - * Extend an object with queuing functionality. - * The object will be extended with a function flush, and the methods provided - * in options.replace will be replaced with queued ones. - * @param {Object} object - * @param {Object} options - * Available options: - * - replace: Array. - * A list with method names of the methods - * on the object to be replaced with queued ones. - * - delay: number When provided, the queue will be flushed - * automatically after an inactivity of this delay - * in milliseconds. - * Default value is null. - * - max: number When the queue exceeds the given maximum number - * of entries, the queue is flushed automatically. - * Default value of max is Infinity. - * @return {Queue} Returns the created queue - */ - Queue.extend = function (object, options) { - var queue = new Queue(options); + this.loaded = false; + this.onLoadCallback = undefined; - if (object.flush !== undefined) { - throw new Error('Target object already has a property flush'); + if (graph.animationPreload) { + this.loaded = false; + this.loadInBackground(); } - object.flush = function () { - queue.flush(); - }; - - var methods = [{ - name: 'flush', - original: undefined - }]; - - if (options && options.replace) { - for (var i = 0; i < options.replace.length; i++) { - var name = options.replace[i]; - methods.push({ - name: name, - original: object[name] - }); - queue.replace(object, name); - } + else { + this.loaded = true; } + }; - queue._extended = { - object: object, - methods: methods - }; - return queue; + /** + * Return the label + * @return {string} label + */ + Filter.prototype.isLoaded = function() { + return this.loaded; }; + /** - * Destroy the queue. The queue will first flush all queued actions, and in - * case it has extended an object, will restore the original object. + * Return the loaded progress + * @return {Number} percentage between 0 and 100 */ - Queue.prototype.destroy = function () { - this.flush(); + Filter.prototype.getLoadedProgress = function() { + var len = this.values.length; - if (this._extended) { - var object = this._extended.object; - var methods = this._extended.methods; - for (var i = 0; i < methods.length; i++) { - var method = methods[i]; - if (method.original) { - object[method.name] = method.original; - } - else { - delete object[method.name]; - } - } - this._extended = null; + var i = 0; + while (this.dataPoints[i]) { + i++; } + + return Math.round(i / len * 100); }; + /** - * Replace a method on an object with a queued version - * @param {Object} object Object having the method - * @param {string} method The method name + * Return the label + * @return {string} label */ - Queue.prototype.replace = function(object, method) { - var me = this; - var original = object[method]; - if (!original) { - throw new Error('Method ' + method + ' undefined'); - } + Filter.prototype.getLabel = function() { + return this.graph.filterLabel; + }; - object[method] = function () { - // create an Array with the arguments - var args = []; - for (var i = 0; i < arguments.length; i++) { - args[i] = arguments[i]; - } - // add this call to the queue - me.queue({ - args: args, - fn: original, - context: this - }); - }; + /** + * Return the columnIndex of the filter + * @return {Number} columnIndex + */ + Filter.prototype.getColumn = function() { + return this.column; }; /** - * Queue a call - * @param {function | {fn: function, args: Array} | {fn: function, args: Array, context: Object}} entry + * Return the currently selected value. Returns undefined if there is no selection + * @return {*} value */ - Queue.prototype.queue = function(entry) { - if (typeof entry === 'function') { - this._queue.push({fn: entry}); - } - else { - this._queue.push(entry); - } + Filter.prototype.getSelectedValue = function() { + if (this.index === undefined) + return undefined; - this._flushIfNeeded(); + return this.values[this.index]; }; /** - * Check whether the queue needs to be flushed - * @private + * Retrieve all values of the filter + * @return {Array} values */ - Queue.prototype._flushIfNeeded = function () { - // flush when the maximum is exceeded. - if (this._queue.length > this.max) { - this.flush(); - } + Filter.prototype.getValues = function() { + return this.values; + }; - // flush after a period of inactivity when a delay is configured - clearTimeout(this._timeout); - if (this.queue.length > 0 && typeof this.delay === 'number') { - var me = this; - this._timeout = setTimeout(function () { - me.flush(); - }, this.delay); - } + /** + * Retrieve one value of the filter + * @param {Number} index + * @return {*} value + */ + Filter.prototype.getValue = function(index) { + if (index >= this.values.length) + throw 'Error: index out of range'; + + return this.values[index]; }; + /** - * Flush all queued calls + * Retrieve the (filtered) dataPoints for the currently selected filter index + * @param {Number} [index] (optional) + * @return {Array} dataPoints */ - Queue.prototype.flush = function () { - while (this._queue.length > 0) { - var entry = this._queue.shift(); - entry.fn.apply(entry.context || entry.fn, entry.args || []); + Filter.prototype._getDataPoints = function(index) { + if (index === undefined) + index = this.index; + + if (index === undefined) + return []; + + var dataPoints; + if (this.dataPoints[index]) { + dataPoints = this.dataPoints[index]; } - }; + else { + var f = {}; + f.column = this.column; + f.value = this.values[index]; - module.exports = Queue; + var dataView = new DataView(this.data,{filter: function (item) {return (item[f.column] == f.value);}}).get(); + dataPoints = this.graph._getDataPoints(dataView); + this.dataPoints[index] = dataPoints; + } + + return dataPoints; + }; -/***/ }, -/* 9 */ -/***/ function(module, exports, __webpack_require__) { - var util = __webpack_require__(1); - var DataSet = __webpack_require__(7); /** - * DataView - * - * a dataview offers a filtered view on a dataset or an other dataview. - * - * @param {DataSet | DataView} data - * @param {Object} [options] Available options: see method get - * - * @constructor DataView + * Set a callback function when the filter is fully loaded. */ - function DataView (data, options) { - this._data = null; - this._ids = {}; // ids of the items currently in memory (just contains a boolean true) - this.length = 0; // number of items in the DataView - this._options = options || {}; - this._fieldId = 'id'; // name of the field containing id - this._subscribers = {}; // event subscribers + Filter.prototype.setOnLoadCallback = function(callback) { + this.onLoadCallback = callback; + }; - var me = this; - this.listener = function () { - me._onEvent.apply(me, arguments); - }; - this.setData(data); - } + /** + * Add a value to the list with available values for this filter + * No double entries will be created. + * @param {Number} index + */ + Filter.prototype.selectValue = function(index) { + if (index >= this.values.length) + throw 'Error: index out of range'; - // TODO: implement a function .config() to dynamically update things like configured filter - // and trigger changes accordingly + this.index = index; + this.value = this.values[index]; + }; /** - * Set a data source for the view - * @param {DataSet | DataView} data + * Load all filtered rows in the background one by one + * Start this method without providing an index! */ - DataView.prototype.setData = function (data) { - var ids, i, len; + Filter.prototype.loadInBackground = function(index) { + if (index === undefined) + index = 0; - if (this._data) { - // unsubscribe from current dataset - if (this._data.unsubscribe) { - this._data.unsubscribe('*', this.listener); - } + var frame = this.graph.frame; - // trigger a remove of all items in memory - ids = []; - for (var id in this._ids) { - if (this._ids.hasOwnProperty(id)) { - ids.push(id); - } - } - this._ids = {}; - this.length = 0; - this._trigger('remove', {items: ids}); - } + if (index < this.values.length) { + var dataPointsTemp = this._getDataPoints(index); + //this.graph.redrawInfo(); // TODO: not neat - this._data = data; + // create a progress box + if (frame.progress === undefined) { + frame.progress = document.createElement('DIV'); + frame.progress.style.position = 'absolute'; + frame.progress.style.color = 'gray'; + frame.appendChild(frame.progress); + } + var progress = this.getLoadedProgress(); + frame.progress.innerHTML = 'Loading animation... ' + progress + '%'; + // TODO: this is no nice solution... + frame.progress.style.bottom = 60 + 'px'; // TODO: use height of slider + frame.progress.style.left = 10 + 'px'; - if (this._data) { - // update fieldId - this._fieldId = this._options.fieldId || - (this._data && this._data.options && this._data.options.fieldId) || - 'id'; + var me = this; + setTimeout(function() {me.loadInBackground(index+1);}, 10); + this.loaded = false; + } + else { + this.loaded = true; - // trigger an add of all added items - ids = this._data.getIds({filter: this._options && this._options.filter}); - for (i = 0, len = ids.length; i < len; i++) { - id = ids[i]; - this._ids[id] = true; + // remove the progress box + if (frame.progress !== undefined) { + frame.removeChild(frame.progress); + frame.progress = undefined; } - this.length = ids.length; - this._trigger('add', {items: ids}); - // subscribe to new dataset - if (this._data.on) { - this._data.on('*', this.listener); - } + if (this.onLoadCallback) + this.onLoadCallback(); } }; + module.exports = Filter; + + +/***/ }, +/* 9 */ +/***/ function(module, exports, __webpack_require__) { + /** - * Get data from the data view - * - * Usage: - * - * get() - * get(options: Object) - * get(options: Object, data: Array | DataTable) - * - * get(id: Number) - * get(id: Number, options: Object) - * get(id: Number, options: Object, data: Array | DataTable) - * - * get(ids: Number[]) - * get(ids: Number[], options: Object) - * get(ids: Number[], options: Object, data: Array | DataTable) - * - * Where: - * - * {Number | String} id The id of an item - * {Number[] | String{}} ids An array with ids of items - * {Object} options An Object with options. Available options: - * {String} [type] Type of data to be returned. Can - * be 'DataTable' or 'Array' (default) - * {Object.} [convert] - * {String[]} [fields] field names to be returned - * {function} [filter] filter items - * {String | function} [order] Order the items by - * a field name or custom sort function. - * {Array | DataTable} [data] If provided, items will be appended to this - * array or table. Required in case of Google - * DataTable. - * @param args + * @prototype Point2d + * @param {Number} [x] + * @param {Number} [y] */ - DataView.prototype.get = function (args) { - var me = this; - - // parse the arguments - var ids, options, data; - var firstType = util.getType(arguments[0]); - if (firstType == 'String' || firstType == 'Number' || firstType == 'Array') { - // get(id(s) [, options] [, data]) - ids = arguments[0]; // can be a single id or an array with ids - options = arguments[1]; - data = arguments[2]; - } - else { - // get([, options] [, data]) - options = arguments[0]; - data = arguments[1]; - } + function Point2d (x, y) { + this.x = x !== undefined ? x : 0; + this.y = y !== undefined ? y : 0; + } - // extend the options with the default options and provided options - var viewOptions = util.extend({}, this._options, options); + module.exports = Point2d; - // create a combined filter method when needed - if (this._options.filter && options && options.filter) { - viewOptions.filter = function (item) { - return me._options.filter(item) && options.filter(item); - } - } - // build up the call to the linked data set - var getArguments = []; - if (ids != undefined) { - getArguments.push(ids); - } - getArguments.push(viewOptions); - getArguments.push(data); +/***/ }, +/* 10 */ +/***/ function(module, exports, __webpack_require__) { - return this._data && this._data.get.apply(this._data, getArguments); + /** + * @prototype Point3d + * @param {Number} [x] + * @param {Number} [y] + * @param {Number} [z] + */ + function Point3d(x, y, z) { + this.x = x !== undefined ? x : 0; + this.y = y !== undefined ? y : 0; + this.z = z !== undefined ? z : 0; }; /** - * Get ids of all items or from a filtered set of items. - * @param {Object} [options] An Object with options. Available options: - * {function} [filter] filter items - * {String | function} [order] Order the items by - * a field name or custom sort function. - * @return {Array} ids + * Subtract the two provided points, returns a-b + * @param {Point3d} a + * @param {Point3d} b + * @return {Point3d} a-b */ - DataView.prototype.getIds = function (options) { - var ids; - - if (this._data) { - var defaultFilter = this._options.filter; - var filter; - - if (options && options.filter) { - if (defaultFilter) { - filter = function (item) { - return defaultFilter(item) && options.filter(item); - } - } - else { - filter = options.filter; - } - } - else { - filter = defaultFilter; - } - - ids = this._data.getIds({ - filter: filter, - order: options && options.order - }); - } - else { - ids = []; - } + Point3d.subtract = function(a, b) { + var sub = new Point3d(); + sub.x = a.x - b.x; + sub.y = a.y - b.y; + sub.z = a.z - b.z; + return sub; + }; - return ids; + /** + * Add the two provided points, returns a+b + * @param {Point3d} a + * @param {Point3d} b + * @return {Point3d} a+b + */ + Point3d.add = function(a, b) { + var sum = new Point3d(); + sum.x = a.x + b.x; + sum.y = a.y + b.y; + sum.z = a.z + b.z; + return sum; }; /** - * Get the DataSet to which this DataView is connected. In case there is a chain - * of multiple DataViews, the root DataSet of this chain is returned. - * @return {DataSet} dataSet + * Calculate the average of two 3d points + * @param {Point3d} a + * @param {Point3d} b + * @return {Point3d} The average, (a+b)/2 */ - DataView.prototype.getDataSet = function () { - var dataSet = this; - while (dataSet instanceof DataView) { - dataSet = dataSet._data; - } - return dataSet || null; + Point3d.avg = function(a, b) { + return new Point3d( + (a.x + b.x) / 2, + (a.y + b.y) / 2, + (a.z + b.z) / 2 + ); }; /** - * Event listener. Will propagate all events from the connected data set to - * the subscribers of the DataView, but will filter the items and only trigger - * when there are changes in the filtered data set. - * @param {String} event - * @param {Object | null} params - * @param {String} senderId - * @private + * Calculate the cross product of the two provided points, returns axb + * Documentation: http://en.wikipedia.org/wiki/Cross_product + * @param {Point3d} a + * @param {Point3d} b + * @return {Point3d} cross product axb */ - DataView.prototype._onEvent = function (event, params, senderId) { - var i, len, id, item, - ids = params && params.items, - data = this._data, - added = [], - updated = [], - removed = []; - - if (ids && data) { - switch (event) { - case 'add': - // filter the ids of the added items - for (i = 0, len = ids.length; i < len; i++) { - id = ids[i]; - item = this.get(id); - if (item) { - this._ids[id] = true; - added.push(id); - } - } - - break; - - case 'update': - // determine the event from the views viewpoint: an updated - // item can be added, updated, or removed from this view. - for (i = 0, len = ids.length; i < len; i++) { - id = ids[i]; - item = this.get(id); - - if (item) { - if (this._ids[id]) { - updated.push(id); - } - else { - this._ids[id] = true; - added.push(id); - } - } - else { - if (this._ids[id]) { - delete this._ids[id]; - removed.push(id); - } - else { - // nothing interesting for me :-( - } - } - } - - break; + Point3d.crossProduct = function(a, b) { + var crossproduct = new Point3d(); - case 'remove': - // filter the ids of the removed items - for (i = 0, len = ids.length; i < len; i++) { - id = ids[i]; - if (this._ids[id]) { - delete this._ids[id]; - removed.push(id); - } - } + crossproduct.x = a.y * b.z - a.z * b.y; + crossproduct.y = a.z * b.x - a.x * b.z; + crossproduct.z = a.x * b.y - a.y * b.x; - break; - } + return crossproduct; + }; - this.length += added.length - removed.length; - if (added.length) { - this._trigger('add', {items: added}, senderId); - } - if (updated.length) { - this._trigger('update', {items: updated}, senderId); - } - if (removed.length) { - this._trigger('remove', {items: removed}, senderId); - } - } + /** + * Rtrieve the length of the vector (or the distance from this point to the origin + * @return {Number} length + */ + Point3d.prototype.length = function() { + return Math.sqrt( + this.x * this.x + + this.y * this.y + + this.z * this.z + ); }; - // copy subscription functionality from DataSet - DataView.prototype.on = DataSet.prototype.on; - DataView.prototype.off = DataSet.prototype.off; - DataView.prototype._trigger = DataSet.prototype._trigger; - - // TODO: make these functions deprecated (replaced with `on` and `off` since version 0.5) - DataView.prototype.subscribe = DataView.prototype.on; - DataView.prototype.unsubscribe = DataView.prototype.off; + module.exports = Point3d; - module.exports = DataView; /***/ }, -/* 10 */ +/* 11 */ /***/ function(module, exports, __webpack_require__) { - var Emitter = __webpack_require__(11); - var DataSet = __webpack_require__(7); - var DataView = __webpack_require__(9); var util = __webpack_require__(1); - var Point3d = __webpack_require__(12); - var Point2d = __webpack_require__(13); - var Camera = __webpack_require__(14); - var Filter = __webpack_require__(15); - var Slider = __webpack_require__(16); - var StepNumber = __webpack_require__(17); /** - * @constructor Graph3d - * Graph3d displays data in 3d. - * - * Graph3d is developed in javascript as a Google Visualization Chart. + * @constructor Slider * - * @param {Element} container The DOM element in which the Graph3d will - * be created. Normally a div element. - * @param {DataSet | DataView | Array} [data] - * @param {Object} [options] + * An html slider control with start/stop/prev/next buttons + * @param {Element} container The element where the slider will be created + * @param {Object} options Available options: + * {boolean} visible If true (default) the + * slider is visible. */ - function Graph3d(container, data, options) { - if (!(this instanceof Graph3d)) { - throw new SyntaxError('Constructor must be called with the new operator'); + function Slider(container, options) { + if (container === undefined) { + throw 'Error: No container element defined'; } + this.container = container; + this.visible = (options && options.visible != undefined) ? options.visible : true; - // create variables and set default values - this.containerElement = container; - this.width = '400px'; - this.height = '400px'; - this.margin = 10; // px - this.defaultXCenter = '55%'; - this.defaultYCenter = '50%'; - - this.xLabel = 'x'; - this.yLabel = 'y'; - this.zLabel = 'z'; - - var passValueFn = function(v) { return v; }; - this.xValueLabel = passValueFn; - this.yValueLabel = passValueFn; - this.zValueLabel = passValueFn; - - this.filterLabel = 'time'; - this.legendLabel = 'value'; + if (this.visible) { + this.frame = document.createElement('DIV'); + //this.frame.style.backgroundColor = '#E5E5E5'; + this.frame.style.width = '100%'; + this.frame.style.position = 'relative'; + this.container.appendChild(this.frame); - this.style = Graph3d.STYLE.DOT; - this.showPerspective = true; - this.showGrid = true; - this.keepAspectRatio = true; - this.showShadow = false; - this.showGrayBottom = false; // TODO: this does not work correctly - this.showTooltip = false; - this.verticalRatio = 0.5; // 0.1 to 1.0, where 1.0 results in a 'cube' + this.frame.prev = document.createElement('INPUT'); + this.frame.prev.type = 'BUTTON'; + this.frame.prev.value = 'Prev'; + this.frame.appendChild(this.frame.prev); - this.animationInterval = 1000; // milliseconds - this.animationPreload = false; + this.frame.play = document.createElement('INPUT'); + this.frame.play.type = 'BUTTON'; + this.frame.play.value = 'Play'; + this.frame.appendChild(this.frame.play); - this.camera = new Camera(); - this.eye = new Point3d(0, 0, -1); // TODO: set eye.z about 3/4 of the width of the window? + this.frame.next = document.createElement('INPUT'); + this.frame.next.type = 'BUTTON'; + this.frame.next.value = 'Next'; + this.frame.appendChild(this.frame.next); - this.dataTable = null; // The original data table - this.dataPoints = null; // The table with point objects + this.frame.bar = document.createElement('INPUT'); + this.frame.bar.type = 'BUTTON'; + this.frame.bar.style.position = 'absolute'; + this.frame.bar.style.border = '1px solid red'; + this.frame.bar.style.width = '100px'; + this.frame.bar.style.height = '6px'; + this.frame.bar.style.borderRadius = '2px'; + this.frame.bar.style.MozBorderRadius = '2px'; + this.frame.bar.style.border = '1px solid #7F7F7F'; + this.frame.bar.style.backgroundColor = '#E5E5E5'; + this.frame.appendChild(this.frame.bar); - // the column indexes - this.colX = undefined; - this.colY = undefined; - this.colZ = undefined; - this.colValue = undefined; - this.colFilter = undefined; + this.frame.slide = document.createElement('INPUT'); + this.frame.slide.type = 'BUTTON'; + this.frame.slide.style.margin = '0px'; + this.frame.slide.value = ' '; + this.frame.slide.style.position = 'relative'; + this.frame.slide.style.left = '-100px'; + this.frame.appendChild(this.frame.slide); - this.xMin = 0; - this.xStep = undefined; // auto by default - this.xMax = 1; - this.yMin = 0; - this.yStep = undefined; // auto by default - this.yMax = 1; - this.zMin = 0; - this.zStep = undefined; // auto by default - this.zMax = 1; - this.valueMin = 0; - this.valueMax = 1; - this.xBarWidth = 1; - this.yBarWidth = 1; - // TODO: customize axis range + // create events + var me = this; + this.frame.slide.onmousedown = function (event) {me._onMouseDown(event);}; + this.frame.prev.onclick = function (event) {me.prev(event);}; + this.frame.play.onclick = function (event) {me.togglePlay(event);}; + this.frame.next.onclick = function (event) {me.next(event);}; + } - // constants - this.colorAxis = '#4D4D4D'; - this.colorGrid = '#D3D3D3'; - this.colorDot = '#7DC1FF'; - this.colorDotBorder = '#3267D2'; + this.onChangeCallback = undefined; - // create a frame and canvas - this.create(); + this.values = []; + this.index = undefined; - // apply options (also when undefined) - this.setOptions(options); + this.playTimeout = undefined; + this.playInterval = 1000; // milliseconds + this.playLoop = true; + } - // apply data - if (data) { - this.setData(data); + /** + * Select the previous index + */ + Slider.prototype.prev = function() { + var index = this.getIndex(); + if (index > 0) { + index--; + this.setIndex(index); } - } + }; - // Extend Graph3d with an Emitter mixin - Emitter(Graph3d.prototype); + /** + * Select the next index + */ + Slider.prototype.next = function() { + var index = this.getIndex(); + if (index < this.values.length - 1) { + index++; + this.setIndex(index); + } + }; /** - * Calculate the scaling values, dependent on the range in x, y, and z direction + * Select the next index */ - Graph3d.prototype._setScale = function() { - this.scale = new Point3d(1 / (this.xMax - this.xMin), - 1 / (this.yMax - this.yMin), - 1 / (this.zMax - this.zMin)); + Slider.prototype.playNext = function() { + var start = new Date(); - // keep aspect ration between x and y scale if desired - if (this.keepAspectRatio) { - if (this.scale.x < this.scale.y) { - //noinspection JSSuspiciousNameCombination - this.scale.y = this.scale.x; - } - else { - //noinspection JSSuspiciousNameCombination - this.scale.x = this.scale.y; - } + var index = this.getIndex(); + if (index < this.values.length - 1) { + index++; + this.setIndex(index); + } + else if (this.playLoop) { + // jump to the start + index = 0; + this.setIndex(index); } - // scale the vertical axis - this.scale.z *= this.verticalRatio; - // TODO: can this be automated? verticalRatio? + var end = new Date(); + var diff = (end - start); - // determine scale for (optional) value - this.scale.value = 1 / (this.valueMax - this.valueMin); + // calculate how much time it to to set the index and to execute the callback + // function. + var interval = Math.max(this.playInterval - diff, 0); + // document.title = diff // TODO: cleanup - // position the camera arm - var xCenter = (this.xMax + this.xMin) / 2 * this.scale.x; - var yCenter = (this.yMax + this.yMin) / 2 * this.scale.y; - var zCenter = (this.zMax + this.zMin) / 2 * this.scale.z; - this.camera.setArmLocation(xCenter, yCenter, zCenter); + var me = this; + this.playTimeout = setTimeout(function() {me.playNext();}, interval); }; - /** - * Convert a 3D location to a 2D location on screen - * http://en.wikipedia.org/wiki/3D_projection - * @param {Point3d} point3d A 3D point with parameters x, y, z - * @return {Point2d} point2d A 2D point with parameters x, y + * Toggle start or stop playing */ - Graph3d.prototype._convert3Dto2D = function(point3d) { - var translation = this._convertPointToTranslation(point3d); - return this._convertTranslationToScreen(translation); + Slider.prototype.togglePlay = function() { + if (this.playTimeout === undefined) { + this.play(); + } else { + this.stop(); + } }; /** - * Convert a 3D location its translation seen from the camera - * http://en.wikipedia.org/wiki/3D_projection - * @param {Point3d} point3d A 3D point with parameters x, y, z - * @return {Point3d} translation A 3D point with parameters x, y, z This is - * the translation of the point, seen from the - * camera + * Start playing */ - Graph3d.prototype._convertPointToTranslation = function(point3d) { - var ax = point3d.x * this.scale.x, - ay = point3d.y * this.scale.y, - az = point3d.z * this.scale.z, - - cx = this.camera.getCameraLocation().x, - cy = this.camera.getCameraLocation().y, - cz = this.camera.getCameraLocation().z, - - // calculate angles - sinTx = Math.sin(this.camera.getCameraRotation().x), - cosTx = Math.cos(this.camera.getCameraRotation().x), - sinTy = Math.sin(this.camera.getCameraRotation().y), - cosTy = Math.cos(this.camera.getCameraRotation().y), - sinTz = Math.sin(this.camera.getCameraRotation().z), - cosTz = Math.cos(this.camera.getCameraRotation().z), + Slider.prototype.play = function() { + // Test whether already playing + if (this.playTimeout) return; - // calculate translation - dx = cosTy * (sinTz * (ay - cy) + cosTz * (ax - cx)) - sinTy * (az - cz), - dy = sinTx * (cosTy * (az - cz) + sinTy * (sinTz * (ay - cy) + cosTz * (ax - cx))) + cosTx * (cosTz * (ay - cy) - sinTz * (ax-cx)), - dz = cosTx * (cosTy * (az - cz) + sinTy * (sinTz * (ay - cy) + cosTz * (ax - cx))) - sinTx * (cosTz * (ay - cy) - sinTz * (ax-cx)); + this.playNext(); - return new Point3d(dx, dy, dz); + if (this.frame) { + this.frame.play.value = 'Stop'; + } }; /** - * Convert a translation point to a point on the screen - * @param {Point3d} translation A 3D point with parameters x, y, z This is - * the translation of the point, seen from the - * camera - * @return {Point2d} point2d A 2D point with parameters x, y + * Stop playing */ - Graph3d.prototype._convertTranslationToScreen = function(translation) { - var ex = this.eye.x, - ey = this.eye.y, - ez = this.eye.z, - dx = translation.x, - dy = translation.y, - dz = translation.z; + Slider.prototype.stop = function() { + clearInterval(this.playTimeout); + this.playTimeout = undefined; - // calculate position on screen from translation - var bx; - var by; - if (this.showPerspective) { - bx = (dx - ex) * (ez / dz); - by = (dy - ey) * (ez / dz); - } - else { - bx = dx * -(ez / this.camera.getArmLength()); - by = dy * -(ez / this.camera.getArmLength()); + if (this.frame) { + this.frame.play.value = 'Play'; } + }; - // shift and scale the point to the center of the screen - // use the width of the graph to scale both horizontally and vertically. - return new Point2d( - this.xcenter + bx * this.frame.canvas.clientWidth, - this.ycenter - by * this.frame.canvas.clientWidth); + /** + * Set a callback function which will be triggered when the value of the + * slider bar has changed. + */ + Slider.prototype.setOnChangeCallback = function(callback) { + this.onChangeCallback = callback; }; /** - * Set the background styling for the graph - * @param {string | {fill: string, stroke: string, strokeWidth: string}} backgroundColor + * Set the interval for playing the list + * @param {Number} interval The interval in milliseconds */ - Graph3d.prototype._setBackgroundColor = function(backgroundColor) { - var fill = 'white'; - var stroke = 'gray'; - var strokeWidth = 1; + Slider.prototype.setPlayInterval = function(interval) { + this.playInterval = interval; + }; - if (typeof(backgroundColor) === 'string') { - fill = backgroundColor; - stroke = 'none'; - strokeWidth = 0; - } - else if (typeof(backgroundColor) === 'object') { - if (backgroundColor.fill !== undefined) fill = backgroundColor.fill; - if (backgroundColor.stroke !== undefined) stroke = backgroundColor.stroke; - if (backgroundColor.strokeWidth !== undefined) strokeWidth = backgroundColor.strokeWidth; - } - else if (backgroundColor === undefined) { - // use use defaults - } - else { - throw 'Unsupported type of backgroundColor'; - } + /** + * Retrieve the current play interval + * @return {Number} interval The interval in milliseconds + */ + Slider.prototype.getPlayInterval = function(interval) { + return this.playInterval; + }; - this.frame.style.backgroundColor = fill; - this.frame.style.borderColor = stroke; - this.frame.style.borderWidth = strokeWidth + 'px'; - this.frame.style.borderStyle = 'solid'; + /** + * Set looping on or off + * @pararm {boolean} doLoop If true, the slider will jump to the start when + * the end is passed, and will jump to the end + * when the start is passed. + */ + Slider.prototype.setPlayLoop = function(doLoop) { + this.playLoop = doLoop; }; - /// enumerate the available styles - Graph3d.STYLE = { - BAR: 0, - BARCOLOR: 1, - BARSIZE: 2, - DOT : 3, - DOTLINE : 4, - DOTCOLOR: 5, - DOTSIZE: 6, - GRID : 7, - LINE: 8, - SURFACE : 9 + /** + * Execute the onchange callback function + */ + Slider.prototype.onChange = function() { + if (this.onChangeCallback !== undefined) { + this.onChangeCallback(); + } }; /** - * Retrieve the style index from given styleName - * @param {string} styleName Style name such as 'dot', 'grid', 'dot-line' - * @return {Number} styleNumber Enumeration value representing the style, or -1 - * when not found + * redraw the slider on the correct place */ - Graph3d.prototype._getStyleNumber = function(styleName) { - switch (styleName) { - case 'dot': return Graph3d.STYLE.DOT; - case 'dot-line': return Graph3d.STYLE.DOTLINE; - case 'dot-color': return Graph3d.STYLE.DOTCOLOR; - case 'dot-size': return Graph3d.STYLE.DOTSIZE; - case 'line': return Graph3d.STYLE.LINE; - case 'grid': return Graph3d.STYLE.GRID; - case 'surface': return Graph3d.STYLE.SURFACE; - case 'bar': return Graph3d.STYLE.BAR; - case 'bar-color': return Graph3d.STYLE.BARCOLOR; - case 'bar-size': return Graph3d.STYLE.BARSIZE; + Slider.prototype.redraw = function() { + if (this.frame) { + // resize the bar + this.frame.bar.style.top = (this.frame.clientHeight/2 - + this.frame.bar.offsetHeight/2) + 'px'; + this.frame.bar.style.width = (this.frame.clientWidth - + this.frame.prev.clientWidth - + this.frame.play.clientWidth - + this.frame.next.clientWidth - 30) + 'px'; + + // position the slider button + var left = this.indexToLeft(this.index); + this.frame.slide.style.left = (left) + 'px'; } + }; - return -1; + + /** + * Set the list with values for the slider + * @param {Array} values A javascript array with values (any type) + */ + Slider.prototype.setValues = function(values) { + this.values = values; + + if (this.values.length > 0) + this.setIndex(0); + else + this.index = undefined; }; /** - * Determine the indexes of the data columns, based on the given style and data - * @param {DataSet} data - * @param {Number} style + * Select a value by its index + * @param {Number} index */ - Graph3d.prototype._determineColumnIndexes = function(data, style) { - if (this.style === Graph3d.STYLE.DOT || - this.style === Graph3d.STYLE.DOTLINE || - this.style === Graph3d.STYLE.LINE || - this.style === Graph3d.STYLE.GRID || - this.style === Graph3d.STYLE.SURFACE || - this.style === Graph3d.STYLE.BAR) { - // 3 columns expected, and optionally a 4th with filter values - this.colX = 0; - this.colY = 1; - this.colZ = 2; - this.colValue = undefined; - - if (data.getNumberOfColumns() > 3) { - this.colFilter = 3; - } - } - else if (this.style === Graph3d.STYLE.DOTCOLOR || - this.style === Graph3d.STYLE.DOTSIZE || - this.style === Graph3d.STYLE.BARCOLOR || - this.style === Graph3d.STYLE.BARSIZE) { - // 4 columns expected, and optionally a 5th with filter values - this.colX = 0; - this.colY = 1; - this.colZ = 2; - this.colValue = 3; + Slider.prototype.setIndex = function(index) { + if (index < this.values.length) { + this.index = index; - if (data.getNumberOfColumns() > 4) { - this.colFilter = 4; - } + this.redraw(); + this.onChange(); } else { - throw 'Unknown style "' + this.style + '"'; - } - }; - - Graph3d.prototype.getNumberOfRows = function(data) { - return data.length; - } - - - Graph3d.prototype.getNumberOfColumns = function(data) { - var counter = 0; - for (var column in data[0]) { - if (data[0].hasOwnProperty(column)) { - counter++; - } - } - return counter; - } - - - Graph3d.prototype.getDistinctValues = function(data, column) { - var distinctValues = []; - for (var i = 0; i < data.length; i++) { - if (distinctValues.indexOf(data[i][column]) == -1) { - distinctValues.push(data[i][column]); - } - } - return distinctValues; - } - - - Graph3d.prototype.getColumnRange = function(data,column) { - var minMax = {min:data[0][column],max:data[0][column]}; - for (var i = 0; i < data.length; i++) { - if (minMax.min > data[i][column]) { minMax.min = data[i][column]; } - if (minMax.max < data[i][column]) { minMax.max = data[i][column]; } + throw 'Error: index out of range'; } - return minMax; }; /** - * Initialize the data from the data table. Calculate minimum and maximum values - * and column index values - * @param {Array | DataSet | DataView} rawData The data containing the items for the Graph. - * @param {Number} style Style Number + * retrieve the index of the currently selected vaue + * @return {Number} index */ - Graph3d.prototype._dataInitialize = function (rawData, style) { - var me = this; - - // unsubscribe from the dataTable - if (this.dataSet) { - this.dataSet.off('*', this._onChange); - } + Slider.prototype.getIndex = function() { + return this.index; + }; - if (rawData === undefined) - return; - if (Array.isArray(rawData)) { - rawData = new DataSet(rawData); - } + /** + * retrieve the currently selected value + * @return {*} value + */ + Slider.prototype.get = function() { + return this.values[this.index]; + }; - var data; - if (rawData instanceof DataSet || rawData instanceof DataView) { - data = rawData.get(); - } - else { - throw new Error('Array, DataSet, or DataView expected'); - } - if (data.length == 0) - return; + Slider.prototype._onMouseDown = function(event) { + // only react on left mouse button down + var leftButtonDown = event.which ? (event.which === 1) : (event.button === 1); + if (!leftButtonDown) return; - this.dataSet = rawData; - this.dataTable = data; + this.startClientX = event.clientX; + this.startSlideX = parseFloat(this.frame.slide.style.left); - // subscribe to changes in the dataset - this._onChange = function () { - me.setData(me.dataSet); - }; - this.dataSet.on('*', this._onChange); + this.frame.style.cursor = 'move'; - // _determineColumnIndexes - // getNumberOfRows (points) - // getNumberOfColumns (x,y,z,v,t,t1,t2...) - // getDistinctValues (unique values?) - // getColumnRange + // add event listeners to handle moving the contents + // we store the function onmousemove and onmouseup in the graph, so we can + // remove the eventlisteners lateron in the function mouseUp() + var me = this; + this.onmousemove = function (event) {me._onMouseMove(event);}; + this.onmouseup = function (event) {me._onMouseUp(event);}; + util.addEventListener(document, 'mousemove', this.onmousemove); + util.addEventListener(document, 'mouseup', this.onmouseup); + util.preventDefault(event); + }; - // determine the location of x,y,z,value,filter columns - this.colX = 'x'; - this.colY = 'y'; - this.colZ = 'z'; - this.colValue = 'style'; - this.colFilter = 'filter'; + Slider.prototype.leftToIndex = function (left) { + var width = parseFloat(this.frame.bar.style.width) - + this.frame.slide.clientWidth - 10; + var x = left - 3; + var index = Math.round(x / width * (this.values.length-1)); + if (index < 0) index = 0; + if (index > this.values.length-1) index = this.values.length-1; - // check if a filter column is provided - if (data[0].hasOwnProperty('filter')) { - if (this.dataFilter === undefined) { - this.dataFilter = new Filter(rawData, this.colFilter, this); - this.dataFilter.setOnLoadCallback(function() {me.redraw();}); - } - } + return index; + }; + Slider.prototype.indexToLeft = function (index) { + var width = parseFloat(this.frame.bar.style.width) - + this.frame.slide.clientWidth - 10; - var withBars = this.style == Graph3d.STYLE.BAR || - this.style == Graph3d.STYLE.BARCOLOR || - this.style == Graph3d.STYLE.BARSIZE; + var x = index / (this.values.length-1) * width; + var left = x + 3; - // determine barWidth from data - if (withBars) { - if (this.defaultXBarWidth !== undefined) { - this.xBarWidth = this.defaultXBarWidth; - } - else { - var dataX = this.getDistinctValues(data,this.colX); - this.xBarWidth = (dataX[1] - dataX[0]) || 1; - } + return left; + }; - if (this.defaultYBarWidth !== undefined) { - this.yBarWidth = this.defaultYBarWidth; - } - else { - var dataY = this.getDistinctValues(data,this.colY); - this.yBarWidth = (dataY[1] - dataY[0]) || 1; - } - } - // calculate minimums and maximums - var xRange = this.getColumnRange(data,this.colX); - if (withBars) { - xRange.min -= this.xBarWidth / 2; - xRange.max += this.xBarWidth / 2; - } - this.xMin = (this.defaultXMin !== undefined) ? this.defaultXMin : xRange.min; - this.xMax = (this.defaultXMax !== undefined) ? this.defaultXMax : xRange.max; - if (this.xMax <= this.xMin) this.xMax = this.xMin + 1; - this.xStep = (this.defaultXStep !== undefined) ? this.defaultXStep : (this.xMax-this.xMin)/5; - var yRange = this.getColumnRange(data,this.colY); - if (withBars) { - yRange.min -= this.yBarWidth / 2; - yRange.max += this.yBarWidth / 2; - } - this.yMin = (this.defaultYMin !== undefined) ? this.defaultYMin : yRange.min; - this.yMax = (this.defaultYMax !== undefined) ? this.defaultYMax : yRange.max; - if (this.yMax <= this.yMin) this.yMax = this.yMin + 1; - this.yStep = (this.defaultYStep !== undefined) ? this.defaultYStep : (this.yMax-this.yMin)/5; + Slider.prototype._onMouseMove = function (event) { + var diff = event.clientX - this.startClientX; + var x = this.startSlideX + diff; - var zRange = this.getColumnRange(data,this.colZ); - this.zMin = (this.defaultZMin !== undefined) ? this.defaultZMin : zRange.min; - this.zMax = (this.defaultZMax !== undefined) ? this.defaultZMax : zRange.max; - if (this.zMax <= this.zMin) this.zMax = this.zMin + 1; - this.zStep = (this.defaultZStep !== undefined) ? this.defaultZStep : (this.zMax-this.zMin)/5; + var index = this.leftToIndex(x); - if (this.colValue !== undefined) { - var valueRange = this.getColumnRange(data,this.colValue); - this.valueMin = (this.defaultValueMin !== undefined) ? this.defaultValueMin : valueRange.min; - this.valueMax = (this.defaultValueMax !== undefined) ? this.defaultValueMax : valueRange.max; - if (this.valueMax <= this.valueMin) this.valueMax = this.valueMin + 1; - } + this.setIndex(index); - // set the scale dependent on the ranges. - this._setScale(); + util.preventDefault(); }; + Slider.prototype._onMouseUp = function (event) { + this.frame.style.cursor = 'auto'; - /** - * Filter the data based on the current filter - * @param {Array} data - * @return {Array} dataPoints Array with point objects which can be drawn on screen - */ - Graph3d.prototype._getDataPoints = function (data) { - // TODO: store the created matrix dataPoints in the filters instead of reloading each time - var x, y, i, z, obj, point; - - var dataPoints = []; + // remove event listeners + util.removeEventListener(document, 'mousemove', this.onmousemove); + util.removeEventListener(document, 'mouseup', this.onmouseup); - if (this.style === Graph3d.STYLE.GRID || - this.style === Graph3d.STYLE.SURFACE) { - // copy all values from the google data table to a matrix - // the provided values are supposed to form a grid of (x,y) positions + util.preventDefault(); + }; - // create two lists with all present x and y values - var dataX = []; - var dataY = []; - for (i = 0; i < this.getNumberOfRows(data); i++) { - x = data[i][this.colX] || 0; - y = data[i][this.colY] || 0; + module.exports = Slider; - if (dataX.indexOf(x) === -1) { - dataX.push(x); - } - if (dataY.indexOf(y) === -1) { - dataY.push(y); - } - } - var sortNumber = function (a, b) { - return a - b; - }; - dataX.sort(sortNumber); - dataY.sort(sortNumber); +/***/ }, +/* 12 */ +/***/ function(module, exports, __webpack_require__) { - // create a grid, a 2d matrix, with all values. - var dataMatrix = []; // temporary data matrix - for (i = 0; i < data.length; i++) { - x = data[i][this.colX] || 0; - y = data[i][this.colY] || 0; - z = data[i][this.colZ] || 0; + /** + * @prototype StepNumber + * The class StepNumber is an iterator for Numbers. You provide a start and end + * value, and a best step size. StepNumber itself rounds to fixed values and + * a finds the step that best fits the provided step. + * + * If prettyStep is true, the step size is chosen as close as possible to the + * provided step, but being a round value like 1, 2, 5, 10, 20, 50, .... + * + * Example usage: + * var step = new StepNumber(0, 10, 2.5, true); + * step.start(); + * while (!step.end()) { + * alert(step.getCurrent()); + * step.next(); + * } + * + * Version: 1.0 + * + * @param {Number} start The start value + * @param {Number} end The end value + * @param {Number} step Optional. Step size. Must be a positive value. + * @param {boolean} prettyStep Optional. If true, the step size is rounded + * To a pretty step size (like 1, 2, 5, 10, 20, 50, ...) + */ + function StepNumber(start, end, step, prettyStep) { + // set default values + this._start = 0; + this._end = 0; + this._step = 1; + this.prettyStep = true; + this.precision = 5; - var xIndex = dataX.indexOf(x); // TODO: implement Array().indexOf() for Internet Explorer - var yIndex = dataY.indexOf(y); + this._current = 0; + this.setRange(start, end, step, prettyStep); + }; - if (dataMatrix[xIndex] === undefined) { - dataMatrix[xIndex] = []; - } + /** + * Set a new range: start, end and step. + * + * @param {Number} start The start value + * @param {Number} end The end value + * @param {Number} step Optional. Step size. Must be a positive value. + * @param {boolean} prettyStep Optional. If true, the step size is rounded + * To a pretty step size (like 1, 2, 5, 10, 20, 50, ...) + */ + StepNumber.prototype.setRange = function(start, end, step, prettyStep) { + this._start = start ? start : 0; + this._end = end ? end : 0; - var point3d = new Point3d(); - point3d.x = x; - point3d.y = y; - point3d.z = z; + this.setStep(step, prettyStep); + }; - obj = {}; - obj.point = point3d; - obj.trans = undefined; - obj.screen = undefined; - obj.bottom = new Point3d(x, y, this.zMin); + /** + * Set a new step size + * @param {Number} step New step size. Must be a positive value + * @param {boolean} prettyStep Optional. If true, the provided step is rounded + * to a pretty step size (like 1, 2, 5, 10, 20, 50, ...) + */ + StepNumber.prototype.setStep = function(step, prettyStep) { + if (step === undefined || step <= 0) + return; - dataMatrix[xIndex][yIndex] = obj; + if (prettyStep !== undefined) + this.prettyStep = prettyStep; - dataPoints.push(obj); - } + if (this.prettyStep === true) + this._step = StepNumber.calculatePrettyStep(step); + else + this._step = step; + }; - // fill in the pointers to the neighbors. - for (x = 0; x < dataMatrix.length; x++) { - for (y = 0; y < dataMatrix[x].length; y++) { - if (dataMatrix[x][y]) { - dataMatrix[x][y].pointRight = (x < dataMatrix.length-1) ? dataMatrix[x+1][y] : undefined; - dataMatrix[x][y].pointTop = (y < dataMatrix[x].length-1) ? dataMatrix[x][y+1] : undefined; - dataMatrix[x][y].pointCross = - (x < dataMatrix.length-1 && y < dataMatrix[x].length-1) ? - dataMatrix[x+1][y+1] : - undefined; - } - } - } - } - else { // 'dot', 'dot-line', etc. - // copy all values from the google data table to a list with Point3d objects - for (i = 0; i < data.length; i++) { - point = new Point3d(); - point.x = data[i][this.colX] || 0; - point.y = data[i][this.colY] || 0; - point.z = data[i][this.colZ] || 0; + /** + * Calculate a nice step size, closest to the desired step size. + * Returns a value in one of the ranges 1*10^n, 2*10^n, or 5*10^n, where n is an + * integer Number. For example 1, 2, 5, 10, 20, 50, etc... + * @param {Number} step Desired step size + * @return {Number} Nice step size + */ + StepNumber.calculatePrettyStep = function (step) { + var log10 = function (x) {return Math.log(x) / Math.LN10;}; - if (this.colValue !== undefined) { - point.value = data[i][this.colValue] || 0; - } + // try three steps (multiple of 1, 2, or 5 + var step1 = Math.pow(10, Math.round(log10(step))), + step2 = 2 * Math.pow(10, Math.round(log10(step / 2))), + step5 = 5 * Math.pow(10, Math.round(log10(step / 5))); - obj = {}; - obj.point = point; - obj.bottom = new Point3d(point.x, point.y, this.zMin); - obj.trans = undefined; - obj.screen = undefined; + // choose the best step (closest to minimum step) + var prettyStep = step1; + if (Math.abs(step2 - step) <= Math.abs(prettyStep - step)) prettyStep = step2; + if (Math.abs(step5 - step) <= Math.abs(prettyStep - step)) prettyStep = step5; - dataPoints.push(obj); - } + // for safety + if (prettyStep <= 0) { + prettyStep = 1; } - return dataPoints; + return prettyStep; }; /** - * Create the main frame for the Graph3d. - * This function is executed once when a Graph3d object is created. The frame - * contains a canvas, and this canvas contains all objects like the axis and - * nodes. + * returns the current value of the step + * @return {Number} current value */ - Graph3d.prototype.create = function () { - // remove all elements from the container element. - while (this.containerElement.hasChildNodes()) { - this.containerElement.removeChild(this.containerElement.firstChild); - } - - this.frame = document.createElement('div'); - this.frame.style.position = 'relative'; - this.frame.style.overflow = 'hidden'; - - // create the graph canvas (HTML canvas element) - this.frame.canvas = document.createElement( 'canvas' ); - this.frame.canvas.style.position = 'relative'; - this.frame.appendChild(this.frame.canvas); - //if (!this.frame.canvas.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.frame.canvas.appendChild(noCanvas); - } - - this.frame.filter = document.createElement( 'div' ); - this.frame.filter.style.position = 'absolute'; - this.frame.filter.style.bottom = '0px'; - this.frame.filter.style.left = '0px'; - this.frame.filter.style.width = '100%'; - this.frame.appendChild(this.frame.filter); - - // add event listeners to handle moving and zooming the contents - var me = this; - var onmousedown = function (event) {me._onMouseDown(event);}; - var ontouchstart = function (event) {me._onTouchStart(event);}; - var onmousewheel = function (event) {me._onWheel(event);}; - var ontooltip = function (event) {me._onTooltip(event);}; - // TODO: these events are never cleaned up... can give a 'memory leakage' - - util.addEventListener(this.frame.canvas, 'keydown', onkeydown); - util.addEventListener(this.frame.canvas, 'mousedown', onmousedown); - util.addEventListener(this.frame.canvas, 'touchstart', ontouchstart); - util.addEventListener(this.frame.canvas, 'mousewheel', onmousewheel); - util.addEventListener(this.frame.canvas, 'mousemove', ontooltip); - - // add the new graph to the container element - this.containerElement.appendChild(this.frame); + StepNumber.prototype.getCurrent = function () { + return parseFloat(this._current.toPrecision(this.precision)); }; - /** - * Set a new size for the graph - * @param {string} width Width in pixels or percentage (for example '800px' - * or '50%') - * @param {string} height Height in pixels or percentage (for example '400px' - * or '30%') + * returns the current step size + * @return {Number} current step size */ - Graph3d.prototype.setSize = function(width, height) { - this.frame.style.width = width; - this.frame.style.height = height; - - this._resizeCanvas(); + StepNumber.prototype.getStep = function () { + return this._step; }; /** - * Resize the canvas to the current size of the frame + * Set the current value to the largest value smaller than start, which + * is a multiple of the step size */ - Graph3d.prototype._resizeCanvas = function() { - this.frame.canvas.style.width = '100%'; - this.frame.canvas.style.height = '100%'; - - this.frame.canvas.width = this.frame.canvas.clientWidth; - this.frame.canvas.height = this.frame.canvas.clientHeight; - - // adjust with for margin - this.frame.filter.style.width = (this.frame.canvas.clientWidth - 2 * 10) + 'px'; + StepNumber.prototype.start = function() { + this._current = this._start - this._start % this._step; }; /** - * Start animation + * Do a step, add the step size to the current value */ - Graph3d.prototype.animationStart = function() { - if (!this.frame.filter || !this.frame.filter.slider) - throw 'No animation available'; - - this.frame.filter.slider.play(); + StepNumber.prototype.next = function () { + this._current += this._step; }; - /** - * Stop animation + * Returns true whether the end is reached + * @return {boolean} True if the current value has passed the end value. */ - Graph3d.prototype.animationStop = function() { - if (!this.frame.filter || !this.frame.filter.slider) return; - - this.frame.filter.slider.stop(); + StepNumber.prototype.end = function () { + return (this._current > this._end); }; + module.exports = StepNumber; - /** - * Resize the center position based on the current values in this.defaultXCenter - * and this.defaultYCenter (which are strings with a percentage or a value - * in pixels). The center positions are the variables this.xCenter - * and this.yCenter - */ - Graph3d.prototype._resizeCenter = function() { - // calculate the horizontal center position - if (this.defaultXCenter.charAt(this.defaultXCenter.length-1) === '%') { - this.xcenter = - parseFloat(this.defaultXCenter) / 100 * - this.frame.canvas.clientWidth; - } - else { - this.xcenter = parseFloat(this.defaultXCenter); // supposed to be in px - } - // calculate the vertical center position - if (this.defaultYCenter.charAt(this.defaultYCenter.length-1) === '%') { - this.ycenter = - parseFloat(this.defaultYCenter) / 100 * - (this.frame.canvas.clientHeight - this.frame.filter.clientHeight); - } - else { - this.ycenter = parseFloat(this.defaultYCenter); // supposed to be in px - } - }; +/***/ }, +/* 13 */ +/***/ function(module, exports, __webpack_require__) { + + var Emitter = __webpack_require__(56); + var Hammer = __webpack_require__(45); + var util = __webpack_require__(1); + var DataSet = __webpack_require__(3); + var DataView = __webpack_require__(4); + var Range = __webpack_require__(17); + var Core = __webpack_require__(46); + var TimeAxis = __webpack_require__(35); + var CurrentTime = __webpack_require__(26); + var CustomTime = __webpack_require__(27); + var ItemSet = __webpack_require__(32); /** - * Set the rotation and distance of the camera - * @param {Object} pos An object with the camera position. The object - * contains three parameters: - * - horizontal {Number} - * The horizontal rotation, between 0 and 2*PI. - * Optional, can be left undefined. - * - vertical {Number} - * The vertical rotation, between 0 and 0.5*PI - * if vertical=0.5*PI, the graph is shown from the - * top. Optional, can be left undefined. - * - distance {Number} - * The (normalized) distance of the camera to the - * center of the graph, a value between 0.71 and 5.0. - * Optional, can be left undefined. + * Create a timeline visualization + * @param {HTMLElement} container + * @param {vis.DataSet | Array | google.visualization.DataTable} [items] + * @param {vis.DataSet | Array | google.visualization.DataTable} [groups] + * @param {Object} [options] See Timeline.setOptions for the available options. + * @constructor + * @extends Core */ - Graph3d.prototype.setCameraPosition = function(pos) { - if (pos === undefined) { - return; + function Timeline (container, items, groups, options) { + if (!(this instanceof Timeline)) { + throw new SyntaxError('Constructor must be called with the new operator'); } - if (pos.horizontal !== undefined && pos.vertical !== undefined) { - this.camera.setArmRotation(pos.horizontal, pos.vertical); + // if the third element is options, the forth is groups (optionally); + if (!(Array.isArray(groups) || groups instanceof DataSet) && groups instanceof Object) { + var forthArgument = options; + options = groups; + groups = forthArgument; } - if (pos.distance !== undefined) { - this.camera.setArmLength(pos.distance); - } + var me = this; + this.defaultOptions = { + start: null, + end: null, - this.redraw(); - }; + autoResize: true, + orientation: 'bottom', + width: null, + height: null, + maxHeight: null, + minHeight: null + }; + this.options = util.deepExtend({}, this.defaultOptions); - /** - * Retrieve the current camera rotation - * @return {object} An object with parameters horizontal, vertical, and - * distance - */ - Graph3d.prototype.getCameraPosition = function() { - var pos = this.camera.getArmRotation(); - pos.distance = this.camera.getArmLength(); - return pos; - }; + // Create the DOM, props, and emitter + this._create(container); - /** - * Load data into the 3D Graph - */ - Graph3d.prototype._readData = function(data) { - // read the data - this._dataInitialize(data, this.style); + // all components listed here will be repainted automatically + this.components = []; + this.body = { + dom: this.dom, + domProps: this.props, + emitter: { + on: this.on.bind(this), + off: this.off.bind(this), + emit: this.emit.bind(this) + }, + hiddenDates: [], + util: { + snap: null, // will be specified after TimeAxis is created + toScreen: me._toScreen.bind(me), + toGlobalScreen: me._toGlobalScreen.bind(me), // this refers to the root.width + toTime: me._toTime.bind(me), + toGlobalTime : me._toGlobalTime.bind(me) + } + }; - if (this.dataFilter) { - // apply filtering - this.dataPoints = this.dataFilter._getDataPoints(); - } - else { - // no filtering. load all data - this.dataPoints = this._getDataPoints(this.dataTable); - } + // range + this.range = new Range(this.body); + this.components.push(this.range); + this.body.range = this.range; - // draw the filter - this._redrawFilter(); - }; + // time axis + this.timeAxis = new TimeAxis(this.body); + this.components.push(this.timeAxis); + this.body.util.snap = this.timeAxis.snap.bind(this.timeAxis); - /** - * Replace the dataset of the Graph3d - * @param {Array | DataSet | DataView} data - */ - Graph3d.prototype.setData = function (data) { - this._readData(data); - this.redraw(); + // current time bar + this.currentTime = new CurrentTime(this.body); + this.components.push(this.currentTime); - // start animation when option is true - if (this.animationAutoStart && this.dataFilter) { - this.animationStart(); - } - }; + // custom time bar + // Note: time bar will be attached in this.setOptions when selected + this.customTime = new CustomTime(this.body); + this.components.push(this.customTime); - /** - * Update the options. Options will be merged with current options - * @param {Object} options - */ - Graph3d.prototype.setOptions = function (options) { - var cameraPosition = undefined; + // item set + this.itemSet = new ItemSet(this.body); + this.components.push(this.itemSet); - this.animationStop(); + this.itemsData = null; // DataSet + this.groupsData = null; // DataSet - if (options !== undefined) { - // retrieve parameter values - if (options.width !== undefined) this.width = options.width; - if (options.height !== undefined) this.height = options.height; + // apply options + if (options) { + this.setOptions(options); + } - if (options.xCenter !== undefined) this.defaultXCenter = options.xCenter; - if (options.yCenter !== undefined) this.defaultYCenter = options.yCenter; + // IMPORTANT: THIS HAPPENS BEFORE SET ITEMS! + if (groups) { + this.setGroups(groups); + } - if (options.filterLabel !== undefined) this.filterLabel = options.filterLabel; - if (options.legendLabel !== undefined) this.legendLabel = options.legendLabel; - if (options.xLabel !== undefined) this.xLabel = options.xLabel; - if (options.yLabel !== undefined) this.yLabel = options.yLabel; - if (options.zLabel !== undefined) this.zLabel = options.zLabel; + // create itemset + if (items) { + this.setItems(items); + } + else { + this.redraw(); + } + } - if (options.xValueLabel !== undefined) this.xValueLabel = options.xValueLabel; - if (options.yValueLabel !== undefined) this.yValueLabel = options.yValueLabel; - if (options.zValueLabel !== undefined) this.zValueLabel = options.zValueLabel; + // Extend the functionality from Core + Timeline.prototype = new Core(); - if (options.style !== undefined) { - var styleNumber = this._getStyleNumber(options.style); - if (styleNumber !== -1) { - this.style = styleNumber; - } - } - if (options.showGrid !== undefined) this.showGrid = options.showGrid; - if (options.showPerspective !== undefined) this.showPerspective = options.showPerspective; - if (options.showShadow !== undefined) this.showShadow = options.showShadow; - if (options.tooltip !== undefined) this.showTooltip = options.tooltip; - if (options.showAnimationControls !== undefined) this.showAnimationControls = options.showAnimationControls; - if (options.keepAspectRatio !== undefined) this.keepAspectRatio = options.keepAspectRatio; - if (options.verticalRatio !== undefined) this.verticalRatio = options.verticalRatio; + /** + * Set items + * @param {vis.DataSet | Array | google.visualization.DataTable | null} items + */ + Timeline.prototype.setItems = function(items) { + var initialLoad = (this.itemsData == null); - if (options.animationInterval !== undefined) this.animationInterval = options.animationInterval; - if (options.animationPreload !== undefined) this.animationPreload = options.animationPreload; - if (options.animationAutoStart !== undefined)this.animationAutoStart = options.animationAutoStart; + // convert to type DataSet when needed + var newDataSet; + if (!items) { + newDataSet = null; + } + else if (items instanceof DataSet || items instanceof DataView) { + newDataSet = items; + } + else { + // turn an array into a dataset + newDataSet = new DataSet(items, { + type: { + start: 'Date', + end: 'Date' + } + }); + } - if (options.xBarWidth !== undefined) this.defaultXBarWidth = options.xBarWidth; - if (options.yBarWidth !== undefined) this.defaultYBarWidth = options.yBarWidth; + // set items + this.itemsData = newDataSet; + this.itemSet && this.itemSet.setItems(newDataSet); - if (options.xMin !== undefined) this.defaultXMin = options.xMin; - if (options.xStep !== undefined) this.defaultXStep = options.xStep; - if (options.xMax !== undefined) this.defaultXMax = options.xMax; - if (options.yMin !== undefined) this.defaultYMin = options.yMin; - if (options.yStep !== undefined) this.defaultYStep = options.yStep; - if (options.yMax !== undefined) this.defaultYMax = options.yMax; - if (options.zMin !== undefined) this.defaultZMin = options.zMin; - if (options.zStep !== undefined) this.defaultZStep = options.zStep; - if (options.zMax !== undefined) this.defaultZMax = options.zMax; - if (options.valueMin !== undefined) this.defaultValueMin = options.valueMin; - if (options.valueMax !== undefined) this.defaultValueMax = options.valueMax; + if (initialLoad) { + if (this.options.start != undefined || this.options.end != undefined) { + if (this.options.start == undefined || this.options.end == undefined) { + var dataRange = this._getDataRange(); + } - if (options.cameraPosition !== undefined) cameraPosition = options.cameraPosition; + var start = this.options.start != undefined ? this.options.start : dataRange.start; + var end = this.options.end != undefined ? this.options.end : dataRange.end; - if (cameraPosition !== undefined) { - this.camera.setArmRotation(cameraPosition.horizontal, cameraPosition.vertical); - this.camera.setArmLength(cameraPosition.distance); + this.setWindow(start, end, {animate: false}); } else { - this.camera.setArmRotation(1.0, 0.5); - this.camera.setArmLength(1.7); + this.fit({animate: false}); } } - - this._setBackgroundColor(options && options.backgroundColor); - - this.setSize(this.width, this.height); - - // re-load the data - if (this.dataTable) { - this.setData(this.dataTable); - } - - // start animation when option is true - if (this.animationAutoStart && this.dataFilter) { - this.animationStart(); - } }; /** - * Redraw the Graph. + * Set groups + * @param {vis.DataSet | Array | google.visualization.DataTable} groups */ - Graph3d.prototype.redraw = function() { - if (this.dataPoints === undefined) { - throw 'Error: graph data not initialized'; - } - - this._resizeCanvas(); - this._resizeCenter(); - this._redrawSlider(); - this._redrawClear(); - this._redrawAxis(); - - if (this.style === Graph3d.STYLE.GRID || - this.style === Graph3d.STYLE.SURFACE) { - this._redrawDataGrid(); - } - else if (this.style === Graph3d.STYLE.LINE) { - this._redrawDataLine(); + Timeline.prototype.setGroups = function(groups) { + // convert to type DataSet when needed + var newDataSet; + if (!groups) { + newDataSet = null; } - else if (this.style === Graph3d.STYLE.BAR || - this.style === Graph3d.STYLE.BARCOLOR || - this.style === Graph3d.STYLE.BARSIZE) { - this._redrawDataBar(); + else if (groups instanceof DataSet || groups instanceof DataView) { + newDataSet = groups; } else { - // style is DOT, DOTLINE, DOTCOLOR, DOTSIZE - this._redrawDataDot(); + // turn an array into a dataset + newDataSet = new DataSet(groups); } - this._redrawInfo(); - this._redrawLegend(); + this.groupsData = newDataSet; + this.itemSet.setGroups(newDataSet); }; /** - * Clear the canvas before redrawing + * Set selected items by their id. Replaces the current selection + * Unknown id's are silently ignored. + * @param {string[] | string} [ids] An array with zero or more id's of the items to be + * selected. If ids is an empty array, all items will be + * unselected. + * @param {Object} [options] Available options: + * `focus: boolean` + * If true, focus will be set to the selected item(s) + * `animate: boolean | number` + * If true (default), the range is animated + * smoothly to the new window. + * If a number, the number is taken as duration + * for the animation. Default duration is 500 ms. + * Only applicable when option focus is true. */ - Graph3d.prototype._redrawClear = function() { - var canvas = this.frame.canvas; - var ctx = canvas.getContext('2d'); + Timeline.prototype.setSelection = function(ids, options) { + this.itemSet && this.itemSet.setSelection(ids); - ctx.clearRect(0, 0, canvas.width, canvas.height); + if (options && options.focus) { + this.focus(ids, options); + } }; + /** + * Get the selected items by their id + * @return {Array} ids The ids of the selected items + */ + Timeline.prototype.getSelection = function() { + return this.itemSet && this.itemSet.getSelection() || []; + }; /** - * Redraw the legend showing the colors + * Adjust the visible window such that the selected item (or multiple items) + * are centered on screen. + * @param {String | String[]} id An item id or array with item ids + * @param {Object} [options] Available options: + * `animate: boolean | number` + * If true (default), the range is animated + * smoothly to the new window. + * If a number, the number is taken as duration + * for the animation. Default duration is 500 ms. + * Only applicable when option focus is true */ - Graph3d.prototype._redrawLegend = function() { - var y; + Timeline.prototype.focus = function(id, options) { + if (!this.itemsData || id == undefined) return; - if (this.style === Graph3d.STYLE.DOTCOLOR || - this.style === Graph3d.STYLE.DOTSIZE) { + var ids = Array.isArray(id) ? id : [id]; - var dotSize = this.frame.clientWidth * 0.02; + // get the specified item(s) + var itemsData = this.itemsData.getDataSet().get(ids, { + type: { + start: 'Date', + end: 'Date' + } + }); - var widthMin, widthMax; - if (this.style === Graph3d.STYLE.DOTSIZE) { - widthMin = dotSize / 2; // px - widthMax = dotSize / 2 + dotSize * 2; // Todo: put this in one function + // calculate minimum start and maximum end of specified items + var start = null; + var end = null; + itemsData.forEach(function (itemData) { + var s = itemData.start.valueOf(); + var e = 'end' in itemData ? itemData.end.valueOf() : itemData.start.valueOf(); + + if (start === null || s < start) { + start = s; } - else { - widthMin = 20; // px - widthMax = 20; // px + + if (end === null || e > end) { + end = e; } + }); - var height = Math.max(this.frame.clientHeight * 0.25, 100); - var top = this.margin; - var right = this.frame.clientWidth - this.margin; - var left = right - widthMax; - var bottom = top + height; - } + if (start !== null && end !== null) { + // calculate the new middle and interval for the window + var middle = (start + end) / 2; + var interval = Math.max((this.range.end - this.range.start), (end - start) * 1.1); - var canvas = this.frame.canvas; - var ctx = canvas.getContext('2d'); - ctx.lineWidth = 1; - ctx.font = '14px arial'; // TODO: put in options + var animate = (options && options.animate !== undefined) ? options.animate : true; + this.range.setRange(middle - interval / 2, middle + interval / 2, animate); + } + }; - if (this.style === Graph3d.STYLE.DOTCOLOR) { - // draw the color bar - var ymin = 0; - var ymax = height; // Todo: make height customizable - for (y = ymin; y < ymax; y++) { - var f = (y - ymin) / (ymax - ymin); + /** + * Get the data range of the item set. + * @returns {{min: Date, max: Date}} range A range with a start and end Date. + * When no minimum is found, min==null + * When no maximum is found, max==null + */ + Timeline.prototype.getItemRange = function() { + // calculate min from start filed + var dataset = this.itemsData.getDataSet(), + min = null, + max = null; - //var width = (dotSize / 2 + (1-f) * dotSize * 2); // Todo: put this in one function - var hue = f * 240; - var color = this._hsv2rgb(hue, 1, 1); + if (dataset) { + // calculate the minimum value of the field 'start' + var minItem = dataset.min('start'); + min = minItem ? util.convert(minItem.start, 'Date').valueOf() : null; + // Note: we convert first to Date and then to number because else + // a conversion from ISODate to Number will fail - ctx.strokeStyle = color; - ctx.beginPath(); - ctx.moveTo(left, top + y); - ctx.lineTo(right, top + y); - ctx.stroke(); + // calculate maximum value of fields 'start' and 'end' + var maxStartItem = dataset.max('start'); + if (maxStartItem) { + max = util.convert(maxStartItem.start, 'Date').valueOf(); + } + var maxEndItem = dataset.max('end'); + if (maxEndItem) { + if (max == null) { + max = util.convert(maxEndItem.end, 'Date').valueOf(); + } + else { + max = Math.max(max, util.convert(maxEndItem.end, 'Date').valueOf()); + } } - - ctx.strokeStyle = this.colorAxis; - ctx.strokeRect(left, top, widthMax, height); } - if (this.style === Graph3d.STYLE.DOTSIZE) { - // draw border around color bar - ctx.strokeStyle = this.colorAxis; - ctx.fillStyle = this.colorDot; - ctx.beginPath(); - ctx.moveTo(left, top); - ctx.lineTo(right, top); - ctx.lineTo(right - widthMax + widthMin, bottom); - ctx.lineTo(left, bottom); - ctx.closePath(); - ctx.fill(); - ctx.stroke(); - } + return { + min: (min != null) ? new Date(min) : null, + max: (max != null) ? new Date(max) : null + }; + }; - if (this.style === Graph3d.STYLE.DOTCOLOR || - this.style === Graph3d.STYLE.DOTSIZE) { - // print values along the color bar - var gridLineLen = 5; // px - var step = new StepNumber(this.valueMin, this.valueMax, (this.valueMax-this.valueMin)/5, true); - step.start(); - if (step.getCurrent() < this.valueMin) { - step.next(); - } - while (!step.end()) { - y = bottom - (step.getCurrent() - this.valueMin) / (this.valueMax - this.valueMin) * height; - ctx.beginPath(); - ctx.moveTo(left - gridLineLen, y); - ctx.lineTo(left, y); - ctx.stroke(); + module.exports = Timeline; - ctx.textAlign = 'right'; - ctx.textBaseline = 'middle'; - ctx.fillStyle = this.colorAxis; - ctx.fillText(step.getCurrent(), left - 2 * gridLineLen, y); - step.next(); - } +/***/ }, +/* 14 */ +/***/ function(module, exports, __webpack_require__) { - ctx.textAlign = 'right'; - ctx.textBaseline = 'top'; - var label = this.legendLabel; - ctx.fillText(label, right, bottom + this.margin); - } - }; + var Emitter = __webpack_require__(56); + var Hammer = __webpack_require__(45); + var util = __webpack_require__(1); + var DataSet = __webpack_require__(3); + var DataView = __webpack_require__(4); + var Range = __webpack_require__(17); + var Core = __webpack_require__(46); + var TimeAxis = __webpack_require__(35); + var CurrentTime = __webpack_require__(26); + var CustomTime = __webpack_require__(27); + var LineGraph = __webpack_require__(34); /** - * Redraw the filter + * Create a timeline visualization + * @param {HTMLElement} container + * @param {vis.DataSet | Array | google.visualization.DataTable} [items] + * @param {Object} [options] See Graph2d.setOptions for the available options. + * @constructor + * @extends Core */ - Graph3d.prototype._redrawFilter = function() { - this.frame.filter.innerHTML = ''; - - if (this.dataFilter) { - var options = { - 'visible': this.showAnimationControls - }; - var slider = new Slider(this.frame.filter, options); - this.frame.filter.slider = slider; - - // TODO: css here is not nice here... - this.frame.filter.style.padding = '10px'; - //this.frame.filter.style.backgroundColor = '#EFEFEF'; - - slider.setValues(this.dataFilter.values); - slider.setPlayInterval(this.animationInterval); - - // create an event handler - var me = this; - var onchange = function () { - var index = slider.getIndex(); - - me.dataFilter.selectValue(index); - me.dataPoints = me.dataFilter._getDataPoints(); - - me.redraw(); - }; - slider.setOnChangeCallback(onchange); - } - else { - this.frame.filter.slider = undefined; + function Graph2d (container, items, groups, options) { + // if the third element is options, the forth is groups (optionally); + if (!(Array.isArray(groups) || groups instanceof DataSet) && groups instanceof Object) { + var forthArgument = options; + options = groups; + groups = forthArgument; } - }; - /** - * Redraw the slider - */ - Graph3d.prototype._redrawSlider = function() { - if ( this.frame.filter.slider !== undefined) { - this.frame.filter.slider.redraw(); - } - }; + var me = this; + this.defaultOptions = { + start: null, + end: null, + autoResize: true, - /** - * Redraw common information - */ - Graph3d.prototype._redrawInfo = function() { - if (this.dataFilter) { - var canvas = this.frame.canvas; - var ctx = canvas.getContext('2d'); + orientation: 'bottom', + width: null, + height: null, + maxHeight: null, + minHeight: null + }; + this.options = util.deepExtend({}, this.defaultOptions); - ctx.font = '14px arial'; // TODO: put in options - ctx.lineStyle = 'gray'; - ctx.fillStyle = 'gray'; - ctx.textAlign = 'left'; - ctx.textBaseline = 'top'; + // Create the DOM, props, and emitter + this._create(container); - var x = this.margin; - var y = this.margin; - ctx.fillText(this.dataFilter.getLabel() + ': ' + this.dataFilter.getSelectedValue(), x, y); - } - }; + // all components listed here will be repainted automatically + this.components = []; + this.body = { + dom: this.dom, + domProps: this.props, + emitter: { + on: this.on.bind(this), + off: this.off.bind(this), + emit: this.emit.bind(this) + }, + hiddenDates: [], + util: { + snap: null, // will be specified after TimeAxis is created + toScreen: me._toScreen.bind(me), + toGlobalScreen: me._toGlobalScreen.bind(me), // this refers to the root.width + toTime: me._toTime.bind(me), + toGlobalTime : me._toGlobalTime.bind(me) + } + }; - /** - * Redraw the axis - */ - Graph3d.prototype._redrawAxis = function() { - var canvas = this.frame.canvas, - ctx = canvas.getContext('2d'), - from, to, step, prettyStep, - text, xText, yText, zText, - offset, xOffset, yOffset, - xMin2d, xMax2d; + // range + this.range = new Range(this.body); + this.components.push(this.range); + this.body.range = this.range; - // TODO: get the actual rendered style of the containerElement - //ctx.font = this.containerElement.style.font; - ctx.font = 24 / this.camera.getArmLength() + 'px arial'; + // time axis + this.timeAxis = new TimeAxis(this.body); + this.components.push(this.timeAxis); + this.body.util.snap = this.timeAxis.snap.bind(this.timeAxis); - // calculate the length for the short grid lines - var gridLenX = 0.025 / this.scale.x; - var gridLenY = 0.025 / this.scale.y; - var textMargin = 5 / this.camera.getArmLength(); // px - var armAngle = this.camera.getArmRotation().horizontal; + // current time bar + this.currentTime = new CurrentTime(this.body); + this.components.push(this.currentTime); - // draw x-grid lines - ctx.lineWidth = 1; - prettyStep = (this.defaultXStep === undefined); - step = new StepNumber(this.xMin, this.xMax, this.xStep, prettyStep); - step.start(); - if (step.getCurrent() < this.xMin) { - step.next(); - } - while (!step.end()) { - var x = step.getCurrent(); + // custom time bar + // Note: time bar will be attached in this.setOptions when selected + this.customTime = new CustomTime(this.body); + this.components.push(this.customTime); - if (this.showGrid) { - from = this._convert3Dto2D(new Point3d(x, this.yMin, this.zMin)); - to = this._convert3Dto2D(new Point3d(x, this.yMax, this.zMin)); - ctx.strokeStyle = this.colorGrid; - ctx.beginPath(); - ctx.moveTo(from.x, from.y); - ctx.lineTo(to.x, to.y); - ctx.stroke(); - } - else { - from = this._convert3Dto2D(new Point3d(x, this.yMin, this.zMin)); - to = this._convert3Dto2D(new Point3d(x, this.yMin+gridLenX, this.zMin)); - ctx.strokeStyle = this.colorAxis; - ctx.beginPath(); - ctx.moveTo(from.x, from.y); - ctx.lineTo(to.x, to.y); - ctx.stroke(); + // item set + this.linegraph = new LineGraph(this.body); + this.components.push(this.linegraph); - from = this._convert3Dto2D(new Point3d(x, this.yMax, this.zMin)); - to = this._convert3Dto2D(new Point3d(x, this.yMax-gridLenX, this.zMin)); - ctx.strokeStyle = this.colorAxis; - ctx.beginPath(); - ctx.moveTo(from.x, from.y); - ctx.lineTo(to.x, to.y); - ctx.stroke(); - } + this.itemsData = null; // DataSet + this.groupsData = null; // DataSet - yText = (Math.cos(armAngle) > 0) ? this.yMin : this.yMax; - text = this._convert3Dto2D(new Point3d(x, yText, this.zMin)); - if (Math.cos(armAngle * 2) > 0) { - ctx.textAlign = 'center'; - ctx.textBaseline = 'top'; - text.y += textMargin; - } - else if (Math.sin(armAngle * 2) < 0){ - ctx.textAlign = 'right'; - ctx.textBaseline = 'middle'; - } - else { - ctx.textAlign = 'left'; - ctx.textBaseline = 'middle'; - } - ctx.fillStyle = this.colorAxis; - ctx.fillText(' ' + this.xValueLabel(step.getCurrent()) + ' ', text.x, text.y); + // apply options + if (options) { + this.setOptions(options); + } - step.next(); + // IMPORTANT: THIS HAPPENS BEFORE SET ITEMS! + if (groups) { + this.setGroups(groups); } - // draw y-grid lines - ctx.lineWidth = 1; - prettyStep = (this.defaultYStep === undefined); - step = new StepNumber(this.yMin, this.yMax, this.yStep, prettyStep); - step.start(); - if (step.getCurrent() < this.yMin) { - step.next(); + // create itemset + if (items) { + this.setItems(items); } - while (!step.end()) { - if (this.showGrid) { - from = this._convert3Dto2D(new Point3d(this.xMin, step.getCurrent(), this.zMin)); - to = this._convert3Dto2D(new Point3d(this.xMax, step.getCurrent(), this.zMin)); - ctx.strokeStyle = this.colorGrid; - ctx.beginPath(); - ctx.moveTo(from.x, from.y); - ctx.lineTo(to.x, to.y); - ctx.stroke(); - } - else { - from = this._convert3Dto2D(new Point3d(this.xMin, step.getCurrent(), this.zMin)); - to = this._convert3Dto2D(new Point3d(this.xMin+gridLenY, step.getCurrent(), this.zMin)); - ctx.strokeStyle = this.colorAxis; - ctx.beginPath(); - ctx.moveTo(from.x, from.y); - ctx.lineTo(to.x, to.y); - ctx.stroke(); + else { + this.redraw(); + } + } - from = this._convert3Dto2D(new Point3d(this.xMax, step.getCurrent(), this.zMin)); - to = this._convert3Dto2D(new Point3d(this.xMax-gridLenY, step.getCurrent(), this.zMin)); - ctx.strokeStyle = this.colorAxis; - ctx.beginPath(); - ctx.moveTo(from.x, from.y); - ctx.lineTo(to.x, to.y); - ctx.stroke(); - } + // Extend the functionality from Core + Graph2d.prototype = new Core(); - xText = (Math.sin(armAngle ) > 0) ? this.xMin : this.xMax; - text = this._convert3Dto2D(new Point3d(xText, step.getCurrent(), this.zMin)); - if (Math.cos(armAngle * 2) < 0) { - ctx.textAlign = 'center'; - ctx.textBaseline = 'top'; - text.y += textMargin; - } - else if (Math.sin(armAngle * 2) > 0){ - ctx.textAlign = 'right'; - ctx.textBaseline = 'middle'; - } - else { - ctx.textAlign = 'left'; - ctx.textBaseline = 'middle'; - } - ctx.fillStyle = this.colorAxis; - ctx.fillText(' ' + this.yValueLabel(step.getCurrent()) + ' ', text.x, text.y); + /** + * Set items + * @param {vis.DataSet | Array | google.visualization.DataTable | null} items + */ + Graph2d.prototype.setItems = function(items) { + var initialLoad = (this.itemsData == null); - step.next(); + // convert to type DataSet when needed + var newDataSet; + if (!items) { + newDataSet = null; } - - // draw z-grid lines and axis - ctx.lineWidth = 1; - prettyStep = (this.defaultZStep === undefined); - step = new StepNumber(this.zMin, this.zMax, this.zStep, prettyStep); - step.start(); - if (step.getCurrent() < this.zMin) { - step.next(); + else if (items instanceof DataSet || items instanceof DataView) { + newDataSet = items; } - xText = (Math.cos(armAngle ) > 0) ? this.xMin : this.xMax; - yText = (Math.sin(armAngle ) < 0) ? this.yMin : this.yMax; - while (!step.end()) { - // TODO: make z-grid lines really 3d? - from = this._convert3Dto2D(new Point3d(xText, yText, step.getCurrent())); - ctx.strokeStyle = this.colorAxis; - ctx.beginPath(); - ctx.moveTo(from.x, from.y); - ctx.lineTo(from.x - textMargin, from.y); - ctx.stroke(); - - ctx.textAlign = 'right'; - ctx.textBaseline = 'middle'; - ctx.fillStyle = this.colorAxis; - ctx.fillText(this.zValueLabel(step.getCurrent()) + ' ', from.x - 5, from.y); - - step.next(); + else { + // turn an array into a dataset + newDataSet = new DataSet(items, { + type: { + start: 'Date', + end: 'Date' + } + }); } - ctx.lineWidth = 1; - from = this._convert3Dto2D(new Point3d(xText, yText, this.zMin)); - to = this._convert3Dto2D(new Point3d(xText, yText, this.zMax)); - ctx.strokeStyle = this.colorAxis; - ctx.beginPath(); - ctx.moveTo(from.x, from.y); - ctx.lineTo(to.x, to.y); - ctx.stroke(); - // draw x-axis - ctx.lineWidth = 1; - // line at yMin - xMin2d = this._convert3Dto2D(new Point3d(this.xMin, this.yMin, this.zMin)); - xMax2d = this._convert3Dto2D(new Point3d(this.xMax, this.yMin, this.zMin)); - ctx.strokeStyle = this.colorAxis; - ctx.beginPath(); - ctx.moveTo(xMin2d.x, xMin2d.y); - ctx.lineTo(xMax2d.x, xMax2d.y); - ctx.stroke(); - // line at ymax - xMin2d = this._convert3Dto2D(new Point3d(this.xMin, this.yMax, this.zMin)); - xMax2d = this._convert3Dto2D(new Point3d(this.xMax, this.yMax, this.zMin)); - ctx.strokeStyle = this.colorAxis; - ctx.beginPath(); - ctx.moveTo(xMin2d.x, xMin2d.y); - ctx.lineTo(xMax2d.x, xMax2d.y); - ctx.stroke(); + // set items + this.itemsData = newDataSet; + this.linegraph && this.linegraph.setItems(newDataSet); - // draw y-axis - ctx.lineWidth = 1; - // line at xMin - from = this._convert3Dto2D(new Point3d(this.xMin, this.yMin, this.zMin)); - to = this._convert3Dto2D(new Point3d(this.xMin, this.yMax, this.zMin)); - ctx.strokeStyle = this.colorAxis; - ctx.beginPath(); - ctx.moveTo(from.x, from.y); - ctx.lineTo(to.x, to.y); - ctx.stroke(); - // line at xMax - from = this._convert3Dto2D(new Point3d(this.xMax, this.yMin, this.zMin)); - to = this._convert3Dto2D(new Point3d(this.xMax, this.yMax, this.zMin)); - ctx.strokeStyle = this.colorAxis; - ctx.beginPath(); - ctx.moveTo(from.x, from.y); - ctx.lineTo(to.x, to.y); - ctx.stroke(); + if (initialLoad) { + if (this.options.start != undefined || this.options.end != undefined) { + var start = this.options.start != undefined ? this.options.start : null; + var end = this.options.end != undefined ? this.options.end : null; - // draw x-label - var xLabel = this.xLabel; - if (xLabel.length > 0) { - yOffset = 0.1 / this.scale.y; - xText = (this.xMin + this.xMax) / 2; - yText = (Math.cos(armAngle) > 0) ? this.yMin - yOffset: this.yMax + yOffset; - text = this._convert3Dto2D(new Point3d(xText, yText, this.zMin)); - if (Math.cos(armAngle * 2) > 0) { - ctx.textAlign = 'center'; - ctx.textBaseline = 'top'; - } - else if (Math.sin(armAngle * 2) < 0){ - ctx.textAlign = 'right'; - ctx.textBaseline = 'middle'; + this.setWindow(start, end, {animate: false}); } else { - ctx.textAlign = 'left'; - ctx.textBaseline = 'middle'; + this.fit({animate: false}); } - ctx.fillStyle = this.colorAxis; - ctx.fillText(xLabel, text.x, text.y); } + }; - // draw y-label - var yLabel = this.yLabel; - if (yLabel.length > 0) { - xOffset = 0.1 / this.scale.x; - xText = (Math.sin(armAngle ) > 0) ? this.xMin - xOffset : this.xMax + xOffset; - yText = (this.yMin + this.yMax) / 2; - text = this._convert3Dto2D(new Point3d(xText, yText, this.zMin)); - if (Math.cos(armAngle * 2) < 0) { - ctx.textAlign = 'center'; - ctx.textBaseline = 'top'; - } - else if (Math.sin(armAngle * 2) > 0){ - ctx.textAlign = 'right'; - ctx.textBaseline = 'middle'; - } - else { - ctx.textAlign = 'left'; - ctx.textBaseline = 'middle'; - } - ctx.fillStyle = this.colorAxis; - ctx.fillText(yLabel, text.x, text.y); + /** + * Set groups + * @param {vis.DataSet | Array | google.visualization.DataTable} groups + */ + Graph2d.prototype.setGroups = function(groups) { + // convert to type DataSet when needed + var newDataSet; + if (!groups) { + newDataSet = null; } - - // draw z-label - var zLabel = this.zLabel; - if (zLabel.length > 0) { - offset = 30; // pixels. // TODO: relate to the max width of the values on the z axis? - xText = (Math.cos(armAngle ) > 0) ? this.xMin : this.xMax; - yText = (Math.sin(armAngle ) < 0) ? this.yMin : this.yMax; - zText = (this.zMin + this.zMax) / 2; - text = this._convert3Dto2D(new Point3d(xText, yText, zText)); - ctx.textAlign = 'right'; - ctx.textBaseline = 'middle'; - ctx.fillStyle = this.colorAxis; - ctx.fillText(zLabel, text.x - offset, text.y); + else if (groups instanceof DataSet || groups instanceof DataView) { + newDataSet = groups; + } + else { + // turn an array into a dataset + newDataSet = new DataSet(groups); } + + this.groupsData = newDataSet; + this.linegraph.setGroups(newDataSet); }; /** - * Calculate the color based on the given value. - * @param {Number} H Hue, a value be between 0 and 360 - * @param {Number} S Saturation, a value between 0 and 1 - * @param {Number} V Value, a value between 0 and 1 + * Returns an object containing an SVG element with the icon of the group (size determined by iconWidth and iconHeight), the label of the group (content) and the yAxisOrientation of the group (left or right). + * @param groupId + * @param width + * @param height */ - Graph3d.prototype._hsv2rgb = function(H, S, V) { - var R, G, B, C, Hi, X; + Graph2d.prototype.getLegend = function(groupId, width, height) { + if (width === undefined) {width = 15;} + if (height === undefined) {height = 15;} + if (this.linegraph.groups[groupId] !== undefined) { + return this.linegraph.groups[groupId].getLegend(width,height); + } + else { + return "cannot find group:" + groupId; + } + } - C = V * S; - Hi = Math.floor(H/60); // hi = 0,1,2,3,4,5 - X = C * (1 - Math.abs(((H/60) % 2) - 1)); + /** + * This checks if the visible option of the supplied group (by ID) is true or false. + * @param groupId + * @returns {*} + */ + Graph2d.prototype.isGroupVisible = function(groupId) { + if (this.linegraph.groups[groupId] !== undefined) { + return (this.linegraph.groups[groupId].visible && (this.linegraph.options.groups.visibility[groupId] === undefined || this.linegraph.options.groups.visibility[groupId] == true)); + } + else { + return false; + } + } - switch (Hi) { - case 0: R = C; G = X; B = 0; break; - case 1: R = X; G = C; B = 0; break; - case 2: R = 0; G = C; B = X; break; - case 3: R = 0; G = X; B = C; break; - case 4: R = X; G = 0; B = C; break; - case 5: R = C; G = 0; B = X; break; - default: R = 0; G = 0; B = 0; break; + /** + * Get the data range of the item set. + * @returns {{min: Date, max: Date}} range A range with a start and end Date. + * When no minimum is found, min==null + * When no maximum is found, max==null + */ + Graph2d.prototype.getItemRange = function() { + var min = null; + var max = null; + + // calculate min from start filed + for (var groupId in this.linegraph.groups) { + if (this.linegraph.groups.hasOwnProperty(groupId)) { + if (this.linegraph.groups[groupId].visible == true) { + for (var i = 0; i < this.linegraph.groups[groupId].itemsData.length; i++) { + var item = this.linegraph.groups[groupId].itemsData[i]; + var value = util.convert(item.x, 'Date').valueOf(); + min = min == null ? value : min > value ? value : min; + max = max == null ? value : max < value ? value : max; + } + } + } } - return 'RGB(' + parseInt(R*255) + ',' + parseInt(G*255) + ',' + parseInt(B*255) + ')'; + return { + min: (min != null) ? new Date(min) : null, + max: (max != null) ? new Date(max) : null + }; }; + + module.exports = Graph2d; + + +/***/ }, +/* 15 */ +/***/ function(module, exports, __webpack_require__) { + /** - * Draw all datapoints as a grid - * This function can be used when the style is 'grid' + * Created by Alex on 10/3/2014. */ - Graph3d.prototype._redrawDataGrid = function() { - var canvas = this.frame.canvas, - ctx = canvas.getContext('2d'), - point, right, top, cross, - i, - topSideVisible, fillStyle, strokeStyle, lineWidth, - h, s, v, zAvg; - - - if (this.dataPoints === undefined || this.dataPoints.length <= 0) - return; // TODO: throw exception? - - // calculate the translations and screen position of all points - for (i = 0; i < this.dataPoints.length; i++) { - var trans = this._convertPointToTranslation(this.dataPoints[i].point); - var screen = this._convertTranslationToScreen(trans); + var moment = __webpack_require__(44); - this.dataPoints[i].trans = trans; - this.dataPoints[i].screen = screen; - // calculate the translation of the point at the bottom (needed for sorting) - var transBottom = this._convertPointToTranslation(this.dataPoints[i].bottom); - this.dataPoints[i].dist = this.showPerspective ? transBottom.length() : -transBottom.z; + /** + * used in Core to convert the options into a volatile variable + * + * @param Core + */ + exports.convertHiddenOptions = function(body, hiddenDates) { + body.hiddenDates = []; + if (hiddenDates) { + if (Array.isArray(hiddenDates) == true) { + for (var i = 0; i < hiddenDates.length; i++) { + if (hiddenDates[i].repeat === undefined) { + var dateItem = {}; + dateItem.start = moment(hiddenDates[i].start).toDate().valueOf(); + dateItem.end = moment(hiddenDates[i].end).toDate().valueOf(); + body.hiddenDates.push(dateItem); + } + } + body.hiddenDates.sort(function (a, b) { + return a.start - b.start; + }); // sort by start time + } } + }; - // sort the points on depth of their (x,y) position (not on z) - var sortDepth = function (a, b) { - return b.dist - a.dist; - }; - this.dataPoints.sort(sortDepth); - if (this.style === Graph3d.STYLE.SURFACE) { - for (i = 0; i < this.dataPoints.length; i++) { - point = this.dataPoints[i]; - right = this.dataPoints[i].pointRight; - top = this.dataPoints[i].pointTop; - cross = this.dataPoints[i].pointCross; + /** + * create new entrees for the repeating hidden dates + * @param body + * @param hiddenDates + */ + exports.updateHiddenDates = function (body, hiddenDates) { + if (hiddenDates && body.domProps.centerContainer.width !== undefined) { + exports.convertHiddenOptions(body, hiddenDates); - if (point !== undefined && right !== undefined && top !== undefined && cross !== undefined) { + var start = moment(body.range.start); + var end = moment(body.range.end); - if (this.showGrayBottom || this.showShadow) { - // calculate the cross product of the two vectors from center - // to left and right, in order to know whether we are looking at the - // bottom or at the top side. We can also use the cross product - // for calculating light intensity - var aDiff = Point3d.subtract(cross.trans, point.trans); - var bDiff = Point3d.subtract(top.trans, right.trans); - var crossproduct = Point3d.crossProduct(aDiff, bDiff); - var len = crossproduct.length(); - // FIXME: there is a bug with determining the surface side (shadow or colored) + var totalRange = (body.range.end - body.range.start); + var pixelTime = totalRange / body.domProps.centerContainer.width; - topSideVisible = (crossproduct.z > 0); + for (var i = 0; i < hiddenDates.length; i++) { + if (hiddenDates[i].repeat !== undefined) { + var startDate = moment(hiddenDates[i].start); + var endDate = moment(hiddenDates[i].end); + + if (startDate._d == "Invalid Date") { + throw new Error("Supplied start date is not valid: " + hiddenDates[i].start); } - else { - topSideVisible = true; + if (endDate._d == "Invalid Date") { + throw new Error("Supplied end date is not valid: " + hiddenDates[i].end); } - if (topSideVisible) { - // calculate Hue from the current value. At zMin the hue is 240, at zMax the hue is 0 - zAvg = (point.point.z + right.point.z + top.point.z + cross.point.z) / 4; - h = (1 - (zAvg - this.zMin) * this.scale.z / this.verticalRatio) * 240; - s = 1; // saturation + var duration = endDate - startDate; + if (duration >= 4 * pixelTime) { - if (this.showShadow) { - v = Math.min(1 + (crossproduct.x / len) / 2, 1); // value. TODO: scale - fillStyle = this._hsv2rgb(h, s, v); - strokeStyle = fillStyle; + var offset = 0; + var runUntil = end.clone(); + switch (hiddenDates[i].repeat) { + case "daily": // case of time + if (startDate.day() != endDate.day()) { + offset = 1; + } + startDate.dayOfYear(start.dayOfYear()); + startDate.year(start.year()); + startDate.subtract(7,'days'); + + endDate.dayOfYear(start.dayOfYear()); + endDate.year(start.year()); + endDate.subtract(7 - offset,'days'); + + runUntil.add(1, 'weeks'); + break; + case "weekly": + var dayOffset = endDate.diff(startDate,'days') + var day = startDate.day(); + + // set the start date to the range.start + startDate.date(start.date()); + startDate.month(start.month()); + startDate.year(start.year()); + endDate = startDate.clone(); + + // force + startDate.day(day); + endDate.day(day); + endDate.add(dayOffset,'days'); + + startDate.subtract(1,'weeks'); + endDate.subtract(1,'weeks'); + + runUntil.add(1, 'weeks'); + break + case "monthly": + if (startDate.month() != endDate.month()) { + offset = 1; + } + startDate.month(start.month()); + startDate.year(start.year()); + startDate.subtract(1,'months'); + + endDate.month(start.month()); + endDate.year(start.year()); + endDate.subtract(1,'months'); + endDate.add(offset,'months'); + + runUntil.add(1, 'months'); + break; + case "yearly": + if (startDate.year() != endDate.year()) { + offset = 1; + } + startDate.year(start.year()); + startDate.subtract(1,'years'); + endDate.year(start.year()); + endDate.subtract(1,'years'); + endDate.add(offset,'years'); + + runUntil.add(1, 'years'); + break; + default: + console.log("Wrong repeat format, allowed are: daily, weekly, monthly, yearly. Given:", hiddenDates[i].repeat); + return; } - else { - v = 1; - fillStyle = this._hsv2rgb(h, s, v); - strokeStyle = this.colorAxis; + while (startDate < runUntil) { + body.hiddenDates.push({start: startDate.valueOf(), end: endDate.valueOf()}); + switch (hiddenDates[i].repeat) { + case "daily": + startDate.add(1, 'days'); + endDate.add(1, 'days'); + break; + case "weekly": + startDate.add(1, 'weeks'); + endDate.add(1, 'weeks'); + break + case "monthly": + startDate.add(1, 'months'); + endDate.add(1, 'months'); + break; + case "yearly": + startDate.add(1, 'y'); + endDate.add(1, 'y'); + break; + default: + console.log("Wrong repeat format, allowed are: daily, weekly, monthly, yearly. Given:", hiddenDates[i].repeat); + return; + } } + body.hiddenDates.push({start: startDate.valueOf(), end: endDate.valueOf()}); } - else { - fillStyle = 'gray'; - strokeStyle = this.colorAxis; - } - lineWidth = 0.5; - - ctx.lineWidth = lineWidth; - ctx.fillStyle = fillStyle; - ctx.strokeStyle = strokeStyle; - ctx.beginPath(); - ctx.moveTo(point.screen.x, point.screen.y); - ctx.lineTo(right.screen.x, right.screen.y); - ctx.lineTo(cross.screen.x, cross.screen.y); - ctx.lineTo(top.screen.x, top.screen.y); - ctx.closePath(); - ctx.fill(); - ctx.stroke(); } } + // remove duplicates, merge where possible + exports.removeDuplicates(body); + // ensure the new positions are not on hidden dates + var startHidden = exports.isHidden(body.range.start, body.hiddenDates); + var endHidden = exports.isHidden(body.range.end,body.hiddenDates); + var rangeStart = body.range.start; + var rangeEnd = body.range.end; + if (startHidden.hidden == true) {rangeStart = body.range.startToFront == true ? startHidden.startDate - 1 : startHidden.endDate + 1;} + if (endHidden.hidden == true) {rangeEnd = body.range.endToFront == true ? endHidden.startDate - 1 : endHidden.endDate + 1;} + if (startHidden.hidden == true || endHidden.hidden == true) { + body.range._applyRange(rangeStart, rangeEnd); + } } - else { // grid style - for (i = 0; i < this.dataPoints.length; i++) { - point = this.dataPoints[i]; - right = this.dataPoints[i].pointRight; - top = this.dataPoints[i].pointTop; - if (point !== undefined) { - if (this.showPerspective) { - lineWidth = 2 / -point.trans.z; + } + + + /** + * remove duplicates from the hidden dates list. Duplicates are evil. They mess everything up. + * Scales with N^2 + * @param body + */ + exports.removeDuplicates = function(body) { + var hiddenDates = body.hiddenDates; + var safeDates = []; + for (var i = 0; i < hiddenDates.length; i++) { + for (var j = 0; j < hiddenDates.length; j++) { + if (i != j && hiddenDates[j].remove != true && hiddenDates[i].remove != true) { + // j inside i + if (hiddenDates[j].start >= hiddenDates[i].start && hiddenDates[j].end <= hiddenDates[i].end) { + hiddenDates[j].remove = true; } - else { - lineWidth = 2 * -(this.eye.z / this.camera.getArmLength()); + // j start inside i + else if (hiddenDates[j].start >= hiddenDates[i].start && hiddenDates[j].start <= hiddenDates[i].end) { + hiddenDates[i].end = hiddenDates[j].end; + hiddenDates[j].remove = true; + } + // j end inside i + else if (hiddenDates[j].end >= hiddenDates[i].start && hiddenDates[j].end <= hiddenDates[i].end) { + hiddenDates[i].start = hiddenDates[j].start; + hiddenDates[j].remove = true; } } + } + } - if (point !== undefined && right !== undefined) { - // calculate Hue from the current value. At zMin the hue is 240, at zMax the hue is 0 - zAvg = (point.point.z + right.point.z) / 2; - h = (1 - (zAvg - this.zMin) * this.scale.z / this.verticalRatio) * 240; + for (var i = 0; i < hiddenDates.length; i++) { + if (hiddenDates[i].remove !== true) { + safeDates.push(hiddenDates[i]); + } + } - ctx.lineWidth = lineWidth; - ctx.strokeStyle = this._hsv2rgb(h, 1, 1); - ctx.beginPath(); - ctx.moveTo(point.screen.x, point.screen.y); - ctx.lineTo(right.screen.x, right.screen.y); - ctx.stroke(); - } + body.hiddenDates = safeDates; + body.hiddenDates.sort(function (a, b) { + return a.start - b.start; + }); // sort by start time + } - if (point !== undefined && top !== undefined) { - // calculate Hue from the current value. At zMin the hue is 240, at zMax the hue is 0 - zAvg = (point.point.z + top.point.z) / 2; - h = (1 - (zAvg - this.zMin) * this.scale.z / this.verticalRatio) * 240; + exports.printDates = function(dates) { + for (var i =0; i < dates.length; i++) { + console.log(i, new Date(dates[i].start),new Date(dates[i].end), dates[i].start, dates[i].end, dates[i].remove); + } + } - ctx.lineWidth = lineWidth; - ctx.strokeStyle = this._hsv2rgb(h, 1, 1); - ctx.beginPath(); - ctx.moveTo(point.screen.x, point.screen.y); - ctx.lineTo(top.screen.x, top.screen.y); - ctx.stroke(); - } + /** + * Used in TimeStep to avoid the hidden times. + * @param timeStep + * @param previousTime + */ + exports.stepOverHiddenDates = function(timeStep, previousTime) { + var stepInHidden = false; + var currentValue = timeStep.current.valueOf(); + for (var i = 0; i < timeStep.hiddenDates.length; i++) { + var startDate = timeStep.hiddenDates[i].start; + var endDate = timeStep.hiddenDates[i].end; + if (currentValue >= startDate && currentValue < endDate) { + stepInHidden = true; + break; } } + + if (stepInHidden == true && currentValue < timeStep._end.valueOf() && currentValue != previousTime) { + var prevValue = moment(previousTime); + var newValue = moment(endDate); + //check if the next step should be major + if (prevValue.year() != newValue.year()) {timeStep.switchedYear = true;} + else if (prevValue.month() != newValue.month()) {timeStep.switchedMonth = true;} + else if (prevValue.dayOfYear() != newValue.dayOfYear()) {timeStep.switchedDay = true;} + + timeStep.current = newValue.toDate(); + } }; + ///** + // * Used in TimeStep to avoid the hidden times. + // * @param timeStep + // * @param previousTime + // */ + //exports.checkFirstStep = function(timeStep) { + // var stepInHidden = false; + // var currentValue = timeStep.current.valueOf(); + // for (var i = 0; i < timeStep.hiddenDates.length; i++) { + // var startDate = timeStep.hiddenDates[i].start; + // var endDate = timeStep.hiddenDates[i].end; + // if (currentValue >= startDate && currentValue < endDate) { + // stepInHidden = true; + // break; + // } + // } + // + // if (stepInHidden == true && currentValue <= timeStep._end.valueOf()) { + // var newValue = moment(endDate); + // timeStep.current = newValue.toDate(); + // } + //}; + /** - * Draw all datapoints as dots. - * This function can be used when the style is 'dot' or 'dot-line' + * replaces the Core toScreen methods + * @param Core + * @param time + * @param width + * @returns {number} */ - Graph3d.prototype._redrawDataDot = function() { - var canvas = this.frame.canvas; - var ctx = canvas.getContext('2d'); - var i; - - if (this.dataPoints === undefined || this.dataPoints.length <= 0) - return; // TODO: throw exception? + exports.toScreen = function(Core, time, width) { + if (Core.body.hiddenDates.length == 0) { + var conversion = Core.range.conversion(width); + return (time.valueOf() - conversion.offset) * conversion.scale; + } + else { + var hidden = exports.isHidden(time, Core.body.hiddenDates) + if (hidden.hidden == true) { + time = hidden.startDate; + } - // calculate the translations of all points - for (i = 0; i < this.dataPoints.length; i++) { - var trans = this._convertPointToTranslation(this.dataPoints[i].point); - var screen = this._convertTranslationToScreen(trans); - this.dataPoints[i].trans = trans; - this.dataPoints[i].screen = screen; + var duration = exports.getHiddenDurationBetween(Core.body.hiddenDates, Core.range.start, Core.range.end); + time = exports.correctTimeForHidden(Core.body.hiddenDates, Core.range, time); - // calculate the distance from the point at the bottom to the camera - var transBottom = this._convertPointToTranslation(this.dataPoints[i].bottom); - this.dataPoints[i].dist = this.showPerspective ? transBottom.length() : -transBottom.z; + var conversion = Core.range.conversion(width, duration); + return (time.valueOf() - conversion.offset) * conversion.scale; } + }; - // order the translated points by depth - var sortDepth = function (a, b) { - return b.dist - a.dist; - }; - this.dataPoints.sort(sortDepth); - - // draw the datapoints as colored circles - var dotSize = this.frame.clientWidth * 0.02; // px - for (i = 0; i < this.dataPoints.length; i++) { - var point = this.dataPoints[i]; - if (this.style === Graph3d.STYLE.DOTLINE) { - // draw a vertical line from the bottom to the graph value - //var from = this._convert3Dto2D(new Point3d(point.point.x, point.point.y, this.zMin)); - var from = this._convert3Dto2D(point.bottom); - ctx.lineWidth = 1; - ctx.strokeStyle = this.colorGrid; - ctx.beginPath(); - ctx.moveTo(from.x, from.y); - ctx.lineTo(point.screen.x, point.screen.y); - ctx.stroke(); - } + /** + * Replaces the core toTime methods + * @param body + * @param range + * @param x + * @param width + * @returns {Date} + */ + exports.toTime = function(Core, x, width) { + if (Core.body.hiddenDates.length == 0) { + var conversion = Core.range.conversion(width); + return new Date(x / conversion.scale + conversion.offset); + } + else { + var hiddenDuration = exports.getHiddenDurationBetween(Core.body.hiddenDates, Core.range.start, Core.range.end); + var totalDuration = Core.range.end - Core.range.start - hiddenDuration; + var partialDuration = totalDuration * x / width; + var accumulatedHiddenDuration = exports.getAccumulatedHiddenDuration(Core.body.hiddenDates, Core.range, partialDuration); - // calculate radius for the circle - var size; - if (this.style === Graph3d.STYLE.DOTSIZE) { - size = dotSize/2 + 2*dotSize * (point.point.value - this.valueMin) / (this.valueMax - this.valueMin); - } - else { - size = dotSize; - } + var newTime = new Date(accumulatedHiddenDuration + partialDuration + Core.range.start); + return newTime; + } + }; - var radius; - if (this.showPerspective) { - radius = size / -point.trans.z; - } - else { - radius = size * -(this.eye.z / this.camera.getArmLength()); - } - if (radius < 0) { - radius = 0; - } - var hue, color, borderColor; - if (this.style === Graph3d.STYLE.DOTCOLOR ) { - // calculate the color based on the value - hue = (1 - (point.point.value - this.valueMin) * this.scale.value) * 240; - color = this._hsv2rgb(hue, 1, 1); - borderColor = this._hsv2rgb(hue, 1, 0.8); - } - else if (this.style === Graph3d.STYLE.DOTSIZE) { - color = this.colorDot; - borderColor = this.colorDotBorder; - } - else { - // calculate Hue from the current value. At zMin the hue is 240, at zMax the hue is 0 - hue = (1 - (point.point.z - this.zMin) * this.scale.z / this.verticalRatio) * 240; - color = this._hsv2rgb(hue, 1, 1); - borderColor = this._hsv2rgb(hue, 1, 0.8); + /** + * Support function + * + * @param hiddenDates + * @param range + * @returns {number} + */ + exports.getHiddenDurationBetween = function(hiddenDates, start, end) { + var duration = 0; + for (var i = 0; i < hiddenDates.length; i++) { + var startDate = hiddenDates[i].start; + var endDate = hiddenDates[i].end; + // if time after the cutout, and the + if (startDate >= start && endDate < end) { + duration += endDate - startDate; } - - // draw the circle - ctx.lineWidth = 1.0; - ctx.strokeStyle = borderColor; - ctx.fillStyle = color; - ctx.beginPath(); - ctx.arc(point.screen.x, point.screen.y, radius, 0, Math.PI*2, true); - ctx.fill(); - ctx.stroke(); } + return duration; }; + /** - * Draw all datapoints as bars. - * This function can be used when the style is 'bar', 'bar-color', or 'bar-size' + * Support function + * @param hiddenDates + * @param range + * @param time + * @returns {{duration: number, time: *, offset: number}} */ - Graph3d.prototype._redrawDataBar = function() { - var canvas = this.frame.canvas; - var ctx = canvas.getContext('2d'); - var i, j, surface, corners; + exports.correctTimeForHidden = function(hiddenDates, range, time) { + time = moment(time).toDate().valueOf(); + time -= exports.getHiddenDurationBefore(hiddenDates,range,time); + return time; + }; - if (this.dataPoints === undefined || this.dataPoints.length <= 0) - return; // TODO: throw exception? + exports.getHiddenDurationBefore = function(hiddenDates, range, time) { + var timeOffset = 0; + time = moment(time).toDate().valueOf(); - // calculate the translations of all points - for (i = 0; i < this.dataPoints.length; i++) { - var trans = this._convertPointToTranslation(this.dataPoints[i].point); - var screen = this._convertTranslationToScreen(trans); - this.dataPoints[i].trans = trans; - this.dataPoints[i].screen = screen; - - // calculate the distance from the point at the bottom to the camera - var transBottom = this._convertPointToTranslation(this.dataPoints[i].bottom); - this.dataPoints[i].dist = this.showPerspective ? transBottom.length() : -transBottom.z; - } - - // order the translated points by depth - var sortDepth = function (a, b) { - return b.dist - a.dist; - }; - this.dataPoints.sort(sortDepth); - - // draw the datapoints as bars - var xWidth = this.xBarWidth / 2; - var yWidth = this.yBarWidth / 2; - for (i = 0; i < this.dataPoints.length; i++) { - var point = this.dataPoints[i]; - - // determine color - var hue, color, borderColor; - if (this.style === Graph3d.STYLE.BARCOLOR ) { - // calculate the color based on the value - hue = (1 - (point.point.value - this.valueMin) * this.scale.value) * 240; - color = this._hsv2rgb(hue, 1, 1); - borderColor = this._hsv2rgb(hue, 1, 0.8); - } - else if (this.style === Graph3d.STYLE.BARSIZE) { - color = this.colorDot; - borderColor = this.colorDotBorder; - } - else { - // calculate Hue from the current value. At zMin the hue is 240, at zMax the hue is 0 - hue = (1 - (point.point.z - this.zMin) * this.scale.z / this.verticalRatio) * 240; - color = this._hsv2rgb(hue, 1, 1); - borderColor = this._hsv2rgb(hue, 1, 0.8); + for (var i = 0; i < hiddenDates.length; i++) { + var startDate = hiddenDates[i].start; + var endDate = hiddenDates[i].end; + // if time after the cutout, and the + if (startDate >= range.start && endDate < range.end) { + if (time >= endDate) { + timeOffset += (endDate - startDate); + } } + } + return timeOffset; + } - // calculate size for the bar - if (this.style === Graph3d.STYLE.BARSIZE) { - xWidth = (this.xBarWidth / 2) * ((point.point.value - this.valueMin) / (this.valueMax - this.valueMin) * 0.8 + 0.2); - yWidth = (this.yBarWidth / 2) * ((point.point.value - this.valueMin) / (this.valueMax - this.valueMin) * 0.8 + 0.2); + /** + * sum the duration from start to finish, including the hidden duration, + * until the required amount has been reached, return the accumulated hidden duration + * @param hiddenDates + * @param range + * @param time + * @returns {{duration: number, time: *, offset: number}} + */ + exports.getAccumulatedHiddenDuration = function(hiddenDates, range, requiredDuration) { + var hiddenDuration = 0; + var duration = 0; + var previousPoint = range.start; + //exports.printDates(hiddenDates) + for (var i = 0; i < hiddenDates.length; i++) { + var startDate = hiddenDates[i].start; + var endDate = hiddenDates[i].end; + // if time after the cutout, and the + if (startDate >= range.start && endDate < range.end) { + duration += startDate - previousPoint; + previousPoint = endDate; + if (duration >= requiredDuration) { + break; + } + else { + hiddenDuration += endDate - startDate; + } } + } - // calculate all corner points - var me = this; - var point3d = point.point; - var top = [ - {point: new Point3d(point3d.x - xWidth, point3d.y - yWidth, point3d.z)}, - {point: new Point3d(point3d.x + xWidth, point3d.y - yWidth, point3d.z)}, - {point: new Point3d(point3d.x + xWidth, point3d.y + yWidth, point3d.z)}, - {point: new Point3d(point3d.x - xWidth, point3d.y + yWidth, point3d.z)} - ]; - var bottom = [ - {point: new Point3d(point3d.x - xWidth, point3d.y - yWidth, this.zMin)}, - {point: new Point3d(point3d.x + xWidth, point3d.y - yWidth, this.zMin)}, - {point: new Point3d(point3d.x + xWidth, point3d.y + yWidth, this.zMin)}, - {point: new Point3d(point3d.x - xWidth, point3d.y + yWidth, this.zMin)} - ]; + return hiddenDuration; + }; - // calculate screen location of the points - top.forEach(function (obj) { - obj.screen = me._convert3Dto2D(obj.point); - }); - bottom.forEach(function (obj) { - obj.screen = me._convert3Dto2D(obj.point); - }); - // create five sides, calculate both corner points and center points - var surfaces = [ - {corners: top, center: Point3d.avg(bottom[0].point, bottom[2].point)}, - {corners: [top[0], top[1], bottom[1], bottom[0]], center: Point3d.avg(bottom[1].point, bottom[0].point)}, - {corners: [top[1], top[2], bottom[2], bottom[1]], center: Point3d.avg(bottom[2].point, bottom[1].point)}, - {corners: [top[2], top[3], bottom[3], bottom[2]], center: Point3d.avg(bottom[3].point, bottom[2].point)}, - {corners: [top[3], top[0], bottom[0], bottom[3]], center: Point3d.avg(bottom[0].point, bottom[3].point)} - ]; - point.surfaces = surfaces; - // calculate the distance of each of the surface centers to the camera - for (j = 0; j < surfaces.length; j++) { - surface = surfaces[j]; - var transCenter = this._convertPointToTranslation(surface.center); - surface.dist = this.showPerspective ? transCenter.length() : -transCenter.z; - // TODO: this dept calculation doesn't work 100% of the cases due to perspective, - // but the current solution is fast/simple and works in 99.9% of all cases - // the issue is visible in example 14, with graph.setCameraPosition({horizontal: 2.97, vertical: 0.5, distance: 0.9}) + /** + * used to step over to either side of a hidden block. Correction is disabled on tablets, might be set to true + * @param hiddenDates + * @param time + * @param direction + * @param correctionEnabled + * @returns {*} + */ + exports.snapAwayFromHidden = function(hiddenDates, time, direction, correctionEnabled) { + var isHidden = exports.isHidden(time, hiddenDates); + if (isHidden.hidden == true) { + if (direction < 0) { + if (correctionEnabled == true) { + return isHidden.startDate - (isHidden.endDate - time) - 1; + } + else { + return isHidden.startDate - 1; + } + } + else { + if (correctionEnabled == true) { + return isHidden.endDate + (time - isHidden.startDate) + 1; + } + else { + return isHidden.endDate + 1; + } } + } + else { + return time; + } - // order the surfaces by their (translated) depth - surfaces.sort(function (a, b) { - var diff = b.dist - a.dist; - if (diff) return diff; + } - // if equal depth, sort the top surface last - if (a.corners === top) return 1; - if (b.corners === top) return -1; - // both are equal - return 0; - }); + /** + * Check if a time is hidden + * + * @param time + * @param hiddenDates + * @returns {{hidden: boolean, startDate: Window.start, endDate: *}} + */ + exports.isHidden = function(time, hiddenDates) { + for (var i = 0; i < hiddenDates.length; i++) { + var startDate = hiddenDates[i].start; + var endDate = hiddenDates[i].end; - // draw the ordered surfaces - ctx.lineWidth = 1; - ctx.strokeStyle = borderColor; - ctx.fillStyle = color; - // NOTE: we start at j=2 instead of j=0 as we don't need to draw the two surfaces at the backside - for (j = 2; j < surfaces.length; j++) { - surface = surfaces[j]; - corners = surface.corners; - ctx.beginPath(); - ctx.moveTo(corners[3].screen.x, corners[3].screen.y); - ctx.lineTo(corners[0].screen.x, corners[0].screen.y); - ctx.lineTo(corners[1].screen.x, corners[1].screen.y); - ctx.lineTo(corners[2].screen.x, corners[2].screen.y); - ctx.lineTo(corners[3].screen.x, corners[3].screen.y); - ctx.fill(); - ctx.stroke(); + if (time >= startDate && time < endDate) { // if the start is entering a hidden zone + return {hidden: true, startDate: startDate, endDate: endDate}; + break; } } - }; + return {hidden: false, startDate: startDate, endDate: endDate}; + } +/***/ }, +/* 16 */ +/***/ function(module, exports, __webpack_require__) { /** - * Draw a line through all datapoints. - * This function can be used when the style is 'line' + * @constructor DataStep + * The class DataStep is an iterator for data for the lineGraph. You provide a start data point and an + * end data point. The class itself determines the best scale (step size) based on the + * provided start Date, end Date, and minimumStep. + * + * If minimumStep is provided, the step size is chosen as close as possible + * to the minimumStep but larger than minimumStep. If minimumStep is not + * provided, the scale is set to 1 DAY. + * The minimumStep should correspond with the onscreen size of about 6 characters + * + * Alternatively, you can set a scale by hand. + * After creation, you can initialize the class by executing first(). Then you + * can iterate from the start date to the end date via next(). You can check if + * the end date is reached with the function hasNext(). After each step, you can + * retrieve the current date via getCurrent(). + * The DataStep has scales ranging from milliseconds, seconds, minutes, hours, + * days, to years. + * + * Version: 1.2 + * + * @param {Date} [start] The start date, for example new Date(2010, 9, 21) + * or new Date(2010, 9, 21, 23, 45, 00) + * @param {Date} [end] The end date + * @param {Number} [minimumStep] Optional. Minimum step size in milliseconds */ - Graph3d.prototype._redrawDataLine = function() { - var canvas = this.frame.canvas, - ctx = canvas.getContext('2d'), - point, i; + function DataStep(start, end, minimumStep, containerHeight, customRange, alignZeros) { + // variables + this.current = 0; - if (this.dataPoints === undefined || this.dataPoints.length <= 0) - return; // TODO: throw exception? + this.autoScale = true; + this.stepIndex = 0; + this.step = 1; + this.scale = 1; - // calculate the translations of all points - for (i = 0; i < this.dataPoints.length; i++) { - var trans = this._convertPointToTranslation(this.dataPoints[i].point); - var screen = this._convertTranslationToScreen(trans); + this.marginStart; + this.marginEnd; + this.deadSpace = 0; - this.dataPoints[i].trans = trans; - this.dataPoints[i].screen = screen; - } + this.majorSteps = [1, 2, 5, 10]; + this.minorSteps = [0.25, 0.5, 1, 2]; - // start the line - if (this.dataPoints.length > 0) { - point = this.dataPoints[0]; + this.alignZeros = alignZeros; - ctx.lineWidth = 1; // TODO: make customizable - ctx.strokeStyle = 'blue'; // TODO: make customizable - ctx.beginPath(); - ctx.moveTo(point.screen.x, point.screen.y); - } + this.setRange(start, end, minimumStep, containerHeight, customRange); + } - // draw the datapoints as colored circles - for (i = 1; i < this.dataPoints.length; i++) { - point = this.dataPoints[i]; - ctx.lineTo(point.screen.x, point.screen.y); - } - // finish the line - if (this.dataPoints.length > 0) { - ctx.stroke(); - } - }; /** - * Start a moving operation inside the provided parent element - * @param {Event} event The event that occurred (required for - * retrieving the mouse position) + * Set a new range + * If minimumStep is provided, the step size is chosen as close as possible + * to the minimumStep but larger than minimumStep. If minimumStep is not + * provided, the scale is set to 1 DAY. + * The minimumStep should correspond with the onscreen size of about 6 characters + * @param {Number} [start] The start date and time. + * @param {Number} [end] The end date and time. + * @param {Number} [minimumStep] Optional. Minimum step size in milliseconds */ - Graph3d.prototype._onMouseDown = function(event) { - event = event || window.event; + DataStep.prototype.setRange = function(start, end, minimumStep, containerHeight, customRange) { + this._start = customRange.min === undefined ? start : customRange.min; + this._end = customRange.max === undefined ? end : customRange.max; - // check if mouse is still down (may be up when focus is lost for example - // in an iframe) - if (this.leftButtonDown) { - this._onMouseUp(event); + if (this._start == this._end) { + this._start -= 0.75; + this._end += 1; } - // only react on left mouse button down - this.leftButtonDown = event.which ? (event.which === 1) : (event.button === 1); - if (!this.leftButtonDown && !this.touchDown) return; - - // get mouse position (different code for IE and all other browsers) - this.startMouseX = getMouseX(event); - this.startMouseY = getMouseY(event); - - this.startStart = new Date(this.start); - this.startEnd = new Date(this.end); - this.startArmRotation = this.camera.getArmRotation(); - - this.frame.style.cursor = 'move'; + if (this.autoScale == true) { + this.setMinimumStep(minimumStep, containerHeight); + } - // add event listeners to handle moving the contents - // we store the function onmousemove and onmouseup in the graph, so we can - // remove the eventlisteners lateron in the function mouseUp() - var me = this; - this.onmousemove = function (event) {me._onMouseMove(event);}; - this.onmouseup = function (event) {me._onMouseUp(event);}; - util.addEventListener(document, 'mousemove', me.onmousemove); - util.addEventListener(document, 'mouseup', me.onmouseup); - util.preventDefault(event); + this.setFirst(customRange); }; - /** - * Perform moving operating. - * This function activated from within the funcion Graph.mouseDown(). - * @param {Event} event Well, eehh, the event + * Automatically determine the scale that bests fits the provided minimum step + * @param {Number} [minimumStep] The minimum step size in milliseconds */ - Graph3d.prototype._onMouseMove = function (event) { - event = event || window.event; - - // calculate change in mouse position - var diffX = parseFloat(getMouseX(event)) - this.startMouseX; - var diffY = parseFloat(getMouseY(event)) - this.startMouseY; - - var horizontalNew = this.startArmRotation.horizontal + diffX / 200; - var verticalNew = this.startArmRotation.vertical + diffY / 200; + DataStep.prototype.setMinimumStep = function(minimumStep, containerHeight) { + // round to floor + var size = this._end - this._start; + var safeSize = size * 1.2; + var minimumStepValue = minimumStep * (safeSize / containerHeight); + var orderOfMagnitude = Math.round(Math.log(safeSize)/Math.LN10); - var snapAngle = 4; // degrees - var snapValue = Math.sin(snapAngle / 360 * 2 * Math.PI); + var minorStepIdx = -1; + var magnitudefactor = Math.pow(10,orderOfMagnitude); - // snap horizontally to nice angles at 0pi, 0.5pi, 1pi, 1.5pi, etc... - // the -0.001 is to take care that the vertical axis is always drawn at the left front corner - if (Math.abs(Math.sin(horizontalNew)) < snapValue) { - horizontalNew = Math.round((horizontalNew / Math.PI)) * Math.PI - 0.001; - } - if (Math.abs(Math.cos(horizontalNew)) < snapValue) { - horizontalNew = (Math.round((horizontalNew/ Math.PI - 0.5)) + 0.5) * Math.PI - 0.001; + var start = 0; + if (orderOfMagnitude < 0) { + start = orderOfMagnitude; } - // snap vertically to nice angles - if (Math.abs(Math.sin(verticalNew)) < snapValue) { - verticalNew = Math.round((verticalNew / Math.PI)) * Math.PI; - } - if (Math.abs(Math.cos(verticalNew)) < snapValue) { - verticalNew = (Math.round((verticalNew/ Math.PI - 0.5)) + 0.5) * Math.PI; + var solutionFound = false; + for (var i = start; Math.abs(i) <= Math.abs(orderOfMagnitude); i++) { + magnitudefactor = Math.pow(10,i); + for (var j = 0; j < this.minorSteps.length; j++) { + var stepSize = magnitudefactor * this.minorSteps[j]; + if (stepSize >= minimumStepValue) { + solutionFound = true; + minorStepIdx = j; + break; + } + } + if (solutionFound == true) { + break; + } } - - this.camera.setArmRotation(horizontalNew, verticalNew); - this.redraw(); - - // fire a cameraPositionChange event - var parameters = this.getCameraPosition(); - this.emit('cameraPositionChange', parameters); - - util.preventDefault(event); + this.stepIndex = minorStepIdx; + this.scale = magnitudefactor; + this.step = magnitudefactor * this.minorSteps[minorStepIdx]; }; + /** - * Stop moving operating. - * This function activated from within the funcion Graph.mouseDown(). - * @param {event} event The event + * Round the current date to the first minor date value + * This must be executed once when the current date is set to start Date */ - Graph3d.prototype._onMouseUp = function (event) { - this.frame.style.cursor = 'auto'; - this.leftButtonDown = false; + DataStep.prototype.setFirst = function(customRange) { + if (customRange === undefined) { + customRange = {}; + } - // remove event listeners here - util.removeEventListener(document, 'mousemove', this.onmousemove); - util.removeEventListener(document, 'mouseup', this.onmouseup); - util.preventDefault(event); - }; + var niceStart = customRange.min === undefined ? this._start - (this.scale * 2 * this.minorSteps[this.stepIndex]) : customRange.min; + var niceEnd = customRange.max === undefined ? this._end + (this.scale * this.minorSteps[this.stepIndex]) : customRange.max; - /** - * After having moved the mouse, a tooltip should pop up when the mouse is resting on a data point - * @param {Event} event A mouse move event - */ - Graph3d.prototype._onTooltip = function (event) { - var delay = 300; // ms - var boundingRect = this.frame.getBoundingClientRect(); - var mouseX = getMouseX(event) - boundingRect.left; - var mouseY = getMouseY(event) - boundingRect.top; + this.marginEnd = customRange.max === undefined ? this.roundToMinor(niceEnd) : customRange.max; + this.marginStart = customRange.min === undefined ? this.roundToMinor(niceStart) : customRange.min; - if (!this.showTooltip) { - return; + // if we need to align the zero's we need to make sure that there is a zero to use. + if (this.alignZeros == true && (this.marginEnd - this.marginStart) % this.step != 0) { + this.marginEnd += this.marginEnd % this.step; } - if (this.tooltipTimeout) { - clearTimeout(this.tooltipTimeout); - } + this.deadSpace = this.roundToMinor(niceEnd) - niceEnd + this.roundToMinor(niceStart) - niceStart; + this.marginRange = this.marginEnd - this.marginStart; - // (delayed) display of a tooltip only if no mouse button is down - if (this.leftButtonDown) { - this._hideTooltip(); - return; - } - if (this.tooltip && this.tooltip.dataPoint) { - // tooltip is currently visible - var dataPoint = this._dataPointFromXY(mouseX, mouseY); - if (dataPoint !== this.tooltip.dataPoint) { - // datapoint changed - if (dataPoint) { - this._showTooltip(dataPoint); - } - else { - this._hideTooltip(); - } - } + this.current = this.marginEnd; + }; + + DataStep.prototype.roundToMinor = function(value) { + var rounded = value - (value % (this.scale * this.minorSteps[this.stepIndex])); + if (value % (this.scale * this.minorSteps[this.stepIndex]) > 0.5 * (this.scale * this.minorSteps[this.stepIndex])) { + return rounded + (this.scale * this.minorSteps[this.stepIndex]); } else { - // tooltip is currently not visible - var me = this; - this.tooltipTimeout = setTimeout(function () { - me.tooltipTimeout = null; - - // show a tooltip if we have a data point - var dataPoint = me._dataPointFromXY(mouseX, mouseY); - if (dataPoint) { - me._showTooltip(dataPoint); - } - }, delay); + return rounded; } - }; + } + /** - * Event handler for touchstart event on mobile devices + * Check if the there is a next step + * @return {boolean} true if the current date has not passed the end date */ - Graph3d.prototype._onTouchStart = function(event) { - this.touchDown = true; - - var me = this; - this.ontouchmove = function (event) {me._onTouchMove(event);}; - this.ontouchend = function (event) {me._onTouchEnd(event);}; - util.addEventListener(document, 'touchmove', me.ontouchmove); - util.addEventListener(document, 'touchend', me.ontouchend); - - this._onMouseDown(event); + DataStep.prototype.hasNext = function () { + return (this.current >= this.marginStart); }; /** - * Event handler for touchmove event on mobile devices + * Do the next step */ - Graph3d.prototype._onTouchMove = function(event) { - this._onMouseMove(event); + DataStep.prototype.next = function() { + var prev = this.current; + this.current -= this.step; + + // safety mechanism: if current time is still unchanged, move to the end + if (this.current == prev) { + this.current = this._end; + } }; /** - * Event handler for touchend event on mobile devices + * Do the next step */ - Graph3d.prototype._onTouchEnd = function(event) { - this.touchDown = false; - - util.removeEventListener(document, 'touchmove', this.ontouchmove); - util.removeEventListener(document, 'touchend', this.ontouchend); - - this._onMouseUp(event); + DataStep.prototype.previous = function() { + this.current += this.step; + this.marginEnd += this.step; + this.marginRange = this.marginEnd - this.marginStart; }; + /** - * Event handler for mouse wheel event, used to zoom the graph - * Code from http://adomas.org/javascript-mouse-wheel/ - * @param {event} event The event + * Get the current datetime + * @return {String} current The current date */ - Graph3d.prototype._onWheel = function(event) { - if (!event) /* For IE. */ - event = window.event; + DataStep.prototype.getCurrent = function(decimals) { + // prevent round-off errors when close to zero + var current = (Math.abs(this.current) < this.step / 2) ? 0 : this.current; + var toPrecision = '' + Number(current).toPrecision(5); - // retrieve delta - var delta = 0; - if (event.wheelDelta) { /* IE/Opera. */ - delta = event.wheelDelta/120; - } else if (event.detail) { /* Mozilla case. */ - // In Mozilla, sign of delta is different than in IE. - // Also, delta is multiple of 3. - delta = -event.detail/3; + // If decimals is specified, then limit or extend the string as required + if(decimals !== undefined && !isNaN(Number(decimals))) { + // If string includes exponent, then we need to add it to the end + var exp = ""; + var index = toPrecision.indexOf("e"); + if(index != -1) { + // Get the exponent + exp = toPrecision.slice(index); + // Remove the exponent in case we need to zero-extend + toPrecision = toPrecision.slice(0, index); + } + index = Math.max(toPrecision.indexOf(","), toPrecision.indexOf(".")); + if(index === -1) { + // No decimal found - if we want decimals, then we need to add it + if(decimals !== 0) { + toPrecision += '.'; + } + // Calculate how long the string should be + index = toPrecision.length + decimals; + } + else if(decimals !== 0) { + // Calculate how long the string should be - accounting for the decimal place + index += decimals + 1; + } + if(index > toPrecision.length) { + // We need to add zeros! + for(var cnt = index - toPrecision.length; cnt > 0; cnt--) { + toPrecision += '0'; + } + } + else { + // we need to remove characters + toPrecision = toPrecision.slice(0, index); + } + // Add the exponent if there is one + toPrecision += exp; } - - // If delta is nonzero, handle it. - // Basically, delta is now positive if wheel was scrolled up, - // and negative, if wheel was scrolled down. - if (delta) { - var oldLength = this.camera.getArmLength(); - var newLength = oldLength * (1 - delta / 10); - - this.camera.setArmLength(newLength); - this.redraw(); - - this._hideTooltip(); + else { + if (toPrecision.indexOf(",") != -1 || toPrecision.indexOf(".") != -1) { + // If no decimal is specified, and there are decimal places, remove trailing zeros + for (var i = toPrecision.length - 1; i > 0; i--) { + if (toPrecision[i] == "0") { + toPrecision = toPrecision.slice(0, i); + } + else if (toPrecision[i] == "." || toPrecision[i] == ",") { + toPrecision = toPrecision.slice(0, i); + break; + } + else { + break; + } + } + } } - // fire a cameraPositionChange event - var parameters = this.getCameraPosition(); - this.emit('cameraPositionChange', parameters); - - // Prevent default actions caused by mouse wheel. - // That might be ugly, but we handle scrolls somehow - // anyway, so don't bother here.. - util.preventDefault(event); + return toPrecision; }; - /** - * Test whether a point lies inside given 2D triangle - * @param {Point2d} point - * @param {Point2d[]} triangle - * @return {boolean} Returns true if given point lies inside or on the edge of the triangle - * @private - */ - Graph3d.prototype._insideTriangle = function (point, triangle) { - var a = triangle[0], - b = triangle[1], - c = triangle[2]; - function sign (x) { - return x > 0 ? 1 : x < 0 ? -1 : 0; - } - var as = sign((b.x - a.x) * (point.y - a.y) - (b.y - a.y) * (point.x - a.x)); - var bs = sign((c.x - b.x) * (point.y - b.y) - (c.y - b.y) * (point.x - b.x)); - var cs = sign((a.x - c.x) * (point.y - c.y) - (a.y - c.y) * (point.x - c.x)); + /** + * Snap a date to a rounded value. + * The snap intervals are dependent on the current scale and step. + * @param {Date} date the date to be snapped. + * @return {Date} snappedDate + */ + DataStep.prototype.snap = function(date) { - // each of the three signs must be either equal to each other or zero - return (as == 0 || bs == 0 || as == bs) && - (bs == 0 || cs == 0 || bs == cs) && - (as == 0 || cs == 0 || as == cs); }; /** - * Find a data point close to given screen position (x, y) - * @param {Number} x - * @param {Number} y - * @return {Object | null} The closest data point or null if not close to any data point - * @private + * Check if the current value is a major value (for example when the step + * is DAY, a major value is each first day of the MONTH) + * @return {boolean} true if current date is major, else false. */ - Graph3d.prototype._dataPointFromXY = function (x, y) { - var i, - distMax = 100, // px - dataPoint = null, - closestDataPoint = null, - closestDist = null, - center = new Point2d(x, y); + DataStep.prototype.isMajor = function() { + return (this.current % (this.scale * this.majorSteps[this.stepIndex]) == 0); + }; - if (this.style === Graph3d.STYLE.BAR || - this.style === Graph3d.STYLE.BARCOLOR || - this.style === Graph3d.STYLE.BARSIZE) { - // the data points are ordered from far away to closest - for (i = this.dataPoints.length - 1; i >= 0; i--) { - dataPoint = this.dataPoints[i]; - var surfaces = dataPoint.surfaces; - if (surfaces) { - for (var s = surfaces.length - 1; s >= 0; s--) { - // split each surface in two triangles, and see if the center point is inside one of these - var surface = surfaces[s]; - var corners = surface.corners; - var triangle1 = [corners[0].screen, corners[1].screen, corners[2].screen]; - var triangle2 = [corners[2].screen, corners[3].screen, corners[0].screen]; - if (this._insideTriangle(center, triangle1) || - this._insideTriangle(center, triangle2)) { - // return immediately at the first hit - return dataPoint; - } - } - } - } - } - else { - // find the closest data point, using distance to the center of the point on 2d screen - for (i = 0; i < this.dataPoints.length; i++) { - dataPoint = this.dataPoints[i]; - var point = dataPoint.screen; - if (point) { - var distX = Math.abs(x - point.x); - var distY = Math.abs(y - point.y); - var dist = Math.sqrt(distX * distX + distY * distY); + module.exports = DataStep; - if ((closestDist === null || dist < closestDist) && dist < distMax) { - closestDist = dist; - closestDataPoint = dataPoint; - } - } - } - } +/***/ }, +/* 17 */ +/***/ function(module, exports, __webpack_require__) { - return closestDataPoint; - }; + var util = __webpack_require__(1); + var hammerUtil = __webpack_require__(47); + var moment = __webpack_require__(44); + var Component = __webpack_require__(25); + var DateUtil = __webpack_require__(15); /** - * Display a tooltip for given data point - * @param {Object} dataPoint - * @private + * @constructor Range + * A Range controls a numeric range with a start and end value. + * The Range adjusts the range based on mouse events or programmatic changes, + * and triggers events when the range is changing or has been changed. + * @param {{dom: Object, domProps: Object, emitter: Emitter}} body + * @param {Object} [options] See description at Range.setOptions */ - Graph3d.prototype._showTooltip = function (dataPoint) { - var content, line, dot; - - if (!this.tooltip) { - content = document.createElement('div'); - content.style.position = 'absolute'; - content.style.padding = '10px'; - content.style.border = '1px solid #4d4d4d'; - content.style.color = '#1a1a1a'; - content.style.background = 'rgba(255,255,255,0.7)'; - content.style.borderRadius = '2px'; - content.style.boxShadow = '5px 5px 10px rgba(128,128,128,0.5)'; + function Range(body, options) { + var now = moment().hours(0).minutes(0).seconds(0).milliseconds(0); + this.start = now.clone().add(-3, 'days').valueOf(); // Number + this.end = now.clone().add(4, 'days').valueOf(); // Number - line = document.createElement('div'); - line.style.position = 'absolute'; - line.style.height = '40px'; - line.style.width = '0'; - line.style.borderLeft = '1px solid #4d4d4d'; + this.body = body; + this.deltaDifference = 0; + this.scaleOffset = 0; + this.startToFront = false; + this.endToFront = true; - dot = document.createElement('div'); - dot.style.position = 'absolute'; - dot.style.height = '0'; - dot.style.width = '0'; - dot.style.border = '5px solid #4d4d4d'; - dot.style.borderRadius = '5px'; + // default options + this.defaultOptions = { + start: null, + end: null, + direction: 'horizontal', // 'horizontal' or 'vertical' + moveable: true, + zoomable: true, + min: null, + max: null, + zoomMin: 10, // milliseconds + zoomMax: 1000 * 60 * 60 * 24 * 365 * 10000 // milliseconds + }; + this.options = util.extend({}, this.defaultOptions); - this.tooltip = { - dataPoint: null, - dom: { - content: content, - line: line, - dot: dot - } - }; - } - else { - content = this.tooltip.dom.content; - line = this.tooltip.dom.line; - dot = this.tooltip.dom.dot; - } + this.props = { + touch: {} + }; + this.animateTimer = null; - this._hideTooltip(); + // drag listeners for dragging + this.body.emitter.on('dragstart', this._onDragStart.bind(this)); + this.body.emitter.on('drag', this._onDrag.bind(this)); + this.body.emitter.on('dragend', this._onDragEnd.bind(this)); - this.tooltip.dataPoint = dataPoint; - if (typeof this.showTooltip === 'function') { - content.innerHTML = this.showTooltip(dataPoint.point); - } - else { - content.innerHTML = '' + - '' + - '' + - '' + - '
x:' + dataPoint.point.x + '
y:' + dataPoint.point.y + '
z:' + dataPoint.point.z + '
'; - } + // ignore dragging when holding + this.body.emitter.on('hold', this._onHold.bind(this)); - content.style.left = '0'; - content.style.top = '0'; - this.frame.appendChild(content); - this.frame.appendChild(line); - this.frame.appendChild(dot); + // mouse wheel for zooming + this.body.emitter.on('mousewheel', this._onMouseWheel.bind(this)); + this.body.emitter.on('DOMMouseScroll', this._onMouseWheel.bind(this)); // For FF - // calculate sizes - var contentWidth = content.offsetWidth; - var contentHeight = content.offsetHeight; - var lineHeight = line.offsetHeight; - var dotWidth = dot.offsetWidth; - var dotHeight = dot.offsetHeight; + // pinch to zoom + this.body.emitter.on('touch', this._onTouch.bind(this)); + this.body.emitter.on('pinch', this._onPinch.bind(this)); - var left = dataPoint.screen.x - contentWidth / 2; - left = Math.min(Math.max(left, 10), this.frame.clientWidth - 10 - contentWidth); + this.setOptions(options); + } - line.style.left = dataPoint.screen.x + 'px'; - line.style.top = (dataPoint.screen.y - lineHeight) + 'px'; - content.style.left = left + 'px'; - content.style.top = (dataPoint.screen.y - lineHeight - contentHeight) + 'px'; - dot.style.left = (dataPoint.screen.x - dotWidth / 2) + 'px'; - dot.style.top = (dataPoint.screen.y - dotHeight / 2) + 'px'; - }; + Range.prototype = new Component(); /** - * Hide the tooltip when displayed - * @private + * Set options for the range controller + * @param {Object} options Available options: + * {Number | Date | String} start Start date for the range + * {Number | Date | String} end End date for the range + * {Number} min Minimum value for start + * {Number} max Maximum value for end + * {Number} zoomMin Set a minimum value for + * (end - start). + * {Number} zoomMax Set a maximum value for + * (end - start). + * {Boolean} moveable Enable moving of the range + * by dragging. True by default + * {Boolean} zoomable Enable zooming of the range + * by pinching/scrolling. True by default */ - Graph3d.prototype._hideTooltip = function () { - if (this.tooltip) { - this.tooltip.dataPoint = null; + Range.prototype.setOptions = function (options) { + if (options) { + // copy the options that we know + var fields = ['direction', 'min', 'max', 'zoomMin', 'zoomMax', 'moveable', 'zoomable', 'activate', 'hiddenDates']; + util.selectiveExtend(fields, this.options, options); - for (var prop in this.tooltip.dom) { - if (this.tooltip.dom.hasOwnProperty(prop)) { - var elem = this.tooltip.dom[prop]; - if (elem && elem.parentNode) { - elem.parentNode.removeChild(elem); - } - } + if ('start' in options || 'end' in options) { + // apply a new range. both start and end are optional + this.setRange(options.start, options.end); } } }; - /**--------------------------------------------------------------------------**/ - - /** - * Get the horizontal mouse position from a mouse event - * @param {Event} event - * @return {Number} mouse x + * Test whether direction has a valid value + * @param {String} direction 'horizontal' or 'vertical' */ - function getMouseX (event) { - if ('clientX' in event) return event.clientX; - return event.targetTouches[0] && event.targetTouches[0].clientX || 0; + function validateDirection (direction) { + if (direction != 'horizontal' && direction != 'vertical') { + throw new TypeError('Unknown direction "' + direction + '". ' + + 'Choose "horizontal" or "vertical".'); + } } /** - * Get the vertical mouse position from a mouse event - * @param {Event} event - * @return {Number} mouse y + * Set a new start and end range + * @param {Date | Number | String} [start] + * @param {Date | Number | String} [end] + * @param {boolean | number} [animate=false] If true, the range is animated + * smoothly to the new window. + * If animate is a number, the + * number is taken as duration + * Default duration is 500 ms. + * @param {Boolean} [byUser=false] + * */ - function getMouseY (event) { - if ('clientY' in event) return event.clientY; - return event.targetTouches[0] && event.targetTouches[0].clientY || 0; - } - - module.exports = Graph3d; - + Range.prototype.setRange = function(start, end, animate, byUser) { + if (byUser !== true) { + byUser = false; + } + var _start = start != undefined ? util.convert(start, 'Date').valueOf() : null; + var _end = end != undefined ? util.convert(end, 'Date').valueOf() : null; + this._cancelAnimation(); -/***/ }, -/* 11 */ -/***/ function(module, exports, __webpack_require__) { + if (animate) { + var me = this; + var initStart = this.start; + var initEnd = this.end; + var duration = typeof animate === 'number' ? animate : 500; + var initTime = new Date().valueOf(); + var anyChanged = false; - - /** - * Expose `Emitter`. - */ + var next = function () { + if (!me.props.touch.dragging) { + var now = new Date().valueOf(); + var time = now - initTime; + var done = time > duration; + var s = (done || _start === null) ? _start : util.easeInOutQuad(time, initStart, _start, duration); + var e = (done || _end === null) ? _end : util.easeInOutQuad(time, initEnd, _end, duration); - module.exports = Emitter; + changed = me._applyRange(s, e); + DateUtil.updateHiddenDates(me.body, me.options.hiddenDates); + anyChanged = anyChanged || changed; + if (changed) { + me.body.emitter.emit('rangechange', {start: new Date(me.start), end: new Date(me.end), byUser:byUser}); + } - /** - * Initialize a new `Emitter`. - * - * @api public - */ + if (done) { + if (anyChanged) { + me.body.emitter.emit('rangechanged', {start: new Date(me.start), end: new Date(me.end), byUser:byUser}); + } + } + else { + // animate with as high as possible frame rate, leave 20 ms in between + // each to prevent the browser from blocking + me.animateTimer = setTimeout(next, 20); + } + } + } - function Emitter(obj) { - if (obj) return mixin(obj); + return next(); + } + else { + var changed = this._applyRange(_start, _end); + DateUtil.updateHiddenDates(this.body, this.options.hiddenDates); + if (changed) { + var params = {start: new Date(this.start), end: new Date(this.end), byUser:byUser}; + this.body.emitter.emit('rangechange', params); + this.body.emitter.emit('rangechanged', params); + } + } }; /** - * Mixin the emitter properties. - * - * @param {Object} obj - * @return {Object} - * @api private + * Stop an animation + * @private */ - - function mixin(obj) { - for (var key in Emitter.prototype) { - obj[key] = Emitter.prototype[key]; + Range.prototype._cancelAnimation = function () { + if (this.animateTimer) { + clearTimeout(this.animateTimer); + this.animateTimer = null; } - 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 + * Set a new start and end range. This method is the same as setRange, but + * does not trigger a range change and range changed event, and it returns + * true when the range is changed + * @param {Number} [start] + * @param {Number} [end] + * @return {Boolean} changed + * @private */ + Range.prototype._applyRange = function(start, end) { + var newStart = (start != null) ? util.convert(start, 'Date').valueOf() : this.start, + newEnd = (end != null) ? util.convert(end, 'Date').valueOf() : this.end, + max = (this.options.max != null) ? util.convert(this.options.max, 'Date').valueOf() : null, + min = (this.options.min != null) ? util.convert(this.options.min, 'Date').valueOf() : null, + diff; - Emitter.prototype.once = function(event, fn){ - var self = this; - this._callbacks = this._callbacks || {}; - - function on() { - self.off(event, on); - fn.apply(this, arguments); + // check for valid number + if (isNaN(newStart) || newStart === null) { + throw new Error('Invalid start "' + start + '"'); + } + if (isNaN(newEnd) || newEnd === null) { + throw new Error('Invalid end "' + end + '"'); } - on.fn = fn; - this.on(event, on); - return this; - }; + // prevent start < end + if (newEnd < newStart) { + newEnd = newStart; + } - /** - * 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 || {}; + // prevent start < min + if (min !== null) { + if (newStart < min) { + diff = (min - newStart); + newStart += diff; + newEnd += diff; - // all - if (0 == arguments.length) { - this._callbacks = {}; - return this; + // prevent end > max + if (max != null) { + if (newEnd > max) { + newEnd = max; + } + } + } } - // specific event - var callbacks = this._callbacks[event]; - if (!callbacks) return this; + // prevent end > max + if (max !== null) { + if (newEnd > max) { + diff = (newEnd - max); + newStart -= diff; + newEnd -= diff; - // remove all handlers - if (1 == arguments.length) { - delete this._callbacks[event]; - return this; + // prevent start < min + if (min != null) { + if (newStart < min) { + newStart = min; + } + } + } } - // 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; + // prevent (end-start) < zoomMin + if (this.options.zoomMin !== null) { + var zoomMin = parseFloat(this.options.zoomMin); + if (zoomMin < 0) { + zoomMin = 0; + } + if ((newEnd - newStart) < zoomMin) { + if ((this.end - this.start) === zoomMin) { + // ignore this action, we are already zoomed to the minimum + newStart = this.start; + newEnd = this.end; + } + else { + // zoom to the minimum + diff = (zoomMin - (newEnd - newStart)); + newStart -= diff / 2; + newEnd += diff / 2; + } } } - 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); + // prevent (end-start) > zoomMax + if (this.options.zoomMax !== null) { + var zoomMax = parseFloat(this.options.zoomMax); + if (zoomMax < 0) { + zoomMax = 0; + } + if ((newEnd - newStart) > zoomMax) { + if ((this.end - this.start) === zoomMax) { + // ignore this action, we are already zoomed to the maximum + newStart = this.start; + newEnd = this.end; + } + else { + // zoom to the maximum + diff = ((newEnd - newStart) - zoomMax); + newStart += diff / 2; + newEnd -= diff / 2; + } } } - 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; - }; - + var changed = (this.start != newStart || this.end != newEnd); -/***/ }, -/* 12 */ -/***/ function(module, exports, __webpack_require__) { + // if the new range does NOT overlap with the old range, emit checkRangedItems to avoid not showing ranged items (ranged meaning has end time, not neccesarily of type Range) + if (!((newStart >= this.start && newStart <= this.end) || (newEnd >= this.start && newEnd <= this.end)) && + !((this.start >= newStart && this.start <= newEnd) || (this.end >= newStart && this.end <= newEnd) )) { + this.body.emitter.emit('checkRangedItems'); + } - /** - * @prototype Point3d - * @param {Number} [x] - * @param {Number} [y] - * @param {Number} [z] - */ - function Point3d(x, y, z) { - this.x = x !== undefined ? x : 0; - this.y = y !== undefined ? y : 0; - this.z = z !== undefined ? z : 0; + this.start = newStart; + this.end = newEnd; + return changed; }; /** - * Subtract the two provided points, returns a-b - * @param {Point3d} a - * @param {Point3d} b - * @return {Point3d} a-b + * Retrieve the current range. + * @return {Object} An object with start and end properties */ - Point3d.subtract = function(a, b) { - var sub = new Point3d(); - sub.x = a.x - b.x; - sub.y = a.y - b.y; - sub.z = a.z - b.z; - return sub; + Range.prototype.getRange = function() { + return { + start: this.start, + end: this.end + }; }; /** - * Add the two provided points, returns a+b - * @param {Point3d} a - * @param {Point3d} b - * @return {Point3d} a+b + * Calculate the conversion offset and scale for current range, based on + * the provided width + * @param {Number} width + * @returns {{offset: number, scale: number}} conversion */ - Point3d.add = function(a, b) { - var sum = new Point3d(); - sum.x = a.x + b.x; - sum.y = a.y + b.y; - sum.z = a.z + b.z; - return sum; + Range.prototype.conversion = function (width, totalHidden) { + return Range.conversion(this.start, this.end, width, totalHidden); }; /** - * Calculate the average of two 3d points - * @param {Point3d} a - * @param {Point3d} b - * @return {Point3d} The average, (a+b)/2 + * Static method to calculate the conversion offset and scale for a range, + * based on the provided start, end, and width + * @param {Number} start + * @param {Number} end + * @param {Number} width + * @returns {{offset: number, scale: number}} conversion */ - Point3d.avg = function(a, b) { - return new Point3d( - (a.x + b.x) / 2, - (a.y + b.y) / 2, - (a.z + b.z) / 2 - ); + Range.conversion = function (start, end, width, totalHidden) { + if (totalHidden === undefined) { + totalHidden = 0; + } + if (width != 0 && (end - start != 0)) { + return { + offset: start, + scale: width / (end - start - totalHidden) + } + } + else { + return { + offset: 0, + scale: 1 + }; + } }; /** - * Calculate the cross product of the two provided points, returns axb - * Documentation: http://en.wikipedia.org/wiki/Cross_product - * @param {Point3d} a - * @param {Point3d} b - * @return {Point3d} cross product axb + * Start dragging horizontally or vertically + * @param {Event} event + * @private */ - Point3d.crossProduct = function(a, b) { - var crossproduct = new Point3d(); - - crossproduct.x = a.y * b.z - a.z * b.y; - crossproduct.y = a.z * b.x - a.x * b.z; - crossproduct.z = a.x * b.y - a.y * b.x; + Range.prototype._onDragStart = function(event) { + this.deltaDifference = 0; + this.previousDelta = 0; + // only allow dragging when configured as movable + if (!this.options.moveable) return; - return crossproduct; - }; + // refuse to drag when we where pinching to prevent the timeline make a jump + // when releasing the fingers in opposite order from the touch screen + if (!this.props.touch.allowDragging) return; + this.props.touch.start = this.start; + this.props.touch.end = this.end; + this.props.touch.dragging = true; - /** - * Rtrieve the length of the vector (or the distance from this point to the origin - * @return {Number} length - */ - Point3d.prototype.length = function() { - return Math.sqrt( - this.x * this.x + - this.y * this.y + - this.z * this.z - ); + if (this.body.dom.root) { + this.body.dom.root.style.cursor = 'move'; + } }; - module.exports = Point3d; - - -/***/ }, -/* 13 */ -/***/ function(module, exports, __webpack_require__) { - /** - * @prototype Point2d - * @param {Number} [x] - * @param {Number} [y] + * Perform dragging operation + * @param {Event} event + * @private */ - function Point2d (x, y) { - this.x = x !== undefined ? x : 0; - this.y = y !== undefined ? y : 0; - } - - module.exports = Point2d; + Range.prototype._onDrag = function (event) { + // only allow dragging when configured as movable + if (!this.options.moveable) return; + // refuse to drag when we where pinching to prevent the timeline make a jump + // when releasing the fingers in opposite order from the touch screen + if (!this.props.touch.allowDragging) return; + var direction = this.options.direction; + validateDirection(direction); -/***/ }, -/* 14 */ -/***/ function(module, exports, __webpack_require__) { + var delta = (direction == 'horizontal') ? event.gesture.deltaX : event.gesture.deltaY; + delta -= this.deltaDifference; + var interval = (this.props.touch.end - this.props.touch.start); - var Point3d = __webpack_require__(12); + // normalize dragging speed if cutout is in between. + var duration = DateUtil.getHiddenDurationBetween(this.body.hiddenDates, this.start, this.end); + interval -= duration; - /** - * @class Camera - * The camera is mounted on a (virtual) camera arm. The camera arm can rotate - * The camera is always looking in the direction of the origin of the arm. - * This way, the camera always rotates around one fixed point, the location - * of the camera arm. - * - * Documentation: - * http://en.wikipedia.org/wiki/3D_projection - */ - function Camera() { - this.armLocation = new Point3d(); - this.armRotation = {}; - this.armRotation.horizontal = 0; - this.armRotation.vertical = 0; - this.armLength = 1.7; + var width = (direction == 'horizontal') ? this.body.domProps.center.width : this.body.domProps.center.height; + var diffRange = -delta / width * interval; + var newStart = this.props.touch.start + diffRange; + var newEnd = this.props.touch.end + diffRange; - this.cameraLocation = new Point3d(); - this.cameraRotation = new Point3d(0.5*Math.PI, 0, 0); - this.calculateCameraOrientation(); - } + // snapping times away from hidden zones + var safeStart = DateUtil.snapAwayFromHidden(this.body.hiddenDates, newStart, this.previousDelta-delta, true); + var safeEnd = DateUtil.snapAwayFromHidden(this.body.hiddenDates, newEnd, this.previousDelta-delta, true); + if (safeStart != newStart || safeEnd != newEnd) { + this.deltaDifference += delta; + this.props.touch.start = safeStart; + this.props.touch.end = safeEnd; + this._onDrag(event); + return; + } - /** - * Set the location (origin) of the arm - * @param {Number} x Normalized value of x - * @param {Number} y Normalized value of y - * @param {Number} z Normalized value of z - */ - Camera.prototype.setArmLocation = function(x, y, z) { - this.armLocation.x = x; - this.armLocation.y = y; - this.armLocation.z = z; + this.previousDelta = delta; + this._applyRange(newStart, newEnd); - this.calculateCameraOrientation(); + // fire a rangechange event + this.body.emitter.emit('rangechange', { + start: new Date(this.start), + end: new Date(this.end), + byUser: true + }); }; /** - * Set the rotation of the camera arm - * @param {Number} horizontal The horizontal rotation, between 0 and 2*PI. - * Optional, can be left undefined. - * @param {Number} vertical The vertical rotation, between 0 and 0.5*PI - * if vertical=0.5*PI, the graph is shown from the - * top. Optional, can be left undefined. + * Stop dragging operation + * @param {event} event + * @private */ - Camera.prototype.setArmRotation = function(horizontal, vertical) { - if (horizontal !== undefined) { - this.armRotation.horizontal = horizontal; - } + Range.prototype._onDragEnd = function (event) { + // only allow dragging when configured as movable + if (!this.options.moveable) return; - if (vertical !== undefined) { - this.armRotation.vertical = vertical; - if (this.armRotation.vertical < 0) this.armRotation.vertical = 0; - if (this.armRotation.vertical > 0.5*Math.PI) this.armRotation.vertical = 0.5*Math.PI; - } + // refuse to drag when we where pinching to prevent the timeline make a jump + // when releasing the fingers in opposite order from the touch screen + if (!this.props.touch.allowDragging) return; - if (horizontal !== undefined || vertical !== undefined) { - this.calculateCameraOrientation(); + this.props.touch.dragging = false; + if (this.body.dom.root) { + this.body.dom.root.style.cursor = 'auto'; } + + // fire a rangechanged event + this.body.emitter.emit('rangechanged', { + start: new Date(this.start), + end: new Date(this.end), + byUser: true + }); }; /** - * Retrieve the current arm rotation - * @return {object} An object with parameters horizontal and vertical + * Event handler for mouse wheel event, used to zoom + * Code from http://adomas.org/javascript-mouse-wheel/ + * @param {Event} event + * @private */ - Camera.prototype.getArmRotation = function() { - var rot = {}; - rot.horizontal = this.armRotation.horizontal; - rot.vertical = this.armRotation.vertical; + Range.prototype._onMouseWheel = function(event) { + // only allow zooming when configured as zoomable and moveable + if (!(this.options.zoomable && this.options.moveable)) return; - return rot; - }; + // retrieve delta + var delta = 0; + if (event.wheelDelta) { /* IE/Opera. */ + delta = event.wheelDelta / 120; + } else if (event.detail) { /* Mozilla case. */ + // In Mozilla, sign of delta is different than in IE. + // Also, delta is multiple of 3. + delta = -event.detail / 3; + } - /** - * Set the (normalized) length of the camera arm. - * @param {Number} length A length between 0.71 and 5.0 - */ - Camera.prototype.setArmLength = function(length) { - if (length === undefined) - return; + // If delta is nonzero, handle it. + // Basically, delta is now positive if wheel was scrolled up, + // and negative, if wheel was scrolled down. + if (delta) { + // perform the zoom action. Delta is normally 1 or -1 - this.armLength = length; + // adjust a negative delta such that zooming in with delta 0.1 + // equals zooming out with a delta -0.1 + var scale; + if (delta < 0) { + scale = 1 - (delta / 5); + } + else { + scale = 1 / (1 + (delta / 5)) ; + } - // Radius must be larger than the corner of the graph, - // which has a distance of sqrt(0.5^2+0.5^2) = 0.71 from the center of the - // graph - if (this.armLength < 0.71) this.armLength = 0.71; - if (this.armLength > 5.0) this.armLength = 5.0; + // calculate center, the date to zoom around + var gesture = hammerUtil.fakeGesture(this, event), + pointer = getPointer(gesture.center, this.body.dom.center), + pointerDate = this._pointerToDate(pointer); - this.calculateCameraOrientation(); - }; + this.zoom(scale, pointerDate, delta); + } - /** - * Retrieve the arm length - * @return {Number} length - */ - Camera.prototype.getArmLength = function() { - return this.armLength; + // Prevent default actions caused by mouse wheel + // (else the page and timeline both zoom and scroll) + event.preventDefault(); }; /** - * Retrieve the camera location - * @return {Point3d} cameraLocation + * Start of a touch gesture + * @private */ - Camera.prototype.getCameraLocation = function() { - return this.cameraLocation; + Range.prototype._onTouch = function (event) { + this.props.touch.start = this.start; + this.props.touch.end = this.end; + this.props.touch.allowDragging = true; + this.props.touch.center = null; + this.scaleOffset = 0; + this.deltaDifference = 0; }; /** - * Retrieve the camera rotation - * @return {Point3d} cameraRotation + * On start of a hold gesture + * @private */ - Camera.prototype.getCameraRotation = function() { - return this.cameraRotation; + Range.prototype._onHold = function () { + this.props.touch.allowDragging = false; }; /** - * Calculate the location and rotation of the camera based on the - * position and orientation of the camera arm + * Handle pinch event + * @param {Event} event + * @private */ - Camera.prototype.calculateCameraOrientation = function() { - // calculate location of the camera - this.cameraLocation.x = this.armLocation.x - this.armLength * Math.sin(this.armRotation.horizontal) * Math.cos(this.armRotation.vertical); - this.cameraLocation.y = this.armLocation.y - this.armLength * Math.cos(this.armRotation.horizontal) * Math.cos(this.armRotation.vertical); - this.cameraLocation.z = this.armLocation.z + this.armLength * Math.sin(this.armRotation.vertical); - - // calculate rotation of the camera - this.cameraRotation.x = Math.PI/2 - this.armRotation.vertical; - this.cameraRotation.y = 0; - this.cameraRotation.z = -this.armRotation.horizontal; - }; + Range.prototype._onPinch = function (event) { + // only allow zooming when configured as zoomable and moveable + if (!(this.options.zoomable && this.options.moveable)) return; - module.exports = Camera; + this.props.touch.allowDragging = false; -/***/ }, -/* 15 */ -/***/ function(module, exports, __webpack_require__) { + if (event.gesture.touches.length > 1) { + if (!this.props.touch.center) { + this.props.touch.center = getPointer(event.gesture.center, this.body.dom.center); + } - var DataView = __webpack_require__(9); + var scale = 1 / (event.gesture.scale + this.scaleOffset); + var centerDate = this._pointerToDate(this.props.touch.center); - /** - * @class Filter - * - * @param {DataSet} data The google data table - * @param {Number} column The index of the column to be filtered - * @param {Graph} graph The graph - */ - function Filter (data, column, graph) { - this.data = data; - this.column = column; - this.graph = graph; // the parent graph + var hiddenDuration = DateUtil.getHiddenDurationBetween(this.body.hiddenDates, this.start, this.end); + var hiddenDurationBefore = DateUtil.getHiddenDurationBefore(this.body.hiddenDates, this, centerDate); + var hiddenDurationAfter = hiddenDuration - hiddenDurationBefore; - this.index = undefined; - this.value = undefined; + // calculate new start and end + var newStart = (centerDate - hiddenDurationBefore) + (this.props.touch.start - (centerDate - hiddenDurationBefore)) * scale; + var newEnd = (centerDate + hiddenDurationAfter) + (this.props.touch.end - (centerDate + hiddenDurationAfter)) * scale; - // read all distinct values and select the first one - this.values = graph.getDistinctValues(data.get(), this.column); + // snapping times away from hidden zones + this.startToFront = 1 - scale > 0 ? false : true; // used to do the right autocorrection with periodic hidden times + this.endToFront = scale - 1 > 0 ? false : true; // used to do the right autocorrection with periodic hidden times - // sort both numeric and string values correctly - this.values.sort(function (a, b) { - return a > b ? 1 : a < b ? -1 : 0; - }); + var safeStart = DateUtil.snapAwayFromHidden(this.body.hiddenDates, newStart, 1 - scale, true); + var safeEnd = DateUtil.snapAwayFromHidden(this.body.hiddenDates, newEnd, scale - 1, true); + if (safeStart != newStart || safeEnd != newEnd) { + this.props.touch.start = safeStart; + this.props.touch.end = safeEnd; + this.scaleOffset = 1 - event.gesture.scale; + newStart = safeStart; + newEnd = safeEnd; + } - if (this.values.length > 0) { - this.selectValue(0); + this.setRange(newStart, newEnd, false, true); + + this.startToFront = false; // revert to default + this.endToFront = true; // revert to default } + }; - // create an array with the filtered datapoints. this will be loaded afterwards - this.dataPoints = []; + /** + * Helper function to calculate the center date for zooming + * @param {{x: Number, y: Number}} pointer + * @return {number} date + * @private + */ + Range.prototype._pointerToDate = function (pointer) { + var conversion; + var direction = this.options.direction; - this.loaded = false; - this.onLoadCallback = undefined; + validateDirection(direction); - if (graph.animationPreload) { - this.loaded = false; - this.loadInBackground(); + if (direction == 'horizontal') { + return this.body.util.toTime(pointer.x).valueOf(); } else { - this.loaded = true; + var height = this.body.domProps.center.height; + conversion = this.conversion(height); + return pointer.y / conversion.scale + conversion.offset; } }; - /** - * Return the label - * @return {string} label + * Get the pointer location relative to the location of the dom element + * @param {{pageX: Number, pageY: Number}} touch + * @param {Element} element HTML DOM element + * @return {{x: Number, y: Number}} pointer + * @private */ - Filter.prototype.isLoaded = function() { - return this.loaded; - }; - + function getPointer (touch, element) { + return { + x: touch.pageX - util.getAbsoluteLeft(element), + y: touch.pageY - util.getAbsoluteTop(element) + }; + } /** - * Return the loaded progress - * @return {Number} percentage between 0 and 100 + * Zoom the range the given scale in or out. Start and end date will + * be adjusted, and the timeline will be redrawn. You can optionally give a + * date around which to zoom. + * For example, try scale = 0.9 or 1.1 + * @param {Number} scale Scaling factor. Values above 1 will zoom out, + * values below 1 will zoom in. + * @param {Number} [center] Value representing a date around which will + * be zoomed. */ - Filter.prototype.getLoadedProgress = function() { - var len = this.values.length; - - var i = 0; - while (this.dataPoints[i]) { - i++; + Range.prototype.zoom = function(scale, center, delta) { + // if centerDate is not provided, take it half between start Date and end Date + if (center == null) { + center = (this.start + this.end) / 2; } - return Math.round(i / len * 100); - }; + var hiddenDuration = DateUtil.getHiddenDurationBetween(this.body.hiddenDates, this.start, this.end); + var hiddenDurationBefore = DateUtil.getHiddenDurationBefore(this.body.hiddenDates, this, center); + var hiddenDurationAfter = hiddenDuration - hiddenDurationBefore; + // calculate new start and end + var newStart = (center-hiddenDurationBefore) + (this.start - (center-hiddenDurationBefore)) * scale; + var newEnd = (center+hiddenDurationAfter) + (this.end - (center+hiddenDurationAfter)) * scale; - /** - * Return the label - * @return {string} label - */ - Filter.prototype.getLabel = function() { - return this.graph.filterLabel; - }; + // snapping times away from hidden zones + this.startToFront = delta > 0 ? false : true; // used to do the right autocorrection with periodic hidden times + this.endToFront = -delta > 0 ? false : true; // used to do the right autocorrection with periodic hidden times + var safeStart = DateUtil.snapAwayFromHidden(this.body.hiddenDates, newStart, delta, true); + var safeEnd = DateUtil.snapAwayFromHidden(this.body.hiddenDates, newEnd, -delta, true); + if (safeStart != newStart || safeEnd != newEnd) { + newStart = safeStart; + newEnd = safeEnd; + } + this.setRange(newStart, newEnd, false, true); - /** - * Return the columnIndex of the filter - * @return {Number} columnIndex - */ - Filter.prototype.getColumn = function() { - return this.column; + this.startToFront = false; // revert to default + this.endToFront = true; // revert to default }; - /** - * Return the currently selected value. Returns undefined if there is no selection - * @return {*} value - */ - Filter.prototype.getSelectedValue = function() { - if (this.index === undefined) - return undefined; - return this.values[this.index]; - }; /** - * Retrieve all values of the filter - * @return {Array} values + * Move the range with a given delta to the left or right. Start and end + * value will be adjusted. For example, try delta = 0.1 or -0.1 + * @param {Number} delta Moving amount. Positive value will move right, + * negative value will move left */ - Filter.prototype.getValues = function() { - return this.values; - }; + Range.prototype.move = function(delta) { + // zoom start Date and end Date relative to the centerDate + var diff = (this.end - this.start); - /** - * Retrieve one value of the filter - * @param {Number} index - * @return {*} value - */ - Filter.prototype.getValue = function(index) { - if (index >= this.values.length) - throw 'Error: index out of range'; + // apply new values + var newStart = this.start + diff * delta; + var newEnd = this.end + diff * delta; - return this.values[index]; - }; + // TODO: reckon with min and max range + this.start = newStart; + this.end = newEnd; + }; /** - * Retrieve the (filtered) dataPoints for the currently selected filter index - * @param {Number} [index] (optional) - * @return {Array} dataPoints + * Move the range to a new center point + * @param {Number} moveTo New center point of the range */ - Filter.prototype._getDataPoints = function(index) { - if (index === undefined) - index = this.index; + Range.prototype.moveTo = function(moveTo) { + var center = (this.start + this.end) / 2; - if (index === undefined) - return []; + var diff = center - moveTo; - var dataPoints; - if (this.dataPoints[index]) { - dataPoints = this.dataPoints[index]; - } - else { - var f = {}; - f.column = this.column; - f.value = this.values[index]; + // calculate new start and end + var newStart = this.start - diff; + var newEnd = this.end - diff; - var dataView = new DataView(this.data,{filter: function (item) {return (item[f.column] == f.value);}}).get(); - dataPoints = this.graph._getDataPoints(dataView); + this.setRange(newStart, newEnd); + }; - this.dataPoints[index] = dataPoints; - } + module.exports = Range; - return dataPoints; - }; +/***/ }, +/* 18 */ +/***/ function(module, exports, __webpack_require__) { + // Utility functions for ordering and stacking of items + var EPSILON = 0.001; // used when checking collisions, to prevent round-off errors /** - * Set a callback function when the filter is fully loaded. + * Order items by their start data + * @param {Item[]} items */ - Filter.prototype.setOnLoadCallback = function(callback) { - this.onLoadCallback = callback; + exports.orderByStart = function(items) { + items.sort(function (a, b) { + return a.data.start - b.data.start; + }); }; - /** - * Add a value to the list with available values for this filter - * No double entries will be created. - * @param {Number} index + * Order items by their end date. If they have no end date, their start date + * is used. + * @param {Item[]} items */ - Filter.prototype.selectValue = function(index) { - if (index >= this.values.length) - throw 'Error: index out of range'; + exports.orderByEnd = function(items) { + items.sort(function (a, b) { + var aTime = ('end' in a.data) ? a.data.end : a.data.start, + bTime = ('end' in b.data) ? b.data.end : b.data.start; - this.index = index; - this.value = this.values[index]; + return aTime - bTime; + }); }; /** - * Load all filtered rows in the background one by one - * Start this method without providing an index! + * Adjust vertical positions of the items such that they don't overlap each + * other. + * @param {Item[]} items + * All visible items + * @param {{item: {horizontal: number, vertical: number}, axis: number}} margin + * Margins between items and between items and the axis. + * @param {boolean} [force=false] + * If true, all items will be repositioned. If false (default), only + * items having a top===null will be re-stacked */ - Filter.prototype.loadInBackground = function(index) { - if (index === undefined) - index = 0; + exports.stack = function(items, margin, force) { + var i, iMax; - var frame = this.graph.frame; + if (force) { + // reset top position of all items + for (i = 0, iMax = items.length; i < iMax; i++) { + items[i].top = null; + } + } - if (index < this.values.length) { - var dataPointsTemp = this._getDataPoints(index); - //this.graph.redrawInfo(); // TODO: not neat + // calculate new, non-overlapping positions + for (i = 0, iMax = items.length; i < iMax; i++) { + var item = items[i]; + if (item.stack && item.top === null) { + // initialize top position + item.top = margin.axis; - // create a progress box - if (frame.progress === undefined) { - frame.progress = document.createElement('DIV'); - frame.progress.style.position = 'absolute'; - frame.progress.style.color = 'gray'; - frame.appendChild(frame.progress); - } - var progress = this.getLoadedProgress(); - frame.progress.innerHTML = 'Loading animation... ' + progress + '%'; - // TODO: this is no nice solution... - frame.progress.style.bottom = 60 + 'px'; // TODO: use height of slider - frame.progress.style.left = 10 + 'px'; + do { + // TODO: optimize checking for overlap. when there is a gap without items, + // you only need to check for items from the next item on, not from zero + var collidingItem = null; + for (var j = 0, jj = items.length; j < jj; j++) { + var other = items[j]; + if (other.top !== null && other !== item && other.stack && exports.collision(item, other, margin.item)) { + collidingItem = other; + break; + } + } - var me = this; - setTimeout(function() {me.loadInBackground(index+1);}, 10); - this.loaded = false; + if (collidingItem != null) { + // There is a collision. Reposition the items above the colliding element + item.top = collidingItem.top + collidingItem.height + margin.item.vertical; + } + } while (collidingItem); + } } - else { - this.loaded = true; + }; - // remove the progress box - if (frame.progress !== undefined) { - frame.removeChild(frame.progress); - frame.progress = undefined; - } - if (this.onLoadCallback) - this.onLoadCallback(); + /** + * Adjust vertical positions of the items without stacking them + * @param {Item[]} items + * All visible items + * @param {{item: {horizontal: number, vertical: number}, axis: number}} margin + * Margins between items and between items and the axis. + */ + exports.nostack = function(items, margin, subgroups) { + var i, iMax, newTop; + + // reset top position of all items + for (i = 0, iMax = items.length; i < iMax; i++) { + if (items[i].data.subgroup !== undefined) { + newTop = margin.axis; + for (var subgroup in subgroups) { + if (subgroups.hasOwnProperty(subgroup)) { + if (subgroups[subgroup].visible == true && subgroups[subgroup].index < subgroups[items[i].data.subgroup].index) { + newTop += subgroups[subgroup].height + margin.item.vertical; + } + } + } + items[i].top = newTop; + } + else { + items[i].top = margin.axis; + } } }; - module.exports = Filter; + /** + * Test if the two provided items collide + * The items must have parameters left, width, top, and height. + * @param {Item} a The first item + * @param {Item} b The second item + * @param {{horizontal: number, vertical: number}} margin + * An object containing a horizontal and vertical + * minimum required margin. + * @return {boolean} true if a and b collide, else false + */ + exports.collision = function(a, b, margin) { + return ((a.left - margin.horizontal + EPSILON) < (b.left + b.width) && + (a.left + a.width + margin.horizontal - EPSILON) > b.left && + (a.top - margin.vertical + EPSILON) < (b.top + b.height) && + (a.top + a.height + margin.vertical - EPSILON) > b.top); + }; /***/ }, -/* 16 */ +/* 19 */ /***/ function(module, exports, __webpack_require__) { + var moment = __webpack_require__(44); + var DateUtil = __webpack_require__(15); var util = __webpack_require__(1); /** - * @constructor Slider + * @constructor TimeStep + * The class TimeStep is an iterator for dates. You provide a start date and an + * end date. The class itself determines the best scale (step size) based on the + * provided start Date, end Date, and minimumStep. * - * An html slider control with start/stop/prev/next buttons - * @param {Element} container The element where the slider will be created - * @param {Object} options Available options: - * {boolean} visible If true (default) the - * slider is visible. + * If minimumStep is provided, the step size is chosen as close as possible + * to the minimumStep but larger than minimumStep. If minimumStep is not + * provided, the scale is set to 1 DAY. + * The minimumStep should correspond with the onscreen size of about 6 characters + * + * Alternatively, you can set a scale by hand. + * After creation, you can initialize the class by executing first(). Then you + * can iterate from the start date to the end date via next(). You can check if + * the end date is reached with the function hasNext(). After each step, you can + * retrieve the current date via getCurrent(). + * The TimeStep has scales ranging from milliseconds, seconds, minutes, hours, + * days, to years. + * + * Version: 1.2 + * + * @param {Date} [start] The start date, for example new Date(2010, 9, 21) + * or new Date(2010, 9, 21, 23, 45, 00) + * @param {Date} [end] The end date + * @param {Number} [minimumStep] Optional. Minimum step size in milliseconds */ - function Slider(container, options) { - if (container === undefined) { - throw 'Error: No container element defined'; - } - this.container = container; - this.visible = (options && options.visible != undefined) ? options.visible : true; - - if (this.visible) { - this.frame = document.createElement('DIV'); - //this.frame.style.backgroundColor = '#E5E5E5'; - this.frame.style.width = '100%'; - this.frame.style.position = 'relative'; - this.container.appendChild(this.frame); - - this.frame.prev = document.createElement('INPUT'); - this.frame.prev.type = 'BUTTON'; - this.frame.prev.value = 'Prev'; - this.frame.appendChild(this.frame.prev); - - this.frame.play = document.createElement('INPUT'); - this.frame.play.type = 'BUTTON'; - this.frame.play.value = 'Play'; - this.frame.appendChild(this.frame.play); - - this.frame.next = document.createElement('INPUT'); - this.frame.next.type = 'BUTTON'; - this.frame.next.value = 'Next'; - this.frame.appendChild(this.frame.next); + function TimeStep(start, end, minimumStep, hiddenDates) { + // variables + this.current = new Date(); + this._start = new Date(); + this._end = new Date(); - this.frame.bar = document.createElement('INPUT'); - this.frame.bar.type = 'BUTTON'; - this.frame.bar.style.position = 'absolute'; - this.frame.bar.style.border = '1px solid red'; - this.frame.bar.style.width = '100px'; - this.frame.bar.style.height = '6px'; - this.frame.bar.style.borderRadius = '2px'; - this.frame.bar.style.MozBorderRadius = '2px'; - this.frame.bar.style.border = '1px solid #7F7F7F'; - this.frame.bar.style.backgroundColor = '#E5E5E5'; - this.frame.appendChild(this.frame.bar); + this.autoScale = true; + this.scale = 'day'; + this.step = 1; - this.frame.slide = document.createElement('INPUT'); - this.frame.slide.type = 'BUTTON'; - this.frame.slide.style.margin = '0px'; - this.frame.slide.value = ' '; - this.frame.slide.style.position = 'relative'; - this.frame.slide.style.left = '-100px'; - this.frame.appendChild(this.frame.slide); + // initialize the range + this.setRange(start, end, minimumStep); - // create events - var me = this; - this.frame.slide.onmousedown = function (event) {me._onMouseDown(event);}; - this.frame.prev.onclick = function (event) {me.prev(event);}; - this.frame.play.onclick = function (event) {me.togglePlay(event);}; - this.frame.next.onclick = function (event) {me.next(event);}; + // hidden Dates options + this.switchedDay = false; + this.switchedMonth = false; + this.switchedYear = false; + this.hiddenDates = hiddenDates; + if (hiddenDates === undefined) { + this.hiddenDates = []; } - this.onChangeCallback = undefined; - - this.values = []; - this.index = undefined; - - this.playTimeout = undefined; - this.playInterval = 1000; // milliseconds - this.playLoop = true; + this.format = TimeStep.FORMAT; // default formatting } - /** - * Select the previous index - */ - Slider.prototype.prev = function() { - var index = this.getIndex(); - if (index > 0) { - index--; - this.setIndex(index); + // Time formatting + TimeStep.FORMAT = { + minorLabels: { + millisecond:'SSS', + second: 's', + minute: 'HH:mm', + hour: 'HH:mm', + weekday: 'ddd D', + day: 'D', + month: 'MMM', + year: 'YYYY' + }, + majorLabels: { + millisecond:'HH:mm:ss', + second: 'D MMMM HH:mm', + minute: 'ddd D MMMM', + hour: 'ddd D MMMM', + weekday: 'MMMM YYYY', + day: 'MMMM YYYY', + month: 'YYYY', + year: '' } }; /** - * Select the next index + * Set custom formatting for the minor an major labels of the TimeStep. + * Both `minorLabels` and `majorLabels` are an Object with properties: + * 'millisecond, 'second, 'minute', 'hour', 'weekday, 'day, 'month, 'year'. + * @param {{minorLabels: Object, majorLabels: Object}} format */ - Slider.prototype.next = function() { - var index = this.getIndex(); - if (index < this.values.length - 1) { - index++; - this.setIndex(index); - } + TimeStep.prototype.setFormat = function (format) { + var defaultFormat = util.deepExtend({}, TimeStep.FORMAT); + this.format = util.deepExtend(defaultFormat, format); }; /** - * Select the next index - */ - Slider.prototype.playNext = function() { - var start = new Date(); - - var index = this.getIndex(); - if (index < this.values.length - 1) { - index++; - this.setIndex(index); - } - else if (this.playLoop) { - // jump to the start - index = 0; - this.setIndex(index); + * Set a new range + * If minimumStep is provided, the step size is chosen as close as possible + * to the minimumStep but larger than minimumStep. If minimumStep is not + * provided, the scale is set to 1 DAY. + * The minimumStep should correspond with the onscreen size of about 6 characters + * @param {Date} [start] The start date and time. + * @param {Date} [end] The end date and time. + * @param {int} [minimumStep] Optional. Minimum step size in milliseconds + */ + TimeStep.prototype.setRange = function(start, end, minimumStep) { + if (!(start instanceof Date) || !(end instanceof Date)) { + throw "No legal start or end date in method setRange"; } - var end = new Date(); - var diff = (end - start); - - // calculate how much time it to to set the index and to execute the callback - // function. - var interval = Math.max(this.playInterval - diff, 0); - // document.title = diff // TODO: cleanup + this._start = (start != undefined) ? new Date(start.valueOf()) : new Date(); + this._end = (end != undefined) ? new Date(end.valueOf()) : new Date(); - var me = this; - this.playTimeout = setTimeout(function() {me.playNext();}, interval); + if (this.autoScale) { + this.setMinimumStep(minimumStep); + } }; /** - * Toggle start or stop playing + * Set the range iterator to the start date. */ - Slider.prototype.togglePlay = function() { - if (this.playTimeout === undefined) { - this.play(); - } else { - this.stop(); - } + TimeStep.prototype.first = function() { + this.current = new Date(this._start.valueOf()); + this.roundToMinor(); }; /** - * Start playing + * Round the current date to the first minor date value + * This must be executed once when the current date is set to start Date */ - Slider.prototype.play = function() { - // Test whether already playing - if (this.playTimeout) return; - - this.playNext(); + TimeStep.prototype.roundToMinor = function() { + // round to floor + // IMPORTANT: we have no breaks in this switch! (this is no bug) + // noinspection FallThroughInSwitchStatementJS + switch (this.scale) { + case 'year': + this.current.setFullYear(this.step * Math.floor(this.current.getFullYear() / this.step)); + this.current.setMonth(0); + case 'month': this.current.setDate(1); + case 'day': // intentional fall through + case 'weekday': this.current.setHours(0); + case 'hour': this.current.setMinutes(0); + case 'minute': this.current.setSeconds(0); + case 'second': this.current.setMilliseconds(0); + //case 'millisecond': // nothing to do for milliseconds + } - if (this.frame) { - this.frame.play.value = 'Stop'; + if (this.step != 1) { + // round down to the first minor value that is a multiple of the current step size + switch (this.scale) { + case 'millisecond': this.current.setMilliseconds(this.current.getMilliseconds() - this.current.getMilliseconds() % this.step); break; + case 'second': this.current.setSeconds(this.current.getSeconds() - this.current.getSeconds() % this.step); break; + case 'minute': this.current.setMinutes(this.current.getMinutes() - this.current.getMinutes() % this.step); break; + case 'hour': this.current.setHours(this.current.getHours() - this.current.getHours() % this.step); break; + case 'weekday': // intentional fall through + case 'day': this.current.setDate((this.current.getDate()-1) - (this.current.getDate()-1) % this.step + 1); break; + case 'month': this.current.setMonth(this.current.getMonth() - this.current.getMonth() % this.step); break; + case 'year': this.current.setFullYear(this.current.getFullYear() - this.current.getFullYear() % this.step); break; + default: break; + } } }; /** - * Stop playing + * Check if the there is a next step + * @return {boolean} true if the current date has not passed the end date */ - Slider.prototype.stop = function() { - clearInterval(this.playTimeout); - this.playTimeout = undefined; - - if (this.frame) { - this.frame.play.value = 'Play'; - } + TimeStep.prototype.hasNext = function () { + return (this.current.valueOf() <= this._end.valueOf()); }; /** - * Set a callback function which will be triggered when the value of the - * slider bar has changed. + * Do the next step */ - Slider.prototype.setOnChangeCallback = function(callback) { - this.onChangeCallback = callback; + TimeStep.prototype.next = function() { + var prev = this.current.valueOf(); + + // Two cases, needed to prevent issues with switching daylight savings + // (end of March and end of October) + if (this.current.getMonth() < 6) { + switch (this.scale) { + case 'millisecond': + + this.current = new Date(this.current.valueOf() + this.step); break; + case 'second': this.current = new Date(this.current.valueOf() + this.step * 1000); break; + case 'minute': this.current = new Date(this.current.valueOf() + this.step * 1000 * 60); break; + case 'hour': + this.current = new Date(this.current.valueOf() + this.step * 1000 * 60 * 60); + // in case of skipping an hour for daylight savings, adjust the hour again (else you get: 0h 5h 9h ... instead of 0h 4h 8h ...) + var h = this.current.getHours(); + this.current.setHours(h - (h % this.step)); + break; + case 'weekday': // intentional fall through + case 'day': this.current.setDate(this.current.getDate() + this.step); break; + case 'month': this.current.setMonth(this.current.getMonth() + this.step); break; + case 'year': this.current.setFullYear(this.current.getFullYear() + this.step); break; + default: break; + } + } + else { + switch (this.scale) { + case 'millisecond': this.current = new Date(this.current.valueOf() + this.step); break; + case 'second': this.current.setSeconds(this.current.getSeconds() + this.step); break; + case 'minute': this.current.setMinutes(this.current.getMinutes() + this.step); break; + case 'hour': this.current.setHours(this.current.getHours() + this.step); break; + case 'weekday': // intentional fall through + case 'day': this.current.setDate(this.current.getDate() + this.step); break; + case 'month': this.current.setMonth(this.current.getMonth() + this.step); break; + case 'year': this.current.setFullYear(this.current.getFullYear() + this.step); break; + default: break; + } + } + + if (this.step != 1) { + // round down to the correct major value + switch (this.scale) { + case 'millisecond': if(this.current.getMilliseconds() < this.step) this.current.setMilliseconds(0); break; + case 'second': if(this.current.getSeconds() < this.step) this.current.setSeconds(0); break; + case 'minute': if(this.current.getMinutes() < this.step) this.current.setMinutes(0); break; + case 'hour': if(this.current.getHours() < this.step) this.current.setHours(0); break; + case 'weekday': // intentional fall through + case 'day': if(this.current.getDate() < this.step+1) this.current.setDate(1); break; + case 'month': if(this.current.getMonth() < this.step) this.current.setMonth(0); break; + case 'year': break; // nothing to do for year + default: break; + } + } + + // safety mechanism: if current time is still unchanged, move to the end + if (this.current.valueOf() == prev) { + this.current = new Date(this._end.valueOf()); + } + + DateUtil.stepOverHiddenDates(this, prev); }; + /** - * Set the interval for playing the list - * @param {Number} interval The interval in milliseconds + * Get the current datetime + * @return {Date} current The current date */ - Slider.prototype.setPlayInterval = function(interval) { - this.playInterval = interval; + TimeStep.prototype.getCurrent = function() { + return this.current; }; /** - * Retrieve the current play interval - * @return {Number} interval The interval in milliseconds + * Set a custom scale. Autoscaling will be disabled. + * For example setScale('minute', 5) will result + * in minor steps of 5 minutes, and major steps of an hour. + * + * @param {{scale: string, step: number}} params + * An object containing two properties: + * - A string 'scale'. Choose from 'millisecond', 'second', + * 'minute', 'hour', 'weekday, 'day, 'month, 'year'. + * - A number 'step'. A step size, by default 1. + * Choose for example 1, 2, 5, or 10. */ - Slider.prototype.getPlayInterval = function(interval) { - return this.playInterval; + TimeStep.prototype.setScale = function(params) { + if (params && typeof params.scale == 'string') { + this.scale = params.scale; + this.step = params.step > 0 ? params.step : 1; + this.autoScale = false; + } }; /** - * Set looping on or off - * @pararm {boolean} doLoop If true, the slider will jump to the start when - * the end is passed, and will jump to the end - * when the start is passed. + * Enable or disable autoscaling + * @param {boolean} enable If true, autoascaling is set true */ - Slider.prototype.setPlayLoop = function(doLoop) { - this.playLoop = doLoop; + TimeStep.prototype.setAutoScale = function (enable) { + this.autoScale = enable; }; /** - * Execute the onchange callback function + * Automatically determine the scale that bests fits the provided minimum step + * @param {Number} [minimumStep] The minimum step size in milliseconds */ - Slider.prototype.onChange = function() { - if (this.onChangeCallback !== undefined) { - this.onChangeCallback(); + TimeStep.prototype.setMinimumStep = function(minimumStep) { + if (minimumStep == undefined) { + return; } - }; - /** - * redraw the slider on the correct place - */ - Slider.prototype.redraw = function() { - if (this.frame) { - // resize the bar - this.frame.bar.style.top = (this.frame.clientHeight/2 - - this.frame.bar.offsetHeight/2) + 'px'; - this.frame.bar.style.width = (this.frame.clientWidth - - this.frame.prev.clientWidth - - this.frame.play.clientWidth - - this.frame.next.clientWidth - 30) + 'px'; + //var b = asc + ds; - // position the slider button - var left = this.indexToLeft(this.index); - this.frame.slide.style.left = (left) + 'px'; - } - }; + var stepYear = (1000 * 60 * 60 * 24 * 30 * 12); + var stepMonth = (1000 * 60 * 60 * 24 * 30); + var stepDay = (1000 * 60 * 60 * 24); + var stepHour = (1000 * 60 * 60); + var stepMinute = (1000 * 60); + var stepSecond = (1000); + var stepMillisecond= (1); + // find the smallest step that is larger than the provided minimumStep + if (stepYear*1000 > minimumStep) {this.scale = 'year'; this.step = 1000;} + if (stepYear*500 > minimumStep) {this.scale = 'year'; this.step = 500;} + if (stepYear*100 > minimumStep) {this.scale = 'year'; this.step = 100;} + if (stepYear*50 > minimumStep) {this.scale = 'year'; this.step = 50;} + if (stepYear*10 > minimumStep) {this.scale = 'year'; this.step = 10;} + if (stepYear*5 > minimumStep) {this.scale = 'year'; this.step = 5;} + if (stepYear > minimumStep) {this.scale = 'year'; this.step = 1;} + if (stepMonth*3 > minimumStep) {this.scale = 'month'; this.step = 3;} + if (stepMonth > minimumStep) {this.scale = 'month'; this.step = 1;} + if (stepDay*5 > minimumStep) {this.scale = 'day'; this.step = 5;} + if (stepDay*2 > minimumStep) {this.scale = 'day'; this.step = 2;} + if (stepDay > minimumStep) {this.scale = 'day'; this.step = 1;} + if (stepDay/2 > minimumStep) {this.scale = 'weekday'; this.step = 1;} + if (stepHour*4 > minimumStep) {this.scale = 'hour'; this.step = 4;} + if (stepHour > minimumStep) {this.scale = 'hour'; this.step = 1;} + if (stepMinute*15 > minimumStep) {this.scale = 'minute'; this.step = 15;} + if (stepMinute*10 > minimumStep) {this.scale = 'minute'; this.step = 10;} + if (stepMinute*5 > minimumStep) {this.scale = 'minute'; this.step = 5;} + if (stepMinute > minimumStep) {this.scale = 'minute'; this.step = 1;} + if (stepSecond*15 > minimumStep) {this.scale = 'second'; this.step = 15;} + if (stepSecond*10 > minimumStep) {this.scale = 'second'; this.step = 10;} + if (stepSecond*5 > minimumStep) {this.scale = 'second'; this.step = 5;} + if (stepSecond > minimumStep) {this.scale = 'second'; this.step = 1;} + if (stepMillisecond*200 > minimumStep) {this.scale = 'millisecond'; this.step = 200;} + if (stepMillisecond*100 > minimumStep) {this.scale = 'millisecond'; this.step = 100;} + if (stepMillisecond*50 > minimumStep) {this.scale = 'millisecond'; this.step = 50;} + if (stepMillisecond*10 > minimumStep) {this.scale = 'millisecond'; this.step = 10;} + if (stepMillisecond*5 > minimumStep) {this.scale = 'millisecond'; this.step = 5;} + if (stepMillisecond > minimumStep) {this.scale = 'millisecond'; this.step = 1;} + }; /** - * Set the list with values for the slider - * @param {Array} values A javascript array with values (any type) + * Snap a date to a rounded value. + * The snap intervals are dependent on the current scale and step. + * @param {Date} date the date to be snapped. + * @return {Date} snappedDate */ - Slider.prototype.setValues = function(values) { - this.values = values; + TimeStep.prototype.snap = function(date) { + var clone = new Date(date.valueOf()); - if (this.values.length > 0) - this.setIndex(0); - else - this.index = undefined; + if (this.scale == 'year') { + var year = clone.getFullYear() + Math.round(clone.getMonth() / 12); + clone.setFullYear(Math.round(year / this.step) * this.step); + clone.setMonth(0); + clone.setDate(0); + clone.setHours(0); + clone.setMinutes(0); + clone.setSeconds(0); + clone.setMilliseconds(0); + } + else if (this.scale == 'month') { + if (clone.getDate() > 15) { + clone.setDate(1); + clone.setMonth(clone.getMonth() + 1); + // important: first set Date to 1, after that change the month. + } + else { + clone.setDate(1); + } + + clone.setHours(0); + clone.setMinutes(0); + clone.setSeconds(0); + clone.setMilliseconds(0); + } + else if (this.scale == 'day') { + //noinspection FallthroughInSwitchStatementJS + switch (this.step) { + case 5: + case 2: + clone.setHours(Math.round(clone.getHours() / 24) * 24); break; + default: + clone.setHours(Math.round(clone.getHours() / 12) * 12); break; + } + clone.setMinutes(0); + clone.setSeconds(0); + clone.setMilliseconds(0); + } + else if (this.scale == 'weekday') { + //noinspection FallthroughInSwitchStatementJS + switch (this.step) { + case 5: + case 2: + clone.setHours(Math.round(clone.getHours() / 12) * 12); break; + default: + clone.setHours(Math.round(clone.getHours() / 6) * 6); break; + } + clone.setMinutes(0); + clone.setSeconds(0); + clone.setMilliseconds(0); + } + else if (this.scale == 'hour') { + switch (this.step) { + case 4: + clone.setMinutes(Math.round(clone.getMinutes() / 60) * 60); break; + default: + clone.setMinutes(Math.round(clone.getMinutes() / 30) * 30); break; + } + clone.setSeconds(0); + clone.setMilliseconds(0); + } else if (this.scale == 'minute') { + //noinspection FallthroughInSwitchStatementJS + switch (this.step) { + case 15: + case 10: + clone.setMinutes(Math.round(clone.getMinutes() / 5) * 5); + clone.setSeconds(0); + break; + case 5: + clone.setSeconds(Math.round(clone.getSeconds() / 60) * 60); break; + default: + clone.setSeconds(Math.round(clone.getSeconds() / 30) * 30); break; + } + clone.setMilliseconds(0); + } + else if (this.scale == 'second') { + //noinspection FallthroughInSwitchStatementJS + switch (this.step) { + case 15: + case 10: + clone.setSeconds(Math.round(clone.getSeconds() / 5) * 5); + clone.setMilliseconds(0); + break; + case 5: + clone.setMilliseconds(Math.round(clone.getMilliseconds() / 1000) * 1000); break; + default: + clone.setMilliseconds(Math.round(clone.getMilliseconds() / 500) * 500); break; + } + } + else if (this.scale == 'millisecond') { + var step = this.step > 5 ? this.step / 2 : 1; + clone.setMilliseconds(Math.round(clone.getMilliseconds() / step) * step); + } + + return clone; }; /** - * Select a value by its index - * @param {Number} index + * Check if the current value is a major value (for example when the step + * is DAY, a major value is each first day of the MONTH) + * @return {boolean} true if current date is major, else false. */ - Slider.prototype.setIndex = function(index) { - if (index < this.values.length) { - this.index = index; - - this.redraw(); - this.onChange(); + TimeStep.prototype.isMajor = function() { + if (this.switchedYear == true) { + this.switchedYear = false; + switch (this.scale) { + case 'year': + case 'month': + case 'weekday': + case 'day': + case 'hour': + case 'minute': + case 'second': + case 'millisecond': + return true; + default: + return false; + } } - else { - throw 'Error: index out of range'; + else if (this.switchedMonth == true) { + this.switchedMonth = false; + switch (this.scale) { + case 'weekday': + case 'day': + case 'hour': + case 'minute': + case 'second': + case 'millisecond': + return true; + default: + return false; + } + } + else if (this.switchedDay == true) { + this.switchedDay = false; + switch (this.scale) { + case 'millisecond': + case 'second': + case 'minute': + case 'hour': + return true; + default: + return false; + } + } + + switch (this.scale) { + case 'millisecond': + return (this.current.getMilliseconds() == 0); + case 'second': + return (this.current.getSeconds() == 0); + case 'minute': + return (this.current.getHours() == 0) && (this.current.getMinutes() == 0); + case 'hour': + return (this.current.getHours() == 0); + case 'weekday': // intentional fall through + case 'day': + return (this.current.getDate() == 1); + case 'month': + return (this.current.getMonth() == 0); + case 'year': + return false; + default: + return false; } }; + /** - * retrieve the index of the currently selected vaue - * @return {Number} index + * Returns formatted text for the minor axislabel, depending on the current + * date and the scale. For example when scale is MINUTE, the current time is + * formatted as "hh:mm". + * @param {Date} [date] custom date. if not provided, current date is taken */ - Slider.prototype.getIndex = function() { - return this.index; - }; + TimeStep.prototype.getLabelMinor = function(date) { + if (date == undefined) { + date = this.current; + } + var format = this.format.minorLabels[this.scale]; + return (format && format.length > 0) ? moment(date).format(format) : ''; + }; /** - * retrieve the currently selected value - * @return {*} value + * Returns formatted text for the major axis label, depending on the current + * date and the scale. For example when scale is MINUTE, the major scale is + * hours, and the hour will be formatted as "hh". + * @param {Date} [date] custom date. if not provided, current date is taken */ - Slider.prototype.get = function() { - return this.values[this.index]; - }; + TimeStep.prototype.getLabelMajor = function(date) { + if (date == undefined) { + date = this.current; + } + var format = this.format.majorLabels[this.scale]; + return (format && format.length > 0) ? moment(date).format(format) : ''; + }; - Slider.prototype._onMouseDown = function(event) { - // only react on left mouse button down - var leftButtonDown = event.which ? (event.which === 1) : (event.button === 1); - if (!leftButtonDown) return; + TimeStep.prototype.getClassName = function() { + var m = moment(this.current); + var date = m.locale ? m.locale('en') : m.lang('en'); // old versions of moment have .lang() function + var step = this.step; - this.startClientX = event.clientX; - this.startSlideX = parseFloat(this.frame.slide.style.left); + function even(value) { + return (value / step % 2 == 0) ? ' even' : ' odd'; + } - this.frame.style.cursor = 'move'; + function today(date) { + if (date.isSame(new Date(), 'day')) { + return ' today'; + } + if (date.isSame(moment().add(1, 'day'), 'day')) { + return ' tomorrow'; + } + if (date.isSame(moment().add(-1, 'day'), 'day')) { + return ' yesterday'; + } + return ''; + } - // add event listeners to handle moving the contents - // we store the function onmousemove and onmouseup in the graph, so we can - // remove the eventlisteners lateron in the function mouseUp() - var me = this; - this.onmousemove = function (event) {me._onMouseMove(event);}; - this.onmouseup = function (event) {me._onMouseUp(event);}; - util.addEventListener(document, 'mousemove', this.onmousemove); - util.addEventListener(document, 'mouseup', this.onmouseup); - util.preventDefault(event); - }; - - - Slider.prototype.leftToIndex = function (left) { - var width = parseFloat(this.frame.bar.style.width) - - this.frame.slide.clientWidth - 10; - var x = left - 3; + function currentWeek(date) { + return date.isSame(new Date(), 'week') ? ' current-week' : ''; + } - var index = Math.round(x / width * (this.values.length-1)); - if (index < 0) index = 0; - if (index > this.values.length-1) index = this.values.length-1; + function currentMonth(date) { + return date.isSame(new Date(), 'month') ? ' current-month' : ''; + } - return index; - }; + function currentYear(date) { + return date.isSame(new Date(), 'year') ? ' current-year' : ''; + } - Slider.prototype.indexToLeft = function (index) { - var width = parseFloat(this.frame.bar.style.width) - - this.frame.slide.clientWidth - 10; + switch (this.scale) { + case 'millisecond': + return even(date.milliseconds()).trim(); - var x = index / (this.values.length-1) * width; - var left = x + 3; + case 'second': + return even(date.seconds()).trim(); - return left; - }; + case 'minute': + return even(date.minutes()).trim(); + case 'hour': + var hours = date.hours(); + if (this.step == 4) { + hours = hours + '-' + (hours + 4); + } + return hours + 'h' + today(date) + even(date.hours()); + case 'weekday': + return date.format('dddd').toLowerCase() + + today(date) + currentWeek(date) + even(date.date()); - Slider.prototype._onMouseMove = function (event) { - var diff = event.clientX - this.startClientX; - var x = this.startSlideX + diff; + case 'day': + var day = date.date(); + var month = date.format('MMMM').toLowerCase(); + return 'day' + day + ' ' + month + currentMonth(date) + even(day - 1); - var index = this.leftToIndex(x); + case 'month': + return date.format('MMMM').toLowerCase() + + currentMonth(date) + even(date.month()); - this.setIndex(index); + case 'year': + var year = date.year(); + return 'year' + year + currentYear(date)+ even(year); - util.preventDefault(); + default: + return ''; + } }; + module.exports = TimeStep; - Slider.prototype._onMouseUp = function (event) { - this.frame.style.cursor = 'auto'; - // remove event listeners - util.removeEventListener(document, 'mousemove', this.onmousemove); - util.removeEventListener(document, 'mouseup', this.onmouseup); +/***/ }, +/* 20 */ +/***/ function(module, exports, __webpack_require__) { - util.preventDefault(); - }; + var Hammer = __webpack_require__(45); + var util = __webpack_require__(1); - module.exports = Slider; + /** + * @constructor Item + * @param {Object} data Object containing (optional) parameters type, + * start, end, content, group, className. + * @param {{toScreen: function, toTime: function}} conversion + * Conversion functions from time to screen and vice versa + * @param {Object} options Configuration options + * // TODO: describe available options + */ + function Item (data, conversion, options) { + this.id = null; + this.parent = null; + this.data = data; + this.dom = null; + this.conversion = conversion || {}; + this.options = options || {}; + this.selected = false; + this.displayed = false; + this.dirty = true; -/***/ }, -/* 17 */ -/***/ function(module, exports, __webpack_require__) { + this.top = null; + this.left = null; + this.width = null; + this.height = null; + } + + Item.prototype.stack = true; /** - * @prototype StepNumber - * The class StepNumber is an iterator for Numbers. You provide a start and end - * value, and a best step size. StepNumber itself rounds to fixed values and - * a finds the step that best fits the provided step. - * - * If prettyStep is true, the step size is chosen as close as possible to the - * provided step, but being a round value like 1, 2, 5, 10, 20, 50, .... - * - * Example usage: - * var step = new StepNumber(0, 10, 2.5, true); - * step.start(); - * while (!step.end()) { - * alert(step.getCurrent()); - * step.next(); - * } - * - * Version: 1.0 - * - * @param {Number} start The start value - * @param {Number} end The end value - * @param {Number} step Optional. Step size. Must be a positive value. - * @param {boolean} prettyStep Optional. If true, the step size is rounded - * To a pretty step size (like 1, 2, 5, 10, 20, 50, ...) + * Select current item */ - function StepNumber(start, end, step, prettyStep) { - // set default values - this._start = 0; - this._end = 0; - this._step = 1; - this.prettyStep = true; - this.precision = 5; - - this._current = 0; - this.setRange(start, end, step, prettyStep); + Item.prototype.select = function() { + this.selected = true; + this.dirty = true; + if (this.displayed) this.redraw(); }; /** - * Set a new range: start, end and step. - * - * @param {Number} start The start value - * @param {Number} end The end value - * @param {Number} step Optional. Step size. Must be a positive value. - * @param {boolean} prettyStep Optional. If true, the step size is rounded - * To a pretty step size (like 1, 2, 5, 10, 20, 50, ...) + * Unselect current item */ - StepNumber.prototype.setRange = function(start, end, step, prettyStep) { - this._start = start ? start : 0; - this._end = end ? end : 0; - - this.setStep(step, prettyStep); + Item.prototype.unselect = function() { + this.selected = false; + this.dirty = true; + if (this.displayed) this.redraw(); }; /** - * Set a new step size - * @param {Number} step New step size. Must be a positive value - * @param {boolean} prettyStep Optional. If true, the provided step is rounded - * to a pretty step size (like 1, 2, 5, 10, 20, 50, ...) + * Set data for the item. Existing data will be updated. The id should not + * be changed. When the item is displayed, it will be redrawn immediately. + * @param {Object} data */ - StepNumber.prototype.setStep = function(step, prettyStep) { - if (step === undefined || step <= 0) - return; - - if (prettyStep !== undefined) - this.prettyStep = prettyStep; - - if (this.prettyStep === true) - this._step = StepNumber.calculatePrettyStep(step); - else - this._step = step; + Item.prototype.setData = function(data) { + this.data = data; + this.dirty = true; + if (this.displayed) this.redraw(); }; /** - * Calculate a nice step size, closest to the desired step size. - * Returns a value in one of the ranges 1*10^n, 2*10^n, or 5*10^n, where n is an - * integer Number. For example 1, 2, 5, 10, 20, 50, etc... - * @param {Number} step Desired step size - * @return {Number} Nice step size + * Set a parent for the item + * @param {ItemSet | Group} parent */ - StepNumber.calculatePrettyStep = function (step) { - var log10 = function (x) {return Math.log(x) / Math.LN10;}; - - // try three steps (multiple of 1, 2, or 5 - var step1 = Math.pow(10, Math.round(log10(step))), - step2 = 2 * Math.pow(10, Math.round(log10(step / 2))), - step5 = 5 * Math.pow(10, Math.round(log10(step / 5))); - - // choose the best step (closest to minimum step) - var prettyStep = step1; - if (Math.abs(step2 - step) <= Math.abs(prettyStep - step)) prettyStep = step2; - if (Math.abs(step5 - step) <= Math.abs(prettyStep - step)) prettyStep = step5; - - // for safety - if (prettyStep <= 0) { - prettyStep = 1; + Item.prototype.setParent = function(parent) { + if (this.displayed) { + this.hide(); + this.parent = parent; + if (this.parent) { + this.show(); + } + } + else { + this.parent = parent; } - - return prettyStep; }; /** - * returns the current value of the step - * @return {Number} current value + * Check whether this item is visible inside given range + * @returns {{start: Number, end: Number}} range with a timestamp for start and end + * @returns {boolean} True if visible */ - StepNumber.prototype.getCurrent = function () { - return parseFloat(this._current.toPrecision(this.precision)); + Item.prototype.isVisible = function(range) { + // Should be implemented by Item implementations + return false; }; /** - * returns the current step size - * @return {Number} current step size + * Show the Item in the DOM (when not already visible) + * @return {Boolean} changed */ - StepNumber.prototype.getStep = function () { - return this._step; + Item.prototype.show = function() { + return false; }; /** - * Set the current value to the largest value smaller than start, which - * is a multiple of the step size + * Hide the Item from the DOM (when visible) + * @return {Boolean} changed */ - StepNumber.prototype.start = function() { - this._current = this._start - this._start % this._step; + Item.prototype.hide = function() { + return false; }; /** - * Do a step, add the step size to the current value + * Repaint the item */ - StepNumber.prototype.next = function () { - this._current += this._step; + Item.prototype.redraw = function() { + // should be implemented by the item }; /** - * Returns true whether the end is reached - * @return {boolean} True if the current value has passed the end value. + * Reposition the Item horizontally */ - StepNumber.prototype.end = function () { - return (this._current > this._end); + Item.prototype.repositionX = function() { + // should be implemented by the item }; - module.exports = StepNumber; - - -/***/ }, -/* 18 */ -/***/ function(module, exports, __webpack_require__) { - - var Emitter = __webpack_require__(11); - var Hammer = __webpack_require__(19); - var util = __webpack_require__(1); - var DataSet = __webpack_require__(7); - var DataView = __webpack_require__(9); - var Range = __webpack_require__(21); - var Core = __webpack_require__(25); - var TimeAxis = __webpack_require__(37); - var CurrentTime = __webpack_require__(39); - var CustomTime = __webpack_require__(41); - var ItemSet = __webpack_require__(26); - /** - * Create a timeline visualization - * @param {HTMLElement} container - * @param {vis.DataSet | Array | google.visualization.DataTable} [items] - * @param {vis.DataSet | Array | google.visualization.DataTable} [groups] - * @param {Object} [options] See Timeline.setOptions for the available options. - * @constructor - * @extends Core + * Reposition the Item vertically */ - function Timeline (container, items, groups, options) { - if (!(this instanceof Timeline)) { - throw new SyntaxError('Constructor must be called with the new operator'); - } - - // if the third element is options, the forth is groups (optionally); - if (!(Array.isArray(groups) || groups instanceof DataSet) && groups instanceof Object) { - var forthArgument = options; - options = groups; - groups = forthArgument; - } - - var me = this; - this.defaultOptions = { - start: null, - end: null, - - autoResize: true, - - orientation: 'bottom', - width: null, - height: null, - maxHeight: null, - minHeight: null - }; - this.options = util.deepExtend({}, this.defaultOptions); - - // Create the DOM, props, and emitter - this._create(container); - - // all components listed here will be repainted automatically - this.components = []; - - this.body = { - dom: this.dom, - domProps: this.props, - emitter: { - on: this.on.bind(this), - off: this.off.bind(this), - emit: this.emit.bind(this) - }, - hiddenDates: [], - util: { - snap: null, // will be specified after TimeAxis is created - toScreen: me._toScreen.bind(me), - toGlobalScreen: me._toGlobalScreen.bind(me), // this refers to the root.width - toTime: me._toTime.bind(me), - toGlobalTime : me._toGlobalTime.bind(me) - } - }; - - // range - this.range = new Range(this.body); - this.components.push(this.range); - this.body.range = this.range; - - // time axis - this.timeAxis = new TimeAxis(this.body); - this.components.push(this.timeAxis); - this.body.util.snap = this.timeAxis.snap.bind(this.timeAxis); - - // current time bar - this.currentTime = new CurrentTime(this.body); - this.components.push(this.currentTime); - - // custom time bar - // Note: time bar will be attached in this.setOptions when selected - this.customTime = new CustomTime(this.body); - this.components.push(this.customTime); - - // item set - this.itemSet = new ItemSet(this.body); - this.components.push(this.itemSet); + Item.prototype.repositionY = function() { + // should be implemented by the item + }; - this.itemsData = null; // DataSet - this.groupsData = null; // DataSet + /** + * Repaint a delete button on the top right of the item when the item is selected + * @param {HTMLElement} anchor + * @protected + */ + Item.prototype._repaintDeleteButton = function (anchor) { + if (this.selected && this.options.editable.remove && !this.dom.deleteButton) { + // create and show button + var me = this; - // apply options - if (options) { - this.setOptions(options); - } + var deleteButton = document.createElement('div'); + deleteButton.className = 'delete'; + deleteButton.title = 'Delete this item'; - // IMPORTANT: THIS HAPPENS BEFORE SET ITEMS! - if (groups) { - this.setGroups(groups); - } + Hammer(deleteButton, { + preventDefault: true + }).on('tap', function (event) { + me.parent.removeFromDataSet(me); + event.stopPropagation(); + }); - // create itemset - if (items) { - this.setItems(items); + anchor.appendChild(deleteButton); + this.dom.deleteButton = deleteButton; } - else { - this.redraw(); + else if (!this.selected && this.dom.deleteButton) { + // remove button + if (this.dom.deleteButton.parentNode) { + this.dom.deleteButton.parentNode.removeChild(this.dom.deleteButton); + } + this.dom.deleteButton = null; } - } - - // Extend the functionality from Core - Timeline.prototype = new Core(); + }; /** - * Set items - * @param {vis.DataSet | Array | google.visualization.DataTable | null} items + * Set HTML contents for the item + * @param {Element} element HTML element to fill with the contents + * @private */ - Timeline.prototype.setItems = function(items) { - var initialLoad = (this.itemsData == null); - - // convert to type DataSet when needed - var newDataSet; - if (!items) { - newDataSet = null; - } - else if (items instanceof DataSet || items instanceof DataView) { - newDataSet = items; + Item.prototype._updateContents = function (element) { + var content; + if (this.options.template) { + var itemData = this.parent.itemSet.itemsData.get(this.id); // get a clone of the data from the dataset + content = this.options.template(itemData); } else { - // turn an array into a dataset - newDataSet = new DataSet(items, { - type: { - start: 'Date', - end: 'Date' - } - }); + content = this.data.content; } - // set items - this.itemsData = newDataSet; - this.itemSet && this.itemSet.setItems(newDataSet); - - if (initialLoad) { - if (this.options.start != undefined || this.options.end != undefined) { - if (this.options.start == undefined || this.options.end == undefined) { - var dataRange = this._getDataRange(); - } - - var start = this.options.start != undefined ? this.options.start : dataRange.start; - var end = this.options.end != undefined ? this.options.end : dataRange.end; - - this.setWindow(start, end, {animate: false}); + if(content !== this.content) { + // only replace the content when changed + if (content instanceof Element) { + element.innerHTML = ''; + element.appendChild(content); + } + else if (content != undefined) { + element.innerHTML = content; } else { - this.fit({animate: false}); + if (!(this.data.type == 'background' && this.data.content === undefined)) { + throw new Error('Property "content" missing in item ' + this.id); + } } + + this.content = content; } }; /** - * Set groups - * @param {vis.DataSet | Array | google.visualization.DataTable} groups + * Set HTML contents for the item + * @param {Element} element HTML element to fill with the contents + * @private */ - Timeline.prototype.setGroups = function(groups) { - // convert to type DataSet when needed - var newDataSet; - if (!groups) { - newDataSet = null; - } - else if (groups instanceof DataSet || groups instanceof DataView) { - newDataSet = groups; + Item.prototype._updateTitle = function (element) { + if (this.data.title != null) { + element.title = this.data.title || ''; } else { - // turn an array into a dataset - newDataSet = new DataSet(groups); + element.removeAttribute('title'); } - - this.groupsData = newDataSet; - this.itemSet.setGroups(newDataSet); }; /** - * Set selected items by their id. Replaces the current selection - * Unknown id's are silently ignored. - * @param {string[] | string} [ids] An array with zero or more id's of the items to be - * selected. If ids is an empty array, all items will be - * unselected. - * @param {Object} [options] Available options: - * `focus: boolean` - * If true, focus will be set to the selected item(s) - * `animate: boolean | number` - * If true (default), the range is animated - * smoothly to the new window. - * If a number, the number is taken as duration - * for the animation. Default duration is 500 ms. - * Only applicable when option focus is true. + * Process dataAttributes timeline option and set as data- attributes on dom.content + * @param {Element} element HTML element to which the attributes will be attached + * @private */ - Timeline.prototype.setSelection = function(ids, options) { - this.itemSet && this.itemSet.setSelection(ids); - - if (options && options.focus) { - this.focus(ids, options); - } - }; + Item.prototype._updateDataAttributes = function(element) { + if (this.options.dataAttributes && this.options.dataAttributes.length > 0) { + var attributes = []; - /** - * Get the selected items by their id - * @return {Array} ids The ids of the selected items - */ - Timeline.prototype.getSelection = function() { - return this.itemSet && this.itemSet.getSelection() || []; - }; - - /** - * Adjust the visible window such that the selected item (or multiple items) - * are centered on screen. - * @param {String | String[]} id An item id or array with item ids - * @param {Object} [options] Available options: - * `animate: boolean | number` - * If true (default), the range is animated - * smoothly to the new window. - * If a number, the number is taken as duration - * for the animation. Default duration is 500 ms. - * Only applicable when option focus is true - */ - Timeline.prototype.focus = function(id, options) { - if (!this.itemsData || id == undefined) return; - - var ids = Array.isArray(id) ? id : [id]; - - // get the specified item(s) - var itemsData = this.itemsData.getDataSet().get(ids, { - type: { - start: 'Date', - end: 'Date' + if (Array.isArray(this.options.dataAttributes)) { + attributes = this.options.dataAttributes; } - }); - - // calculate minimum start and maximum end of specified items - var start = null; - var end = null; - itemsData.forEach(function (itemData) { - var s = itemData.start.valueOf(); - var e = 'end' in itemData ? itemData.end.valueOf() : itemData.start.valueOf(); - - if (start === null || s < start) { - start = s; + else if (this.options.dataAttributes == 'all') { + attributes = Object.keys(this.data); } - - if (end === null || e > end) { - end = e; + else { + return; } - }); - if (start !== null && end !== null) { - // calculate the new middle and interval for the window - var middle = (start + end) / 2; - var interval = Math.max((this.range.end - this.range.start), (end - start) * 1.1); + for (var i = 0; i < attributes.length; i++) { + var name = attributes[i]; + var value = this.data[name]; - var animate = (options && options.animate !== undefined) ? options.animate : true; - this.range.setRange(middle - interval / 2, middle + interval / 2, animate); + if (value != null) { + element.setAttribute('data-' + name, value); + } + else { + element.removeAttribute('data-' + name); + } + } } }; /** - * Get the data range of the item set. - * @returns {{min: Date, max: Date}} range A range with a start and end Date. - * When no minimum is found, min==null - * When no maximum is found, max==null + * Update custom styles of the element + * @param element + * @private */ - Timeline.prototype.getItemRange = function() { - // calculate min from start filed - var dataset = this.itemsData.getDataSet(), - min = null, - max = null; - - if (dataset) { - // calculate the minimum value of the field 'start' - var minItem = dataset.min('start'); - min = minItem ? util.convert(minItem.start, 'Date').valueOf() : null; - // Note: we convert first to Date and then to number because else - // a conversion from ISODate to Number will fail - - // calculate maximum value of fields 'start' and 'end' - var maxStartItem = dataset.max('start'); - if (maxStartItem) { - max = util.convert(maxStartItem.start, 'Date').valueOf(); - } - var maxEndItem = dataset.max('end'); - if (maxEndItem) { - if (max == null) { - max = util.convert(maxEndItem.end, 'Date').valueOf(); - } - else { - max = Math.max(max, util.convert(maxEndItem.end, 'Date').valueOf()); - } - } + Item.prototype._updateStyle = function(element) { + // remove old styles + if (this.style) { + util.removeCssText(element, this.style); + this.style = null; } - return { - min: (min != null) ? new Date(min) : null, - max: (max != null) ? new Date(max) : null - }; + // append new styles + if (this.data.style) { + util.addCssText(element, this.data.style); + this.style = this.data.style; + } }; - - module.exports = Timeline; + module.exports = Item; /***/ }, -/* 19 */ +/* 21 */ /***/ function(module, exports, __webpack_require__) { - // Only load hammer.js when in a browser environment - // (loading hammer.js in a node.js environment gives errors) - if (typeof window !== 'undefined') { - module.exports = window['Hammer'] || __webpack_require__(20); - } - else { - module.exports = function () { - throw Error('hammer.js is only available in a browser, not in node.js.'); - } - } + var Hammer = __webpack_require__(45); + var Item = __webpack_require__(20); + var BackgroundGroup = __webpack_require__(31); + var RangeItem = __webpack_require__(24); + + /** + * @constructor BackgroundItem + * @extends Item + * @param {Object} data Object containing parameters start, end + * content, className. + * @param {{toScreen: function, toTime: function}} conversion + * Conversion functions from time to screen and vice versa + * @param {Object} [options] Configuration options + * // TODO: describe options + */ + // TODO: implement support for the BackgroundItem just having a start, then being displayed as a sort of an annotation + function BackgroundItem (data, conversion, options) { + this.props = { + content: { + width: 0 + } + }; + this.overflow = false; // if contents can overflow (css styling), this flag is set to true + // validate data + if (data) { + if (data.start == undefined) { + throw new Error('Property "start" missing in item ' + data.id); + } + if (data.end == undefined) { + throw new Error('Property "end" missing in item ' + data.id); + } + } -/***/ }, -/* 20 */ -/***/ function(module, exports, __webpack_require__) { + Item.call(this, data, conversion, options); - var __WEBPACK_AMD_DEFINE_RESULT__;/*! Hammer.JS - v1.1.3 - 2014-05-20 - * http://eightmedia.github.io/hammer.js - * - * Copyright (c) 2014 Jorik Tangelder ; - * Licensed under the MIT license */ + this.emptyContent = false; + } - (function(window, undefined) { - 'use strict'; + BackgroundItem.prototype = new Item (null, null, null); - /** - * @main - * @module hammer - * - * @class Hammer - * @static - */ + BackgroundItem.prototype.baseClassName = 'item background'; + BackgroundItem.prototype.stack = false; /** - * Hammer, use this to create instances - * ```` - * var hammertime = new Hammer(myElement); - * ```` - * - * @method Hammer - * @param {HTMLElement} element - * @param {Object} [options={}] - * @return {Hammer.Instance} + * Check whether this item is visible inside given range + * @returns {{start: Number, end: Number}} range with a timestamp for start and end + * @returns {boolean} True if visible */ - var Hammer = function Hammer(element, options) { - return new Hammer.Instance(element, options || {}); + BackgroundItem.prototype.isVisible = function(range) { + // determine visibility + return (this.data.start < range.end) && (this.data.end > range.start); }; /** - * version, as defined in package.json - * the value will be set at each build - * @property VERSION - * @final - * @type {String} - */ - Hammer.VERSION = '1.1.3'; - - /** - * default settings. - * more settings are defined per gesture at `/gestures`. Each gesture can be disabled/enabled - * by setting it's name (like `swipe`) to false. - * You can set the defaults for all instances by changing this object before creating an instance. - * @example - * ```` - * Hammer.defaults.drag = false; - * Hammer.defaults.behavior.touchAction = 'pan-y'; - * delete Hammer.defaults.behavior.userSelect; - * ```` - * @property defaults - * @type {Object} + * Repaint the item */ - Hammer.defaults = { - /** - * this setting object adds styles and attributes to the element to prevent the browser from doing - * its native behavior. The css properties are auto prefixed for the browsers when needed. - * @property defaults.behavior - * @type {Object} - */ - behavior: { - /** - * Disables text selection to improve the dragging gesture. When the value is `none` it also sets - * `onselectstart=false` for IE on the element. Mainly for desktop browsers. - * @property defaults.behavior.userSelect - * @type {String} - * @default 'none' - */ - userSelect: 'none', + BackgroundItem.prototype.redraw = function() { + var dom = this.dom; + if (!dom) { + // create DOM + this.dom = {}; + dom = this.dom; - /** - * Specifies whether and how a given region can be manipulated by the user (for instance, by panning or zooming). - * Used by Chrome 35> and IE10>. By default this makes the element blocking any touch event. - * @property defaults.behavior.touchAction - * @type {String} - * @default: 'pan-y' - */ - touchAction: 'pan-y', + // background box + dom.box = document.createElement('div'); + // className is updated in redraw() - /** - * Disables the default callout shown when you touch and hold a touch target. - * On iOS, when you touch and hold a touch target such as a link, Safari displays - * a callout containing information about the link. This property allows you to disable that callout. - * @property defaults.behavior.touchCallout - * @type {String} - * @default 'none' - */ - touchCallout: 'none', + // contents box + dom.content = document.createElement('div'); + dom.content.className = 'content'; + dom.box.appendChild(dom.content); - /** - * Specifies whether zooming is enabled. Used by IE10> - * @property defaults.behavior.contentZooming - * @type {String} - * @default 'none' - */ - contentZooming: 'none', + // Note: we do NOT attach this item as attribute to the DOM, + // such that background items cannot be selected + //dom.box['timeline-item'] = this; - /** - * Specifies that an entire element should be draggable instead of its contents. - * Mainly for desktop browsers. - * @property defaults.behavior.userDrag - * @type {String} - * @default 'none' - */ - userDrag: 'none', + this.dirty = true; + } - /** - * Overrides the highlight color shown when the user taps a link or a JavaScript - * clickable element in Safari on iPhone. This property obeys the alpha value, if specified. - * - * If you don't specify an alpha value, Safari on iPhone applies a default alpha value - * to the color. To disable tap highlighting, set the alpha value to 0 (invisible). - * If you set the alpha value to 1.0 (opaque), the element is not visible when tapped. - * @property defaults.behavior.tapHighlightColor - * @type {String} - * @default 'rgba(0,0,0,0)' - */ - tapHighlightColor: 'rgba(0,0,0,0)' + // append DOM to parent DOM + if (!this.parent) { + throw new Error('Cannot redraw item: no parent attached'); + } + if (!dom.box.parentNode) { + var background = this.parent.dom.background; + if (!background) { + throw new Error('Cannot redraw item: parent has no background container element'); } - }; + background.appendChild(dom.box); + } + this.displayed = true; - /** - * hammer document where the base events are added at - * @property DOCUMENT - * @type {HTMLElement} - * @default window.document - */ - Hammer.DOCUMENT = document; + // Update DOM when item is marked dirty. An item is marked dirty when: + // - the item is not yet rendered + // - the item's data is changed + // - the item is selected/deselected + if (this.dirty) { + this._updateContents(this.dom.content); + this._updateTitle(this.dom.content); + this._updateDataAttributes(this.dom.content); + this._updateStyle(this.dom.box); - /** - * detect support for pointer events - * @property HAS_POINTEREVENTS - * @type {Boolean} - */ - Hammer.HAS_POINTEREVENTS = navigator.pointerEnabled || navigator.msPointerEnabled; + // update class + var className = (this.data.className ? (' ' + this.data.className) : '') + + (this.selected ? ' selected' : ''); + dom.box.className = this.baseClassName + className; - /** - * detect support for touch events - * @property HAS_TOUCHEVENTS - * @type {Boolean} - */ - Hammer.HAS_TOUCHEVENTS = ('ontouchstart' in window); + // determine from css whether this box has overflow + this.overflow = window.getComputedStyle(dom.content).overflow !== 'hidden'; - /** - * detect mobile browsers - * @property IS_MOBILE - * @type {Boolean} - */ - Hammer.IS_MOBILE = /mobile|tablet|ip(ad|hone|od)|android|silk/i.test(navigator.userAgent); + // recalculate size + this.props.content.width = this.dom.content.offsetWidth; + this.height = 0; // set height zero, so this item will be ignored when stacking items - /** - * detect if we want to support mouseevents at all - * @property NO_MOUSEEVENTS - * @type {Boolean} - */ - Hammer.NO_MOUSEEVENTS = (Hammer.HAS_TOUCHEVENTS && Hammer.IS_MOBILE) || Hammer.HAS_POINTEREVENTS; + this.dirty = false; + } + }; /** - * interval in which Hammer recalculates current velocity/direction/angle in ms - * @property CALCULATE_INTERVAL - * @type {Number} - * @default 25 + * Show the item in the DOM (when not already visible). The items DOM will + * be created when needed. */ - Hammer.CALCULATE_INTERVAL = 25; + BackgroundItem.prototype.show = RangeItem.prototype.show; /** - * eventtypes per touchevent (start, move, end) are filled by `Event.determineEventTypes` on `setup` - * the object contains the DOM event names per type (`EVENT_START`, `EVENT_MOVE`, `EVENT_END`) - * @property EVENT_TYPES - * @private - * @writeOnce - * @type {Object} + * Hide the item from the DOM (when visible) + * @return {Boolean} changed */ - var EVENT_TYPES = {}; + BackgroundItem.prototype.hide = RangeItem.prototype.hide; /** - * direction strings, for safe comparisons - * @property DIRECTION_DOWN|LEFT|UP|RIGHT - * @final - * @type {String} - * @default 'down' 'left' 'up' 'right' + * Reposition the item horizontally + * @Override */ - var DIRECTION_DOWN = Hammer.DIRECTION_DOWN = 'down'; - var DIRECTION_LEFT = Hammer.DIRECTION_LEFT = 'left'; - var DIRECTION_UP = Hammer.DIRECTION_UP = 'up'; - var DIRECTION_RIGHT = Hammer.DIRECTION_RIGHT = 'right'; + BackgroundItem.prototype.repositionX = RangeItem.prototype.repositionX; /** - * pointertype strings, for safe comparisons - * @property POINTER_MOUSE|TOUCH|PEN - * @final - * @type {String} - * @default 'mouse' 'touch' 'pen' + * Reposition the item vertically + * @Override */ - var POINTER_MOUSE = Hammer.POINTER_MOUSE = 'mouse'; - var POINTER_TOUCH = Hammer.POINTER_TOUCH = 'touch'; - var POINTER_PEN = Hammer.POINTER_PEN = 'pen'; + BackgroundItem.prototype.repositionY = function(margin) { + var onTop = this.options.orientation === 'top'; + this.dom.content.style.top = onTop ? '' : '0'; + this.dom.content.style.bottom = onTop ? '0' : ''; + var height; - /** - * eventtypes - * @property EVENT_START|MOVE|END|RELEASE|TOUCH - * @final - * @type {String} - * @default 'start' 'change' 'move' 'end' 'release' 'touch' - */ - var EVENT_START = Hammer.EVENT_START = 'start'; - var EVENT_MOVE = Hammer.EVENT_MOVE = 'move'; - var EVENT_END = Hammer.EVENT_END = 'end'; - var EVENT_RELEASE = Hammer.EVENT_RELEASE = 'release'; - var EVENT_TOUCH = Hammer.EVENT_TOUCH = 'touch'; + // special positioning for subgroups + if (this.data.subgroup !== undefined) { + var itemSubgroup = this.data.subgroup; + var subgroups = this.parent.subgroups; + var subgroupIndex = subgroups[itemSubgroup].index; + // if the orientation is top, we need to take the difference in height into account. + if (onTop == true) { + // the first subgroup will have to account for the distance from the top to the first item. + height = this.parent.subgroups[itemSubgroup].height + margin.item.vertical; + height += subgroupIndex == 0 ? margin.axis - 0.5*margin.item.vertical : 0; + var newTop = this.parent.top; + for (var subgroup in subgroups) { + if (subgroups.hasOwnProperty(subgroup)) { + if (subgroups[subgroup].visible == true && subgroups[subgroup].index < subgroupIndex) { + newTop += subgroups[subgroup].height + margin.item.vertical; + } + } + } - /** - * if the window events are set... - * @property READY - * @writeOnce - * @type {Boolean} - * @default false - */ - Hammer.READY = false; + // the others will have to be offset downwards with this same distance. + newTop += subgroupIndex != 0 ? margin.axis - 0.5 * margin.item.vertical : 0; + this.dom.box.style.top = newTop + 'px'; + this.dom.box.style.bottom = ''; + } + // and when the orientation is bottom: + else { + var newTop = this.parent.top; + for (var subgroup in subgroups) { + if (subgroups.hasOwnProperty(subgroup)) { + if (subgroups[subgroup].visible == true && subgroups[subgroup].index > subgroupIndex) { + newTop += subgroups[subgroup].height + margin.item.vertical; + } + } + } + height = this.parent.subgroups[itemSubgroup].height + margin.item.vertical; + this.dom.box.style.top = newTop + 'px'; + this.dom.box.style.bottom = ''; + } + } + // and in the case of no subgroups: + else { + // we want backgrounds with groups to only show in groups. + if (this.parent instanceof BackgroundGroup) { + // if the item is not in a group: + height = Math.max(this.parent.height, + this.parent.itemSet.body.domProps.center.height, + this.parent.itemSet.body.domProps.centerContainer.height); + this.dom.box.style.top = onTop ? '0' : ''; + this.dom.box.style.bottom = onTop ? '' : '0'; + } + else { + height = this.parent.height; + // same alignment for items when orientation is top or bottom + this.dom.box.style.top = this.parent.top + 'px'; + this.dom.box.style.bottom = ''; + } + } + this.dom.box.style.height = height + 'px'; + }; - /** - * plugins namespace - * @property plugins - * @type {Object} - */ - Hammer.plugins = Hammer.plugins || {}; + module.exports = BackgroundItem; - /** - * gestures namespace - * see `/gestures` for the definitions - * @property gestures - * @type {Object} - */ - Hammer.gestures = Hammer.gestures || {}; - /** - * setup events to detect gestures on the document - * this function is called when creating an new instance - * @private - */ - function setup() { - if(Hammer.READY) { - return; - } +/***/ }, +/* 22 */ +/***/ function(module, exports, __webpack_require__) { - // find what eventtypes we add listeners to - Event.determineEventTypes(); + var Item = __webpack_require__(20); + var util = __webpack_require__(1); - // Register all gestures inside Hammer.gestures - Utils.each(Hammer.gestures, function(gesture) { - Detection.register(gesture); - }); + /** + * @constructor BoxItem + * @extends Item + * @param {Object} data Object containing parameters start + * content, className. + * @param {{toScreen: function, toTime: function}} conversion + * Conversion functions from time to screen and vice versa + * @param {Object} [options] Configuration options + * // TODO: describe available options + */ + function BoxItem (data, conversion, options) { + this.props = { + dot: { + width: 0, + height: 0 + }, + line: { + width: 0, + height: 0 + } + }; - // Add touch events on the document - Event.onTouch(Hammer.DOCUMENT, EVENT_MOVE, Detection.detect); - Event.onTouch(Hammer.DOCUMENT, EVENT_END, Detection.detect); + // validate data + if (data) { + if (data.start == undefined) { + throw new Error('Property "start" missing in item ' + data); + } + } - // Hammer is ready...! - Hammer.READY = true; + Item.call(this, data, conversion, options); } + BoxItem.prototype = new Item (null, null, null); + /** - * @module hammer - * - * @class Utils - * @static + * Check whether this item is visible inside given range + * @returns {{start: Number, end: Number}} range with a timestamp for start and end + * @returns {boolean} True if visible */ - var Utils = Hammer.utils = { - /** - * extend method, could also be used for cloning when `dest` is an empty object. - * changes the dest object - * @method extend - * @param {Object} dest - * @param {Object} src - * @param {Boolean} [merge=false] do a merge - * @return {Object} dest - */ - extend: function extend(dest, src, merge) { - for(var key in src) { - if(!src.hasOwnProperty(key) || (dest[key] !== undefined && merge)) { - continue; - } - dest[key] = src[key]; - } - return dest; - }, + BoxItem.prototype.isVisible = function(range) { + // determine visibility + // TODO: account for the real width of the item. Right now we just add 1/4 to the window + var interval = (range.end - range.start) / 4; + return (this.data.start > range.start - interval) && (this.data.start < range.end + interval); + }; - /** - * simple addEventListener wrapper - * @method on - * @param {HTMLElement} element - * @param {String} type - * @param {Function} handler - */ - on: function on(element, type, handler) { - element.addEventListener(type, handler, false); - }, + /** + * Repaint the item + */ + BoxItem.prototype.redraw = function() { + var dom = this.dom; + if (!dom) { + // create DOM + this.dom = {}; + dom = this.dom; - /** - * simple removeEventListener wrapper - * @method off - * @param {HTMLElement} element - * @param {String} type - * @param {Function} handler - */ - off: function off(element, type, handler) { - element.removeEventListener(type, handler, false); - }, + // create main box + dom.box = document.createElement('DIV'); - /** - * forEach over arrays and objects - * @method each - * @param {Object|Array} obj - * @param {Function} iterator - * @param {any} iterator.item - * @param {Number} iterator.index - * @param {Object|Array} iterator.obj the source object - * @param {Object} context value to use as `this` in the iterator - */ - each: function each(obj, iterator, context) { - var i, len; + // contents box (inside the background box). used for making margins + dom.content = document.createElement('DIV'); + dom.content.className = 'content'; + dom.box.appendChild(dom.content); - // native forEach on arrays - if('forEach' in obj) { - obj.forEach(iterator, context); - // arrays - } else if(obj.length !== undefined) { - for(i = 0, len = obj.length; i < len; i++) { - if(iterator.call(context, obj[i], i, obj) === false) { - return; - } - } - // objects - } else { - for(i in obj) { - if(obj.hasOwnProperty(i) && - iterator.call(context, obj[i], i, obj) === false) { - return; - } - } - } - }, + // line to axis + dom.line = document.createElement('DIV'); + dom.line.className = 'line'; - /** - * find if a string contains the string using indexOf - * @method inStr - * @param {String} src - * @param {String} find - * @return {Boolean} found - */ - inStr: function inStr(src, find) { - return src.indexOf(find) > -1; - }, + // dot on axis + dom.dot = document.createElement('DIV'); + dom.dot.className = 'dot'; - /** - * find if a array contains the object using indexOf or a simple polyfill - * @method inArray - * @param {String} src - * @param {String} find - * @return {Boolean|Number} false when not found, or the index - */ - inArray: function inArray(src, find) { - if(src.indexOf) { - var index = src.indexOf(find); - return (index === -1) ? false : index; - } else { - for(var i = 0, len = src.length; i < len; i++) { - if(src[i] === find) { - return i; - } - } - return false; - } - }, + // attach this item as attribute + dom.box['timeline-item'] = this; - /** - * convert an array-like object (`arguments`, `touchlist`) to an array - * @method toArray - * @param {Object} obj - * @return {Array} - */ - toArray: function toArray(obj) { - return Array.prototype.slice.call(obj, 0); - }, + this.dirty = true; + } - /** - * find if a node is in the given parent - * @method hasParent - * @param {HTMLElement} node - * @param {HTMLElement} parent - * @return {Boolean} found - */ - hasParent: function hasParent(node, parent) { - while(node) { - if(node == parent) { - return true; - } - node = node.parentNode; - } - return false; - }, + // append DOM to parent DOM + if (!this.parent) { + throw new Error('Cannot redraw item: no parent attached'); + } + if (!dom.box.parentNode) { + var foreground = this.parent.dom.foreground; + if (!foreground) throw new Error('Cannot redraw item: parent has no foreground container element'); + foreground.appendChild(dom.box); + } + if (!dom.line.parentNode) { + var background = this.parent.dom.background; + if (!background) throw new Error('Cannot redraw item: parent has no background container element'); + background.appendChild(dom.line); + } + if (!dom.dot.parentNode) { + var axis = this.parent.dom.axis; + if (!background) throw new Error('Cannot redraw item: parent has no axis container element'); + axis.appendChild(dom.dot); + } + this.displayed = true; - /** - * get the center of all the touches - * @method getCenter - * @param {Array} touches - * @return {Object} center contains `pageX`, `pageY`, `clientX` and `clientY` properties - */ - getCenter: function getCenter(touches) { - var pageX = [], - pageY = [], - clientX = [], - clientY = [], - min = Math.min, - max = Math.max; + // Update DOM when item is marked dirty. An item is marked dirty when: + // - the item is not yet rendered + // - the item's data is changed + // - the item is selected/deselected + if (this.dirty) { + this._updateContents(this.dom.content); + this._updateTitle(this.dom.box); + this._updateDataAttributes(this.dom.box); + this._updateStyle(this.dom.box); - // no need to loop when only one touch - if(touches.length === 1) { - return { - pageX: touches[0].pageX, - pageY: touches[0].pageY, - clientX: touches[0].clientX, - clientY: touches[0].clientY - }; - } + // update class + var className = (this.data.className? ' ' + this.data.className : '') + + (this.selected ? ' selected' : ''); + dom.box.className = 'item box' + className; + dom.line.className = 'item line' + className; + dom.dot.className = 'item dot' + className; - Utils.each(touches, function(touch) { - pageX.push(touch.pageX); - pageY.push(touch.pageY); - clientX.push(touch.clientX); - clientY.push(touch.clientY); - }); + // recalculate size + this.props.dot.height = dom.dot.offsetHeight; + this.props.dot.width = dom.dot.offsetWidth; + this.props.line.width = dom.line.offsetWidth; + this.width = dom.box.offsetWidth; + this.height = dom.box.offsetHeight; - return { - pageX: (min.apply(Math, pageX) + max.apply(Math, pageX)) / 2, - pageY: (min.apply(Math, pageY) + max.apply(Math, pageY)) / 2, - clientX: (min.apply(Math, clientX) + max.apply(Math, clientX)) / 2, - clientY: (min.apply(Math, clientY) + max.apply(Math, clientY)) / 2 - }; - }, + this.dirty = false; + } - /** - * calculate the velocity between two points. unit is in px per ms. - * @method getVelocity - * @param {Number} deltaTime - * @param {Number} deltaX - * @param {Number} deltaY - * @return {Object} velocity `x` and `y` - */ - getVelocity: function getVelocity(deltaTime, deltaX, deltaY) { - return { - x: Math.abs(deltaX / deltaTime) || 0, - y: Math.abs(deltaY / deltaTime) || 0 - }; - }, + this._repaintDeleteButton(dom.box); + }; - /** - * calculate the angle between two coordinates - * @method getAngle - * @param {Touch} touch1 - * @param {Touch} touch2 - * @return {Number} angle - */ - getAngle: function getAngle(touch1, touch2) { - var x = touch2.clientX - touch1.clientX, - y = touch2.clientY - touch1.clientY; + /** + * Show the item in the DOM (when not already displayed). The items DOM will + * be created when needed. + */ + BoxItem.prototype.show = function() { + if (!this.displayed) { + this.redraw(); + } + }; - return Math.atan2(y, x) * 180 / Math.PI; - }, + /** + * Hide the item from the DOM (when visible) + */ + BoxItem.prototype.hide = function() { + if (this.displayed) { + var dom = this.dom; - /** - * do a small comparision to get the direction between two touches. - * @method getDirection - * @param {Touch} touch1 - * @param {Touch} touch2 - * @return {String} direction matches `DIRECTION_LEFT|RIGHT|UP|DOWN` - */ - getDirection: function getDirection(touch1, touch2) { - var x = Math.abs(touch1.clientX - touch2.clientX), - y = Math.abs(touch1.clientY - touch2.clientY); + if (dom.box.parentNode) dom.box.parentNode.removeChild(dom.box); + if (dom.line.parentNode) dom.line.parentNode.removeChild(dom.line); + if (dom.dot.parentNode) dom.dot.parentNode.removeChild(dom.dot); - if(x >= y) { - return touch1.clientX - touch2.clientX > 0 ? DIRECTION_LEFT : DIRECTION_RIGHT; - } - return touch1.clientY - touch2.clientY > 0 ? DIRECTION_UP : DIRECTION_DOWN; - }, + this.top = null; + this.left = null; - /** - * calculate the distance between two touches - * @method getDistance - * @param {Touch}touch1 - * @param {Touch} touch2 - * @return {Number} distance - */ - getDistance: function getDistance(touch1, touch2) { - var x = touch2.clientX - touch1.clientX, - y = touch2.clientY - touch1.clientY; + this.displayed = false; + } + }; - return Math.sqrt((x * x) + (y * y)); - }, + /** + * Reposition the item horizontally + * @Override + */ + BoxItem.prototype.repositionX = function() { + var start = this.conversion.toScreen(this.data.start); + var align = this.options.align; + var left; + var box = this.dom.box; + var line = this.dom.line; + var dot = this.dom.dot; - /** - * calculate the scale factor between two touchLists - * no scale is 1, and goes down to 0 when pinched together, and bigger when pinched out - * @method getScale - * @param {Array} start array of touches - * @param {Array} end array of touches - * @return {Number} scale - */ - getScale: function getScale(start, end) { - // need two fingers... - if(start.length >= 2 && end.length >= 2) { - return this.getDistance(end[0], end[1]) / this.getDistance(start[0], start[1]); - } - return 1; - }, + // calculate left position of the box + if (align == 'right') { + this.left = start - this.width; + } + else if (align == 'left') { + this.left = start; + } + else { + // default or 'center' + this.left = start - this.width / 2; + } - /** - * calculate the rotation degrees between two touchLists - * @method getRotation - * @param {Array} start array of touches - * @param {Array} end array of touches - * @return {Number} rotation - */ - getRotation: function getRotation(start, end) { - // need two fingers - if(start.length >= 2 && end.length >= 2) { - return this.getAngle(end[1], end[0]) - this.getAngle(start[1], start[0]); - } - return 0; - }, + // reposition box + box.style.left = this.left + 'px'; - /** - * find out if the direction is vertical * - * @method isVertical - * @param {String} direction matches `DIRECTION_UP|DOWN` - * @return {Boolean} is_vertical - */ - isVertical: function isVertical(direction) { - return direction == DIRECTION_UP || direction == DIRECTION_DOWN; - }, + // reposition line + line.style.left = (start - this.props.line.width / 2) + 'px'; - /** - * set css properties with their prefixes - * @param {HTMLElement} element - * @param {String} prop - * @param {String} value - * @param {Boolean} [toggle=true] - * @return {Boolean} - */ - setPrefixedCss: function setPrefixedCss(element, prop, value, toggle) { - var prefixes = ['', 'Webkit', 'Moz', 'O', 'ms']; - prop = Utils.toCamelCase(prop); + // reposition dot + dot.style.left = (start - this.props.dot.width / 2) + 'px'; + }; - for(var i = 0; i < prefixes.length; i++) { - var p = prop; - // prefixes - if(prefixes[i]) { - p = prefixes[i] + p.slice(0, 1).toUpperCase() + p.slice(1); - } + /** + * Reposition the item vertically + * @Override + */ + BoxItem.prototype.repositionY = function() { + var orientation = this.options.orientation; + var box = this.dom.box; + var line = this.dom.line; + var dot = this.dom.dot; - // test the style - if(p in element.style) { - element.style[p] = (toggle == null || toggle) && value || ''; - break; - } - } - }, + if (orientation == 'top') { + box.style.top = (this.top || 0) + 'px'; - /** - * toggle browser default behavior by setting css properties. - * `userSelect='none'` also sets `element.onselectstart` to false - * `userDrag='none'` also sets `element.ondragstart` to false - * - * @method toggleBehavior - * @param {HtmlElement} element - * @param {Object} props - * @param {Boolean} [toggle=true] - */ - toggleBehavior: function toggleBehavior(element, props, toggle) { - if(!props || !element || !element.style) { - return; - } + line.style.top = '0'; + line.style.height = (this.parent.top + this.top + 1) + 'px'; + line.style.bottom = ''; + } + else { // orientation 'bottom' + var itemSetHeight = this.parent.itemSet.props.height; // TODO: this is nasty + var lineHeight = itemSetHeight - this.parent.top - this.parent.height + this.top; - // set the css properties - Utils.each(props, function(value, prop) { - Utils.setPrefixedCss(element, prop, value, toggle); - }); + box.style.top = (this.parent.height - this.top - this.height || 0) + 'px'; + line.style.top = (itemSetHeight - lineHeight) + 'px'; + line.style.bottom = '0'; + } - var falseFn = toggle && function() { - return false; - }; + dot.style.top = (-this.props.dot.height / 2) + 'px'; + }; - // also the disable onselectstart - if(props.userSelect == 'none') { - element.onselectstart = falseFn; - } - // and disable ondragstart - if(props.userDrag == 'none') { - element.ondragstart = falseFn; - } - }, + module.exports = BoxItem; - /** - * convert a string with underscores to camelCase - * so prevent_default becomes preventDefault - * @param {String} str - * @return {String} camelCaseStr - */ - toCamelCase: function toCamelCase(str) { - return str.replace(/[_-]([a-z])/g, function(s) { - return s[1].toUpperCase(); - }); - } - }; +/***/ }, +/* 23 */ +/***/ function(module, exports, __webpack_require__) { + + var Item = __webpack_require__(20); /** - * @module hammer - */ - /** - * @class Event - * @static + * @constructor PointItem + * @extends Item + * @param {Object} data Object containing parameters start + * content, className. + * @param {{toScreen: function, toTime: function}} conversion + * Conversion functions from time to screen and vice versa + * @param {Object} [options] Configuration options + * // TODO: describe available options */ - var Event = Hammer.event = { - /** - * when touch events have been fired, this is true - * this is used to stop mouse events - * @property prevent_mouseevents - * @private - * @type {Boolean} - */ - preventMouseEvents: false, - - /** - * if EVENT_START has been fired - * @property started - * @private - * @type {Boolean} - */ - started: false, - - /** - * when the mouse is hold down, this is true - * @property should_detect - * @private - * @type {Boolean} - */ - shouldDetect: false, - - /** - * simple event binder with a hook and support for multiple types - * @method on - * @param {HTMLElement} element - * @param {String} type - * @param {Function} handler - * @param {Function} [hook] - * @param {Object} hook.type - */ - on: function on(element, type, handler, hook) { - var types = type.split(' '); - Utils.each(types, function(type) { - Utils.on(element, type, handler); - hook && hook(type); - }); - }, - - /** - * simple event unbinder with a hook and support for multiple types - * @method off - * @param {HTMLElement} element - * @param {String} type - * @param {Function} handler - * @param {Function} [hook] - * @param {Object} hook.type - */ - off: function off(element, type, handler, hook) { - var types = type.split(' '); - Utils.each(types, function(type) { - Utils.off(element, type, handler); - hook && hook(type); - }); + function PointItem (data, conversion, options) { + this.props = { + dot: { + top: 0, + width: 0, + height: 0 }, + content: { + height: 0, + marginLeft: 0 + } + }; - /** - * the core touch event handler. - * this finds out if we should to detect gestures - * @method onTouch - * @param {HTMLElement} element - * @param {String} eventType matches `EVENT_START|MOVE|END` - * @param {Function} handler - * @return onTouchHandler {Function} the core event handler - */ - onTouch: function onTouch(element, eventType, handler) { - var self = this; + // validate data + if (data) { + if (data.start == undefined) { + throw new Error('Property "start" missing in item ' + data); + } + } - var onTouchHandler = function onTouchHandler(ev) { - var srcType = ev.type.toLowerCase(), - isPointer = Hammer.HAS_POINTEREVENTS, - isMouse = Utils.inStr(srcType, 'mouse'), - triggerType; + Item.call(this, data, conversion, options); + } - // if we are in a mouseevent, but there has been a touchevent triggered in this session - // we want to do nothing. simply break out of the event. - if(isMouse && self.preventMouseEvents) { - return; + PointItem.prototype = new Item (null, null, null); - // mousebutton must be down - } else if(isMouse && eventType == EVENT_START && ev.button === 0) { - self.preventMouseEvents = false; - self.shouldDetect = true; - } else if(isPointer && eventType == EVENT_START) { - self.shouldDetect = (ev.buttons === 1 || PointerEvent.matchType(POINTER_TOUCH, ev)); - // just a valid start event, but no mouse - } else if(!isMouse && eventType == EVENT_START) { - self.preventMouseEvents = true; - self.shouldDetect = true; - } + /** + * Check whether this item is visible inside given range + * @returns {{start: Number, end: Number}} range with a timestamp for start and end + * @returns {boolean} True if visible + */ + PointItem.prototype.isVisible = function(range) { + // determine visibility + // TODO: account for the real width of the item. Right now we just add 1/4 to the window + var interval = (range.end - range.start) / 4; + return (this.data.start > range.start - interval) && (this.data.start < range.end + interval); + }; - // update the pointer event before entering the detection - if(isPointer && eventType != EVENT_END) { - PointerEvent.updatePointer(eventType, ev); - } + /** + * Repaint the item + */ + PointItem.prototype.redraw = function() { + var dom = this.dom; + if (!dom) { + // create DOM + this.dom = {}; + dom = this.dom; - // we are in a touch/down state, so allowed detection of gestures - if(self.shouldDetect) { - triggerType = self.doDetect.call(self, ev, eventType, element, handler); - } + // background box + dom.point = document.createElement('div'); + // className is updated in redraw() - // ...and we are done with the detection - // so reset everything to start each detection totally fresh - if(triggerType == EVENT_END) { - self.preventMouseEvents = false; - self.shouldDetect = false; - PointerEvent.reset(); - // update the pointerevent object after the detection - } + // contents box, right from the dot + dom.content = document.createElement('div'); + dom.content.className = 'content'; + dom.point.appendChild(dom.content); - if(isPointer && eventType == EVENT_END) { - PointerEvent.updatePointer(eventType, ev); - } - }; + // dot at start + dom.dot = document.createElement('div'); + dom.point.appendChild(dom.dot); - this.on(element, EVENT_TYPES[eventType], onTouchHandler); - return onTouchHandler; - }, + // attach this item as attribute + dom.point['timeline-item'] = this; - /** - * the core detection method - * this finds out what hammer-touch-events to trigger - * @method doDetect - * @param {Object} ev - * @param {String} eventType matches `EVENT_START|MOVE|END` - * @param {HTMLElement} element - * @param {Function} handler - * @return {String} triggerType matches `EVENT_START|MOVE|END` - */ - doDetect: function doDetect(ev, eventType, element, handler) { - var touchList = this.getTouchList(ev, eventType); - var touchListLength = touchList.length; - var triggerType = eventType; - var triggerChange = touchList.trigger; // used by fakeMultitouch plugin - var changedLength = touchListLength; + this.dirty = true; + } - // at each touchstart-like event we want also want to trigger a TOUCH event... - if(eventType == EVENT_START) { - triggerChange = EVENT_TOUCH; - // ...the same for a touchend-like event - } else if(eventType == EVENT_END) { - triggerChange = EVENT_RELEASE; + // append DOM to parent DOM + if (!this.parent) { + throw new Error('Cannot redraw item: no parent attached'); + } + if (!dom.point.parentNode) { + var foreground = this.parent.dom.foreground; + if (!foreground) { + throw new Error('Cannot redraw item: parent has no foreground container element'); + } + foreground.appendChild(dom.point); + } + this.displayed = true; - // keep track of how many touches have been removed - changedLength = touchList.length - ((ev.changedTouches) ? ev.changedTouches.length : 1); - } + // Update DOM when item is marked dirty. An item is marked dirty when: + // - the item is not yet rendered + // - the item's data is changed + // - the item is selected/deselected + if (this.dirty) { + this._updateContents(this.dom.content); + this._updateTitle(this.dom.point); + this._updateDataAttributes(this.dom.point); + this._updateStyle(this.dom.point); - // after there are still touches on the screen, - // we just want to trigger a MOVE event. so change the START or END to a MOVE - // but only after detection has been started, the first time we actualy want a START - if(changedLength > 0 && this.started) { - triggerType = EVENT_MOVE; - } + // update class + var className = (this.data.className? ' ' + this.data.className : '') + + (this.selected ? ' selected' : ''); + dom.point.className = 'item point' + className; + dom.dot.className = 'item dot' + className; - // detection has been started, we keep track of this, see above - this.started = true; + // recalculate size + this.width = dom.point.offsetWidth; + this.height = dom.point.offsetHeight; + this.props.dot.width = dom.dot.offsetWidth; + this.props.dot.height = dom.dot.offsetHeight; + this.props.content.height = dom.content.offsetHeight; - // generate some event data, some basic information - var evData = this.collectEventData(element, triggerType, touchList, ev); + // resize contents + dom.content.style.marginLeft = 2 * this.props.dot.width + 'px'; + //dom.content.style.marginRight = ... + 'px'; // TODO: margin right - // trigger the triggerType event before the change (TOUCH, RELEASE) events - // but the END event should be at last - if(eventType != EVENT_END) { - handler.call(Detection, evData); - } + dom.dot.style.top = ((this.height - this.props.dot.height) / 2) + 'px'; + dom.dot.style.left = (this.props.dot.width / 2) + 'px'; - // trigger a change (TOUCH, RELEASE) event, this means the length of the touches changed - if(triggerChange) { - evData.changedLength = changedLength; - evData.eventType = triggerChange; + this.dirty = false; + } - handler.call(Detection, evData); + this._repaintDeleteButton(dom.point); + }; - evData.eventType = triggerType; - delete evData.changedLength; - } + /** + * Show the item in the DOM (when not already visible). The items DOM will + * be created when needed. + */ + PointItem.prototype.show = function() { + if (!this.displayed) { + this.redraw(); + } + }; - // trigger the END event - if(triggerType == EVENT_END) { - handler.call(Detection, evData); + /** + * Hide the item from the DOM (when visible) + */ + PointItem.prototype.hide = function() { + if (this.displayed) { + if (this.dom.point.parentNode) { + this.dom.point.parentNode.removeChild(this.dom.point); + } - // ...and we are done with the detection - // so reset everything to start each detection totally fresh - this.started = false; - } + this.top = null; + this.left = null; - return triggerType; - }, + this.displayed = false; + } + }; - /** - * we have different events for each device/browser - * determine what we need and set them in the EVENT_TYPES constant - * the `onTouch` method is bind to these properties. - * @method determineEventTypes - * @return {Object} events - */ - determineEventTypes: function determineEventTypes() { - var types; - if(Hammer.HAS_POINTEREVENTS) { - if(window.PointerEvent) { - types = [ - 'pointerdown', - 'pointermove', - 'pointerup pointercancel lostpointercapture' - ]; - } else { - types = [ - 'MSPointerDown', - 'MSPointerMove', - 'MSPointerUp MSPointerCancel MSLostPointerCapture' - ]; - } - } else if(Hammer.NO_MOUSEEVENTS) { - types = [ - 'touchstart', - 'touchmove', - 'touchend touchcancel' - ]; - } else { - types = [ - 'touchstart mousedown', - 'touchmove mousemove', - 'touchend touchcancel mouseup' - ]; - } + /** + * Reposition the item horizontally + * @Override + */ + PointItem.prototype.repositionX = function() { + var start = this.conversion.toScreen(this.data.start); - EVENT_TYPES[EVENT_START] = types[0]; - EVENT_TYPES[EVENT_MOVE] = types[1]; - EVENT_TYPES[EVENT_END] = types[2]; - return EVENT_TYPES; - }, + this.left = start - this.props.dot.width; - /** - * create touchList depending on the event - * @method getTouchList - * @param {Object} ev - * @param {String} eventType - * @return {Array} touches - */ - getTouchList: function getTouchList(ev, eventType) { - // get the fake pointerEvent touchlist - if(Hammer.HAS_POINTEREVENTS) { - return PointerEvent.getTouchList(); - } + // reposition point + this.dom.point.style.left = this.left + 'px'; + }; - // get the touchlist - if(ev.touches) { - if(eventType == EVENT_MOVE) { - return ev.touches; - } + /** + * Reposition the item vertically + * @Override + */ + PointItem.prototype.repositionY = function() { + var orientation = this.options.orientation, + point = this.dom.point; - var identifiers = []; - var concat = [].concat(Utils.toArray(ev.touches), Utils.toArray(ev.changedTouches)); - var touchList = []; + if (orientation == 'top') { + point.style.top = this.top + 'px'; + } + else { + point.style.top = (this.parent.height - this.top - this.height) + 'px'; + } + }; - Utils.each(concat, function(touch) { - if(Utils.inArray(identifiers, touch.identifier) === false) { - touchList.push(touch); - } - identifiers.push(touch.identifier); - }); + module.exports = PointItem; - return touchList; - } - // make fake touchList from mouse position - ev.identifier = 1; - return [ev]; - }, +/***/ }, +/* 24 */ +/***/ function(module, exports, __webpack_require__) { - /** - * collect basic event data - * @method collectEventData - * @param {HTMLElement} element - * @param {String} eventType matches `EVENT_START|MOVE|END` - * @param {Array} touches - * @param {Object} ev - * @return {Object} ev - */ - collectEventData: function collectEventData(element, eventType, touches, ev) { - // find out pointerType - var pointerType = POINTER_TOUCH; - if(Utils.inStr(ev.type, 'mouse') || PointerEvent.matchType(POINTER_MOUSE, ev)) { - pointerType = POINTER_MOUSE; - } else if(PointerEvent.matchType(POINTER_PEN, ev)) { - pointerType = POINTER_PEN; - } + var Hammer = __webpack_require__(45); + var Item = __webpack_require__(20); - return { - center: Utils.getCenter(touches), - timeStamp: Date.now(), - target: ev.target, - touches: touches, - eventType: eventType, - pointerType: pointerType, - srcEvent: ev, + /** + * @constructor RangeItem + * @extends Item + * @param {Object} data Object containing parameters start, end + * content, className. + * @param {{toScreen: function, toTime: function}} conversion + * Conversion functions from time to screen and vice versa + * @param {Object} [options] Configuration options + * // TODO: describe options + */ + function RangeItem (data, conversion, options) { + this.props = { + content: { + width: 0 + } + }; + this.overflow = false; // if contents can overflow (css styling), this flag is set to true - /** - * prevent the browser default actions - * mostly used to disable scrolling of the browser - */ - preventDefault: function() { - var srcEvent = this.srcEvent; - srcEvent.preventManipulation && srcEvent.preventManipulation(); - srcEvent.preventDefault && srcEvent.preventDefault(); - }, + // validate data + if (data) { + if (data.start == undefined) { + throw new Error('Property "start" missing in item ' + data.id); + } + if (data.end == undefined) { + throw new Error('Property "end" missing in item ' + data.id); + } + } - /** - * stop bubbling the event up to its parents - */ - stopPropagation: function() { - this.srcEvent.stopPropagation(); - }, + Item.call(this, data, conversion, options); + } - /** - * immediately stop gesture detection - * might be useful after a swipe was detected - * @return {*} - */ - stopDetect: function() { - return Detection.stopDetect(); - } - }; - } - }; + RangeItem.prototype = new Item (null, null, null); + RangeItem.prototype.baseClassName = 'item range'; /** - * @module hammer - * - * @class PointerEvent - * @static + * Check whether this item is visible inside given range + * @returns {{start: Number, end: Number}} range with a timestamp for start and end + * @returns {boolean} True if visible */ - var PointerEvent = Hammer.PointerEvent = { - /** - * holds all pointers, by `identifier` - * @property pointers - * @type {Object} - */ - pointers: {}, + RangeItem.prototype.isVisible = function(range) { + // determine visibility + return (this.data.start < range.end) && (this.data.end > range.start); + }; - /** - * get the pointers as an array - * @method getTouchList - * @return {Array} touchlist - */ - getTouchList: function getTouchList() { - var touchlist = []; - // we can use forEach since pointerEvents only is in IE10 - Utils.each(this.pointers, function(pointer) { - touchlist.push(pointer); - }); - return touchlist; - }, + /** + * Repaint the item + */ + RangeItem.prototype.redraw = function() { + var dom = this.dom; + if (!dom) { + // create DOM + this.dom = {}; + dom = this.dom; - /** - * update the position of a pointer - * @method updatePointer - * @param {String} eventType matches `EVENT_START|MOVE|END` - * @param {Object} pointerEvent - */ - updatePointer: function updatePointer(eventType, pointerEvent) { - if(eventType == EVENT_END || (eventType != EVENT_END && pointerEvent.buttons !== 1)) { - delete this.pointers[pointerEvent.pointerId]; - } else { - pointerEvent.identifier = pointerEvent.pointerId; - this.pointers[pointerEvent.pointerId] = pointerEvent; - } - }, + // background box + dom.box = document.createElement('div'); + // className is updated in redraw() - /** - * check if ev matches pointertype - * @method matchType - * @param {String} pointerType matches `POINTER_MOUSE|TOUCH|PEN` - * @param {PointerEvent} ev - */ - matchType: function matchType(pointerType, ev) { - if(!ev.pointerType) { - return false; - } + // contents box + dom.content = document.createElement('div'); + dom.content.className = 'content'; + dom.box.appendChild(dom.content); - var pt = ev.pointerType, - types = {}; + // attach this item as attribute + dom.box['timeline-item'] = this; - types[POINTER_MOUSE] = (pt === (ev.MSPOINTER_TYPE_MOUSE || POINTER_MOUSE)); - types[POINTER_TOUCH] = (pt === (ev.MSPOINTER_TYPE_TOUCH || POINTER_TOUCH)); - types[POINTER_PEN] = (pt === (ev.MSPOINTER_TYPE_PEN || POINTER_PEN)); - return types[pointerType]; - }, + this.dirty = true; + } - /** - * reset the stored pointers - * @method reset - */ - reset: function resetList() { - this.pointers = {}; + // append DOM to parent DOM + if (!this.parent) { + throw new Error('Cannot redraw item: no parent attached'); + } + if (!dom.box.parentNode) { + var foreground = this.parent.dom.foreground; + if (!foreground) { + throw new Error('Cannot redraw item: parent has no foreground container element'); } - }; + foreground.appendChild(dom.box); + } + this.displayed = true; + // Update DOM when item is marked dirty. An item is marked dirty when: + // - the item is not yet rendered + // - the item's data is changed + // - the item is selected/deselected + if (this.dirty) { + this._updateContents(this.dom.content); + this._updateTitle(this.dom.box); + this._updateDataAttributes(this.dom.box); + this._updateStyle(this.dom.box); - /** - * @module hammer - * - * @class Detection - * @static - */ - var Detection = Hammer.detection = { - // contains all registred Hammer.gestures in the correct order - gestures: [], + // update class + var className = (this.data.className ? (' ' + this.data.className) : '') + + (this.selected ? ' selected' : ''); + dom.box.className = this.baseClassName + className; - // data of the current Hammer.gesture detection session - current: null, + // determine from css whether this box has overflow + this.overflow = window.getComputedStyle(dom.content).overflow !== 'hidden'; - // the previous Hammer.gesture session data - // is a full clone of the previous gesture.current object - previous: null, + // recalculate size + // turn off max-width to be able to calculate the real width + // this causes an extra browser repaint/reflow, but so be it + this.dom.content.style.maxWidth = 'none'; + this.props.content.width = this.dom.content.offsetWidth; + this.height = this.dom.box.offsetHeight; + this.dom.content.style.maxWidth = ''; - // when this becomes true, no gestures are fired - stopped: false, + this.dirty = false; + } - /** - * start Hammer.gesture detection - * @method startDetect - * @param {Hammer.Instance} inst - * @param {Object} eventData - */ - startDetect: function startDetect(inst, eventData) { - // already busy with a Hammer.gesture detection on an element - if(this.current) { - return; - } + this._repaintDeleteButton(dom.box); + this._repaintDragLeft(); + this._repaintDragRight(); + }; - this.stopped = false; + /** + * Show the item in the DOM (when not already visible). The items DOM will + * be created when needed. + */ + RangeItem.prototype.show = function() { + if (!this.displayed) { + this.redraw(); + } + }; - // holds current session - this.current = { - inst: inst, // reference to HammerInstance we're working for - startEvent: Utils.extend({}, eventData), // start eventData for distances, timing etc - lastEvent: false, // last eventData - lastCalcEvent: false, // last eventData for calculations. - futureCalcEvent: false, // last eventData for calculations. - lastCalcData: {}, // last lastCalcData - name: '' // current gesture we're in/detected, can be 'tap', 'hold' etc - }; + /** + * Hide the item from the DOM (when visible) + * @return {Boolean} changed + */ + RangeItem.prototype.hide = function() { + if (this.displayed) { + var box = this.dom.box; - this.detect(eventData); - }, + if (box.parentNode) { + box.parentNode.removeChild(box); + } - /** - * Hammer.gesture detection - * @method detect - * @param {Object} eventData - * @return {any} - */ - detect: function detect(eventData) { - if(!this.current || this.stopped) { - return; - } + this.top = null; + this.left = null; - // extend event data with calculations about scale, distance etc - eventData = this.extendEventData(eventData); + this.displayed = false; + } + }; - // hammer instance and instance options - var inst = this.current.inst, - instOptions = inst.options; + /** + * Reposition the item horizontally + * @Override + */ + RangeItem.prototype.repositionX = function() { + var parentWidth = this.parent.width; + var start = this.conversion.toScreen(this.data.start); + var end = this.conversion.toScreen(this.data.end); + var contentLeft; + var contentWidth; - // call Hammer.gesture handlers - Utils.each(this.gestures, function triggerGesture(gesture) { - // only when the instance options have enabled this gesture - if(!this.stopped && inst.enabled && instOptions[gesture.name]) { - gesture.handler.call(gesture, eventData, inst); - } - }, this); + // limit the width of the this, as browsers cannot draw very wide divs + if (start < -parentWidth) { + start = -parentWidth; + } + if (end > 2 * parentWidth) { + end = 2 * parentWidth; + } + var boxWidth = Math.max(end - start, 1); - // store as previous event event - if(this.current) { - this.current.lastEvent = eventData; - } + if (this.overflow) { + this.left = start; + this.width = boxWidth + this.props.content.width; + contentWidth = this.props.content.width; - if(eventData.eventType == EVENT_END) { - this.stopDetect(); - } + // Note: The calculation of width is an optimistic calculation, giving + // a width which will not change when moving the Timeline + // So no re-stacking needed, which is nicer for the eye; + } + else { + this.left = start; + this.width = boxWidth; + contentWidth = Math.min(end - start - 2 * this.options.padding, this.props.content.width); + } - return eventData; - }, + this.dom.box.style.left = this.left + 'px'; + this.dom.box.style.width = boxWidth + 'px'; - /** - * clear the Hammer.gesture vars - * this is called on endDetect, but can also be used when a final Hammer.gesture has been detected - * to stop other Hammer.gestures from being fired - * @method stopDetect - */ - stopDetect: function stopDetect() { - // clone current data to the store as the previous gesture - // used for the double tap gesture, since this is an other gesture detect session - this.previous = Utils.extend({}, this.current); + switch (this.options.align) { + case 'left': + this.dom.content.style.left = '0'; + break; - // reset the current - this.current = null; - this.stopped = true; - }, + case 'right': + this.dom.content.style.left = Math.max((boxWidth - contentWidth - 2 * this.options.padding), 0) + 'px'; + break; - /** - * calculate velocity, angle and direction - * @method getVelocityData - * @param {Object} ev - * @param {Object} center - * @param {Number} deltaTime - * @param {Number} deltaX - * @param {Number} deltaY - */ - getCalculatedData: function getCalculatedData(ev, center, deltaTime, deltaX, deltaY) { - var cur = this.current, - recalc = false, - calcEv = cur.lastCalcEvent, - calcData = cur.lastCalcData; + case 'center': + this.dom.content.style.left = Math.max((boxWidth - contentWidth - 2 * this.options.padding) / 2, 0) + 'px'; + break; - if(calcEv && ev.timeStamp - calcEv.timeStamp > Hammer.CALCULATE_INTERVAL) { - center = calcEv.center; - deltaTime = ev.timeStamp - calcEv.timeStamp; - deltaX = ev.center.clientX - calcEv.center.clientX; - deltaY = ev.center.clientY - calcEv.center.clientY; - recalc = true; + default: // 'auto' + // when range exceeds left of the window, position the contents at the left of the visible area + if (this.overflow) { + if (end > 0) { + contentLeft = Math.max(-start, 0); } - - if(ev.eventType == EVENT_TOUCH || ev.eventType == EVENT_RELEASE) { - cur.futureCalcEvent = ev; + else { + contentLeft = -contentWidth; // ensure it's not visible anymore } - - if(!cur.lastCalcEvent || recalc) { - calcData.velocity = Utils.getVelocity(deltaTime, deltaX, deltaY); - calcData.angle = Utils.getAngle(center, ev.center); - calcData.direction = Utils.getDirection(center, ev.center); - - cur.lastCalcEvent = cur.futureCalcEvent || ev; - cur.futureCalcEvent = ev; + } + else { + if (start < 0) { + contentLeft = Math.min(-start, + (end - start - contentWidth - 2 * this.options.padding)); + // TODO: remove the need for options.padding. it's terrible. } - - ev.velocityX = calcData.velocity.x; - ev.velocityY = calcData.velocity.y; - ev.interimAngle = calcData.angle; - ev.interimDirection = calcData.direction; - }, - - /** - * extend eventData for Hammer.gestures - * @method extendEventData - * @param {Object} ev - * @return {Object} ev - */ - extendEventData: function extendEventData(ev) { - var cur = this.current, - startEv = cur.startEvent, - lastEv = cur.lastEvent || startEv; - - // update the start touchlist to calculate the scale/rotation - if(ev.eventType == EVENT_TOUCH || ev.eventType == EVENT_RELEASE) { - startEv.touches = []; - Utils.each(ev.touches, function(touch) { - startEv.touches.push({ - clientX: touch.clientX, - clientY: touch.clientY - }); - }); + else { + contentLeft = 0; } + } + this.dom.content.style.left = contentLeft + 'px'; + } + }; - var deltaTime = ev.timeStamp - startEv.timeStamp, - deltaX = ev.center.clientX - startEv.center.clientX, - deltaY = ev.center.clientY - startEv.center.clientY; - - this.getCalculatedData(ev, lastEv.center, deltaTime, deltaX, deltaY); + /** + * Reposition the item vertically + * @Override + */ + RangeItem.prototype.repositionY = function() { + var orientation = this.options.orientation, + box = this.dom.box; - Utils.extend(ev, { - startEvent: startEv, + if (orientation == 'top') { + box.style.top = this.top + 'px'; + } + else { + box.style.top = (this.parent.height - this.top - this.height) + 'px'; + } + }; - deltaTime: deltaTime, - deltaX: deltaX, - deltaY: deltaY, + /** + * Repaint a drag area on the left side of the range when the range is selected + * @protected + */ + RangeItem.prototype._repaintDragLeft = function () { + if (this.selected && this.options.editable.updateTime && !this.dom.dragLeft) { + // create and show drag area + var dragLeft = document.createElement('div'); + dragLeft.className = 'drag-left'; + dragLeft.dragLeftItem = this; - distance: Utils.getDistance(startEv.center, ev.center), - angle: Utils.getAngle(startEv.center, ev.center), - direction: Utils.getDirection(startEv.center, ev.center), - scale: Utils.getScale(startEv.touches, ev.touches), - rotation: Utils.getRotation(startEv.touches, ev.touches) + // TODO: this should be redundant? + Hammer(dragLeft, { + preventDefault: true + }).on('drag', function () { + //console.log('drag left') }); - return ev; - }, + this.dom.box.appendChild(dragLeft); + this.dom.dragLeft = dragLeft; + } + else if (!this.selected && this.dom.dragLeft) { + // delete drag area + if (this.dom.dragLeft.parentNode) { + this.dom.dragLeft.parentNode.removeChild(this.dom.dragLeft); + } + this.dom.dragLeft = null; + } + }; - /** - * register new gesture - * @method register - * @param {Object} gesture object, see `gestures/` for documentation - * @return {Array} gestures - */ - register: function register(gesture) { - // add an enable gesture options if there is no given - var options = gesture.defaults || {}; - if(options[gesture.name] === undefined) { - options[gesture.name] = true; - } + /** + * Repaint a drag area on the right side of the range when the range is selected + * @protected + */ + RangeItem.prototype._repaintDragRight = function () { + if (this.selected && this.options.editable.updateTime && !this.dom.dragRight) { + // create and show drag area + var dragRight = document.createElement('div'); + dragRight.className = 'drag-right'; + dragRight.dragRightItem = this; - // extend Hammer default options with the Hammer.gesture options - Utils.extend(Hammer.defaults, options, true); + // TODO: this should be redundant? + Hammer(dragRight, { + preventDefault: true + }).on('drag', function () { + //console.log('drag right') + }); - // set its index - gesture.index = gesture.index || 1000; + this.dom.box.appendChild(dragRight); + this.dom.dragRight = dragRight; + } + else if (!this.selected && this.dom.dragRight) { + // delete drag area + if (this.dom.dragRight.parentNode) { + this.dom.dragRight.parentNode.removeChild(this.dom.dragRight); + } + this.dom.dragRight = null; + } + }; - // add Hammer.gesture to the list - this.gestures.push(gesture); + module.exports = RangeItem; - // sort the list by index - this.gestures.sort(function(a, b) { - if(a.index < b.index) { - return -1; - } - if(a.index > b.index) { - return 1; - } - return 0; - }); - return this.gestures; - } - }; +/***/ }, +/* 25 */ +/***/ function(module, exports, __webpack_require__) { + /** + * Prototype for visual components + * @param {{dom: Object, domProps: Object, emitter: Emitter, range: Range}} [body] + * @param {Object} [options] + */ + function Component (body, options) { + this.options = null; + this.props = null; + } /** - * @module hammer + * Set options for the component. The new options will be merged into the + * current options. + * @param {Object} options */ + Component.prototype.setOptions = function(options) { + if (options) { + util.extend(this.options, options); + } + }; /** - * create new hammer instance - * all methods should return the instance itself, so it is chainable. - * - * @class Instance - * @constructor - * @param {HTMLElement} element - * @param {Object} [options={}] options are merged with `Hammer.defaults` - * @return {Hammer.Instance} + * Repaint the component + * @return {boolean} Returns true if the component is resized */ - Hammer.Instance = function(element, options) { - var self = this; + Component.prototype.redraw = function() { + // should be implemented by the component + return false; + }; - // setup HammerJS window events and register all gestures - // this also sets up the default options - setup(); + /** + * Destroy the component. Cleanup DOM and event listeners + */ + Component.prototype.destroy = function() { + // should be implemented by the component + }; - /** - * @property element - * @type {HTMLElement} - */ - this.element = element; + /** + * Test whether the component is resized since the last time _isResized() was + * called. + * @return {Boolean} Returns true if the component is resized + * @protected + */ + Component.prototype._isResized = function() { + var resized = (this.props._previousWidth !== this.props.width || + this.props._previousHeight !== this.props.height); - /** - * @property enabled - * @type {Boolean} - * @protected - */ - this.enabled = true; + this.props._previousWidth = this.props.width; + this.props._previousHeight = this.props.height; - /** - * options, merged with the defaults - * options with an _ are converted to camelCase - * @property options - * @type {Object} - */ - Utils.each(options, function(value, name) { - delete options[name]; - options[Utils.toCamelCase(name)] = value; - }); + return resized; + }; - this.options = Utils.extend(Utils.extend({}, Hammer.defaults), options || {}); + module.exports = Component; - // add some css to the element to prevent the browser from doing its native behavoir - if(this.options.behavior) { - Utils.toggleBehavior(this.element, this.options.behavior, true); - } - /** - * event start handler on the element to start the detection - * @property eventStartHandler - * @type {Object} - */ - this.eventStartHandler = Event.onTouch(element, EVENT_START, function(ev) { - if(self.enabled && ev.eventType == EVENT_START) { - Detection.startDetect(self, ev); - } else if(ev.eventType == EVENT_TOUCH) { - Detection.detect(ev); - } - }); +/***/ }, +/* 26 */ +/***/ function(module, exports, __webpack_require__) { - /** - * keep a list of user event handlers which needs to be removed when calling 'dispose' - * @property eventHandlers - * @type {Array} - */ - this.eventHandlers = []; - }; + var util = __webpack_require__(1); + var Component = __webpack_require__(25); + var moment = __webpack_require__(44); + var locales = __webpack_require__(48); - Hammer.Instance.prototype = { - /** - * bind events to the instance - * @method on - * @chainable - * @param {String} gestures multiple gestures by splitting with a space - * @param {Function} handler - * @param {Object} handler.ev event object - */ - on: function onEvent(gestures, handler) { - var self = this; - Event.on(self.element, gestures, handler, function(type) { - self.eventHandlers.push({ gesture: type, handler: handler }); - }); - return self; - }, + /** + * A current time bar + * @param {{range: Range, dom: Object, domProps: Object}} body + * @param {Object} [options] Available parameters: + * {Boolean} [showCurrentTime] + * @constructor CurrentTime + * @extends Component + */ + function CurrentTime (body, options) { + this.body = body; - /** - * unbind events to the instance - * @method off - * @chainable - * @param {String} gestures - * @param {Function} handler - */ - off: function offEvent(gestures, handler) { - var self = this; + // default options + this.defaultOptions = { + showCurrentTime: true, - Event.off(self.element, gestures, handler, function(type) { - var index = Utils.inArray({ gesture: type, handler: handler }); - if(index !== false) { - self.eventHandlers.splice(index, 1); - } - }); - return self; - }, + locales: locales, + locale: 'en' + }; + this.options = util.extend({}, this.defaultOptions); + this.offset = 0; - /** - * trigger gesture event - * @method trigger - * @chainable - * @param {String} gesture - * @param {Object} [eventData] - */ - trigger: function triggerEvent(gesture, eventData) { - // optional - if(!eventData) { - eventData = {}; - } + this._create(); - // create DOM event - var event = Hammer.DOCUMENT.createEvent('Event'); - event.initEvent(gesture, true, true); - event.gesture = eventData; + this.setOptions(options); + } - // trigger on the target if it is in the instance element, - // this is for event delegation tricks - var element = this.element; - if(Utils.hasParent(eventData.target, element)) { - element = eventData.target; - } + CurrentTime.prototype = new Component(); - element.dispatchEvent(event); - return this; - }, + /** + * Create the HTML DOM for the current time bar + * @private + */ + CurrentTime.prototype._create = function() { + var bar = document.createElement('div'); + bar.className = 'currenttime'; + bar.style.position = 'absolute'; + bar.style.top = '0px'; + bar.style.height = '100%'; - /** - * enable of disable hammer.js detection - * @method enable - * @chainable - * @param {Boolean} state - */ - enable: function enable(state) { - this.enabled = state; - return this; - }, + this.bar = bar; + }; - /** - * dispose this hammer instance - * @method dispose - * @return {Null} - */ - dispose: function dispose() { - var i, eh; + /** + * Destroy the CurrentTime bar + */ + CurrentTime.prototype.destroy = function () { + this.options.showCurrentTime = false; + this.redraw(); // will remove the bar from the DOM and stop refreshing - // undo all changes made by stop_browser_behavior - Utils.toggleBehavior(this.element, this.options.behavior, false); + this.body = null; + }; - // unbind all custom event handlers - for(i = -1; (eh = this.eventHandlers[++i]);) { - Utils.off(this.element, eh.gesture, eh.handler); - } + /** + * Set options for the component. Options will be merged in current options. + * @param {Object} options Available parameters: + * {boolean} [showCurrentTime] + */ + CurrentTime.prototype.setOptions = function(options) { + if (options) { + // copy all options that we know + util.selectiveExtend(['showCurrentTime', 'locale', 'locales'], this.options, options); + } + }; - this.eventHandlers = []; + /** + * Repaint the component + * @return {boolean} Returns true if the component is resized + */ + CurrentTime.prototype.redraw = function() { + if (this.options.showCurrentTime) { + var parent = this.body.dom.backgroundVertical; + if (this.bar.parentNode != parent) { + // attach to the dom + if (this.bar.parentNode) { + this.bar.parentNode.removeChild(this.bar); + } + parent.appendChild(this.bar); - // unbind the start event listener - Event.off(this.element, EVENT_TYPES[EVENT_START], this.eventStartHandler); + this.start(); + } - return null; + var now = new Date(new Date().valueOf() + this.offset); + var x = this.body.util.toScreen(now); + + var locale = this.options.locales[this.options.locale]; + var title = locale.current + ' ' + locale.time + ': ' + moment(now).format('dddd, MMMM Do YYYY, H:mm:ss'); + title = title.charAt(0).toUpperCase() + title.substring(1); + + this.bar.style.left = x + 'px'; + this.bar.title = title; + } + else { + // remove the line from the DOM + if (this.bar.parentNode) { + this.bar.parentNode.removeChild(this.bar); } - }; + this.stop(); + } + return false; + }; /** - * @module gestures - */ - /** - * Move with x fingers (default 1) around on the page. - * Preventing the default browser behavior is a good way to improve feel and working. - * ```` - * hammertime.on("drag", function(ev) { - * console.log(ev); - * ev.gesture.preventDefault(); - * }); - * ```` - * - * @class Drag - * @static + * Start auto refreshing the current time bar */ + CurrentTime.prototype.start = function() { + var me = this; + + function update () { + me.stop(); + + // determine interval to refresh + var scale = me.body.range.conversion(me.body.domProps.center.width).scale; + var interval = 1 / scale / 10; + if (interval < 30) interval = 30; + if (interval > 1000) interval = 1000; + + me.redraw(); + + // start a timer to adjust for the new time + me.currentTimeTimer = setTimeout(update, interval); + } + + update(); + }; + /** - * @event drag - * @param {Object} ev + * Stop auto refreshing the current time bar */ + CurrentTime.prototype.stop = function() { + if (this.currentTimeTimer !== undefined) { + clearTimeout(this.currentTimeTimer); + delete this.currentTimeTimer; + } + }; + /** - * @event dragstart - * @param {Object} ev + * Set a current time. This can be used for example to ensure that a client's + * time is synchronized with a shared server time. + * @param {Date | String | Number} time A Date, unix timestamp, or + * ISO date string. */ + CurrentTime.prototype.setCurrentTime = function(time) { + var t = util.convert(time, 'Date').valueOf(); + var now = new Date().valueOf(); + this.offset = t - now; + this.redraw(); + }; + /** - * @event dragend - * @param {Object} ev + * Get the current time. + * @return {Date} Returns the current time. */ + CurrentTime.prototype.getCurrentTime = function() { + return new Date(new Date().valueOf() + this.offset); + }; + + module.exports = CurrentTime; + + +/***/ }, +/* 27 */ +/***/ function(module, exports, __webpack_require__) { + + var Hammer = __webpack_require__(45); + var util = __webpack_require__(1); + var Component = __webpack_require__(25); + var moment = __webpack_require__(44); + var locales = __webpack_require__(48); + /** - * @event drapleft - * @param {Object} ev + * A custom time bar + * @param {{range: Range, dom: Object}} body + * @param {Object} [options] Available parameters: + * {Boolean} [showCustomTime] + * @constructor CustomTime + * @extends Component */ + + function CustomTime (body, options) { + this.body = body; + + // default options + this.defaultOptions = { + showCustomTime: false, + locales: locales, + locale: 'en' + }; + this.options = util.extend({}, this.defaultOptions); + + this.customTime = new Date(); + this.eventParams = {}; // stores state parameters while dragging the bar + + // create the DOM + this._create(); + + this.setOptions(options); + } + + CustomTime.prototype = new Component(); + /** - * @event dragright - * @param {Object} ev + * Set options for the component. Options will be merged in current options. + * @param {Object} options Available parameters: + * {boolean} [showCustomTime] */ + CustomTime.prototype.setOptions = function(options) { + if (options) { + // copy all options that we know + util.selectiveExtend(['showCustomTime', 'locale', 'locales'], this.options, options); + } + }; + /** - * @event dragup - * @param {Object} ev + * Create the DOM for the custom time + * @private */ + CustomTime.prototype._create = function() { + var bar = document.createElement('div'); + bar.className = 'customtime'; + bar.style.position = 'absolute'; + bar.style.top = '0px'; + bar.style.height = '100%'; + this.bar = bar; + + var drag = document.createElement('div'); + drag.style.position = 'relative'; + drag.style.top = '0px'; + drag.style.left = '-10px'; + drag.style.height = '100%'; + drag.style.width = '20px'; + bar.appendChild(drag); + + // attach event listeners + this.hammer = Hammer(bar, { + prevent_default: true + }); + this.hammer.on('dragstart', this._onDragStart.bind(this)); + this.hammer.on('drag', this._onDrag.bind(this)); + this.hammer.on('dragend', this._onDragEnd.bind(this)); + }; + /** - * @event dragdown - * @param {Object} ev + * Destroy the CustomTime bar */ + CustomTime.prototype.destroy = function () { + this.options.showCustomTime = false; + this.redraw(); // will remove the bar from the DOM + + this.hammer.enable(false); + this.hammer = null; + + this.body = null; + }; /** - * @param {String} name + * Repaint the component + * @return {boolean} Returns true if the component is resized */ - (function(name) { - var triggered = false; + CustomTime.prototype.redraw = function () { + if (this.options.showCustomTime) { + var parent = this.body.dom.backgroundVertical; + if (this.bar.parentNode != parent) { + // attach to the dom + if (this.bar.parentNode) { + this.bar.parentNode.removeChild(this.bar); + } + parent.appendChild(this.bar); + } - function dragGesture(ev, inst) { - var cur = Detection.current; + var x = this.body.util.toScreen(this.customTime); - // max touches - if(inst.options.dragMaxTouches > 0 && - ev.touches.length > inst.options.dragMaxTouches) { - return; - } + var locale = this.options.locales[this.options.locale]; + var title = locale.time + ': ' + moment(this.customTime).format('dddd, MMMM Do YYYY, H:mm:ss'); + title = title.charAt(0).toUpperCase() + title.substring(1); - switch(ev.eventType) { - case EVENT_START: - triggered = false; - break; + this.bar.style.left = x + 'px'; + this.bar.title = title; + } + else { + // remove the line from the DOM + if (this.bar.parentNode) { + this.bar.parentNode.removeChild(this.bar); + } + } - case EVENT_MOVE: - // when the distance we moved is too small we skip this gesture - // or we can be already in dragging - if(ev.distance < inst.options.dragMinDistance && - cur.name != name) { - return; - } + return false; + }; - var startCenter = cur.startEvent.center; + /** + * Set custom time. + * @param {Date | number | string} time + */ + CustomTime.prototype.setCustomTime = function(time) { + this.customTime = util.convert(time, 'Date'); + this.redraw(); + }; - // we are dragging! - if(cur.name != name) { - cur.name = name; - if(inst.options.dragDistanceCorrection && ev.distance > 0) { - // When a drag is triggered, set the event center to dragMinDistance pixels from the original event center. - // Without this correction, the dragged distance would jumpstart at dragMinDistance pixels instead of at 0. - // It might be useful to save the original start point somewhere - var factor = Math.abs(inst.options.dragMinDistance / ev.distance); - startCenter.pageX += ev.deltaX * factor; - startCenter.pageY += ev.deltaY * factor; - startCenter.clientX += ev.deltaX * factor; - startCenter.clientY += ev.deltaY * factor; + /** + * Retrieve the current custom time. + * @return {Date} customTime + */ + CustomTime.prototype.getCustomTime = function() { + return new Date(this.customTime.valueOf()); + }; - // recalculate event data using new start point - ev = Detection.extendEventData(ev); - } - } + /** + * Start moving horizontally + * @param {Event} event + * @private + */ + CustomTime.prototype._onDragStart = function(event) { + this.eventParams.dragging = true; + this.eventParams.customTime = this.customTime; - // lock drag to axis? - if(cur.lastEvent.dragLockToAxis || - ( inst.options.dragLockToAxis && - inst.options.dragLockMinDistance <= ev.distance - )) { - ev.dragLockToAxis = true; - } + event.stopPropagation(); + event.preventDefault(); + }; - // keep direction on the axis that the drag gesture started on - var lastDirection = cur.lastEvent.direction; - if(ev.dragLockToAxis && lastDirection !== ev.direction) { - if(Utils.isVertical(lastDirection)) { - ev.direction = (ev.deltaY < 0) ? DIRECTION_UP : DIRECTION_DOWN; - } else { - ev.direction = (ev.deltaX < 0) ? DIRECTION_LEFT : DIRECTION_RIGHT; - } - } + /** + * Perform moving operating. + * @param {Event} event + * @private + */ + CustomTime.prototype._onDrag = function (event) { + if (!this.eventParams.dragging) return; - // first time, trigger dragstart event - if(!triggered) { - inst.trigger(name + 'start', ev); - triggered = true; - } + var deltaX = event.gesture.deltaX, + x = this.body.util.toScreen(this.eventParams.customTime) + deltaX, + time = this.body.util.toTime(x); - // trigger events - inst.trigger(name, ev); - inst.trigger(name + ev.direction, ev); + this.setCustomTime(time); - var isVertical = Utils.isVertical(ev.direction); + // fire a timechange event + this.body.emitter.emit('timechange', { + time: new Date(this.customTime.valueOf()) + }); - // block the browser events - if((inst.options.dragBlockVertical && isVertical) || - (inst.options.dragBlockHorizontal && !isVertical)) { - ev.preventDefault(); - } - break; - - case EVENT_RELEASE: - if(triggered && ev.changedLength <= inst.options.dragMaxTouches) { - inst.trigger(name + 'end', ev); - triggered = false; - } - break; - - case EVENT_END: - triggered = false; - break; - } - } + event.stopPropagation(); + event.preventDefault(); + }; - Hammer.gestures.Drag = { - name: name, - index: 50, - handler: dragGesture, - defaults: { - /** - * minimal movement that have to be made before the drag event gets triggered - * @property dragMinDistance - * @type {Number} - * @default 10 - */ - dragMinDistance: 10, + /** + * Stop moving operating. + * @param {event} event + * @private + */ + CustomTime.prototype._onDragEnd = function (event) { + if (!this.eventParams.dragging) return; - /** - * Set dragDistanceCorrection to true to make the starting point of the drag - * be calculated from where the drag was triggered, not from where the touch started. - * Useful to avoid a jerk-starting drag, which can make fine-adjustments - * through dragging difficult, and be visually unappealing. - * @property dragDistanceCorrection - * @type {Boolean} - * @default true - */ - dragDistanceCorrection: true, + // fire a timechanged event + this.body.emitter.emit('timechanged', { + time: new Date(this.customTime.valueOf()) + }); - /** - * set 0 for unlimited, but this can conflict with transform - * @property dragMaxTouches - * @type {Number} - * @default 1 - */ - dragMaxTouches: 1, + event.stopPropagation(); + event.preventDefault(); + }; - /** - * prevent default browser behavior when dragging occurs - * be careful with it, it makes the element a blocking element - * when you are using the drag gesture, it is a good practice to set this true - * @property dragBlockHorizontal - * @type {Boolean} - * @default false - */ - dragBlockHorizontal: false, + module.exports = CustomTime; - /** - * same as `dragBlockHorizontal`, but for vertical movement - * @property dragBlockVertical - * @type {Boolean} - * @default false - */ - dragBlockVertical: false, - /** - * dragLockToAxis keeps the drag gesture on the axis that it started on, - * It disallows vertical directions if the initial direction was horizontal, and vice versa. - * @property dragLockToAxis - * @type {Boolean} - * @default false - */ - dragLockToAxis: false, +/***/ }, +/* 28 */ +/***/ function(module, exports, __webpack_require__) { - /** - * drag lock only kicks in when distance > dragLockMinDistance - * This way, locking occurs only when the distance has become large enough to reliably determine the direction - * @property dragLockMinDistance - * @type {Number} - * @default 25 - */ - dragLockMinDistance: 25 - } - }; - })('drag'); + var util = __webpack_require__(1); + var DOMutil = __webpack_require__(2); + var Component = __webpack_require__(25); + var DataStep = __webpack_require__(16); /** - * @module gestures - */ - /** - * trigger a simple gesture event, so you can do anything in your handler. - * only usable if you know what your doing... - * - * @class Gesture - * @static - */ - /** - * @event gesture - * @param {Object} ev + * A horizontal time axis + * @param {Object} [options] See DataAxis.setOptions for the available + * options. + * @constructor DataAxis + * @extends Component + * @param body */ - Hammer.gestures.Gesture = { - name: 'gesture', - index: 1337, - handler: function releaseGesture(ev, inst) { - inst.trigger(this.name, ev); + function DataAxis (body, options, svg, linegraphOptions) { + this.id = util.randomUUID(); + this.body = body; + + this.defaultOptions = { + orientation: 'left', // supported: 'left', 'right' + showMinorLabels: true, + showMajorLabels: true, + icons: true, + majorLinesOffset: 7, + minorLinesOffset: 4, + labelOffsetX: 10, + labelOffsetY: 2, + iconWidth: 20, + width: '40px', + visible: true, + alignZeros: true, + customRange: { + left: {min:undefined, max:undefined}, + right: {min:undefined, max:undefined} + }, + title: { + left: {text:undefined}, + right: {text:undefined} + }, + format: { + left: {decimals: undefined}, + right: {decimals: undefined} } - }; + }; - /** - * @module gestures - */ - /** - * Touch stays at the same place for x time - * - * @class Hold - * @static - */ - /** - * @event hold - * @param {Object} ev - */ + this.linegraphOptions = linegraphOptions; + this.linegraphSVG = svg; + this.props = {}; + this.DOMelements = { // dynamic elements + lines: {}, + labels: {}, + title: {} + }; - /** - * @param {String} name - */ - (function(name) { - var timer; + this.dom = {}; - function holdGesture(ev, inst) { - var options = inst.options, - current = Detection.current; + this.range = {start:0, end:0}; - switch(ev.eventType) { - case EVENT_START: - clearTimeout(timer); + this.options = util.extend({}, this.defaultOptions); + this.conversionFactor = 1; - // set the gesture so we can check in the timeout if it still is - current.name = name; + this.setOptions(options); + this.width = Number(('' + this.options.width).replace("px","")); + this.minWidth = this.width; + this.height = this.linegraphSVG.offsetHeight; + this.hidden = false; - // set timer and if after the timeout it still is hold, - // we trigger the hold event - timer = setTimeout(function() { - if(current && current.name == name) { - inst.trigger(name, ev); - } - }, options.holdTimeout); - break; + this.stepPixels = 25; + this.stepPixelsForced = 25; + this.zeroCrossing = -1; - case EVENT_MOVE: - if(ev.distance > options.holdThreshold) { - clearTimeout(timer); - } - break; + this.lineOffset = 0; + this.master = true; + this.svgElements = {}; + this.iconsRemoved = false; - case EVENT_RELEASE: - clearTimeout(timer); - break; - } - } - Hammer.gestures.Hold = { - name: name, - index: 10, - defaults: { - /** - * @property holdTimeout - * @type {Number} - * @default 500 - */ - holdTimeout: 500, + this.groups = {}; + this.amountOfGroups = 0; - /** - * movement allowed while holding - * @property holdThreshold - * @type {Number} - * @default 2 - */ - holdThreshold: 2 - }, - handler: holdGesture - }; - })('hold'); + // create the HTML DOM + this._create(); - /** - * @module gestures - */ - /** - * when a touch is being released from the page - * - * @class Release - * @static - */ - /** - * @event release - * @param {Object} ev - */ - Hammer.gestures.Release = { - name: 'release', - index: Infinity, - handler: function releaseGesture(ev, inst) { - if(ev.eventType == EVENT_RELEASE) { - inst.trigger(this.name, ev); - } - } - }; + var me = this; + this.body.emitter.on("verticalDrag", function() { + me.dom.lineContainer.style.top = me.body.domProps.scrollTop + 'px'; + }); + } - /** - * @module gestures - */ - /** - * triggers swipe events when the end velocity is above the threshold - * for best usage, set `preventDefault` (on the drag gesture) to `true` - * ```` - * hammertime.on("dragleft swipeleft", function(ev) { - * console.log(ev); - * ev.gesture.preventDefault(); - * }); - * ```` - * - * @class Swipe - * @static - */ - /** - * @event swipe - * @param {Object} ev - */ - /** - * @event swipeleft - * @param {Object} ev - */ - /** - * @event swiperight - * @param {Object} ev - */ - /** - * @event swipeup - * @param {Object} ev - */ - /** - * @event swipedown - * @param {Object} ev - */ - Hammer.gestures.Swipe = { - name: 'swipe', - index: 40, - defaults: { - /** - * @property swipeMinTouches - * @type {Number} - * @default 1 - */ - swipeMinTouches: 1, + DataAxis.prototype = new Component(); - /** - * @property swipeMaxTouches - * @type {Number} - * @default 1 - */ - swipeMaxTouches: 1, - /** - * horizontal swipe velocity - * @property swipeVelocityX - * @type {Number} - * @default 0.6 - */ - swipeVelocityX: 0.6, + DataAxis.prototype.addGroup = function(label, graphOptions) { + if (!this.groups.hasOwnProperty(label)) { + this.groups[label] = graphOptions; + } + this.amountOfGroups += 1; + }; - /** - * vertical swipe velocity - * @property swipeVelocityY - * @type {Number} - * @default 0.6 - */ - swipeVelocityY: 0.6 - }, + DataAxis.prototype.updateGroup = function(label, graphOptions) { + this.groups[label] = graphOptions; + }; - handler: function swipeGesture(ev, inst) { - if(ev.eventType == EVENT_RELEASE) { - var touches = ev.touches.length, - options = inst.options; + DataAxis.prototype.removeGroup = function(label) { + if (this.groups.hasOwnProperty(label)) { + delete this.groups[label]; + this.amountOfGroups -= 1; + } + }; - // max touches - if(touches < options.swipeMinTouches || - touches > options.swipeMaxTouches) { - return; - } - // when the distance we moved is too small we skip this gesture - // or we can be already in dragging - if(ev.velocityX > options.swipeVelocityX || - ev.velocityY > options.swipeVelocityY) { - // trigger swipe events - inst.trigger(this.name, ev); - inst.trigger(this.name + ev.direction, ev); - } - } + DataAxis.prototype.setOptions = function (options) { + if (options) { + var redraw = false; + if (this.options.orientation != options.orientation && options.orientation !== undefined) { + redraw = true; + } + var fields = [ + 'orientation', + 'showMinorLabels', + 'showMajorLabels', + 'icons', + 'majorLinesOffset', + 'minorLinesOffset', + 'labelOffsetX', + 'labelOffsetY', + 'iconWidth', + 'width', + 'visible', + 'customRange', + 'title', + 'format', + 'alignZeros' + ]; + util.selectiveExtend(fields, this.options, options); + + this.minWidth = Number(('' + this.options.width).replace("px","")); + + if (redraw == true && this.dom.frame) { + this.hide(); + this.show(); } + } }; - /** - * @module gestures - */ - /** - * Single tap and a double tap on a place - * - * @class Tap - * @static - */ - /** - * @event tap - * @param {Object} ev - */ - /** - * @event doubletap - * @param {Object} ev - */ /** - * @param {String} name + * Create the HTML DOM for the DataAxis */ - (function(name) { - var hasMoved = false; + DataAxis.prototype._create = function() { + this.dom.frame = document.createElement('div'); + this.dom.frame.style.width = this.options.width; + this.dom.frame.style.height = this.height; - function tapGesture(ev, inst) { - var options = inst.options, - current = Detection.current, - prev = Detection.previous, - sincePrev, - didDoubleTap; + this.dom.lineContainer = document.createElement('div'); + this.dom.lineContainer.style.width = '100%'; + this.dom.lineContainer.style.height = this.height; + this.dom.lineContainer.style.position = 'relative'; - switch(ev.eventType) { - case EVENT_START: - hasMoved = false; - break; + // create svg element for graph drawing. + this.svg = document.createElementNS('http://www.w3.org/2000/svg',"svg"); + this.svg.style.position = "absolute"; + this.svg.style.top = '0px'; + this.svg.style.height = '100%'; + this.svg.style.width = '100%'; + this.svg.style.display = "block"; + this.dom.frame.appendChild(this.svg); + }; - case EVENT_MOVE: - hasMoved = hasMoved || (ev.distance > options.tapMaxDistance); - break; + DataAxis.prototype._redrawGroupIcons = function () { + DOMutil.prepareElements(this.svgElements); - case EVENT_END: - if(!Utils.inStr(ev.srcEvent.type, 'cancel') && ev.deltaTime < options.tapMaxTime && !hasMoved) { - // previous gesture, for the double tap since these are two different gesture detections - sincePrev = prev && prev.lastEvent && ev.timeStamp - prev.lastEvent.timeStamp; - didDoubleTap = false; + var x; + var iconWidth = this.options.iconWidth; + var iconHeight = 15; + var iconOffset = 4; + var y = iconOffset + 0.5 * iconHeight; - // check if double tap - if(prev && prev.name == name && - (sincePrev && sincePrev < options.doubleTapInterval) && - ev.distance < options.doubleTapDistance) { - inst.trigger('doubletap', ev); - didDoubleTap = true; - } + if (this.options.orientation == 'left') { + x = iconOffset; + } + else { + x = this.width - iconWidth - iconOffset; + } - // do a single tap - if(!didDoubleTap || options.tapAlways) { - current.name = name; - inst.trigger(current.name, ev); - } - } - break; - } + for (var groupId in this.groups) { + if (this.groups.hasOwnProperty(groupId)) { + if (this.groups[groupId].visible == true && (this.linegraphOptions.visibility[groupId] === undefined || this.linegraphOptions.visibility[groupId] == true)) { + this.groups[groupId].drawIcon(x, y, this.svgElements, this.svg, iconWidth, iconHeight); + y += iconHeight + iconOffset; + } } + } - Hammer.gestures.Tap = { - name: name, - index: 100, - handler: tapGesture, - defaults: { - /** - * max time of a tap, this is for the slow tappers - * @property tapMaxTime - * @type {Number} - * @default 250 - */ - tapMaxTime: 250, - - /** - * max distance of movement of a tap, this is for the slow tappers - * @property tapMaxDistance - * @type {Number} - * @default 10 - */ - tapMaxDistance: 10, - - /** - * always trigger the `tap` event, even while double-tapping - * @property tapAlways - * @type {Boolean} - * @default true - */ - tapAlways: true, - - /** - * max distance between two taps - * @property doubleTapDistance - * @type {Number} - * @default 20 - */ - doubleTapDistance: 20, + DOMutil.cleanupElements(this.svgElements); + this.iconsRemoved = false; + }; - /** - * max time between two taps - * @property doubleTapInterval - * @type {Number} - * @default 300 - */ - doubleTapInterval: 300 - } - }; - })('tap'); + DataAxis.prototype._cleanupIcons = function() { + if (this.iconsRemoved == false) { + DOMutil.prepareElements(this.svgElements); + DOMutil.cleanupElements(this.svgElements); + this.iconsRemoved = true; + } + } /** - * @module gestures - */ - /** - * when a touch is being touched at the page - * - * @class Touch - * @static - */ - /** - * @event touch - * @param {Object} ev + * Create the HTML DOM for the DataAxis */ - Hammer.gestures.Touch = { - name: 'touch', - index: -Infinity, - defaults: { - /** - * call preventDefault at touchstart, and makes the element blocking by disabling the scrolling of the page, - * but it improves gestures like transforming and dragging. - * be careful with using this, it can be very annoying for users to be stuck on the page - * @property preventDefault - * @type {Boolean} - * @default false - */ - preventDefault: false, - - /** - * disable mouse events, so only touch (or pen!) input triggers events - * @property preventMouse - * @type {Boolean} - * @default false - */ - preventMouse: false - }, - handler: function touchGesture(ev, inst) { - if(inst.options.preventMouse && ev.pointerType == POINTER_MOUSE) { - ev.stopDetect(); - return; - } - - if(inst.options.preventDefault) { - ev.preventDefault(); - } - - if(ev.eventType == EVENT_TOUCH) { - inst.trigger('touch', ev); - } + DataAxis.prototype.show = function() { + this.hidden = false; + if (!this.dom.frame.parentNode) { + if (this.options.orientation == 'left') { + this.body.dom.left.appendChild(this.dom.frame); + } + else { + this.body.dom.right.appendChild(this.dom.frame); } + } + + if (!this.dom.lineContainer.parentNode) { + this.body.dom.backgroundHorizontal.appendChild(this.dom.lineContainer); + } }; /** - * @module gestures - */ - /** - * User want to scale or rotate with 2 fingers - * Preventing the default browser behavior is a good way to improve feel and working. This can be done with the - * `preventDefault` option. - * - * @class Transform - * @static - */ - /** - * @event transform - * @param {Object} ev - */ - /** - * @event transformstart - * @param {Object} ev - */ - /** - * @event transformend - * @param {Object} ev - */ - /** - * @event pinchin - * @param {Object} ev - */ - /** - * @event pinchout - * @param {Object} ev + * Create the HTML DOM for the DataAxis */ + DataAxis.prototype.hide = function() { + this.hidden = true; + if (this.dom.frame.parentNode) { + this.dom.frame.parentNode.removeChild(this.dom.frame); + } + + if (this.dom.lineContainer.parentNode) { + this.dom.lineContainer.parentNode.removeChild(this.dom.lineContainer); + } + }; + /** - * @event rotate - * @param {Object} ev + * Set a range (start and end) + * @param end + * @param start + * @param end */ + DataAxis.prototype.setRange = function (start, end) { + if (this.master == false && this.options.alignZeros == true && this.zeroCrossing != -1) { + if (start > 0) { + start = 0; + } + } + this.range.start = start; + this.range.end = end; + }; /** - * @param {String} name + * Repaint the component + * @return {boolean} Returns true if the component is resized */ - (function(name) { - var triggered = false; - - function transformGesture(ev, inst) { - switch(ev.eventType) { - case EVENT_START: - triggered = false; - break; + DataAxis.prototype.redraw = function () { + var resized = false; + var activeGroups = 0; + + // Make sure the line container adheres to the vertical scrolling. + this.dom.lineContainer.style.top = this.body.domProps.scrollTop + 'px'; - case EVENT_MOVE: - // at least multitouch - if(ev.touches.length < 2) { - return; - } + for (var groupId in this.groups) { + if (this.groups.hasOwnProperty(groupId)) { + if (this.groups[groupId].visible == true && (this.linegraphOptions.visibility[groupId] === undefined || this.linegraphOptions.visibility[groupId] == true)) { + activeGroups++; + } + } + } + if (this.amountOfGroups == 0 || activeGroups == 0) { + this.hide(); + } + else { + this.show(); + this.height = Number(this.linegraphSVG.style.height.replace("px","")); - var scaleThreshold = Math.abs(1 - ev.scale); - var rotationThreshold = Math.abs(ev.rotation); + // svg offsetheight did not work in firefox and explorer... + this.dom.lineContainer.style.height = this.height + 'px'; + this.width = this.options.visible == true ? Number(('' + this.options.width).replace("px","")) : 0; - // when the distance we moved is too small we skip this gesture - // or we can be already in dragging - if(scaleThreshold < inst.options.transformMinScale && - rotationThreshold < inst.options.transformMinRotation) { - return; - } + var props = this.props; + var frame = this.dom.frame; - // we are transforming! - Detection.current.name = name; + // update classname + frame.className = 'dataaxis'; - // first time, trigger dragstart event - if(!triggered) { - inst.trigger(name + 'start', ev); - triggered = true; - } + // calculate character width and height + this._calculateCharSize(); - inst.trigger(name, ev); // basic transform event + var orientation = this.options.orientation; + var showMinorLabels = this.options.showMinorLabels; + var showMajorLabels = this.options.showMajorLabels; - // trigger rotate event - if(rotationThreshold > inst.options.transformMinRotation) { - inst.trigger('rotate', ev); - } + // determine the width and height of the elements for the axis + props.minorLabelHeight = showMinorLabels ? props.minorCharHeight : 0; + props.majorLabelHeight = showMajorLabels ? props.majorCharHeight : 0; - // trigger pinch event - if(scaleThreshold > inst.options.transformMinScale) { - inst.trigger('pinch', ev); - inst.trigger('pinch' + (ev.scale < 1 ? 'in' : 'out'), ev); - } - break; + props.minorLineWidth = this.body.dom.backgroundHorizontal.offsetWidth - this.lineOffset - this.width + 2 * this.options.minorLinesOffset; + props.minorLineHeight = 1; + props.majorLineWidth = this.body.dom.backgroundHorizontal.offsetWidth - this.lineOffset - this.width + 2 * this.options.majorLinesOffset; + props.majorLineHeight = 1; - case EVENT_RELEASE: - if(triggered && ev.changedLength < 2) { - inst.trigger(name + 'end', ev); - triggered = false; - } - break; - } + // take frame offline while updating (is almost twice as fast) + if (orientation == 'left') { + frame.style.top = '0'; + frame.style.left = '0'; + frame.style.bottom = ''; + frame.style.width = this.width + 'px'; + frame.style.height = this.height + "px"; + this.props.width = this.body.domProps.left.width; + this.props.height = this.body.domProps.left.height; + } + else { // right + frame.style.top = ''; + frame.style.bottom = '0'; + frame.style.left = '0'; + frame.style.width = this.width + 'px'; + frame.style.height = this.height + "px"; + this.props.width = this.body.domProps.right.width; + this.props.height = this.body.domProps.right.height; } - Hammer.gestures.Transform = { - name: name, - index: 45, - defaults: { - /** - * minimal scale factor, no scale is 1, zoomin is to 0 and zoomout until higher then 1 - * @property transformMinScale - * @type {Number} - * @default 0.01 - */ - transformMinScale: 0.01, + resized = this._redrawLabels(); + resized = this._isResized() || resized; - /** - * rotation in degrees - * @property transformMinRotation - * @type {Number} - * @default 1 - */ - transformMinRotation: 1 - }, + if (this.options.icons == true) { + this._redrawGroupIcons(); + } + else { + this._cleanupIcons(); + } - handler: transformGesture - }; - })('transform'); + this._redrawTitle(orientation); + } + return resized; + }; /** - * @module hammer + * Repaint major and minor text labels and vertical grid lines + * @private */ + DataAxis.prototype._redrawLabels = function () { + var resized = false; + DOMutil.prepareElements(this.DOMelements.lines); + DOMutil.prepareElements(this.DOMelements.labels); - // AMD export - if(true) { - !(__WEBPACK_AMD_DEFINE_RESULT__ = function() { - return Hammer; - }.call(exports, __webpack_require__, exports, module), __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__)); - // commonjs export - } else if(typeof module !== 'undefined' && module.exports) { - module.exports = Hammer; - // browser export - } else { - window.Hammer = Hammer; - } + var orientation = this.options['orientation']; - })(window); + // calculate range and step (step such that we have space for 7 characters per label) + var minimumStep = this.master ? this.props.majorCharHeight || 10 : this.stepPixelsForced; -/***/ }, -/* 21 */ -/***/ function(module, exports, __webpack_require__) { + var step = new DataStep( + this.range.start, + this.range.end, + minimumStep, + this.dom.frame.offsetHeight, + this.options.customRange[this.options.orientation], + this.master == false && this.options.alignZeros // doess the step have to align zeros? only if not master and the options is on + ); - var util = __webpack_require__(1); - var hammerUtil = __webpack_require__(22); - var moment = __webpack_require__(2); - var Component = __webpack_require__(23); - var DateUtil = __webpack_require__(24); + this.step = step; + // get the distance in pixels for a step + // dead space is space that is "left over" after a step + var stepPixels = (this.dom.frame.offsetHeight - (step.deadSpace * (this.dom.frame.offsetHeight / step.marginRange))) / (((step.marginRange - step.deadSpace) / step.step)); - /** - * @constructor Range - * A Range controls a numeric range with a start and end value. - * The Range adjusts the range based on mouse events or programmatic changes, - * and triggers events when the range is changing or has been changed. - * @param {{dom: Object, domProps: Object, emitter: Emitter}} body - * @param {Object} [options] See description at Range.setOptions - */ - function Range(body, options) { - var now = moment().hours(0).minutes(0).seconds(0).milliseconds(0); - this.start = now.clone().add(-3, 'days').valueOf(); // Number - this.end = now.clone().add(4, 'days').valueOf(); // Number + this.stepPixels = stepPixels; - this.body = body; - this.deltaDifference = 0; - this.scaleOffset = 0; - this.startToFront = false; - this.endToFront = true; + var amountOfSteps = this.height / stepPixels; + var stepDifference = 0; - // default options - this.defaultOptions = { - start: null, - end: null, - direction: 'horizontal', // 'horizontal' or 'vertical' - moveable: true, - zoomable: true, - min: null, - max: null, - zoomMin: 10, // milliseconds - zoomMax: 1000 * 60 * 60 * 24 * 365 * 10000 // milliseconds - }; - this.options = util.extend({}, this.defaultOptions); + // the slave axis needs to use the same horizontal lines as the master axis. + if (this.master == false) { + stepPixels = this.stepPixelsForced; + stepDifference = Math.round((this.dom.frame.offsetHeight / stepPixels) - amountOfSteps); + for (var i = 0; i < 0.5 * stepDifference; i++) { + step.previous(); + } + amountOfSteps = this.height / stepPixels; - this.props = { - touch: {} - }; - this.animateTimer = null; + if (this.zeroCrossing != -1 && this.options.alignZeros == true) { + var zeroStepDifference = (step.marginEnd / step.step) - this.zeroCrossing; + if (zeroStepDifference > 0) { + for (var i = 0; i < zeroStepDifference; i++) {step.next();} + } + else if (zeroStepDifference < 0) { + for (var i = 0; i < -zeroStepDifference; i++) {step.previous();} + } + } + } + else { + amountOfSteps += 0.25; + } - // drag listeners for dragging - this.body.emitter.on('dragstart', this._onDragStart.bind(this)); - this.body.emitter.on('drag', this._onDrag.bind(this)); - this.body.emitter.on('dragend', this._onDragEnd.bind(this)); - // ignore dragging when holding - this.body.emitter.on('hold', this._onHold.bind(this)); + this.valueAtZero = step.marginEnd; + var marginStartPos = 0; - // mouse wheel for zooming - this.body.emitter.on('mousewheel', this._onMouseWheel.bind(this)); - this.body.emitter.on('DOMMouseScroll', this._onMouseWheel.bind(this)); // For FF + // do not draw the first label + var max = 1; - // pinch to zoom - this.body.emitter.on('touch', this._onTouch.bind(this)); - this.body.emitter.on('pinch', this._onPinch.bind(this)); + // Get the number of decimal places + var decimals; + if(this.options.format[orientation] !== undefined) { + decimals = this.options.format[orientation].decimals; + } - this.setOptions(options); - } + this.maxLabelSize = 0; + var y = 0; + while (max < Math.round(amountOfSteps)) { + step.next(); + y = Math.round(max * stepPixels); + marginStartPos = max * stepPixels; + var isMajor = step.isMajor(); - Range.prototype = new Component(); + if (this.options['showMinorLabels'] && isMajor == false || this.master == false && this.options['showMinorLabels'] == true) { + this._redrawLabel(y - 2, step.getCurrent(decimals), orientation, 'yAxis minor', this.props.minorCharHeight); + } - /** - * Set options for the range controller - * @param {Object} options Available options: - * {Number | Date | String} start Start date for the range - * {Number | Date | String} end End date for the range - * {Number} min Minimum value for start - * {Number} max Maximum value for end - * {Number} zoomMin Set a minimum value for - * (end - start). - * {Number} zoomMax Set a maximum value for - * (end - start). - * {Boolean} moveable Enable moving of the range - * by dragging. True by default - * {Boolean} zoomable Enable zooming of the range - * by pinching/scrolling. True by default - */ - Range.prototype.setOptions = function (options) { - if (options) { - // copy the options that we know - var fields = ['direction', 'min', 'max', 'zoomMin', 'zoomMax', 'moveable', 'zoomable', 'activate', 'hiddenDates']; - util.selectiveExtend(fields, this.options, options); + if (isMajor && this.options['showMajorLabels'] && this.master == true || + this.options['showMinorLabels'] == false && this.master == false && isMajor == true) { + if (y >= 0) { + this._redrawLabel(y - 2, step.getCurrent(decimals), orientation, 'yAxis major', this.props.majorCharHeight); + } + this._redrawLine(y, orientation, 'grid horizontal major', this.options.majorLinesOffset, this.props.majorLineWidth); + } + else { + this._redrawLine(y, orientation, 'grid horizontal minor', this.options.minorLinesOffset, this.props.minorLineWidth); + } - if ('start' in options || 'end' in options) { - // apply a new range. both start and end are optional - this.setRange(options.start, options.end); + if (this.master == true && step.current == 0) { + this.zeroCrossing = max; } + + max++; } - }; - /** - * Test whether direction has a valid value - * @param {String} direction 'horizontal' or 'vertical' - */ - function validateDirection (direction) { - if (direction != 'horizontal' && direction != 'vertical') { - throw new TypeError('Unknown direction "' + direction + '". ' + - 'Choose "horizontal" or "vertical".'); + if (this.master == false) { + this.conversionFactor = y / (this.valueAtZero - step.current); + } + else { + this.conversionFactor = this.dom.frame.offsetHeight / step.marginRange; } - } - /** - * Set a new start and end range - * @param {Date | Number | String} [start] - * @param {Date | Number | String} [end] - * @param {boolean | number} [animate=false] If true, the range is animated - * smoothly to the new window. - * If animate is a number, the - * number is taken as duration - * Default duration is 500 ms. - * @param {Boolean} [byUser=false] - * - */ - Range.prototype.setRange = function(start, end, animate, byUser) { - if (byUser !== true) { - byUser = false; + // Note that title is rotated, so we're using the height, not width! + var titleWidth = 0; + if (this.options.title[orientation] !== undefined && this.options.title[orientation].text !== undefined) { + titleWidth = this.props.titleCharHeight; } - var _start = start != undefined ? util.convert(start, 'Date').valueOf() : null; - var _end = end != undefined ? util.convert(end, 'Date').valueOf() : null; - this._cancelAnimation(); + var offset = this.options.icons == true ? Math.max(this.options.iconWidth, titleWidth) + this.options.labelOffsetX + 15 : titleWidth + this.options.labelOffsetX + 15; - if (animate) { - var me = this; - var initStart = this.start; - var initEnd = this.end; - var duration = typeof animate === 'number' ? animate : 500; - var initTime = new Date().valueOf(); - var anyChanged = false; - - var next = function () { - if (!me.props.touch.dragging) { - var now = new Date().valueOf(); - var time = now - initTime; - var done = time > duration; - var s = (done || _start === null) ? _start : util.easeInOutQuad(time, initStart, _start, duration); - var e = (done || _end === null) ? _end : util.easeInOutQuad(time, initEnd, _end, duration); - - changed = me._applyRange(s, e); - DateUtil.updateHiddenDates(me.body, me.options.hiddenDates); - anyChanged = anyChanged || changed; - if (changed) { - me.body.emitter.emit('rangechange', {start: new Date(me.start), end: new Date(me.end), byUser:byUser}); - } - - if (done) { - if (anyChanged) { - me.body.emitter.emit('rangechanged', {start: new Date(me.start), end: new Date(me.end), byUser:byUser}); - } - } - else { - // animate with as high as possible frame rate, leave 20 ms in between - // each to prevent the browser from blocking - me.animateTimer = setTimeout(next, 20); - } - } - } - - return next(); + // this will resize the yAxis to accommodate the labels. + if (this.maxLabelSize > (this.width - offset) && this.options.visible == true) { + this.width = this.maxLabelSize + offset; + this.options.width = this.width + "px"; + DOMutil.cleanupElements(this.DOMelements.lines); + DOMutil.cleanupElements(this.DOMelements.labels); + this.redraw(); + resized = true; + } + // this will resize the yAxis if it is too big for the labels. + else if (this.maxLabelSize < (this.width - offset) && this.options.visible == true && this.width > this.minWidth) { + this.width = Math.max(this.minWidth,this.maxLabelSize + offset); + this.options.width = this.width + "px"; + DOMutil.cleanupElements(this.DOMelements.lines); + DOMutil.cleanupElements(this.DOMelements.labels); + this.redraw(); + resized = true; } else { - var changed = this._applyRange(_start, _end); - DateUtil.updateHiddenDates(this.body, this.options.hiddenDates); - if (changed) { - var params = {start: new Date(this.start), end: new Date(this.end), byUser:byUser}; - this.body.emitter.emit('rangechange', params); - this.body.emitter.emit('rangechanged', params); - } + DOMutil.cleanupElements(this.DOMelements.lines); + DOMutil.cleanupElements(this.DOMelements.labels); + resized = false; } + + return resized; }; - /** - * Stop an animation - * @private - */ - Range.prototype._cancelAnimation = function () { - if (this.animateTimer) { - clearTimeout(this.animateTimer); - this.animateTimer = null; - } + DataAxis.prototype.convertValue = function (value) { + var invertedValue = this.valueAtZero - value; + var convertedValue = invertedValue * this.conversionFactor; + return convertedValue; }; /** - * Set a new start and end range. This method is the same as setRange, but - * does not trigger a range change and range changed event, and it returns - * true when the range is changed - * @param {Number} [start] - * @param {Number} [end] - * @return {Boolean} changed + * Create a label for the axis at position x * @private + * @param y + * @param text + * @param orientation + * @param className + * @param characterHeight */ - Range.prototype._applyRange = function(start, end) { - var newStart = (start != null) ? util.convert(start, 'Date').valueOf() : this.start, - newEnd = (end != null) ? util.convert(end, 'Date').valueOf() : this.end, - max = (this.options.max != null) ? util.convert(this.options.max, 'Date').valueOf() : null, - min = (this.options.min != null) ? util.convert(this.options.min, 'Date').valueOf() : null, - diff; - - // check for valid number - if (isNaN(newStart) || newStart === null) { - throw new Error('Invalid start "' + start + '"'); - } - if (isNaN(newEnd) || newEnd === null) { - throw new Error('Invalid end "' + end + '"'); + DataAxis.prototype._redrawLabel = function (y, text, orientation, className, characterHeight) { + // reuse redundant label + var label = DOMutil.getDOMElement('div',this.DOMelements.labels, this.dom.frame); //this.dom.redundant.labels.shift(); + label.className = className; + label.innerHTML = text; + if (orientation == 'left') { + label.style.left = '-' + this.options.labelOffsetX + 'px'; + label.style.textAlign = "right"; } - - // prevent start < end - if (newEnd < newStart) { - newEnd = newStart; + else { + label.style.right = '-' + this.options.labelOffsetX + 'px'; + label.style.textAlign = "left"; } - // prevent start < min - if (min !== null) { - if (newStart < min) { - diff = (min - newStart); - newStart += diff; - newEnd += diff; - - // prevent end > max - if (max != null) { - if (newEnd > max) { - newEnd = max; - } - } - } - } + label.style.top = y - 0.5 * characterHeight + this.options.labelOffsetY + 'px'; - // prevent end > max - if (max !== null) { - if (newEnd > max) { - diff = (newEnd - max); - newStart -= diff; - newEnd -= diff; + text += ''; - // prevent start < min - if (min != null) { - if (newStart < min) { - newStart = min; - } - } - } + var largestWidth = Math.max(this.props.majorCharWidth,this.props.minorCharWidth); + if (this.maxLabelSize < text.length * largestWidth) { + this.maxLabelSize = text.length * largestWidth; } + }; - // prevent (end-start) < zoomMin - if (this.options.zoomMin !== null) { - var zoomMin = parseFloat(this.options.zoomMin); - if (zoomMin < 0) { - zoomMin = 0; - } - if ((newEnd - newStart) < zoomMin) { - if ((this.end - this.start) === zoomMin) { - // ignore this action, we are already zoomed to the minimum - newStart = this.start; - newEnd = this.end; - } - else { - // zoom to the minimum - diff = (zoomMin - (newEnd - newStart)); - newStart -= diff / 2; - newEnd += diff / 2; - } - } - } + /** + * Create a minor line for the axis at position y + * @param y + * @param orientation + * @param className + * @param offset + * @param width + */ + DataAxis.prototype._redrawLine = function (y, orientation, className, offset, width) { + if (this.master == true) { + var line = DOMutil.getDOMElement('div',this.DOMelements.lines, this.dom.lineContainer);//this.dom.redundant.lines.shift(); + line.className = className; + line.innerHTML = ''; - // prevent (end-start) > zoomMax - if (this.options.zoomMax !== null) { - var zoomMax = parseFloat(this.options.zoomMax); - if (zoomMax < 0) { - zoomMax = 0; + if (orientation == 'left') { + line.style.left = (this.width - offset) + 'px'; } - if ((newEnd - newStart) > zoomMax) { - if ((this.end - this.start) === zoomMax) { - // ignore this action, we are already zoomed to the maximum - newStart = this.start; - newEnd = this.end; - } - else { - // zoom to the maximum - diff = ((newEnd - newStart) - zoomMax); - newStart += diff / 2; - newEnd -= diff / 2; - } + else { + line.style.right = (this.width - offset) + 'px'; } - } - - var changed = (this.start != newStart || this.end != newEnd); - // if the new range does NOT overlap with the old range, emit checkRangedItems to avoid not showing ranged items (ranged meaning has end time, not neccesarily of type Range) - if (!((newStart >= this.start && newStart <= this.end) || (newEnd >= this.start && newEnd <= this.end)) && - !((this.start >= newStart && this.start <= newEnd) || (this.end >= newStart && this.end <= newEnd) )) { - this.body.emitter.emit('checkRangedItems'); + line.style.width = width + 'px'; + line.style.top = y + 'px'; } - - this.start = newStart; - this.end = newEnd; - return changed; }; /** - * Retrieve the current range. - * @return {Object} An object with start and end properties + * Create a title for the axis + * @private + * @param orientation */ - Range.prototype.getRange = function() { - return { - start: this.start, - end: this.end - }; - }; + DataAxis.prototype._redrawTitle = function (orientation) { + DOMutil.prepareElements(this.DOMelements.title); - /** - * Calculate the conversion offset and scale for current range, based on - * the provided width - * @param {Number} width - * @returns {{offset: number, scale: number}} conversion - */ - Range.prototype.conversion = function (width, totalHidden) { - return Range.conversion(this.start, this.end, width, totalHidden); - }; + // Check if the title is defined for this axes + if (this.options.title[orientation] !== undefined && this.options.title[orientation].text !== undefined) { + var title = DOMutil.getDOMElement('div', this.DOMelements.title, this.dom.frame); + title.className = 'yAxis title ' + orientation; + title.innerHTML = this.options.title[orientation].text; - /** - * Static method to calculate the conversion offset and scale for a range, - * based on the provided start, end, and width - * @param {Number} start - * @param {Number} end - * @param {Number} width - * @returns {{offset: number, scale: number}} conversion - */ - Range.conversion = function (start, end, width, totalHidden) { - if (totalHidden === undefined) { - totalHidden = 0; - } - if (width != 0 && (end - start != 0)) { - return { - offset: start, - scale: width / (end - start - totalHidden) + // Add style - if provided + if (this.options.title[orientation].style !== undefined) { + util.addCssText(title, this.options.title[orientation].style); } + + if (orientation == 'left') { + title.style.left = this.props.titleCharHeight + 'px'; + } + else { + title.style.right = this.props.titleCharHeight + 'px'; + } + + title.style.width = this.height + 'px'; } - else { - return { - offset: 0, - scale: 1 - }; - } - }; - /** - * Start dragging horizontally or vertically - * @param {Event} event - * @private - */ - Range.prototype._onDragStart = function(event) { - this.deltaDifference = 0; - this.previousDelta = 0; - // only allow dragging when configured as movable - if (!this.options.moveable) return; + // we need to clean up in case we did not use all elements. + DOMutil.cleanupElements(this.DOMelements.title); + }; - // refuse to drag when we where pinching to prevent the timeline make a jump - // when releasing the fingers in opposite order from the touch screen - if (!this.props.touch.allowDragging) return; - this.props.touch.start = this.start; - this.props.touch.end = this.end; - this.props.touch.dragging = true; - if (this.body.dom.root) { - this.body.dom.root.style.cursor = 'move'; - } - }; /** - * Perform dragging operation - * @param {Event} event + * Determine the size of text on the axis (both major and minor axis). + * The size is calculated only once and then cached in this.props. * @private */ - Range.prototype._onDrag = function (event) { - // only allow dragging when configured as movable - if (!this.options.moveable) return; - // refuse to drag when we where pinching to prevent the timeline make a jump - // when releasing the fingers in opposite order from the touch screen - if (!this.props.touch.allowDragging) return; - - var direction = this.options.direction; - validateDirection(direction); + DataAxis.prototype._calculateCharSize = function () { + // determine the char width and height on the minor axis + if (!('minorCharHeight' in this.props)) { + var textMinor = document.createTextNode('0'); + var measureCharMinor = document.createElement('div'); + measureCharMinor.className = 'yAxis minor measure'; + measureCharMinor.appendChild(textMinor); + this.dom.frame.appendChild(measureCharMinor); - var delta = (direction == 'horizontal') ? event.gesture.deltaX : event.gesture.deltaY; - delta -= this.deltaDifference; - var interval = (this.props.touch.end - this.props.touch.start); + this.props.minorCharHeight = measureCharMinor.clientHeight; + this.props.minorCharWidth = measureCharMinor.clientWidth; - // normalize dragging speed if cutout is in between. - var duration = DateUtil.getHiddenDurationBetween(this.body.hiddenDates, this.start, this.end); - interval -= duration; + this.dom.frame.removeChild(measureCharMinor); + } - var width = (direction == 'horizontal') ? this.body.domProps.center.width : this.body.domProps.center.height; - var diffRange = -delta / width * interval; - var newStart = this.props.touch.start + diffRange; - var newEnd = this.props.touch.end + diffRange; + if (!('majorCharHeight' in this.props)) { + var textMajor = document.createTextNode('0'); + var measureCharMajor = document.createElement('div'); + measureCharMajor.className = 'yAxis major measure'; + measureCharMajor.appendChild(textMajor); + this.dom.frame.appendChild(measureCharMajor); + this.props.majorCharHeight = measureCharMajor.clientHeight; + this.props.majorCharWidth = measureCharMajor.clientWidth; - // snapping times away from hidden zones - var safeStart = DateUtil.snapAwayFromHidden(this.body.hiddenDates, newStart, this.previousDelta-delta, true); - var safeEnd = DateUtil.snapAwayFromHidden(this.body.hiddenDates, newEnd, this.previousDelta-delta, true); - if (safeStart != newStart || safeEnd != newEnd) { - this.deltaDifference += delta; - this.props.touch.start = safeStart; - this.props.touch.end = safeEnd; - this._onDrag(event); - return; + this.dom.frame.removeChild(measureCharMajor); } - this.previousDelta = delta; - this._applyRange(newStart, newEnd); - - // fire a rangechange event - this.body.emitter.emit('rangechange', { - start: new Date(this.start), - end: new Date(this.end), - byUser: true - }); - }; - - /** - * Stop dragging operation - * @param {event} event - * @private - */ - Range.prototype._onDragEnd = function (event) { - // only allow dragging when configured as movable - if (!this.options.moveable) return; + if (!('titleCharHeight' in this.props)) { + var textTitle = document.createTextNode('0'); + var measureCharTitle = document.createElement('div'); + measureCharTitle.className = 'yAxis title measure'; + measureCharTitle.appendChild(textTitle); + this.dom.frame.appendChild(measureCharTitle); - // refuse to drag when we where pinching to prevent the timeline make a jump - // when releasing the fingers in opposite order from the touch screen - if (!this.props.touch.allowDragging) return; + this.props.titleCharHeight = measureCharTitle.clientHeight; + this.props.titleCharWidth = measureCharTitle.clientWidth; - this.props.touch.dragging = false; - if (this.body.dom.root) { - this.body.dom.root.style.cursor = 'auto'; + this.dom.frame.removeChild(measureCharTitle); } - - // fire a rangechanged event - this.body.emitter.emit('rangechanged', { - start: new Date(this.start), - end: new Date(this.end), - byUser: true - }); }; /** - * Event handler for mouse wheel event, used to zoom - * Code from http://adomas.org/javascript-mouse-wheel/ - * @param {Event} event - * @private + * Snap a date to a rounded value. + * The snap intervals are dependent on the current scale and step. + * @param {Date} date the date to be snapped. + * @return {Date} snappedDate */ - Range.prototype._onMouseWheel = function(event) { - // only allow zooming when configured as zoomable and moveable - if (!(this.options.zoomable && this.options.moveable)) return; + DataAxis.prototype.snap = function(date) { + return this.step.snap(date); + }; - // retrieve delta - var delta = 0; - if (event.wheelDelta) { /* IE/Opera. */ - delta = event.wheelDelta / 120; - } else if (event.detail) { /* Mozilla case. */ - // In Mozilla, sign of delta is different than in IE. - // Also, delta is multiple of 3. - delta = -event.detail / 3; - } + module.exports = DataAxis; - // If delta is nonzero, handle it. - // Basically, delta is now positive if wheel was scrolled up, - // and negative, if wheel was scrolled down. - if (delta) { - // perform the zoom action. Delta is normally 1 or -1 - // adjust a negative delta such that zooming in with delta 0.1 - // equals zooming out with a delta -0.1 - var scale; - if (delta < 0) { - scale = 1 - (delta / 5); - } - else { - scale = 1 / (1 + (delta / 5)) ; - } +/***/ }, +/* 29 */ +/***/ function(module, exports, __webpack_require__) { - // calculate center, the date to zoom around - var gesture = hammerUtil.fakeGesture(this, event), - pointer = getPointer(gesture.center, this.body.dom.center), - pointerDate = this._pointerToDate(pointer); + var util = __webpack_require__(1); + var DOMutil = __webpack_require__(2); + var Line = __webpack_require__(49); + var Bar = __webpack_require__(50); + var Points = __webpack_require__(51); - this.zoom(scale, pointerDate, delta); + /** + * /** + * @param {object} group | the object of the group from the dataset + * @param {string} groupId | ID of the group + * @param {object} options | the default options + * @param {array} groupsUsingDefaultStyles | this array has one entree. + * It is passed as an array so it is passed by reference. + * It enumerates through the default styles + * @constructor + */ + function GraphGroup (group, groupId, options, groupsUsingDefaultStyles) { + this.id = groupId; + var fields = ['sampling','style','sort','yAxisOrientation','barChart','drawPoints','shaded','catmullRom'] + this.options = util.selectiveBridgeObject(fields,options); + this.usingDefaultStyle = group.className === undefined; + this.groupsUsingDefaultStyles = groupsUsingDefaultStyles; + this.zeroPosition = 0; + this.update(group); + if (this.usingDefaultStyle == true) { + this.groupsUsingDefaultStyles[0] += 1; } + this.itemsData = []; + this.visible = group.visible === undefined ? true : group.visible; + } - // Prevent default actions caused by mouse wheel - // (else the page and timeline both zoom and scroll) - event.preventDefault(); - }; /** - * Start of a touch gesture - * @private + * this loads a reference to all items in this group into this group. + * @param {array} items */ - Range.prototype._onTouch = function (event) { - this.props.touch.start = this.start; - this.props.touch.end = this.end; - this.props.touch.allowDragging = true; - this.props.touch.center = null; - this.scaleOffset = 0; - this.deltaDifference = 0; + GraphGroup.prototype.setItems = function(items) { + if (items != null) { + this.itemsData = items; + if (this.options.sort == true) { + this.itemsData.sort(function (a,b) {return a.x - b.x;}) + } + } + else { + this.itemsData = []; + } }; + /** - * On start of a hold gesture - * @private + * this is used for plotting barcharts, this way, we only have to calculate it once. + * @param pos */ - Range.prototype._onHold = function () { - this.props.touch.allowDragging = false; + GraphGroup.prototype.setZeroPosition = function(pos) { + this.zeroPosition = pos; }; + /** - * Handle pinch event - * @param {Event} event - * @private + * set the options of the graph group over the default options. + * @param options */ - Range.prototype._onPinch = function (event) { - // only allow zooming when configured as zoomable and moveable - if (!(this.options.zoomable && this.options.moveable)) return; - - this.props.touch.allowDragging = false; + GraphGroup.prototype.setOptions = function(options) { + if (options !== undefined) { + var fields = ['sampling','style','sort','yAxisOrientation','barChart']; + util.selectiveDeepExtend(fields, this.options, options); - if (event.gesture.touches.length > 1) { - if (!this.props.touch.center) { - this.props.touch.center = getPointer(event.gesture.center, this.body.dom.center); - } - - var scale = 1 / (event.gesture.scale + this.scaleOffset); - var centerDate = this._pointerToDate(this.props.touch.center); - - var hiddenDuration = DateUtil.getHiddenDurationBetween(this.body.hiddenDates, this.start, this.end); - var hiddenDurationBefore = DateUtil.getHiddenDurationBefore(this.body.hiddenDates, this, centerDate); - var hiddenDurationAfter = hiddenDuration - hiddenDurationBefore; - - // calculate new start and end - var newStart = (centerDate - hiddenDurationBefore) + (this.props.touch.start - (centerDate - hiddenDurationBefore)) * scale; - var newEnd = (centerDate + hiddenDurationAfter) + (this.props.touch.end - (centerDate + hiddenDurationAfter)) * scale; - - // snapping times away from hidden zones - this.startToFront = 1 - scale > 0 ? false : true; // used to do the right autocorrection with periodic hidden times - this.endToFront = scale - 1 > 0 ? false : true; // used to do the right autocorrection with periodic hidden times + util.mergeOptions(this.options, options,'catmullRom'); + util.mergeOptions(this.options, options,'drawPoints'); + util.mergeOptions(this.options, options,'shaded'); - var safeStart = DateUtil.snapAwayFromHidden(this.body.hiddenDates, newStart, 1 - scale, true); - var safeEnd = DateUtil.snapAwayFromHidden(this.body.hiddenDates, newEnd, scale - 1, true); - if (safeStart != newStart || safeEnd != newEnd) { - this.props.touch.start = safeStart; - this.props.touch.end = safeEnd; - this.scaleOffset = 1 - event.gesture.scale; - newStart = safeStart; - newEnd = safeEnd; + if (options.catmullRom) { + if (typeof options.catmullRom == 'object') { + if (options.catmullRom.parametrization) { + if (options.catmullRom.parametrization == 'uniform') { + this.options.catmullRom.alpha = 0; + } + else if (options.catmullRom.parametrization == 'chordal') { + this.options.catmullRom.alpha = 1.0; + } + else { + this.options.catmullRom.parametrization = 'centripetal'; + this.options.catmullRom.alpha = 0.5; + } + } + } } - - this.setRange(newStart, newEnd, false, true); - - this.startToFront = false; // revert to default - this.endToFront = true; // revert to default } - }; - - /** - * Helper function to calculate the center date for zooming - * @param {{x: Number, y: Number}} pointer - * @return {number} date - * @private - */ - Range.prototype._pointerToDate = function (pointer) { - var conversion; - var direction = this.options.direction; - validateDirection(direction); - - if (direction == 'horizontal') { - return this.body.util.toTime(pointer.x).valueOf(); + if (this.options.style == 'line') { + this.type = new Line(this.id, this.options); } - else { - var height = this.body.domProps.center.height; - conversion = this.conversion(height); - return pointer.y / conversion.scale + conversion.offset; + else if (this.options.style == 'bar') { + this.type = new Bar(this.id, this.options); + } + else if (this.options.style == 'points') { + this.type = new Points(this.id, this.options); } }; + /** - * Get the pointer location relative to the location of the dom element - * @param {{pageX: Number, pageY: Number}} touch - * @param {Element} element HTML DOM element - * @return {{x: Number, y: Number}} pointer - * @private + * this updates the current group class with the latest group dataset entree, used in _updateGroup in linegraph + * @param group */ - function getPointer (touch, element) { - return { - x: touch.pageX - util.getAbsoluteLeft(element), - y: touch.pageY - util.getAbsoluteTop(element) - }; - } + GraphGroup.prototype.update = function(group) { + this.group = group; + this.content = group.content || 'graph'; + this.className = group.className || this.className || "graphGroup" + this.groupsUsingDefaultStyles[0] % 10; + this.visible = group.visible === undefined ? true : group.visible; + this.style = group.style; + this.setOptions(group.options); + }; + /** - * Zoom the range the given scale in or out. Start and end date will - * be adjusted, and the timeline will be redrawn. You can optionally give a - * date around which to zoom. - * For example, try scale = 0.9 or 1.1 - * @param {Number} scale Scaling factor. Values above 1 will zoom out, - * values below 1 will zoom in. - * @param {Number} [center] Value representing a date around which will - * be zoomed. + * draw the icon for the legend. + * + * @param x + * @param y + * @param JSONcontainer + * @param SVGcontainer + * @param iconWidth + * @param iconHeight */ - Range.prototype.zoom = function(scale, center, delta) { - // if centerDate is not provided, take it half between start Date and end Date - if (center == null) { - center = (this.start + this.end) / 2; - } + GraphGroup.prototype.drawIcon = function(x, y, JSONcontainer, SVGcontainer, iconWidth, iconHeight) { + var fillHeight = iconHeight * 0.5; + var path, fillPath; - var hiddenDuration = DateUtil.getHiddenDurationBetween(this.body.hiddenDates, this.start, this.end); - var hiddenDurationBefore = DateUtil.getHiddenDurationBefore(this.body.hiddenDates, this, center); - var hiddenDurationAfter = hiddenDuration - hiddenDurationBefore; + var outline = DOMutil.getSVGElement("rect", JSONcontainer, SVGcontainer); + outline.setAttributeNS(null, "x", x); + outline.setAttributeNS(null, "y", y - fillHeight); + outline.setAttributeNS(null, "width", iconWidth); + outline.setAttributeNS(null, "height", 2*fillHeight); + outline.setAttributeNS(null, "class", "outline"); - // calculate new start and end - var newStart = (center-hiddenDurationBefore) + (this.start - (center-hiddenDurationBefore)) * scale; - var newEnd = (center+hiddenDurationAfter) + (this.end - (center+hiddenDurationAfter)) * scale; + if (this.options.style == 'line') { + path = DOMutil.getSVGElement("path", JSONcontainer, SVGcontainer); + path.setAttributeNS(null, "class", this.className); + if(this.style !== undefined) { + path.setAttributeNS(null, "style", this.style); + } - // snapping times away from hidden zones - this.startToFront = delta > 0 ? false : true; // used to do the right autocorrection with periodic hidden times - this.endToFront = -delta > 0 ? false : true; // used to do the right autocorrection with periodic hidden times - var safeStart = DateUtil.snapAwayFromHidden(this.body.hiddenDates, newStart, delta, true); - var safeEnd = DateUtil.snapAwayFromHidden(this.body.hiddenDates, newEnd, -delta, true); - if (safeStart != newStart || safeEnd != newEnd) { - newStart = safeStart; - newEnd = safeEnd; + path.setAttributeNS(null, "d", "M" + x + ","+y+" L" + (x + iconWidth) + ","+y+""); + if (this.options.shaded.enabled == true) { + fillPath = DOMutil.getSVGElement("path", JSONcontainer, SVGcontainer); + if (this.options.shaded.orientation == 'top') { + fillPath.setAttributeNS(null, "d", "M"+x+", " + (y - fillHeight) + + "L"+x+","+y+" L"+ (x + iconWidth) + ","+y+" L"+ (x + iconWidth) + "," + (y - fillHeight)); + } + else { + fillPath.setAttributeNS(null, "d", "M"+x+","+y+" " + + "L"+x+"," + (y + fillHeight) + " " + + "L"+ (x + iconWidth) + "," + (y + fillHeight) + + "L"+ (x + iconWidth) + ","+y); + } + fillPath.setAttributeNS(null, "class", this.className + " iconFill"); + } + + if (this.options.drawPoints.enabled == true) { + DOMutil.drawPoint(x + 0.5 * iconWidth,y, this, JSONcontainer, SVGcontainer); + } } + else { + var barWidth = Math.round(0.3 * iconWidth); + var bar1Height = Math.round(0.4 * iconHeight); + var bar2Height = Math.round(0.75 * iconHeight); - this.setRange(newStart, newEnd, false, true); + var offset = Math.round((iconWidth - (2 * barWidth))/3); - this.startToFront = false; // revert to default - this.endToFront = true; // revert to default + DOMutil.drawBar(x + 0.5*barWidth + offset , y + fillHeight - bar1Height - 1, barWidth, bar1Height, this.className + ' bar', JSONcontainer, SVGcontainer); + DOMutil.drawBar(x + 1.5*barWidth + offset + 2, y + fillHeight - bar2Height - 1, barWidth, bar2Height, this.className + ' bar', JSONcontainer, SVGcontainer); + } }; - - /** - * Move the range with a given delta to the left or right. Start and end - * value will be adjusted. For example, try delta = 0.1 or -0.1 - * @param {Number} delta Moving amount. Positive value will move right, - * negative value will move left - */ - Range.prototype.move = function(delta) { - // zoom start Date and end Date relative to the centerDate - var diff = (this.end - this.start); - - // apply new values - var newStart = this.start + diff * delta; - var newEnd = this.end + diff * delta; - - // TODO: reckon with min and max range - - this.start = newStart; - this.end = newEnd; - }; - /** - * Move the range to a new center point - * @param {Number} moveTo New center point of the range + * return the legend entree for this group. + * + * @param iconWidth + * @param iconHeight + * @returns {{icon: HTMLElement, label: (group.content|*|string), orientation: (.options.yAxisOrientation|*)}} */ - Range.prototype.moveTo = function(moveTo) { - var center = (this.start + this.end) / 2; + GraphGroup.prototype.getLegend = function(iconWidth, iconHeight) { + var svg = document.createElementNS('http://www.w3.org/2000/svg',"svg"); + this.drawIcon(0,0.5*iconHeight,[],svg,iconWidth,iconHeight); + return {icon: svg, label: this.content, orientation:this.options.yAxisOrientation}; + } - var diff = center - moveTo; + GraphGroup.prototype.getYRange = function(groupData) { + return this.type.getYRange(groupData); + } - // calculate new start and end - var newStart = this.start - diff; - var newEnd = this.end - diff; + GraphGroup.prototype.draw = function(dataset, group, framework) { + this.type.draw(dataset, group, framework); + } - this.setRange(newStart, newEnd); - }; - module.exports = Range; + module.exports = GraphGroup; /***/ }, -/* 22 */ +/* 30 */ /***/ function(module, exports, __webpack_require__) { - var Hammer = __webpack_require__(19); + var util = __webpack_require__(1); + var stack = __webpack_require__(18); + var RangeItem = __webpack_require__(24); /** - * Fake a hammer.js gesture. Event can be a ScrollEvent or MouseMoveEvent - * @param {Element} element - * @param {Event} event + * @constructor Group + * @param {Number | String} groupId + * @param {Object} data + * @param {ItemSet} itemSet */ - exports.fakeGesture = function(element, event) { - var eventType = null; - - // for hammer.js 1.0.5 - // var gesture = Hammer.event.collectEventData(this, eventType, event); - - // for hammer.js 1.0.6+ - var touches = Hammer.event.getTouchList(event, eventType); - var gesture = Hammer.event.collectEventData(this, eventType, touches, event); - - // on IE in standards mode, no touches are recognized by hammer.js, - // resulting in NaN values for center.pageX and center.pageY - if (isNaN(gesture.center.pageX)) { - gesture.center.pageX = event.pageX; - } - if (isNaN(gesture.center.pageY)) { - gesture.center.pageY = event.pageY; - } + function Group (groupId, data, itemSet) { + this.groupId = groupId; + this.subgroups = {}; + this.subgroupIndex = 0; + this.subgroupOrderer = data && data.subgroupOrder; + this.itemSet = itemSet; - return gesture; - }; + this.dom = {}; + this.props = { + label: { + width: 0, + height: 0 + } + }; + this.className = null; + this.items = {}; // items filtered by groupId of this group + this.visibleItems = []; // items currently visible in window + this.orderedItems = { + byStart: [], + byEnd: [] + }; + this.checkRangedItems = false; // needed to refresh the ranged items if the window is programatically changed with NO overlap. + var me = this; + this.itemSet.body.emitter.on("checkRangedItems", function () { + me.checkRangedItems = true; + }) -/***/ }, -/* 23 */ -/***/ function(module, exports, __webpack_require__) { + this._create(); - /** - * Prototype for visual components - * @param {{dom: Object, domProps: Object, emitter: Emitter, range: Range}} [body] - * @param {Object} [options] - */ - function Component (body, options) { - this.options = null; - this.props = null; + this.setData(data); } /** - * Set options for the component. The new options will be merged into the - * current options. - * @param {Object} options + * Create DOM elements for the group + * @private */ - Component.prototype.setOptions = function(options) { - if (options) { - util.extend(this.options, options); - } - }; + Group.prototype._create = function() { + var label = document.createElement('div'); + label.className = 'vlabel'; + this.dom.label = label; - /** - * Repaint the component - * @return {boolean} Returns true if the component is resized - */ - Component.prototype.redraw = function() { - // should be implemented by the component - return false; - }; + var inner = document.createElement('div'); + inner.className = 'inner'; + label.appendChild(inner); + this.dom.inner = inner; - /** - * Destroy the component. Cleanup DOM and event listeners - */ - Component.prototype.destroy = function() { - // should be implemented by the component - }; + var foreground = document.createElement('div'); + foreground.className = 'group'; + foreground['timeline-group'] = this; + this.dom.foreground = foreground; - /** - * Test whether the component is resized since the last time _isResized() was - * called. - * @return {Boolean} Returns true if the component is resized - * @protected - */ - Component.prototype._isResized = function() { - var resized = (this.props._previousWidth !== this.props.width || - this.props._previousHeight !== this.props.height); + this.dom.background = document.createElement('div'); + this.dom.background.className = 'group'; - this.props._previousWidth = this.props.width; - this.props._previousHeight = this.props.height; + this.dom.axis = document.createElement('div'); + this.dom.axis.className = 'group'; - return resized; + // create a hidden marker to detect when the Timelines container is attached + // to the DOM, or the style of a parent of the Timeline is changed from + // display:none is changed to visible. + this.dom.marker = document.createElement('div'); + this.dom.marker.style.visibility = 'hidden'; // TODO: ask jos why this is not none? + this.dom.marker.innerHTML = '?'; + this.dom.background.appendChild(this.dom.marker); }; - module.exports = Component; + /** + * Set the group data for this group + * @param {Object} data Group data, can contain properties content and className + */ + Group.prototype.setData = function(data) { + // update contents + var content = data && data.content; + if (content instanceof Element) { + this.dom.inner.appendChild(content); + } + else if (content !== undefined && content !== null) { + this.dom.inner.innerHTML = content; + } + else { + this.dom.inner.innerHTML = this.groupId || ''; // groupId can be null + } + // update title + this.dom.label.title = data && data.title || ''; -/***/ }, -/* 24 */ -/***/ function(module, exports, __webpack_require__) { + if (!this.dom.inner.firstChild) { + util.addClassName(this.dom.inner, 'hidden'); + } + else { + util.removeClassName(this.dom.inner, 'hidden'); + } - /** - * Created by Alex on 10/3/2014. - */ - var moment = __webpack_require__(2); + // update className + var className = data && data.className || null; + if (className != this.className) { + if (this.className) { + util.removeClassName(this.dom.label, this.className); + util.removeClassName(this.dom.foreground, this.className); + util.removeClassName(this.dom.background, this.className); + util.removeClassName(this.dom.axis, this.className); + } + util.addClassName(this.dom.label, className); + util.addClassName(this.dom.foreground, className); + util.addClassName(this.dom.background, className); + util.addClassName(this.dom.axis, className); + this.className = className; + } + // update style + if (this.style) { + util.removeCssText(this.dom.label, this.style); + this.style = null; + } + if (data && data.style) { + util.addCssText(this.dom.label, data.style); + this.style = data.style; + } + }; /** - * used in Core to convert the options into a volatile variable - * - * @param Core + * Get the width of the group label + * @return {number} width */ - exports.convertHiddenOptions = function(body, hiddenDates) { - body.hiddenDates = []; - if (hiddenDates) { - if (Array.isArray(hiddenDates) == true) { - for (var i = 0; i < hiddenDates.length; i++) { - if (hiddenDates[i].repeat === undefined) { - var dateItem = {}; - dateItem.start = moment(hiddenDates[i].start).toDate().valueOf(); - dateItem.end = moment(hiddenDates[i].end).toDate().valueOf(); - body.hiddenDates.push(dateItem); - } - } - body.hiddenDates.sort(function (a, b) { - return a.start - b.start; - }); // sort by start time - } - } + Group.prototype.getLabelWidth = function() { + return this.props.label.width; }; /** - * create new entrees for the repeating hidden dates - * @param body - * @param hiddenDates + * Repaint this group + * @param {{start: number, end: number}} range + * @param {{item: {horizontal: number, vertical: number}, axis: number}} margin + * @param {boolean} [restack=false] Force restacking of all items + * @return {boolean} Returns true if the group is resized */ - exports.updateHiddenDates = function (body, hiddenDates) { - if (hiddenDates && body.domProps.centerContainer.width !== undefined) { - exports.convertHiddenOptions(body, hiddenDates); + Group.prototype.redraw = function(range, margin, restack) { + var resized = false; - var start = moment(body.range.start); - var end = moment(body.range.end); + this.visibleItems = this._updateVisibleItems(this.orderedItems, this.visibleItems, range); - var totalRange = (body.range.end - body.range.start); - var pixelTime = totalRange / body.domProps.centerContainer.width; + // force recalculation of the height of the items when the marker height changed + // (due to the Timeline being attached to the DOM or changed from display:none to visible) + var markerHeight = this.dom.marker.clientHeight; + if (markerHeight != this.lastMarkerHeight) { + this.lastMarkerHeight = markerHeight; - for (var i = 0; i < hiddenDates.length; i++) { - if (hiddenDates[i].repeat !== undefined) { - var startDate = moment(hiddenDates[i].start); - var endDate = moment(hiddenDates[i].end); + util.forEach(this.items, function (item) { + item.dirty = true; + if (item.displayed) item.redraw(); + }); - if (startDate._d == "Invalid Date") { - throw new Error("Supplied start date is not valid: " + hiddenDates[i].start); - } - if (endDate._d == "Invalid Date") { - throw new Error("Supplied end date is not valid: " + hiddenDates[i].end); - } + restack = true; + } - var duration = endDate - startDate; - if (duration >= 4 * pixelTime) { + // reposition visible items vertically + if (this.itemSet.options.stack) { // TODO: ugly way to access options... + stack.stack(this.visibleItems, margin, restack); + } + else { // no stacking + stack.nostack(this.visibleItems, margin, this.subgroups); + } - var offset = 0; - var runUntil = end.clone(); - switch (hiddenDates[i].repeat) { - case "daily": // case of time - if (startDate.day() != endDate.day()) { - offset = 1; - } - startDate.dayOfYear(start.dayOfYear()); - startDate.year(start.year()); - startDate.subtract(7,'days'); + // recalculate the height of the group + var height = this._calculateHeight(margin); - endDate.dayOfYear(start.dayOfYear()); - endDate.year(start.year()); - endDate.subtract(7 - offset,'days'); + // calculate actual size and position + var foreground = this.dom.foreground; + this.top = foreground.offsetTop; + this.left = foreground.offsetLeft; + this.width = foreground.offsetWidth; + resized = util.updateProperty(this, 'height', height) || resized; - runUntil.add(1, 'weeks'); - break; - case "weekly": - var dayOffset = endDate.diff(startDate,'days') - var day = startDate.day(); + // recalculate size of label + resized = util.updateProperty(this.props.label, 'width', this.dom.inner.clientWidth) || resized; + resized = util.updateProperty(this.props.label, 'height', this.dom.inner.clientHeight) || resized; - // set the start date to the range.start - startDate.date(start.date()); - startDate.month(start.month()); - startDate.year(start.year()); - endDate = startDate.clone(); - - // force - startDate.day(day); - endDate.day(day); - endDate.add(dayOffset,'days'); - - startDate.subtract(1,'weeks'); - endDate.subtract(1,'weeks'); - - runUntil.add(1, 'weeks'); - break - case "monthly": - if (startDate.month() != endDate.month()) { - offset = 1; - } - startDate.month(start.month()); - startDate.year(start.year()); - startDate.subtract(1,'months'); - - endDate.month(start.month()); - endDate.year(start.year()); - endDate.subtract(1,'months'); - endDate.add(offset,'months'); - - runUntil.add(1, 'months'); - break; - case "yearly": - if (startDate.year() != endDate.year()) { - offset = 1; - } - startDate.year(start.year()); - startDate.subtract(1,'years'); - endDate.year(start.year()); - endDate.subtract(1,'years'); - endDate.add(offset,'years'); + // apply new height + this.dom.background.style.height = height + 'px'; + this.dom.foreground.style.height = height + 'px'; + this.dom.label.style.height = height + 'px'; - runUntil.add(1, 'years'); - break; - default: - console.log("Wrong repeat format, allowed are: daily, weekly, monthly, yearly. Given:", hiddenDates[i].repeat); - return; - } - while (startDate < runUntil) { - body.hiddenDates.push({start: startDate.valueOf(), end: endDate.valueOf()}); - switch (hiddenDates[i].repeat) { - case "daily": - startDate.add(1, 'days'); - endDate.add(1, 'days'); - break; - case "weekly": - startDate.add(1, 'weeks'); - endDate.add(1, 'weeks'); - break - case "monthly": - startDate.add(1, 'months'); - endDate.add(1, 'months'); - break; - case "yearly": - startDate.add(1, 'y'); - endDate.add(1, 'y'); - break; - default: - console.log("Wrong repeat format, allowed are: daily, weekly, monthly, yearly. Given:", hiddenDates[i].repeat); - return; - } - } - body.hiddenDates.push({start: startDate.valueOf(), end: endDate.valueOf()}); - } - } - } - // remove duplicates, merge where possible - exports.removeDuplicates(body); - // ensure the new positions are not on hidden dates - var startHidden = exports.isHidden(body.range.start, body.hiddenDates); - var endHidden = exports.isHidden(body.range.end,body.hiddenDates); - var rangeStart = body.range.start; - var rangeEnd = body.range.end; - if (startHidden.hidden == true) {rangeStart = body.range.startToFront == true ? startHidden.startDate - 1 : startHidden.endDate + 1;} - if (endHidden.hidden == true) {rangeEnd = body.range.endToFront == true ? endHidden.startDate - 1 : endHidden.endDate + 1;} - if (startHidden.hidden == true || endHidden.hidden == true) { - body.range._applyRange(rangeStart, rangeEnd); - } + // update vertical position of items after they are re-stacked and the height of the group is calculated + for (var i = 0, ii = this.visibleItems.length; i < ii; i++) { + var item = this.visibleItems[i]; + item.repositionY(margin); } - } - + return resized; + }; /** - * remove duplicates from the hidden dates list. Duplicates are evil. They mess everything up. - * Scales with N^2 - * @param body + * recalculate the height of the group + * @param {{item: {horizontal: number, vertical: number}, axis: number}} margin + * @returns {number} Returns the height + * @private */ - exports.removeDuplicates = function(body) { - var hiddenDates = body.hiddenDates; - var safeDates = []; - for (var i = 0; i < hiddenDates.length; i++) { - for (var j = 0; j < hiddenDates.length; j++) { - if (i != j && hiddenDates[j].remove != true && hiddenDates[i].remove != true) { - // j inside i - if (hiddenDates[j].start >= hiddenDates[i].start && hiddenDates[j].end <= hiddenDates[i].end) { - hiddenDates[j].remove = true; - } - // j start inside i - else if (hiddenDates[j].start >= hiddenDates[i].start && hiddenDates[j].start <= hiddenDates[i].end) { - hiddenDates[i].end = hiddenDates[j].end; - hiddenDates[j].remove = true; - } - // j end inside i - else if (hiddenDates[j].end >= hiddenDates[i].start && hiddenDates[j].end <= hiddenDates[i].end) { - hiddenDates[i].start = hiddenDates[j].start; - hiddenDates[j].remove = true; - } + Group.prototype._calculateHeight = function (margin) { + // recalculate the height of the group + var height; + var visibleItems = this.visibleItems; + //var visibleSubgroups = []; + //this.visibleSubgroups = 0; + this.resetSubgroups(); + var me = this; + if (visibleItems.length) { + var min = visibleItems[0].top; + var max = visibleItems[0].top + visibleItems[0].height; + util.forEach(visibleItems, function (item) { + min = Math.min(min, item.top); + max = Math.max(max, (item.top + item.height)); + if (item.data.subgroup !== undefined) { + me.subgroups[item.data.subgroup].height = Math.max(me.subgroups[item.data.subgroup].height,item.height); + me.subgroups[item.data.subgroup].visible = true; + //if (visibleSubgroups.indexOf(item.data.subgroup) == -1){ + // visibleSubgroups.push(item.data.subgroup); + // me.visibleSubgroups += 1; + //} } + }); + if (min > margin.axis) { + // there is an empty gap between the lowest item and the axis + var offset = min - margin.axis; + max -= offset; + util.forEach(visibleItems, function (item) { + item.top -= offset; + }); } + height = max + margin.item.vertical / 2; } - - for (var i = 0; i < hiddenDates.length; i++) { - if (hiddenDates[i].remove !== true) { - safeDates.push(hiddenDates[i]); - } + else { + height = margin.axis + margin.item.vertical; } + height = Math.max(height, this.props.label.height); - body.hiddenDates = safeDates; - body.hiddenDates.sort(function (a, b) { - return a.start - b.start; - }); // sort by start time - } - - exports.printDates = function(dates) { - for (var i =0; i < dates.length; i++) { - console.log(i, new Date(dates[i].start),new Date(dates[i].end), dates[i].start, dates[i].end, dates[i].remove); - } - } + return height; + }; /** - * Used in TimeStep to avoid the hidden times. - * @param timeStep - * @param previousTime + * Show this group: attach to the DOM */ - exports.stepOverHiddenDates = function(timeStep, previousTime) { - var stepInHidden = false; - var currentValue = timeStep.current.valueOf(); - for (var i = 0; i < timeStep.hiddenDates.length; i++) { - var startDate = timeStep.hiddenDates[i].start; - var endDate = timeStep.hiddenDates[i].end; - if (currentValue >= startDate && currentValue < endDate) { - stepInHidden = true; - break; - } + Group.prototype.show = function() { + if (!this.dom.label.parentNode) { + this.itemSet.dom.labelSet.appendChild(this.dom.label); } - if (stepInHidden == true && currentValue < timeStep._end.valueOf() && currentValue != previousTime) { - var prevValue = moment(previousTime); - var newValue = moment(endDate); - //check if the next step should be major - if (prevValue.year() != newValue.year()) {timeStep.switchedYear = true;} - else if (prevValue.month() != newValue.month()) {timeStep.switchedMonth = true;} - else if (prevValue.dayOfYear() != newValue.dayOfYear()) {timeStep.switchedDay = true;} - - timeStep.current = newValue.toDate(); + if (!this.dom.foreground.parentNode) { + this.itemSet.dom.foreground.appendChild(this.dom.foreground); } - }; + if (!this.dom.background.parentNode) { + this.itemSet.dom.background.appendChild(this.dom.background); + } - ///** - // * Used in TimeStep to avoid the hidden times. - // * @param timeStep - // * @param previousTime - // */ - //exports.checkFirstStep = function(timeStep) { - // var stepInHidden = false; - // var currentValue = timeStep.current.valueOf(); - // for (var i = 0; i < timeStep.hiddenDates.length; i++) { - // var startDate = timeStep.hiddenDates[i].start; - // var endDate = timeStep.hiddenDates[i].end; - // if (currentValue >= startDate && currentValue < endDate) { - // stepInHidden = true; - // break; - // } - // } - // - // if (stepInHidden == true && currentValue <= timeStep._end.valueOf()) { - // var newValue = moment(endDate); - // timeStep.current = newValue.toDate(); - // } - //}; + if (!this.dom.axis.parentNode) { + this.itemSet.dom.axis.appendChild(this.dom.axis); + } + }; /** - * replaces the Core toScreen methods - * @param Core - * @param time - * @param width - * @returns {number} + * Hide this group: remove from the DOM */ - exports.toScreen = function(Core, time, width) { - if (Core.body.hiddenDates.length == 0) { - var conversion = Core.range.conversion(width); - return (time.valueOf() - conversion.offset) * conversion.scale; + Group.prototype.hide = function() { + var label = this.dom.label; + if (label.parentNode) { + label.parentNode.removeChild(label); } - else { - var hidden = exports.isHidden(time, Core.body.hiddenDates) - if (hidden.hidden == true) { - time = hidden.startDate; - } - var duration = exports.getHiddenDurationBetween(Core.body.hiddenDates, Core.range.start, Core.range.end); - time = exports.correctTimeForHidden(Core.body.hiddenDates, Core.range, time); + var foreground = this.dom.foreground; + if (foreground.parentNode) { + foreground.parentNode.removeChild(foreground); + } - var conversion = Core.range.conversion(width, duration); - return (time.valueOf() - conversion.offset) * conversion.scale; + var background = this.dom.background; + if (background.parentNode) { + background.parentNode.removeChild(background); } - }; + var axis = this.dom.axis; + if (axis.parentNode) { + axis.parentNode.removeChild(axis); + } + }; /** - * Replaces the core toTime methods - * @param body - * @param range - * @param x - * @param width - * @returns {Date} + * Add an item to the group + * @param {Item} item */ - exports.toTime = function(Core, x, width) { - if (Core.body.hiddenDates.length == 0) { - var conversion = Core.range.conversion(width); - return new Date(x / conversion.scale + conversion.offset); + Group.prototype.add = function(item) { + this.items[item.id] = item; + item.setParent(this); + + // add to + if (item.data.subgroup !== undefined) { + if (this.subgroups[item.data.subgroup] === undefined) { + this.subgroups[item.data.subgroup] = {height:0, visible: false, index:this.subgroupIndex, items: []}; + this.subgroupIndex++; + } + this.subgroups[item.data.subgroup].items.push(item); } - else { - var hiddenDuration = exports.getHiddenDurationBetween(Core.body.hiddenDates, Core.range.start, Core.range.end); - var totalDuration = Core.range.end - Core.range.start - hiddenDuration; - var partialDuration = totalDuration * x / width; - var accumulatedHiddenDuration = exports.getAccumulatedHiddenDuration(Core.body.hiddenDates, Core.range, partialDuration); + this.orderSubgroups(); - var newTime = new Date(accumulatedHiddenDuration + partialDuration + Core.range.start); - return newTime; + if (this.visibleItems.indexOf(item) == -1) { + var range = this.itemSet.body.range; // TODO: not nice accessing the range like this + this._checkIfVisible(item, this.visibleItems, range); } }; + Group.prototype.orderSubgroups = function() { + if (this.subgroupOrderer !== undefined) { + var sortArray = []; + if (typeof this.subgroupOrderer == 'string') { + for (var subgroup in this.subgroups) { + sortArray.push({subgroup: subgroup, sortField: this.subgroups[subgroup].items[0].data[this.subgroupOrderer]}) + } + sortArray.sort(function (a, b) { + return a.sortField - b.sortField; + }) + } + else if (typeof this.subgroupOrderer == 'function') { + for (var subgroup in this.subgroups) { + sortArray.push(this.subgroups[subgroup].items[0].data); + } + sortArray.sort(this.subgroupOrderer); + } - /** - * Support function - * - * @param hiddenDates - * @param range - * @returns {number} - */ - exports.getHiddenDurationBetween = function(hiddenDates, start, end) { - var duration = 0; - for (var i = 0; i < hiddenDates.length; i++) { - var startDate = hiddenDates[i].start; - var endDate = hiddenDates[i].end; - // if time after the cutout, and the - if (startDate >= start && endDate < end) { - duration += endDate - startDate; + if (sortArray.length > 0) { + for (var i = 0; i < sortArray.length; i++) { + this.subgroups[sortArray[i].subgroup].index = i; + } } } - return duration; }; + Group.prototype.resetSubgroups = function() { + for (var subgroup in this.subgroups) { + if (this.subgroups.hasOwnProperty(subgroup)) { + this.subgroups[subgroup].visible = false; + } + } + }; /** - * Support function - * @param hiddenDates - * @param range - * @param time - * @returns {{duration: number, time: *, offset: number}} + * Remove an item from the group + * @param {Item} item */ - exports.correctTimeForHidden = function(hiddenDates, range, time) { - time = moment(time).toDate().valueOf(); - time -= exports.getHiddenDurationBefore(hiddenDates,range,time); - return time; - }; + Group.prototype.remove = function(item) { + delete this.items[item.id]; + item.setParent(null); - exports.getHiddenDurationBefore = function(hiddenDates, range, time) { - var timeOffset = 0; - time = moment(time).toDate().valueOf(); + // remove from visible items + var index = this.visibleItems.indexOf(item); + if (index != -1) this.visibleItems.splice(index, 1); + + // TODO: also remove from ordered items? + }; - for (var i = 0; i < hiddenDates.length; i++) { - var startDate = hiddenDates[i].start; - var endDate = hiddenDates[i].end; - // if time after the cutout, and the - if (startDate >= range.start && endDate < range.end) { - if (time >= endDate) { - timeOffset += (endDate - startDate); - } - } - } - return timeOffset; - } /** - * sum the duration from start to finish, including the hidden duration, - * until the required amount has been reached, return the accumulated hidden duration - * @param hiddenDates - * @param range - * @param time - * @returns {{duration: number, time: *, offset: number}} + * Remove an item from the corresponding DataSet + * @param {Item} item */ - exports.getAccumulatedHiddenDuration = function(hiddenDates, range, requiredDuration) { - var hiddenDuration = 0; - var duration = 0; - var previousPoint = range.start; - //exports.printDates(hiddenDates) - for (var i = 0; i < hiddenDates.length; i++) { - var startDate = hiddenDates[i].start; - var endDate = hiddenDates[i].end; - // if time after the cutout, and the - if (startDate >= range.start && endDate < range.end) { - duration += startDate - previousPoint; - previousPoint = endDate; - if (duration >= requiredDuration) { - break; - } - else { - hiddenDuration += endDate - startDate; - } - } - } - - return hiddenDuration; + Group.prototype.removeFromDataSet = function(item) { + this.itemSet.removeItem(item.id); }; - /** - * used to step over to either side of a hidden block. Correction is disabled on tablets, might be set to true - * @param hiddenDates - * @param time - * @param direction - * @param correctionEnabled - * @returns {*} + * Reorder the items */ - exports.snapAwayFromHidden = function(hiddenDates, time, direction, correctionEnabled) { - var isHidden = exports.isHidden(time, hiddenDates); - if (isHidden.hidden == true) { - if (direction < 0) { - if (correctionEnabled == true) { - return isHidden.startDate - (isHidden.endDate - time) - 1; - } - else { - return isHidden.startDate - 1; - } - } - else { - if (correctionEnabled == true) { - return isHidden.endDate + (time - isHidden.startDate) + 1; - } - else { - return isHidden.endDate + 1; - } + Group.prototype.order = function() { + var array = util.toArray(this.items); + var startArray = []; + var endArray = []; + + for (var i = 0; i < array.length; i++) { + if (array[i].data.end !== undefined) { + endArray.push(array[i]); } + startArray.push(array[i]); } - else { - return time; - } + this.orderedItems = { + byStart: startArray, + byEnd: endArray + }; - } + stack.orderByStart(this.orderedItems.byStart); + stack.orderByEnd(this.orderedItems.byEnd); + }; /** - * Check if a time is hidden - * - * @param time - * @param hiddenDates - * @returns {{hidden: boolean, startDate: Window.start, endDate: *}} + * Update the visible items + * @param {{byStart: Item[], byEnd: Item[]}} orderedItems All items ordered by start date and by end date + * @param {Item[]} visibleItems The previously visible items. + * @param {{start: number, end: number}} range Visible range + * @return {Item[]} visibleItems The new visible items. + * @private */ - exports.isHidden = function(time, hiddenDates) { - for (var i = 0; i < hiddenDates.length; i++) { - var startDate = hiddenDates[i].start; - var endDate = hiddenDates[i].end; + Group.prototype._updateVisibleItems = function(orderedItems, oldVisibleItems, range) { + var visibleItems = []; + var visibleItemsLookup = {}; // we keep this to quickly look up if an item already exists in the list without using indexOf on visibleItems + var interval = (range.end - range.start) / 4; + var lowerBound = range.start - interval; + var upperBound = range.end + interval; + var item, i; - if (time >= startDate && time < endDate) { // if the start is entering a hidden zone - return {hidden: true, startDate: startDate, endDate: endDate}; - break; + // this function is used to do the binary search. + var searchFunction = function (value) { + if (value < lowerBound) {return -1;} + else if (value <= upperBound) {return 0;} + else {return 1;} + } + + // first check if the items that were in view previously are still in view. + // IMPORTANT: this handles the case for the items with startdate before the window and enddate after the window! + // also cleans up invisible items. + if (oldVisibleItems.length > 0) { + for (i = 0; i < oldVisibleItems.length; i++) { + this._checkIfVisibleWithReference(oldVisibleItems[i], visibleItems, visibleItemsLookup, range); } } - return {hidden: false, startDate: startDate, endDate: endDate}; - } -/***/ }, -/* 25 */ -/***/ function(module, exports, __webpack_require__) { + // we do a binary search for the items that have only start values. + var initialPosByStart = util.binarySearchCustom(orderedItems.byStart, searchFunction, 'data','start'); - var Emitter = __webpack_require__(11); - var Hammer = __webpack_require__(19); - var util = __webpack_require__(1); - var DataSet = __webpack_require__(7); - var DataView = __webpack_require__(9); - var Range = __webpack_require__(21); - var ItemSet = __webpack_require__(26); - var Activator = __webpack_require__(35); - var DateUtil = __webpack_require__(24); + // trace the visible items from the inital start pos both ways until an invisible item is found, we only look at the start values. + this._traceVisible(initialPosByStart, orderedItems.byStart, visibleItems, visibleItemsLookup, function (item) { + return (item.data.start < lowerBound || item.data.start > upperBound); + }); - /** - * Create a timeline visualization - * @param {HTMLElement} container - * @param {vis.DataSet | Array | google.visualization.DataTable} [items] - * @param {Object} [options] See Core.setOptions for the available options. - * @constructor - */ - function Core () {} - - // turn Core into an event emitter - Emitter(Core.prototype); - - /** - * Create the main DOM for the Core: a root panel containing left, right, - * top, bottom, content, and background panel. - * @param {Element} container The container element where the Core will - * be attached. - * @private - */ - Core.prototype._create = function (container) { - this.dom = {}; - - this.dom.root = document.createElement('div'); - this.dom.background = document.createElement('div'); - this.dom.backgroundVertical = document.createElement('div'); - this.dom.backgroundHorizontal = document.createElement('div'); - this.dom.centerContainer = document.createElement('div'); - this.dom.leftContainer = document.createElement('div'); - this.dom.rightContainer = document.createElement('div'); - this.dom.center = document.createElement('div'); - this.dom.left = document.createElement('div'); - this.dom.right = document.createElement('div'); - this.dom.top = document.createElement('div'); - this.dom.bottom = document.createElement('div'); - this.dom.shadowTop = document.createElement('div'); - this.dom.shadowBottom = document.createElement('div'); - this.dom.shadowTopLeft = document.createElement('div'); - this.dom.shadowBottomLeft = document.createElement('div'); - this.dom.shadowTopRight = document.createElement('div'); - this.dom.shadowBottomRight = document.createElement('div'); - - this.dom.root.className = 'vis timeline root'; - this.dom.background.className = 'vispanel background'; - this.dom.backgroundVertical.className = 'vispanel background vertical'; - this.dom.backgroundHorizontal.className = 'vispanel background horizontal'; - this.dom.centerContainer.className = 'vispanel center'; - this.dom.leftContainer.className = 'vispanel left'; - this.dom.rightContainer.className = 'vispanel right'; - this.dom.top.className = 'vispanel top'; - this.dom.bottom.className = 'vispanel bottom'; - this.dom.left.className = 'content'; - this.dom.center.className = 'content'; - this.dom.right.className = 'content'; - this.dom.shadowTop.className = 'shadow top'; - this.dom.shadowBottom.className = 'shadow bottom'; - this.dom.shadowTopLeft.className = 'shadow top'; - this.dom.shadowBottomLeft.className = 'shadow bottom'; - this.dom.shadowTopRight.className = 'shadow top'; - this.dom.shadowBottomRight.className = 'shadow bottom'; - - this.dom.root.appendChild(this.dom.background); - this.dom.root.appendChild(this.dom.backgroundVertical); - this.dom.root.appendChild(this.dom.backgroundHorizontal); - this.dom.root.appendChild(this.dom.centerContainer); - this.dom.root.appendChild(this.dom.leftContainer); - this.dom.root.appendChild(this.dom.rightContainer); - this.dom.root.appendChild(this.dom.top); - this.dom.root.appendChild(this.dom.bottom); - - this.dom.centerContainer.appendChild(this.dom.center); - this.dom.leftContainer.appendChild(this.dom.left); - this.dom.rightContainer.appendChild(this.dom.right); - - this.dom.centerContainer.appendChild(this.dom.shadowTop); - this.dom.centerContainer.appendChild(this.dom.shadowBottom); - this.dom.leftContainer.appendChild(this.dom.shadowTopLeft); - this.dom.leftContainer.appendChild(this.dom.shadowBottomLeft); - this.dom.rightContainer.appendChild(this.dom.shadowTopRight); - this.dom.rightContainer.appendChild(this.dom.shadowBottomRight); - - this.on('rangechange', this.redraw.bind(this)); - this.on('touch', this._onTouch.bind(this)); - this.on('pinch', this._onPinch.bind(this)); - this.on('dragstart', this._onDragStart.bind(this)); - this.on('drag', this._onDrag.bind(this)); - - var me = this; - this.on('change', function (properties) { - if (properties && properties.queue == true) { - // redraw once on next tick - if (!me._redrawTimer) { - me._redrawTimer = setTimeout(function () { - me._redrawTimer = null; - me.redraw(); - }, 0) - } - } - else { - // redraw immediately - me.redraw(); + // if the window has changed programmatically without overlapping the old window, the ranged items with start < lowerBound and end > upperbound are not shown. + // We therefore have to brute force check all items in the byEnd list + if (this.checkRangedItems == true) { + this.checkRangedItems = false; + for (i = 0; i < orderedItems.byEnd.length; i++) { + this._checkIfVisibleWithReference(orderedItems.byEnd[i], visibleItems, visibleItemsLookup, range); } - }); + } + else { + // we do a binary search for the items that have defined end times. + var initialPosByEnd = util.binarySearchCustom(orderedItems.byEnd, searchFunction, 'data','end'); - // create event listeners for all interesting events, these events will be - // emitted via emitter - this.hammer = Hammer(this.dom.root, { - preventDefault: true - }); - this.listeners = {}; + // trace the visible items from the inital start pos both ways until an invisible item is found, we only look at the end values. + this._traceVisible(initialPosByEnd, orderedItems.byEnd, visibleItems, visibleItemsLookup, function (item) { + return (item.data.end < lowerBound || item.data.end > upperBound); + }); + } - var events = [ - 'touch', 'pinch', - 'tap', 'doubletap', 'hold', - 'dragstart', 'drag', 'dragend', - 'mousewheel', 'DOMMouseScroll' // DOMMouseScroll is needed for Firefox - ]; - events.forEach(function (event) { - var listener = function () { - var args = [event].concat(Array.prototype.slice.call(arguments, 0)); - if (me.isActive()) { - me.emit.apply(me, args); - } - }; - me.hammer.on(event, listener); - me.listeners[event] = listener; - }); - // size properties of each of the panels - this.props = { - root: {}, - background: {}, - centerContainer: {}, - leftContainer: {}, - rightContainer: {}, - center: {}, - left: {}, - right: {}, - top: {}, - bottom: {}, - border: {}, - scrollTop: 0, - scrollTopMin: 0 - }; - this.touch = {}; // store state information needed for touch events + // finally, we reposition all the visible items. + for (i = 0; i < visibleItems.length; i++) { + item = visibleItems[i]; + if (!item.displayed) item.show(); + // reposition item horizontally + item.repositionX(); + } - this.redrawCount = 0; + // debug + //console.log("new line") + //if (this.groupId == null) { + // for (i = 0; i < orderedItems.byStart.length; i++) { + // item = orderedItems.byStart[i].data; + // console.log('start',i,initialPosByStart, item.start.valueOf(), item.content, item.start >= lowerBound && item.start <= upperBound,i == initialPosByStart ? "<------------------- HEREEEE" : "") + // } + // for (i = 0; i < orderedItems.byEnd.length; i++) { + // item = orderedItems.byEnd[i].data; + // console.log('rangeEnd',i,initialPosByEnd, item.end.valueOf(), item.content, item.end >= range.start && item.end <= range.end,i == initialPosByEnd ? "<------------------- HEREEEE" : "") + // } + //} - // attach the root panel to the provided container - if (!container) throw new Error('No container provided'); - container.appendChild(this.dom.root); + return visibleItems; }; - /** - * Set options. Options will be passed to all components loaded in the Timeline. - * @param {Object} [options] - * {String} orientation - * Vertical orientation for the Timeline, - * can be 'bottom' (default) or 'top'. - * {String | Number} width - * Width for the timeline, a number in pixels or - * a css string like '1000px' or '75%'. '100%' by default. - * {String | Number} height - * Fixed height for the Timeline, a number in pixels or - * a css string like '400px' or '75%'. If undefined, - * The Timeline will automatically size such that - * its contents fit. - * {String | Number} minHeight - * Minimum height for the Timeline, a number in pixels or - * a css string like '400px' or '75%'. - * {String | Number} maxHeight - * Maximum height for the Timeline, a number in pixels or - * a css string like '400px' or '75%'. - * {Number | Date | String} start - * Start date for the visible window - * {Number | Date | String} end - * End date for the visible window - */ - Core.prototype.setOptions = function (options) { - if (options) { - // copy the known options - var fields = ['width', 'height', 'minHeight', 'maxHeight', 'autoResize', 'start', 'end', 'orientation', 'clickToUse', 'dataAttributes', 'hiddenDates']; - util.selectiveExtend(fields, this.options, options); + Group.prototype._traceVisible = function (initialPos, items, visibleItems, visibleItemsLookup, breakCondition) { + var item; + var i; - if ('hiddenDates' in this.options) { - DateUtil.convertHiddenOptions(this.body, this.options.hiddenDates); + if (initialPos != -1) { + for (i = initialPos; i >= 0; i--) { + item = items[i]; + if (breakCondition(item)) { + break; + } + else { + if (visibleItemsLookup[item.id] === undefined) { + visibleItemsLookup[item.id] = true; + visibleItems.push(item); + } + } } - if ('clickToUse' in options) { - if (options.clickToUse) { - if (!this.activator) { - this.activator = new Activator(this.dom.root); - } + for (i = initialPos + 1; i < items.length; i++) { + item = items[i]; + if (breakCondition(item)) { + break; } else { - if (this.activator) { - this.activator.destroy(); - delete this.activator; + if (visibleItemsLookup[item.id] === undefined) { + visibleItemsLookup[item.id] = true; + visibleItems.push(item); } } } - - // enable/disable autoResize - this._initAutoResize(); - } - - // propagate options to all components - this.components.forEach(function (component) { - component.setOptions(options); - }); - - // TODO: remove deprecation error one day (deprecated since version 0.8.0) - if (options && options.order) { - throw new Error('Option order is deprecated. There is no replacement for this feature.'); } + } - // redraw everything - this.redraw(); - }; /** - * Returns true when the Timeline is active. - * @returns {boolean} + * this function is very similar to the _checkIfInvisible() but it does not + * return booleans, hides the item if it should not be seen and always adds to + * the visibleItems. + * this one is for brute forcing and hiding. + * + * @param {Item} item + * @param {Array} visibleItems + * @param {{start:number, end:number}} range + * @private */ - Core.prototype.isActive = function () { - return !this.activator || this.activator.active; + Group.prototype._checkIfVisible = function(item, visibleItems, range) { + if (item.isVisible(range)) { + if (!item.displayed) item.show(); + // reposition item horizontally + item.repositionX(); + visibleItems.push(item); + } + else { + if (item.displayed) item.hide(); + } }; + /** - * Destroy the Core, clean up all DOM elements and event listeners. + * this function is very similar to the _checkIfInvisible() but it does not + * return booleans, hides the item if it should not be seen and always adds to + * the visibleItems. + * this one is for brute forcing and hiding. + * + * @param {Item} item + * @param {Array} visibleItems + * @param {{start:number, end:number}} range + * @private */ - Core.prototype.destroy = function () { - // unbind datasets - this.clear(); - - // remove all event listeners - this.off(); - - // stop checking for changed size - this._stopAutoResize(); - - // remove from DOM - if (this.dom.root.parentNode) { - this.dom.root.parentNode.removeChild(this.dom.root); + Group.prototype._checkIfVisibleWithReference = function(item, visibleItems, visibleItemsLookup, range) { + if (item.isVisible(range)) { + if (visibleItemsLookup[item.id] === undefined) { + visibleItemsLookup[item.id] = true; + visibleItems.push(item); + } } - this.dom = null; - - // remove Activator - if (this.activator) { - this.activator.destroy(); - delete this.activator; + else { + if (item.displayed) item.hide(); } + }; - // cleanup hammer touch events - for (var event in this.listeners) { - if (this.listeners.hasOwnProperty(event)) { - delete this.listeners[event]; - } - } - this.listeners = null; - this.hammer = null; - // give all components the opportunity to cleanup - this.components.forEach(function (component) { - component.destroy(); - }); - this.body = null; - }; + module.exports = Group; - /** - * Set a custom time bar - * @param {Date} time - */ - Core.prototype.setCustomTime = function (time) { - if (!this.customTime) { - throw new Error('Cannot get custom time: Custom time bar is not enabled'); - } +/***/ }, +/* 31 */ +/***/ function(module, exports, __webpack_require__) { - this.customTime.setCustomTime(time); - }; + var util = __webpack_require__(1); + var Group = __webpack_require__(30); /** - * Retrieve the current custom time. - * @return {Date} customTime + * @constructor BackgroundGroup + * @param {Number | String} groupId + * @param {Object} data + * @param {ItemSet} itemSet */ - Core.prototype.getCustomTime = function() { - if (!this.customTime) { - throw new Error('Cannot get custom time: Custom time bar is not enabled'); - } + function BackgroundGroup (groupId, data, itemSet) { + Group.call(this, groupId, data, itemSet); - return this.customTime.getCustomTime(); - }; + this.width = 0; + this.height = 0; + this.top = 0; + this.left = 0; + } + BackgroundGroup.prototype = Object.create(Group.prototype); /** - * Get the id's of the currently visible items. - * @returns {Array} The ids of the visible items + * Repaint this group + * @param {{start: number, end: number}} range + * @param {{item: {horizontal: number, vertical: number}, axis: number}} margin + * @param {boolean} [restack=false] Force restacking of all items + * @return {boolean} Returns true if the group is resized */ - Core.prototype.getVisibleItems = function() { - return this.itemSet && this.itemSet.getVisibleItems() || []; - }; + BackgroundGroup.prototype.redraw = function(range, margin, restack) { + var resized = false; + this.visibleItems = this._updateVisibleItems(this.orderedItems, this.visibleItems, range); + // calculate actual size + this.width = this.dom.background.offsetWidth; - /** - * Clear the Core. By Default, items, groups and options are cleared. - * Example usage: - * - * timeline.clear(); // clear items, groups, and options - * timeline.clear({options: true}); // clear options only - * - * @param {Object} [what] Optionally specify what to clear. By default: - * {items: true, groups: true, options: true} - */ - Core.prototype.clear = function(what) { - // clear items - if (!what || what.items) { - this.setItems(null); - } + // apply new height (just always zero for BackgroundGroup + this.dom.background.style.height = '0'; - // clear groups - if (!what || what.groups) { - this.setGroups(null); + // update vertical position of items after they are re-stacked and the height of the group is calculated + for (var i = 0, ii = this.visibleItems.length; i < ii; i++) { + var item = this.visibleItems[i]; + item.repositionY(margin); } - // clear options of timeline and of each of the components - if (!what || what.options) { - this.components.forEach(function (component) { - component.setOptions(component.defaultOptions); - }); - - this.setOptions(this.defaultOptions); // this will also do a redraw - } + return resized; }; /** - * Set Core window such that it fits all items - * @param {Object} [options] Available options: - * `animate: boolean | number` - * If true (default), the range is animated - * smoothly to the new window. - * If a number, the number is taken as duration - * for the animation. Default duration is 500 ms. + * Show this group: attach to the DOM */ - Core.prototype.fit = function(options) { - var range = this._getDataRange(); - - // skip range set if there is no start and end date - if (range.start === null && range.end === null) { - return; + BackgroundGroup.prototype.show = function() { + if (!this.dom.background.parentNode) { + this.itemSet.dom.background.appendChild(this.dom.background); } - - var animate = (options && options.animate !== undefined) ? options.animate : true; - this.range.setRange(range.start, range.end, animate); }; - /** - * Calculate the data range of the items and applies a 5% window around it. - * @returns {{start: Date | null, end: Date | null}} - * @protected - */ - Core.prototype._getDataRange = function() { - // apply the data range as range - var dataRange = this.getItemRange(); + module.exports = BackgroundGroup; - // add 5% space on both sides - var start = dataRange.min; - var end = dataRange.max; - if (start != null && end != null) { - var interval = (end.valueOf() - start.valueOf()); - if (interval <= 0) { - // prevent an empty interval - interval = 24 * 60 * 60 * 1000; // 1 day - } - start = new Date(start.valueOf() - interval * 0.05); - end = new Date(end.valueOf() + interval * 0.05); - } - return { - start: start, - end: end - } - }; +/***/ }, +/* 32 */ +/***/ function(module, exports, __webpack_require__) { + + var Hammer = __webpack_require__(45); + var util = __webpack_require__(1); + var DataSet = __webpack_require__(3); + var DataView = __webpack_require__(4); + var Component = __webpack_require__(25); + var Group = __webpack_require__(30); + var BackgroundGroup = __webpack_require__(31); + var BoxItem = __webpack_require__(22); + var PointItem = __webpack_require__(23); + var RangeItem = __webpack_require__(24); + var BackgroundItem = __webpack_require__(21); + + + var UNGROUPED = '__ungrouped__'; // reserved group id for ungrouped items + var BACKGROUND = '__background__'; // reserved group id for background items without group /** - * Set the visible window. Both parameters are optional, you can change only - * start or only end. Syntax: - * - * TimeLine.setWindow(start, end) - * TimeLine.setWindow(range) - * - * Where start and end can be a Date, number, or string, and range is an - * object with properties start and end. - * - * @param {Date | Number | String | Object} [start] Start date of visible window - * @param {Date | Number | String} [end] End date of visible window - * @param {Object} [options] Available options: - * `animate: boolean | number` - * If true (default), the range is animated - * smoothly to the new window. - * If a number, the number is taken as duration - * for the animation. Default duration is 500 ms. + * An ItemSet holds a set of items and ranges which can be displayed in a + * range. The width is determined by the parent of the ItemSet, and the height + * is determined by the size of the items. + * @param {{dom: Object, domProps: Object, emitter: Emitter, range: Range}} body + * @param {Object} [options] See ItemSet.setOptions for the available options. + * @constructor ItemSet + * @extends Component */ - Core.prototype.setWindow = function(start, end, options) { - var animate = (options && options.animate !== undefined) ? options.animate : true; - if (arguments.length == 1) { - var range = arguments[0]; - this.range.setRange(range.start, range.end, animate); - } - else { - this.range.setRange(start, end, animate); - } - }; - - /** - * Move the window such that given time is centered on screen. - * @param {Date | Number | String} time - * @param {Object} [options] Available options: - * `animate: boolean | number` - * If true (default), the range is animated - * smoothly to the new window. - * If a number, the number is taken as duration - * for the animation. Default duration is 500 ms. - */ - Core.prototype.moveTo = function(time, options) { - var interval = this.range.end - this.range.start; - var t = util.convert(time, 'Date').valueOf(); - - var start = t - interval / 2; - var end = t + interval / 2; - var animate = (options && options.animate !== undefined) ? options.animate : true; - - this.range.setRange(start, end, animate); - }; - - /** - * Get the visible window - * @return {{start: Date, end: Date}} Visible range - */ - Core.prototype.getWindow = function() { - var range = this.range.getRange(); - return { - start: new Date(range.start), - end: new Date(range.end) - }; - }; - - /** - * Force a redraw of the Core. Can be useful to manually redraw when - * option autoResize=false - */ - Core.prototype.redraw = function() { - var resized = false; - var options = this.options; - var props = this.props; - var dom = this.dom; - - if (!dom) return; // when destroyed - - DateUtil.updateHiddenDates(this.body, this.options.hiddenDates); - - // update class names - if (options.orientation == 'top') { - util.addClassName(dom.root, 'top'); - util.removeClassName(dom.root, 'bottom'); - } - else { - util.removeClassName(dom.root, 'top'); - util.addClassName(dom.root, 'bottom'); - } - - // update root width and height options - dom.root.style.maxHeight = util.option.asSize(options.maxHeight, ''); - dom.root.style.minHeight = util.option.asSize(options.minHeight, ''); - dom.root.style.width = util.option.asSize(options.width, ''); - - // calculate border widths - props.border.left = (dom.centerContainer.offsetWidth - dom.centerContainer.clientWidth) / 2; - props.border.right = props.border.left; - props.border.top = (dom.centerContainer.offsetHeight - dom.centerContainer.clientHeight) / 2; - props.border.bottom = props.border.top; - var borderRootHeight= dom.root.offsetHeight - dom.root.clientHeight; - var borderRootWidth = dom.root.offsetWidth - dom.root.clientWidth; - - // workaround for a bug in IE: the clientWidth of an element with - // a height:0px and overflow:hidden is not calculated and always has value 0 - if (dom.centerContainer.clientHeight === 0) { - props.border.left = props.border.top; - props.border.right = props.border.left; - } - if (dom.root.clientHeight === 0) { - borderRootWidth = borderRootHeight; - } - - // calculate the heights. If any of the side panels is empty, we set the height to - // minus the border width, such that the border will be invisible - props.center.height = dom.center.offsetHeight; - props.left.height = dom.left.offsetHeight; - props.right.height = dom.right.offsetHeight; - props.top.height = dom.top.clientHeight || -props.border.top; - props.bottom.height = dom.bottom.clientHeight || -props.border.bottom; - - // TODO: compensate borders when any of the panels is empty. - - // apply auto height - // TODO: only calculate autoHeight when needed (else we cause an extra reflow/repaint of the DOM) - var contentHeight = Math.max(props.left.height, props.center.height, props.right.height); - var autoHeight = props.top.height + contentHeight + props.bottom.height + - borderRootHeight + props.border.top + props.border.bottom; - dom.root.style.height = util.option.asSize(options.height, autoHeight + 'px'); - - // calculate heights of the content panels - props.root.height = dom.root.offsetHeight; - props.background.height = props.root.height - borderRootHeight; - var containerHeight = props.root.height - props.top.height - props.bottom.height - - borderRootHeight; - props.centerContainer.height = containerHeight; - props.leftContainer.height = containerHeight; - props.rightContainer.height = props.leftContainer.height; - - // calculate the widths of the panels - props.root.width = dom.root.offsetWidth; - props.background.width = props.root.width - borderRootWidth; - props.left.width = dom.leftContainer.clientWidth || -props.border.left; - props.leftContainer.width = props.left.width; - props.right.width = dom.rightContainer.clientWidth || -props.border.right; - props.rightContainer.width = props.right.width; - var centerWidth = props.root.width - props.left.width - props.right.width - borderRootWidth; - props.center.width = centerWidth; - props.centerContainer.width = centerWidth; - props.top.width = centerWidth; - props.bottom.width = centerWidth; - - // resize the panels - dom.background.style.height = props.background.height + 'px'; - dom.backgroundVertical.style.height = props.background.height + 'px'; - dom.backgroundHorizontal.style.height = props.centerContainer.height + 'px'; - dom.centerContainer.style.height = props.centerContainer.height + 'px'; - dom.leftContainer.style.height = props.leftContainer.height + 'px'; - dom.rightContainer.style.height = props.rightContainer.height + 'px'; - - dom.background.style.width = props.background.width + 'px'; - dom.backgroundVertical.style.width = props.centerContainer.width + 'px'; - dom.backgroundHorizontal.style.width = props.background.width + 'px'; - dom.centerContainer.style.width = props.center.width + 'px'; - dom.top.style.width = props.top.width + 'px'; - dom.bottom.style.width = props.bottom.width + 'px'; - - // reposition the panels - dom.background.style.left = '0'; - dom.background.style.top = '0'; - dom.backgroundVertical.style.left = (props.left.width + props.border.left) + 'px'; - dom.backgroundVertical.style.top = '0'; - dom.backgroundHorizontal.style.left = '0'; - dom.backgroundHorizontal.style.top = props.top.height + 'px'; - dom.centerContainer.style.left = props.left.width + 'px'; - dom.centerContainer.style.top = props.top.height + 'px'; - dom.leftContainer.style.left = '0'; - dom.leftContainer.style.top = props.top.height + 'px'; - dom.rightContainer.style.left = (props.left.width + props.center.width) + 'px'; - dom.rightContainer.style.top = props.top.height + 'px'; - dom.top.style.left = props.left.width + 'px'; - dom.top.style.top = '0'; - dom.bottom.style.left = props.left.width + 'px'; - dom.bottom.style.top = (props.top.height + props.centerContainer.height) + 'px'; - - // update the scrollTop, feasible range for the offset can be changed - // when the height of the Core or of the contents of the center changed - this._updateScrollTop(); - - // reposition the scrollable contents - var offset = this.props.scrollTop; - if (options.orientation == 'bottom') { - offset += Math.max(this.props.centerContainer.height - this.props.center.height - - this.props.border.top - this.props.border.bottom, 0); - } - dom.center.style.left = '0'; - dom.center.style.top = offset + 'px'; - dom.left.style.left = '0'; - dom.left.style.top = offset + 'px'; - dom.right.style.left = '0'; - dom.right.style.top = offset + 'px'; - - // show shadows when vertical scrolling is available - var visibilityTop = this.props.scrollTop == 0 ? 'hidden' : ''; - var visibilityBottom = this.props.scrollTop == this.props.scrollTopMin ? 'hidden' : ''; - dom.shadowTop.style.visibility = visibilityTop; - dom.shadowBottom.style.visibility = visibilityBottom; - dom.shadowTopLeft.style.visibility = visibilityTop; - dom.shadowBottomLeft.style.visibility = visibilityBottom; - dom.shadowTopRight.style.visibility = visibilityTop; - dom.shadowBottomRight.style.visibility = visibilityBottom; - - // redraw all components - this.components.forEach(function (component) { - resized = component.redraw() || resized; - }); - if (resized) { - // keep repainting until all sizes are settled - var MAX_REDRAWS = 3; // maximum number of consecutive redraws - if (this.redrawCount < MAX_REDRAWS) { - this.redrawCount++; - this.redraw(); - } - else { - console.log('WARNING: infinite loop in redraw?'); - } - this.redrawCount = 0; - } - - this.emit("finishedRedraw"); - }; - - // TODO: deprecated since version 1.1.0, remove some day - Core.prototype.repaint = function () { - throw new Error('Function repaint is deprecated. Use redraw instead.'); - }; - - /** - * Set a current time. This can be used for example to ensure that a client's - * time is synchronized with a shared server time. - * Only applicable when option `showCurrentTime` is true. - * @param {Date | String | Number} time A Date, unix timestamp, or - * ISO date string. - */ - Core.prototype.setCurrentTime = function(time) { - if (!this.currentTime) { - throw new Error('Option showCurrentTime must be true'); - } - - this.currentTime.setCurrentTime(time); - }; - - /** - * Get the current time. - * Only applicable when option `showCurrentTime` is true. - * @return {Date} Returns the current time. - */ - Core.prototype.getCurrentTime = function() { - if (!this.currentTime) { - throw new Error('Option showCurrentTime must be true'); - } - - return this.currentTime.getCurrentTime(); - }; - - /** - * Convert a position on screen (pixels) to a datetime - * @param {int} x Position on the screen in pixels - * @return {Date} time The datetime the corresponds with given position x - * @private - */ - // TODO: move this function to Range - Core.prototype._toTime = function(x) { - return DateUtil.toTime(this, x, this.props.center.width); - }; - - /** - * Convert a position on the global screen (pixels) to a datetime - * @param {int} x Position on the screen in pixels - * @return {Date} time The datetime the corresponds with given position x - * @private - */ - // TODO: move this function to Range - Core.prototype._toGlobalTime = function(x) { - return DateUtil.toTime(this, x, this.props.root.width); - //var conversion = this.range.conversion(this.props.root.width); - //return new Date(x / conversion.scale + conversion.offset); - }; - - /** - * Convert a datetime (Date object) into a position on the screen - * @param {Date} time A date - * @return {int} x The position on the screen in pixels which corresponds - * with the given date. - * @private - */ - // TODO: move this function to Range - Core.prototype._toScreen = function(time) { - return DateUtil.toScreen(this, time, this.props.center.width); - }; - - - - /** - * Convert a datetime (Date object) into a position on the root - * This is used to get the pixel density estimate for the screen, not the center panel - * @param {Date} time A date - * @return {int} x The position on root in pixels which corresponds - * with the given date. - * @private - */ - // TODO: move this function to Range - Core.prototype._toGlobalScreen = function(time) { - return DateUtil.toScreen(this, time, this.props.root.width); - //var conversion = this.range.conversion(this.props.root.width); - //return (time.valueOf() - conversion.offset) * conversion.scale; - }; - - - /** - * Initialize watching when option autoResize is true - * @private - */ - Core.prototype._initAutoResize = function () { - if (this.options.autoResize == true) { - this._startAutoResize(); - } - else { - this._stopAutoResize(); - } - }; - - /** - * Watch for changes in the size of the container. On resize, the Panel will - * automatically redraw itself. - * @private - */ - Core.prototype._startAutoResize = function () { - var me = this; - - this._stopAutoResize(); - - this._onResize = function() { - if (me.options.autoResize != true) { - // stop watching when the option autoResize is changed to false - me._stopAutoResize(); - return; - } - - if (me.dom.root) { - // check whether the frame is resized - // Note: we compare offsetWidth here, not clientWidth. For some reason, - // IE does not restore the clientWidth from 0 to the actual width after - // changing the timeline's container display style from none to visible - if ((me.dom.root.offsetWidth != me.props.lastWidth) || - (me.dom.root.offsetHeight != me.props.lastHeight)) { - me.props.lastWidth = me.dom.root.offsetWidth; - me.props.lastHeight = me.dom.root.offsetHeight; - - me.emit('change'); - } - } - }; - - // add event listener to window resize - util.addEventListener(window, 'resize', this._onResize); - - this.watchTimer = setInterval(this._onResize, 1000); - }; - - /** - * Stop watching for a resize of the frame. - * @private - */ - Core.prototype._stopAutoResize = function () { - if (this.watchTimer) { - clearInterval(this.watchTimer); - this.watchTimer = undefined; - } - - // remove event listener on window.resize - util.removeEventListener(window, 'resize', this._onResize); - this._onResize = null; - }; - - /** - * Start moving the timeline vertically - * @param {Event} event - * @private - */ - Core.prototype._onTouch = function (event) { - this.touch.allowDragging = true; - }; - - /** - * Start moving the timeline vertically - * @param {Event} event - * @private - */ - Core.prototype._onPinch = function (event) { - this.touch.allowDragging = false; - }; - - /** - * Start moving the timeline vertically - * @param {Event} event - * @private - */ - Core.prototype._onDragStart = function (event) { - this.touch.initialScrollTop = this.props.scrollTop; - }; - - /** - * Move the timeline vertically - * @param {Event} event - * @private - */ - Core.prototype._onDrag = function (event) { - // refuse to drag when we where pinching to prevent the timeline make a jump - // when releasing the fingers in opposite order from the touch screen - if (!this.touch.allowDragging) return; - - var delta = event.gesture.deltaY; - - var oldScrollTop = this._getScrollTop(); - var newScrollTop = this._setScrollTop(this.touch.initialScrollTop + delta); - - - if (newScrollTop != oldScrollTop) { - this.redraw(); // TODO: this causes two redraws when dragging, the other is triggered by rangechange already - this.emit("verticalDrag"); - } - }; - - /** - * Apply a scrollTop - * @param {Number} scrollTop - * @returns {Number} scrollTop Returns the applied scrollTop - * @private - */ - Core.prototype._setScrollTop = function (scrollTop) { - this.props.scrollTop = scrollTop; - this._updateScrollTop(); - return this.props.scrollTop; - }; - - /** - * Update the current scrollTop when the height of the containers has been changed - * @returns {Number} scrollTop Returns the applied scrollTop - * @private - */ - Core.prototype._updateScrollTop = function () { - // recalculate the scrollTopMin - var scrollTopMin = Math.min(this.props.centerContainer.height - this.props.center.height, 0); // is negative or zero - if (scrollTopMin != this.props.scrollTopMin) { - // in case of bottom orientation, change the scrollTop such that the contents - // do not move relative to the time axis at the bottom - if (this.options.orientation == 'bottom') { - this.props.scrollTop += (scrollTopMin - this.props.scrollTopMin); - } - this.props.scrollTopMin = scrollTopMin; - } - - // limit the scrollTop to the feasible scroll range - if (this.props.scrollTop > 0) this.props.scrollTop = 0; - if (this.props.scrollTop < scrollTopMin) this.props.scrollTop = scrollTopMin; - - return this.props.scrollTop; - }; - - /** - * Get the current scrollTop - * @returns {number} scrollTop - * @private - */ - Core.prototype._getScrollTop = function () { - return this.props.scrollTop; - }; - - module.exports = Core; - - -/***/ }, -/* 26 */ -/***/ function(module, exports, __webpack_require__) { - - var Hammer = __webpack_require__(19); - var util = __webpack_require__(1); - var DataSet = __webpack_require__(7); - var DataView = __webpack_require__(9); - var Component = __webpack_require__(23); - var Group = __webpack_require__(27); - var BackgroundGroup = __webpack_require__(31); - var BoxItem = __webpack_require__(32); - var PointItem = __webpack_require__(33); - var RangeItem = __webpack_require__(29); - var BackgroundItem = __webpack_require__(34); - - - var UNGROUPED = '__ungrouped__'; // reserved group id for ungrouped items - var BACKGROUND = '__background__'; // reserved group id for background items without group - - /** - * An ItemSet holds a set of items and ranges which can be displayed in a - * range. The width is determined by the parent of the ItemSet, and the height - * is determined by the size of the items. - * @param {{dom: Object, domProps: Object, emitter: Emitter, range: Range}} body - * @param {Object} [options] See ItemSet.setOptions for the available options. - * @constructor ItemSet - * @extends Component - */ - function ItemSet(body, options) { - this.body = body; + function ItemSet(body, options) { + this.body = body; this.defaultOptions = { type: null, // 'box', 'point', 'range', 'background' @@ -15777,14464 +13800,16055 @@ return /******/ (function(modules) { // webpackBootstrap /***/ }, -/* 27 */ +/* 33 */ /***/ function(module, exports, __webpack_require__) { var util = __webpack_require__(1); - var stack = __webpack_require__(28); - var RangeItem = __webpack_require__(29); + var DOMutil = __webpack_require__(2); + var Component = __webpack_require__(25); /** - * @constructor Group - * @param {Number | String} groupId - * @param {Object} data - * @param {ItemSet} itemSet + * Legend for Graph2d */ - function Group (groupId, data, itemSet) { - this.groupId = groupId; - this.subgroups = {}; - this.subgroupIndex = 0; - this.subgroupOrderer = data && data.subgroupOrder; - this.itemSet = itemSet; - - this.dom = {}; - this.props = { - label: { - width: 0, - height: 0 + function Legend(body, options, side, linegraphOptions) { + this.body = body; + this.defaultOptions = { + enabled: true, + icons: true, + iconSize: 20, + iconSpacing: 6, + left: { + visible: true, + position: 'top-left' // top/bottom - left,center,right + }, + right: { + visible: true, + position: 'top-left' // top/bottom - left,center,right } - }; - this.className = null; - - this.items = {}; // items filtered by groupId of this group - this.visibleItems = []; // items currently visible in window - this.orderedItems = { - byStart: [], - byEnd: [] - }; - this.checkRangedItems = false; // needed to refresh the ranged items if the window is programatically changed with NO overlap. - var me = this; - this.itemSet.body.emitter.on("checkRangedItems", function () { - me.checkRangedItems = true; - }) + } + this.side = side; + this.options = util.extend({},this.defaultOptions); + this.linegraphOptions = linegraphOptions; + this.svgElements = {}; + this.dom = {}; + this.groups = {}; + this.amountOfGroups = 0; this._create(); - this.setData(data); + this.setOptions(options); } - /** - * Create DOM elements for the group - * @private - */ - Group.prototype._create = function() { - var label = document.createElement('div'); - label.className = 'vlabel'; - this.dom.label = label; - - var inner = document.createElement('div'); - inner.className = 'inner'; - label.appendChild(inner); - this.dom.inner = inner; - - var foreground = document.createElement('div'); - foreground.className = 'group'; - foreground['timeline-group'] = this; - this.dom.foreground = foreground; - - this.dom.background = document.createElement('div'); - this.dom.background.className = 'group'; - - this.dom.axis = document.createElement('div'); - this.dom.axis.className = 'group'; - - // create a hidden marker to detect when the Timelines container is attached - // to the DOM, or the style of a parent of the Timeline is changed from - // display:none is changed to visible. - this.dom.marker = document.createElement('div'); - this.dom.marker.style.visibility = 'hidden'; // TODO: ask jos why this is not none? - this.dom.marker.innerHTML = '?'; - this.dom.background.appendChild(this.dom.marker); - }; - - /** - * Set the group data for this group - * @param {Object} data Group data, can contain properties content and className - */ - Group.prototype.setData = function(data) { - // update contents - var content = data && data.content; - if (content instanceof Element) { - this.dom.inner.appendChild(content); - } - else if (content !== undefined && content !== null) { - this.dom.inner.innerHTML = content; - } - else { - this.dom.inner.innerHTML = this.groupId || ''; // groupId can be null - } - - // update title - this.dom.label.title = data && data.title || ''; + Legend.prototype = new Component(); - if (!this.dom.inner.firstChild) { - util.addClassName(this.dom.inner, 'hidden'); - } - else { - util.removeClassName(this.dom.inner, 'hidden'); - } + Legend.prototype.clear = function() { + this.groups = {}; + this.amountOfGroups = 0; + } - // update className - var className = data && data.className || null; - if (className != this.className) { - if (this.className) { - util.removeClassName(this.dom.label, this.className); - util.removeClassName(this.dom.foreground, this.className); - util.removeClassName(this.dom.background, this.className); - util.removeClassName(this.dom.axis, this.className); - } - util.addClassName(this.dom.label, className); - util.addClassName(this.dom.foreground, className); - util.addClassName(this.dom.background, className); - util.addClassName(this.dom.axis, className); - this.className = className; - } + Legend.prototype.addGroup = function(label, graphOptions) { - // update style - if (this.style) { - util.removeCssText(this.dom.label, this.style); - this.style = null; - } - if (data && data.style) { - util.addCssText(this.dom.label, data.style); - this.style = data.style; + if (!this.groups.hasOwnProperty(label)) { + this.groups[label] = graphOptions; } + this.amountOfGroups += 1; }; - /** - * Get the width of the group label - * @return {number} width - */ - Group.prototype.getLabelWidth = function() { - return this.props.label.width; + Legend.prototype.updateGroup = function(label, graphOptions) { + this.groups[label] = graphOptions; }; - - /** - * Repaint this group - * @param {{start: number, end: number}} range - * @param {{item: {horizontal: number, vertical: number}, axis: number}} margin - * @param {boolean} [restack=false] Force restacking of all items - * @return {boolean} Returns true if the group is resized - */ - Group.prototype.redraw = function(range, margin, restack) { - var resized = false; - - this.visibleItems = this._updateVisibleItems(this.orderedItems, this.visibleItems, range); - - // force recalculation of the height of the items when the marker height changed - // (due to the Timeline being attached to the DOM or changed from display:none to visible) - var markerHeight = this.dom.marker.clientHeight; - if (markerHeight != this.lastMarkerHeight) { - this.lastMarkerHeight = markerHeight; - - util.forEach(this.items, function (item) { - item.dirty = true; - if (item.displayed) item.redraw(); - }); - - restack = true; - } - - // reposition visible items vertically - if (this.itemSet.options.stack) { // TODO: ugly way to access options... - stack.stack(this.visibleItems, margin, restack); - } - else { // no stacking - stack.nostack(this.visibleItems, margin, this.subgroups); + Legend.prototype.removeGroup = function(label) { + if (this.groups.hasOwnProperty(label)) { + delete this.groups[label]; + this.amountOfGroups -= 1; } + }; - // recalculate the height of the group - var height = this._calculateHeight(margin); - - // calculate actual size and position - var foreground = this.dom.foreground; - this.top = foreground.offsetTop; - this.left = foreground.offsetLeft; - this.width = foreground.offsetWidth; - resized = util.updateProperty(this, 'height', height) || resized; - - // recalculate size of label - resized = util.updateProperty(this.props.label, 'width', this.dom.inner.clientWidth) || resized; - resized = util.updateProperty(this.props.label, 'height', this.dom.inner.clientHeight) || resized; + Legend.prototype._create = function() { + this.dom.frame = document.createElement('div'); + this.dom.frame.className = 'legend'; + this.dom.frame.style.position = "absolute"; + this.dom.frame.style.top = "10px"; + this.dom.frame.style.display = "block"; - // apply new height - this.dom.background.style.height = height + 'px'; - this.dom.foreground.style.height = height + 'px'; - this.dom.label.style.height = height + 'px'; + this.dom.textArea = document.createElement('div'); + this.dom.textArea.className = 'legendText'; + this.dom.textArea.style.position = "relative"; + this.dom.textArea.style.top = "0px"; - // update vertical position of items after they are re-stacked and the height of the group is calculated - for (var i = 0, ii = this.visibleItems.length; i < ii; i++) { - var item = this.visibleItems[i]; - item.repositionY(margin); - } + this.svg = document.createElementNS('http://www.w3.org/2000/svg',"svg"); + this.svg.style.position = 'absolute'; + this.svg.style.top = 0 +'px'; + this.svg.style.width = this.options.iconSize + 5 + 'px'; + this.svg.style.height = '100%'; - return resized; + this.dom.frame.appendChild(this.svg); + this.dom.frame.appendChild(this.dom.textArea); }; /** - * recalculate the height of the group - * @param {{item: {horizontal: number, vertical: number}, axis: number}} margin - * @returns {number} Returns the height - * @private + * Hide the component from the DOM */ - Group.prototype._calculateHeight = function (margin) { - // recalculate the height of the group - var height; - var visibleItems = this.visibleItems; - //var visibleSubgroups = []; - //this.visibleSubgroups = 0; - this.resetSubgroups(); - var me = this; - if (visibleItems.length) { - var min = visibleItems[0].top; - var max = visibleItems[0].top + visibleItems[0].height; - util.forEach(visibleItems, function (item) { - min = Math.min(min, item.top); - max = Math.max(max, (item.top + item.height)); - if (item.data.subgroup !== undefined) { - me.subgroups[item.data.subgroup].height = Math.max(me.subgroups[item.data.subgroup].height,item.height); - me.subgroups[item.data.subgroup].visible = true; - //if (visibleSubgroups.indexOf(item.data.subgroup) == -1){ - // visibleSubgroups.push(item.data.subgroup); - // me.visibleSubgroups += 1; - //} - } - }); - if (min > margin.axis) { - // there is an empty gap between the lowest item and the axis - var offset = min - margin.axis; - max -= offset; - util.forEach(visibleItems, function (item) { - item.top -= offset; - }); - } - height = max + margin.item.vertical / 2; - } - else { - height = margin.axis + margin.item.vertical; + Legend.prototype.hide = function() { + // remove the frame containing the items + if (this.dom.frame.parentNode) { + this.dom.frame.parentNode.removeChild(this.dom.frame); } - height = Math.max(height, this.props.label.height); - - return height; }; /** - * Show this group: attach to the DOM + * Show the component in the DOM (when not already visible). + * @return {Boolean} changed */ - Group.prototype.show = function() { - if (!this.dom.label.parentNode) { - this.itemSet.dom.labelSet.appendChild(this.dom.label); - } - - if (!this.dom.foreground.parentNode) { - this.itemSet.dom.foreground.appendChild(this.dom.foreground); - } - - if (!this.dom.background.parentNode) { - this.itemSet.dom.background.appendChild(this.dom.background); - } - - if (!this.dom.axis.parentNode) { - this.itemSet.dom.axis.appendChild(this.dom.axis); + Legend.prototype.show = function() { + // show frame containing the items + if (!this.dom.frame.parentNode) { + this.body.dom.center.appendChild(this.dom.frame); } }; - /** - * Hide this group: remove from the DOM - */ - Group.prototype.hide = function() { - var label = this.dom.label; - if (label.parentNode) { - label.parentNode.removeChild(label); - } - - var foreground = this.dom.foreground; - if (foreground.parentNode) { - foreground.parentNode.removeChild(foreground); - } - - var background = this.dom.background; - if (background.parentNode) { - background.parentNode.removeChild(background); - } - - var axis = this.dom.axis; - if (axis.parentNode) { - axis.parentNode.removeChild(axis); - } + Legend.prototype.setOptions = function(options) { + var fields = ['enabled','orientation','icons','left','right']; + util.selectiveDeepExtend(fields, this.options, options); }; - /** - * Add an item to the group - * @param {Item} item - */ - Group.prototype.add = function(item) { - this.items[item.id] = item; - item.setParent(this); - - // add to - if (item.data.subgroup !== undefined) { - if (this.subgroups[item.data.subgroup] === undefined) { - this.subgroups[item.data.subgroup] = {height:0, visible: false, index:this.subgroupIndex, items: []}; - this.subgroupIndex++; + Legend.prototype.redraw = function() { + var activeGroups = 0; + for (var groupId in this.groups) { + if (this.groups.hasOwnProperty(groupId)) { + if (this.groups[groupId].visible == true && (this.linegraphOptions.visibility[groupId] === undefined || this.linegraphOptions.visibility[groupId] == true)) { + activeGroups++; + } } - this.subgroups[item.data.subgroup].items.push(item); } - this.orderSubgroups(); - if (this.visibleItems.indexOf(item) == -1) { - var range = this.itemSet.body.range; // TODO: not nice accessing the range like this - this._checkIfVisible(item, this.visibleItems, range); + if (this.options[this.side].visible == false || this.amountOfGroups == 0 || this.options.enabled == false || activeGroups == 0) { + this.hide(); } - }; + else { + this.show(); + if (this.options[this.side].position == 'top-left' || this.options[this.side].position == 'bottom-left') { + this.dom.frame.style.left = '4px'; + this.dom.frame.style.textAlign = "left"; + this.dom.textArea.style.textAlign = "left"; + this.dom.textArea.style.left = (this.options.iconSize + 15) + 'px'; + this.dom.textArea.style.right = ''; + this.svg.style.left = 0 +'px'; + this.svg.style.right = ''; + } + else { + this.dom.frame.style.right = '4px'; + this.dom.frame.style.textAlign = "right"; + this.dom.textArea.style.textAlign = "right"; + this.dom.textArea.style.right = (this.options.iconSize + 15) + 'px'; + this.dom.textArea.style.left = ''; + this.svg.style.right = 0 +'px'; + this.svg.style.left = ''; + } - Group.prototype.orderSubgroups = function() { - if (this.subgroupOrderer !== undefined) { - var sortArray = []; - if (typeof this.subgroupOrderer == 'string') { - for (var subgroup in this.subgroups) { - sortArray.push({subgroup: subgroup, sortField: this.subgroups[subgroup].items[0].data[this.subgroupOrderer]}) - } - sortArray.sort(function (a, b) { - return a.sortField - b.sortField; - }) + if (this.options[this.side].position == 'top-left' || this.options[this.side].position == 'top-right') { + this.dom.frame.style.top = 4 - Number(this.body.dom.center.style.top.replace("px","")) + 'px'; + this.dom.frame.style.bottom = ''; } - else if (typeof this.subgroupOrderer == 'function') { - for (var subgroup in this.subgroups) { - sortArray.push(this.subgroups[subgroup].items[0].data); - } - sortArray.sort(this.subgroupOrderer); + else { + var scrollableHeight = this.body.domProps.center.height - this.body.domProps.centerContainer.height; + this.dom.frame.style.bottom = 4 + scrollableHeight + Number(this.body.dom.center.style.top.replace("px","")) + 'px'; + this.dom.frame.style.top = ''; } - if (sortArray.length > 0) { - for (var i = 0; i < sortArray.length; i++) { - this.subgroups[sortArray[i].subgroup].index = i; - } + if (this.options.icons == false) { + this.dom.frame.style.width = this.dom.textArea.offsetWidth + 10 + 'px'; + this.dom.textArea.style.right = ''; + this.dom.textArea.style.left = ''; + this.svg.style.width = '0px'; + } + else { + this.dom.frame.style.width = this.options.iconSize + 15 + this.dom.textArea.offsetWidth + 10 + 'px' + this.drawLegendIcons(); } - } - }; - Group.prototype.resetSubgroups = function() { - for (var subgroup in this.subgroups) { - if (this.subgroups.hasOwnProperty(subgroup)) { - this.subgroups[subgroup].visible = false; + var content = ''; + for (var groupId in this.groups) { + if (this.groups.hasOwnProperty(groupId)) { + if (this.groups[groupId].visible == true && (this.linegraphOptions.visibility[groupId] === undefined || this.linegraphOptions.visibility[groupId] == true)) { + content += this.groups[groupId].content + '
'; + } + } } + this.dom.textArea.innerHTML = content; + this.dom.textArea.style.lineHeight = ((0.75 * this.options.iconSize) + this.options.iconSpacing) + 'px'; } }; - /** - * Remove an item from the group - * @param {Item} item - */ - Group.prototype.remove = function(item) { - delete this.items[item.id]; - item.setParent(null); - - // remove from visible items - var index = this.visibleItems.indexOf(item); - if (index != -1) this.visibleItems.splice(index, 1); + Legend.prototype.drawLegendIcons = function() { + if (this.dom.frame.parentNode) { + DOMutil.prepareElements(this.svgElements); + var padding = window.getComputedStyle(this.dom.frame).paddingTop; + var iconOffset = Number(padding.replace('px','')); + var x = iconOffset; + var iconWidth = this.options.iconSize; + var iconHeight = 0.75 * this.options.iconSize; + var y = iconOffset + 0.5 * iconHeight + 3; - // TODO: also remove from ordered items? - }; + this.svg.style.width = iconWidth + 5 + iconOffset + 'px'; + for (var groupId in this.groups) { + if (this.groups.hasOwnProperty(groupId)) { + if (this.groups[groupId].visible == true && (this.linegraphOptions.visibility[groupId] === undefined || this.linegraphOptions.visibility[groupId] == true)) { + this.groups[groupId].drawIcon(x, y, this.svgElements, this.svg, iconWidth, iconHeight); + y += iconHeight + this.options.iconSpacing; + } + } + } - /** - * Remove an item from the corresponding DataSet - * @param {Item} item - */ - Group.prototype.removeFromDataSet = function(item) { - this.itemSet.removeItem(item.id); + DOMutil.cleanupElements(this.svgElements); + } }; + module.exports = Legend; - /** - * Reorder the items - */ - Group.prototype.order = function() { - var array = util.toArray(this.items); - var startArray = []; - var endArray = []; - for (var i = 0; i < array.length; i++) { - if (array[i].data.end !== undefined) { - endArray.push(array[i]); - } - startArray.push(array[i]); - } - this.orderedItems = { - byStart: startArray, - byEnd: endArray - }; +/***/ }, +/* 34 */ +/***/ function(module, exports, __webpack_require__) { - stack.orderByStart(this.orderedItems.byStart); - stack.orderByEnd(this.orderedItems.byEnd); - }; + var util = __webpack_require__(1); + var DOMutil = __webpack_require__(2); + var DataSet = __webpack_require__(3); + var DataView = __webpack_require__(4); + var Component = __webpack_require__(25); + var DataAxis = __webpack_require__(28); + var GraphGroup = __webpack_require__(29); + var Legend = __webpack_require__(33); + var BarGraphFunctions = __webpack_require__(50); + var UNGROUPED = '__ungrouped__'; // reserved group id for ungrouped items /** - * Update the visible items - * @param {{byStart: Item[], byEnd: Item[]}} orderedItems All items ordered by start date and by end date - * @param {Item[]} visibleItems The previously visible items. - * @param {{start: number, end: number}} range Visible range - * @return {Item[]} visibleItems The new visible items. - * @private + * This is the constructor of the LineGraph. It requires a Timeline body and options. + * + * @param body + * @param options + * @constructor */ - Group.prototype._updateVisibleItems = function(orderedItems, oldVisibleItems, range) { - var visibleItems = []; - var visibleItemsLookup = {}; // we keep this to quickly look up if an item already exists in the list without using indexOf on visibleItems - var interval = (range.end - range.start) / 4; - var lowerBound = range.start - interval; - var upperBound = range.end + interval; - var item, i; - - // this function is used to do the binary search. - var searchFunction = function (value) { - if (value < lowerBound) {return -1;} - else if (value <= upperBound) {return 0;} - else {return 1;} - } + function LineGraph(body, options) { + this.id = util.randomUUID(); + this.body = body; - // first check if the items that were in view previously are still in view. - // IMPORTANT: this handles the case for the items with startdate before the window and enddate after the window! - // also cleans up invisible items. - if (oldVisibleItems.length > 0) { - for (i = 0; i < oldVisibleItems.length; i++) { - this._checkIfVisibleWithReference(oldVisibleItems[i], visibleItems, visibleItemsLookup, range); + this.defaultOptions = { + yAxisOrientation: 'left', + defaultGroup: 'default', + sort: true, + sampling: true, + graphHeight: '400px', + shaded: { + enabled: false, + orientation: 'bottom' // top, bottom + }, + style: 'line', // line, bar + barChart: { + width: 50, + handleOverlap: 'overlap', + align: 'center' // left, center, right + }, + catmullRom: { + enabled: true, + parametrization: 'centripetal', // uniform (alpha = 0.0), chordal (alpha = 1.0), centripetal (alpha = 0.5) + alpha: 0.5 + }, + drawPoints: { + enabled: true, + size: 6, + style: 'square' // square, circle + }, + dataAxis: { + showMinorLabels: true, + showMajorLabels: true, + icons: false, + width: '40px', + visible: true, + alignZeros: true, + customRange: { + left: {min:undefined, max:undefined}, + right: {min:undefined, max:undefined} + } + //, these options are not set by default, but this shows the format they will be in + //format: { + // left: {decimals: 2}, + // right: {decimals: 2} + //}, + //title: { + // left: { + // text: 'left', + // style: 'color:black;' + // }, + // right: { + // text: 'right', + // style: 'color:black;' + // } + //} + }, + legend: { + enabled: false, + icons: true, + left: { + visible: true, + position: 'top-left' // top/bottom - left,right + }, + right: { + visible: true, + position: 'top-right' // top/bottom - left,right + } + }, + groups: { + visibility: {} } - } + }; - // we do a binary search for the items that have only start values. - var initialPosByStart = util.binarySearchCustom(orderedItems.byStart, searchFunction, 'data','start'); + // options is shared by this ItemSet and all its items + this.options = util.extend({}, this.defaultOptions); + this.dom = {}; + this.props = {}; + this.hammer = null; + this.groups = {}; + this.abortedGraphUpdate = false; + this.updateSVGheight = false; + this.updateSVGheightOnResize = false; - // trace the visible items from the inital start pos both ways until an invisible item is found, we only look at the start values. - this._traceVisible(initialPosByStart, orderedItems.byStart, visibleItems, visibleItemsLookup, function (item) { - return (item.data.start < lowerBound || item.data.start > upperBound); - }); + var me = this; + this.itemsData = null; // DataSet + this.groupsData = null; // DataSet - // if the window has changed programmatically without overlapping the old window, the ranged items with start < lowerBound and end > upperbound are not shown. - // We therefore have to brute force check all items in the byEnd list - if (this.checkRangedItems == true) { - this.checkRangedItems = false; - for (i = 0; i < orderedItems.byEnd.length; i++) { - this._checkIfVisibleWithReference(orderedItems.byEnd[i], visibleItems, visibleItemsLookup, range); + // listeners for the DataSet of the items + this.itemListeners = { + 'add': function (event, params, senderId) { + me._onAdd(params.items); + }, + 'update': function (event, params, senderId) { + me._onUpdate(params.items); + }, + 'remove': function (event, params, senderId) { + me._onRemove(params.items); } - } - else { - // we do a binary search for the items that have defined end times. - var initialPosByEnd = util.binarySearchCustom(orderedItems.byEnd, searchFunction, 'data','end'); + }; - // trace the visible items from the inital start pos both ways until an invisible item is found, we only look at the end values. - this._traceVisible(initialPosByEnd, orderedItems.byEnd, visibleItems, visibleItemsLookup, function (item) { - return (item.data.end < lowerBound || item.data.end > upperBound); - }); - } + // listeners for the DataSet of the groups + this.groupListeners = { + 'add': function (event, params, senderId) { + me._onAddGroups(params.items); + }, + 'update': function (event, params, senderId) { + me._onUpdateGroups(params.items); + }, + 'remove': function (event, params, senderId) { + me._onRemoveGroups(params.items); + } + }; + this.items = {}; // object with an Item for every data item + this.selection = []; // list with the ids of all selected nodes + this.lastStart = this.body.range.start; + this.touchParams = {}; // stores properties while dragging - // finally, we reposition all the visible items. - for (i = 0; i < visibleItems.length; i++) { - item = visibleItems[i]; - if (!item.displayed) item.show(); - // reposition item horizontally - item.repositionX(); - } + this.svgElements = {}; + this.setOptions(options); + this.groupsUsingDefaultStyles = [0]; + this.COUNTER = 0; + this.body.emitter.on('rangechanged', function() { + me.lastStart = me.body.range.start; + me.svg.style.left = util.option.asSize(-me.props.width); + me.redraw.call(me,true); + }); - // debug - //console.log("new line") - //if (this.groupId == null) { - // for (i = 0; i < orderedItems.byStart.length; i++) { - // item = orderedItems.byStart[i].data; - // console.log('start',i,initialPosByStart, item.start.valueOf(), item.content, item.start >= lowerBound && item.start <= upperBound,i == initialPosByStart ? "<------------------- HEREEEE" : "") - // } - // for (i = 0; i < orderedItems.byEnd.length; i++) { - // item = orderedItems.byEnd[i].data; - // console.log('rangeEnd',i,initialPosByEnd, item.end.valueOf(), item.content, item.end >= range.start && item.end <= range.end,i == initialPosByEnd ? "<------------------- HEREEEE" : "") - // } - //} + // create the HTML DOM + this._create(); + this.framework = {svg: this.svg, svgElements: this.svgElements, options: this.options, groups: this.groups}; + this.body.emitter.emit('change'); - return visibleItems; - }; + } - Group.prototype._traceVisible = function (initialPos, items, visibleItems, visibleItemsLookup, breakCondition) { - var item; - var i; + LineGraph.prototype = new Component(); - if (initialPos != -1) { - for (i = initialPos; i >= 0; i--) { - item = items[i]; - if (breakCondition(item)) { - break; + /** + * Create the HTML DOM for the ItemSet + */ + LineGraph.prototype._create = function(){ + var frame = document.createElement('div'); + frame.className = 'LineGraph'; + this.dom.frame = frame; + + // create svg element for graph drawing. + this.svg = document.createElementNS('http://www.w3.org/2000/svg','svg'); + this.svg.style.position = 'relative'; + this.svg.style.height = ('' + this.options.graphHeight).replace('px','') + 'px'; + this.svg.style.display = 'block'; + frame.appendChild(this.svg); + + // data axis + this.options.dataAxis.orientation = 'left'; + this.yAxisLeft = new DataAxis(this.body, this.options.dataAxis, this.svg, this.options.groups); + + this.options.dataAxis.orientation = 'right'; + this.yAxisRight = new DataAxis(this.body, this.options.dataAxis, this.svg, this.options.groups); + delete this.options.dataAxis.orientation; + + // legends + this.legendLeft = new Legend(this.body, this.options.legend, 'left', this.options.groups); + this.legendRight = new Legend(this.body, this.options.legend, 'right', this.options.groups); + + this.show(); + }; + + /** + * set the options of the LineGraph. the mergeOptions is used for subObjects that have an enabled element. + * @param {object} options + */ + LineGraph.prototype.setOptions = function(options) { + if (options) { + var fields = ['sampling','defaultGroup','height','graphHeight','yAxisOrientation','style','barChart','dataAxis','sort','groups']; + if (options.graphHeight === undefined && options.height !== undefined && this.body.domProps.centerContainer.height !== undefined) { + this.updateSVGheight = true; + this.updateSVGheightOnResize = true; + } + else if (this.body.domProps.centerContainer.height !== undefined && options.graphHeight !== undefined) { + if (parseInt((options.graphHeight + '').replace("px",'')) < this.body.domProps.centerContainer.height) { + this.updateSVGheight = true; } - else { - if (visibleItemsLookup[item.id] === undefined) { - visibleItemsLookup[item.id] = true; - visibleItems.push(item); + } + util.selectiveDeepExtend(fields, this.options, options); + util.mergeOptions(this.options, options,'catmullRom'); + util.mergeOptions(this.options, options,'drawPoints'); + util.mergeOptions(this.options, options,'shaded'); + util.mergeOptions(this.options, options,'legend'); + + if (options.catmullRom) { + if (typeof options.catmullRom == 'object') { + if (options.catmullRom.parametrization) { + if (options.catmullRom.parametrization == 'uniform') { + this.options.catmullRom.alpha = 0; + } + else if (options.catmullRom.parametrization == 'chordal') { + this.options.catmullRom.alpha = 1.0; + } + else { + this.options.catmullRom.parametrization = 'centripetal'; + this.options.catmullRom.alpha = 0.5; + } } } } - for (i = initialPos + 1; i < items.length; i++) { - item = items[i]; - if (breakCondition(item)) { - break; + if (this.yAxisLeft) { + if (options.dataAxis !== undefined) { + this.yAxisLeft.setOptions(this.options.dataAxis); + this.yAxisRight.setOptions(this.options.dataAxis); } - else { - if (visibleItemsLookup[item.id] === undefined) { - visibleItemsLookup[item.id] = true; - visibleItems.push(item); - } + } + + if (this.legendLeft) { + if (options.legend !== undefined) { + this.legendLeft.setOptions(this.options.legend); + this.legendRight.setOptions(this.options.legend); } } + + if (this.groups.hasOwnProperty(UNGROUPED)) { + this.groups[UNGROUPED].setOptions(options); + } } - } + // this is used to redraw the graph if the visibility of the groups is changed. + if (this.dom.frame) { + this.redraw(true); + } + }; /** - * this function is very similar to the _checkIfInvisible() but it does not - * return booleans, hides the item if it should not be seen and always adds to - * the visibleItems. - * this one is for brute forcing and hiding. - * - * @param {Item} item - * @param {Array} visibleItems - * @param {{start:number, end:number}} range - * @private + * Hide the component from the DOM */ - Group.prototype._checkIfVisible = function(item, visibleItems, range) { - if (item.isVisible(range)) { - if (!item.displayed) item.show(); - // reposition item horizontally - item.repositionX(); - visibleItems.push(item); - } - else { - if (item.displayed) item.hide(); - } + LineGraph.prototype.hide = function() { + // remove the frame containing the items + if (this.dom.frame.parentNode) { + this.dom.frame.parentNode.removeChild(this.dom.frame); + } }; /** - * this function is very similar to the _checkIfInvisible() but it does not - * return booleans, hides the item if it should not be seen and always adds to - * the visibleItems. - * this one is for brute forcing and hiding. - * - * @param {Item} item - * @param {Array} visibleItems - * @param {{start:number, end:number}} range - * @private + * Show the component in the DOM (when not already visible). + * @return {Boolean} changed */ - Group.prototype._checkIfVisibleWithReference = function(item, visibleItems, visibleItemsLookup, range) { - if (item.isVisible(range)) { - if (visibleItemsLookup[item.id] === undefined) { - visibleItemsLookup[item.id] = true; - visibleItems.push(item); - } - } - else { - if (item.displayed) item.hide(); + LineGraph.prototype.show = function() { + // show frame containing the items + if (!this.dom.frame.parentNode) { + this.body.dom.center.appendChild(this.dom.frame); } }; + /** + * Set items + * @param {vis.DataSet | null} items + */ + LineGraph.prototype.setItems = function(items) { + var me = this, + ids, + oldItemsData = this.itemsData; - module.exports = Group; + // replace the dataset + if (!items) { + this.itemsData = null; + } + else if (items instanceof DataSet || items instanceof DataView) { + this.itemsData = items; + } + else { + throw new TypeError('Data must be an instance of DataSet or DataView'); + } + if (oldItemsData) { + // unsubscribe from old dataset + util.forEach(this.itemListeners, function (callback, event) { + oldItemsData.off(event, callback); + }); -/***/ }, -/* 28 */ -/***/ function(module, exports, __webpack_require__) { + // remove all drawn items + ids = oldItemsData.getIds(); + this._onRemove(ids); + } - // Utility functions for ordering and stacking of items - var EPSILON = 0.001; // used when checking collisions, to prevent round-off errors + if (this.itemsData) { + // subscribe to new dataset + var id = this.id; + util.forEach(this.itemListeners, function (callback, event) { + me.itemsData.on(event, callback, id); + }); - /** - * Order items by their start data - * @param {Item[]} items - */ - exports.orderByStart = function(items) { - items.sort(function (a, b) { - return a.data.start - b.data.start; - }); + // add all new items + ids = this.itemsData.getIds(); + this._onAdd(ids); + } + this._updateUngrouped(); + //this._updateGraph(); + this.redraw(true); }; - /** - * Order items by their end date. If they have no end date, their start date - * is used. - * @param {Item[]} items - */ - exports.orderByEnd = function(items) { - items.sort(function (a, b) { - var aTime = ('end' in a.data) ? a.data.end : a.data.start, - bTime = ('end' in b.data) ? b.data.end : b.data.start; - - return aTime - bTime; - }); - }; /** - * Adjust vertical positions of the items such that they don't overlap each - * other. - * @param {Item[]} items - * All visible items - * @param {{item: {horizontal: number, vertical: number}, axis: number}} margin - * Margins between items and between items and the axis. - * @param {boolean} [force=false] - * If true, all items will be repositioned. If false (default), only - * items having a top===null will be re-stacked + * Set groups + * @param {vis.DataSet} groups */ - exports.stack = function(items, margin, force) { - var i, iMax; + LineGraph.prototype.setGroups = function(groups) { + var me = this; + var ids; - if (force) { - // reset top position of all items - for (i = 0, iMax = items.length; i < iMax; i++) { - items[i].top = null; - } + // unsubscribe from current dataset + if (this.groupsData) { + util.forEach(this.groupListeners, function (callback, event) { + me.groupsData.unsubscribe(event, callback); + }); + + // remove all drawn groups + ids = this.groupsData.getIds(); + this.groupsData = null; + this._onRemoveGroups(ids); // note: this will cause a redraw } - // calculate new, non-overlapping positions - for (i = 0, iMax = items.length; i < iMax; i++) { - var item = items[i]; - if (item.stack && item.top === null) { - // initialize top position - item.top = margin.axis; + // replace the dataset + if (!groups) { + this.groupsData = null; + } + else if (groups instanceof DataSet || groups instanceof DataView) { + this.groupsData = groups; + } + else { + throw new TypeError('Data must be an instance of DataSet or DataView'); + } - do { - // TODO: optimize checking for overlap. when there is a gap without items, - // you only need to check for items from the next item on, not from zero - var collidingItem = null; - for (var j = 0, jj = items.length; j < jj; j++) { - var other = items[j]; - if (other.top !== null && other !== item && other.stack && exports.collision(item, other, margin.item)) { - collidingItem = other; - break; - } - } + if (this.groupsData) { + // subscribe to new dataset + var id = this.id; + util.forEach(this.groupListeners, function (callback, event) { + me.groupsData.on(event, callback, id); + }); - if (collidingItem != null) { - // There is a collision. Reposition the items above the colliding element - item.top = collidingItem.top + collidingItem.height + margin.item.vertical; - } - } while (collidingItem); - } + // draw all ms + ids = this.groupsData.getIds(); + this._onAddGroups(ids); } + this._onUpdate(); }; /** - * Adjust vertical positions of the items without stacking them - * @param {Item[]} items - * All visible items - * @param {{item: {horizontal: number, vertical: number}, axis: number}} margin - * Margins between items and between items and the axis. + * Update the data + * @param [ids] + * @private */ - exports.nostack = function(items, margin, subgroups) { - var i, iMax, newTop; - - // reset top position of all items - for (i = 0, iMax = items.length; i < iMax; i++) { - if (items[i].data.subgroup !== undefined) { - newTop = margin.axis; - for (var subgroup in subgroups) { - if (subgroups.hasOwnProperty(subgroup)) { - if (subgroups[subgroup].visible == true && subgroups[subgroup].index < subgroups[items[i].data.subgroup].index) { - newTop += subgroups[subgroup].height + margin.item.vertical; - } - } - } - items[i].top = newTop; - } - else { - items[i].top = margin.axis; - } - } + LineGraph.prototype._onUpdate = function(ids) { + this._updateUngrouped(); + this._updateAllGroupData(); + //this._updateGraph(); + this.redraw(true); }; - - /** - * Test if the two provided items collide - * The items must have parameters left, width, top, and height. - * @param {Item} a The first item - * @param {Item} b The second item - * @param {{horizontal: number, vertical: number}} margin - * An object containing a horizontal and vertical - * minimum required margin. - * @return {boolean} true if a and b collide, else false - */ - exports.collision = function(a, b, margin) { - return ((a.left - margin.horizontal + EPSILON) < (b.left + b.width) && - (a.left + a.width + margin.horizontal - EPSILON) > b.left && - (a.top - margin.vertical + EPSILON) < (b.top + b.height) && - (a.top + a.height + margin.vertical - EPSILON) > b.top); + LineGraph.prototype._onAdd = function (ids) {this._onUpdate(ids);}; + LineGraph.prototype._onRemove = function (ids) {this._onUpdate(ids);}; + LineGraph.prototype._onUpdateGroups = function (groupIds) { + for (var i = 0; i < groupIds.length; i++) { + var group = this.groupsData.get(groupIds[i]); + this._updateGroup(group, groupIds[i]); + } + + //this._updateGraph(); + this.redraw(true); }; + LineGraph.prototype._onAddGroups = function (groupIds) {this._onUpdateGroups(groupIds);}; -/***/ }, -/* 29 */ -/***/ function(module, exports, __webpack_require__) { + /** + * this cleans the group out off the legends and the dataaxis, updates the ungrouped and updates the graph + * @param {Array} groupIds + * @private + */ + LineGraph.prototype._onRemoveGroups = function (groupIds) { + for (var i = 0; i < groupIds.length; i++) { + if (this.groups.hasOwnProperty(groupIds[i])) { + if (this.groups[groupIds[i]].options.yAxisOrientation == 'right') { + this.yAxisRight.removeGroup(groupIds[i]); + this.legendRight.removeGroup(groupIds[i]); + this.legendRight.redraw(); + } + else { + this.yAxisLeft.removeGroup(groupIds[i]); + this.legendLeft.removeGroup(groupIds[i]); + this.legendLeft.redraw(); + } + delete this.groups[groupIds[i]]; + } + } + this._updateUngrouped(); + //this._updateGraph(); + this.redraw(true); + }; - var Hammer = __webpack_require__(19); - var Item = __webpack_require__(30); /** - * @constructor RangeItem - * @extends Item - * @param {Object} data Object containing parameters start, end - * content, className. - * @param {{toScreen: function, toTime: function}} conversion - * Conversion functions from time to screen and vice versa - * @param {Object} [options] Configuration options - * // TODO: describe options + * update a group object with the group dataset entree + * + * @param group + * @param groupId + * @private */ - function RangeItem (data, conversion, options) { - this.props = { - content: { - width: 0 + LineGraph.prototype._updateGroup = function (group, groupId) { + if (!this.groups.hasOwnProperty(groupId)) { + this.groups[groupId] = new GraphGroup(group, groupId, this.options, this.groupsUsingDefaultStyles); + if (this.groups[groupId].options.yAxisOrientation == 'right') { + this.yAxisRight.addGroup(groupId, this.groups[groupId]); + this.legendRight.addGroup(groupId, this.groups[groupId]); } - }; - this.overflow = false; // if contents can overflow (css styling), this flag is set to true - - // validate data - if (data) { - if (data.start == undefined) { - throw new Error('Property "start" missing in item ' + data.id); + else { + this.yAxisLeft.addGroup(groupId, this.groups[groupId]); + this.legendLeft.addGroup(groupId, this.groups[groupId]); } - if (data.end == undefined) { - throw new Error('Property "end" missing in item ' + data.id); + } + else { + this.groups[groupId].update(group); + if (this.groups[groupId].options.yAxisOrientation == 'right') { + this.yAxisRight.updateGroup(groupId, this.groups[groupId]); + this.legendRight.updateGroup(groupId, this.groups[groupId]); + } + else { + this.yAxisLeft.updateGroup(groupId, this.groups[groupId]); + this.legendLeft.updateGroup(groupId, this.groups[groupId]); } } + this.legendLeft.redraw(); + this.legendRight.redraw(); + }; - Item.call(this, data, conversion, options); - } - - RangeItem.prototype = new Item (null, null, null); - - RangeItem.prototype.baseClassName = 'item range'; /** - * Check whether this item is visible inside given range - * @returns {{start: Number, end: Number}} range with a timestamp for start and end - * @returns {boolean} True if visible + * this updates all groups, it is used when there is an update the the itemset. + * + * @private */ - RangeItem.prototype.isVisible = function(range) { - // determine visibility - return (this.data.start < range.end) && (this.data.end > range.start); + LineGraph.prototype._updateAllGroupData = function () { + if (this.itemsData != null) { + var groupsContent = {}; + var groupId; + for (groupId in this.groups) { + if (this.groups.hasOwnProperty(groupId)) { + groupsContent[groupId] = []; + } + } + for (var itemId in this.itemsData._data) { + if (this.itemsData._data.hasOwnProperty(itemId)) { + var item = this.itemsData._data[itemId]; + if (groupsContent[item.group] === undefined) { + throw new Error('Cannot find referenced group. Possible reason: items added before groups? Groups need to be added before items, as items refer to groups.') + } + item.x = util.convert(item.x,'Date'); + groupsContent[item.group].push(item); + } + } + for (groupId in this.groups) { + if (this.groups.hasOwnProperty(groupId)) { + this.groups[groupId].setItems(groupsContent[groupId]); + } + } + } }; + /** - * Repaint the item + * Create or delete the group holding all ungrouped items. This group is used when + * there are no groups specified. This anonymous group is called 'graph'. + * @protected */ - RangeItem.prototype.redraw = function() { - var dom = this.dom; - if (!dom) { - // create DOM - this.dom = {}; - dom = this.dom; - - // background box - dom.box = document.createElement('div'); - // className is updated in redraw() - - // contents box - dom.content = document.createElement('div'); - dom.content.className = 'content'; - dom.box.appendChild(dom.content); - - // attach this item as attribute - dom.box['timeline-item'] = this; - - this.dirty = true; - } + LineGraph.prototype._updateUngrouped = function() { + if (this.itemsData && this.itemsData != null) { + var ungroupedCounter = 0; + for (var itemId in this.itemsData._data) { + if (this.itemsData._data.hasOwnProperty(itemId)) { + var item = this.itemsData._data[itemId]; + if (item != undefined) { + if (item.hasOwnProperty('group')) { + if (item.group === undefined) { + item.group = UNGROUPED; + } + } + else { + item.group = UNGROUPED; + } + ungroupedCounter = item.group == UNGROUPED ? ungroupedCounter + 1 : ungroupedCounter; + } + } + } - // append DOM to parent DOM - if (!this.parent) { - throw new Error('Cannot redraw item: no parent attached'); - } - if (!dom.box.parentNode) { - var foreground = this.parent.dom.foreground; - if (!foreground) { - throw new Error('Cannot redraw item: parent has no foreground container element'); + if (ungroupedCounter == 0) { + delete this.groups[UNGROUPED]; + this.legendLeft.removeGroup(UNGROUPED); + this.legendRight.removeGroup(UNGROUPED); + this.yAxisLeft.removeGroup(UNGROUPED); + this.yAxisRight.removeGroup(UNGROUPED); + } + else { + var group = {id: UNGROUPED, content: this.options.defaultGroup}; + this._updateGroup(group, UNGROUPED); } - foreground.appendChild(dom.box); } - this.displayed = true; - - // Update DOM when item is marked dirty. An item is marked dirty when: - // - the item is not yet rendered - // - the item's data is changed - // - the item is selected/deselected - if (this.dirty) { - this._updateContents(this.dom.content); - this._updateTitle(this.dom.box); - this._updateDataAttributes(this.dom.box); - this._updateStyle(this.dom.box); - - // update class - var className = (this.data.className ? (' ' + this.data.className) : '') + - (this.selected ? ' selected' : ''); - dom.box.className = this.baseClassName + className; - - // determine from css whether this box has overflow - this.overflow = window.getComputedStyle(dom.content).overflow !== 'hidden'; - - // recalculate size - // turn off max-width to be able to calculate the real width - // this causes an extra browser repaint/reflow, but so be it - this.dom.content.style.maxWidth = 'none'; - this.props.content.width = this.dom.content.offsetWidth; - this.height = this.dom.box.offsetHeight; - this.dom.content.style.maxWidth = ''; - - this.dirty = false; + else { + delete this.groups[UNGROUPED]; + this.legendLeft.removeGroup(UNGROUPED); + this.legendRight.removeGroup(UNGROUPED); + this.yAxisLeft.removeGroup(UNGROUPED); + this.yAxisRight.removeGroup(UNGROUPED); } - this._repaintDeleteButton(dom.box); - this._repaintDragLeft(); - this._repaintDragRight(); + this.legendLeft.redraw(); + this.legendRight.redraw(); }; + /** - * Show the item in the DOM (when not already visible). The items DOM will - * be created when needed. + * Redraw the component, mandatory function + * @return {boolean} Returns true if the component is resized */ - RangeItem.prototype.show = function() { - if (!this.displayed) { - this.redraw(); + LineGraph.prototype.redraw = function(forceGraphUpdate) { + var resized = false; + + // calculate actual size and position + this.props.width = this.dom.frame.offsetWidth; + this.props.height = this.body.domProps.centerContainer.height; + + // update the graph if there is no lastWidth or with, used for the initial draw + if (this.lastWidth === undefined && this.props.width) { + forceGraphUpdate = true; } - }; - /** - * Hide the item from the DOM (when visible) - * @return {Boolean} changed - */ - RangeItem.prototype.hide = function() { - if (this.displayed) { - var box = this.dom.box; + // check if this component is resized + resized = this._isResized() || resized; - if (box.parentNode) { - box.parentNode.removeChild(box); - } + // check whether zoomed (in that case we need to re-stack everything) + var visibleInterval = this.body.range.end - this.body.range.start; + var zoomed = (visibleInterval != this.lastVisibleInterval); + this.lastVisibleInterval = visibleInterval; - this.top = null; - this.left = null; - this.displayed = false; - } - }; + // the svg element is three times as big as the width, this allows for fully dragging left and right + // without reloading the graph. the controls for this are bound to events in the constructor + if (resized == true) { + this.svg.style.width = util.option.asSize(3*this.props.width); + this.svg.style.left = util.option.asSize(-this.props.width); - /** - * Reposition the item horizontally - * @Override - */ - RangeItem.prototype.repositionX = function() { - var parentWidth = this.parent.width; - var start = this.conversion.toScreen(this.data.start); - var end = this.conversion.toScreen(this.data.end); - var contentLeft; - var contentWidth; + // if the height of the graph is set as proportional, change the height of the svg + if ((this.options.height + '').indexOf("%") != -1 || this.updateSVGheightOnResize == true) { + this.updateSVGheight = true; + } + } - // limit the width of the this, as browsers cannot draw very wide divs - if (start < -parentWidth) { - start = -parentWidth; + // update the height of the graph on each redraw of the graph. + if (this.updateSVGheight == true) { + if (this.options.graphHeight != this.body.domProps.centerContainer.height + 'px') { + this.options.graphHeight = this.body.domProps.centerContainer.height + 'px'; + this.svg.style.height = this.body.domProps.centerContainer.height + 'px'; + } + this.updateSVGheight = false; } - if (end > 2 * parentWidth) { - end = 2 * parentWidth; + else { + this.svg.style.height = ('' + this.options.graphHeight).replace('px','') + 'px'; } - var boxWidth = Math.max(end - start, 1); - - if (this.overflow) { - this.left = start; - this.width = boxWidth + this.props.content.width; - contentWidth = this.props.content.width; - // Note: The calculation of width is an optimistic calculation, giving - // a width which will not change when moving the Timeline - // So no re-stacking needed, which is nicer for the eye; + // zoomed is here to ensure that animations are shown correctly. + if (resized == true || zoomed == true || this.abortedGraphUpdate == true || forceGraphUpdate == true) { + resized = this._updateGraph() || resized; } else { - this.left = start; - this.width = boxWidth; - contentWidth = Math.min(end - start - 2 * this.options.padding, this.props.content.width); + // move the whole svg while dragging + if (this.lastStart != 0) { + var offset = this.body.range.start - this.lastStart; + var range = this.body.range.end - this.body.range.start; + if (this.props.width != 0) { + var rangePerPixelInv = this.props.width/range; + var xOffset = offset * rangePerPixelInv; + this.svg.style.left = (-this.props.width - xOffset) + 'px'; + } + } } - this.dom.box.style.left = this.left + 'px'; - this.dom.box.style.width = boxWidth + 'px'; - - switch (this.options.align) { - case 'left': - this.dom.content.style.left = '0'; - break; + this.legendLeft.redraw(); + this.legendRight.redraw(); + return resized; + }; - case 'right': - this.dom.content.style.left = Math.max((boxWidth - contentWidth - 2 * this.options.padding), 0) + 'px'; - break; - case 'center': - this.dom.content.style.left = Math.max((boxWidth - contentWidth - 2 * this.options.padding) / 2, 0) + 'px'; - break; + /** + * Update and redraw the graph. + * + */ + LineGraph.prototype._updateGraph = function () { + // reset the svg elements + DOMutil.prepareElements(this.svgElements); + if (this.props.width != 0 && this.itemsData != null) { + var group, i; + var preprocessedGroupData = {}; + var processedGroupData = {}; + var groupRanges = {}; + var changeCalled = false; - default: // 'auto' - // when range exceeds left of the window, position the contents at the left of the visible area - if (this.overflow) { - if (end > 0) { - contentLeft = Math.max(-start, 0); - } - else { - contentLeft = -contentWidth; // ensure it's not visible anymore + // getting group Ids + var groupIds = []; + for (var groupId in this.groups) { + if (this.groups.hasOwnProperty(groupId)) { + group = this.groups[groupId]; + if (group.visible == true && (this.options.groups.visibility[groupId] === undefined || this.options.groups.visibility[groupId] == true)) { + groupIds.push(groupId); } } + } + if (groupIds.length > 0) { + // this is the range of the SVG canvas + var minDate = this.body.util.toGlobalTime(-this.body.domProps.root.width); + var maxDate = this.body.util.toGlobalTime(2 * this.body.domProps.root.width); + var groupsData = {}; + // fill groups data, this only loads the data we require based on the timewindow + this._getRelevantData(groupIds, groupsData, minDate, maxDate); + + // apply sampling, if disabled, it will pass through this function. + this._applySampling(groupIds, groupsData); + + // we transform the X coordinates to detect collisions + for (i = 0; i < groupIds.length; i++) { + preprocessedGroupData[groupIds[i]] = this._convertXcoordinates(groupsData[groupIds[i]]); + } + + // now all needed data has been collected we start the processing. + this._getYRanges(groupIds, preprocessedGroupData, groupRanges); + + // update the Y axis first, we use this data to draw at the correct Y points + // changeCalled is required to clean the SVG on a change emit. + changeCalled = this._updateYAxis(groupIds, groupRanges); + var MAX_CYCLES = 5; + if (changeCalled == true && this.COUNTER < MAX_CYCLES) { + DOMutil.cleanupElements(this.svgElements); + this.abortedGraphUpdate = true; + this.COUNTER++; + this.body.emitter.emit('change'); + return true; + } else { - if (start < 0) { - contentLeft = Math.min(-start, - (end - start - contentWidth - 2 * this.options.padding)); - // TODO: remove the need for options.padding. it's terrible. + if (this.COUNTER > MAX_CYCLES) { + console.log("WARNING: there may be an infinite loop in the _updateGraph emitter cycle.") } - else { - contentLeft = 0; + this.COUNTER = 0; + this.abortedGraphUpdate = false; + + // With the yAxis scaled correctly, use this to get the Y values of the points. + for (i = 0; i < groupIds.length; i++) { + group = this.groups[groupIds[i]]; + processedGroupData[groupIds[i]] = this._convertYcoordinates(groupsData[groupIds[i]], group); } + + // draw the groups + for (i = 0; i < groupIds.length; i++) { + group = this.groups[groupIds[i]]; + if (group.options.style != 'bar') { // bar needs to be drawn enmasse + group.draw(processedGroupData[groupIds[i]], group, this.framework); + } + } + BarGraphFunctions.draw(groupIds, processedGroupData, this.framework); } - this.dom.content.style.left = contentLeft + 'px'; + } } + + // cleanup unused svg elements + DOMutil.cleanupElements(this.svgElements); + return false; }; + /** - * Reposition the item vertically - * @Override + * first select and preprocess the data from the datasets. + * the groups have their preselection of data, we now loop over this data to see + * what data we need to draw. Sorted data is much faster. + * more optimization is possible by doing the sampling before and using the binary search + * to find the end date to determine the increment. + * + * @param {array} groupIds + * @param {object} groupsData + * @param {date} minDate + * @param {date} maxDate + * @private */ - RangeItem.prototype.repositionY = function() { - var orientation = this.options.orientation, - box = this.dom.box; - - if (orientation == 'top') { - box.style.top = this.top + 'px'; - } - else { - box.style.top = (this.parent.height - this.top - this.height) + 'px'; + LineGraph.prototype._getRelevantData = function (groupIds, groupsData, minDate, maxDate) { + var group, i, j, item; + if (groupIds.length > 0) { + for (i = 0; i < groupIds.length; i++) { + group = this.groups[groupIds[i]]; + groupsData[groupIds[i]] = []; + var dataContainer = groupsData[groupIds[i]]; + // optimization for sorted data + if (group.options.sort == true) { + var guess = Math.max(0, util.binarySearchValue(group.itemsData, minDate, 'x', 'before')); + for (j = guess; j < group.itemsData.length; j++) { + item = group.itemsData[j]; + if (item !== undefined) { + if (item.x > maxDate) { + dataContainer.push(item); + break; + } + else { + dataContainer.push(item); + } + } + } + } + else { + for (j = 0; j < group.itemsData.length; j++) { + item = group.itemsData[j]; + if (item !== undefined) { + if (item.x > minDate && item.x < maxDate) { + dataContainer.push(item); + } + } + } + } + } } }; + /** - * Repaint a drag area on the left side of the range when the range is selected - * @protected + * + * @param groupIds + * @param groupsData + * @private */ - RangeItem.prototype._repaintDragLeft = function () { - if (this.selected && this.options.editable.updateTime && !this.dom.dragLeft) { - // create and show drag area - var dragLeft = document.createElement('div'); - dragLeft.className = 'drag-left'; - dragLeft.dragLeftItem = this; - - // TODO: this should be redundant? - Hammer(dragLeft, { - preventDefault: true - }).on('drag', function () { - //console.log('drag left') - }); - - this.dom.box.appendChild(dragLeft); - this.dom.dragLeft = dragLeft; - } - else if (!this.selected && this.dom.dragLeft) { - // delete drag area - if (this.dom.dragLeft.parentNode) { - this.dom.dragLeft.parentNode.removeChild(this.dom.dragLeft); - } - this.dom.dragLeft = null; - } - }; + LineGraph.prototype._applySampling = function (groupIds, groupsData) { + var group; + if (groupIds.length > 0) { + for (var i = 0; i < groupIds.length; i++) { + group = this.groups[groupIds[i]]; + if (group.options.sampling == true) { + var dataContainer = groupsData[groupIds[i]]; + if (dataContainer.length > 0) { + var increment = 1; + var amountOfPoints = dataContainer.length; - /** - * Repaint a drag area on the right side of the range when the range is selected - * @protected - */ - RangeItem.prototype._repaintDragRight = function () { - if (this.selected && this.options.editable.updateTime && !this.dom.dragRight) { - // create and show drag area - var dragRight = document.createElement('div'); - dragRight.className = 'drag-right'; - dragRight.dragRightItem = this; + // the global screen is used because changing the width of the yAxis may affect the increment, resulting in an endless loop + // of width changing of the yAxis. + var xDistance = this.body.util.toGlobalScreen(dataContainer[dataContainer.length - 1].x) - this.body.util.toGlobalScreen(dataContainer[0].x); + var pointsPerPixel = amountOfPoints / xDistance; + increment = Math.min(Math.ceil(0.2 * amountOfPoints), Math.max(1, Math.round(pointsPerPixel))); - // TODO: this should be redundant? - Hammer(dragRight, { - preventDefault: true - }).on('drag', function () { - //console.log('drag right') - }); + var sampledData = []; + for (var j = 0; j < amountOfPoints; j += increment) { + sampledData.push(dataContainer[j]); - this.dom.box.appendChild(dragRight); - this.dom.dragRight = dragRight; - } - else if (!this.selected && this.dom.dragRight) { - // delete drag area - if (this.dom.dragRight.parentNode) { - this.dom.dragRight.parentNode.removeChild(this.dom.dragRight); + } + groupsData[groupIds[i]] = sampledData; + } + } } - this.dom.dragRight = null; } }; - module.exports = RangeItem; - - -/***/ }, -/* 30 */ -/***/ function(module, exports, __webpack_require__) { - - var Hammer = __webpack_require__(19); - var util = __webpack_require__(1); - - /** - * @constructor Item - * @param {Object} data Object containing (optional) parameters type, - * start, end, content, group, className. - * @param {{toScreen: function, toTime: function}} conversion - * Conversion functions from time to screen and vice versa - * @param {Object} options Configuration options - * // TODO: describe available options - */ - function Item (data, conversion, options) { - this.id = null; - this.parent = null; - this.data = data; - this.dom = null; - this.conversion = conversion || {}; - this.options = options || {}; - - this.selected = false; - this.displayed = false; - this.dirty = true; - - this.top = null; - this.left = null; - this.width = null; - this.height = null; - } - - Item.prototype.stack = true; - - /** - * Select current item - */ - Item.prototype.select = function() { - this.selected = true; - this.dirty = true; - if (this.displayed) this.redraw(); - }; - - /** - * Unselect current item - */ - Item.prototype.unselect = function() { - this.selected = false; - this.dirty = true; - if (this.displayed) this.redraw(); - }; - - /** - * Set data for the item. Existing data will be updated. The id should not - * be changed. When the item is displayed, it will be redrawn immediately. - * @param {Object} data - */ - Item.prototype.setData = function(data) { - this.data = data; - this.dirty = true; - if (this.displayed) this.redraw(); - }; /** - * Set a parent for the item - * @param {ItemSet | Group} parent + * + * + * @param {array} groupIds + * @param {object} groupsData + * @param {object} groupRanges | this is being filled here + * @private */ - Item.prototype.setParent = function(parent) { - if (this.displayed) { - this.hide(); - this.parent = parent; - if (this.parent) { - this.show(); + LineGraph.prototype._getYRanges = function (groupIds, groupsData, groupRanges) { + var groupData, group, i; + var barCombinedDataLeft = []; + var barCombinedDataRight = []; + var options; + if (groupIds.length > 0) { + for (i = 0; i < groupIds.length; i++) { + groupData = groupsData[groupIds[i]]; + options = this.groups[groupIds[i]].options; + if (groupData.length > 0) { + group = this.groups[groupIds[i]]; + // if bar graphs are stacked, their range need to be handled differently and accumulated over all groups. + if (options.barChart.handleOverlap == 'stack' && options.style == 'bar') { + if (options.yAxisOrientation == 'left') {barCombinedDataLeft = barCombinedDataLeft.concat(group.getYRange(groupData)) ;} + else {barCombinedDataRight = barCombinedDataRight.concat(group.getYRange(groupData));} + } + else { + groupRanges[groupIds[i]] = group.getYRange(groupData,groupIds[i]); + } + } } - } - else { - this.parent = parent; - } - }; - - /** - * Check whether this item is visible inside given range - * @returns {{start: Number, end: Number}} range with a timestamp for start and end - * @returns {boolean} True if visible - */ - Item.prototype.isVisible = function(range) { - // Should be implemented by Item implementations - return false; - }; - - /** - * Show the Item in the DOM (when not already visible) - * @return {Boolean} changed - */ - Item.prototype.show = function() { - return false; - }; - - /** - * Hide the Item from the DOM (when visible) - * @return {Boolean} changed - */ - Item.prototype.hide = function() { - return false; - }; - - /** - * Repaint the item - */ - Item.prototype.redraw = function() { - // should be implemented by the item - }; - /** - * Reposition the Item horizontally - */ - Item.prototype.repositionX = function() { - // should be implemented by the item + // if bar graphs are stacked, their range need to be handled differently and accumulated over all groups. + BarGraphFunctions.getStackedBarYRange(barCombinedDataLeft , groupRanges, groupIds, '__barchartLeft' , 'left' ); + BarGraphFunctions.getStackedBarYRange(barCombinedDataRight, groupRanges, groupIds, '__barchartRight', 'right'); + } }; - /** - * Reposition the Item vertically - */ - Item.prototype.repositionY = function() { - // should be implemented by the item - }; /** - * Repaint a delete button on the top right of the item when the item is selected - * @param {HTMLElement} anchor - * @protected + * this sets the Y ranges for the Y axis. It also determines which of the axis should be shown or hidden. + * @param {Array} groupIds + * @param {Object} groupRanges + * @private */ - Item.prototype._repaintDeleteButton = function (anchor) { - if (this.selected && this.options.editable.remove && !this.dom.deleteButton) { - // create and show button - var me = this; + LineGraph.prototype._updateYAxis = function (groupIds, groupRanges) { + var resized = false; + var yAxisLeftUsed = false; + var yAxisRightUsed = false; + var minLeft = 1e9, minRight = 1e9, maxLeft = -1e9, maxRight = -1e9, minVal, maxVal; + // if groups are present + if (groupIds.length > 0) { + // this is here to make sure that if there are no items in the axis but there are groups, that there is no infinite draw/redraw loop. + for (var i = 0; i < groupIds.length; i++) { + var group = this.groups[groupIds[i]]; + if (group && group.options.yAxisOrientation != 'right') { + yAxisLeftUsed = true; + minLeft = 0; + maxLeft = 0; + } + else if (group && group.options.yAxisOrientation) { + yAxisRightUsed = true; + minRight = 0; + maxRight = 0; + } + } - var deleteButton = document.createElement('div'); - deleteButton.className = 'delete'; - deleteButton.title = 'Delete this item'; + // if there are items: + for (var i = 0; i < groupIds.length; i++) { + if (groupRanges.hasOwnProperty(groupIds[i])) { + if (groupRanges[groupIds[i]].ignore !== true) { + minVal = groupRanges[groupIds[i]].min; + maxVal = groupRanges[groupIds[i]].max; - Hammer(deleteButton, { - preventDefault: true - }).on('tap', function (event) { - me.parent.removeFromDataSet(me); - event.stopPropagation(); - }); + if (groupRanges[groupIds[i]].yAxisOrientation != 'right') { + yAxisLeftUsed = true; + minLeft = minLeft > minVal ? minVal : minLeft; + maxLeft = maxLeft < maxVal ? maxVal : maxLeft; + } + else { + yAxisRightUsed = true; + minRight = minRight > minVal ? minVal : minRight; + maxRight = maxRight < maxVal ? maxVal : maxRight; + } + } + } + } - anchor.appendChild(deleteButton); - this.dom.deleteButton = deleteButton; - } - else if (!this.selected && this.dom.deleteButton) { - // remove button - if (this.dom.deleteButton.parentNode) { - this.dom.deleteButton.parentNode.removeChild(this.dom.deleteButton); + if (yAxisLeftUsed == true) { + this.yAxisLeft.setRange(minLeft, maxLeft); + } + if (yAxisRightUsed == true) { + this.yAxisRight.setRange(minRight, maxRight); } - this.dom.deleteButton = null; } - }; + resized = this._toggleAxisVisiblity(yAxisLeftUsed , this.yAxisLeft) || resized; + resized = this._toggleAxisVisiblity(yAxisRightUsed, this.yAxisRight) || resized; - /** - * Set HTML contents for the item - * @param {Element} element HTML element to fill with the contents - * @private - */ - Item.prototype._updateContents = function (element) { - var content; - if (this.options.template) { - var itemData = this.parent.itemSet.itemsData.get(this.id); // get a clone of the data from the dataset - content = this.options.template(itemData); + if (yAxisRightUsed == true && yAxisLeftUsed == true) { + this.yAxisLeft.drawIcons = true; + this.yAxisRight.drawIcons = true; } else { - content = this.data.content; + this.yAxisLeft.drawIcons = false; + this.yAxisRight.drawIcons = false; } + this.yAxisRight.master = !yAxisLeftUsed; + if (this.yAxisRight.master == false) { + if (yAxisRightUsed == true) {this.yAxisLeft.lineOffset = this.yAxisRight.width;} + else {this.yAxisLeft.lineOffset = 0;} - if(content !== this.content) { - // only replace the content when changed - if (content instanceof Element) { - element.innerHTML = ''; - element.appendChild(content); - } - else if (content != undefined) { - element.innerHTML = content; - } - else { - if (!(this.data.type == 'background' && this.data.content === undefined)) { - throw new Error('Property "content" missing in item ' + this.id); - } - } + resized = this.yAxisLeft.redraw() || resized; + this.yAxisRight.stepPixelsForced = this.yAxisLeft.stepPixels; + this.yAxisRight.zeroCrossing = this.yAxisLeft.zeroCrossing; + resized = this.yAxisRight.redraw() || resized; + } + else { + resized = this.yAxisRight.redraw() || resized; + } - this.content = content; + // clean the accumulated lists + if (groupIds.indexOf('__barchartLeft') != -1) { + groupIds.splice(groupIds.indexOf('__barchartLeft'),1); + } + if (groupIds.indexOf('__barchartRight') != -1) { + groupIds.splice(groupIds.indexOf('__barchartRight'),1); } + + return resized; }; + /** - * Set HTML contents for the item - * @param {Element} element HTML element to fill with the contents + * This shows or hides the Y axis if needed. If there is a change, the changed event is emitted by the updateYAxis function + * + * @param {boolean} axisUsed + * @returns {boolean} * @private + * @param axis */ - Item.prototype._updateTitle = function (element) { - if (this.data.title != null) { - element.title = this.data.title || ''; + LineGraph.prototype._toggleAxisVisiblity = function (axisUsed, axis) { + var changed = false; + if (axisUsed == false) { + if (axis.dom.frame.parentNode && axis.hidden == false) { + axis.hide() + changed = true; + } } else { - element.removeAttribute('title'); + if (!axis.dom.frame.parentNode && axis.hidden == true) { + axis.show(); + changed = true; + } } + return changed; }; + /** - * Process dataAttributes timeline option and set as data- attributes on dom.content - * @param {Element} element HTML element to which the attributes will be attached + * This uses the DataAxis object to generate the correct X coordinate on the SVG window. It uses the + * util function toScreen to get the x coordinate from the timestamp. It also pre-filters the data and get the minMax ranges for + * the yAxis. + * + * @param datapoints + * @returns {Array} * @private */ - Item.prototype._updateDataAttributes = function(element) { - if (this.options.dataAttributes && this.options.dataAttributes.length > 0) { - var attributes = []; - - if (Array.isArray(this.options.dataAttributes)) { - attributes = this.options.dataAttributes; - } - else if (this.options.dataAttributes == 'all') { - attributes = Object.keys(this.data); - } - else { - return; - } - - for (var i = 0; i < attributes.length; i++) { - var name = attributes[i]; - var value = this.data[name]; + LineGraph.prototype._convertXcoordinates = function (datapoints) { + var extractedData = []; + var xValue, yValue; + var toScreen = this.body.util.toScreen; - if (value != null) { - element.setAttribute('data-' + name, value); - } - else { - element.removeAttribute('data-' + name); - } - } + for (var i = 0; i < datapoints.length; i++) { + xValue = toScreen(datapoints[i].x) + this.props.width; + yValue = datapoints[i].y; + extractedData.push({x: xValue, y: yValue}); } + + return extractedData; }; + /** - * Update custom styles of the element - * @param element + * This uses the DataAxis object to generate the correct X coordinate on the SVG window. It uses the + * util function toScreen to get the x coordinate from the timestamp. It also pre-filters the data and get the minMax ranges for + * the yAxis. + * + * @param datapoints + * @param group + * @returns {Array} * @private */ - Item.prototype._updateStyle = function(element) { - // remove old styles - if (this.style) { - util.removeCssText(element, this.style); - this.style = null; + LineGraph.prototype._convertYcoordinates = function (datapoints, group) { + var extractedData = []; + var xValue, yValue; + var toScreen = this.body.util.toScreen; + var axis = this.yAxisLeft; + var svgHeight = Number(this.svg.style.height.replace('px','')); + if (group.options.yAxisOrientation == 'right') { + axis = this.yAxisRight; } - // append new styles - if (this.data.style) { - util.addCssText(element, this.data.style); - this.style = this.data.style; + for (var i = 0; i < datapoints.length; i++) { + xValue = toScreen(datapoints[i].x) + this.props.width; + yValue = Math.round(axis.convertValue(datapoints[i].y)); + extractedData.push({x: xValue, y: yValue}); } + + group.setZeroPosition(Math.min(svgHeight, axis.convertValue(0))); + + return extractedData; }; - module.exports = Item; + + module.exports = LineGraph; /***/ }, -/* 31 */ +/* 35 */ /***/ function(module, exports, __webpack_require__) { var util = __webpack_require__(1); - var Group = __webpack_require__(27); + var Component = __webpack_require__(25); + var TimeStep = __webpack_require__(19); + var DateUtil = __webpack_require__(15); + var moment = __webpack_require__(44); /** - * @constructor BackgroundGroup - * @param {Number | String} groupId - * @param {Object} data - * @param {ItemSet} itemSet + * A horizontal time axis + * @param {{dom: Object, domProps: Object, emitter: Emitter, range: Range}} body + * @param {Object} [options] See TimeAxis.setOptions for the available + * options. + * @constructor TimeAxis + * @extends Component */ - function BackgroundGroup (groupId, data, itemSet) { - Group.call(this, groupId, data, itemSet); + function TimeAxis (body, options) { + this.dom = { + foreground: null, + lines: [], + majorTexts: [], + minorTexts: [], + redundant: { + lines: [], + majorTexts: [], + minorTexts: [] + } + }; + this.props = { + range: { + start: 0, + end: 0, + minimumStep: 0 + }, + lineTop: 0 + }; - this.width = 0; - this.height = 0; - this.top = 0; - this.left = 0; + this.defaultOptions = { + orientation: 'bottom', // supported: 'top', 'bottom' + // TODO: implement timeaxis orientations 'left' and 'right' + showMinorLabels: true, + showMajorLabels: true, + format: null, + timeAxis: null + }; + this.options = util.extend({}, this.defaultOptions); + + this.body = body; + + // create the HTML DOM + this._create(); + + this.setOptions(options); } - BackgroundGroup.prototype = Object.create(Group.prototype); + TimeAxis.prototype = new Component(); /** - * Repaint this group - * @param {{start: number, end: number}} range - * @param {{item: {horizontal: number, vertical: number}, axis: number}} margin - * @param {boolean} [restack=false] Force restacking of all items - * @return {boolean} Returns true if the group is resized + * Set options for the TimeAxis. + * Parameters will be merged in current options. + * @param {Object} options Available options: + * {string} [orientation] + * {boolean} [showMinorLabels] + * {boolean} [showMajorLabels] */ - BackgroundGroup.prototype.redraw = function(range, margin, restack) { - var resized = false; + TimeAxis.prototype.setOptions = function(options) { + if (options) { + // copy all options that we know + util.selectiveExtend([ + 'orientation', + 'showMinorLabels', + 'showMajorLabels', + 'hiddenDates', + 'format', + 'timeAxis' + ], this.options, options); - this.visibleItems = this._updateVisibleItems(this.orderedItems, this.visibleItems, range); + // apply locale to moment.js + // TODO: not so nice, this is applied globally to moment.js + if ('locale' in options) { + if (typeof moment.locale === 'function') { + // moment.js 2.8.1+ + moment.locale(options.locale); + } + else { + moment.lang(options.locale); + } + } + } + }; - // calculate actual size - this.width = this.dom.background.offsetWidth; + /** + * Create the HTML DOM for the TimeAxis + */ + TimeAxis.prototype._create = function() { + this.dom.foreground = document.createElement('div'); + this.dom.background = document.createElement('div'); - // apply new height (just always zero for BackgroundGroup - this.dom.background.style.height = '0'; + this.dom.foreground.className = 'timeaxis foreground'; + this.dom.background.className = 'timeaxis background'; + }; - // update vertical position of items after they are re-stacked and the height of the group is calculated - for (var i = 0, ii = this.visibleItems.length; i < ii; i++) { - var item = this.visibleItems[i]; - item.repositionY(margin); + /** + * Destroy the TimeAxis + */ + TimeAxis.prototype.destroy = function() { + // remove from DOM + if (this.dom.foreground.parentNode) { + this.dom.foreground.parentNode.removeChild(this.dom.foreground); + } + if (this.dom.background.parentNode) { + this.dom.background.parentNode.removeChild(this.dom.background); } - return resized; + this.body = null; }; /** - * Show this group: attach to the DOM + * Repaint the component + * @return {boolean} Returns true if the component is resized */ - BackgroundGroup.prototype.show = function() { - if (!this.dom.background.parentNode) { - this.itemSet.dom.background.appendChild(this.dom.background); - } - }; + TimeAxis.prototype.redraw = function () { + var options = this.options; + var props = this.props; + var foreground = this.dom.foreground; + var background = this.dom.background; - module.exports = BackgroundGroup; + // determine the correct parent DOM element (depending on option orientation) + var parent = (options.orientation == 'top') ? this.body.dom.top : this.body.dom.bottom; + var parentChanged = (foreground.parentNode !== parent); + // calculate character width and height + this._calculateCharSize(); -/***/ }, -/* 32 */ -/***/ function(module, exports, __webpack_require__) { + // TODO: recalculate sizes only needed when parent is resized or options is changed + var orientation = this.options.orientation, + showMinorLabels = this.options.showMinorLabels, + showMajorLabels = this.options.showMajorLabels; - var Item = __webpack_require__(30); - var util = __webpack_require__(1); + // determine the width and height of the elemens for the axis + props.minorLabelHeight = showMinorLabels ? props.minorCharHeight : 0; + props.majorLabelHeight = showMajorLabels ? props.majorCharHeight : 0; + props.height = props.minorLabelHeight + props.majorLabelHeight; + props.width = foreground.offsetWidth; - /** - * @constructor BoxItem - * @extends Item - * @param {Object} data Object containing parameters start - * content, className. - * @param {{toScreen: function, toTime: function}} conversion - * Conversion functions from time to screen and vice versa - * @param {Object} [options] Configuration options - * // TODO: describe available options - */ - function BoxItem (data, conversion, options) { - this.props = { - dot: { - width: 0, - height: 0 - }, - line: { - width: 0, - height: 0 - } - }; + props.minorLineHeight = this.body.domProps.root.height - props.majorLabelHeight - + (options.orientation == 'top' ? this.body.domProps.bottom.height : this.body.domProps.top.height); + props.minorLineWidth = 1; // TODO: really calculate width + props.majorLineHeight = props.minorLineHeight + props.majorLabelHeight; + props.majorLineWidth = 1; // TODO: really calculate width - // validate data - if (data) { - if (data.start == undefined) { - throw new Error('Property "start" missing in item ' + data); - } - } + // take foreground and background offline while updating (is almost twice as fast) + var foregroundNextSibling = foreground.nextSibling; + var backgroundNextSibling = background.nextSibling; + foreground.parentNode && foreground.parentNode.removeChild(foreground); + background.parentNode && background.parentNode.removeChild(background); - Item.call(this, data, conversion, options); - } + foreground.style.height = this.props.height + 'px'; - BoxItem.prototype = new Item (null, null, null); + this._repaintLabels(); - /** - * Check whether this item is visible inside given range - * @returns {{start: Number, end: Number}} range with a timestamp for start and end - * @returns {boolean} True if visible - */ - BoxItem.prototype.isVisible = function(range) { - // determine visibility - // TODO: account for the real width of the item. Right now we just add 1/4 to the window - var interval = (range.end - range.start) / 4; - return (this.data.start > range.start - interval) && (this.data.start < range.end + interval); + // put DOM online again (at the same place) + if (foregroundNextSibling) { + parent.insertBefore(foreground, foregroundNextSibling); + } + else { + parent.appendChild(foreground) + } + if (backgroundNextSibling) { + this.body.dom.backgroundVertical.insertBefore(background, backgroundNextSibling); + } + else { + this.body.dom.backgroundVertical.appendChild(background) + } + + return this._isResized() || parentChanged; }; /** - * Repaint the item + * Repaint major and minor text labels and vertical grid lines + * @private */ - BoxItem.prototype.redraw = function() { + TimeAxis.prototype._repaintLabels = function () { + var orientation = this.options.orientation; + + // calculate range and step (step such that we have space for 7 characters per label) + var start = util.convert(this.body.range.start, 'Number'); + var end = util.convert(this.body.range.end, 'Number'); + var timeLabelsize = this.body.util.toTime((this.props.minorCharWidth || 10) * 7).valueOf(); + var minimumStep = timeLabelsize - DateUtil.getHiddenDurationBefore(this.body.hiddenDates, this.body.range, timeLabelsize); + minimumStep -= this.body.util.toTime(0).valueOf(); + + var step = new TimeStep(new Date(start), new Date(end), minimumStep, this.body.hiddenDates); + if (this.options.format) { + step.setFormat(this.options.format); + } + if (this.options.timeAxis) { + step.setScale(this.options.timeAxis); + } + this.step = step; + + // Move all DOM elements to a "redundant" list, where they + // can be picked for re-use, and clear the lists with lines and texts. + // At the end of the function _repaintLabels, left over elements will be cleaned up var dom = this.dom; - if (!dom) { - // create DOM - this.dom = {}; - dom = this.dom; + dom.redundant.lines = dom.lines; + dom.redundant.majorTexts = dom.majorTexts; + dom.redundant.minorTexts = dom.minorTexts; + dom.lines = []; + dom.majorTexts = []; + dom.minorTexts = []; - // create main box - dom.box = document.createElement('DIV'); + var cur; + var x = 0; + var isMajor; + var xPrev = 0; + var width = 0; + var prevLine; + var xFirstMajorLabel = undefined; + var max = 0; + var className; - // contents box (inside the background box). used for making margins - dom.content = document.createElement('DIV'); - dom.content.className = 'content'; - dom.box.appendChild(dom.content); + step.first(); + while (step.hasNext() && max < 1000) { + max++; - // line to axis - dom.line = document.createElement('DIV'); - dom.line.className = 'line'; + cur = step.getCurrent(); + isMajor = step.isMajor(); + className = step.getClassName(); - // dot on axis - dom.dot = document.createElement('DIV'); - dom.dot.className = 'dot'; + xPrev = x; + x = this.body.util.toScreen(cur); + width = x - xPrev; + if (prevLine) { + prevLine.style.width = width + 'px'; + } - // attach this item as attribute - dom.box['timeline-item'] = this; + if (this.options.showMinorLabels) { + this._repaintMinorText(x, step.getLabelMinor(), orientation, className); + } - this.dirty = true; - } + if (isMajor && this.options.showMajorLabels) { + if (x > 0) { + if (xFirstMajorLabel == undefined) { + xFirstMajorLabel = x; + } + this._repaintMajorText(x, step.getLabelMajor(), orientation, className); + } + prevLine = this._repaintMajorLine(x, orientation, className); + } + else { + prevLine = this._repaintMinorLine(x, orientation, className); + } - // append DOM to parent DOM - if (!this.parent) { - throw new Error('Cannot redraw item: no parent attached'); - } - if (!dom.box.parentNode) { - var foreground = this.parent.dom.foreground; - if (!foreground) throw new Error('Cannot redraw item: parent has no foreground container element'); - foreground.appendChild(dom.box); - } - if (!dom.line.parentNode) { - var background = this.parent.dom.background; - if (!background) throw new Error('Cannot redraw item: parent has no background container element'); - background.appendChild(dom.line); - } - if (!dom.dot.parentNode) { - var axis = this.parent.dom.axis; - if (!background) throw new Error('Cannot redraw item: parent has no axis container element'); - axis.appendChild(dom.dot); + step.next(); } - this.displayed = true; - // Update DOM when item is marked dirty. An item is marked dirty when: - // - the item is not yet rendered - // - the item's data is changed - // - the item is selected/deselected - if (this.dirty) { - this._updateContents(this.dom.content); - this._updateTitle(this.dom.box); - this._updateDataAttributes(this.dom.box); - this._updateStyle(this.dom.box); + // create a major label on the left when needed + if (this.options.showMajorLabels) { + var leftTime = this.body.util.toTime(0), + leftText = step.getLabelMajor(leftTime), + widthText = leftText.length * (this.props.majorCharWidth || 10) + 10; // upper bound estimation - // update class - var className = (this.data.className? ' ' + this.data.className : '') + - (this.selected ? ' selected' : ''); - dom.box.className = 'item box' + className; - dom.line.className = 'item line' + className; - dom.dot.className = 'item dot' + className; + if (xFirstMajorLabel == undefined || widthText < xFirstMajorLabel) { + this._repaintMajorText(0, leftText, orientation, className); + } + } - // recalculate size - this.props.dot.height = dom.dot.offsetHeight; - this.props.dot.width = dom.dot.offsetWidth; - this.props.line.width = dom.line.offsetWidth; - this.width = dom.box.offsetWidth; - this.height = dom.box.offsetHeight; + // Cleanup leftover DOM elements from the redundant list + util.forEach(this.dom.redundant, function (arr) { + while (arr.length) { + var elem = arr.pop(); + if (elem && elem.parentNode) { + elem.parentNode.removeChild(elem); + } + } + }); + }; - this.dirty = false; + /** + * Create a minor label for the axis at position x + * @param {Number} x + * @param {String} text + * @param {String} orientation "top" or "bottom" (default) + * @param {String} className + * @private + */ + TimeAxis.prototype._repaintMinorText = function (x, text, orientation, className) { + // reuse redundant label + var label = this.dom.redundant.minorTexts.shift(); + + if (!label) { + // create new label + var content = document.createTextNode(''); + label = document.createElement('div'); + label.appendChild(content); + this.dom.foreground.appendChild(label); } + this.dom.minorTexts.push(label); - this._repaintDeleteButton(dom.box); + label.childNodes[0].nodeValue = text; + + label.style.top = (orientation == 'top') ? (this.props.majorLabelHeight + 'px') : '0'; + label.style.left = x + 'px'; + label.className = 'text minor ' + className; + //label.title = title; // TODO: this is a heavy operation }; /** - * Show the item in the DOM (when not already displayed). The items DOM will - * be created when needed. + * Create a Major label for the axis at position x + * @param {Number} x + * @param {String} text + * @param {String} orientation "top" or "bottom" (default) + * @param {String} className + * @private */ - BoxItem.prototype.show = function() { - if (!this.displayed) { - this.redraw(); + TimeAxis.prototype._repaintMajorText = function (x, text, orientation, className) { + // reuse redundant label + var label = this.dom.redundant.majorTexts.shift(); + + if (!label) { + // create label + var content = document.createTextNode(text); + label = document.createElement('div'); + label.appendChild(content); + this.dom.foreground.appendChild(label); } + this.dom.majorTexts.push(label); + + label.childNodes[0].nodeValue = text; + label.className = 'text major ' + className; + //label.title = title; // TODO: this is a heavy operation + + label.style.top = (orientation == 'top') ? '0' : (this.props.minorLabelHeight + 'px'); + label.style.left = x + 'px'; }; /** - * Hide the item from the DOM (when visible) + * Create a minor line for the axis at position x + * @param {Number} x + * @param {String} orientation "top" or "bottom" (default) + * @param {String} className + * @return {Element} Returns the created line + * @private */ - BoxItem.prototype.hide = function() { - if (this.displayed) { - var dom = this.dom; + TimeAxis.prototype._repaintMinorLine = function (x, orientation, className) { + // reuse redundant line + var line = this.dom.redundant.lines.shift(); + if (!line) { + // create vertical line + line = document.createElement('div'); + this.dom.background.appendChild(line); + } + this.dom.lines.push(line); - if (dom.box.parentNode) dom.box.parentNode.removeChild(dom.box); - if (dom.line.parentNode) dom.line.parentNode.removeChild(dom.line); - if (dom.dot.parentNode) dom.dot.parentNode.removeChild(dom.dot); + var props = this.props; + if (orientation == 'top') { + line.style.top = props.majorLabelHeight + 'px'; + } + else { + line.style.top = this.body.domProps.top.height + 'px'; + } + line.style.height = props.minorLineHeight + 'px'; + line.style.left = (x - props.minorLineWidth / 2) + 'px'; - this.top = null; - this.left = null; + line.className = 'grid vertical minor ' + className; - this.displayed = false; - } + return line; }; /** - * Reposition the item horizontally - * @Override + * Create a Major line for the axis at position x + * @param {Number} x + * @param {String} orientation "top" or "bottom" (default) + * @param {String} className + * @return {Element} Returns the created line + * @private */ - BoxItem.prototype.repositionX = function() { - var start = this.conversion.toScreen(this.data.start); - var align = this.options.align; - var left; - var box = this.dom.box; - var line = this.dom.line; - var dot = this.dom.dot; - - // calculate left position of the box - if (align == 'right') { - this.left = start - this.width; + TimeAxis.prototype._repaintMajorLine = function (x, orientation, className) { + // reuse redundant line + var line = this.dom.redundant.lines.shift(); + if (!line) { + // create vertical line + line = document.createElement('div'); + this.dom.background.appendChild(line); } - else if (align == 'left') { - this.left = start; + this.dom.lines.push(line); + + var props = this.props; + if (orientation == 'top') { + line.style.top = '0'; } else { - // default or 'center' - this.left = start - this.width / 2; + line.style.top = this.body.domProps.top.height + 'px'; } + line.style.left = (x - props.majorLineWidth / 2) + 'px'; + line.style.height = props.majorLineHeight + 'px'; - // reposition box - box.style.left = this.left + 'px'; - - // reposition line - line.style.left = (start - this.props.line.width / 2) + 'px'; + line.className = 'grid vertical major ' + className; - // reposition dot - dot.style.left = (start - this.props.dot.width / 2) + 'px'; + return line; }; /** - * Reposition the item vertically - * @Override + * Determine the size of text on the axis (both major and minor axis). + * The size is calculated only once and then cached in this.props. + * @private */ - BoxItem.prototype.repositionY = function() { - var orientation = this.options.orientation; - var box = this.dom.box; - var line = this.dom.line; - var dot = this.dom.dot; + TimeAxis.prototype._calculateCharSize = function () { + // Note: We calculate char size with every redraw. Size may change, for + // example when any of the timelines parents had display:none for example. - if (orientation == 'top') { - box.style.top = (this.top || 0) + 'px'; + // determine the char width and height on the minor axis + if (!this.dom.measureCharMinor) { + this.dom.measureCharMinor = document.createElement('DIV'); + this.dom.measureCharMinor.className = 'text minor measure'; + this.dom.measureCharMinor.style.position = 'absolute'; - line.style.top = '0'; - line.style.height = (this.parent.top + this.top + 1) + 'px'; - line.style.bottom = ''; + this.dom.measureCharMinor.appendChild(document.createTextNode('0')); + this.dom.foreground.appendChild(this.dom.measureCharMinor); } - else { // orientation 'bottom' - var itemSetHeight = this.parent.itemSet.props.height; // TODO: this is nasty - var lineHeight = itemSetHeight - this.parent.top - this.parent.height + this.top; + this.props.minorCharHeight = this.dom.measureCharMinor.clientHeight; + this.props.minorCharWidth = this.dom.measureCharMinor.clientWidth; - box.style.top = (this.parent.height - this.top - this.height || 0) + 'px'; - line.style.top = (itemSetHeight - lineHeight) + 'px'; - line.style.bottom = '0'; + // determine the char width and height on the major axis + if (!this.dom.measureCharMajor) { + this.dom.measureCharMajor = document.createElement('DIV'); + this.dom.measureCharMajor.className = 'text major measure'; + this.dom.measureCharMajor.style.position = 'absolute'; + + this.dom.measureCharMajor.appendChild(document.createTextNode('0')); + this.dom.foreground.appendChild(this.dom.measureCharMajor); } + this.props.majorCharHeight = this.dom.measureCharMajor.clientHeight; + this.props.majorCharWidth = this.dom.measureCharMajor.clientWidth; + }; - dot.style.top = (-this.props.dot.height / 2) + 'px'; + /** + * Snap a date to a rounded value. + * The snap intervals are dependent on the current scale and step. + * @param {Date} date the date to be snapped. + * @return {Date} snappedDate + */ + TimeAxis.prototype.snap = function(date) { + return this.step.snap(date); }; - module.exports = BoxItem; + module.exports = TimeAxis; /***/ }, -/* 33 */ +/* 36 */ /***/ function(module, exports, __webpack_require__) { - var Item = __webpack_require__(30); + var Emitter = __webpack_require__(56); + var Hammer = __webpack_require__(45); + var keycharm = __webpack_require__(57); + var util = __webpack_require__(1); + var hammerUtil = __webpack_require__(47); + var DataSet = __webpack_require__(3); + var DataView = __webpack_require__(4); + var dotparser = __webpack_require__(42); + var gephiParser = __webpack_require__(43); + var Groups = __webpack_require__(38); + var Images = __webpack_require__(39); + var Node = __webpack_require__(40); + var Edge = __webpack_require__(37); + var Popup = __webpack_require__(41); + var MixinLoader = __webpack_require__(52); + var Activator = __webpack_require__(53); + var locales = __webpack_require__(54); - /** - * @constructor PointItem - * @extends Item - * @param {Object} data Object containing parameters start - * content, className. - * @param {{toScreen: function, toTime: function}} conversion - * Conversion functions from time to screen and vice versa - * @param {Object} [options] Configuration options - * // TODO: describe available options + // Load custom shapes into CanvasRenderingContext2D + __webpack_require__(55); + + /** + * @constructor Network + * Create a network visualization, displaying nodes and edges. + * + * @param {Element} container The DOM element in which the Network will + * be created. Normally a div element. + * @param {Object} data An object containing parameters + * {Array} nodes + * {Array} edges + * @param {Object} options Options */ - function PointItem (data, conversion, options) { - this.props = { - dot: { - top: 0, - width: 0, - height: 0 + function Network (container, data, options) { + if (!(this instanceof Network)) { + throw new SyntaxError('Constructor must be called with the new operator'); + } + + this._determineBrowserMethod(); + this._initializeMixinLoaders(); + + // create variables and set default values + this.containerElement = container; + + // render and calculation settings + this.renderRefreshRate = 60; // hz (fps) + this.renderTimestep = 1000 / this.renderRefreshRate; // ms -- saves calculation later on + this.renderTime = 0; // measured time it takes to render a frame + this.physicsTime = 0; // measured time it takes to render a frame + this.runDoubleSpeed = false; + this.physicsDiscreteStepsize = 0.50; // discrete stepsize of the simulation + + this.initializing = true; + + this.triggerFunctions = {add:null,edit:null,editEdge:null,connect:null,del:null}; + + // set constant values + this.defaultOptions = { + nodes: { + mass: 1, + radiusMin: 10, + radiusMax: 30, + radius: 10, + shape: 'ellipse', + image: undefined, + widthMin: 16, // px + widthMax: 64, // px + fontColor: 'black', + fontSize: 14, // px + fontFace: 'verdana', + fontFill: undefined, + fontStrokeWidth: 0, // px + fontStrokeColor: 'white', + level: -1, + color: { + border: '#2B7CE9', + background: '#97C2FC', + highlight: { + border: '#2B7CE9', + background: '#D2E5FF' + }, + hover: { + border: '#2B7CE9', + background: '#D2E5FF' + } + }, + group: undefined, + borderWidth: 1, + borderWidthSelected: undefined }, - content: { - height: 0, - marginLeft: 0 - } + edges: { + widthMin: 1, // + widthMax: 15,// + width: 1, + widthSelectionMultiplier: 2, + hoverWidth: 1.5, + style: 'line', + color: { + color:'#848484', + highlight:'#848484', + hover: '#848484' + }, + fontColor: '#343434', + fontSize: 14, // px + fontFace: 'arial', + fontFill: 'white', + fontStrokeWidth: 0, // px + fontStrokeColor: 'white', + labelAlignment:'horizontal', + arrowScaleFactor: 1, + dash: { + length: 10, + gap: 5, + altLength: undefined + }, + inheritColor: "from" // to, from, false, true (== from) + }, + configurePhysics:false, + physics: { + barnesHut: { + enabled: true, + thetaInverted: 1 / 0.5, // inverted to save time during calculation + gravitationalConstant: -2000, + centralGravity: 0.3, + springLength: 95, + springConstant: 0.04, + damping: 0.09 + }, + repulsion: { + centralGravity: 0.0, + springLength: 200, + springConstant: 0.05, + nodeDistance: 100, + damping: 0.09 + }, + hierarchicalRepulsion: { + enabled: false, + centralGravity: 0.0, + springLength: 100, + springConstant: 0.01, + nodeDistance: 150, + damping: 0.09 + }, + damping: null, + centralGravity: null, + springLength: null, + springConstant: null + }, + clustering: { // Per Node in Cluster = PNiC + enabled: false, // (Boolean) | global on/off switch for clustering. + initialMaxNodes: 100, // (# nodes) | if the initial amount of nodes is larger than this, we cluster until the total number is less than this threshold. + clusterThreshold:500, // (# nodes) | during calculate forces, we check if the total number of nodes is larger than this. If it is, cluster until reduced to reduceToNodes + reduceToNodes:300, // (# nodes) | during calculate forces, we check if the total number of nodes is larger than clusterThreshold. If it is, cluster until reduced to this + chainThreshold: 0.4, // (% of all drawn nodes)| maximum percentage of allowed chainnodes (long strings of connected nodes) within all nodes. (lower means less chains). + clusterEdgeThreshold: 20, // (px) | edge length threshold. if smaller, this node is clustered. + sectorThreshold: 100, // (# nodes in cluster) | cluster size threshold. If larger, expanding in own sector. + screenSizeThreshold: 0.2, // (% of canvas) | relative size threshold. If the width or height of a clusternode takes up this much of the screen, decluster node. + fontSizeMultiplier: 4.0, // (px PNiC) | how much the cluster font size grows per node in cluster (in px). + maxFontSize: 1000, + forceAmplification: 0.1, // (multiplier PNiC) | factor of increase fo the repulsion force of a cluster (per node in cluster). + distanceAmplification: 0.1, // (multiplier PNiC) | factor how much the repulsion distance of a cluster increases (per node in cluster). + edgeGrowth: 20, // (px PNiC) | amount of clusterSize connected to the edge is multiplied with this and added to edgeLength. + nodeScaling: {width: 1, // (px PNiC) | growth of the width per node in cluster. + height: 1, // (px PNiC) | growth of the height per node in cluster. + radius: 1}, // (px PNiC) | growth of the radius per node in cluster. + maxNodeSizeIncrements: 600, // (# increments) | max growth of the width per node in cluster. + activeAreaBoxSize: 80, // (px) | box area around the curser where clusters are popped open. + clusterLevelDifference: 2, + clusterByZoom: true // enable clustering through zooming in and out + }, + navigation: { + enabled: false + }, + keyboard: { + enabled: false, + speed: {x: 10, y: 10, zoom: 0.02}, + bindToWindow: true + }, + dataManipulation: { + enabled: false, + initiallyVisible: false + }, + hierarchicalLayout: { + enabled:false, + levelSeparation: 150, + nodeSpacing: 100, + direction: "UD", // UD, DU, LR, RL + layout: "hubsize" // hubsize, directed + }, + freezeForStabilization: false, + smoothCurves: { + enabled: true, + dynamic: true, + type: "continuous", + roundness: 0.5 + }, + maxVelocity: 30, + minVelocity: 0.1, // px/s + stabilize: true, // stabilize before displaying the network + stabilizationIterations: 1000, // maximum number of iteration to stabilize + zoomExtentOnStabilize: true, + locale: 'en', + locales: locales, + tooltip: { + delay: 300, + fontColor: 'black', + fontSize: 14, // px + fontFace: 'verdana', + color: { + border: '#666', + background: '#FFFFC6' + } + }, + dragNetwork: true, + dragNodes: true, + zoomable: true, + hover: false, + hideEdgesOnDrag: false, + hideNodesOnDrag: false, + width : '100%', + height : '100%', + selectable: true }; + this.constants = util.extend({}, this.defaultOptions); + this.pixelRatio = 1; + + + this.hoverObj = {nodes:{},edges:{}}; + this.controlNodesActive = false; + this.navigationHammers = {existing:[], _new: []}; - // validate data - if (data) { - if (data.start == undefined) { - throw new Error('Property "start" missing in item ' + data); - } - } + // animation properties + this.animationSpeed = 1/this.renderRefreshRate; + this.animationEasingFunction = "easeInOutQuint"; + this.animating = false; + this.easingTime = 0; + this.sourceScale = 0; + this.targetScale = 0; + this.sourceTranslation = 0; + this.targetTranslation = 0; + this.lockedOnNodeId = null; + this.lockedOnNodeOffset = null; + this.touchTime = 0; - Item.call(this, data, conversion, options); - } + // Node variables + var network = this; + this.groups = new Groups(); // object with groups + this.images = new Images(); // object with images + this.images.setOnloadCallback(function (status) { + network._redraw(); + }); - PointItem.prototype = new Item (null, null, null); + // keyboard navigation variables + this.xIncrement = 0; + this.yIncrement = 0; + this.zoomIncrement = 0; - /** - * Check whether this item is visible inside given range - * @returns {{start: Number, end: Number}} range with a timestamp for start and end - * @returns {boolean} True if visible - */ - PointItem.prototype.isVisible = function(range) { - // determine visibility - // TODO: account for the real width of the item. Right now we just add 1/4 to the window - var interval = (range.end - range.start) / 4; - return (this.data.start > range.start - interval) && (this.data.start < range.end + interval); - }; + // loading all the mixins: + // load the force calculation functions, grouped under the physics system. + this._loadPhysicsSystem(); + // create a frame and canvas + this._create(); + // load the sector system. (mandatory, fully integrated with Network) + this._loadSectorSystem(); + // load the cluster system. (mandatory, even when not using the cluster system, there are function calls to it) + this._loadClusterSystem(); + // load the selection system. (mandatory, required by Network) + this._loadSelectionSystem(); + // load the selection system. (mandatory, required by Network) + this._loadHierarchySystem(); - /** - * Repaint the item - */ - PointItem.prototype.redraw = function() { - var dom = this.dom; - if (!dom) { - // create DOM - this.dom = {}; - dom = this.dom; - // background box - dom.point = document.createElement('div'); - // className is updated in redraw() + // apply options + this._setTranslation(this.frame.clientWidth / 2, this.frame.clientHeight / 2); + this._setScale(1); + this.setOptions(options); - // contents box, right from the dot - dom.content = document.createElement('div'); - dom.content.className = 'content'; - dom.point.appendChild(dom.content); + // other vars + this.freezeSimulation = false;// freeze the simulation + this.cachedFunctions = {}; + this.startedStabilization = false; + this.stabilized = false; + this.stabilizationIterations = null; + this.draggingNodes = false; - // dot at start - dom.dot = document.createElement('div'); - dom.point.appendChild(dom.dot); + // containers for nodes and edges + this.calculationNodes = {}; + this.calculationNodeIndices = []; + this.nodeIndices = []; // array with all the indices of the nodes. Used to speed up forces calculation + this.nodes = {}; // object with Node objects + this.edges = {}; // object with Edge objects - // attach this item as attribute - dom.point['timeline-item'] = this; + // position and scale variables and objects + this.canvasTopLeft = {"x": 0,"y": 0}; // coordinates of the top left of the canvas. they will be set during _redraw. + this.canvasBottomRight = {"x": 0,"y": 0}; // coordinates of the bottom right of the canvas. they will be set during _redraw + this.pointerPosition = {"x": 0,"y": 0}; // coordinates of the bottom right of the canvas. they will be set during _redraw + this.areaCenter = {}; // object with x and y elements used for determining the center of the zoom action + this.scale = 1; // defining the global scale variable in the constructor + this.previousScale = this.scale; // this is used to check if the zoom operation is zooming in or out - this.dirty = true; - } + // datasets or dataviews + this.nodesData = null; // A DataSet or DataView + this.edgesData = null; // A DataSet or DataView - // append DOM to parent DOM - if (!this.parent) { - throw new Error('Cannot redraw item: no parent attached'); - } - if (!dom.point.parentNode) { - var foreground = this.parent.dom.foreground; - if (!foreground) { - throw new Error('Cannot redraw item: parent has no foreground container element'); + // create event listeners used to subscribe on the DataSets of the nodes and edges + this.nodesListeners = { + 'add': function (event, params) { + network._addNodes(params.items); + network.start(); + }, + 'update': function (event, params) { + network._updateNodes(params.items, params.data); + network.start(); + }, + 'remove': function (event, params) { + network._removeNodes(params.items); + network.start(); } - foreground.appendChild(dom.point); - } - this.displayed = true; - - // Update DOM when item is marked dirty. An item is marked dirty when: - // - the item is not yet rendered - // - the item's data is changed - // - the item is selected/deselected - if (this.dirty) { - this._updateContents(this.dom.content); - this._updateTitle(this.dom.point); - this._updateDataAttributes(this.dom.point); - this._updateStyle(this.dom.point); - - // update class - var className = (this.data.className? ' ' + this.data.className : '') + - (this.selected ? ' selected' : ''); - dom.point.className = 'item point' + className; - dom.dot.className = 'item dot' + className; + }; + this.edgesListeners = { + 'add': function (event, params) { + network._addEdges(params.items); + network.start(); + }, + 'update': function (event, params) { + network._updateEdges(params.items); + network.start(); + }, + 'remove': function (event, params) { + network._removeEdges(params.items); + network.start(); + } + }; - // recalculate size - this.width = dom.point.offsetWidth; - this.height = dom.point.offsetHeight; - this.props.dot.width = dom.dot.offsetWidth; - this.props.dot.height = dom.dot.offsetHeight; - this.props.content.height = dom.content.offsetHeight; + // properties for the animation + this.moving = true; + this.timer = undefined; // Scheduling function. Is definded in this.start(); - // resize contents - dom.content.style.marginLeft = 2 * this.props.dot.width + 'px'; - //dom.content.style.marginRight = ... + 'px'; // TODO: margin right + // load data (the disable start variable will be the same as the enabled clustering) + this.setData(data,this.constants.clustering.enabled || this.constants.hierarchicalLayout.enabled); - dom.dot.style.top = ((this.height - this.props.dot.height) / 2) + 'px'; - dom.dot.style.left = (this.props.dot.width / 2) + 'px'; + // hierarchical layout + this.initializing = false; + if (this.constants.hierarchicalLayout.enabled == true) { + this._setupHierarchicalLayout(); + } + else { + // zoom so all data will fit on the screen, if clustering is enabled, we do not want start to be called here. + if (this.constants.stabilize == false) { + this.zoomExtent(undefined, true,this.constants.clustering.enabled); + } + } - this.dirty = false; + // if clustering is disabled, the simulation will have started in the setData function + if (this.constants.clustering.enabled) { + this.startWithClustering(); } + } - this._repaintDeleteButton(dom.point); - }; + // Extend Network with an Emitter mixin + Emitter(Network.prototype); /** - * Show the item in the DOM (when not already visible). The items DOM will - * be created when needed. + * Determine if the browser requires a setTimeout or a requestAnimationFrame. This was required because + * some implementations (safari and IE9) did not support requestAnimationFrame + * @private */ - PointItem.prototype.show = function() { - if (!this.displayed) { - this.redraw(); + Network.prototype._determineBrowserMethod = function() { + var browserType = navigator.userAgent.toLowerCase(); + this.requiresTimeout = false; + if (browserType.indexOf('msie 9.0') != -1) { // IE 9 + this.requiresTimeout = true; } - }; - - /** - * Hide the item from the DOM (when visible) - */ - PointItem.prototype.hide = function() { - if (this.displayed) { - if (this.dom.point.parentNode) { - this.dom.point.parentNode.removeChild(this.dom.point); + else if (browserType.indexOf('safari') != -1) { // safari + if (browserType.indexOf('chrome') <= -1) { + this.requiresTimeout = true; } - - this.top = null; - this.left = null; - - this.displayed = false; } - }; + } + /** - * Reposition the item horizontally - * @Override + * Get the script path where the vis.js library is located + * + * @returns {string | null} path Path or null when not found. Path does not + * end with a slash. + * @private */ - PointItem.prototype.repositionX = function() { - var start = this.conversion.toScreen(this.data.start); + Network.prototype._getScriptPath = function() { + var scripts = document.getElementsByTagName( 'script' ); - this.left = start - this.props.dot.width; + // find script named vis.js or vis.min.js + for (var i = 0; i < scripts.length; i++) { + var src = scripts[i].src; + var match = src && /\/?vis(.min)?\.js$/.exec(src); + if (match) { + // return path without the script name + return src.substring(0, src.length - match[0].length); + } + } - // reposition point - this.dom.point.style.left = this.left + 'px'; + return null; }; + /** - * Reposition the item vertically - * @Override + * Find the center position of the network + * @private */ - PointItem.prototype.repositionY = function() { - var orientation = this.options.orientation, - point = this.dom.point; - - if (orientation == 'top') { - point.style.top = this.top + 'px'; + Network.prototype._getRange = function() { + var minY = 1e9, maxY = -1e9, minX = 1e9, maxX = -1e9, node; + for (var nodeId in this.nodes) { + if (this.nodes.hasOwnProperty(nodeId)) { + node = this.nodes[nodeId]; + if (minX > (node.boundingBox.left)) {minX = node.boundingBox.left;} + if (maxX < (node.boundingBox.right)) {maxX = node.boundingBox.right;} + if (minY > (node.boundingBox.bottom)) {minY = node.boundingBox.top;} // top is negative, bottom is positive + if (maxY < (node.boundingBox.top)) {maxY = node.boundingBox.bottom;} // top is negative, bottom is positive + } } - else { - point.style.top = (this.parent.height - this.top - this.height) + 'px'; + if (minX == 1e9 && maxX == -1e9 && minY == 1e9 && maxY == -1e9) { + minY = 0, maxY = 0, minX = 0, maxX = 0; } + return {minX: minX, maxX: maxX, minY: minY, maxY: maxY}; }; - module.exports = PointItem; - - -/***/ }, -/* 34 */ -/***/ function(module, exports, __webpack_require__) { - - var Hammer = __webpack_require__(19); - var Item = __webpack_require__(30); - var BackgroundGroup = __webpack_require__(31); - var RangeItem = __webpack_require__(29); - - /** - * @constructor BackgroundItem - * @extends Item - * @param {Object} data Object containing parameters start, end - * content, className. - * @param {{toScreen: function, toTime: function}} conversion - * Conversion functions from time to screen and vice versa - * @param {Object} [options] Configuration options - * // TODO: describe options - */ - // TODO: implement support for the BackgroundItem just having a start, then being displayed as a sort of an annotation - function BackgroundItem (data, conversion, options) { - this.props = { - content: { - width: 0 - } - }; - this.overflow = false; // if contents can overflow (css styling), this flag is set to true - - // validate data - if (data) { - if (data.start == undefined) { - throw new Error('Property "start" missing in item ' + data.id); - } - if (data.end == undefined) { - throw new Error('Property "end" missing in item ' + data.id); - } - } - - Item.call(this, data, conversion, options); - - this.emptyContent = false; - } - - BackgroundItem.prototype = new Item (null, null, null); - - BackgroundItem.prototype.baseClassName = 'item background'; - BackgroundItem.prototype.stack = false; /** - * Check whether this item is visible inside given range - * @returns {{start: Number, end: Number}} range with a timestamp for start and end - * @returns {boolean} True if visible + * @param {object} range = {minX: minX, maxX: maxX, minY: minY, maxY: maxY}; + * @returns {{x: number, y: number}} + * @private */ - BackgroundItem.prototype.isVisible = function(range) { - // determine visibility - return (this.data.start < range.end) && (this.data.end > range.start); + Network.prototype._findCenter = function(range) { + return {x: (0.5 * (range.maxX + range.minX)), + y: (0.5 * (range.maxY + range.minY))}; }; + /** - * Repaint the item + * This function zooms out to fit all data on screen based on amount of nodes + * + * @param {Boolean} [initialZoom] | zoom based on fitted formula or range, true = fitted, default = false; + * @param {Boolean} [disableStart] | If true, start is not called. */ - BackgroundItem.prototype.redraw = function() { - var dom = this.dom; - if (!dom) { - // create DOM - this.dom = {}; - dom = this.dom; + Network.prototype.zoomExtent = function(animationOptions, initialZoom, disableStart) { + this._redraw(true); - // background box - dom.box = document.createElement('div'); - // className is updated in redraw() + if (initialZoom === undefined) {initialZoom = false;} + if (disableStart === undefined) {disableStart = false;} + if (animationOptions === undefined) {animationOptions = false;} - // contents box - dom.content = document.createElement('div'); - dom.content.className = 'content'; - dom.box.appendChild(dom.content); + var range = this._getRange(); + var zoomLevel; - // Note: we do NOT attach this item as attribute to the DOM, - // such that background items cannot be selected - //dom.box['timeline-item'] = this; + if (initialZoom == true) { + var numberOfNodes = this.nodeIndices.length; + if (this.constants.smoothCurves == true) { + if (this.constants.clustering.enabled == true && + numberOfNodes >= this.constants.clustering.initialMaxNodes) { + zoomLevel = 49.07548 / (numberOfNodes + 142.05338) + 9.1444e-04; // this is obtained from fitting a dataset from 5 points with scale levels that looked good. + } + else { + zoomLevel = 12.662 / (numberOfNodes + 7.4147) + 0.0964822; // this is obtained from fitting a dataset from 5 points with scale levels that looked good. + } + } + else { + if (this.constants.clustering.enabled == true && + numberOfNodes >= this.constants.clustering.initialMaxNodes) { + zoomLevel = 77.5271985 / (numberOfNodes + 187.266146) + 4.76710517e-05; // this is obtained from fitting a dataset from 5 points with scale levels that looked good. + } + else { + zoomLevel = 30.5062972 / (numberOfNodes + 19.93597763) + 0.08413486; // this is obtained from fitting a dataset from 5 points with scale levels that looked good. + } + } - this.dirty = true; + // correct for larger canvasses. + var factor = Math.min(this.frame.canvas.clientWidth / 600, this.frame.canvas.clientHeight / 600); + zoomLevel *= factor; } + else { + var xDistance = Math.abs(range.maxX - range.minX) * 1.1; + var yDistance = Math.abs(range.maxY - range.minY) * 1.1; - // append DOM to parent DOM - if (!this.parent) { - throw new Error('Cannot redraw item: no parent attached'); - } - if (!dom.box.parentNode) { - var background = this.parent.dom.background; - if (!background) { - throw new Error('Cannot redraw item: parent has no background container element'); - } - background.appendChild(dom.box); + var xZoomLevel = this.frame.canvas.clientWidth / xDistance; + var yZoomLevel = this.frame.canvas.clientHeight / yDistance; + zoomLevel = (xZoomLevel <= yZoomLevel) ? xZoomLevel : yZoomLevel; } - this.displayed = true; - - // Update DOM when item is marked dirty. An item is marked dirty when: - // - the item is not yet rendered - // - the item's data is changed - // - the item is selected/deselected - if (this.dirty) { - this._updateContents(this.dom.content); - this._updateTitle(this.dom.content); - this._updateDataAttributes(this.dom.content); - this._updateStyle(this.dom.box); - - // update class - var className = (this.data.className ? (' ' + this.data.className) : '') + - (this.selected ? ' selected' : ''); - dom.box.className = this.baseClassName + className; - // determine from css whether this box has overflow - this.overflow = window.getComputedStyle(dom.content).overflow !== 'hidden'; + if (zoomLevel > 1.0) { + zoomLevel = 1.0; + } - // recalculate size - this.props.content.width = this.dom.content.offsetWidth; - this.height = 0; // set height zero, so this item will be ignored when stacking items - this.dirty = false; + var center = this._findCenter(range); + if (disableStart == false) { + var options = {position: center, scale: zoomLevel, animation: animationOptions}; + this.moveTo(options); + this.moving = true; + this.start(); + } + else { + center.x *= zoomLevel; + center.y *= zoomLevel; + center.x -= 0.5 * this.frame.canvas.clientWidth; + center.y -= 0.5 * this.frame.canvas.clientHeight; + this._setScale(zoomLevel); + this._setTranslation(-center.x,-center.y); } }; - /** - * Show the item in the DOM (when not already visible). The items DOM will - * be created when needed. - */ - BackgroundItem.prototype.show = RangeItem.prototype.show; /** - * Hide the item from the DOM (when visible) - * @return {Boolean} changed + * Update the this.nodeIndices with the most recent node index list + * @private */ - BackgroundItem.prototype.hide = RangeItem.prototype.hide; + Network.prototype._updateNodeIndexList = function() { + this._clearNodeIndexList(); + for (var idx in this.nodes) { + if (this.nodes.hasOwnProperty(idx)) { + this.nodeIndices.push(idx); + } + } + }; - /** - * Reposition the item horizontally - * @Override - */ - BackgroundItem.prototype.repositionX = RangeItem.prototype.repositionX; /** - * Reposition the item vertically - * @Override + * Set nodes and edges, and optionally options as well. + * + * @param {Object} data Object containing parameters: + * {Array | DataSet | DataView} [nodes] Array with nodes + * {Array | DataSet | DataView} [edges] Array with edges + * {String} [dot] String containing data in DOT format + * {String} [gephi] String containing data in gephi JSON format + * {Options} [options] Object with options + * @param {Boolean} [disableStart] | optional: disable the calling of the start function. */ - BackgroundItem.prototype.repositionY = function(margin) { - var onTop = this.options.orientation === 'top'; - this.dom.content.style.top = onTop ? '' : '0'; - this.dom.content.style.bottom = onTop ? '0' : ''; - var height; + Network.prototype.setData = function(data, disableStart) { + if (disableStart === undefined) { + disableStart = false; + } + // we set initializing to true to ensure that the hierarchical layout is not performed until both nodes and edges are added. + this.initializing = true; - // special positioning for subgroups - if (this.data.subgroup !== undefined) { - var itemSubgroup = this.data.subgroup; - var subgroups = this.parent.subgroups; - var subgroupIndex = subgroups[itemSubgroup].index; - // if the orientation is top, we need to take the difference in height into account. - if (onTop == true) { - // the first subgroup will have to account for the distance from the top to the first item. - height = this.parent.subgroups[itemSubgroup].height + margin.item.vertical; - height += subgroupIndex == 0 ? margin.axis - 0.5*margin.item.vertical : 0; - var newTop = this.parent.top; - for (var subgroup in subgroups) { - if (subgroups.hasOwnProperty(subgroup)) { - if (subgroups[subgroup].visible == true && subgroups[subgroup].index < subgroupIndex) { - newTop += subgroups[subgroup].height + margin.item.vertical; - } - } - } + if (data && data.dot && (data.nodes || data.edges)) { + throw new SyntaxError('Data must contain either parameter "dot" or ' + + ' parameter pair "nodes" and "edges", but not both.'); + } - // the others will have to be offset downwards with this same distance. - newTop += subgroupIndex != 0 ? margin.axis - 0.5 * margin.item.vertical : 0; - this.dom.box.style.top = newTop + 'px'; - this.dom.box.style.bottom = ''; + // clean up in case there is anyone in an active mode of the manipulation. This is the same option as bound to the escape button. + if (this.constants.dataManipulation.enabled == true) { + this._createManipulatorBar(); + } + + // set options + this.setOptions(data && data.options); + // set all data + if (data && data.dot) { + // parse DOT file + if(data && data.dot) { + var dotData = dotparser.DOTToGraph(data.dot); + this.setData(dotData); + return; } - // and when the orientation is bottom: - else { - var newTop = this.parent.top; - for (var subgroup in subgroups) { - if (subgroups.hasOwnProperty(subgroup)) { - if (subgroups[subgroup].visible == true && subgroups[subgroup].index > subgroupIndex) { - newTop += subgroups[subgroup].height + margin.item.vertical; - } - } - } - height = this.parent.subgroups[itemSubgroup].height + margin.item.vertical; - this.dom.box.style.top = newTop + 'px'; - this.dom.box.style.bottom = ''; + } + else if (data && data.gephi) { + // parse DOT file + if(data && data.gephi) { + var gephiData = gephiParser.parseGephi(data.gephi); + this.setData(gephiData); + return; } } - // and in the case of no subgroups: else { - // we want backgrounds with groups to only show in groups. - if (this.parent instanceof BackgroundGroup) { - // if the item is not in a group: - height = Math.max(this.parent.height, - this.parent.itemSet.body.domProps.center.height, - this.parent.itemSet.body.domProps.centerContainer.height); - this.dom.box.style.top = onTop ? '0' : ''; - this.dom.box.style.bottom = onTop ? '' : '0'; + this._setNodes(data && data.nodes); + this._setEdges(data && data.edges); + } + this._putDataInSector(); + if (disableStart == false) { + if (this.constants.hierarchicalLayout.enabled == true) { + this._resetLevels(); + this._setupHierarchicalLayout(); } else { - height = this.parent.height; - // same alignment for items when orientation is top or bottom - this.dom.box.style.top = this.parent.top + 'px'; - this.dom.box.style.bottom = ''; + // find a stable position or start animating to a stable position + if (this.constants.stabilize) { + this._stabilize(); + } } + this.start(); } - this.dom.box.style.height = height + 'px'; + this.initializing = false; }; - module.exports = BackgroundItem; + /** + * Set options + * @param {Object} options + */ + Network.prototype.setOptions = function (options) { + if (options) { + var prop; + var fields = ['nodes','edges','smoothCurves','hierarchicalLayout','clustering','navigation', + 'keyboard','dataManipulation','onAdd','onEdit','onEditEdge','onConnect','onDelete','clickToUse' + ]; + // extend all but the values in fields + util.selectiveNotDeepExtend(fields,this.constants, options); + util.selectiveNotDeepExtend(['color'],this.constants.nodes, options.nodes); + util.selectiveNotDeepExtend(['color','length'],this.constants.edges, options.edges); + if (options.physics) { + util.mergeOptions(this.constants.physics, options.physics,'barnesHut'); + util.mergeOptions(this.constants.physics, options.physics,'repulsion'); -/***/ }, -/* 35 */ -/***/ function(module, exports, __webpack_require__) { + if (options.physics.hierarchicalRepulsion) { + this.constants.hierarchicalLayout.enabled = true; + this.constants.physics.hierarchicalRepulsion.enabled = true; + this.constants.physics.barnesHut.enabled = false; + for (prop in options.physics.hierarchicalRepulsion) { + if (options.physics.hierarchicalRepulsion.hasOwnProperty(prop)) { + this.constants.physics.hierarchicalRepulsion[prop] = options.physics.hierarchicalRepulsion[prop]; + } + } + } + } - var keycharm = __webpack_require__(36); - var Emitter = __webpack_require__(11); - var Hammer = __webpack_require__(19); - var util = __webpack_require__(1); + if (options.onAdd) {this.triggerFunctions.add = options.onAdd;} + if (options.onEdit) {this.triggerFunctions.edit = options.onEdit;} + if (options.onEditEdge) {this.triggerFunctions.editEdge = options.onEditEdge;} + if (options.onConnect) {this.triggerFunctions.connect = options.onConnect;} + if (options.onDelete) {this.triggerFunctions.del = options.onDelete;} - /** - * Turn an element into an clickToUse element. - * When not active, the element has a transparent overlay. When the overlay is - * clicked, the mode is changed to active. - * When active, the element is displayed with a blue border around it, and - * the interactive contents of the element can be used. When clicked outside - * the element, the elements mode is changed to inactive. - * @param {Element} container - * @constructor - */ - function Activator(container) { - this.active = false; + util.mergeOptions(this.constants, options,'smoothCurves'); + util.mergeOptions(this.constants, options,'hierarchicalLayout'); + util.mergeOptions(this.constants, options,'clustering'); + util.mergeOptions(this.constants, options,'navigation'); + util.mergeOptions(this.constants, options,'keyboard'); + util.mergeOptions(this.constants, options,'dataManipulation'); - this.dom = { - container: container - }; - this.dom.overlay = document.createElement('div'); - this.dom.overlay.className = 'overlay'; + if (options.dataManipulation) { + this.editMode = this.constants.dataManipulation.initiallyVisible; + } - this.dom.container.appendChild(this.dom.overlay); - this.hammer = Hammer(this.dom.overlay, {prevent_default: false}); - this.hammer.on('tap', this._onTapOverlay.bind(this)); + // TODO: work out these options and document them + if (options.edges) { + if (options.edges.color !== undefined) { + if (util.isString(options.edges.color)) { + this.constants.edges.color = {}; + this.constants.edges.color.color = options.edges.color; + this.constants.edges.color.highlight = options.edges.color; + this.constants.edges.color.hover = options.edges.color; + } + else { + if (options.edges.color.color !== undefined) {this.constants.edges.color.color = options.edges.color.color;} + if (options.edges.color.highlight !== undefined) {this.constants.edges.color.highlight = options.edges.color.highlight;} + if (options.edges.color.hover !== undefined) {this.constants.edges.color.hover = options.edges.color.hover;} + } + this.constants.edges.inheritColor = false; + } - // block all touch events (except tap) - var me = this; - var events = [ - 'touch', 'pinch', - 'doubletap', 'hold', - 'dragstart', 'drag', 'dragend', - 'mousewheel', 'DOMMouseScroll' // DOMMouseScroll is needed for Firefox - ]; - events.forEach(function (event) { - me.hammer.on(event, function (event) { - event.stopPropagation(); - }); - }); + if (!options.edges.fontColor) { + if (options.edges.color !== undefined) { + if (util.isString(options.edges.color)) {this.constants.edges.fontColor = options.edges.color;} + else if (options.edges.color.color !== undefined) {this.constants.edges.fontColor = options.edges.color.color;} + } + } + } - // attach a tap event to the window, in order to deactivate when clicking outside the timeline - this.windowHammer = Hammer(window, {prevent_default: false}); - this.windowHammer.on('tap', function (event) { - // deactivate when clicked outside the container - if (!_hasParent(event.target, container)) { - me.deactivate(); + if (options.nodes) { + if (options.nodes.color) { + var newColorObj = util.parseColor(options.nodes.color); + this.constants.nodes.color.background = newColorObj.background; + this.constants.nodes.color.border = newColorObj.border; + this.constants.nodes.color.highlight.background = newColorObj.highlight.background; + this.constants.nodes.color.highlight.border = newColorObj.highlight.border; + this.constants.nodes.color.hover.background = newColorObj.hover.background; + this.constants.nodes.color.hover.border = newColorObj.hover.border; + } + } + if (options.groups) { + for (var groupname in options.groups) { + if (options.groups.hasOwnProperty(groupname)) { + var group = options.groups[groupname]; + this.groups.add(groupname, group); + } + } } - }); - if (this.keycharm !== undefined) { - this.keycharm.destroy(); - } - this.keycharm = keycharm(); + if (options.tooltip) { + for (prop in options.tooltip) { + if (options.tooltip.hasOwnProperty(prop)) { + this.constants.tooltip[prop] = options.tooltip[prop]; + } + } + if (options.tooltip.color) { + this.constants.tooltip.color = util.parseColor(options.tooltip.color); + } + } - // keycharm listener only bounded when active) - this.escListener = this.deactivate.bind(this); - } + if ('clickToUse' in options) { + if (options.clickToUse) { + if (!this.activator) { + this.activator = new Activator(this.frame); + this.activator.on('change', this._createKeyBinds.bind(this)); + } + } + else { + if (this.activator) { + this.activator.destroy(); + delete this.activator; + } + } + } - // turn into an event emitter - Emitter(Activator.prototype); + if (options.labels) { + throw new Error('Option "labels" is deprecated. Use options "locale" and "locales" instead.'); + } - // The currently active activator - Activator.current = null; - /** - * Destroy the activator. Cleans up all created DOM and event listeners - */ - Activator.prototype.destroy = function () { - this.deactivate(); + // (Re)loading the mixins that can be enabled or disabled in the options. + // load the force calculation functions, grouped under the physics system. + this._loadPhysicsSystem(); + // load the navigation system. + this._loadNavigationControls(); + // load the data manipulation system + this._loadManipulationSystem(); + // configure the smooth curves + this._configureSmoothCurves(); - // remove dom - this.dom.overlay.parentNode.removeChild(this.dom.overlay); + // bind hammer + this._bindHammer(); - // cleanup hammer instances - this.hammer = null; - this.windowHammer = null; - // FIXME: cleaning up hammer instances doesn't work (Timeline not removed from memory) - }; + // bind keys. If disabled, this will not do anything; + this._createKeyBinds(); - /** - * Activate the element - * Overlay is hidden, element is decorated with a blue shadow border - */ - Activator.prototype.activate = function () { - // we allow only one active activator at a time - if (Activator.current) { - Activator.current.deactivate(); - } - Activator.current = this; - this.active = true; - this.dom.overlay.style.display = 'none'; - util.addClassName(this.dom.container, 'vis-active'); + this.setSize(this.constants.width, this.constants.height); + this.moving = true; + this.start(); + } + }; - this.emit('change'); - this.emit('activate'); - // ugly hack: bind ESC after emitting the events, as the Network rebinds all - // keyboard events on a 'change' event - this.keycharm.bind('esc', this.escListener); - }; /** - * Deactivate the element - * Overlay is displayed on top of the element - */ - Activator.prototype.deactivate = function () { - this.active = false; - this.dom.overlay.style.display = ''; - util.removeClassName(this.dom.container, 'vis-active'); - this.keycharm.unbind('esc', this.escListener); - - this.emit('change'); - this.emit('deactivate'); - }; - - /** - * Handle a tap event: activate the container - * @param event - * @private - */ - Activator.prototype._onTapOverlay = function (event) { - // activate the container - this.activate(); - event.stopPropagation(); - }; - - /** - * Test whether the element has the requested parent element somewhere in - * its chain of parent nodes. - * @param {HTMLElement} element - * @param {HTMLElement} parent - * @returns {boolean} Returns true when the parent is found somewhere in the - * chain of parent nodes. + * Create the main frame for the Network. + * This function is executed once when a Network object is created. The frame + * contains a canvas, and this canvas contains all objects like the axis and + * nodes. * @private */ - function _hasParent(element, parent) { - while (element) { - if (element === parent) { - return true - } - element = element.parentNode; + Network.prototype._create = function () { + // remove all elements from the container element. + while (this.containerElement.hasChildNodes()) { + this.containerElement.removeChild(this.containerElement.firstChild); } - return false; - } - module.exports = Activator; + this.frame = document.createElement('div'); + this.frame.className = 'vis network-frame'; + this.frame.style.position = 'relative'; + this.frame.style.overflow = 'hidden'; + this.frame.tabIndex = 900; -/***/ }, -/* 36 */ -/***/ 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. - */ + this.frame.canvas = document.createElement("canvas"); + this.frame.canvas.style.position = 'relative'; + this.frame.appendChild(this.frame.canvas); - // 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(); + if (!this.frame.canvas.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.frame.canvas.appendChild(noCanvas); } - }(this, function () { + else { + var ctx = this.frame.canvas.getContext("2d"); + this.pixelRatio = (window.devicePixelRatio || 1) / (ctx.webkitBackingStorePixelRatio || + ctx.mozBackingStorePixelRatio || + ctx.msBackingStorePixelRatio || + ctx.oBackingStorePixelRatio || + ctx.backingStorePixelRatio || 1); - function keycharm(options) { - var preventDefault = options && options.preventDefault || false; + this.frame.canvas.getContext("2d").setTransform(this.pixelRatio, 0, 0, this.pixelRatio, 0, 0); + } - var container = options && options.container || window; - var _exportFunctions = {}; - var _bound = {keydown:{}, keyup:{}}; - var _keys = {}; - var i; + this._bindHammer(); + }; - // a - z - for (i = 97; i <= 122; i++) {_keys[String.fromCharCode(i)] = {code:65 + (i - 97), shift: false};} - // A - Z - for (i = 65; i <= 90; i++) {_keys[String.fromCharCode(i)] = {code:i, shift: true};} - // 0 - 9 - for (i = 0; i <= 9; i++) {_keys['' + i] = {code:48 + i, shift: false};} - // F1 - F12 - for (i = 1; i <= 12; i++) {_keys['F' + i] = {code:111 + i, shift: false};} - // num0 - num9 - for (i = 0; i <= 9; i++) {_keys['num' + i] = {code:96 + i, shift: false};} - // numpad misc - _keys['num*'] = {code:106, shift: false}; - _keys['num+'] = {code:107, shift: false}; - _keys['num-'] = {code:109, shift: false}; - _keys['num/'] = {code:111, shift: false}; - _keys['num.'] = {code:110, shift: false}; - // arrows - _keys['left'] = {code:37, shift: false}; - _keys['up'] = {code:38, shift: false}; - _keys['right'] = {code:39, shift: false}; - _keys['down'] = {code:40, shift: false}; - // extra keys - _keys['space'] = {code:32, shift: false}; - _keys['enter'] = {code:13, shift: false}; - _keys['shift'] = {code:16, shift: undefined}; - _keys['esc'] = {code:27, shift: false}; - _keys['backspace'] = {code:8, shift: false}; - _keys['tab'] = {code:9, shift: false}; - _keys['ctrl'] = {code:17, shift: false}; - _keys['alt'] = {code:18, shift: false}; - _keys['delete'] = {code:46, shift: false}; - _keys['pageup'] = {code:33, shift: false}; - _keys['pagedown'] = {code:34, shift: false}; - // symbols - _keys['='] = {code:187, shift: false}; - _keys['-'] = {code:189, shift: false}; - _keys[']'] = {code:221, shift: false}; - _keys['['] = {code:219, shift: false}; + /** + * This function binds hammer, it can be repeated over and over due to the uniqueness check. + * @private + */ + Network.prototype._bindHammer = function() { + var me = this; + if (this.hammer !== undefined) { + this.hammer.dispose(); + } + this.drag = {}; + this.pinch = {}; + this.hammer = Hammer(this.frame.canvas, { + prevent_default: true + }); + this.hammer.on('tap', me._onTap.bind(me) ); + this.hammer.on('doubletap', me._onDoubleTap.bind(me) ); + this.hammer.on('hold', me._onHold.bind(me) ); + this.hammer.on('touch', me._onTouch.bind(me) ); + this.hammer.on('dragstart', me._onDragStart.bind(me) ); + this.hammer.on('drag', me._onDrag.bind(me) ); + this.hammer.on('dragend', me._onDragEnd.bind(me) ); + if (this.constants.zoomable == true) { + this.hammer.on('mousewheel', me._onMouseWheel.bind(me)); + this.hammer.on('DOMMouseScroll', me._onMouseWheel.bind(me)); // for FF + this.hammer.on('pinch', me._onPinch.bind(me) ); + } + this.hammer.on('mousemove', me._onMouseMoveTitle.bind(me) ); - var down = function(event) {handleEvent(event,'keydown');}; - var up = function(event) {handleEvent(event,'keyup');}; + this.hammerFrame = Hammer(this.frame, { + prevent_default: true + }); + this.hammerFrame.on('release', me._onRelease.bind(me) ); - // 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); - } - } + // add the frame to the container element + this.containerElement.appendChild(this.frame); + } - if (preventDefault == true) { - event.preventDefault(); - } - } - }; + /** + * Binding the keys for keyboard navigation. These functions are defined in the NavigationMixin + * @private + */ + Network.prototype._createKeyBinds = function() { + var me = this; + if (this.keycharm !== undefined) { + this.keycharm.destroy(); + } - // 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}); - }; + if (this.constants.keyboard.bindToWindow == true) { + this.keycharm = keycharm({container: window, preventDefault: false}); + } + else { + this.keycharm = keycharm({container: this.frame, preventDefault: false}); + } + this.keycharm.reset(); - // 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); - } - } - }; + if (this.constants.keyboard.enabled && this.isActive()) { + this.keycharm.bind("up", this._moveUp.bind(me) , "keydown"); + this.keycharm.bind("up", this._yStopMoving.bind(me), "keyup"); + this.keycharm.bind("down", this._moveDown.bind(me) , "keydown"); + this.keycharm.bind("down", this._yStopMoving.bind(me), "keyup"); + this.keycharm.bind("left", this._moveLeft.bind(me) , "keydown"); + this.keycharm.bind("left", this._xStopMoving.bind(me), "keyup"); + this.keycharm.bind("right",this._moveRight.bind(me), "keydown"); + this.keycharm.bind("right",this._xStopMoving.bind(me), "keyup"); + this.keycharm.bind("=", this._zoomIn.bind(me), "keydown"); + this.keycharm.bind("=", this._stopZoom.bind(me), "keyup"); + this.keycharm.bind("num+", this._zoomIn.bind(me), "keydown"); + this.keycharm.bind("num+", this._stopZoom.bind(me), "keyup"); + this.keycharm.bind("num-", this._zoomOut.bind(me), "keydown"); + this.keycharm.bind("num-", this._stopZoom.bind(me), "keyup"); + this.keycharm.bind("-", this._zoomOut.bind(me), "keydown"); + this.keycharm.bind("-", this._stopZoom.bind(me), "keyup"); + this.keycharm.bind("[", this._zoomIn.bind(me), "keydown"); + this.keycharm.bind("[", this._stopZoom.bind(me), "keyup"); + this.keycharm.bind("]", this._zoomOut.bind(me), "keydown"); + this.keycharm.bind("]", this._stopZoom.bind(me), "keyup"); + this.keycharm.bind("pageup",this._zoomIn.bind(me), "keydown"); + this.keycharm.bind("pageup",this._stopZoom.bind(me), "keyup"); + this.keycharm.bind("pagedown",this._zoomOut.bind(me),"keydown"); + this.keycharm.bind("pagedown",this._stopZoom.bind(me), "keyup"); + } + this.keycharm.bind("1",this.increaseClusterLevel.bind(me), "keydown"); + this.keycharm.bind("2",this.decreaseClusterLevel.bind(me), "keydown"); + this.keycharm.bind("3",this.forceAggregateHubs.bind(me,true),"keydown"); + this.keycharm.bind("4",this.normalizeClusterLevels.bind(me), "keydown"); + if (this.constants.dataManipulation.enabled == true) { + this.keycharm.bind("esc",this._createManipulatorBar.bind(me)); + this.keycharm.bind("delete",this._deleteSelected.bind(me)); + } + }; - // 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"; - }; + /** + * Cleans up all bindings of the network, removing it fully from the memory IF the variable is set to null after calling this function. + * var network = new vis.Network(..); + * network.destroy(); + * network = null; + */ + Network.prototype.destroy = function() { + this.start = function () {}; + this.redraw = function () {}; + this.timer = false; - // 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] = []; - } - }; + // cleanup physicsConfiguration if it exists + this._cleanupPhysicsConfiguration(); - // reset all bound variables. - _exportFunctions.reset = function() { - _bound = {keydown:{}, keyup:{}}; - }; + // remove keybindings + this.keycharm.reset(); - // unbind all listeners and reset all variables. - _exportFunctions.destroy = function() { - _bound = {keydown:{}, keyup:{}}; - container.removeEventListener('keydown', down, true); - container.removeEventListener('keyup', up, true); - }; + // clear hammer bindings + this.hammer.dispose(); - // create listeners. - container.addEventListener('keydown',down,true); - container.addEventListener('keyup',up,true); + // clear events + this.off(); - // return the public functions. - return _exportFunctions; + this._recursiveDOMDelete(this.containerElement); + } + + Network.prototype._recursiveDOMDelete = function(DOMobject) { + while (DOMobject.hasChildNodes() == true) { + this._recursiveDOMDelete(DOMobject.firstChild); + DOMobject.removeChild(DOMobject.firstChild); } + } - return keycharm; - })); + /** + * Get the pointer location from a touch location + * @param {{pageX: Number, pageY: Number}} touch + * @return {{x: Number, y: Number}} pointer + * @private + */ + Network.prototype._getPointer = function (touch) { + return { + x: touch.pageX - util.getAbsoluteLeft(this.frame.canvas), + y: touch.pageY - util.getAbsoluteTop(this.frame.canvas) + }; + }; + /** + * On start of a touch gesture, store the pointer + * @param event + * @private + */ + Network.prototype._onTouch = function (event) { + if (new Date().valueOf() - this.touchTime > 100) { + this.drag.pointer = this._getPointer(event.gesture.center); + this.drag.pinched = false; + this.pinch.scale = this._getScale(); + // to avoid double fireing of this event because we have two hammer instances. (on canvas and on frame) + this.touchTime = new Date().valueOf(); + this._handleTouch(this.drag.pointer); + } + }; -/***/ }, -/* 37 */ -/***/ function(module, exports, __webpack_require__) { + /** + * handle drag start event + * @private + */ + Network.prototype._onDragStart = function (event) { + this._handleDragStart(event); + }; - var util = __webpack_require__(1); - var Component = __webpack_require__(23); - var TimeStep = __webpack_require__(38); - var DateUtil = __webpack_require__(24); - var moment = __webpack_require__(2); /** - * A horizontal time axis - * @param {{dom: Object, domProps: Object, emitter: Emitter, range: Range}} body - * @param {Object} [options] See TimeAxis.setOptions for the available - * options. - * @constructor TimeAxis - * @extends Component + * This function is called by _onDragStart. + * It is separated out because we can then overload it for the datamanipulation system. + * + * @private */ - function TimeAxis (body, options) { - this.dom = { - foreground: null, - lines: [], - majorTexts: [], - minorTexts: [], - redundant: { - lines: [], - majorTexts: [], - minorTexts: [] - } - }; - this.props = { - range: { - start: 0, - end: 0, - minimumStep: 0 - }, - lineTop: 0 - }; + Network.prototype._handleDragStart = function(event) { + // in case the touch event was triggered on an external div, do the initial touch now. + if (this.drag.pointer === undefined) { + this._onTouch(event); + } - this.defaultOptions = { - orientation: 'bottom', // supported: 'top', 'bottom' - // TODO: implement timeaxis orientations 'left' and 'right' - showMinorLabels: true, - showMajorLabels: true, - format: null, - timeAxis: null - }; - this.options = util.extend({}, this.defaultOptions); + var node = this._getNodeAt(this.drag.pointer); + // note: drag.pointer is set in _onTouch to get the initial touch location - this.body = body; + this.drag.dragging = true; + this.drag.selection = []; + this.drag.translation = this._getTranslation(); + this.drag.nodeId = null; + this.draggingNodes = false; - // create the HTML DOM - this._create(); + if (node != null && this.constants.dragNodes == true) { + this.draggingNodes = true; + this.drag.nodeId = node.id; + // select the clicked node if not yet selected + if (!node.isSelected()) { + this._selectObject(node,false); + } - this.setOptions(options); - } + this.emit("dragStart",{nodeIds:this.getSelection().nodes}); - TimeAxis.prototype = new Component(); + // create an array with the selected nodes and their original location and status + for (var objectId in this.selectionObj.nodes) { + if (this.selectionObj.nodes.hasOwnProperty(objectId)) { + var object = this.selectionObj.nodes[objectId]; + var s = { + id: object.id, + node: object, - /** - * Set options for the TimeAxis. - * Parameters will be merged in current options. - * @param {Object} options Available options: - * {string} [orientation] - * {boolean} [showMinorLabels] - * {boolean} [showMajorLabels] - */ - TimeAxis.prototype.setOptions = function(options) { - if (options) { - // copy all options that we know - util.selectiveExtend([ - 'orientation', - 'showMinorLabels', - 'showMajorLabels', - 'hiddenDates', - 'format', - 'timeAxis' - ], this.options, options); + // store original x, y, xFixed and yFixed, make the node temporarily Fixed + x: object.x, + y: object.y, + xFixed: object.xFixed, + yFixed: object.yFixed + }; - // apply locale to moment.js - // TODO: not so nice, this is applied globally to moment.js - if ('locale' in options) { - if (typeof moment.locale === 'function') { - // moment.js 2.8.1+ - moment.locale(options.locale); - } - else { - moment.lang(options.locale); + object.xFixed = true; + object.yFixed = true; + + this.drag.selection.push(s); } } } }; + /** - * Create the HTML DOM for the TimeAxis + * handle drag event + * @private */ - TimeAxis.prototype._create = function() { - this.dom.foreground = document.createElement('div'); - this.dom.background = document.createElement('div'); + Network.prototype._onDrag = function (event) { + this._handleOnDrag(event) + }; - this.dom.foreground.className = 'timeaxis foreground'; - this.dom.background.className = 'timeaxis background'; - }; /** - * Destroy the TimeAxis + * This function is called by _onDrag. + * It is separated out because we can then overload it for the datamanipulation system. + * + * @private */ - TimeAxis.prototype.destroy = function() { - // remove from DOM - if (this.dom.foreground.parentNode) { - this.dom.foreground.parentNode.removeChild(this.dom.foreground); - } - if (this.dom.background.parentNode) { - this.dom.background.parentNode.removeChild(this.dom.background); + Network.prototype._handleOnDrag = function(event) { + if (this.drag.pinched) { + return; } - this.body = null; - }; + // remove the focus on node if it is focussed on by the focusOnNode + this.releaseNode(); - /** - * Repaint the component - * @return {boolean} Returns true if the component is resized - */ - TimeAxis.prototype.redraw = function () { - var options = this.options; - var props = this.props; - var foreground = this.dom.foreground; - var background = this.dom.background; + var pointer = this._getPointer(event.gesture.center); + var me = this; + var drag = this.drag; + var selection = drag.selection; + if (selection && selection.length && this.constants.dragNodes == true) { + // calculate delta's and new location + var deltaX = pointer.x - drag.pointer.x; + var deltaY = pointer.y - drag.pointer.y; - // determine the correct parent DOM element (depending on option orientation) - var parent = (options.orientation == 'top') ? this.body.dom.top : this.body.dom.bottom; - var parentChanged = (foreground.parentNode !== parent); + // update position of all selected nodes + selection.forEach(function (s) { + var node = s.node; - // calculate character width and height - this._calculateCharSize(); + if (!s.xFixed) { + node.x = me._XconvertDOMtoCanvas(me._XconvertCanvasToDOM(s.x) + deltaX); + } - // TODO: recalculate sizes only needed when parent is resized or options is changed - var orientation = this.options.orientation, - showMinorLabels = this.options.showMinorLabels, - showMajorLabels = this.options.showMajorLabels; + if (!s.yFixed) { + node.y = me._YconvertDOMtoCanvas(me._YconvertCanvasToDOM(s.y) + deltaY); + } + }); - // determine the width and height of the elemens for the axis - props.minorLabelHeight = showMinorLabels ? props.minorCharHeight : 0; - props.majorLabelHeight = showMajorLabels ? props.majorCharHeight : 0; - props.height = props.minorLabelHeight + props.majorLabelHeight; - props.width = foreground.offsetWidth; - props.minorLineHeight = this.body.domProps.root.height - props.majorLabelHeight - - (options.orientation == 'top' ? this.body.domProps.bottom.height : this.body.domProps.top.height); - props.minorLineWidth = 1; // TODO: really calculate width - props.majorLineHeight = props.minorLineHeight + props.majorLabelHeight; - props.majorLineWidth = 1; // TODO: really calculate width + // start _animationStep if not yet running + if (!this.moving) { + this.moving = true; + this.start(); + } + } + else { + // move the network + if (this.constants.dragNetwork == true) { + // if the drag was not started properly because the click started outside the network div, start it now. + if (this.drag.pointer === undefined) { + this._handleDragStart(event); + return; + } + var diffX = pointer.x - this.drag.pointer.x; + var diffY = pointer.y - this.drag.pointer.y; - // take foreground and background offline while updating (is almost twice as fast) - var foregroundNextSibling = foreground.nextSibling; - var backgroundNextSibling = background.nextSibling; - foreground.parentNode && foreground.parentNode.removeChild(foreground); - background.parentNode && background.parentNode.removeChild(background); + this._setTranslation( + this.drag.translation.x + diffX, + this.drag.translation.y + diffY + ); + this._redraw(); + } + } + }; - foreground.style.height = this.props.height + 'px'; + /** + * handle drag start event + * @private + */ + Network.prototype._onDragEnd = function (event) { + this._handleDragEnd(event); + }; - this._repaintLabels(); - // put DOM online again (at the same place) - if (foregroundNextSibling) { - parent.insertBefore(foreground, foregroundNextSibling); + Network.prototype._handleDragEnd = function(event) { + this.drag.dragging = false; + var selection = this.drag.selection; + if (selection && selection.length) { + selection.forEach(function (s) { + // restore original xFixed and yFixed + s.node.xFixed = s.xFixed; + s.node.yFixed = s.yFixed; + }); + this.moving = true; + this.start(); } else { - parent.appendChild(foreground) + this._redraw(); } - if (backgroundNextSibling) { - this.body.dom.backgroundVertical.insertBefore(background, backgroundNextSibling); + if (this.draggingNodes == false) { + this.emit("dragEnd",{nodeIds:[]}); } else { - this.body.dom.backgroundVertical.appendChild(background) + this.emit("dragEnd",{nodeIds:this.getSelection().nodes}); } - return this._isResized() || parentChanged; + } + /** + * handle tap/click event: select/unselect a node + * @private + */ + Network.prototype._onTap = function (event) { + var pointer = this._getPointer(event.gesture.center); + this.pointerPosition = pointer; + this._handleTap(pointer); + }; + /** - * Repaint major and minor text labels and vertical grid lines + * handle doubletap event * @private */ - TimeAxis.prototype._repaintLabels = function () { - var orientation = this.options.orientation; + Network.prototype._onDoubleTap = function (event) { + var pointer = this._getPointer(event.gesture.center); + this._handleDoubleTap(pointer); + }; - // calculate range and step (step such that we have space for 7 characters per label) - var start = util.convert(this.body.range.start, 'Number'); - var end = util.convert(this.body.range.end, 'Number'); - var timeLabelsize = this.body.util.toTime((this.props.minorCharWidth || 10) * 7).valueOf(); - var minimumStep = timeLabelsize - DateUtil.getHiddenDurationBefore(this.body.hiddenDates, this.body.range, timeLabelsize); - minimumStep -= this.body.util.toTime(0).valueOf(); - var step = new TimeStep(new Date(start), new Date(end), minimumStep, this.body.hiddenDates); - if (this.options.format) { - step.setFormat(this.options.format); - } - if (this.options.timeAxis) { - step.setScale(this.options.timeAxis); - } - this.step = step; + /** + * handle long tap event: multi select nodes + * @private + */ + Network.prototype._onHold = function (event) { + var pointer = this._getPointer(event.gesture.center); + this.pointerPosition = pointer; + this._handleOnHold(pointer); + }; - // Move all DOM elements to a "redundant" list, where they - // can be picked for re-use, and clear the lists with lines and texts. - // At the end of the function _repaintLabels, left over elements will be cleaned up - var dom = this.dom; - dom.redundant.lines = dom.lines; - dom.redundant.majorTexts = dom.majorTexts; - dom.redundant.minorTexts = dom.minorTexts; - dom.lines = []; - dom.majorTexts = []; - dom.minorTexts = []; + /** + * handle the release of the screen + * + * @private + */ + Network.prototype._onRelease = function (event) { + var pointer = this._getPointer(event.gesture.center); + this._handleOnRelease(pointer); + }; - var cur; - var x = 0; - var isMajor; - var xPrev = 0; - var width = 0; - var prevLine; - var xFirstMajorLabel = undefined; - var max = 0; - var className; + /** + * Handle pinch event + * @param event + * @private + */ + Network.prototype._onPinch = function (event) { + var pointer = this._getPointer(event.gesture.center); - step.first(); - while (step.hasNext() && max < 1000) { - max++; + this.drag.pinched = true; + if (!('scale' in this.pinch)) { + this.pinch.scale = 1; + } - cur = step.getCurrent(); - isMajor = step.isMajor(); - className = step.getClassName(); + // TODO: enabled moving while pinching? + var scale = this.pinch.scale * event.gesture.scale; + this._zoom(scale, pointer) + }; - xPrev = x; - x = this.body.util.toScreen(cur); - width = x - xPrev; - if (prevLine) { - prevLine.style.width = width + 'px'; + /** + * Zoom the network in or out + * @param {Number} scale a number around 1, and between 0.01 and 10 + * @param {{x: Number, y: Number}} pointer Position on screen + * @return {Number} appliedScale scale is limited within the boundaries + * @private + */ + Network.prototype._zoom = function(scale, pointer) { + if (this.constants.zoomable == true) { + var scaleOld = this._getScale(); + if (scale < 0.00001) { + scale = 0.00001; } - - if (this.options.showMinorLabels) { - this._repaintMinorText(x, step.getLabelMinor(), orientation, className); + if (scale > 10) { + scale = 10; } - if (isMajor && this.options.showMajorLabels) { - if (x > 0) { - if (xFirstMajorLabel == undefined) { - xFirstMajorLabel = x; - } - this._repaintMajorText(x, step.getLabelMajor(), orientation, className); + var preScaleDragPointer = null; + if (this.drag !== undefined) { + if (this.drag.dragging == true) { + preScaleDragPointer = this.DOMtoCanvas(this.drag.pointer); } - prevLine = this._repaintMajorLine(x, orientation, className); - } - else { - prevLine = this._repaintMinorLine(x, orientation, className); } + // + this.frame.canvas.clientHeight / 2 + var translation = this._getTranslation(); - step.next(); - } + var scaleFrac = scale / scaleOld; + var tx = (1 - scaleFrac) * pointer.x + translation.x * scaleFrac; + var ty = (1 - scaleFrac) * pointer.y + translation.y * scaleFrac; - // create a major label on the left when needed - if (this.options.showMajorLabels) { - var leftTime = this.body.util.toTime(0), - leftText = step.getLabelMajor(leftTime), - widthText = leftText.length * (this.props.majorCharWidth || 10) + 10; // upper bound estimation + this.areaCenter = {"x" : this._XconvertDOMtoCanvas(pointer.x), + "y" : this._YconvertDOMtoCanvas(pointer.y)}; - if (xFirstMajorLabel == undefined || widthText < xFirstMajorLabel) { - this._repaintMajorText(0, leftText, orientation, className); + this._setScale(scale); + this._setTranslation(tx, ty); + this.updateClustersDefault(); + + if (preScaleDragPointer != null) { + var postScaleDragPointer = this.canvasToDOM(preScaleDragPointer); + this.drag.pointer.x = postScaleDragPointer.x; + this.drag.pointer.y = postScaleDragPointer.y; } - } - // Cleanup leftover DOM elements from the redundant list - util.forEach(this.dom.redundant, function (arr) { - while (arr.length) { - var elem = arr.pop(); - if (elem && elem.parentNode) { - elem.parentNode.removeChild(elem); - } + this._redraw(); + + if (scaleOld < scale) { + this.emit("zoom", {direction:"+"}); } - }); + else { + this.emit("zoom", {direction:"-"}); + } + + return scale; + } }; + /** - * Create a minor label for the axis at position x - * @param {Number} x - * @param {String} text - * @param {String} orientation "top" or "bottom" (default) - * @param {String} className + * Event handler for mouse wheel event, used to zoom the timeline + * See http://adomas.org/javascript-mouse-wheel/ + * https://github.com/EightMedia/hammer.js/issues/256 + * @param {MouseEvent} event * @private */ - TimeAxis.prototype._repaintMinorText = function (x, text, orientation, className) { - // reuse redundant label - var label = this.dom.redundant.minorTexts.shift(); - - if (!label) { - // create new label - var content = document.createTextNode(''); - label = document.createElement('div'); - label.appendChild(content); - this.dom.foreground.appendChild(label); + Network.prototype._onMouseWheel = function(event) { + // retrieve delta + var delta = 0; + if (event.wheelDelta) { /* IE/Opera. */ + delta = event.wheelDelta/120; + } else if (event.detail) { /* Mozilla case. */ + // In Mozilla, sign of delta is different than in IE. + // Also, delta is multiple of 3. + delta = -event.detail/3; } - this.dom.minorTexts.push(label); - label.childNodes[0].nodeValue = text; + // If delta is nonzero, handle it. + // Basically, delta is now positive if wheel was scrolled up, + // and negative, if wheel was scrolled down. + if (delta) { - label.style.top = (orientation == 'top') ? (this.props.majorLabelHeight + 'px') : '0'; - label.style.left = x + 'px'; - label.className = 'text minor ' + className; - //label.title = title; // TODO: this is a heavy operation - }; + // calculate the new scale + var scale = this._getScale(); + var zoom = delta / 10; + if (delta < 0) { + zoom = zoom / (1 - zoom); + } + scale *= (1 + zoom); - /** - * Create a Major label for the axis at position x - * @param {Number} x - * @param {String} text - * @param {String} orientation "top" or "bottom" (default) - * @param {String} className - * @private - */ - TimeAxis.prototype._repaintMajorText = function (x, text, orientation, className) { - // reuse redundant label - var label = this.dom.redundant.majorTexts.shift(); + // calculate the pointer location + var gesture = hammerUtil.fakeGesture(this, event); + var pointer = this._getPointer(gesture.center); - if (!label) { - // create label - var content = document.createTextNode(text); - label = document.createElement('div'); - label.appendChild(content); - this.dom.foreground.appendChild(label); + // apply the new scale + this._zoom(scale, pointer); } - this.dom.majorTexts.push(label); - label.childNodes[0].nodeValue = text; - label.className = 'text major ' + className; - //label.title = title; // TODO: this is a heavy operation - - label.style.top = (orientation == 'top') ? '0' : (this.props.minorLabelHeight + 'px'); - label.style.left = x + 'px'; + // Prevent default actions caused by mouse wheel. + event.preventDefault(); }; + /** - * Create a minor line for the axis at position x - * @param {Number} x - * @param {String} orientation "top" or "bottom" (default) - * @param {String} className - * @return {Element} Returns the created line + * Mouse move handler for checking whether the title moves over a node with a title. + * @param {Event} event * @private */ - TimeAxis.prototype._repaintMinorLine = function (x, orientation, className) { - // reuse redundant line - var line = this.dom.redundant.lines.shift(); - if (!line) { - // create vertical line - line = document.createElement('div'); - this.dom.background.appendChild(line); - } - this.dom.lines.push(line); + Network.prototype._onMouseMoveTitle = function (event) { + var gesture = hammerUtil.fakeGesture(this, event); + var pointer = this._getPointer(gesture.center); - var props = this.props; - if (orientation == 'top') { - line.style.top = props.majorLabelHeight + 'px'; - } - else { - line.style.top = this.body.domProps.top.height + 'px'; + // check if the previously selected node is still selected + if (this.popupObj) { + this._checkHidePopup(pointer); } - line.style.height = props.minorLineHeight + 'px'; - line.style.left = (x - props.minorLineWidth / 2) + 'px'; - - line.className = 'grid vertical minor ' + className; - - return line; - }; - /** - * Create a Major line for the axis at position x - * @param {Number} x - * @param {String} orientation "top" or "bottom" (default) - * @param {String} className - * @return {Element} Returns the created line - * @private - */ - TimeAxis.prototype._repaintMajorLine = function (x, orientation, className) { - // reuse redundant line - var line = this.dom.redundant.lines.shift(); - if (!line) { - // create vertical line - line = document.createElement('div'); - this.dom.background.appendChild(line); + // if we bind the keyboard to the div, we have to highlight it to use it. This highlights it on mouse over + if (this.constants.keyboard.bindToWindow == false && this.constants.keyboard.enabled == true) { + this.frame.focus(); } - this.dom.lines.push(line); - var props = this.props; - if (orientation == 'top') { - line.style.top = '0'; + // start a timeout that will check if the mouse is positioned above + // an element + var me = this; + var checkShow = function() { + me._checkShowPopup(pointer); + }; + if (this.popupTimer) { + clearInterval(this.popupTimer); // stop any running calculationTimer } - else { - line.style.top = this.body.domProps.top.height + 'px'; + if (!this.drag.dragging) { + this.popupTimer = setTimeout(checkShow, this.constants.tooltip.delay); } - line.style.left = (x - props.majorLineWidth / 2) + 'px'; - line.style.height = props.majorLineHeight + 'px'; - line.className = 'grid vertical major ' + className; - return line; + /** + * Adding hover highlights + */ + if (this.constants.hover == true) { + // removing all hover highlights + for (var edgeId in this.hoverObj.edges) { + if (this.hoverObj.edges.hasOwnProperty(edgeId)) { + this.hoverObj.edges[edgeId].hover = false; + delete this.hoverObj.edges[edgeId]; + } + } + + // adding hover highlights + var obj = this._getNodeAt(pointer); + if (obj == null) { + obj = this._getEdgeAt(pointer); + } + if (obj != null) { + this._hoverObject(obj); + } + + // removing all node hover highlights except for the selected one. + for (var nodeId in this.hoverObj.nodes) { + if (this.hoverObj.nodes.hasOwnProperty(nodeId)) { + if (obj instanceof Node && obj.id != nodeId || obj instanceof Edge || obj == null) { + this._blurObject(this.hoverObj.nodes[nodeId]); + delete this.hoverObj.nodes[nodeId]; + } + } + } + this.redraw(); + } }; /** - * Determine the size of text on the axis (both major and minor axis). - * The size is calculated only once and then cached in this.props. + * Check if there is an element on the given position in the network + * (a node or edge). If so, and if this element has a title, + * show a popup window with its title. + * + * @param {{x:Number, y:Number}} pointer * @private */ - TimeAxis.prototype._calculateCharSize = function () { - // Note: We calculate char size with every redraw. Size may change, for - // example when any of the timelines parents had display:none for example. + Network.prototype._checkShowPopup = function (pointer) { + var obj = { + left: this._XconvertDOMtoCanvas(pointer.x), + top: this._YconvertDOMtoCanvas(pointer.y), + right: this._XconvertDOMtoCanvas(pointer.x), + bottom: this._YconvertDOMtoCanvas(pointer.y) + }; - // determine the char width and height on the minor axis - if (!this.dom.measureCharMinor) { - this.dom.measureCharMinor = document.createElement('DIV'); - this.dom.measureCharMinor.className = 'text minor measure'; - this.dom.measureCharMinor.style.position = 'absolute'; + var id; + var lastPopupNode = this.popupObj; + var nodeUnderCursor = false; - this.dom.measureCharMinor.appendChild(document.createTextNode('0')); - this.dom.foreground.appendChild(this.dom.measureCharMinor); - } - this.props.minorCharHeight = this.dom.measureCharMinor.clientHeight; - this.props.minorCharWidth = this.dom.measureCharMinor.clientWidth; + if (this.popupObj == undefined) { + // search the nodes for overlap, select the top one in case of multiple nodes + var nodes = this.nodes; + var overlappingNodes = []; + for (id in nodes) { + if (nodes.hasOwnProperty(id)) { + var node = nodes[id]; + if (node.isOverlappingWith(obj)) { + if (node.getTitle() !== undefined) { + overlappingNodes.push(id); + } + } + } + } - // determine the char width and height on the major axis - if (!this.dom.measureCharMajor) { - this.dom.measureCharMajor = document.createElement('DIV'); - this.dom.measureCharMajor.className = 'text major measure'; - this.dom.measureCharMajor.style.position = 'absolute'; + if (overlappingNodes.length > 0) { + // if there are overlapping nodes, select the last one, this is the + // one which is drawn on top of the others + this.popupObj = this.nodes[overlappingNodes[overlappingNodes.length - 1]]; + // if you hover over a node, the title of the edge is not supposed to be shown. + nodeUnderCursor = true; + } + } - this.dom.measureCharMajor.appendChild(document.createTextNode('0')); - this.dom.foreground.appendChild(this.dom.measureCharMajor); + if (this.popupObj === undefined && nodeUnderCursor == false) { + // search the edges for overlap + var edges = this.edges; + var overlappingEdges = []; + for (id in edges) { + if (edges.hasOwnProperty(id)) { + var edge = edges[id]; + if (edge.connected && (edge.getTitle() !== undefined) && + edge.isOverlappingWith(obj)) { + overlappingEdges.push(id); + } + } + } + + if (overlappingEdges.length > 0) { + this.popupObj = this.edges[overlappingEdges[overlappingEdges.length - 1]]; + } + } + + if (this.popupObj) { + // show popup message window + if (this.popupObj != lastPopupNode) { + var me = this; + if (!me.popup) { + me.popup = new Popup(me.frame, me.constants.tooltip); + } + + // adjust a small offset such that the mouse cursor is located in the + // bottom left location of the popup, and you can easily move over the + // popup area + me.popup.setPosition(pointer.x - 3, pointer.y - 3); + me.popup.setText(me.popupObj.getTitle()); + me.popup.show(); + } + } + else { + if (this.popup) { + this.popup.hide(); + } } - this.props.majorCharHeight = this.dom.measureCharMajor.clientHeight; - this.props.majorCharWidth = this.dom.measureCharMajor.clientWidth; }; + /** - * Snap a date to a rounded value. - * The snap intervals are dependent on the current scale and step. - * @param {Date} date the date to be snapped. - * @return {Date} snappedDate + * Check if the popup must be hided, which is the case when the mouse is no + * longer hovering on the object + * @param {{x:Number, y:Number}} pointer + * @private */ - TimeAxis.prototype.snap = function(date) { - return this.step.snap(date); + Network.prototype._checkHidePopup = function (pointer) { + if (!this.popupObj || !this._getNodeAt(pointer) ) { + this.popupObj = undefined; + if (this.popup) { + this.popup.hide(); + } + } }; - module.exports = TimeAxis; - - -/***/ }, -/* 38 */ -/***/ function(module, exports, __webpack_require__) { - - var moment = __webpack_require__(2); - var DateUtil = __webpack_require__(24); - var util = __webpack_require__(1); /** - * @constructor TimeStep - * The class TimeStep is an iterator for dates. You provide a start date and an - * end date. The class itself determines the best scale (step size) based on the - * provided start Date, end Date, and minimumStep. - * - * If minimumStep is provided, the step size is chosen as close as possible - * to the minimumStep but larger than minimumStep. If minimumStep is not - * provided, the scale is set to 1 DAY. - * The minimumStep should correspond with the onscreen size of about 6 characters - * - * Alternatively, you can set a scale by hand. - * After creation, you can initialize the class by executing first(). Then you - * can iterate from the start date to the end date via next(). You can check if - * the end date is reached with the function hasNext(). After each step, you can - * retrieve the current date via getCurrent(). - * The TimeStep has scales ranging from milliseconds, seconds, minutes, hours, - * days, to years. - * - * Version: 1.2 - * - * @param {Date} [start] The start date, for example new Date(2010, 9, 21) - * or new Date(2010, 9, 21, 23, 45, 00) - * @param {Date} [end] The end date - * @param {Number} [minimumStep] Optional. Minimum step size in milliseconds + * Set a new size for the network + * @param {string} width Width in pixels or percentage (for example '800px' + * or '50%') + * @param {string} height Height in pixels or percentage (for example '400px' + * or '30%') */ - function TimeStep(start, end, minimumStep, hiddenDates) { - // variables - this.current = new Date(); - this._start = new Date(); - this._end = new Date(); + Network.prototype.setSize = function(width, height) { + var emitEvent = false; + var oldWidth = this.frame.canvas.width; + var oldHeight = this.frame.canvas.height; + if (width != this.constants.width || height != this.constants.height || this.frame.style.width != width || this.frame.style.height != height) { + this.frame.style.width = width; + this.frame.style.height = height; - this.autoScale = true; - this.scale = 'day'; - this.step = 1; + this.frame.canvas.style.width = '100%'; + this.frame.canvas.style.height = '100%'; - // initialize the range - this.setRange(start, end, minimumStep); + this.frame.canvas.width = this.frame.canvas.clientWidth * this.pixelRatio; + this.frame.canvas.height = this.frame.canvas.clientHeight * this.pixelRatio; - // hidden Dates options - this.switchedDay = false; - this.switchedMonth = false; - this.switchedYear = false; - this.hiddenDates = hiddenDates; - if (hiddenDates === undefined) { - this.hiddenDates = []; + this.constants.width = width; + this.constants.height = height; + + emitEvent = true; } + else { + // this would adapt the width of the canvas to the width from 100% if and only if + // there is a change. - this.format = TimeStep.FORMAT; // default formatting - } + if (this.frame.canvas.width != this.frame.canvas.clientWidth * this.pixelRatio) { + this.frame.canvas.width = this.frame.canvas.clientWidth * this.pixelRatio; + emitEvent = true; + } + if (this.frame.canvas.height != this.frame.canvas.clientHeight * this.pixelRatio) { + this.frame.canvas.height = this.frame.canvas.clientHeight * this.pixelRatio; + emitEvent = true; + } + } - // Time formatting - TimeStep.FORMAT = { - minorLabels: { - millisecond:'SSS', - second: 's', - minute: 'HH:mm', - hour: 'HH:mm', - weekday: 'ddd D', - day: 'D', - month: 'MMM', - year: 'YYYY' - }, - majorLabels: { - millisecond:'HH:mm:ss', - second: 'D MMMM HH:mm', - minute: 'ddd D MMMM', - hour: 'ddd D MMMM', - weekday: 'MMMM YYYY', - day: 'MMMM YYYY', - month: 'YYYY', - year: '' + if (emitEvent == true) { + this.emit('resize', {width:this.frame.canvas.width * this.pixelRatio,height:this.frame.canvas.height * this.pixelRatio, oldWidth: oldWidth * this.pixelRatio, oldHeight: oldHeight * this.pixelRatio}); } }; /** - * Set custom formatting for the minor an major labels of the TimeStep. - * Both `minorLabels` and `majorLabels` are an Object with properties: - * 'millisecond, 'second, 'minute', 'hour', 'weekday, 'day, 'month, 'year'. - * @param {{minorLabels: Object, majorLabels: Object}} format + * Set a data set with nodes for the network + * @param {Array | DataSet | DataView} nodes The data containing the nodes. + * @private */ - TimeStep.prototype.setFormat = function (format) { - var defaultFormat = util.deepExtend({}, TimeStep.FORMAT); - this.format = util.deepExtend(defaultFormat, format); - }; + Network.prototype._setNodes = function(nodes) { + var oldNodesData = this.nodesData; - /** - * Set a new range - * If minimumStep is provided, the step size is chosen as close as possible - * to the minimumStep but larger than minimumStep. If minimumStep is not - * provided, the scale is set to 1 DAY. - * The minimumStep should correspond with the onscreen size of about 6 characters - * @param {Date} [start] The start date and time. - * @param {Date} [end] The end date and time. - * @param {int} [minimumStep] Optional. Minimum step size in milliseconds - */ - TimeStep.prototype.setRange = function(start, end, minimumStep) { - if (!(start instanceof Date) || !(end instanceof Date)) { - throw "No legal start or end date in method setRange"; + if (nodes instanceof DataSet || nodes instanceof DataView) { + this.nodesData = nodes; + } + else if (Array.isArray(nodes)) { + this.nodesData = new DataSet(); + this.nodesData.add(nodes); + } + else if (!nodes) { + this.nodesData = new DataSet(); + } + else { + throw new TypeError('Array or DataSet expected'); } - this._start = (start != undefined) ? new Date(start.valueOf()) : new Date(); - this._end = (end != undefined) ? new Date(end.valueOf()) : new Date(); + if (oldNodesData) { + // unsubscribe from old dataset + util.forEach(this.nodesListeners, function (callback, event) { + oldNodesData.off(event, callback); + }); + } - if (this.autoScale) { - this.setMinimumStep(minimumStep); + // remove drawn nodes + this.nodes = {}; + + if (this.nodesData) { + // subscribe to new dataset + var me = this; + util.forEach(this.nodesListeners, function (callback, event) { + me.nodesData.on(event, callback); + }); + + // draw all new nodes + var ids = this.nodesData.getIds(); + this._addNodes(ids); } + this._updateSelection(); }; /** - * Set the range iterator to the start date. + * Add nodes + * @param {Number[] | String[]} ids + * @private */ - TimeStep.prototype.first = function() { - this.current = new Date(this._start.valueOf()); - this.roundToMinor(); + Network.prototype._addNodes = function(ids) { + var id; + for (var i = 0, len = ids.length; i < len; i++) { + id = ids[i]; + var data = this.nodesData.get(id); + var node = new Node(data, this.images, this.groups, this.constants); + this.nodes[id] = node; // note: this may replace an existing node + if ((node.xFixed == false || node.yFixed == false) && (node.x === null || node.y === null)) { + var radius = 10 * 0.1*ids.length + 10; + var angle = 2 * Math.PI * Math.random(); + if (node.xFixed == false) {node.x = radius * Math.cos(angle);} + if (node.yFixed == false) {node.y = radius * Math.sin(angle);} + } + this.moving = true; + } + + this._updateNodeIndexList(); + if (this.constants.hierarchicalLayout.enabled == true && this.initializing == false) { + this._resetLevels(); + this._setupHierarchicalLayout(); + } + this._updateCalculationNodes(); + this._reconnectEdges(); + this._updateValueRange(this.nodes); + this.updateLabels(); }; /** - * Round the current date to the first minor date value - * This must be executed once when the current date is set to start Date + * Update existing nodes, or create them when not yet existing + * @param {Number[] | String[]} ids + * @private */ - TimeStep.prototype.roundToMinor = function() { - // round to floor - // IMPORTANT: we have no breaks in this switch! (this is no bug) - // noinspection FallThroughInSwitchStatementJS - switch (this.scale) { - case 'year': - this.current.setFullYear(this.step * Math.floor(this.current.getFullYear() / this.step)); - this.current.setMonth(0); - case 'month': this.current.setDate(1); - case 'day': // intentional fall through - case 'weekday': this.current.setHours(0); - case 'hour': this.current.setMinutes(0); - case 'minute': this.current.setSeconds(0); - case 'second': this.current.setMilliseconds(0); - //case 'millisecond': // nothing to do for milliseconds - } - - if (this.step != 1) { - // round down to the first minor value that is a multiple of the current step size - switch (this.scale) { - case 'millisecond': this.current.setMilliseconds(this.current.getMilliseconds() - this.current.getMilliseconds() % this.step); break; - case 'second': this.current.setSeconds(this.current.getSeconds() - this.current.getSeconds() % this.step); break; - case 'minute': this.current.setMinutes(this.current.getMinutes() - this.current.getMinutes() % this.step); break; - case 'hour': this.current.setHours(this.current.getHours() - this.current.getHours() % this.step); break; - case 'weekday': // intentional fall through - case 'day': this.current.setDate((this.current.getDate()-1) - (this.current.getDate()-1) % this.step + 1); break; - case 'month': this.current.setMonth(this.current.getMonth() - this.current.getMonth() % this.step); break; - case 'year': this.current.setFullYear(this.current.getFullYear() - this.current.getFullYear() % this.step); break; - default: break; + Network.prototype._updateNodes = function(ids,changedData) { + var nodes = this.nodes; + for (var i = 0, len = ids.length; i < len; i++) { + var id = ids[i]; + var node = nodes[id]; + var data = changedData[i]; + if (node) { + // update node + node.setProperties(data, this.constants); + } + else { + // create node + node = new Node(properties, this.images, this.groups, this.constants); + nodes[id] = node; } } + this.moving = true; + if (this.constants.hierarchicalLayout.enabled == true && this.initializing == false) { + this._resetLevels(); + this._setupHierarchicalLayout(); + } + this._updateNodeIndexList(); + this._updateValueRange(nodes); }; /** - * Check if the there is a next step - * @return {boolean} true if the current date has not passed the end date + * Remove existing nodes. If nodes do not exist, the method will just ignore it. + * @param {Number[] | String[]} ids + * @private */ - TimeStep.prototype.hasNext = function () { - return (this.current.valueOf() <= this._end.valueOf()); + Network.prototype._removeNodes = function(ids) { + var nodes = this.nodes; + for (var i = 0, len = ids.length; i < len; i++) { + var id = ids[i]; + delete nodes[id]; + } + this._updateNodeIndexList(); + if (this.constants.hierarchicalLayout.enabled == true && this.initializing == false) { + this._resetLevels(); + this._setupHierarchicalLayout(); + } + this._updateCalculationNodes(); + this._reconnectEdges(); + this._updateSelection(); + this._updateValueRange(nodes); }; /** - * Do the next step + * Load edges by reading the data table + * @param {Array | DataSet | DataView} edges The data containing the edges. + * @private + * @private */ - TimeStep.prototype.next = function() { - var prev = this.current.valueOf(); - - // Two cases, needed to prevent issues with switching daylight savings - // (end of March and end of October) - if (this.current.getMonth() < 6) { - switch (this.scale) { - case 'millisecond': + Network.prototype._setEdges = function(edges) { + var oldEdgesData = this.edgesData; - this.current = new Date(this.current.valueOf() + this.step); break; - case 'second': this.current = new Date(this.current.valueOf() + this.step * 1000); break; - case 'minute': this.current = new Date(this.current.valueOf() + this.step * 1000 * 60); break; - case 'hour': - this.current = new Date(this.current.valueOf() + this.step * 1000 * 60 * 60); - // in case of skipping an hour for daylight savings, adjust the hour again (else you get: 0h 5h 9h ... instead of 0h 4h 8h ...) - var h = this.current.getHours(); - this.current.setHours(h - (h % this.step)); - break; - case 'weekday': // intentional fall through - case 'day': this.current.setDate(this.current.getDate() + this.step); break; - case 'month': this.current.setMonth(this.current.getMonth() + this.step); break; - case 'year': this.current.setFullYear(this.current.getFullYear() + this.step); break; - default: break; - } + if (edges instanceof DataSet || edges instanceof DataView) { + this.edgesData = edges; + } + else if (Array.isArray(edges)) { + this.edgesData = new DataSet(); + this.edgesData.add(edges); + } + else if (!edges) { + this.edgesData = new DataSet(); } else { - switch (this.scale) { - case 'millisecond': this.current = new Date(this.current.valueOf() + this.step); break; - case 'second': this.current.setSeconds(this.current.getSeconds() + this.step); break; - case 'minute': this.current.setMinutes(this.current.getMinutes() + this.step); break; - case 'hour': this.current.setHours(this.current.getHours() + this.step); break; - case 'weekday': // intentional fall through - case 'day': this.current.setDate(this.current.getDate() + this.step); break; - case 'month': this.current.setMonth(this.current.getMonth() + this.step); break; - case 'year': this.current.setFullYear(this.current.getFullYear() + this.step); break; - default: break; - } + throw new TypeError('Array or DataSet expected'); } - if (this.step != 1) { - // round down to the correct major value - switch (this.scale) { - case 'millisecond': if(this.current.getMilliseconds() < this.step) this.current.setMilliseconds(0); break; - case 'second': if(this.current.getSeconds() < this.step) this.current.setSeconds(0); break; - case 'minute': if(this.current.getMinutes() < this.step) this.current.setMinutes(0); break; - case 'hour': if(this.current.getHours() < this.step) this.current.setHours(0); break; - case 'weekday': // intentional fall through - case 'day': if(this.current.getDate() < this.step+1) this.current.setDate(1); break; - case 'month': if(this.current.getMonth() < this.step) this.current.setMonth(0); break; - case 'year': break; // nothing to do for year - default: break; - } + if (oldEdgesData) { + // unsubscribe from old dataset + util.forEach(this.edgesListeners, function (callback, event) { + oldEdgesData.off(event, callback); + }); } - // safety mechanism: if current time is still unchanged, move to the end - if (this.current.valueOf() == prev) { - this.current = new Date(this._end.valueOf()); - } + // remove drawn edges + this.edges = {}; - DateUtil.stepOverHiddenDates(this, prev); - }; + if (this.edgesData) { + // subscribe to new dataset + var me = this; + util.forEach(this.edgesListeners, function (callback, event) { + me.edgesData.on(event, callback); + }); + // draw all new nodes + var ids = this.edgesData.getIds(); + this._addEdges(ids); + } - /** - * Get the current datetime - * @return {Date} current The current date - */ - TimeStep.prototype.getCurrent = function() { - return this.current; + this._reconnectEdges(); }; /** - * Set a custom scale. Autoscaling will be disabled. - * For example setScale('minute', 5) will result - * in minor steps of 5 minutes, and major steps of an hour. - * - * @param {{scale: string, step: number}} params - * An object containing two properties: - * - A string 'scale'. Choose from 'millisecond', 'second', - * 'minute', 'hour', 'weekday, 'day, 'month, 'year'. - * - A number 'step'. A step size, by default 1. - * Choose for example 1, 2, 5, or 10. + * Add edges + * @param {Number[] | String[]} ids + * @private */ - TimeStep.prototype.setScale = function(params) { - if (params && typeof params.scale == 'string') { - this.scale = params.scale; - this.step = params.step > 0 ? params.step : 1; - this.autoScale = false; + Network.prototype._addEdges = function (ids) { + var edges = this.edges, + edgesData = this.edgesData; + + for (var i = 0, len = ids.length; i < len; i++) { + var id = ids[i]; + + var oldEdge = edges[id]; + if (oldEdge) { + oldEdge.disconnect(); + } + + var data = edgesData.get(id, {"showInternalIds" : true}); + edges[id] = new Edge(data, this, this.constants); + } + this.moving = true; + this._updateValueRange(edges); + this._createBezierNodes(); + this._updateCalculationNodes(); + if (this.constants.hierarchicalLayout.enabled == true && this.initializing == false) { + this._resetLevels(); + this._setupHierarchicalLayout(); } }; /** - * Enable or disable autoscaling - * @param {boolean} enable If true, autoascaling is set true + * Update existing edges, or create them when not yet existing + * @param {Number[] | String[]} ids + * @private */ - TimeStep.prototype.setAutoScale = function (enable) { - this.autoScale = enable; - }; + Network.prototype._updateEdges = function (ids) { + var edges = this.edges, + edgesData = this.edgesData; + for (var i = 0, len = ids.length; i < len; i++) { + var id = ids[i]; - - /** - * Automatically determine the scale that bests fits the provided minimum step - * @param {Number} [minimumStep] The minimum step size in milliseconds - */ - TimeStep.prototype.setMinimumStep = function(minimumStep) { - if (minimumStep == undefined) { - return; + var data = edgesData.get(id); + var edge = edges[id]; + if (edge) { + // update edge + edge.disconnect(); + edge.setProperties(data, this.constants); + edge.connect(); + } + else { + // create edge + edge = new Edge(data, this, this.constants); + this.edges[id] = edge; + } } - //var b = asc + ds; - - var stepYear = (1000 * 60 * 60 * 24 * 30 * 12); - var stepMonth = (1000 * 60 * 60 * 24 * 30); - var stepDay = (1000 * 60 * 60 * 24); - var stepHour = (1000 * 60 * 60); - var stepMinute = (1000 * 60); - var stepSecond = (1000); - var stepMillisecond= (1); - - // find the smallest step that is larger than the provided minimumStep - if (stepYear*1000 > minimumStep) {this.scale = 'year'; this.step = 1000;} - if (stepYear*500 > minimumStep) {this.scale = 'year'; this.step = 500;} - if (stepYear*100 > minimumStep) {this.scale = 'year'; this.step = 100;} - if (stepYear*50 > minimumStep) {this.scale = 'year'; this.step = 50;} - if (stepYear*10 > minimumStep) {this.scale = 'year'; this.step = 10;} - if (stepYear*5 > minimumStep) {this.scale = 'year'; this.step = 5;} - if (stepYear > minimumStep) {this.scale = 'year'; this.step = 1;} - if (stepMonth*3 > minimumStep) {this.scale = 'month'; this.step = 3;} - if (stepMonth > minimumStep) {this.scale = 'month'; this.step = 1;} - if (stepDay*5 > minimumStep) {this.scale = 'day'; this.step = 5;} - if (stepDay*2 > minimumStep) {this.scale = 'day'; this.step = 2;} - if (stepDay > minimumStep) {this.scale = 'day'; this.step = 1;} - if (stepDay/2 > minimumStep) {this.scale = 'weekday'; this.step = 1;} - if (stepHour*4 > minimumStep) {this.scale = 'hour'; this.step = 4;} - if (stepHour > minimumStep) {this.scale = 'hour'; this.step = 1;} - if (stepMinute*15 > minimumStep) {this.scale = 'minute'; this.step = 15;} - if (stepMinute*10 > minimumStep) {this.scale = 'minute'; this.step = 10;} - if (stepMinute*5 > minimumStep) {this.scale = 'minute'; this.step = 5;} - if (stepMinute > minimumStep) {this.scale = 'minute'; this.step = 1;} - if (stepSecond*15 > minimumStep) {this.scale = 'second'; this.step = 15;} - if (stepSecond*10 > minimumStep) {this.scale = 'second'; this.step = 10;} - if (stepSecond*5 > minimumStep) {this.scale = 'second'; this.step = 5;} - if (stepSecond > minimumStep) {this.scale = 'second'; this.step = 1;} - if (stepMillisecond*200 > minimumStep) {this.scale = 'millisecond'; this.step = 200;} - if (stepMillisecond*100 > minimumStep) {this.scale = 'millisecond'; this.step = 100;} - if (stepMillisecond*50 > minimumStep) {this.scale = 'millisecond'; this.step = 50;} - if (stepMillisecond*10 > minimumStep) {this.scale = 'millisecond'; this.step = 10;} - if (stepMillisecond*5 > minimumStep) {this.scale = 'millisecond'; this.step = 5;} - if (stepMillisecond > minimumStep) {this.scale = 'millisecond'; this.step = 1;} + this._createBezierNodes(); + if (this.constants.hierarchicalLayout.enabled == true && this.initializing == false) { + this._resetLevels(); + this._setupHierarchicalLayout(); + } + this.moving = true; + this._updateValueRange(edges); }; /** - * Snap a date to a rounded value. - * The snap intervals are dependent on the current scale and step. - * @param {Date} date the date to be snapped. - * @return {Date} snappedDate + * Remove existing edges. Non existing ids will be ignored + * @param {Number[] | String[]} ids + * @private */ - TimeStep.prototype.snap = function(date) { - var clone = new Date(date.valueOf()); - - if (this.scale == 'year') { - var year = clone.getFullYear() + Math.round(clone.getMonth() / 12); - clone.setFullYear(Math.round(year / this.step) * this.step); - clone.setMonth(0); - clone.setDate(0); - clone.setHours(0); - clone.setMinutes(0); - clone.setSeconds(0); - clone.setMilliseconds(0); - } - else if (this.scale == 'month') { - if (clone.getDate() > 15) { - clone.setDate(1); - clone.setMonth(clone.getMonth() + 1); - // important: first set Date to 1, after that change the month. - } - else { - clone.setDate(1); - } - - clone.setHours(0); - clone.setMinutes(0); - clone.setSeconds(0); - clone.setMilliseconds(0); - } - else if (this.scale == 'day') { - //noinspection FallthroughInSwitchStatementJS - switch (this.step) { - case 5: - case 2: - clone.setHours(Math.round(clone.getHours() / 24) * 24); break; - default: - clone.setHours(Math.round(clone.getHours() / 12) * 12); break; - } - clone.setMinutes(0); - clone.setSeconds(0); - clone.setMilliseconds(0); - } - else if (this.scale == 'weekday') { - //noinspection FallthroughInSwitchStatementJS - switch (this.step) { - case 5: - case 2: - clone.setHours(Math.round(clone.getHours() / 12) * 12); break; - default: - clone.setHours(Math.round(clone.getHours() / 6) * 6); break; - } - clone.setMinutes(0); - clone.setSeconds(0); - clone.setMilliseconds(0); - } - else if (this.scale == 'hour') { - switch (this.step) { - case 4: - clone.setMinutes(Math.round(clone.getMinutes() / 60) * 60); break; - default: - clone.setMinutes(Math.round(clone.getMinutes() / 30) * 30); break; - } - clone.setSeconds(0); - clone.setMilliseconds(0); - } else if (this.scale == 'minute') { - //noinspection FallthroughInSwitchStatementJS - switch (this.step) { - case 15: - case 10: - clone.setMinutes(Math.round(clone.getMinutes() / 5) * 5); - clone.setSeconds(0); - break; - case 5: - clone.setSeconds(Math.round(clone.getSeconds() / 60) * 60); break; - default: - clone.setSeconds(Math.round(clone.getSeconds() / 30) * 30); break; - } - clone.setMilliseconds(0); - } - else if (this.scale == 'second') { - //noinspection FallthroughInSwitchStatementJS - switch (this.step) { - case 15: - case 10: - clone.setSeconds(Math.round(clone.getSeconds() / 5) * 5); - clone.setMilliseconds(0); - break; - case 5: - clone.setMilliseconds(Math.round(clone.getMilliseconds() / 1000) * 1000); break; - default: - clone.setMilliseconds(Math.round(clone.getMilliseconds() / 500) * 500); break; + Network.prototype._removeEdges = function (ids) { + var edges = this.edges; + for (var i = 0, len = ids.length; i < len; i++) { + var id = ids[i]; + var edge = edges[id]; + if (edge) { + if (edge.via != null) { + delete this.sectors['support']['nodes'][edge.via.id]; + } + edge.disconnect(); + delete edges[id]; } } - else if (this.scale == 'millisecond') { - var step = this.step > 5 ? this.step / 2 : 1; - clone.setMilliseconds(Math.round(clone.getMilliseconds() / step) * step); + + this.moving = true; + this._updateValueRange(edges); + if (this.constants.hierarchicalLayout.enabled == true && this.initializing == false) { + this._resetLevels(); + this._setupHierarchicalLayout(); } - - return clone; + this._updateCalculationNodes(); }; /** - * Check if the current value is a major value (for example when the step - * is DAY, a major value is each first day of the MONTH) - * @return {boolean} true if current date is major, else false. + * Reconnect all edges + * @private */ - TimeStep.prototype.isMajor = function() { - if (this.switchedYear == true) { - this.switchedYear = false; - switch (this.scale) { - case 'year': - case 'month': - case 'weekday': - case 'day': - case 'hour': - case 'minute': - case 'second': - case 'millisecond': - return true; - default: - return false; + Network.prototype._reconnectEdges = function() { + var id, + nodes = this.nodes, + edges = this.edges; + for (id in nodes) { + if (nodes.hasOwnProperty(id)) { + nodes[id].edges = []; + nodes[id].dynamicEdges = []; } } - else if (this.switchedMonth == true) { - this.switchedMonth = false; - switch (this.scale) { - case 'weekday': - case 'day': - case 'hour': - case 'minute': - case 'second': - case 'millisecond': - return true; - default: - return false; + + for (id in edges) { + if (edges.hasOwnProperty(id)) { + var edge = edges[id]; + edge.from = null; + edge.to = null; + edge.connect(); } } - else if (this.switchedDay == true) { - this.switchedDay = false; - switch (this.scale) { - case 'millisecond': - case 'second': - case 'minute': - case 'hour': - return true; - default: - return false; + }; + + /** + * Update the values of all object in the given array according to the current + * value range of the objects in the array. + * @param {Object} obj An object containing a set of Edges or Nodes + * The objects must have a method getValue() and + * setValueRange(min, max). + * @private + */ + Network.prototype._updateValueRange = function(obj) { + var id; + + // determine the range of the objects + var valueMin = undefined; + var valueMax = undefined; + for (id in obj) { + if (obj.hasOwnProperty(id)) { + var value = obj[id].getValue(); + if (value !== undefined) { + valueMin = (valueMin === undefined) ? value : Math.min(value, valueMin); + valueMax = (valueMax === undefined) ? value : Math.max(value, valueMax); + } } } - switch (this.scale) { - case 'millisecond': - return (this.current.getMilliseconds() == 0); - case 'second': - return (this.current.getSeconds() == 0); - case 'minute': - return (this.current.getHours() == 0) && (this.current.getMinutes() == 0); - case 'hour': - return (this.current.getHours() == 0); - case 'weekday': // intentional fall through - case 'day': - return (this.current.getDate() == 1); - case 'month': - return (this.current.getMonth() == 0); - case 'year': - return false; - default: - return false; + // adjust the range of all objects + if (valueMin !== undefined && valueMax !== undefined) { + for (id in obj) { + if (obj.hasOwnProperty(id)) { + obj[id].setValueRange(valueMin, valueMax); + } + } } }; - /** - * Returns formatted text for the minor axislabel, depending on the current - * date and the scale. For example when scale is MINUTE, the current time is - * formatted as "hh:mm". - * @param {Date} [date] custom date. if not provided, current date is taken + * Redraw the network with the current data + * chart will be resized too. */ - TimeStep.prototype.getLabelMinor = function(date) { - if (date == undefined) { - date = this.current; - } - - var format = this.format.minorLabels[this.scale]; - return (format && format.length > 0) ? moment(date).format(format) : ''; + Network.prototype.redraw = function() { + this.setSize(this.constants.width, this.constants.height); + this._redraw(); }; /** - * Returns formatted text for the major axis label, depending on the current - * date and the scale. For example when scale is MINUTE, the major scale is - * hours, and the hour will be formatted as "hh". - * @param {Date} [date] custom date. if not provided, current date is taken + * Redraw the network with the current data + * @param hidden | used to get the first estimate of the node sizes. only the nodes are drawn after which they are quickly drawn over. + * @private */ - TimeStep.prototype.getLabelMajor = function(date) { - if (date == undefined) { - date = this.current; - } + Network.prototype._redraw = function(hidden) { + var ctx = this.frame.canvas.getContext('2d'); - var format = this.format.majorLabels[this.scale]; - return (format && format.length > 0) ? moment(date).format(format) : ''; - }; + ctx.setTransform(this.pixelRatio, 0, 0, this.pixelRatio, 0, 0); - TimeStep.prototype.getClassName = function() { - var m = moment(this.current); - var date = m.locale ? m.locale('en') : m.lang('en'); // old versions of moment have .lang() function - var step = this.step; + // clear the canvas + var w = this.frame.canvas.width * this.pixelRatio; + var h = this.frame.canvas.height * this.pixelRatio; + ctx.clearRect(0, 0, w, h); - function even(value) { - return (value / step % 2 == 0) ? ' even' : ' odd'; - } + // set scaling and translation + ctx.save(); + ctx.translate(this.translation.x, this.translation.y); + ctx.scale(this.scale, this.scale); - function today(date) { - if (date.isSame(new Date(), 'day')) { - return ' today'; - } - if (date.isSame(moment().add(1, 'day'), 'day')) { - return ' tomorrow'; - } - if (date.isSame(moment().add(-1, 'day'), 'day')) { - return ' yesterday'; - } - return ''; - } + this.canvasTopLeft = { + "x": this._XconvertDOMtoCanvas(0), + "y": this._YconvertDOMtoCanvas(0) + }; + this.canvasBottomRight = { + "x": this._XconvertDOMtoCanvas(this.frame.canvas.clientWidth * this.pixelRatio), + "y": this._YconvertDOMtoCanvas(this.frame.canvas.clientHeight * this.pixelRatio) + }; - function currentWeek(date) { - return date.isSame(new Date(), 'week') ? ' current-week' : ''; + if (!(hidden == true)) { + this._doInAllSectors("_drawAllSectorNodes", ctx); + if (this.drag.dragging == false || this.drag.dragging === undefined || this.constants.hideEdgesOnDrag == false) { + this._doInAllSectors("_drawEdges", ctx); + } } - function currentMonth(date) { - return date.isSame(new Date(), 'month') ? ' current-month' : ''; + if (this.drag.dragging == false || this.drag.dragging === undefined || this.constants.hideNodesOnDrag == false) { + this._doInAllSectors("_drawNodes",ctx,false); } - function currentYear(date) { - return date.isSame(new Date(), 'year') ? ' current-year' : ''; + if (!(hidden == true)) { + if (this.controlNodesActive == true) { + this._doInAllSectors("_drawControlNodes", ctx); + } } - switch (this.scale) { - case 'millisecond': - return even(date.milliseconds()).trim(); - - case 'second': - return even(date.seconds()).trim(); - - case 'minute': - return even(date.minutes()).trim(); - - case 'hour': - var hours = date.hours(); - if (this.step == 4) { - hours = hours + '-' + (hours + 4); - } - return hours + 'h' + today(date) + even(date.hours()); - - case 'weekday': - return date.format('dddd').toLowerCase() + - today(date) + currentWeek(date) + even(date.date()); - - case 'day': - var day = date.date(); - var month = date.format('MMMM').toLowerCase(); - return 'day' + day + ' ' + month + currentMonth(date) + even(day - 1); - - case 'month': - return date.format('MMMM').toLowerCase() + - currentMonth(date) + even(date.month()); + // this._doInSupportSector("_drawNodes",ctx,true); + // this._drawTree(ctx,"#F00F0F"); - case 'year': - var year = date.year(); - return 'year' + year + currentYear(date)+ even(year); + // restore original scaling and translation + ctx.restore(); - default: - return ''; + if (hidden == true) { + ctx.clearRect(0, 0, w, h); } }; - module.exports = TimeStep; - - -/***/ }, -/* 39 */ -/***/ function(module, exports, __webpack_require__) { - - var util = __webpack_require__(1); - var Component = __webpack_require__(23); - var moment = __webpack_require__(2); - var locales = __webpack_require__(40); - /** - * A current time bar - * @param {{range: Range, dom: Object, domProps: Object}} body - * @param {Object} [options] Available parameters: - * {Boolean} [showCurrentTime] - * @constructor CurrentTime - * @extends Component + * Set the translation of the network + * @param {Number} offsetX Horizontal offset + * @param {Number} offsetY Vertical offset + * @private */ - function CurrentTime (body, options) { - this.body = body; - - // default options - this.defaultOptions = { - showCurrentTime: true, - - locales: locales, - locale: 'en' - }; - this.options = util.extend({}, this.defaultOptions); - this.offset = 0; - - this._create(); + Network.prototype._setTranslation = function(offsetX, offsetY) { + if (this.translation === undefined) { + this.translation = { + x: 0, + y: 0 + }; + } - this.setOptions(options); - } + if (offsetX !== undefined) { + this.translation.x = offsetX; + } + if (offsetY !== undefined) { + this.translation.y = offsetY; + } - CurrentTime.prototype = new Component(); + this.emit('viewChanged'); + }; /** - * Create the HTML DOM for the current time bar + * Get the translation of the network + * @return {Object} translation An object with parameters x and y, both a number * @private */ - CurrentTime.prototype._create = function() { - var bar = document.createElement('div'); - bar.className = 'currenttime'; - bar.style.position = 'absolute'; - bar.style.top = '0px'; - bar.style.height = '100%'; - - this.bar = bar; + Network.prototype._getTranslation = function() { + return { + x: this.translation.x, + y: this.translation.y + }; }; /** - * Destroy the CurrentTime bar + * Scale the network + * @param {Number} scale Scaling factor 1.0 is unscaled + * @private */ - CurrentTime.prototype.destroy = function () { - this.options.showCurrentTime = false; - this.redraw(); // will remove the bar from the DOM and stop refreshing - - this.body = null; + Network.prototype._setScale = function(scale) { + this.scale = scale; }; /** - * Set options for the component. Options will be merged in current options. - * @param {Object} options Available parameters: - * {boolean} [showCurrentTime] + * Get the current scale of the network + * @return {Number} scale Scaling factor 1.0 is unscaled + * @private */ - CurrentTime.prototype.setOptions = function(options) { - if (options) { - // copy all options that we know - util.selectiveExtend(['showCurrentTime', 'locale', 'locales'], this.options, options); - } + Network.prototype._getScale = function() { + return this.scale; }; /** - * Repaint the component - * @return {boolean} Returns true if the component is resized + * Convert the X coordinate in DOM-space (coordinate point in browser relative to the container div) to + * the X coordinate in canvas-space (the simulation sandbox, which the camera looks upon) + * @param {number} x + * @returns {number} + * @private */ - CurrentTime.prototype.redraw = function() { - if (this.options.showCurrentTime) { - var parent = this.body.dom.backgroundVertical; - if (this.bar.parentNode != parent) { - // attach to the dom - if (this.bar.parentNode) { - this.bar.parentNode.removeChild(this.bar); - } - parent.appendChild(this.bar); - - this.start(); - } - - var now = new Date(new Date().valueOf() + this.offset); - var x = this.body.util.toScreen(now); - - var locale = this.options.locales[this.options.locale]; - var title = locale.current + ' ' + locale.time + ': ' + moment(now).format('dddd, MMMM Do YYYY, H:mm:ss'); - title = title.charAt(0).toUpperCase() + title.substring(1); - - this.bar.style.left = x + 'px'; - this.bar.title = title; - } - else { - // remove the line from the DOM - if (this.bar.parentNode) { - this.bar.parentNode.removeChild(this.bar); - } - this.stop(); - } - - return false; + Network.prototype._XconvertDOMtoCanvas = function(x) { + return (x - this.translation.x) / this.scale; }; /** - * Start auto refreshing the current time bar + * Convert the X coordinate in canvas-space (the simulation sandbox, which the camera looks upon) to + * the X coordinate in DOM-space (coordinate point in browser relative to the container div) + * @param {number} x + * @returns {number} + * @private */ - CurrentTime.prototype.start = function() { - var me = this; - - function update () { - me.stop(); - - // determine interval to refresh - var scale = me.body.range.conversion(me.body.domProps.center.width).scale; - var interval = 1 / scale / 10; - if (interval < 30) interval = 30; - if (interval > 1000) interval = 1000; - - me.redraw(); - - // start a timer to adjust for the new time - me.currentTimeTimer = setTimeout(update, interval); - } - - update(); + Network.prototype._XconvertCanvasToDOM = function(x) { + return x * this.scale + this.translation.x; }; /** - * Stop auto refreshing the current time bar + * Convert the Y coordinate in DOM-space (coordinate point in browser relative to the container div) to + * the Y coordinate in canvas-space (the simulation sandbox, which the camera looks upon) + * @param {number} y + * @returns {number} + * @private */ - CurrentTime.prototype.stop = function() { - if (this.currentTimeTimer !== undefined) { - clearTimeout(this.currentTimeTimer); - delete this.currentTimeTimer; - } + Network.prototype._YconvertDOMtoCanvas = function(y) { + return (y - this.translation.y) / this.scale; }; /** - * Set a current time. This can be used for example to ensure that a client's - * time is synchronized with a shared server time. - * @param {Date | String | Number} time A Date, unix timestamp, or - * ISO date string. + * Convert the Y coordinate in canvas-space (the simulation sandbox, which the camera looks upon) to + * the Y coordinate in DOM-space (coordinate point in browser relative to the container div) + * @param {number} y + * @returns {number} + * @private */ - CurrentTime.prototype.setCurrentTime = function(time) { - var t = util.convert(time, 'Date').valueOf(); - var now = new Date().valueOf(); - this.offset = t - now; - this.redraw(); + Network.prototype._YconvertCanvasToDOM = function(y) { + return y * this.scale + this.translation.y ; }; + /** - * Get the current time. - * @return {Date} Returns the current time. + * + * @param {object} pos = {x: number, y: number} + * @returns {{x: number, y: number}} + * @constructor */ - CurrentTime.prototype.getCurrentTime = function() { - return new Date(new Date().valueOf() + this.offset); - }; - - module.exports = CurrentTime; - - -/***/ }, -/* 40 */ -/***/ function(module, exports, __webpack_require__) { - - // English - exports['en'] = { - current: 'current', - time: 'time' + Network.prototype.canvasToDOM = function (pos) { + return {x: this._XconvertCanvasToDOM(pos.x), y: this._YconvertCanvasToDOM(pos.y)}; }; - exports['en_EN'] = exports['en']; - exports['en_US'] = exports['en']; - // Dutch - exports['nl'] = { - custom: 'aangepaste', - time: 'tijd' + /** + * + * @param {object} pos = {x: number, y: number} + * @returns {{x: number, y: number}} + * @constructor + */ + Network.prototype.DOMtoCanvas = function (pos) { + return {x: this._XconvertDOMtoCanvas(pos.x), y: this._YconvertDOMtoCanvas(pos.y)}; }; - exports['nl_NL'] = exports['nl']; - exports['nl_BE'] = exports['nl']; - - -/***/ }, -/* 41 */ -/***/ function(module, exports, __webpack_require__) { - - var Hammer = __webpack_require__(19); - var util = __webpack_require__(1); - var Component = __webpack_require__(23); - var moment = __webpack_require__(2); - var locales = __webpack_require__(40); /** - * A custom time bar - * @param {{range: Range, dom: Object}} body - * @param {Object} [options] Available parameters: - * {Boolean} [showCustomTime] - * @constructor CustomTime - * @extends Component + * Redraw all nodes + * The 2d context of a HTML canvas can be retrieved by canvas.getContext('2d'); + * @param {CanvasRenderingContext2D} ctx + * @param {Boolean} [alwaysShow] + * @private */ + Network.prototype._drawNodes = function(ctx,alwaysShow) { + if (alwaysShow === undefined) { + alwaysShow = false; + } - function CustomTime (body, options) { - this.body = body; - - // default options - this.defaultOptions = { - showCustomTime: false, - locales: locales, - locale: 'en' - }; - this.options = util.extend({}, this.defaultOptions); - - this.customTime = new Date(); - this.eventParams = {}; // stores state parameters while dragging the bar - - // create the DOM - this._create(); + // first draw the unselected nodes + var nodes = this.nodes; + var selected = []; - this.setOptions(options); - } + for (var id in nodes) { + if (nodes.hasOwnProperty(id)) { + nodes[id].setScaleAndPos(this.scale,this.canvasTopLeft,this.canvasBottomRight); + if (nodes[id].isSelected()) { + selected.push(id); + } + else { + if (nodes[id].inArea() || alwaysShow) { + nodes[id].draw(ctx); + } + } + } + } - CustomTime.prototype = new Component(); + // draw the selected nodes on top + for (var s = 0, sMax = selected.length; s < sMax; s++) { + if (nodes[selected[s]].inArea() || alwaysShow) { + nodes[selected[s]].draw(ctx); + } + } + }; /** - * Set options for the component. Options will be merged in current options. - * @param {Object} options Available parameters: - * {boolean} [showCustomTime] + * Redraw all edges + * The 2d context of a HTML canvas can be retrieved by canvas.getContext('2d'); + * @param {CanvasRenderingContext2D} ctx + * @private */ - CustomTime.prototype.setOptions = function(options) { - if (options) { - // copy all options that we know - util.selectiveExtend(['showCustomTime', 'locale', 'locales'], this.options, options); + Network.prototype._drawEdges = function(ctx) { + var edges = this.edges; + for (var id in edges) { + if (edges.hasOwnProperty(id)) { + var edge = edges[id]; + edge.setScale(this.scale); + if (edge.connected) { + edges[id].draw(ctx); + } + } } }; /** - * Create the DOM for the custom time + * Redraw all edges + * The 2d context of a HTML canvas can be retrieved by canvas.getContext('2d'); + * @param {CanvasRenderingContext2D} ctx * @private */ - CustomTime.prototype._create = function() { - var bar = document.createElement('div'); - bar.className = 'customtime'; - bar.style.position = 'absolute'; - bar.style.top = '0px'; - bar.style.height = '100%'; - this.bar = bar; - - var drag = document.createElement('div'); - drag.style.position = 'relative'; - drag.style.top = '0px'; - drag.style.left = '-10px'; - drag.style.height = '100%'; - drag.style.width = '20px'; - bar.appendChild(drag); - - // attach event listeners - this.hammer = Hammer(bar, { - prevent_default: true - }); - this.hammer.on('dragstart', this._onDragStart.bind(this)); - this.hammer.on('drag', this._onDrag.bind(this)); - this.hammer.on('dragend', this._onDragEnd.bind(this)); + Network.prototype._drawControlNodes = function(ctx) { + var edges = this.edges; + for (var id in edges) { + if (edges.hasOwnProperty(id)) { + edges[id]._drawControlNodes(ctx); + } + } }; /** - * Destroy the CustomTime bar + * Find a stable position for all nodes + * @private */ - CustomTime.prototype.destroy = function () { - this.options.showCustomTime = false; - this.redraw(); // will remove the bar from the DOM + Network.prototype._stabilize = function() { + if (this.constants.freezeForStabilization == true) { + this._freezeDefinedNodes(); + } - this.hammer.enable(false); - this.hammer = null; + // find stable position + var count = 0; + while (this.moving && count < this.constants.stabilizationIterations) { + this._physicsTick(); + count++; + } - this.body = null; + if (this.constants.zoomExtentOnStabilize == true) { + this.zoomExtent(undefined, false, true); + } + + if (this.constants.freezeForStabilization == true) { + this._restoreFrozenNodes(); + } }; /** - * Repaint the component - * @return {boolean} Returns true if the component is resized + * When initializing and stabilizing, we can freeze nodes with a predefined position. This greatly speeds up stabilization + * because only the supportnodes for the smoothCurves have to settle. + * + * @private */ - CustomTime.prototype.redraw = function () { - if (this.options.showCustomTime) { - var parent = this.body.dom.backgroundVertical; - if (this.bar.parentNode != parent) { - // attach to the dom - if (this.bar.parentNode) { - this.bar.parentNode.removeChild(this.bar); + Network.prototype._freezeDefinedNodes = function() { + var nodes = this.nodes; + for (var id in nodes) { + if (nodes.hasOwnProperty(id)) { + if (nodes[id].x != null && nodes[id].y != null) { + nodes[id].fixedData.x = nodes[id].xFixed; + nodes[id].fixedData.y = nodes[id].yFixed; + nodes[id].xFixed = true; + nodes[id].yFixed = true; } - parent.appendChild(this.bar); - } - - var x = this.body.util.toScreen(this.customTime); - - var locale = this.options.locales[this.options.locale]; - var title = locale.time + ': ' + moment(this.customTime).format('dddd, MMMM Do YYYY, H:mm:ss'); - title = title.charAt(0).toUpperCase() + title.substring(1); - - this.bar.style.left = x + 'px'; - this.bar.title = title; - } - else { - // remove the line from the DOM - if (this.bar.parentNode) { - this.bar.parentNode.removeChild(this.bar); } } - - return false; }; /** - * Set custom time. - * @param {Date | number | string} time + * Unfreezes the nodes that have been frozen by _freezeDefinedNodes. + * + * @private */ - CustomTime.prototype.setCustomTime = function(time) { - this.customTime = util.convert(time, 'Date'); - this.redraw(); + Network.prototype._restoreFrozenNodes = function() { + var nodes = this.nodes; + for (var id in nodes) { + if (nodes.hasOwnProperty(id)) { + if (nodes[id].fixedData.x != null) { + nodes[id].xFixed = nodes[id].fixedData.x; + nodes[id].yFixed = nodes[id].fixedData.y; + } + } + } }; + /** - * Retrieve the current custom time. - * @return {Date} customTime + * Check if any of the nodes is still moving + * @param {number} vmin the minimum velocity considered as 'moving' + * @return {boolean} true if moving, false if non of the nodes is moving + * @private */ - CustomTime.prototype.getCustomTime = function() { - return new Date(this.customTime.valueOf()); + Network.prototype._isMoving = function(vmin) { + var nodes = this.nodes; + for (var id in nodes) { + if (nodes.hasOwnProperty(id) && nodes[id].isMoving(vmin)) { + return true; + } + } + return false; }; + /** - * Start moving horizontally - * @param {Event} event + * /** + * Perform one discrete step for all nodes + * * @private */ - CustomTime.prototype._onDragStart = function(event) { - this.eventParams.dragging = true; - this.eventParams.customTime = this.customTime; + Network.prototype._discreteStepNodes = function() { + var interval = this.physicsDiscreteStepsize; + var nodes = this.nodes; + var nodeId; + var nodesPresent = false; - event.stopPropagation(); - event.preventDefault(); + if (this.constants.maxVelocity > 0) { + for (nodeId in nodes) { + if (nodes.hasOwnProperty(nodeId)) { + nodes[nodeId].discreteStepLimited(interval, this.constants.maxVelocity); + nodesPresent = true; + } + } + } + else { + for (nodeId in nodes) { + if (nodes.hasOwnProperty(nodeId)) { + nodes[nodeId].discreteStep(interval); + nodesPresent = true; + } + } + } + + if (nodesPresent == true) { + var vminCorrected = this.constants.minVelocity / Math.max(this.scale,0.05); + if (vminCorrected > 0.5*this.constants.maxVelocity) { + return true; + } + else { + return this._isMoving(vminCorrected); + } + } + return false; }; + + Network.prototype._revertPhysicsState = function() { + var nodes = this.nodes; + for (var nodeId in nodes) { + if (nodes.hasOwnProperty(nodeId)) { + nodes[nodeId].revertPosition(); + } + } + } + + Network.prototype._revertPhysicsTick = function() { + this._doInAllActiveSectors("_revertPhysicsState"); + if (this.constants.smoothCurves.enabled == true && this.constants.smoothCurves.dynamic == true) { + this._doInSupportSector("_revertPhysicsState"); + } + } + /** - * Perform moving operating. - * @param {Event} event + * A single simulation step (or "tick") in the physics simulation + * * @private */ - CustomTime.prototype._onDrag = function (event) { - if (!this.eventParams.dragging) return; + Network.prototype._physicsTick = function() { + if (!this.freezeSimulation) { + if (this.moving == true) { + var mainMovingStatus = false; + var supportMovingStatus = false; - var deltaX = event.gesture.deltaX, - x = this.body.util.toScreen(this.eventParams.customTime) + deltaX, - time = this.body.util.toTime(x); + this._doInAllActiveSectors("_initializeForceCalculation"); + var mainMoving = this._doInAllActiveSectors("_discreteStepNodes"); + if (this.constants.smoothCurves.enabled == true && this.constants.smoothCurves.dynamic == true) { + supportMovingStatus = this._doInSupportSector("_discreteStepNodes"); + } - this.setCustomTime(time); + // gather movement data from all sectors, if one moves, we are NOT stabilzied + for (var i = 0; i < mainMoving.length; i++) {mainMovingStatus = mainMoving[0] || mainMovingStatus;} - // fire a timechange event - this.body.emitter.emit('timechange', { - time: new Date(this.customTime.valueOf()) - }); + // determine if the network has stabilzied + this.moving = mainMovingStatus || supportMovingStatus; - event.stopPropagation(); - event.preventDefault(); + if (this.moving == false) { + this._revertPhysicsTick(); + } + else { + // this is here to ensure that there is no start event when the network is already stable. + if (this.startedStabilization == false) { + this.emit("startStabilization"); + this.startedStabilization = true; + } + } + + this.stabilizationIterations++; + } + } }; + /** - * Stop moving operating. - * @param {event} event + * This function runs one step of the animation. It calls an x amount of physics ticks and one render tick. + * It reschedules itself at the beginning of the function + * * @private */ - CustomTime.prototype._onDragEnd = function (event) { - if (!this.eventParams.dragging) return; + Network.prototype._animationStep = function() { + // reset the timer so a new scheduled animation step can be set + this.timer = undefined; - // fire a timechanged event - this.body.emitter.emit('timechanged', { - time: new Date(this.customTime.valueOf()) - }); + // handle the keyboad movement + this._handleNavigation(); - event.stopPropagation(); - event.preventDefault(); - }; + // check if the physics have settled + if (this.moving == true) { + var startTime = Date.now(); + this._physicsTick(); + var physicsTime = Date.now() - startTime; - module.exports = CustomTime; + // run double speed if it is a little graph + if ((this.renderTimestep - this.renderTime > 2 * physicsTime || this.runDoubleSpeed == true) && this.moving == true) { + this._physicsTick(); + // this makes sure there is no jitter. The decision is taken once to run it at double speed. + if (this.renderTime != 0) { + this.runDoubleSpeed = true + } + } + } -/***/ }, -/* 42 */ -/***/ function(module, exports, __webpack_require__) { + var renderStartTime = Date.now(); + this._redraw(); + this.renderTime = Date.now() - renderStartTime; - var Emitter = __webpack_require__(11); - var Hammer = __webpack_require__(19); - var util = __webpack_require__(1); - var DataSet = __webpack_require__(7); - var DataView = __webpack_require__(9); - var Range = __webpack_require__(21); - var Core = __webpack_require__(25); - var TimeAxis = __webpack_require__(37); - var CurrentTime = __webpack_require__(39); - var CustomTime = __webpack_require__(41); - var LineGraph = __webpack_require__(43); + // this schedules a new animation step + this.start(); + }; + + if (typeof window !== 'undefined') { + window.requestAnimationFrame = window.requestAnimationFrame || window.mozRequestAnimationFrame || + window.webkitRequestAnimationFrame || window.msRequestAnimationFrame; + } /** - * Create a timeline visualization - * @param {HTMLElement} container - * @param {vis.DataSet | Array | google.visualization.DataTable} [items] - * @param {Object} [options] See Graph2d.setOptions for the available options. - * @constructor - * @extends Core + * Schedule a animation step with the refreshrate interval. */ - function Graph2d (container, items, groups, options) { - // if the third element is options, the forth is groups (optionally); - if (!(Array.isArray(groups) || groups instanceof DataSet) && groups instanceof Object) { - var forthArgument = options; - options = groups; - groups = forthArgument; + Network.prototype.start = function() { + if (this.moving == true || this.xIncrement != 0 || this.yIncrement != 0 || this.zoomIncrement != 0 || this.animating == true) { + if (!this.timer) { + if (this.requiresTimeout == true) { + this.timer = window.setTimeout(this._animationStep.bind(this), this.renderTimestep); // wait this.renderTimeStep milliseconds and perform the animation step function + } + else { + this.timer = window.requestAnimationFrame(this._animationStep.bind(this)); // wait this.renderTimeStep milliseconds and perform the animation step function + } + } } - - var me = this; - this.defaultOptions = { - start: null, - end: null, - - autoResize: true, - - orientation: 'bottom', - width: null, - height: null, - maxHeight: null, - minHeight: null - }; - this.options = util.deepExtend({}, this.defaultOptions); - - // Create the DOM, props, and emitter - this._create(container); - - // all components listed here will be repainted automatically - this.components = []; - - this.body = { - dom: this.dom, - domProps: this.props, - emitter: { - on: this.on.bind(this), - off: this.off.bind(this), - emit: this.emit.bind(this) - }, - hiddenDates: [], - util: { - snap: null, // will be specified after TimeAxis is created - toScreen: me._toScreen.bind(me), - toGlobalScreen: me._toGlobalScreen.bind(me), // this refers to the root.width - toTime: me._toTime.bind(me), - toGlobalTime : me._toGlobalTime.bind(me) + else { + this._redraw(); + // this check is to ensure that the network does not emit these events if it was already stabilized and setOptions is called (setting moving to true and calling start()) + if (this.stabilizationIterations > 1) { + // trigger the "stabilized" event. + // The event is triggered on the next tick, to prevent the case that + // it is fired while initializing the Network, in which case you would not + // be able to catch it + var me = this; + var params = { + iterations: me.stabilizationIterations + }; + this.stabilizationIterations = 0; + this.startedStabilization = false; + setTimeout(function () { + me.emit("stabilized", params); + }, 0); } - }; - - // range - this.range = new Range(this.body); - this.components.push(this.range); - this.body.range = this.range; - - // time axis - this.timeAxis = new TimeAxis(this.body); - this.components.push(this.timeAxis); - this.body.util.snap = this.timeAxis.snap.bind(this.timeAxis); - - // current time bar - this.currentTime = new CurrentTime(this.body); - this.components.push(this.currentTime); - - // custom time bar - // Note: time bar will be attached in this.setOptions when selected - this.customTime = new CustomTime(this.body); - this.components.push(this.customTime); - - // item set - this.linegraph = new LineGraph(this.body); - this.components.push(this.linegraph); + else { + this.stabilizationIterations = 0; + } + } + }; - this.itemsData = null; // DataSet - this.groupsData = null; // DataSet - // apply options - if (options) { - this.setOptions(options); + /** + * Move the network according to the keyboard presses. + * + * @private + */ + Network.prototype._handleNavigation = function() { + if (this.xIncrement != 0 || this.yIncrement != 0) { + var translation = this._getTranslation(); + this._setTranslation(translation.x+this.xIncrement, translation.y+this.yIncrement); } - - // IMPORTANT: THIS HAPPENS BEFORE SET ITEMS! - if (groups) { - this.setGroups(groups); + if (this.zoomIncrement != 0) { + var center = { + x: this.frame.canvas.clientWidth / 2, + y: this.frame.canvas.clientHeight / 2 + }; + this._zoom(this.scale*(1 + this.zoomIncrement), center); } + }; - // create itemset - if (items) { - this.setItems(items); + + /** + * Freeze the _animationStep + */ + Network.prototype.toggleFreeze = function() { + if (this.freezeSimulation == false) { + this.freezeSimulation = true; } else { - this.redraw(); + this.freezeSimulation = false; + this.start(); } - } + }; - // Extend the functionality from Core - Graph2d.prototype = new Core(); /** - * Set items - * @param {vis.DataSet | Array | google.visualization.DataTable | null} items + * This function cleans the support nodes if they are not needed and adds them when they are. + * + * @param {boolean} [disableStart] + * @private */ - Graph2d.prototype.setItems = function(items) { - var initialLoad = (this.itemsData == null); - - // convert to type DataSet when needed - var newDataSet; - if (!items) { - newDataSet = null; + Network.prototype._configureSmoothCurves = function(disableStart) { + if (disableStart === undefined) { + disableStart = true; } - else if (items instanceof DataSet || items instanceof DataView) { - newDataSet = items; + if (this.constants.smoothCurves.enabled == true && this.constants.smoothCurves.dynamic == true) { + this._createBezierNodes(); + // cleanup unused support nodes + for (var nodeId in this.sectors['support']['nodes']) { + if (this.sectors['support']['nodes'].hasOwnProperty(nodeId)) { + if (this.edges[this.sectors['support']['nodes'][nodeId].parentEdgeId] === undefined) { + delete this.sectors['support']['nodes'][nodeId]; + } + } + } } else { - // turn an array into a dataset - newDataSet = new DataSet(items, { - type: { - start: 'Date', - end: 'Date' + // delete the support nodes + this.sectors['support']['nodes'] = {}; + for (var edgeId in this.edges) { + if (this.edges.hasOwnProperty(edgeId)) { + this.edges[edgeId].via = null; } - }); + } } - // set items - this.itemsData = newDataSet; - this.linegraph && this.linegraph.setItems(newDataSet); - if (initialLoad) { - if (this.options.start != undefined || this.options.end != undefined) { - var start = this.options.start != undefined ? this.options.start : null; - var end = this.options.end != undefined ? this.options.end : null; + this._updateCalculationNodes(); + if (!disableStart) { + this.moving = true; + this.start(); + } + }; - this.setWindow(start, end, {animate: false}); + + /** + * 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. + * + * @private + */ + Network.prototype._createBezierNodes = function() { + if (this.constants.smoothCurves.enabled == true && this.constants.smoothCurves.dynamic == true) { + for (var edgeId in this.edges) { + if (this.edges.hasOwnProperty(edgeId)) { + var edge = this.edges[edgeId]; + if (edge.via == null) { + var nodeId = "edgeId:".concat(edge.id); + this.sectors['support']['nodes'][nodeId] = new Node( + {id:nodeId, + mass:1, + shape:'circle', + image:"", + internalMultiplier:1 + },{},{},this.constants); + edge.via = this.sectors['support']['nodes'][nodeId]; + edge.via.parentEdgeId = edge.id; + edge.positionBezierNode(); + } + } } - else { - this.fit({animate: false}); + } + }; + + /** + * load the functions that load the mixins into the prototype. + * + * @private + */ + Network.prototype._initializeMixinLoaders = function () { + for (var mixin in MixinLoader) { + if (MixinLoader.hasOwnProperty(mixin)) { + Network.prototype[mixin] = MixinLoader[mixin]; } } }; /** - * Set groups - * @param {vis.DataSet | Array | google.visualization.DataTable} groups + * Load the XY positions of the nodes into the dataset. */ - Graph2d.prototype.setGroups = function(groups) { - // convert to type DataSet when needed - var newDataSet; - if (!groups) { - newDataSet = null; + Network.prototype.storePosition = function() { + console.log("storePosition is depricated: use .storePositions() from now on.") + this.storePositions(); + }; + + /** + * Load the XY positions of the nodes into the dataset. + */ + Network.prototype.storePositions = function() { + var dataArray = []; + for (var nodeId in this.nodes) { + if (this.nodes.hasOwnProperty(nodeId)) { + var node = this.nodes[nodeId]; + var allowedToMoveX = !this.nodes.xFixed; + var allowedToMoveY = !this.nodes.yFixed; + if (this.nodesData._data[nodeId].x != Math.round(node.x) || this.nodesData._data[nodeId].y != Math.round(node.y)) { + dataArray.push({id:nodeId,x:Math.round(node.x),y:Math.round(node.y),allowedToMoveX:allowedToMoveX,allowedToMoveY:allowedToMoveY}); + } + } } - else if (groups instanceof DataSet || groups instanceof DataView) { - newDataSet = groups; + this.nodesData.update(dataArray); + }; + + /** + * Return the positions of the nodes. + */ + Network.prototype.getPositions = function(ids) { + var dataArray = {}; + if (ids !== undefined) { + if (Array.isArray(ids) == true) { + for (var i = 0; i < ids.length; i++) { + if (this.nodes[ids[i]] !== undefined) { + var node = this.nodes[ids[i]]; + dataArray[ids[i]] = {x: Math.round(node.x), y: Math.round(node.y)}; + } + } + } + else { + if (this.nodes[ids] !== undefined) { + var node = this.nodes[ids]; + dataArray[ids] = {x: Math.round(node.x), y: Math.round(node.y)}; + } + } } else { - // turn an array into a dataset - newDataSet = new DataSet(groups); + for (var nodeId in this.nodes) { + if (this.nodes.hasOwnProperty(nodeId)) { + var node = this.nodes[nodeId]; + dataArray[nodeId] = {x: Math.round(node.x), y: Math.round(node.y)}; + } + } } - - this.groupsData = newDataSet; - this.linegraph.setGroups(newDataSet); + return dataArray; }; + + /** - * Returns an object containing an SVG element with the icon of the group (size determined by iconWidth and iconHeight), the label of the group (content) and the yAxisOrientation of the group (left or right). - * @param groupId - * @param width - * @param height + * Center a node in view. + * + * @param {Number} nodeId + * @param {Number} [options] */ - Graph2d.prototype.getLegend = function(groupId, width, height) { - if (width === undefined) {width = 15;} - if (height === undefined) {height = 15;} - if (this.linegraph.groups[groupId] !== undefined) { - return this.linegraph.groups[groupId].getLegend(width,height); + Network.prototype.focusOnNode = function (nodeId, options) { + if (this.nodes.hasOwnProperty(nodeId)) { + if (options === undefined) { + options = {}; + } + var nodePosition = {x: this.nodes[nodeId].x, y: this.nodes[nodeId].y}; + options.position = nodePosition; + options.lockedOnNode = nodeId; + + this.moveTo(options) } else { - return "cannot find group:" + groupId; + console.log("This nodeId cannot be found."); } - } + }; /** - * This checks if the visible option of the supplied group (by ID) is true or false. - * @param groupId - * @returns {*} + * + * @param {Object} options | options.offset = {x:Number, y:Number} // offset from the center in DOM pixels + * | options.scale = Number // scale to move to + * | options.position = {x:Number, y:Number} // position to move to + * | options.animation = {duration:Number, easingFunction:String} || Boolean // position to move to */ - Graph2d.prototype.isGroupVisible = function(groupId) { - if (this.linegraph.groups[groupId] !== undefined) { - return (this.linegraph.groups[groupId].visible && (this.linegraph.options.groups.visibility[groupId] === undefined || this.linegraph.options.groups.visibility[groupId] == true)); - } - else { - return false; + Network.prototype.moveTo = function (options) { + if (options === undefined) { + options = {}; + return; } - } + if (options.offset === undefined) {options.offset = {x: 0, y: 0}; } + if (options.offset.x === undefined) {options.offset.x = 0; } + if (options.offset.y === undefined) {options.offset.y = 0; } + if (options.scale === undefined) {options.scale = this._getScale(); } + if (options.position === undefined) {options.position = this._getTranslation();} + if (options.animation === undefined) {options.animation = {duration:0}; } + if (options.animation === false ) {options.animation = {duration:0}; } + if (options.animation === true ) {options.animation = {}; } + if (options.animation.duration === undefined) {options.animation.duration = 1000; } // default duration + if (options.animation.easingFunction === undefined) {options.animation.easingFunction = "easeInOutQuad"; } // default easing function + this.animateView(options); + }; /** - * Get the data range of the item set. - * @returns {{min: Date, max: Date}} range A range with a start and end Date. - * When no minimum is found, min==null - * When no maximum is found, max==null + * + * @param {Object} options | options.offset = {x:Number, y:Number} // offset from the center in DOM pixels + * | options.time = Number // animation time in milliseconds + * | options.scale = Number // scale to animate to + * | options.position = {x:Number, y:Number} // position to animate to + * | options.easingFunction = String // linear, easeInQuad, easeOutQuad, easeInOutQuad, + * // easeInCubic, easeOutCubic, easeInOutCubic, + * // easeInQuart, easeOutQuart, easeInOutQuart, + * // easeInQuint, easeOutQuint, easeInOutQuint */ - Graph2d.prototype.getItemRange = function() { - var min = null; - var max = null; - - // calculate min from start filed - for (var groupId in this.linegraph.groups) { - if (this.linegraph.groups.hasOwnProperty(groupId)) { - if (this.linegraph.groups[groupId].visible == true) { - for (var i = 0; i < this.linegraph.groups[groupId].itemsData.length; i++) { - var item = this.linegraph.groups[groupId].itemsData[i]; - var value = util.convert(item.x, 'Date').valueOf(); - min = min == null ? value : min > value ? value : min; - max = max == null ? value : max < value ? value : max; - } - } - } + Network.prototype.animateView = function (options) { + if (options === undefined) { + options = {}; + return; } - return { - min: (min != null) ? new Date(min) : null, - max: (max != null) ? new Date(max) : null - }; - }; + // release if something focussed on the node + this.releaseNode(); + if (options.locked == true) { + this.lockedOnNodeId = options.lockedOnNode; + this.lockedOnNodeOffset = options.offset; + } + // forcefully complete the old animation if it was still running + if (this.easingTime != 0) { + this._transitionRedraw(1); // by setting easingtime to 1, we finish the animation. + } + this.sourceScale = this._getScale(); + this.sourceTranslation = this._getTranslation(); + this.targetScale = options.scale; - module.exports = Graph2d; + // set the scale so the viewCenter is based on the correct zoom level. This is overridden in the transitionRedraw + // but at least then we'll have the target transition + this._setScale(this.targetScale); + var viewCenter = this.DOMtoCanvas({x: 0.5 * this.frame.canvas.clientWidth, y: 0.5 * this.frame.canvas.clientHeight}); + var distanceFromCenter = { // offset from view, distance view has to change by these x and y to center the node + x: viewCenter.x - options.position.x, + y: viewCenter.y - options.position.y + }; + this.targetTranslation = { + x: this.sourceTranslation.x + distanceFromCenter.x * this.targetScale + options.offset.x, + y: this.sourceTranslation.y + distanceFromCenter.y * this.targetScale + options.offset.y + }; + // if the time is set to 0, don't do an animation + if (options.animation.duration == 0) { + if (this.lockedOnNodeId != null) { + this._classicRedraw = this._redraw; + this._redraw = this._lockedRedraw; + } + else { + this._setScale(this.targetScale); + this._setTranslation(this.targetTranslation.x, this.targetTranslation.y); + this._redraw(); + } + } + else { + this.animating = true; + this.animationSpeed = 1 / (this.renderRefreshRate * options.animation.duration * 0.001) || 1 / this.renderRefreshRate; + this.animationEasingFunction = options.animation.easingFunction; + this._classicRedraw = this._redraw; + this._redraw = this._transitionRedraw; + this._redraw(); + this.start(); + } + }; -/***/ }, -/* 43 */ -/***/ function(module, exports, __webpack_require__) { + /** + * used to animate smoothly by hijacking the redraw function. + * @private + */ + Network.prototype._lockedRedraw = function () { + var nodePosition = {x: this.nodes[this.lockedOnNodeId].x, y: this.nodes[this.lockedOnNodeId].y}; + var viewCenter = this.DOMtoCanvas({x: 0.5 * this.frame.canvas.clientWidth, y: 0.5 * this.frame.canvas.clientHeight}); + var distanceFromCenter = { // offset from view, distance view has to change by these x and y to center the node + x: viewCenter.x - nodePosition.x, + y: viewCenter.y - nodePosition.y + }; + var sourceTranslation = this._getTranslation(); + var targetTranslation = { + x: sourceTranslation.x + distanceFromCenter.x * this.scale + this.lockedOnNodeOffset.x, + y: sourceTranslation.y + distanceFromCenter.y * this.scale + this.lockedOnNodeOffset.y + }; - var util = __webpack_require__(1); - var DOMutil = __webpack_require__(6); - var DataSet = __webpack_require__(7); - var DataView = __webpack_require__(9); - var Component = __webpack_require__(23); - var DataAxis = __webpack_require__(44); - var GraphGroup = __webpack_require__(46); - var Legend = __webpack_require__(50); - var BarGraphFunctions = __webpack_require__(49); + this._setTranslation(targetTranslation.x,targetTranslation.y); + this._classicRedraw(); + } - var UNGROUPED = '__ungrouped__'; // reserved group id for ungrouped items + Network.prototype.releaseNode = function () { + if (this.lockedOnNodeId != null) { + this._redraw = this._classicRedraw; + this.lockedOnNodeId = null; + this.lockedOnNodeOffset = null; + } + } /** - * This is the constructor of the LineGraph. It requires a Timeline body and options. * - * @param body - * @param options - * @constructor + * @param easingTime + * @private */ - function LineGraph(body, options) { - this.id = util.randomUUID(); - this.body = body; + Network.prototype._transitionRedraw = function (easingTime) { + this.easingTime = easingTime || this.easingTime + this.animationSpeed; + this.easingTime += this.animationSpeed; - this.defaultOptions = { - yAxisOrientation: 'left', - defaultGroup: 'default', - sort: true, - sampling: true, - graphHeight: '400px', - shaded: { - enabled: false, - orientation: 'bottom' // top, bottom - }, - style: 'line', // line, bar - barChart: { - width: 50, - handleOverlap: 'overlap', - align: 'center' // left, center, right - }, - catmullRom: { - enabled: true, - parametrization: 'centripetal', // uniform (alpha = 0.0), chordal (alpha = 1.0), centripetal (alpha = 0.5) - alpha: 0.5 - }, - drawPoints: { - enabled: true, - size: 6, - style: 'square' // square, circle - }, - dataAxis: { - showMinorLabels: true, - showMajorLabels: true, - icons: false, - width: '40px', - visible: true, - alignZeros: true, - customRange: { - left: {min:undefined, max:undefined}, - right: {min:undefined, max:undefined} - } - //, these options are not set by default, but this shows the format they will be in - //format: { - // left: {decimals: 2}, - // right: {decimals: 2} - //}, - //title: { - // left: { - // text: 'left', - // style: 'color:black;' - // }, - // right: { - // text: 'right', - // style: 'color:black;' - // } - //} - }, - legend: { - enabled: false, - icons: true, - left: { - visible: true, - position: 'top-left' // top/bottom - left,right - }, - right: { - visible: true, - position: 'top-right' // top/bottom - left,right - } - }, - groups: { - visibility: {} - } - }; + var progress = util.easingFunctions[this.animationEasingFunction](this.easingTime); - // options is shared by this ItemSet and all its items - this.options = util.extend({}, this.defaultOptions); - this.dom = {}; - this.props = {}; - this.hammer = null; - this.groups = {}; - this.abortedGraphUpdate = false; - this.updateSVGheight = false; - this.updateSVGheightOnResize = false; + this._setScale(this.sourceScale + (this.targetScale - this.sourceScale) * progress); + this._setTranslation( + this.sourceTranslation.x + (this.targetTranslation.x - this.sourceTranslation.x) * progress, + this.sourceTranslation.y + (this.targetTranslation.y - this.sourceTranslation.y) * progress + ); - var me = this; - this.itemsData = null; // DataSet - this.groupsData = null; // DataSet + this._classicRedraw(); - // listeners for the DataSet of the items - this.itemListeners = { - 'add': function (event, params, senderId) { - me._onAdd(params.items); - }, - 'update': function (event, params, senderId) { - me._onUpdate(params.items); - }, - 'remove': function (event, params, senderId) { - me._onRemove(params.items); + // cleanup + if (this.easingTime >= 1.0) { + this.animating = false; + this.easingTime = 0; + if (this.lockedOnNodeId != null) { + this._redraw = this._lockedRedraw; } - }; - - // listeners for the DataSet of the groups - this.groupListeners = { - 'add': function (event, params, senderId) { - me._onAddGroups(params.items); - }, - 'update': function (event, params, senderId) { - me._onUpdateGroups(params.items); - }, - 'remove': function (event, params, senderId) { - me._onRemoveGroups(params.items); + else { + this._redraw = this._classicRedraw; } - }; + this.emit("animationFinished"); + } + }; - this.items = {}; // object with an Item for every data item - this.selection = []; // list with the ids of all selected nodes - this.lastStart = this.body.range.start; - this.touchParams = {}; // stores properties while dragging + Network.prototype._classicRedraw = function () { + // placeholder function to be overloaded by animations; + }; - this.svgElements = {}; - this.setOptions(options); - this.groupsUsingDefaultStyles = [0]; - this.COUNTER = 0; - this.body.emitter.on('rangechanged', function() { - me.lastStart = me.body.range.start; - me.svg.style.left = util.option.asSize(-me.props.width); - me.redraw.call(me,true); - }); + /** + * Returns true when the Network is active. + * @returns {boolean} + */ + Network.prototype.isActive = function () { + return !this.activator || this.activator.active; + }; - // create the HTML DOM - this._create(); - this.framework = {svg: this.svg, svgElements: this.svgElements, options: this.options, groups: this.groups}; - this.body.emitter.emit('change'); - } + /** + * Sets the scale + * @returns {Number} + */ + Network.prototype.setScale = function () { + return this._setScale(); + }; - LineGraph.prototype = new Component(); /** - * Create the HTML DOM for the ItemSet + * Returns the scale + * @returns {Number} */ - LineGraph.prototype._create = function(){ - var frame = document.createElement('div'); - frame.className = 'LineGraph'; - this.dom.frame = frame; + Network.prototype.getScale = function () { + return this._getScale(); + }; - // create svg element for graph drawing. - this.svg = document.createElementNS('http://www.w3.org/2000/svg','svg'); - this.svg.style.position = 'relative'; - this.svg.style.height = ('' + this.options.graphHeight).replace('px','') + 'px'; - this.svg.style.display = 'block'; - frame.appendChild(this.svg); - // data axis - this.options.dataAxis.orientation = 'left'; - this.yAxisLeft = new DataAxis(this.body, this.options.dataAxis, this.svg, this.options.groups); + /** + * Returns the scale + * @returns {Number} + */ + Network.prototype.getCenterCoordinates = function () { + return this.DOMtoCanvas({x: 0.5 * this.frame.canvas.clientWidth, y: 0.5 * this.frame.canvas.clientHeight}); + }; - this.options.dataAxis.orientation = 'right'; - this.yAxisRight = new DataAxis(this.body, this.options.dataAxis, this.svg, this.options.groups); - delete this.options.dataAxis.orientation; - // legends - this.legendLeft = new Legend(this.body, this.options.legend, 'left', this.options.groups); - this.legendRight = new Legend(this.body, this.options.legend, 'right', this.options.groups); + Network.prototype.getBoundingBox = function(nodeId) { + if (this.nodes[nodeId] !== undefined) { + return this.nodes[nodeId].boundingBox; + } + } - this.show(); - }; + module.exports = Network; + + +/***/ }, +/* 37 */ +/***/ function(module, exports, __webpack_require__) { + + var util = __webpack_require__(1); + var Node = __webpack_require__(40); /** - * set the options of the LineGraph. the mergeOptions is used for subObjects that have an enabled element. - * @param {object} options + * @class Edge + * + * A edge connects two nodes + * @param {Object} properties Object with properties. Must contain + * At least properties from and to. + * Available properties: 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 */ - LineGraph.prototype.setOptions = function(options) { - if (options) { - var fields = ['sampling','defaultGroup','height','graphHeight','yAxisOrientation','style','barChart','dataAxis','sort','groups']; - if (options.graphHeight === undefined && options.height !== undefined && this.body.domProps.centerContainer.height !== undefined) { - this.updateSVGheight = true; - this.updateSVGheightOnResize = true; - } - else if (this.body.domProps.centerContainer.height !== undefined && options.graphHeight !== undefined) { - if (parseInt((options.graphHeight + '').replace("px",'')) < this.body.domProps.centerContainer.height) { - this.updateSVGheight = true; - } - } - util.selectiveDeepExtend(fields, this.options, options); - util.mergeOptions(this.options, options,'catmullRom'); - util.mergeOptions(this.options, options,'drawPoints'); - util.mergeOptions(this.options, options,'shaded'); - util.mergeOptions(this.options, options,'legend'); + function Edge (properties, network, networkConstants) { + if (!network) { + throw "No network provided"; + } + var fields = ['edges','physics']; + var constants = util.selectiveBridgeObject(fields,networkConstants); + this.options = constants.edges; + this.physics = constants.physics; + this.options['smoothCurves'] = networkConstants['smoothCurves']; - if (options.catmullRom) { - if (typeof options.catmullRom == 'object') { - if (options.catmullRom.parametrization) { - if (options.catmullRom.parametrization == 'uniform') { - this.options.catmullRom.alpha = 0; - } - else if (options.catmullRom.parametrization == 'chordal') { - this.options.catmullRom.alpha = 1.0; - } - else { - this.options.catmullRom.parametrization = 'centripetal'; - this.options.catmullRom.alpha = 0.5; - } - } - } - } - if (this.yAxisLeft) { - if (options.dataAxis !== undefined) { - this.yAxisLeft.setOptions(this.options.dataAxis); - this.yAxisRight.setOptions(this.options.dataAxis); - } - } + this.network = network; - if (this.legendLeft) { - if (options.legend !== undefined) { - this.legendLeft.setOptions(this.options.legend); - this.legendRight.setOptions(this.options.legend); - } - } + // initialize variables + this.id = undefined; + this.fromId = undefined; + this.toId = undefined; + this.title = undefined; + this.widthSelected = this.options.width * this.options.widthSelectionMultiplier; + this.value = undefined; + this.selected = false; + this.hover = false; + this.labelDimensions = {top:0,left:0,width:0,height:0,yLine:0}; // could be cached + this.dirtyLabel = true; - if (this.groups.hasOwnProperty(UNGROUPED)) { - this.groups[UNGROUPED].setOptions(options); - } - } + this.from = null; // a node + this.to = null; // a node + this.via = null; // a temp node - // this is used to redraw the graph if the visibility of the groups is changed. - if (this.dom.frame) { - this.redraw(true); - } - }; + this.fromBackup = null; // used to clean up after reconnect + this.toBackup = null;; // used to clean up after reconnect - /** - * Hide the component from the DOM - */ - LineGraph.prototype.hide = function() { - // remove the frame containing the items - if (this.dom.frame.parentNode) { - this.dom.frame.parentNode.removeChild(this.dom.frame); - } - }; + // we use this to be able to reconnect the edge to a cluster if its node is put into a cluster + // by storing the original information we can revert to the original connection when the cluser is opened. + this.originalFromId = []; + this.originalToId = []; + + this.connected = false; + + this.widthFixed = false; + this.lengthFixed = false; + + this.setProperties(properties); + this.controlNodesEnabled = false; + this.controlNodes = {from:null, to:null, positions:{}}; + this.connectedNode = null; + } /** - * Show the component in the DOM (when not already visible). - * @return {Boolean} changed + * Set or overwrite properties for the edge + * @param {Object} properties an object with properties + * @param {Object} constants and object with default, global properties */ - LineGraph.prototype.show = function() { - // show frame containing the items - if (!this.dom.frame.parentNode) { - this.body.dom.center.appendChild(this.dom.frame); + Edge.prototype.setProperties = function(properties) { + if (!properties) { + return; } - }; + var fields = ['style','fontSize','fontFace','fontColor','fontFill','fontStrokeWidth','fontStrokeColor','width', + 'widthSelectionMultiplier','hoverWidth','arrowScaleFactor','dash','inheritColor','labelAlignment' + ]; + util.selectiveDeepExtend(fields, this.options, properties); + + if (properties.from !== undefined) {this.fromId = properties.from;} + if (properties.to !== undefined) {this.toId = properties.to;} - /** - * Set items - * @param {vis.DataSet | null} items - */ - LineGraph.prototype.setItems = function(items) { - var me = this, - ids, - oldItemsData = this.itemsData; + if (properties.id !== undefined) {this.id = properties.id;} + if (properties.label !== undefined) {this.label = properties.label; this.dirtyLabel = true;} - // replace the dataset - if (!items) { - this.itemsData = null; - } - else if (items instanceof DataSet || items instanceof DataView) { - this.itemsData = items; - } - else { - throw new TypeError('Data must be an instance of DataSet or DataView'); + if (properties.title !== undefined) {this.title = properties.title;} + if (properties.value !== undefined) {this.value = properties.value;} + if (properties.length !== undefined) {this.physics.springLength = properties.length;} + + if (properties.color !== undefined) { + this.options.inheritColor = false; + if (util.isString(properties.color)) { + this.options.color.color = properties.color; + this.options.color.highlight = properties.color; + } + else { + if (properties.color.color !== undefined) {this.options.color.color = properties.color.color;} + if (properties.color.highlight !== undefined) {this.options.color.highlight = properties.color.highlight;} + if (properties.color.hover !== undefined) {this.options.color.hover = properties.color.hover;} + } } - if (oldItemsData) { - // unsubscribe from old dataset - util.forEach(this.itemListeners, function (callback, event) { - oldItemsData.off(event, callback); - }); + // A node is connected when it has a from and to node. + this.connect(); - // remove all drawn items - ids = oldItemsData.getIds(); - this._onRemove(ids); - } + this.widthFixed = this.widthFixed || (properties.width !== undefined); + this.lengthFixed = this.lengthFixed || (properties.length !== undefined); - if (this.itemsData) { - // subscribe to new dataset - var id = this.id; - util.forEach(this.itemListeners, function (callback, event) { - me.itemsData.on(event, callback, id); - }); + this.widthSelected = this.options.width* this.options.widthSelectionMultiplier; - // add all new items - ids = this.itemsData.getIds(); - this._onAdd(ids); + // set draw method based on style + switch (this.options.style) { + case 'line': this.draw = this._drawLine; break; + case 'arrow': this.draw = this._drawArrow; break; + case 'arrow-center': this.draw = this._drawArrowCenter; break; + case 'dash-line': this.draw = this._drawDashLine; break; + default: this.draw = this._drawLine; break; } - this._updateUngrouped(); - //this._updateGraph(); - this.redraw(true); + }; - /** - * Set groups - * @param {vis.DataSet} groups + * Connect an edge to its nodes */ - LineGraph.prototype.setGroups = function(groups) { - var me = this; - var ids; - - // unsubscribe from current dataset - if (this.groupsData) { - util.forEach(this.groupListeners, function (callback, event) { - me.groupsData.unsubscribe(event, callback); - }); + Edge.prototype.connect = function () { + this.disconnect(); - // remove all drawn groups - ids = this.groupsData.getIds(); - this.groupsData = null; - this._onRemoveGroups(ids); // note: this will cause a redraw - } + this.from = this.network.nodes[this.fromId] || null; + this.to = this.network.nodes[this.toId] || null; + this.connected = (this.from && this.to); - // replace the dataset - if (!groups) { - this.groupsData = null; - } - else if (groups instanceof DataSet || groups instanceof DataView) { - this.groupsData = groups; + if (this.connected) { + this.from.attachEdge(this); + this.to.attachEdge(this); } else { - throw new TypeError('Data must be an instance of DataSet or DataView'); - } - - if (this.groupsData) { - // subscribe to new dataset - var id = this.id; - util.forEach(this.groupListeners, function (callback, event) { - me.groupsData.on(event, callback, id); - }); - - // draw all ms - ids = this.groupsData.getIds(); - this._onAddGroups(ids); + if (this.from) { + this.from.detachEdge(this); + } + if (this.to) { + this.to.detachEdge(this); + } } - this._onUpdate(); }; - /** - * Update the data - * @param [ids] - * @private + * Disconnect an edge from its nodes */ - LineGraph.prototype._onUpdate = function(ids) { - this._updateUngrouped(); - this._updateAllGroupData(); - //this._updateGraph(); - this.redraw(true); - }; - LineGraph.prototype._onAdd = function (ids) {this._onUpdate(ids);}; - LineGraph.prototype._onRemove = function (ids) {this._onUpdate(ids);}; - LineGraph.prototype._onUpdateGroups = function (groupIds) { - for (var i = 0; i < groupIds.length; i++) { - var group = this.groupsData.get(groupIds[i]); - this._updateGroup(group, groupIds[i]); + Edge.prototype.disconnect = function () { + if (this.from) { + this.from.detachEdge(this); + this.from = null; + } + if (this.to) { + this.to.detachEdge(this); + this.to = null; } - //this._updateGraph(); - this.redraw(true); + this.connected = false; }; - LineGraph.prototype._onAddGroups = function (groupIds) {this._onUpdateGroups(groupIds);}; - /** - * this cleans the group out off the legends and the dataaxis, updates the ungrouped and updates the graph - * @param {Array} groupIds - * @private + * get the title of this edge. + * @return {string} title The title of the edge, or undefined when no title + * has been set. */ - LineGraph.prototype._onRemoveGroups = function (groupIds) { - for (var i = 0; i < groupIds.length; i++) { - if (this.groups.hasOwnProperty(groupIds[i])) { - if (this.groups[groupIds[i]].options.yAxisOrientation == 'right') { - this.yAxisRight.removeGroup(groupIds[i]); - this.legendRight.removeGroup(groupIds[i]); - this.legendRight.redraw(); - } - else { - this.yAxisLeft.removeGroup(groupIds[i]); - this.legendLeft.removeGroup(groupIds[i]); - this.legendLeft.redraw(); - } - delete this.groups[groupIds[i]]; - } - } - this._updateUngrouped(); - //this._updateGraph(); - this.redraw(true); + Edge.prototype.getTitle = function() { + return typeof this.title === "function" ? this.title() : this.title; }; /** - * update a group object with the group dataset entree - * - * @param group - * @param groupId - * @private + * Retrieve the value of the edge. Can be undefined + * @return {Number} value */ - LineGraph.prototype._updateGroup = function (group, groupId) { - if (!this.groups.hasOwnProperty(groupId)) { - this.groups[groupId] = new GraphGroup(group, groupId, this.options, this.groupsUsingDefaultStyles); - if (this.groups[groupId].options.yAxisOrientation == 'right') { - this.yAxisRight.addGroup(groupId, this.groups[groupId]); - this.legendRight.addGroup(groupId, this.groups[groupId]); - } - else { - this.yAxisLeft.addGroup(groupId, this.groups[groupId]); - this.legendLeft.addGroup(groupId, this.groups[groupId]); - } - } - else { - this.groups[groupId].update(group); - if (this.groups[groupId].options.yAxisOrientation == 'right') { - this.yAxisRight.updateGroup(groupId, this.groups[groupId]); - this.legendRight.updateGroup(groupId, this.groups[groupId]); - } - else { - this.yAxisLeft.updateGroup(groupId, this.groups[groupId]); - this.legendLeft.updateGroup(groupId, this.groups[groupId]); - } - } - this.legendLeft.redraw(); - this.legendRight.redraw(); + Edge.prototype.getValue = function() { + return this.value; }; - /** - * this updates all groups, it is used when there is an update the the itemset. - * - * @private + * Adjust the value range of the edge. The edge will adjust it's width + * based on its value. + * @param {Number} min + * @param {Number} max */ - LineGraph.prototype._updateAllGroupData = function () { - if (this.itemsData != null) { - var groupsContent = {}; - var groupId; - for (groupId in this.groups) { - if (this.groups.hasOwnProperty(groupId)) { - groupsContent[groupId] = []; - } - } - for (var itemId in this.itemsData._data) { - if (this.itemsData._data.hasOwnProperty(itemId)) { - var item = this.itemsData._data[itemId]; - if (groupsContent[item.group] === undefined) { - throw new Error('Cannot find referenced group. Possible reason: items added before groups? Groups need to be added before items, as items refer to groups.') - } - item.x = util.convert(item.x,'Date'); - groupsContent[item.group].push(item); - } - } - for (groupId in this.groups) { - if (this.groups.hasOwnProperty(groupId)) { - this.groups[groupId].setItems(groupsContent[groupId]); - } - } + Edge.prototype.setValueRange = function(min, max) { + if (!this.widthFixed && this.value !== undefined) { + var scale = (this.options.widthMax - this.options.widthMin) / (max - min); + this.options.width= (this.value - min) * scale + this.options.widthMin; + this.widthSelected = this.options.width* this.options.widthSelectionMultiplier; } }; - /** - * Create or delete the group holding all ungrouped items. This group is used when - * there are no groups specified. This anonymous group is called 'graph'. - * @protected + * 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 */ - LineGraph.prototype._updateUngrouped = function() { - if (this.itemsData && this.itemsData != null) { - var ungroupedCounter = 0; - for (var itemId in this.itemsData._data) { - if (this.itemsData._data.hasOwnProperty(itemId)) { - var item = this.itemsData._data[itemId]; - if (item != undefined) { - if (item.hasOwnProperty('group')) { - if (item.group === undefined) { - item.group = UNGROUPED; - } - } - else { - item.group = UNGROUPED; - } - ungroupedCounter = item.group == UNGROUPED ? ungroupedCounter + 1 : ungroupedCounter; - } - } - } - - if (ungroupedCounter == 0) { - delete this.groups[UNGROUPED]; - this.legendLeft.removeGroup(UNGROUPED); - this.legendRight.removeGroup(UNGROUPED); - this.yAxisLeft.removeGroup(UNGROUPED); - this.yAxisRight.removeGroup(UNGROUPED); - } - else { - var group = {id: UNGROUPED, content: this.options.defaultGroup}; - this._updateGroup(group, UNGROUPED); - } - } - else { - delete this.groups[UNGROUPED]; - this.legendLeft.removeGroup(UNGROUPED); - this.legendRight.removeGroup(UNGROUPED); - this.yAxisLeft.removeGroup(UNGROUPED); - this.yAxisRight.removeGroup(UNGROUPED); - } - - this.legendLeft.redraw(); - this.legendRight.redraw(); + Edge.prototype.draw = function(ctx) { + throw "Method draw not initialized in edge"; }; - /** - * Redraw the component, mandatory function - * @return {boolean} Returns true if the component is resized + * 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 */ - LineGraph.prototype.redraw = function(forceGraphUpdate) { - var resized = false; - - // calculate actual size and position - this.props.width = this.dom.frame.offsetWidth; - this.props.height = this.body.domProps.centerContainer.height; - - // update the graph if there is no lastWidth or with, used for the initial draw - if (this.lastWidth === undefined && this.props.width) { - forceGraphUpdate = true; - } - - // check if this component is resized - resized = this._isResized() || resized; - - // check whether zoomed (in that case we need to re-stack everything) - var visibleInterval = this.body.range.end - this.body.range.start; - var zoomed = (visibleInterval != this.lastVisibleInterval); - this.lastVisibleInterval = visibleInterval; - - - // the svg element is three times as big as the width, this allows for fully dragging left and right - // without reloading the graph. the controls for this are bound to events in the constructor - if (resized == true) { - this.svg.style.width = util.option.asSize(3*this.props.width); - this.svg.style.left = util.option.asSize(-this.props.width); + Edge.prototype.isOverlappingWith = function(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; - // if the height of the graph is set as proportional, change the height of the svg - if ((this.options.height + '').indexOf("%") != -1 || this.updateSVGheightOnResize == true) { - this.updateSVGheight = true; - } - } + var dist = this._getDistanceToEdge(xFrom, yFrom, xTo, yTo, xObj, yObj); - // update the height of the graph on each redraw of the graph. - if (this.updateSVGheight == true) { - if (this.options.graphHeight != this.body.domProps.centerContainer.height + 'px') { - this.options.graphHeight = this.body.domProps.centerContainer.height + 'px'; - this.svg.style.height = this.body.domProps.centerContainer.height + 'px'; - } - this.updateSVGheight = false; + return (dist < distMax); } else { - this.svg.style.height = ('' + this.options.graphHeight).replace('px','') + 'px'; + return false } + }; - // zoomed is here to ensure that animations are shown correctly. - if (resized == true || zoomed == true || this.abortedGraphUpdate == true || forceGraphUpdate == true) { - resized = this._updateGraph() || resized; + Edge.prototype._getColor = function() { + var colorObj = this.options.color; + if (this.options.inheritColor == "to") { + colorObj = { + highlight: this.to.options.color.highlight.border, + hover: this.to.options.color.hover.border, + color: this.to.options.color.border + }; } - else { - // move the whole svg while dragging - if (this.lastStart != 0) { - var offset = this.body.range.start - this.lastStart; - var range = this.body.range.end - this.body.range.start; - if (this.props.width != 0) { - var rangePerPixelInv = this.props.width/range; - var xOffset = offset * rangePerPixelInv; - this.svg.style.left = (-this.props.width - xOffset) + 'px'; - } - } + else if (this.options.inheritColor == "from" || this.options.inheritColor == true) { + colorObj = { + highlight: this.from.options.color.highlight.border, + hover: this.from.options.color.hover.border, + color: this.from.options.color.border + }; } - this.legendLeft.redraw(); - this.legendRight.redraw(); - return resized; + if (this.selected == true) {return colorObj.highlight;} + else if (this.hover == true) {return colorObj.hover;} + else {return colorObj.color;} }; /** - * Update and redraw the graph. - * + * Redraw a edge as a line + * Draw this edge in the given canvas + * The 2d context of a HTML canvas can be retrieved by canvas.getContext("2d"); + * @param {CanvasRenderingContext2D} ctx + * @private */ - LineGraph.prototype._updateGraph = function () { - // reset the svg elements - DOMutil.prepareElements(this.svgElements); - if (this.props.width != 0 && this.itemsData != null) { - var group, i; - var preprocessedGroupData = {}; - var processedGroupData = {}; - var groupRanges = {}; - var changeCalled = false; - - // getting group Ids - var groupIds = []; - for (var groupId in this.groups) { - if (this.groups.hasOwnProperty(groupId)) { - group = this.groups[groupId]; - if (group.visible == true && (this.options.groups.visibility[groupId] === undefined || this.options.groups.visibility[groupId] == true)) { - groupIds.push(groupId); - } - } - } - if (groupIds.length > 0) { - // this is the range of the SVG canvas - var minDate = this.body.util.toGlobalTime(-this.body.domProps.root.width); - var maxDate = this.body.util.toGlobalTime(2 * this.body.domProps.root.width); - var groupsData = {}; - // fill groups data, this only loads the data we require based on the timewindow - this._getRelevantData(groupIds, groupsData, minDate, maxDate); - - // apply sampling, if disabled, it will pass through this function. - this._applySampling(groupIds, groupsData); - - // we transform the X coordinates to detect collisions - for (i = 0; i < groupIds.length; i++) { - preprocessedGroupData[groupIds[i]] = this._convertXcoordinates(groupsData[groupIds[i]]); - } + Edge.prototype._drawLine = function(ctx) { + // set style + ctx.strokeStyle = this._getColor(); + ctx.lineWidth = this._getLineWidth(); - // now all needed data has been collected we start the processing. - this._getYRanges(groupIds, preprocessedGroupData, groupRanges); + if (this.from != this.to) { + // draw line + var via = this._line(ctx); - // update the Y axis first, we use this data to draw at the correct Y points - // changeCalled is required to clean the SVG on a change emit. - changeCalled = this._updateYAxis(groupIds, groupRanges); - var MAX_CYCLES = 5; - if (changeCalled == true && this.COUNTER < MAX_CYCLES) { - DOMutil.cleanupElements(this.svgElements); - this.abortedGraphUpdate = true; - this.COUNTER++; - this.body.emitter.emit('change'); - return true; + // draw label + var point; + if (this.label) { + if (this.options.smoothCurves.enabled == true && via != null) { + var midpointX = 0.5*(0.5*(this.from.x + via.x) + 0.5*(this.to.x + via.x)); + var midpointY = 0.5*(0.5*(this.from.y + via.y) + 0.5*(this.to.y + via.y)); + point = {x:midpointX, y:midpointY}; } else { - if (this.COUNTER > MAX_CYCLES) { - console.log("WARNING: there may be an infinite loop in the _updateGraph emitter cycle.") - } - this.COUNTER = 0; - this.abortedGraphUpdate = false; - - // With the yAxis scaled correctly, use this to get the Y values of the points. - for (i = 0; i < groupIds.length; i++) { - group = this.groups[groupIds[i]]; - processedGroupData[groupIds[i]] = this._convertYcoordinates(groupsData[groupIds[i]], group); - } - - // draw the groups - for (i = 0; i < groupIds.length; i++) { - group = this.groups[groupIds[i]]; - if (group.options.style != 'bar') { // bar needs to be drawn enmasse - group.draw(processedGroupData[groupIds[i]], group, this.framework); - } - } - BarGraphFunctions.draw(groupIds, processedGroupData, this.framework); + point = this._pointOnLine(0.5); } + this._label(ctx, this.label, point.x, point.y); } } - - // cleanup unused svg elements - DOMutil.cleanupElements(this.svgElements); - return false; + else { + var x, y; + var radius = this.physics.springLength / 4; + var node = this.from; + if (!node.width) { + node.resize(ctx); + } + if (node.width > node.height) { + x = node.x + node.width / 2; + y = node.y - radius; + } + else { + x = node.x + radius; + y = node.y - node.height / 2; + } + this._circle(ctx, x, y, radius); + point = this._pointOnCircle(x, y, radius, 0.5); + this._label(ctx, this.label, point.x, point.y); + } }; - /** - * first select and preprocess the data from the datasets. - * the groups have their preselection of data, we now loop over this data to see - * what data we need to draw. Sorted data is much faster. - * more optimization is possible by doing the sampling before and using the binary search - * to find the end date to determine the increment. - * - * @param {array} groupIds - * @param {object} groupsData - * @param {date} minDate - * @param {date} maxDate + * Get the line width of the edge. Depends on width and whether one of the + * connected nodes is selected. + * @return {Number} width * @private */ - LineGraph.prototype._getRelevantData = function (groupIds, groupsData, minDate, maxDate) { - var group, i, j, item; - if (groupIds.length > 0) { - for (i = 0; i < groupIds.length; i++) { - group = this.groups[groupIds[i]]; - groupsData[groupIds[i]] = []; - var dataContainer = groupsData[groupIds[i]]; - // optimization for sorted data - if (group.options.sort == true) { - var guess = Math.max(0, util.binarySearchValue(group.itemsData, minDate, 'x', 'before')); - for (j = guess; j < group.itemsData.length; j++) { - item = group.itemsData[j]; - if (item !== undefined) { - if (item.x > maxDate) { - dataContainer.push(item); - break; - } - else { - dataContainer.push(item); - } - } - } - } - else { - for (j = 0; j < group.itemsData.length; j++) { - item = group.itemsData[j]; - if (item !== undefined) { - if (item.x > minDate && item.x < maxDate) { - dataContainer.push(item); - } - } - } - } + Edge.prototype._getLineWidth = function() { + if (this.selected == true) { + return Math.max(Math.min(this.widthSelected, this.options.widthMax), 0.3*this.networkScaleInv); + } + else { + if (this.hover == true) { + return Math.max(Math.min(this.options.hoverWidth, this.options.widthMax), 0.3*this.networkScaleInv); + } + else { + return Math.max(this.options.width, 0.3*this.networkScaleInv); } } }; + Edge.prototype._getViaCoordinates = function () { + if (this.options.smoothCurves.dynamic == true && this.options.smoothCurves.enabled == true ) { + return this.via; + } + else if (this.options.smoothCurves.enabled == false) { + return {x:0,y:0}; + } + else { + var xVia = null; + var yVia = null; + var factor = this.options.smoothCurves.roundness; + var type = this.options.smoothCurves.type; - /** - * - * @param groupIds - * @param groupsData - * @private - */ - LineGraph.prototype._applySampling = function (groupIds, groupsData) { - var group; - if (groupIds.length > 0) { - for (var i = 0; i < groupIds.length; i++) { - group = this.groups[groupIds[i]]; - if (group.options.sampling == true) { - var dataContainer = groupsData[groupIds[i]]; - if (dataContainer.length > 0) { - var increment = 1; - var amountOfPoints = dataContainer.length; - - // the global screen is used because changing the width of the yAxis may affect the increment, resulting in an endless loop - // of width changing of the yAxis. - var xDistance = this.body.util.toGlobalScreen(dataContainer[dataContainer.length - 1].x) - this.body.util.toGlobalScreen(dataContainer[0].x); - var pointsPerPixel = amountOfPoints / xDistance; - increment = Math.min(Math.ceil(0.2 * amountOfPoints), Math.max(1, Math.round(pointsPerPixel))); - - var sampledData = []; - for (var j = 0; j < amountOfPoints; j += increment) { - sampledData.push(dataContainer[j]); - + var dx = Math.abs(this.from.x - this.to.x); + var dy = Math.abs(this.from.y - this.to.y); + if (type == 'discrete' || type == 'diagonalCross') { + if (Math.abs(this.from.x - this.to.x) < Math.abs(this.from.y - this.to.y)) { + if (this.from.y > this.to.y) { + if (this.from.x < this.to.x) { + xVia = this.from.x + factor * dy; + yVia = this.from.y - factor * dy; } - groupsData[groupIds[i]] = sampledData; + else if (this.from.x > this.to.x) { + xVia = this.from.x - factor * dy; + yVia = this.from.y - factor * dy; + } + } + else if (this.from.y < this.to.y) { + if (this.from.x < this.to.x) { + xVia = this.from.x + factor * dy; + yVia = this.from.y + factor * dy; + } + else if (this.from.x > this.to.x) { + xVia = this.from.x - factor * dy; + yVia = this.from.y + factor * dy; + } + } + if (type == "discrete") { + xVia = dx < factor * dy ? this.from.x : xVia; + } + } + else if (Math.abs(this.from.x - this.to.x) > Math.abs(this.from.y - this.to.y)) { + if (this.from.y > this.to.y) { + if (this.from.x < this.to.x) { + xVia = this.from.x + factor * dx; + yVia = this.from.y - factor * dx; + } + else if (this.from.x > this.to.x) { + xVia = this.from.x - factor * dx; + yVia = this.from.y - factor * dx; + } + } + else if (this.from.y < this.to.y) { + if (this.from.x < this.to.x) { + xVia = this.from.x + factor * dx; + yVia = this.from.y + factor * dx; + } + else if (this.from.x > this.to.x) { + xVia = this.from.x - factor * dx; + yVia = this.from.y + factor * dx; + } + } + if (type == "discrete") { + yVia = dy < factor * dx ? this.from.y : yVia; } } } - } - }; - - - /** - * - * - * @param {array} groupIds - * @param {object} groupsData - * @param {object} groupRanges | this is being filled here - * @private - */ - LineGraph.prototype._getYRanges = function (groupIds, groupsData, groupRanges) { - var groupData, group, i; - var barCombinedDataLeft = []; - var barCombinedDataRight = []; - var options; - if (groupIds.length > 0) { - for (i = 0; i < groupIds.length; i++) { - groupData = groupsData[groupIds[i]]; - options = this.groups[groupIds[i]].options; - if (groupData.length > 0) { - group = this.groups[groupIds[i]]; - // if bar graphs are stacked, their range need to be handled differently and accumulated over all groups. - if (options.barChart.handleOverlap == 'stack' && options.style == 'bar') { - if (options.yAxisOrientation == 'left') {barCombinedDataLeft = barCombinedDataLeft.concat(group.getYRange(groupData)) ;} - else {barCombinedDataRight = barCombinedDataRight.concat(group.getYRange(groupData));} + else if (type == "straightCross") { + if (Math.abs(this.from.x - this.to.x) < Math.abs(this.from.y - this.to.y)) { // up - down + xVia = this.from.x; + if (this.from.y < this.to.y) { + yVia = this.to.y - (1 - factor) * dy; } else { - groupRanges[groupIds[i]] = group.getYRange(groupData,groupIds[i]); + yVia = this.to.y + (1 - factor) * dy; + } + } + else if (Math.abs(this.from.x - this.to.x) > Math.abs(this.from.y - this.to.y)) { // left - right + if (this.from.x < this.to.x) { + xVia = this.to.x - (1 - factor) * dx; + } + else { + xVia = this.to.x + (1 - factor) * dx; } + yVia = this.from.y; } } - - // if bar graphs are stacked, their range need to be handled differently and accumulated over all groups. - BarGraphFunctions.getStackedBarYRange(barCombinedDataLeft , groupRanges, groupIds, '__barchartLeft' , 'left' ); - BarGraphFunctions.getStackedBarYRange(barCombinedDataRight, groupRanges, groupIds, '__barchartRight', 'right'); - } - }; - - - /** - * this sets the Y ranges for the Y axis. It also determines which of the axis should be shown or hidden. - * @param {Array} groupIds - * @param {Object} groupRanges - * @private - */ - LineGraph.prototype._updateYAxis = function (groupIds, groupRanges) { - var resized = false; - var yAxisLeftUsed = false; - var yAxisRightUsed = false; - var minLeft = 1e9, minRight = 1e9, maxLeft = -1e9, maxRight = -1e9, minVal, maxVal; - // if groups are present - if (groupIds.length > 0) { - // this is here to make sure that if there are no items in the axis but there are groups, that there is no infinite draw/redraw loop. - for (var i = 0; i < groupIds.length; i++) { - var group = this.groups[groupIds[i]]; - if (group && group.options.yAxisOrientation != 'right') { - yAxisLeftUsed = true; - minLeft = 0; - maxLeft = 0; + else if (type == 'horizontal') { + if (this.from.x < this.to.x) { + xVia = this.to.x - (1 - factor) * dx; } - else if (group && group.options.yAxisOrientation) { - yAxisRightUsed = true; - minRight = 0; - maxRight = 0; + else { + xVia = this.to.x + (1 - factor) * dx; } + yVia = this.from.y; } - - // if there are items: - for (var i = 0; i < groupIds.length; i++) { - if (groupRanges.hasOwnProperty(groupIds[i])) { - if (groupRanges[groupIds[i]].ignore !== true) { - minVal = groupRanges[groupIds[i]].min; - maxVal = groupRanges[groupIds[i]].max; - - if (groupRanges[groupIds[i]].yAxisOrientation != 'right') { - yAxisLeftUsed = true; - minLeft = minLeft > minVal ? minVal : minLeft; - maxLeft = maxLeft < maxVal ? maxVal : maxLeft; + else if (type == 'vertical') { + xVia = this.from.x; + if (this.from.y < this.to.y) { + yVia = this.to.y - (1 - factor) * dy; + } + else { + yVia = this.to.y + (1 - factor) * dy; + } + } + else { // continuous + if (Math.abs(this.from.x - this.to.x) < Math.abs(this.from.y - this.to.y)) { + if (this.from.y > this.to.y) { + if (this.from.x < this.to.x) { + // console.log(1) + xVia = this.from.x + factor * dy; + yVia = this.from.y - factor * dy; + xVia = this.to.x < xVia ? this.to.x : xVia; } - else { - yAxisRightUsed = true; - minRight = minRight > minVal ? minVal : minRight; - maxRight = maxRight < maxVal ? maxVal : maxRight; + else if (this.from.x > this.to.x) { + // console.log(2) + xVia = this.from.x - factor * dy; + yVia = this.from.y - factor * dy; + xVia = this.to.x > xVia ? this.to.x : xVia; + } + } + else if (this.from.y < this.to.y) { + if (this.from.x < this.to.x) { + // console.log(3) + xVia = this.from.x + factor * dy; + yVia = this.from.y + factor * dy; + xVia = this.to.x < xVia ? this.to.x : xVia; + } + else if (this.from.x > this.to.x) { + // console.log(4, this.from.x, this.to.x) + xVia = this.from.x - factor * dy; + yVia = this.from.y + factor * dy; + xVia = this.to.x > xVia ? this.to.x : xVia; + } + } + } + else if (Math.abs(this.from.x - this.to.x) > Math.abs(this.from.y - this.to.y)) { + if (this.from.y > this.to.y) { + if (this.from.x < this.to.x) { + // console.log(5) + xVia = this.from.x + factor * dx; + yVia = this.from.y - factor * dx; + yVia = this.to.y > yVia ? this.to.y : yVia; + } + else if (this.from.x > this.to.x) { + // console.log(6) + xVia = this.from.x - factor * dx; + yVia = this.from.y - factor * dx; + yVia = this.to.y > yVia ? this.to.y : yVia; + } + } + else if (this.from.y < this.to.y) { + if (this.from.x < this.to.x) { + // console.log(7) + xVia = this.from.x + factor * dx; + yVia = this.from.y + factor * dx; + yVia = this.to.y < yVia ? this.to.y : yVia; + } + else if (this.from.x > this.to.x) { + // console.log(8) + xVia = this.from.x - factor * dx; + yVia = this.from.y + factor * dx; + yVia = this.to.y < yVia ? this.to.y : yVia; } } } } - if (yAxisLeftUsed == true) { - this.yAxisLeft.setRange(minLeft, maxLeft); - } - if (yAxisRightUsed == true) { - this.yAxisRight.setRange(minRight, maxRight); - } - } - resized = this._toggleAxisVisiblity(yAxisLeftUsed , this.yAxisLeft) || resized; - resized = this._toggleAxisVisiblity(yAxisRightUsed, this.yAxisRight) || resized; - - if (yAxisRightUsed == true && yAxisLeftUsed == true) { - this.yAxisLeft.drawIcons = true; - this.yAxisRight.drawIcons = true; - } - else { - this.yAxisLeft.drawIcons = false; - this.yAxisRight.drawIcons = false; - } - this.yAxisRight.master = !yAxisLeftUsed; - if (this.yAxisRight.master == false) { - if (yAxisRightUsed == true) {this.yAxisLeft.lineOffset = this.yAxisRight.width;} - else {this.yAxisLeft.lineOffset = 0;} - - resized = this.yAxisLeft.redraw() || resized; - this.yAxisRight.stepPixelsForced = this.yAxisLeft.stepPixels; - this.yAxisRight.zeroCrossing = this.yAxisLeft.zeroCrossing; - resized = this.yAxisRight.redraw() || resized; - } - else { - resized = this.yAxisRight.redraw() || resized; - } - // clean the accumulated lists - if (groupIds.indexOf('__barchartLeft') != -1) { - groupIds.splice(groupIds.indexOf('__barchartLeft'),1); - } - if (groupIds.indexOf('__barchartRight') != -1) { - groupIds.splice(groupIds.indexOf('__barchartRight'),1); + return {x: xVia, y: yVia}; } - - return resized; }; - /** - * This shows or hides the Y axis if needed. If there is a change, the changed event is emitted by the updateYAxis function - * - * @param {boolean} axisUsed - * @returns {boolean} + * Draw a line between two nodes + * @param {CanvasRenderingContext2D} ctx * @private - * @param axis */ - LineGraph.prototype._toggleAxisVisiblity = function (axisUsed, axis) { - var changed = false; - if (axisUsed == false) { - if (axis.dom.frame.parentNode && axis.hidden == false) { - axis.hide() - changed = true; + Edge.prototype._line = function (ctx) { + // draw a straight line + ctx.beginPath(); + ctx.moveTo(this.from.x, this.from.y); + if (this.options.smoothCurves.enabled == true) { + if (this.options.smoothCurves.dynamic == false) { + var via = this._getViaCoordinates(); + if (via.x == null) { + ctx.lineTo(this.to.x, this.to.y); + ctx.stroke(); + return null; + } + else { + // this.via.x = via.x; + // this.via.y = via.y; + ctx.quadraticCurveTo(via.x,via.y,this.to.x, this.to.y); + ctx.stroke(); + return via; + } + } + else { + ctx.quadraticCurveTo(this.via.x,this.via.y,this.to.x, this.to.y); + ctx.stroke(); + return this.via; } } else { - if (!axis.dom.frame.parentNode && axis.hidden == true) { - axis.show(); - changed = true; - } + ctx.lineTo(this.to.x, this.to.y); + ctx.stroke(); + return null; } - return changed; }; - /** - * This uses the DataAxis object to generate the correct X coordinate on the SVG window. It uses the - * util function toScreen to get the x coordinate from the timestamp. It also pre-filters the data and get the minMax ranges for - * the yAxis. - * - * @param datapoints - * @returns {Array} + * Draw a line from a node to itself, a circle + * @param {CanvasRenderingContext2D} ctx + * @param {Number} x + * @param {Number} y + * @param {Number} radius * @private */ - LineGraph.prototype._convertXcoordinates = function (datapoints) { - var extractedData = []; - var xValue, yValue; - var toScreen = this.body.util.toScreen; - - for (var i = 0; i < datapoints.length; i++) { - xValue = toScreen(datapoints[i].x) + this.props.width; - yValue = datapoints[i].y; - extractedData.push({x: xValue, y: yValue}); - } - - return extractedData; + Edge.prototype._circle = function (ctx, x, y, radius) { + // draw a circle + ctx.beginPath(); + ctx.arc(x, y, radius, 0, 2 * Math.PI, false); + ctx.stroke(); }; - /** - * This uses the DataAxis object to generate the correct X coordinate on the SVG window. It uses the - * util function toScreen to get the x coordinate from the timestamp. It also pre-filters the data and get the minMax ranges for - * the yAxis. - * - * @param datapoints - * @param group - * @returns {Array} + * Draw label with white background and with the middle at (x, y) + * @param {CanvasRenderingContext2D} ctx + * @param {String} text + * @param {Number} x + * @param {Number} y * @private */ - LineGraph.prototype._convertYcoordinates = function (datapoints, group) { - var extractedData = []; - var xValue, yValue; - var toScreen = this.body.util.toScreen; - var axis = this.yAxisLeft; - var svgHeight = Number(this.svg.style.height.replace('px','')); - if (group.options.yAxisOrientation == 'right') { - axis = this.yAxisRight; - } - - for (var i = 0; i < datapoints.length; i++) { - xValue = toScreen(datapoints[i].x) + this.props.width; - yValue = Math.round(axis.convertValue(datapoints[i].y)); - extractedData.push({x: xValue, y: yValue}); - } - - group.setZeroPosition(Math.min(svgHeight, axis.convertValue(0))); - - return extractedData; - }; - - - module.exports = LineGraph; - - -/***/ }, -/* 44 */ -/***/ function(module, exports, __webpack_require__) { + Edge.prototype._label = function (ctx, text, x, y) { + if (text) { + ctx.font = ((this.from.selected || this.to.selected) ? "bold " : "") + + this.options.fontSize + "px " + this.options.fontFace; + var yLine; - var util = __webpack_require__(1); - var DOMutil = __webpack_require__(6); - var Component = __webpack_require__(23); - var DataStep = __webpack_require__(45); + if (this.dirtyLabel == true) { + var lines = String(text).split('\n'); + var lineCount = lines.length; + var fontSize = Number(this.options.fontSize); + yLine = y + (1 - lineCount) / 2 * fontSize; - /** - * A horizontal time axis - * @param {Object} [options] See DataAxis.setOptions for the available - * options. - * @constructor DataAxis - * @extends Component - * @param body - */ - function DataAxis (body, options, svg, linegraphOptions) { - this.id = util.randomUUID(); - this.body = body; + var 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; + } + var height = this.options.fontSize * lineCount; + var left = x - width / 2; + var top = y - height / 2; - this.defaultOptions = { - orientation: 'left', // supported: 'left', 'right' - showMinorLabels: true, - showMajorLabels: true, - icons: true, - majorLinesOffset: 7, - minorLinesOffset: 4, - labelOffsetX: 10, - labelOffsetY: 2, - iconWidth: 20, - width: '40px', - visible: true, - alignZeros: true, - customRange: { - left: {min:undefined, max:undefined}, - right: {min:undefined, max:undefined} - }, - title: { - left: {text:undefined}, - right: {text:undefined} - }, - format: { - left: {decimals: undefined}, - right: {decimals: undefined} + // cache + this.labelDimensions = {top:top,left:left,width:width,height:height,yLine:yLine}; } - }; - - this.linegraphOptions = linegraphOptions; - this.linegraphSVG = svg; - this.props = {}; - this.DOMelements = { // dynamic elements - lines: {}, - labels: {}, - title: {} - }; - - this.dom = {}; - - this.range = {start:0, end:0}; - - this.options = util.extend({}, this.defaultOptions); - this.conversionFactor = 1; - - this.setOptions(options); - this.width = Number(('' + this.options.width).replace("px","")); - this.minWidth = this.width; - this.height = this.linegraphSVG.offsetHeight; - this.hidden = false; - - this.stepPixels = 25; - this.stepPixelsForced = 25; - this.zeroCrossing = -1; - - this.lineOffset = 0; - this.master = true; - this.svgElements = {}; - this.iconsRemoved = false; - - - this.groups = {}; - this.amountOfGroups = 0; - - // create the HTML DOM - this._create(); - - var me = this; - this.body.emitter.on("verticalDrag", function() { - me.dom.lineContainer.style.top = me.body.domProps.scrollTop + 'px'; - }); - } - - DataAxis.prototype = new Component(); + var yLine = this.labelDimensions.yLine; + + ctx.save(); + + if (this.options.labelAlignment != "horizontal"){ + ctx.translate(x, yLine); + this._rotateForLabelAlignment(ctx); + x = 0; + yLine = 0; + } - DataAxis.prototype.addGroup = function(label, graphOptions) { - if (!this.groups.hasOwnProperty(label)) { - this.groups[label] = graphOptions; + + this._drawLabelRect(ctx); + this._drawLabelText(ctx,x,yLine, lines, lineCount, fontSize); + + ctx.restore(); } - this.amountOfGroups += 1; }; - DataAxis.prototype.updateGroup = function(label, graphOptions) { - this.groups[label] = graphOptions; - }; + /** + * Rotates the canvas so the text is most readable + * @param {CanvasRenderingContext2D} ctx + * @private + */ + Edge.prototype._rotateForLabelAlignment = function(ctx) { + var dy = this.from.y - this.to.y; + var dx = this.from.x - this.to.x; + var angleInDegrees = Math.atan2(dy, dx); - DataAxis.prototype.removeGroup = function(label) { - if (this.groups.hasOwnProperty(label)) { - delete this.groups[label]; - this.amountOfGroups -= 1; - } + // rotate so label it is readable + if((angleInDegrees < -1 && dx < 0) || (angleInDegrees > 0 && dx < 0)){ + angleInDegrees = angleInDegrees + Math.PI; + } + + ctx.rotate(angleInDegrees); }; + /** + * Draws the label rectangle + * @param {CanvasRenderingContext2D} ctx + * @param {String} labelAlignment + * @private + */ + Edge.prototype._drawLabelRect = function(ctx) { + if (this.options.fontFill !== undefined && this.options.fontFill !== null && this.options.fontFill !== "none") { + ctx.fillStyle = this.options.fontFill; + + var lineMargin = 2; - DataAxis.prototype.setOptions = function (options) { - if (options) { - var redraw = false; - if (this.options.orientation != options.orientation && options.orientation !== undefined) { - redraw = true; + if (this.options.labelAlignment == 'line-center') { + ctx.fillRect(-this.labelDimensions.width * 0.5, -this.labelDimensions.height * 0.5, this.labelDimensions.width, this.labelDimensions.height); } - var fields = [ - 'orientation', - 'showMinorLabels', - 'showMajorLabels', - 'icons', - 'majorLinesOffset', - 'minorLinesOffset', - 'labelOffsetX', - 'labelOffsetY', - 'iconWidth', - 'width', - 'visible', - 'customRange', - 'title', - 'format', - 'alignZeros' - ]; - util.selectiveExtend(fields, this.options, options); - - this.minWidth = Number(('' + this.options.width).replace("px","")); - - if (redraw == true && this.dom.frame) { - this.hide(); - this.show(); + else if (this.options.labelAlignment == 'line-above') { + ctx.fillRect(-this.labelDimensions.width * 0.5, -(this.labelDimensions.height + lineMargin), this.labelDimensions.width, this.labelDimensions.height); + } + else if (this.options.labelAlignment == 'line-below') { + ctx.fillRect(-this.labelDimensions.width * 0.5, lineMargin, this.labelDimensions.width, this.labelDimensions.height); + } + else { + ctx.fillRect(this.labelDimensions.left, this.labelDimensions.top, this.labelDimensions.width, this.labelDimensions.height); } } }; - /** - * Create the HTML DOM for the DataAxis + * Draws the label text + * @param {CanvasRenderingContext2D} ctx + * @param {Number} x + * @param {Number} yLine + * @param {Array} lines + * @param {Number} lineCount + * @param {Number} fontSize + * @private */ - DataAxis.prototype._create = function() { - this.dom.frame = document.createElement('div'); - this.dom.frame.style.width = this.options.width; - this.dom.frame.style.height = this.height; + Edge.prototype._drawLabelText = function(ctx, x, yLine, lines, lineCount, fontSize) { + // draw text + ctx.fillStyle = this.options.fontColor || "black"; + ctx.textAlign = "center"; - this.dom.lineContainer = document.createElement('div'); - this.dom.lineContainer.style.width = '100%'; - this.dom.lineContainer.style.height = this.height; - this.dom.lineContainer.style.position = 'relative'; + // check for label alignment + if (this.options.labelAlignment != 'horizontal') { + var lineMargin = 2; + if (this.options.labelAlignment == 'line-above') { + ctx.textBaseline = "alphabetic"; + yLine -= 2 * lineMargin; // distance from edge, required because we use alphabetic. Alphabetic has less difference between browsers + } + else if (this.options.labelAlignment == 'line-below') { + 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 = "middle"; + } - // create svg element for graph drawing. - this.svg = document.createElementNS('http://www.w3.org/2000/svg',"svg"); - this.svg.style.position = "absolute"; - this.svg.style.top = '0px'; - this.svg.style.height = '100%'; - this.svg.style.width = '100%'; - this.svg.style.display = "block"; - this.dom.frame.appendChild(this.svg); - }; - - DataAxis.prototype._redrawGroupIcons = function () { - DOMutil.prepareElements(this.svgElements); - - var x; - var iconWidth = this.options.iconWidth; - var iconHeight = 15; - var iconOffset = 4; - var y = iconOffset + 0.5 * iconHeight; - - if (this.options.orientation == 'left') { - x = iconOffset; - } - else { - x = this.width - iconWidth - iconOffset; + // check for strokeWidth + if (this.options.fontStrokeWidth > 0){ + ctx.lineWidth = this.options.fontStrokeWidth; + ctx.strokeStyle = this.options.fontStrokeColor; + ctx.lineJoin = 'round'; } - - for (var groupId in this.groups) { - if (this.groups.hasOwnProperty(groupId)) { - if (this.groups[groupId].visible == true && (this.linegraphOptions.visibility[groupId] === undefined || this.linegraphOptions.visibility[groupId] == true)) { - this.groups[groupId].drawIcon(x, y, this.svgElements, this.svg, iconWidth, iconHeight); - y += iconHeight + iconOffset; - } + for (var i = 0; i < lineCount; i++) { + if(this.options.fontStrokeWidth > 0){ + ctx.strokeText(lines[i], x, yLine); } - } - - DOMutil.cleanupElements(this.svgElements); - this.iconsRemoved = false; + ctx.fillText(lines[i], x, yLine); + yLine += fontSize; + } }; - DataAxis.prototype._cleanupIcons = function() { - if (this.iconsRemoved == false) { - DOMutil.prepareElements(this.svgElements); - DOMutil.cleanupElements(this.svgElements); - this.iconsRemoved = true; - } - } - /** - * Create the HTML DOM for the DataAxis + * Redraw a edge as a dashed line + * Draw this edge in the given canvas + * @author David Jordan + * @date 2012-08-08 + * The 2d context of a HTML canvas can be retrieved by canvas.getContext("2d"); + * @param {CanvasRenderingContext2D} ctx + * @private */ - DataAxis.prototype.show = function() { - this.hidden = false; - if (!this.dom.frame.parentNode) { - if (this.options.orientation == 'left') { - this.body.dom.left.appendChild(this.dom.frame); + Edge.prototype._drawDashLine = function(ctx) { + // set style + ctx.strokeStyle = this._getColor(); + ctx.lineWidth = this._getLineWidth(); + + var via = null; + // only firefox and chrome support this method, else we use the legacy one. + if (ctx.setLineDash !== undefined) { + ctx.save(); + // configure the dash pattern + var pattern = [0]; + if (this.options.dash.length !== undefined && this.options.dash.gap !== undefined) { + pattern = [this.options.dash.length,this.options.dash.gap]; } else { - this.body.dom.right.appendChild(this.dom.frame); + pattern = [5,5]; + } + + // set dash settings for chrome or firefox + ctx.setLineDash(pattern); + ctx.lineDashOffset = 0; + + // draw the line + via = this._line(ctx); + + // restore the dash settings. + ctx.setLineDash([0]); + ctx.lineDashOffset = 0; + ctx.restore(); + } + else { // unsupporting smooth lines + // draw dashed line + ctx.beginPath(); + ctx.lineCap = 'round'; + if (this.options.dash.altLength !== undefined) //If an alt dash value has been set add to the array this value + { + ctx.dashedLine(this.from.x,this.from.y,this.to.x,this.to.y, + [this.options.dash.length,this.options.dash.gap,this.options.dash.altLength,this.options.dash.gap]); + } + else if (this.options.dash.length !== undefined && this.options.dash.gap !== undefined) //If a dash and gap value has been set add to the array this value + { + ctx.dashedLine(this.from.x,this.from.y,this.to.x,this.to.y, + [this.options.dash.length,this.options.dash.gap]); + } + else //If all else fails draw a line + { + ctx.moveTo(this.from.x, this.from.y); + ctx.lineTo(this.to.x, this.to.y); } + ctx.stroke(); } - if (!this.dom.lineContainer.parentNode) { - this.body.dom.backgroundHorizontal.appendChild(this.dom.lineContainer); + // draw label + if (this.label) { + var point; + if (this.options.smoothCurves.enabled == true && via != null) { + var midpointX = 0.5*(0.5*(this.from.x + via.x) + 0.5*(this.to.x + via.x)); + var midpointY = 0.5*(0.5*(this.from.y + via.y) + 0.5*(this.to.y + via.y)); + point = {x:midpointX, y:midpointY}; + } + else { + point = this._pointOnLine(0.5); + } + this._label(ctx, this.label, point.x, point.y); } }; /** - * Create the HTML DOM for the DataAxis + * Get a point on a line + * @param {Number} percentage. Value between 0 (line start) and 1 (line end) + * @return {Object} point + * @private */ - DataAxis.prototype.hide = function() { - this.hidden = true; - if (this.dom.frame.parentNode) { - this.dom.frame.parentNode.removeChild(this.dom.frame); - } - - if (this.dom.lineContainer.parentNode) { - this.dom.lineContainer.parentNode.removeChild(this.dom.lineContainer); + Edge.prototype._pointOnLine = function (percentage) { + return { + x: (1 - percentage) * this.from.x + percentage * this.to.x, + y: (1 - percentage) * this.from.y + percentage * this.to.y } }; /** - * Set a range (start and end) - * @param end - * @param start - * @param end + * 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 */ - DataAxis.prototype.setRange = function (start, end) { - if (this.master == false && this.options.alignZeros == true && this.zeroCrossing != -1) { - if (start > 0) { - start = 0; - } + Edge.prototype._pointOnCircle = function (x, y, radius, percentage) { + var angle = (percentage - 3/8) * 2 * Math.PI; + return { + x: x + radius * Math.cos(angle), + y: y - radius * Math.sin(angle) } - this.range.start = start; - this.range.end = end; }; /** - * Repaint the component - * @return {boolean} Returns true if the component is resized + * Redraw a edge as a line with an arrow halfway the line + * Draw this edge in the given canvas + * The 2d context of a HTML canvas can be retrieved by canvas.getContext("2d"); + * @param {CanvasRenderingContext2D} ctx + * @private */ - DataAxis.prototype.redraw = function () { - var resized = false; - var activeGroups = 0; - - // Make sure the line container adheres to the vertical scrolling. - this.dom.lineContainer.style.top = this.body.domProps.scrollTop + 'px'; + Edge.prototype._drawArrowCenter = function(ctx) { + var point; + // set style + ctx.strokeStyle = this._getColor(); + ctx.fillStyle = ctx.strokeStyle; + ctx.lineWidth = this._getLineWidth(); - for (var groupId in this.groups) { - if (this.groups.hasOwnProperty(groupId)) { - if (this.groups[groupId].visible == true && (this.linegraphOptions.visibility[groupId] === undefined || this.linegraphOptions.visibility[groupId] == true)) { - activeGroups++; - } + if (this.from != this.to) { + // draw line + var via = this._line(ctx); + + var angle = Math.atan2((this.to.y - this.from.y), (this.to.x - this.from.x)); + var length = (10 + 5 * this.options.width) * this.options.arrowScaleFactor; + // draw an arrow halfway the line + if (this.options.smoothCurves.enabled == true && via != null) { + var midpointX = 0.5*(0.5*(this.from.x + via.x) + 0.5*(this.to.x + via.x)); + var midpointY = 0.5*(0.5*(this.from.y + via.y) + 0.5*(this.to.y + via.y)); + point = {x:midpointX, y:midpointY}; + } + else { + point = this._pointOnLine(0.5); + } + + ctx.arrow(point.x, point.y, angle, length); + ctx.fill(); + ctx.stroke(); + + // draw label + if (this.label) { + this._label(ctx, this.label, point.x, point.y); } - } - if (this.amountOfGroups == 0 || activeGroups == 0) { - this.hide(); } else { - this.show(); - this.height = Number(this.linegraphSVG.style.height.replace("px","")); + // draw circle + var x, y; + var radius = 0.25 * Math.max(100,this.physics.springLength); + var node = this.from; + if (!node.width) { + node.resize(ctx); + } + if (node.width > node.height) { + x = node.x + node.width * 0.5; + y = node.y - radius; + } + else { + x = node.x + radius; + y = node.y - node.height * 0.5; + } + this._circle(ctx, x, y, radius); - // svg offsetheight did not work in firefox and explorer... - this.dom.lineContainer.style.height = this.height + 'px'; - this.width = this.options.visible == true ? Number(('' + this.options.width).replace("px","")) : 0; + // draw all arrows + var angle = 0.2 * Math.PI; + var length = (10 + 5 * this.options.width) * this.options.arrowScaleFactor; + point = this._pointOnCircle(x, y, radius, 0.5); + ctx.arrow(point.x, point.y, angle, length); + ctx.fill(); + ctx.stroke(); - var props = this.props; - var frame = this.dom.frame; + // draw label + if (this.label) { + point = this._pointOnCircle(x, y, radius, 0.5); + this._label(ctx, this.label, point.x, point.y); + } + } + }; - // update classname - frame.className = 'dataaxis'; + Edge.prototype._pointOnBezier = function(t) { + var via = this._getViaCoordinates(); - // calculate character width and height - this._calculateCharSize(); + var x = Math.pow(1-t,2)*this.from.x + (2*t*(1 - t))*via.x + Math.pow(t,2)*this.to.x; + var y = Math.pow(1-t,2)*this.from.y + (2*t*(1 - t))*via.y + Math.pow(t,2)*this.to.y; - var orientation = this.options.orientation; - var showMinorLabels = this.options.showMinorLabels; - var showMajorLabels = this.options.showMajorLabels; + return {x:x,y:y}; + } - // determine the width and height of the elements for the axis - props.minorLabelHeight = showMinorLabels ? props.minorCharHeight : 0; - props.majorLabelHeight = showMajorLabels ? props.majorCharHeight : 0; + /** + * This function uses binary search to look for the point where the bezier curve crosses the border of the node. + * + * @param from + * @param ctx + * @returns {*} + * @private + */ + Edge.prototype._findBorderPosition = function(from,ctx) { + var maxIterations = 10; + var iteration = 0; + var low = 0; + var high = 1; + var pos,angle,distanceToBorder, distanceToNodes, difference; + var threshold = 0.2; + var node = this.to; + if (from == true) { + node = this.from; + } - props.minorLineWidth = this.body.dom.backgroundHorizontal.offsetWidth - this.lineOffset - this.width + 2 * this.options.minorLinesOffset; - props.minorLineHeight = 1; - props.majorLineWidth = this.body.dom.backgroundHorizontal.offsetWidth - this.lineOffset - this.width + 2 * this.options.majorLinesOffset; - props.majorLineHeight = 1; + while (low <= high && iteration < maxIterations) { + var middle = (low + high) * 0.5; - // take frame offline while updating (is almost twice as fast) - if (orientation == 'left') { - frame.style.top = '0'; - frame.style.left = '0'; - frame.style.bottom = ''; - frame.style.width = this.width + 'px'; - frame.style.height = this.height + "px"; - this.props.width = this.body.domProps.left.width; - this.props.height = this.body.domProps.left.height; - } - else { // right - frame.style.top = ''; - frame.style.bottom = '0'; - frame.style.left = '0'; - frame.style.width = this.width + 'px'; - frame.style.height = this.height + "px"; - this.props.width = this.body.domProps.right.width; - this.props.height = this.body.domProps.right.height; + pos = this._pointOnBezier(middle); + angle = Math.atan2((node.y - pos.y), (node.x - pos.x)); + distanceToBorder = node.distanceToBorder(ctx,angle); + distanceToNodes = Math.sqrt(Math.pow(pos.x-node.x,2) + Math.pow(pos.y-node.y,2)); + difference = distanceToBorder - distanceToNodes; + if (Math.abs(difference) < threshold) { + break; // found } - - resized = this._redrawLabels(); - resized = this._isResized() || resized; - - if (this.options.icons == true) { - this._redrawGroupIcons(); + else if (difference < 0) { // distance to nodes is larger than distance to border --> t needs to be bigger if we're looking at the to node. + if (from == false) { + low = middle; + } + else { + high = middle; + } } else { - this._cleanupIcons(); + if (from == false) { + high = middle; + } + else { + low = middle; + } } - this._redrawTitle(orientation); + iteration++; } - return resized; + pos.t = middle; + + return pos; }; /** - * Repaint major and minor text labels and vertical grid lines + * Redraw a edge as a line with an arrow + * Draw this edge in the given canvas + * The 2d context of a HTML canvas can be retrieved by canvas.getContext("2d"); + * @param {CanvasRenderingContext2D} ctx * @private */ - DataAxis.prototype._redrawLabels = function () { - var resized = false; - DOMutil.prepareElements(this.DOMelements.lines); - DOMutil.prepareElements(this.DOMelements.labels); - - var orientation = this.options['orientation']; - - // calculate range and step (step such that we have space for 7 characters per label) - var minimumStep = this.master ? this.props.majorCharHeight || 10 : this.stepPixelsForced; - - var step = new DataStep( - this.range.start, - this.range.end, - minimumStep, - this.dom.frame.offsetHeight, - this.options.customRange[this.options.orientation], - this.master == false && this.options.alignZeros // doess the step have to align zeros? only if not master and the options is on - ); + Edge.prototype._drawArrow = function(ctx) { + // set style + ctx.strokeStyle = this._getColor(); + ctx.fillStyle = ctx.strokeStyle; + ctx.lineWidth = this._getLineWidth(); - this.step = step; - // get the distance in pixels for a step - // dead space is space that is "left over" after a step - var stepPixels = (this.dom.frame.offsetHeight - (step.deadSpace * (this.dom.frame.offsetHeight / step.marginRange))) / (((step.marginRange - step.deadSpace) / step.step)); + // set vars + var angle, length, arrowPos; - this.stepPixels = stepPixels; + // if not connected to itself + if (this.from != this.to) { + // draw line + this._line(ctx); - var amountOfSteps = this.height / stepPixels; - var stepDifference = 0; + // draw arrow head + if (this.options.smoothCurves.enabled == true) { + var via = this._getViaCoordinates(); + arrowPos = this._findBorderPosition(false, ctx); + var guidePos = this._pointOnBezier(Math.max(0.0, arrowPos.t - 0.1)) + angle = Math.atan2((arrowPos.y - guidePos.y), (arrowPos.x - guidePos.x)); + } + else { + angle = Math.atan2((this.to.y - this.from.y), (this.to.x - this.from.x)); + var dx = (this.to.x - this.from.x); + var dy = (this.to.y - this.from.y); + var edgeSegmentLength = Math.sqrt(dx * dx + dy * dy); + var toBorderDist = this.to.distanceToBorder(ctx, angle); + var toBorderPoint = (edgeSegmentLength - toBorderDist) / edgeSegmentLength; - // the slave axis needs to use the same horizontal lines as the master axis. - if (this.master == false) { - stepPixels = this.stepPixelsForced; - stepDifference = Math.round((this.dom.frame.offsetHeight / stepPixels) - amountOfSteps); - for (var i = 0; i < 0.5 * stepDifference; i++) { - step.previous(); + arrowPos = {}; + arrowPos.x = (1 - toBorderPoint) * this.from.x + toBorderPoint * this.to.x; + arrowPos.y = (1 - toBorderPoint) * this.from.y + toBorderPoint * this.to.y; } - amountOfSteps = this.height / stepPixels; - if (this.zeroCrossing != -1 && this.options.alignZeros == true) { - var zeroStepDifference = (step.marginEnd / step.step) - this.zeroCrossing; - if (zeroStepDifference > 0) { - for (var i = 0; i < zeroStepDifference; i++) {step.next();} + // draw arrow at the end of the line + length = (10 + 5 * this.options.width) * this.options.arrowScaleFactor; + ctx.arrow(arrowPos.x,arrowPos.y, angle, length); + ctx.fill(); + ctx.stroke(); + + // draw label + if (this.label) { + var point; + if (this.options.smoothCurves.enabled == true && via != null) { + point = this._pointOnBezier(0.5); } - else if (zeroStepDifference < 0) { - for (var i = 0; i < -zeroStepDifference; i++) {step.previous();} + else { + point = this._pointOnLine(0.5); } + this._label(ctx, this.label, point.x, point.y); } } else { - amountOfSteps += 0.25; - } - - - this.valueAtZero = step.marginEnd; - var marginStartPos = 0; - - // do not draw the first label - var max = 1; - - // Get the number of decimal places - var decimals; - if(this.options.format[orientation] !== undefined) { - decimals = this.options.format[orientation].decimals; - } - - this.maxLabelSize = 0; - var y = 0; - while (max < Math.round(amountOfSteps)) { - step.next(); - y = Math.round(max * stepPixels); - marginStartPos = max * stepPixels; - var isMajor = step.isMajor(); - - if (this.options['showMinorLabels'] && isMajor == false || this.master == false && this.options['showMinorLabels'] == true) { - this._redrawLabel(y - 2, step.getCurrent(decimals), orientation, 'yAxis minor', this.props.minorCharHeight); + // draw circle + var node = this.from; + var x, y, arrow; + var radius = 0.25 * Math.max(100,this.physics.springLength); + if (!node.width) { + node.resize(ctx); } - - if (isMajor && this.options['showMajorLabels'] && this.master == true || - this.options['showMinorLabels'] == false && this.master == false && isMajor == true) { - if (y >= 0) { - this._redrawLabel(y - 2, step.getCurrent(decimals), orientation, 'yAxis major', this.props.majorCharHeight); - } - this._redrawLine(y, orientation, 'grid horizontal major', this.options.majorLinesOffset, this.props.majorLineWidth); + if (node.width > node.height) { + x = node.x + node.width * 0.5; + y = node.y - radius; + arrow = { + x: x, + y: node.y, + angle: 0.9 * Math.PI + }; } else { - this._redrawLine(y, orientation, 'grid horizontal minor', this.options.minorLinesOffset, this.props.minorLineWidth); - } - - if (this.master == true && step.current == 0) { - this.zeroCrossing = max; + x = node.x + radius; + y = node.y - node.height * 0.5; + arrow = { + x: node.x, + y: y, + angle: 0.6 * Math.PI + }; } + ctx.beginPath(); + // TODO: similarly, for a line without arrows, draw to the border of the nodes instead of the center + ctx.arc(x, y, radius, 0, 2 * Math.PI, false); + ctx.stroke(); - max++; - } + // draw all arrows + var length = (10 + 5 * this.options.width) * this.options.arrowScaleFactor; + ctx.arrow(arrow.x, arrow.y, arrow.angle, length); + ctx.fill(); + ctx.stroke(); - if (this.master == false) { - this.conversionFactor = y / (this.valueAtZero - step.current); - } - else { - this.conversionFactor = this.dom.frame.offsetHeight / step.marginRange; + // draw label + if (this.label) { + point = this._pointOnCircle(x, y, radius, 0.5); + this._label(ctx, this.label, point.x, point.y); + } } + }; - // Note that title is rotated, so we're using the height, not width! - var titleWidth = 0; - if (this.options.title[orientation] !== undefined && this.options.title[orientation].text !== undefined) { - titleWidth = this.props.titleCharHeight; + /** + * Calculate the distance between a point (x3,y3) and a line segment from + * (x1,y1) to (x2,y2). + * http://stackoverflow.com/questions/849211/shortest-distancae-between-a-point-and-a-line-segment + * @param {number} x1 + * @param {number} y1 + * @param {number} x2 + * @param {number} y2 + * @param {number} x3 + * @param {number} y3 + * @private + */ + Edge.prototype._getDistanceToEdge = function (x1,y1, x2,y2, x3,y3) { // x3,y3 is the point + var returnValue = 0; + if (this.from != this.to) { + if (this.options.smoothCurves.enabled == true) { + var xVia, yVia; + if (this.options.smoothCurves.enabled == true && this.options.smoothCurves.dynamic == true) { + xVia = this.via.x; + yVia = this.via.y; + } + else { + var via = this._getViaCoordinates(); + xVia = via.x; + yVia = via.y; + } + var minDistance = 1e9; + var distance; + var i,t,x,y, lastX, lastY; + for (i = 0; i < 10; i++) { + t = 0.1*i; + x = Math.pow(1-t,2)*x1 + (2*t*(1 - t))*xVia + Math.pow(t,2)*x2; + y = Math.pow(1-t,2)*y1 + (2*t*(1 - t))*yVia + Math.pow(t,2)*y2; + if (i > 0) { + distance = this._getDistanceToLine(lastX,lastY,x,y, x3,y3); + minDistance = distance < minDistance ? distance : minDistance; + } + lastX = x; lastY = y; + } + returnValue = minDistance; + } + else { + returnValue = this._getDistanceToLine(x1,y1,x2,y2,x3,y3); + } } - var offset = this.options.icons == true ? Math.max(this.options.iconWidth, titleWidth) + this.options.labelOffsetX + 15 : titleWidth + this.options.labelOffsetX + 15; - - // this will resize the yAxis to accommodate the labels. - if (this.maxLabelSize > (this.width - offset) && this.options.visible == true) { - this.width = this.maxLabelSize + offset; - this.options.width = this.width + "px"; - DOMutil.cleanupElements(this.DOMelements.lines); - DOMutil.cleanupElements(this.DOMelements.labels); - this.redraw(); - resized = true; + else { + var x, y, dx, dy; + var radius = 0.25 * this.physics.springLength; + var node = this.from; + if (node.width > node.height) { + x = node.x + 0.5 * node.width; + y = node.y - radius; + } + else { + x = node.x + radius; + y = node.y - 0.5 * node.height; + } + dx = x - x3; + dy = y - y3; + returnValue = Math.abs(Math.sqrt(dx*dx + dy*dy) - radius); } - // this will resize the yAxis if it is too big for the labels. - else if (this.maxLabelSize < (this.width - offset) && this.options.visible == true && this.width > this.minWidth) { - this.width = Math.max(this.minWidth,this.maxLabelSize + offset); - this.options.width = this.width + "px"; - DOMutil.cleanupElements(this.DOMelements.lines); - DOMutil.cleanupElements(this.DOMelements.labels); - this.redraw(); - resized = true; + + if (this.labelDimensions.left < x3 && + this.labelDimensions.left + this.labelDimensions.width > x3 && + this.labelDimensions.top < y3 && + this.labelDimensions.top + this.labelDimensions.height > y3) { + return 0; } else { - DOMutil.cleanupElements(this.DOMelements.lines); - DOMutil.cleanupElements(this.DOMelements.labels); - resized = false; + return returnValue; } - - return resized; }; - DataAxis.prototype.convertValue = function (value) { - var invertedValue = this.valueAtZero - value; - var convertedValue = invertedValue * this.conversionFactor; - return convertedValue; - }; + Edge.prototype._getDistanceToLine = function(x1,y1,x2,y2,x3,y3) { + var px = x2-x1, + py = y2-y1, + something = px*px + py*py, + u = ((x3 - x1) * px + (y3 - y1) * py) / something; - /** - * Create a label for the axis at position x - * @private - * @param y - * @param text - * @param orientation - * @param className - * @param characterHeight - */ - DataAxis.prototype._redrawLabel = function (y, text, orientation, className, characterHeight) { - // reuse redundant label - var label = DOMutil.getDOMElement('div',this.DOMelements.labels, this.dom.frame); //this.dom.redundant.labels.shift(); - label.className = className; - label.innerHTML = text; - if (orientation == 'left') { - label.style.left = '-' + this.options.labelOffsetX + 'px'; - label.style.textAlign = "right"; + if (u > 1) { + u = 1; } - else { - label.style.right = '-' + this.options.labelOffsetX + 'px'; - label.style.textAlign = "left"; + else if (u < 0) { + u = 0; } - label.style.top = y - 0.5 * characterHeight + this.options.labelOffsetY + 'px'; + var x = x1 + u * px, + y = y1 + u * py, + dx = x - x3, + dy = y - y3; - text += ''; + //# Note: If the actual distance does not matter, + //# if you only want to compare what this function + //# returns to other results of this function, you + //# can just return the squared distance instead + //# (i.e. remove the sqrt) to gain a little performance - var largestWidth = Math.max(this.props.majorCharWidth,this.props.minorCharWidth); - if (this.maxLabelSize < text.length * largestWidth) { - this.maxLabelSize = text.length * largestWidth; - } + return Math.sqrt(dx*dx + dy*dy); }; /** - * Create a minor line for the axis at position y - * @param y - * @param orientation - * @param className - * @param offset - * @param width + * This allows the zoom level of the network to influence the rendering + * + * @param scale */ - DataAxis.prototype._redrawLine = function (y, orientation, className, offset, width) { - if (this.master == true) { - var line = DOMutil.getDOMElement('div',this.DOMelements.lines, this.dom.lineContainer);//this.dom.redundant.lines.shift(); - line.className = className; - line.innerHTML = ''; + Edge.prototype.setScale = function(scale) { + this.networkScaleInv = 1.0/scale; + }; - if (orientation == 'left') { - line.style.left = (this.width - offset) + 'px'; - } - else { - line.style.right = (this.width - offset) + 'px'; - } - line.style.width = width + 'px'; - line.style.top = y + 'px'; + Edge.prototype.select = function() { + this.selected = true; + }; + + Edge.prototype.unselect = function() { + this.selected = false; + }; + + Edge.prototype.positionBezierNode = function() { + if (this.via !== null && this.from !== null && this.to !== null) { + 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 !== null) { + this.via.x = 0; + this.via.y = 0; } }; /** - * Create a title for the axis - * @private - * @param orientation + * This function draws the control nodes for the manipulator. + * In order to enable this, only set the this.controlNodesEnabled to true. + * @param ctx */ - DataAxis.prototype._redrawTitle = function (orientation) { - DOMutil.prepareElements(this.DOMelements.title); - - // Check if the title is defined for this axes - if (this.options.title[orientation] !== undefined && this.options.title[orientation].text !== undefined) { - var title = DOMutil.getDOMElement('div', this.DOMelements.title, this.dom.frame); - title.className = 'yAxis title ' + orientation; - title.innerHTML = this.options.title[orientation].text; - - // Add style - if provided - if (this.options.title[orientation].style !== undefined) { - util.addCssText(title, this.options.title[orientation].style); + Edge.prototype._drawControlNodes = function(ctx) { + if (this.controlNodesEnabled == true) { + if (this.controlNodes.from === null && this.controlNodes.to === null) { + var nodeIdFrom = "edgeIdFrom:".concat(this.id); + var nodeIdTo = "edgeIdTo:".concat(this.id); + var constants = { + nodes:{group:'', radius:7, borderWidth:2, borderWidthSelected: 2}, + physics:{damping:0}, + clustering: {maxNodeSizeIncrements: 0 ,nodeScaling: {width:0, height: 0, radius:0}} + }; + this.controlNodes.from = new Node( + {id:nodeIdFrom, + shape:'dot', + color:{background:'#ff0000', border:'#3c3c3c', highlight: {background:'#07f968'}} + },{},{},constants); + this.controlNodes.to = new Node( + {id:nodeIdTo, + shape:'dot', + color:{background:'#ff0000', border:'#3c3c3c', highlight: {background:'#07f968'}} + },{},{},constants); } - if (orientation == 'left') { - title.style.left = this.props.titleCharHeight + 'px'; + this.controlNodes.positions = {}; + if (this.controlNodes.from.selected == false) { + this.controlNodes.positions.from = this.getControlNodeFromPosition(ctx); + this.controlNodes.from.x = this.controlNodes.positions.from.x; + this.controlNodes.from.y = this.controlNodes.positions.from.y; } - else { - title.style.right = this.props.titleCharHeight + 'px'; + if (this.controlNodes.to.selected == false) { + this.controlNodes.positions.to = this.getControlNodeToPosition(ctx); + this.controlNodes.to.x = this.controlNodes.positions.to.x; + this.controlNodes.to.y = this.controlNodes.positions.to.y; } - title.style.width = this.height + 'px'; + this.controlNodes.from.draw(ctx); + this.controlNodes.to.draw(ctx); + } + else { + this.controlNodes = {from:null, to:null, positions:{}}; } + }; - // we need to clean up in case we did not use all elements. - DOMutil.cleanupElements(this.DOMelements.title); + /** + * Enable control nodes. + * @private + */ + Edge.prototype._enableControlNodes = function() { + this.fromBackup = this.from; + this.toBackup = this.to; + this.controlNodesEnabled = true; }; + /** + * disable control nodes and remove from dynamicEdges from old node + * @private + */ + Edge.prototype._disableControlNodes = function() { + this.fromId = this.from.id; + this.toId = this.to.id; + if (this.fromId != this.fromBackup.id) { // from was changed, remove edge from old 'from' node dynamic edges + this.fromBackup.detachEdge(this); + } + else if (this.toId != this.toBackup.id) { // to was changed, remove edge from old 'to' node dynamic edges + this.toBackup.detachEdge(this); + } + this.fromBackup = null; + this.toBackup = null; + this.controlNodesEnabled = false; + }; /** - * Determine the size of text on the axis (both major and minor axis). - * The size is calculated only once and then cached in this.props. + * This checks if one of the control nodes is selected and if so, returns the control node object. Else it returns null. + * @param x + * @param y + * @returns {null} * @private */ - DataAxis.prototype._calculateCharSize = function () { - // determine the char width and height on the minor axis - if (!('minorCharHeight' in this.props)) { - var textMinor = document.createTextNode('0'); - var measureCharMinor = document.createElement('div'); - measureCharMinor.className = 'yAxis minor measure'; - measureCharMinor.appendChild(textMinor); - this.dom.frame.appendChild(measureCharMinor); - - this.props.minorCharHeight = measureCharMinor.clientHeight; - this.props.minorCharWidth = measureCharMinor.clientWidth; + Edge.prototype._getSelectedControlNode = function(x,y) { + var positions = this.controlNodes.positions; + var fromDistance = Math.sqrt(Math.pow(x - positions.from.x,2) + Math.pow(y - positions.from.y,2)); + var toDistance = Math.sqrt(Math.pow(x - positions.to.x ,2) + Math.pow(y - positions.to.y ,2)); - this.dom.frame.removeChild(measureCharMinor); + if (fromDistance < 15) { + this.connectedNode = this.from; + this.from = this.controlNodes.from; + return this.controlNodes.from; } - - if (!('majorCharHeight' in this.props)) { - var textMajor = document.createTextNode('0'); - var measureCharMajor = document.createElement('div'); - measureCharMajor.className = 'yAxis major measure'; - measureCharMajor.appendChild(textMajor); - this.dom.frame.appendChild(measureCharMajor); - - this.props.majorCharHeight = measureCharMajor.clientHeight; - this.props.majorCharWidth = measureCharMajor.clientWidth; - - this.dom.frame.removeChild(measureCharMajor); + else if (toDistance < 15) { + this.connectedNode = this.to; + this.to = this.controlNodes.to; + return this.controlNodes.to; } - - if (!('titleCharHeight' in this.props)) { - var textTitle = document.createTextNode('0'); - var measureCharTitle = document.createElement('div'); - measureCharTitle.className = 'yAxis title measure'; - measureCharTitle.appendChild(textTitle); - this.dom.frame.appendChild(measureCharTitle); - - this.props.titleCharHeight = measureCharTitle.clientHeight; - this.props.titleCharWidth = measureCharTitle.clientWidth; - - this.dom.frame.removeChild(measureCharTitle); + else { + return null; } }; + /** - * Snap a date to a rounded value. - * The snap intervals are dependent on the current scale and step. - * @param {Date} date the date to be snapped. - * @return {Date} snappedDate + * this resets the control nodes to their original position. + * @private */ - DataAxis.prototype.snap = function(date) { - return this.step.snap(date); + Edge.prototype._restoreControlNodes = function() { + if (this.controlNodes.from.selected == true) { + this.from = this.connectedNode; + this.connectedNode = null; + this.controlNodes.from.unselect(); + } + else if (this.controlNodes.to.selected == true) { + this.to = this.connectedNode; + this.connectedNode = null; + this.controlNodes.to.unselect(); + } }; - module.exports = DataAxis; + /** + * this calculates the position of the control nodes on the edges of the parent nodes. + * + * @param ctx + * @returns {x: *, y: *} + */ + Edge.prototype.getControlNodeFromPosition = function(ctx) { + // draw arrow head + var controlnodeFromPos; + if (this.options.smoothCurves.enabled == true) { + controlnodeFromPos = this._findBorderPosition(true, ctx); + } + else { + var angle = Math.atan2((this.to.y - this.from.y), (this.to.x - this.from.x)); + var dx = (this.to.x - this.from.x); + var dy = (this.to.y - this.from.y); + var edgeSegmentLength = Math.sqrt(dx * dx + dy * dy); + var fromBorderDist = this.from.distanceToBorder(ctx, angle + Math.PI); + var fromBorderPoint = (edgeSegmentLength - fromBorderDist) / edgeSegmentLength; + controlnodeFromPos = {}; + controlnodeFromPos.x = (fromBorderPoint) * this.from.x + (1 - fromBorderPoint) * this.to.x; + controlnodeFromPos.y = (fromBorderPoint) * this.from.y + (1 - fromBorderPoint) * this.to.y; + } -/***/ }, -/* 45 */ -/***/ function(module, exports, __webpack_require__) { + return controlnodeFromPos; + }; /** - * @constructor DataStep - * The class DataStep is an iterator for data for the lineGraph. You provide a start data point and an - * end data point. The class itself determines the best scale (step size) based on the - * provided start Date, end Date, and minimumStep. - * - * If minimumStep is provided, the step size is chosen as close as possible - * to the minimumStep but larger than minimumStep. If minimumStep is not - * provided, the scale is set to 1 DAY. - * The minimumStep should correspond with the onscreen size of about 6 characters - * - * Alternatively, you can set a scale by hand. - * After creation, you can initialize the class by executing first(). Then you - * can iterate from the start date to the end date via next(). You can check if - * the end date is reached with the function hasNext(). After each step, you can - * retrieve the current date via getCurrent(). - * The DataStep has scales ranging from milliseconds, seconds, minutes, hours, - * days, to years. - * - * Version: 1.2 + * this calculates the position of the control nodes on the edges of the parent nodes. * - * @param {Date} [start] The start date, for example new Date(2010, 9, 21) - * or new Date(2010, 9, 21, 23, 45, 00) - * @param {Date} [end] The end date - * @param {Number} [minimumStep] Optional. Minimum step size in milliseconds + * @param ctx + * @returns {{from: {x: number, y: number}, to: {x: *, y: *}}} */ - function DataStep(start, end, minimumStep, containerHeight, customRange, alignZeros) { - // variables - this.current = 0; + Edge.prototype.getControlNodeToPosition = function(ctx) { + // draw arrow head + var controlnodeFromPos,controlnodeToPos; + if (this.options.smoothCurves.enabled == true) { + controlnodeToPos = this._findBorderPosition(false, ctx); + } + else { + var angle = Math.atan2((this.to.y - this.from.y), (this.to.x - this.from.x)); + var dx = (this.to.x - this.from.x); + var dy = (this.to.y - this.from.y); + var edgeSegmentLength = Math.sqrt(dx * dx + dy * dy); + var toBorderDist = this.to.distanceToBorder(ctx, angle); + var toBorderPoint = (edgeSegmentLength - toBorderDist) / edgeSegmentLength; - this.autoScale = true; - this.stepIndex = 0; - this.step = 1; - this.scale = 1; + controlnodeToPos = {}; + controlnodeToPos.x = (1 - toBorderPoint) * this.from.x + toBorderPoint * this.to.x; + controlnodeToPos.y = (1 - toBorderPoint) * this.from.y + toBorderPoint * this.to.y; + } - this.marginStart; - this.marginEnd; - this.deadSpace = 0; + return controlnodeToPos; + }; - this.majorSteps = [1, 2, 5, 10]; - this.minorSteps = [0.25, 0.5, 1, 2]; + module.exports = Edge; - this.alignZeros = alignZeros; +/***/ }, +/* 38 */ +/***/ function(module, exports, __webpack_require__) { - this.setRange(start, end, minimumStep, containerHeight, customRange); - } + var util = __webpack_require__(1); + /** + * @class Groups + * This class can store groups and properties specific for groups. + */ + function Groups() { + this.clear(); + this.defaultIndex = 0; + } /** - * Set a new range - * If minimumStep is provided, the step size is chosen as close as possible - * to the minimumStep but larger than minimumStep. If minimumStep is not - * provided, the scale is set to 1 DAY. - * The minimumStep should correspond with the onscreen size of about 6 characters - * @param {Number} [start] The start date and time. - * @param {Number} [end] The end date and time. - * @param {Number} [minimumStep] Optional. Minimum step size in milliseconds + * default constants for group colors */ - DataStep.prototype.setRange = function(start, end, minimumStep, containerHeight, customRange) { - this._start = customRange.min === undefined ? start : customRange.min; - this._end = customRange.max === undefined ? end : customRange.max; + Groups.DEFAULT = [ + {border: "#2B7CE9", background: "#97C2FC", highlight: {border: "#2B7CE9", background: "#D2E5FF"}, hover: {border: "#2B7CE9", background: "#D2E5FF"}}, // blue + {border: "#FFA500", background: "#FFFF00", highlight: {border: "#FFA500", background: "#FFFFA3"}, hover: {border: "#FFA500", background: "#FFFFA3"}}, // yellow + {border: "#FA0A10", background: "#FB7E81", highlight: {border: "#FA0A10", background: "#FFAFB1"}, hover: {border: "#FA0A10", background: "#FFAFB1"}}, // red + {border: "#41A906", background: "#7BE141", highlight: {border: "#41A906", background: "#A1EC76"}, hover: {border: "#41A906", background: "#A1EC76"}}, // green + {border: "#E129F0", background: "#EB7DF4", highlight: {border: "#E129F0", background: "#F0B3F5"}, hover: {border: "#E129F0", background: "#F0B3F5"}}, // magenta + {border: "#7C29F0", background: "#AD85E4", highlight: {border: "#7C29F0", background: "#D3BDF0"}, hover: {border: "#7C29F0", background: "#D3BDF0"}}, // purple + {border: "#C37F00", background: "#FFA807", highlight: {border: "#C37F00", background: "#FFCA66"}, hover: {border: "#C37F00", background: "#FFCA66"}}, // orange + {border: "#4220FB", background: "#6E6EFD", highlight: {border: "#4220FB", background: "#9B9BFD"}, hover: {border: "#4220FB", background: "#9B9BFD"}}, // darkblue + {border: "#FD5A77", background: "#FFC0CB", highlight: {border: "#FD5A77", background: "#FFD1D9"}, hover: {border: "#FD5A77", background: "#FFD1D9"}}, // pink + {border: "#4AD63A", background: "#C2FABC", highlight: {border: "#4AD63A", background: "#E6FFE3"}, hover: {border: "#4AD63A", background: "#E6FFE3"}} // mint + ]; - if (this._start == this._end) { - this._start -= 0.75; - this._end += 1; - } - if (this.autoScale == true) { - this.setMinimumStep(minimumStep, containerHeight); + /** + * Clear all groups + */ + Groups.prototype.clear = function () { + this.groups = {}; + this.groups.length = function() + { + var i = 0; + for ( var p in this ) { + if (this.hasOwnProperty(p)) { + i++; + } + } + return i; } - - this.setFirst(customRange); }; + /** - * Automatically determine the scale that bests fits the provided minimum step - * @param {Number} [minimumStep] The minimum step size in milliseconds + * get group properties of a groupname. If groupname is not found, a new group + * is added. + * @param {*} groupname Can be a number, string, Date, etc. + * @return {Object} group The created group, containing all group properties */ - DataStep.prototype.setMinimumStep = function(minimumStep, containerHeight) { - // round to floor - var size = this._end - this._start; - var safeSize = size * 1.2; - var minimumStepValue = minimumStep * (safeSize / containerHeight); - var orderOfMagnitude = Math.round(Math.log(safeSize)/Math.LN10); + Groups.prototype.get = function (groupname) { + var group = this.groups[groupname]; + if (group == undefined) { + // create new group + var index = this.defaultIndex % Groups.DEFAULT.length; + this.defaultIndex++; + group = {}; + group.color = Groups.DEFAULT[index]; + this.groups[groupname] = group; + } - var minorStepIdx = -1; - var magnitudefactor = Math.pow(10,orderOfMagnitude); - - var start = 0; - if (orderOfMagnitude < 0) { - start = orderOfMagnitude; - } - - var solutionFound = false; - for (var i = start; Math.abs(i) <= Math.abs(orderOfMagnitude); i++) { - magnitudefactor = Math.pow(10,i); - for (var j = 0; j < this.minorSteps.length; j++) { - var stepSize = magnitudefactor * this.minorSteps[j]; - if (stepSize >= minimumStepValue) { - solutionFound = true; - minorStepIdx = j; - break; - } - } - if (solutionFound == true) { - break; - } - } - this.stepIndex = minorStepIdx; - this.scale = magnitudefactor; - this.step = magnitudefactor * this.minorSteps[minorStepIdx]; + return group; }; - - /** - * Round the current date to the first minor date value - * This must be executed once when the current date is set to start Date + * Add a custom group style + * @param {String} groupname + * @param {Object} style An object containing borderColor, + * backgroundColor, etc. + * @return {Object} group The created group object */ - DataStep.prototype.setFirst = function(customRange) { - if (customRange === undefined) { - customRange = {}; - } - - var niceStart = customRange.min === undefined ? this._start - (this.scale * 2 * this.minorSteps[this.stepIndex]) : customRange.min; - var niceEnd = customRange.max === undefined ? this._end + (this.scale * this.minorSteps[this.stepIndex]) : customRange.max; - - this.marginEnd = customRange.max === undefined ? this.roundToMinor(niceEnd) : customRange.max; - this.marginStart = customRange.min === undefined ? this.roundToMinor(niceStart) : customRange.min; - - // if we need to align the zero's we need to make sure that there is a zero to use. - if (this.alignZeros == true && (this.marginEnd - this.marginStart) % this.step != 0) { - this.marginEnd += this.marginEnd % this.step; - } - - this.deadSpace = this.roundToMinor(niceEnd) - niceEnd + this.roundToMinor(niceStart) - niceStart; - this.marginRange = this.marginEnd - this.marginStart; - - - this.current = this.marginEnd; + Groups.prototype.add = function (groupname, style) { + this.groups[groupname] = style; + return style; }; - DataStep.prototype.roundToMinor = function(value) { - var rounded = value - (value % (this.scale * this.minorSteps[this.stepIndex])); - if (value % (this.scale * this.minorSteps[this.stepIndex]) > 0.5 * (this.scale * this.minorSteps[this.stepIndex])) { - return rounded + (this.scale * this.minorSteps[this.stepIndex]); - } - else { - return rounded; - } - } + module.exports = Groups; - /** - * Check if the there is a next step - * @return {boolean} true if the current date has not passed the end date - */ - DataStep.prototype.hasNext = function () { - return (this.current >= this.marginStart); - }; +/***/ }, +/* 39 */ +/***/ function(module, exports, __webpack_require__) { /** - * Do the next step + * @class Images + * This class loads images and keeps them stored. */ - DataStep.prototype.next = function() { - var prev = this.current; - this.current -= this.step; - - // safety mechanism: if current time is still unchanged, move to the end - if (this.current == prev) { - this.current = this._end; - } - }; + function Images() { + this.images = {}; + this.imageBroken = {}; + this.callback = undefined; + } /** - * Do the next step + * Set an onload callback function. This will be called each time an image + * is loaded + * @param {function} callback */ - DataStep.prototype.previous = function() { - this.current += this.step; - this.marginEnd += this.step; - this.marginRange = this.marginEnd - this.marginStart; + Images.prototype.setOnloadCallback = function(callback) { + this.callback = callback; }; - - /** - * Get the current datetime - * @return {String} current The current date + * + * @param {string} url Url of the image + * @param {string} url Url of an image to use if the url image is not found + * @return {Image} img The image object */ - DataStep.prototype.getCurrent = function(decimals) { - // prevent round-off errors when close to zero - var current = (Math.abs(this.current) < this.step / 2) ? 0 : this.current; - var toPrecision = '' + Number(current).toPrecision(5); - - // If decimals is specified, then limit or extend the string as required - if(decimals !== undefined && !isNaN(Number(decimals))) { - // If string includes exponent, then we need to add it to the end - var exp = ""; - var index = toPrecision.indexOf("e"); - if(index != -1) { - // Get the exponent - exp = toPrecision.slice(index); - // Remove the exponent in case we need to zero-extend - toPrecision = toPrecision.slice(0, index); - } - index = Math.max(toPrecision.indexOf(","), toPrecision.indexOf(".")); - if(index === -1) { - // No decimal found - if we want decimals, then we need to add it - if(decimals !== 0) { - toPrecision += '.'; + Images.prototype.load = function(url, brokenUrl) { + var img = this.images[url]; // make a pointer + if (img === undefined) { + // create the image + var me = this; + img = new Image(); + img.onload = function () { + // IE11 fix -- thanks dponch! + if (this.width == 0) { + document.body.appendChild(this); + this.width = this.offsetWidth; + this.height = this.offsetHeight; + document.body.removeChild(this); } - // Calculate how long the string should be - index = toPrecision.length + decimals; - } - else if(decimals !== 0) { - // Calculate how long the string should be - accounting for the decimal place - index += decimals + 1; - } - if(index > toPrecision.length) { - // We need to add zeros! - for(var cnt = index - toPrecision.length; cnt > 0; cnt--) { - toPrecision += '0'; + + if (me.callback) { + me.images[url] = img; + me.callback(this); } - } - else { - // we need to remove characters - toPrecision = toPrecision.slice(0, index); - } - // Add the exponent if there is one - toPrecision += exp; - } - else { - if (toPrecision.indexOf(",") != -1 || toPrecision.indexOf(".") != -1) { - // If no decimal is specified, and there are decimal places, remove trailing zeros - for (var i = toPrecision.length - 1; i > 0; i--) { - if (toPrecision[i] == "0") { - toPrecision = toPrecision.slice(0, i); + }; + + img.onerror = function () { + if (brokenUrl === undefined) { + console.error("Could not load image:", url); + delete this.src; + if (me.callback) { + me.callback(this); } - else if (toPrecision[i] == "." || toPrecision[i] == ",") { - toPrecision = toPrecision.slice(0, i); - break; + } + else { + if (me.imageBroken[url] === true) { + if (this.src == brokenUrl) { + console.error("Could not load brokenImage:", brokenUrl); + delete this.src; + if (me.callback) { + me.callback(this); + } + } + else { + console.error("Could not load image:", url); + this.src = brokenUrl; + } } else { - break; + console.error("Could not load image:", url); + this.src = brokenUrl; + me.imageBroken[url] = true; } } - } + }; + + img.src = url; } - return toPrecision; + return img; }; + module.exports = Images; - /** - * Snap a date to a rounded value. - * The snap intervals are dependent on the current scale and step. - * @param {Date} date the date to be snapped. - * @return {Date} snappedDate - */ - DataStep.prototype.snap = function(date) { +/***/ }, +/* 40 */ +/***/ function(module, exports, __webpack_require__) { - }; + var util = __webpack_require__(1); /** - * Check if the current value is a major value (for example when the step - * is DAY, a major value is each first day of the MONTH) - * @return {boolean} true if current date is major, else false. + * @class Node + * A node. A node can be connected to other nodes via one or multiple edges. + * @param {object} properties An object containing properties for the node. All + * properties 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" + * {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 properties + * @param {Object} constants An object with default values for + * example for the color + * */ - DataStep.prototype.isMajor = function() { - return (this.current % (this.scale * this.majorSteps[this.stepIndex]) == 0); - }; + function Node(properties, imagelist, grouplist, networkConstants) { + var constants = util.selectiveBridgeObject(['nodes'],networkConstants); + this.options = constants.nodes; - module.exports = DataStep; + this.selected = false; + this.hover = false; + this.edges = []; // all edges connected to this node + this.dynamicEdges = []; + this.reroutedEdges = {}; -/***/ }, -/* 46 */ -/***/ function(module, exports, __webpack_require__) { + this.fontDrawThreshold = 3; - var util = __webpack_require__(1); - var DOMutil = __webpack_require__(6); - var Line = __webpack_require__(47); - var Bar = __webpack_require__(49); - var Points = __webpack_require__(48); + // set defaults for the properties + this.id = undefined; + this.allowedToMoveX = false; + this.allowedToMoveY = false; + this.xFixed = false; + this.yFixed = false; + this.horizontalAlignLeft = true; // these are for the navigation controls + this.verticalAlignTop = true; // these are for the navigation controls + this.baseRadiusValue = networkConstants.nodes.radius; + this.radiusFixed = false; + this.level = -1; + this.preassignedLevel = false; + this.hierarchyEnumerated = false; + this.labelDimensions = {top:0, left:0, width:0, height:0, yLine:0}; // could be cached + this.boundingBox = {top:0, left:0, right:0, bottom:0}; - /** - * /** - * @param {object} group | the object of the group from the dataset - * @param {string} groupId | ID of the group - * @param {object} options | the default options - * @param {array} groupsUsingDefaultStyles | this array has one entree. - * It is passed as an array so it is passed by reference. - * It enumerates through the default styles - * @constructor - */ - function GraphGroup (group, groupId, options, groupsUsingDefaultStyles) { - this.id = groupId; - var fields = ['sampling','style','sort','yAxisOrientation','barChart','drawPoints','shaded','catmullRom'] - this.options = util.selectiveBridgeObject(fields,options); - this.usingDefaultStyle = group.className === undefined; - this.groupsUsingDefaultStyles = groupsUsingDefaultStyles; - this.zeroPosition = 0; - this.update(group); - if (this.usingDefaultStyle == true) { - this.groupsUsingDefaultStyles[0] += 1; - } - this.itemsData = []; - this.visible = group.visible === undefined ? true : group.visible; + this.imagelist = imagelist; + this.grouplist = grouplist; + + // physics properties + this.fx = 0.0; // external force x + this.fy = 0.0; // external force y + this.vx = 0.0; // velocity x + this.vy = 0.0; // velocity y + this.x = null; + this.y = null; + + // used for reverting to previous position on stabilization + this.previousState = {vx:0,vy:0,x:0,y:0}; + + this.damping = networkConstants.physics.damping; // written every time gravity is calculated + this.fixedData = {x:null,y:null}; + + this.setProperties(properties, constants); + + // creating the variables for clustering + this.resetCluster(); + this.dynamicEdgesLength = 0; + this.clusterSession = 0; + this.clusterSizeWidthFactor = networkConstants.clustering.nodeScaling.width; + this.clusterSizeHeightFactor = networkConstants.clustering.nodeScaling.height; + this.clusterSizeRadiusFactor = networkConstants.clustering.nodeScaling.radius; + this.maxNodeSizeIncrements = networkConstants.clustering.maxNodeSizeIncrements; + this.growthIndicator = 0; + + // variables to tell the node about the network. + this.networkScaleInv = 1; + this.networkScale = 1; + this.canvasTopLeft = {"x": -300, "y": -300}; + this.canvasBottomRight = {"x": 300, "y": 300}; + this.parentEdgeId = null; } /** - * this loads a reference to all items in this group into this group. - * @param {array} items + * Revert the position and velocity of the previous step. */ - GraphGroup.prototype.setItems = function(items) { - if (items != null) { - this.itemsData = items; - if (this.options.sort == true) { - this.itemsData.sort(function (a,b) {return a.x - b.x;}) - } - } - else { - this.itemsData = []; - } - }; + Node.prototype.revertPosition = function() { + this.x = this.previousState.x; + this.y = this.previousState.y; + this.vx = this.previousState.vx; + this.vy = this.previousState.vy; + } /** - * this is used for plotting barcharts, this way, we only have to calculate it once. - * @param pos + * (re)setting the clustering variables and objects */ - GraphGroup.prototype.setZeroPosition = function(pos) { - this.zeroPosition = pos; + Node.prototype.resetCluster = function() { + // clustering variables + this.formationScale = undefined; // this is used to determine when to open the cluster + this.clusterSize = 1; // this signifies the total amount of nodes in this cluster + this.containedNodes = {}; + this.containedEdges = {}; + this.clusterSessions = []; }; - /** - * set the options of the graph group over the default options. - * @param options + * Attach a edge to the node + * @param {Edge} edge */ - GraphGroup.prototype.setOptions = function(options) { - if (options !== undefined) { - var fields = ['sampling','style','sort','yAxisOrientation','barChart']; - util.selectiveDeepExtend(fields, this.options, options); - - util.mergeOptions(this.options, options,'catmullRom'); - util.mergeOptions(this.options, options,'drawPoints'); - util.mergeOptions(this.options, options,'shaded'); - - if (options.catmullRom) { - if (typeof options.catmullRom == 'object') { - if (options.catmullRom.parametrization) { - if (options.catmullRom.parametrization == 'uniform') { - this.options.catmullRom.alpha = 0; - } - else if (options.catmullRom.parametrization == 'chordal') { - this.options.catmullRom.alpha = 1.0; - } - else { - this.options.catmullRom.parametrization = 'centripetal'; - this.options.catmullRom.alpha = 0.5; - } - } - } - } - } - - if (this.options.style == 'line') { - this.type = new Line(this.id, this.options); - } - else if (this.options.style == 'bar') { - this.type = new Bar(this.id, this.options); + Node.prototype.attachEdge = function(edge) { + if (this.edges.indexOf(edge) == -1) { + this.edges.push(edge); } - else if (this.options.style == 'points') { - this.type = new Points(this.id, this.options); + if (this.dynamicEdges.indexOf(edge) == -1) { + this.dynamicEdges.push(edge); } + this.dynamicEdgesLength = this.dynamicEdges.length; }; - /** - * this updates the current group class with the latest group dataset entree, used in _updateGroup in linegraph - * @param group + * Detach a edge from the node + * @param {Edge} edge */ - GraphGroup.prototype.update = function(group) { - this.group = group; - this.content = group.content || 'graph'; - this.className = group.className || this.className || "graphGroup" + this.groupsUsingDefaultStyles[0] % 10; - this.visible = group.visible === undefined ? true : group.visible; - this.style = group.style; - this.setOptions(group.options); + Node.prototype.detachEdge = function(edge) { + var index = this.edges.indexOf(edge); + if (index != -1) { + this.edges.splice(index, 1); + } + index = this.dynamicEdges.indexOf(edge); + if (index != -1) { + this.dynamicEdges.splice(index, 1); + } + this.dynamicEdgesLength = this.dynamicEdges.length; }; /** - * draw the icon for the legend. - * - * @param x - * @param y - * @param JSONcontainer - * @param SVGcontainer - * @param iconWidth - * @param iconHeight + * Set or overwrite properties for the node + * @param {Object} properties an object with properties + * @param {Object} constants and object with default, global properties */ - GraphGroup.prototype.drawIcon = function(x, y, JSONcontainer, SVGcontainer, iconWidth, iconHeight) { - var fillHeight = iconHeight * 0.5; - var path, fillPath; + Node.prototype.setProperties = function(properties, constants) { + if (!properties) { + return; + } - var outline = DOMutil.getSVGElement("rect", JSONcontainer, SVGcontainer); - outline.setAttributeNS(null, "x", x); - outline.setAttributeNS(null, "y", y - fillHeight); - outline.setAttributeNS(null, "width", iconWidth); - outline.setAttributeNS(null, "height", 2*fillHeight); - outline.setAttributeNS(null, "class", "outline"); + var fields = ['borderWidth','borderWidthSelected','shape','image','brokenImage','radius','fontColor', + 'fontSize','fontFace','fontFill','fontStrokeWidth','fontStrokeColor','group','mass' + ]; + util.selectiveDeepExtend(fields, this.options, properties); - if (this.options.style == 'line') { - path = DOMutil.getSVGElement("path", JSONcontainer, SVGcontainer); - path.setAttributeNS(null, "class", this.className); - if(this.style !== undefined) { - path.setAttributeNS(null, "style", this.style); - } + // basic properties + if (properties.id !== undefined) {this.id = properties.id;} + if (properties.label !== undefined) {this.label = properties.label; this.originalLabel = properties.label;} + if (properties.title !== undefined) {this.title = properties.title;} + if (properties.x !== undefined) {this.x = properties.x;} + if (properties.y !== undefined) {this.y = properties.y;} + if (properties.value !== undefined) {this.value = properties.value;} + if (properties.level !== undefined) {this.level = properties.level; this.preassignedLevel = true;} - path.setAttributeNS(null, "d", "M" + x + ","+y+" L" + (x + iconWidth) + ","+y+""); - if (this.options.shaded.enabled == true) { - fillPath = DOMutil.getSVGElement("path", JSONcontainer, SVGcontainer); - if (this.options.shaded.orientation == 'top') { - fillPath.setAttributeNS(null, "d", "M"+x+", " + (y - fillHeight) + - "L"+x+","+y+" L"+ (x + iconWidth) + ","+y+" L"+ (x + iconWidth) + "," + (y - fillHeight)); - } - else { - fillPath.setAttributeNS(null, "d", "M"+x+","+y+" " + - "L"+x+"," + (y + fillHeight) + " " + - "L"+ (x + iconWidth) + "," + (y + fillHeight) + - "L"+ (x + iconWidth) + ","+y); - } - fillPath.setAttributeNS(null, "class", this.className + " iconFill"); - } + // navigation controls properties + if (properties.horizontalAlignLeft !== undefined) {this.horizontalAlignLeft = properties.horizontalAlignLeft;} + if (properties.verticalAlignTop !== undefined) {this.verticalAlignTop = properties.verticalAlignTop;} + if (properties.triggerFunction !== undefined) {this.triggerFunction = properties.triggerFunction;} - if (this.options.drawPoints.enabled == true) { - DOMutil.drawPoint(x + 0.5 * iconWidth,y, this, JSONcontainer, SVGcontainer); - } + if (this.id === undefined) { + throw "Node must have an id"; } - else { - var barWidth = Math.round(0.3 * iconWidth); - var bar1Height = Math.round(0.4 * iconHeight); - var bar2Height = Math.round(0.75 * iconHeight); - var offset = Math.round((iconWidth - (2 * barWidth))/3); - - DOMutil.drawBar(x + 0.5*barWidth + offset , y + fillHeight - bar1Height - 1, barWidth, bar1Height, this.className + ' bar', JSONcontainer, SVGcontainer); - DOMutil.drawBar(x + 1.5*barWidth + offset + 2, y + fillHeight - bar2Height - 1, barWidth, bar2Height, this.className + ' bar', JSONcontainer, SVGcontainer); + // copy group properties + if (typeof properties.group === 'number' || (typeof properties.group === 'string' && properties.group != '')) { + var groupObj = this.grouplist.get(properties.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); } - }; + // individual shape properties + if (properties.radius !== undefined) {this.baseRadiusValue = this.options.radius;} + if (properties.color !== undefined) {this.options.color = util.parseColor(properties.color);} + 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"; + } + } - /** - * return the legend entree for this group. - * - * @param iconWidth - * @param iconHeight - * @returns {{icon: HTMLElement, label: (group.content|*|string), orientation: (.options.yAxisOrientation|*)}} - */ - GraphGroup.prototype.getLegend = function(iconWidth, iconHeight) { - var svg = document.createElementNS('http://www.w3.org/2000/svg',"svg"); - this.drawIcon(0,0.5*iconHeight,[],svg,iconWidth,iconHeight); - return {icon: svg, label: this.content, orientation:this.options.yAxisOrientation}; - } + if (properties.allowedToMoveX !== undefined) { + this.xFixed = !properties.allowedToMoveX; + this.allowedToMoveX = properties.allowedToMoveX; + } + else if (properties.x !== undefined && this.allowedToMoveX == false) { + this.xFixed = true; + } - GraphGroup.prototype.getYRange = function(groupData) { - return this.type.getYRange(groupData); - } - GraphGroup.prototype.draw = function(dataset, group, framework) { - this.type.draw(dataset, group, framework); - } + if (properties.allowedToMoveY !== undefined) { + this.yFixed = !properties.allowedToMoveY; + this.allowedToMoveY = properties.allowedToMoveY; + } + else if (properties.y !== undefined && this.allowedToMoveY == false) { + this.yFixed = true; + } + this.radiusFixed = this.radiusFixed || (properties.radius !== undefined); - module.exports = GraphGroup; + if (this.options.shape === 'image' || this.options.shape === 'circularImage') { + this.options.radiusMin = constants.nodes.widthMin; + this.options.radiusMax = constants.nodes.widthMax; + } + // choose draw method depending on the shape + switch (this.options.shape) { + case 'database': this.draw = this._drawDatabase; this.resize = this._resizeDatabase; break; + case 'box': this.draw = this._drawBox; this.resize = this._resizeBox; break; + case 'circle': this.draw = this._drawCircle; this.resize = this._resizeCircle; break; + case 'ellipse': this.draw = this._drawEllipse; this.resize = this._resizeEllipse; break; + // TODO: add diamond shape + case 'image': this.draw = this._drawImage; this.resize = this._resizeImage; break; + case 'circularImage': this.draw = this._drawCircularImage; this.resize = this._resizeCircularImage; break; + case 'text': this.draw = this._drawText; this.resize = this._resizeText; break; + case 'dot': this.draw = this._drawDot; this.resize = this._resizeShape; break; + case 'square': this.draw = this._drawSquare; this.resize = this._resizeShape; break; + case 'triangle': this.draw = this._drawTriangle; this.resize = this._resizeShape; break; + case 'triangleDown': this.draw = this._drawTriangleDown; this.resize = this._resizeShape; break; + case 'star': this.draw = this._drawStar; this.resize = this._resizeShape; break; + default: this.draw = this._drawEllipse; this.resize = this._resizeEllipse; break; + } + // reset the size of the node, this can be changed + this._reset(); -/***/ }, -/* 47 */ -/***/ function(module, exports, __webpack_require__) { + }; /** - * Created by Alex on 11/11/2014. + * select this node */ - var DOMutil = __webpack_require__(6); - var Points = __webpack_require__(48); - - function Line(groupId, options) { - this.groupId = groupId; - this.options = options; - } - - Line.prototype.getYRange = function(groupData) { - var yMin = groupData[0].y; - var yMax = groupData[0].y; - for (var j = 0; j < groupData.length; j++) { - yMin = yMin > groupData[j].y ? groupData[j].y : yMin; - yMax = yMax < groupData[j].y ? groupData[j].y : yMax; - } - return {min: yMin, max: yMax, yAxisOrientation: this.options.yAxisOrientation}; + Node.prototype.select = function() { + this.selected = true; + this._reset(); }; - /** - * draw a line graph - * - * @param dataset - * @param group + * unselect this node */ - Line.prototype.draw = function (dataset, group, framework) { - if (dataset != null) { - if (dataset.length > 0) { - var path, d; - var svgHeight = Number(framework.svg.style.height.replace('px','')); - path = DOMutil.getSVGElement('path', framework.svgElements, framework.svg); - path.setAttributeNS(null, "class", group.className); - if(group.style !== undefined) { - path.setAttributeNS(null, "style", group.style); - } - - // construct path from dataset - if (group.options.catmullRom.enabled == true) { - d = Line._catmullRom(dataset, group); - } - else { - d = Line._linear(dataset); - } + Node.prototype.unselect = function() { + this.selected = false; + this._reset(); + }; - // append with points for fill and finalize the path - if (group.options.shaded.enabled == true) { - var fillPath = DOMutil.getSVGElement('path', framework.svgElements, framework.svg); - var dFill; - if (group.options.shaded.orientation == 'top') { - dFill = 'M' + dataset[0].x + ',' + 0 + ' ' + d + 'L' + dataset[dataset.length - 1].x + ',' + 0; - } - else { - dFill = 'M' + dataset[0].x + ',' + svgHeight + ' ' + d + 'L' + dataset[dataset.length - 1].x + ',' + svgHeight; - } - fillPath.setAttributeNS(null, "class", group.className + " fill"); - if(group.options.shaded.style !== undefined) { - fillPath.setAttributeNS(null, "style", group.options.shaded.style); - } - fillPath.setAttributeNS(null, "d", dFill); - } - // copy properties to path for drawing. - path.setAttributeNS(null, 'd', 'M' + d); - // draw points - if (group.options.drawPoints.enabled == true) { - Points.draw(dataset, group, framework); - } - } - } + /** + * Reset the calculated size of the node, forces it to recalculate its size + */ + Node.prototype.clearSizeCache = function() { + this._reset(); }; + /** + * Reset the calculated size of the node, forces it to recalculate its size + * @private + */ + Node.prototype._reset = function() { + this.width = undefined; + this.height = undefined; + }; + /** + * get the title of this node. + * @return {string} title The title of the node, or undefined when no title + * has been set. + */ + Node.prototype.getTitle = function() { + return typeof this.title === "function" ? this.title() : this.title; + }; /** - * This uses an uniform parametrization of the CatmullRom algorithm: - * 'On the Parameterization of Catmull-Rom Curves' by Cem Yuksel et al. - * @param data - * @returns {string} - * @private + * 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 */ - Line._catmullRomUniform = function(data) { - // catmull rom - var p0, p1, p2, p3, bp1, bp2; - var d = Math.round(data[0].x) + ',' + Math.round(data[0].y) + ' '; - var normalization = 1/6; - var length = data.length; - for (var i = 0; i < length - 1; i++) { + Node.prototype.distanceToBorder = function (ctx, angle) { + var borderWidth = 1; - p0 = (i == 0) ? data[0] : data[i-1]; - p1 = data[i]; - p2 = data[i+1]; - p3 = (i + 2 < length) ? data[i+2] : p2; + if (!this.width) { + this.resize(ctx); + } + switch (this.options.shape) { + case 'circle': + case 'dot': + return this.options.radius+ borderWidth; - // Catmull-Rom to Cubic Bezier conversion matrix - // 0 1 0 0 - // -1/6 1 1/6 0 - // 0 1/6 1 -1/6 - // 0 0 1 0 + case 'ellipse': + 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); - // bp0 = { x: p1.x, y: p1.y }; - bp1 = { x: ((-p0.x + 6*p1.x + p2.x) *normalization), y: ((-p0.y + 6*p1.y + p2.y) *normalization)}; - bp2 = { x: (( p1.x + 6*p2.x - p3.x) *normalization), y: (( p1.y + 6*p2.y - p3.y) *normalization)}; - // bp0 = { x: p2.x, y: p2.y }; + // TODO: implement distanceToBorder for database + // TODO: implement distanceToBorder for triangle + // TODO: implement distanceToBorder for triangleDown + + case 'box': + case 'image': + case 'text': + default: + if (this.width) { + return Math.min( + Math.abs(this.width / 2 / Math.cos(angle)), + Math.abs(this.height / 2 / Math.sin(angle))) + borderWidth; + // TODO: reckon with border radius too in case of box + } + else { + return 0; + } - d += 'C' + - bp1.x + ',' + - bp1.y + ' ' + - bp2.x + ',' + - bp2.y + ' ' + - p2.x + ',' + - p2.y + ' '; } + // TODO: implement calculation of distance to border for all shapes + }; - return d; + /** + * Set forces acting on the node + * @param {number} fx Force in horizontal direction + * @param {number} fy Force in vertical direction + */ + Node.prototype._setForce = function(fx, fy) { + this.fx = fx; + this.fy = fy; }; /** - * This uses either the chordal or centripetal parameterization of the catmull-rom algorithm. - * By default, the centripetal parameterization is used because this gives the nicest results. - * These parameterizations are relatively heavy because the distance between 4 points have to be calculated. - * - * One optimization can be used to reuse distances since this is a sliding window approach. - * @param data - * @param group - * @returns {string} + * Add forces acting on the node + * @param {number} fx Force in horizontal direction + * @param {number} fy Force in vertical direction * @private */ - Line._catmullRom = function(data, group) { - var alpha = group.options.catmullRom.alpha; - if (alpha == 0 || alpha === undefined) { - return this._catmullRomUniform(data); + Node.prototype._addForce = function(fx, fy) { + this.fx += fx; + this.fy += fy; + }; + + /** + * Store the state before the next step + */ + Node.prototype.storeState = function() { + this.previousState.x = this.x; + this.previousState.y = this.y; + this.previousState.vx = this.vx; + this.previousState.vy = this.vy; + } + + /** + * Perform one discrete step for the node + * @param {number} interval Time interval in seconds + */ + Node.prototype.discreteStep = function(interval) { + this.storeState(); + if (!this.xFixed) { + var dx = this.damping * this.vx; // damping force + var ax = (this.fx - dx) / this.options.mass; // acceleration + this.vx += ax * interval; // velocity + this.x += this.vx * interval; // position } else { - var p0, p1, p2, p3, bp1, bp2, d1,d2,d3, A, B, N, M; - var d3powA, d2powA, d3pow2A, d2pow2A, d1pow2A, d1powA; - var d = Math.round(data[0].x) + ',' + Math.round(data[0].y) + ' '; - var length = data.length; - for (var i = 0; i < length - 1; i++) { - - p0 = (i == 0) ? data[0] : data[i-1]; - p1 = data[i]; - p2 = data[i+1]; - p3 = (i + 2 < length) ? data[i+2] : p2; + this.fx = 0; + this.vx = 0; + } - d1 = Math.sqrt(Math.pow(p0.x - p1.x,2) + Math.pow(p0.y - p1.y,2)); - d2 = Math.sqrt(Math.pow(p1.x - p2.x,2) + Math.pow(p1.y - p2.y,2)); - d3 = Math.sqrt(Math.pow(p2.x - p3.x,2) + Math.pow(p2.y - p3.y,2)); + if (!this.yFixed) { + var dy = this.damping * this.vy; // damping force + var ay = (this.fy - dy) / this.options.mass; // acceleration + this.vy += ay * interval; // velocity + this.y += this.vy * interval; // position + } + else { + this.fy = 0; + this.vy = 0; + } + }; - // Catmull-Rom to Cubic Bezier conversion matrix - // A = 2d1^2a + 3d1^a * d2^a + d3^2a - // B = 2d3^2a + 3d3^a * d2^a + d2^2a - // [ 0 1 0 0 ] - // [ -d2^2a /N A/N d1^2a /N 0 ] - // [ 0 d3^2a /M B/M -d2^2a /M ] - // [ 0 0 1 0 ] + /** + * Perform one discrete step for the node + * @param {number} interval Time interval in seconds + * @param {number} maxVelocity The speed limit imposed on the velocity + */ + Node.prototype.discreteStepLimited = function(interval, maxVelocity) { + this.storeState(); + if (!this.xFixed) { + var dx = this.damping * this.vx; // damping force + var ax = (this.fx - dx) / this.options.mass; // acceleration + this.vx += ax * interval; // velocity + this.vx = (Math.abs(this.vx) > maxVelocity) ? ((this.vx > 0) ? maxVelocity : -maxVelocity) : this.vx; + this.x += this.vx * interval; // position + } + else { + this.fx = 0; + this.vx = 0; + } - d3powA = Math.pow(d3, alpha); - d3pow2A = Math.pow(d3,2*alpha); - d2powA = Math.pow(d2, alpha); - d2pow2A = Math.pow(d2,2*alpha); - d1powA = Math.pow(d1, alpha); - d1pow2A = Math.pow(d1,2*alpha); + if (!this.yFixed) { + var dy = this.damping * this.vy; // damping force + var ay = (this.fy - dy) / this.options.mass; // acceleration + this.vy += ay * interval; // velocity + this.vy = (Math.abs(this.vy) > maxVelocity) ? ((this.vy > 0) ? maxVelocity : -maxVelocity) : this.vy; + this.y += this.vy * interval; // position + } + else { + this.fy = 0; + this.vy = 0; + } + }; - A = 2*d1pow2A + 3*d1powA * d2powA + d2pow2A; - B = 2*d3pow2A + 3*d3powA * d2powA + d2pow2A; - N = 3*d1powA * (d1powA + d2powA); - if (N > 0) {N = 1 / N;} - M = 3*d3powA * (d3powA + d2powA); - if (M > 0) {M = 1 / M;} + /** + * Check if this node has a fixed x and y position + * @return {boolean} true if fixed, false if not + */ + Node.prototype.isFixed = function() { + return (this.xFixed && this.yFixed); + }; - bp1 = { x: ((-d2pow2A * p0.x + A*p1.x + d1pow2A * p2.x) * N), - y: ((-d2pow2A * p0.y + A*p1.y + d1pow2A * p2.y) * N)}; + /** + * Check if this node is moving + * @param {number} vmin the minimum velocity considered as "moving" + * @return {boolean} true if moving, false if it has no velocity + */ + Node.prototype.isMoving = function(vmin) { + var velocity = Math.sqrt(Math.pow(this.vx,2) + Math.pow(this.vy,2)); + // this.velocity = Math.sqrt(Math.pow(this.vx,2) + Math.pow(this.vy,2)) + return (velocity > vmin); + }; - bp2 = { x: (( d3pow2A * p1.x + B*p2.x - d2pow2A * p3.x) * M), - y: (( d3pow2A * p1.y + B*p2.y - d2pow2A * p3.y) * M)}; + /** + * check if this node is selecte + * @return {boolean} selected True if node is selected, else false + */ + Node.prototype.isSelected = function() { + return this.selected; + }; - if (bp1.x == 0 && bp1.y == 0) {bp1 = p1;} - if (bp2.x == 0 && bp2.y == 0) {bp2 = p2;} - d += 'C' + - bp1.x + ',' + - bp1.y + ' ' + - bp2.x + ',' + - bp2.y + ' ' + - p2.x + ',' + - p2.y + ' '; - } + /** + * Retrieve the value of the node. Can be undefined + * @return {Number} value + */ + Node.prototype.getValue = function() { + return this.value; + }; - return d; - } + /** + * Calculate the distance from the nodes location to the given location (x,y) + * @param {Number} x + * @param {Number} y + * @return {Number} value + */ + Node.prototype.getDistance = function(x, y) { + var dx = this.x - x, + dy = this.y - y; + return Math.sqrt(dx * dx + dy * dy); }; + /** - * this generates the SVG path for a linear drawing between datapoints. - * @param data - * @returns {string} - * @private + * Adjust the value range of the node. The node will adjust it's radius + * based on its value. + * @param {Number} min + * @param {Number} max */ - Line._linear = function(data) { - // linear - var d = ''; - for (var i = 0; i < data.length; i++) { - if (i == 0) { - d += data[i].x + ',' + data[i].y; + Node.prototype.setValueRange = function(min, max) { + if (!this.radiusFixed && this.value !== undefined) { + if (max == min) { + this.options.radius= (this.options.radiusMin + this.options.radiusMax) / 2; } else { - d += ' ' + data[i].x + ',' + data[i].y; + var scale = (this.options.radiusMax - this.options.radiusMin) / (max - min); + this.options.radius= (this.value - min) * scale + this.options.radiusMin; } } - return d; + this.baseRadiusValue = this.options.radius; }; - module.exports = Line; - - -/***/ }, -/* 48 */ -/***/ function(module, exports, __webpack_require__) { - /** - * Created by Alex on 11/11/2014. + * Draw this node in the given canvas + * The 2d context of a HTML canvas can be retrieved by canvas.getContext("2d"); + * @param {CanvasRenderingContext2D} ctx */ - var DOMutil = __webpack_require__(6); - - function Points(groupId, options) { - this.groupId = groupId; - this.options = options; - } - - - Points.prototype.getYRange = function(groupData) { - var yMin = groupData[0].y; - var yMax = groupData[0].y; - for (var j = 0; j < groupData.length; j++) { - yMin = yMin > groupData[j].y ? groupData[j].y : yMin; - yMax = yMax < groupData[j].y ? groupData[j].y : yMax; - } - return {min: yMin, max: yMax, yAxisOrientation: this.options.yAxisOrientation}; + Node.prototype.draw = function(ctx) { + throw "Draw method not initialized for node"; }; - Points.prototype.draw = function(dataset, group, framework, offset) { - Points.draw(dataset, group, framework, offset); - } - /** - * draw the data points - * - * @param {Array} dataset - * @param {Object} JSONcontainer - * @param {Object} svg | SVG DOM element - * @param {GraphGroup} group - * @param {Number} [offset] + * 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 */ - Points.draw = function (dataset, group, framework, offset) { - if (offset === undefined) {offset = 0;} - for (var i = 0; i < dataset.length; i++) { - DOMutil.drawPoint(dataset[i].x + offset, dataset[i].y, group, framework.svgElements, framework.svg); - } + Node.prototype.resize = function(ctx) { + throw "Resize method not initialized for node"; }; - - module.exports = Points; - -/***/ }, -/* 49 */ -/***/ function(module, exports, __webpack_require__) { - /** - * Created by Alex on 11/11/2014. + * 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 */ - var DOMutil = __webpack_require__(6); - var Points = __webpack_require__(48); - - function Bargraph(groupId, options) { - this.groupId = groupId; - this.options = options; - } - - Bargraph.prototype.getYRange = function(groupData) { - if (this.options.barChart.handleOverlap != 'stack') { - var yMin = groupData[0].y; - var yMax = groupData[0].y; - for (var j = 0; j < groupData.length; j++) { - yMin = yMin > groupData[j].y ? groupData[j].y : yMin; - yMax = yMax < groupData[j].y ? groupData[j].y : yMax; - } - return {min: yMin, max: yMax, yAxisOrientation: this.options.yAxisOrientation}; - } - else { - var barCombinedData = []; - for (var j = 0; j < groupData.length; j++) { - barCombinedData.push({ - x: groupData[j].x, - y: groupData[j].y, - groupId: this.groupId - }); - } - return barCombinedData; - } + Node.prototype.isOverlappingWith = function(obj) { + return (this.left < obj.right && + this.left + this.width > obj.left && + this.top < obj.bottom && + this.top + this.height > obj.top); }; + Node.prototype._resizeImage = function (ctx) { + // TODO: pre calculate the image size - - /** - * draw a bar graph - * - * @param groupIds - * @param processedGroupData - */ - Bargraph.draw = function (groupIds, processedGroupData, framework) { - var combinedData = []; - var intersections = {}; - var coreDistance; - var key, drawData; - var group; - var i,j; - var barPoints = 0; - - // combine all barchart data - for (i = 0; i < groupIds.length; i++) { - group = framework.groups[groupIds[i]]; - if (group.options.style == 'bar') { - if (group.visible == true && (framework.options.groups.visibility[groupIds[i]] === undefined || framework.options.groups.visibility[groupIds[i]] == true)) { - for (j = 0; j < processedGroupData[groupIds[i]].length; j++) { - combinedData.push({ - x: processedGroupData[groupIds[i]][j].x, - y: processedGroupData[groupIds[i]][j].y, - groupId: groupIds[i] - }); - barPoints += 1; - } + if (!this.width || !this.height) { // undefined or 0 + var width, height; + if (this.value) { + this.options.radius= this.baseRadiusValue; + var scale = this.imageObj.height / this.imageObj.width; + if (scale !== undefined) { + width = this.options.radius|| this.imageObj.width; + height = this.options.radius* scale || this.imageObj.height; + } + else { + width = 0; + height = 0; } } - } - - if (barPoints == 0) {return;} - - // sort by time and by group - combinedData.sort(function (a, b) { - if (a.x == b.x) { - return a.groupId - b.groupId; - } else { - return a.x - b.x; + else { + width = this.imageObj.width; + height = this.imageObj.height; } - }); + this.width = width; + this.height = height; - // get intersections - Bargraph._getDataIntersections(intersections, combinedData); + this.growthIndicator = 0; + if (this.width > 0 && this.height > 0) { + this.width += Math.min(this.clusterSize - 1, this.maxNodeSizeIncrements) * this.clusterSizeWidthFactor; + this.height += Math.min(this.clusterSize - 1, this.maxNodeSizeIncrements) * this.clusterSizeHeightFactor; + this.options.radius+= Math.min(this.clusterSize - 1, this.maxNodeSizeIncrements) * this.clusterSizeRadiusFactor; + this.growthIndicator = this.width - width; + } + } + }; - // plot barchart - for (i = 0; i < combinedData.length; i++) { - group = framework.groups[combinedData[i].groupId]; - var minWidth = 0.1 * group.options.barChart.width; + Node.prototype._drawImageAtPosition = function (ctx) { + if (this.imageObj.width != 0 ) { + // draw the shade + if (this.clusterSize > 1) { + var lineWidth = ((this.clusterSize > 1) ? 10 : 0.0); + lineWidth *= this.networkScaleInv; + lineWidth = Math.min(0.2 * this.width,lineWidth); - key = combinedData[i].x; - var heightOffset = 0; - if (intersections[key] === undefined) { - if (i+1 < combinedData.length) {coreDistance = Math.abs(combinedData[i+1].x - key);} - if (i > 0) {coreDistance = Math.min(coreDistance,Math.abs(combinedData[i-1].x - key));} - drawData = Bargraph._getSafeDrawData(coreDistance, group, minWidth); + ctx.globalAlpha = 0.5; + ctx.drawImage(this.imageObj, this.left - lineWidth, this.top - lineWidth, this.width + 2*lineWidth, this.height + 2*lineWidth); } - else { - var nextKey = i + (intersections[key].amount - intersections[key].resolved); - var prevKey = i - (intersections[key].resolved + 1); - if (nextKey < combinedData.length) {coreDistance = Math.abs(combinedData[nextKey].x - key);} - if (prevKey > 0) {coreDistance = Math.min(coreDistance,Math.abs(combinedData[prevKey].x - key));} - drawData = Bargraph._getSafeDrawData(coreDistance, group, minWidth); - intersections[key].resolved += 1; - if (group.options.barChart.handleOverlap == 'stack') { - heightOffset = intersections[key].accumulated; - intersections[key].accumulated += group.zeroPosition - combinedData[i].y; - } - else if (group.options.barChart.handleOverlap == 'sideBySide') { - drawData.width = drawData.width / intersections[key].amount; - drawData.offset += (intersections[key].resolved) * drawData.width - (0.5*drawData.width * (intersections[key].amount+1)); - if (group.options.barChart.align == 'left') {drawData.offset -= 0.5*drawData.width;} - else if (group.options.barChart.align == 'right') {drawData.offset += 0.5*drawData.width;} - } - } - DOMutil.drawBar(combinedData[i].x + drawData.offset, combinedData[i].y - heightOffset, drawData.width, group.zeroPosition - combinedData[i].y, group.className + ' bar', framework.svgElements, framework.svg); - // draw points - if (group.options.drawPoints.enabled == true) { - DOMutil.drawPoint(combinedData[i].x + drawData.offset, combinedData[i].y, group, framework.svgElements, framework.svg); - } + // draw the image + ctx.globalAlpha = 1.0; + ctx.drawImage(this.imageObj, this.left, this.top, this.width, this.height); } }; - - /** - * Fill the intersections object with counters of how many datapoints share the same x coordinates - * @param intersections - * @param combinedData - * @private - */ - Bargraph._getDataIntersections = function (intersections, combinedData) { - // get intersections - var coreDistance; - for (var i = 0; i < combinedData.length; i++) { - if (i + 1 < combinedData.length) { - coreDistance = Math.abs(combinedData[i + 1].x - combinedData[i].x); - } - if (i > 0) { - coreDistance = Math.min(coreDistance, Math.abs(combinedData[i - 1].x - combinedData[i].x)); - } - if (coreDistance == 0) { - if (intersections[combinedData[i].x] === undefined) { - intersections[combinedData[i].x] = {amount: 0, resolved: 0, accumulated: 0}; - } - intersections[combinedData[i].x].amount += 1; + Node.prototype._drawImageLabel = function (ctx) { + var yLabel; + var offset = 0; + + if (this.height){ + offset = this.height / 2; + var labelDimensions = this.getTextSize(ctx); + + if (labelDimensions.lineCount >= 1){ + offset += labelDimensions.height / 2; + offset += 3; } } + + yLabel = this.y + offset; + + this._label(ctx, this.label, this.x, yLabel, undefined); }; + Node.prototype._drawImage = function (ctx) { + this._resizeImage(ctx); + this.left = this.x - this.width / 2; + this.top = this.y - this.height / 2; - /** - * Get the width and offset for bargraphs based on the coredistance between datapoints - * - * @param coreDistance - * @param group - * @param minWidth - * @returns {{width: Number, offset: Number}} - * @private - */ - Bargraph._getSafeDrawData = function (coreDistance, group, minWidth) { - var width, offset; - if (coreDistance < group.options.barChart.width && coreDistance > 0) { - width = coreDistance < minWidth ? minWidth : coreDistance; + this._drawImageAtPosition(ctx); - offset = 0; // recalculate offset with the new width; - if (group.options.barChart.align == 'left') { - offset -= 0.5 * coreDistance; - } - else if (group.options.barChart.align == 'right') { - offset += 0.5 * coreDistance; - } - } - else { - // default settings - width = group.options.barChart.width; - offset = 0; - if (group.options.barChart.align == 'left') { - offset -= 0.5 * group.options.barChart.width; - } - else if (group.options.barChart.align == 'right') { - offset += 0.5 * group.options.barChart.width; - } - } + this.boundingBox.top = this.top; + this.boundingBox.left = this.left; + this.boundingBox.right = this.left + this.width; + this.boundingBox.bottom = this.top + this.height; - return {width: width, offset: offset}; + this._drawImageLabel(ctx); + this.boundingBox.left = Math.min(this.boundingBox.left, this.labelDimensions.left); + this.boundingBox.right = Math.max(this.boundingBox.right, this.labelDimensions.left + this.labelDimensions.width); + this.boundingBox.bottom = Math.max(this.boundingBox.bottom, this.boundingBox.bottom + this.labelDimensions.height); }; - Bargraph.getStackedBarYRange = function(barCombinedData, groupRanges, groupIds, groupLabel, orientation) { - if (barCombinedData.length > 0) { - // sort by time and by group - barCombinedData.sort(function (a, b) { - if (a.x == b.x) { - return a.groupId - b.groupId; - } else { - return a.x - b.x; - } - }); - var intersections = {}; - - Bargraph._getDataIntersections(intersections, barCombinedData); - groupRanges[groupLabel] = Bargraph._getStackedBarYRange(intersections, barCombinedData); - groupRanges[groupLabel].yAxisOrientation = orientation; - groupIds.push(groupLabel); - } - } + Node.prototype._resizeCircularImage = function (ctx) { + if(!this.imageObj.src || !this.imageObj.width || !this.imageObj.height){ + if (!this.width) { + var diameter = this.options.radius * 2; + this.width = diameter; + this.height = diameter; - Bargraph._getStackedBarYRange = function (intersections, combinedData) { - var key; - var yMin = combinedData[0].y; - var yMax = combinedData[0].y; - for (var i = 0; i < combinedData.length; i++) { - key = combinedData[i].x; - if (intersections[key] === undefined) { - yMin = yMin > combinedData[i].y ? combinedData[i].y : yMin; - yMax = yMax < combinedData[i].y ? combinedData[i].y : yMax; - } - else { - intersections[key].accumulated += combinedData[i].y; + // scaling used for clustering + //this.width += Math.min(this.clusterSize - 1, this.maxNodeSizeIncrements) * 0.5 * this.clusterSizeWidthFactor; + //this.height += Math.min(this.clusterSize - 1, this.maxNodeSizeIncrements) * 0.5 * this.clusterSizeHeightFactor; + this.options.radius += Math.min(this.clusterSize - 1, this.maxNodeSizeIncrements) * 0.5 * this.clusterSizeRadiusFactor; + this.growthIndicator = this.options.radius- 0.5*diameter; + this._swapToImageResizeWhenImageLoaded = true; } } - for (var xpos in intersections) { - if (intersections.hasOwnProperty(xpos)) { - yMin = yMin > intersections[xpos].accumulated ? intersections[xpos].accumulated : yMin; - yMax = yMax < intersections[xpos].accumulated ? intersections[xpos].accumulated : yMax; + else { + if (this._swapToImageResizeWhenImageLoaded) { + this.width = 0; + this.height = 0; + delete this._swapToImageResizeWhenImageLoaded; } + this._resizeImage(ctx); } - return {min: yMin, max: yMax}; }; - module.exports = Bargraph; + Node.prototype._drawCircularImage = function (ctx) { + this._resizeCircularImage(ctx); -/***/ }, -/* 50 */ -/***/ function(module, exports, __webpack_require__) { + this.left = this.x - this.width / 2; + this.top = this.y - this.height / 2; + + var centerX = this.left + (this.width / 2); + var centerY = this.top + (this.height / 2); + var radius = Math.abs(this.height / 2); - var util = __webpack_require__(1); - var DOMutil = __webpack_require__(6); - var Component = __webpack_require__(23); + this._drawRawCircle(ctx, centerX, centerY, radius); - /** - * Legend for Graph2d - */ - function Legend(body, options, side, linegraphOptions) { - this.body = body; - this.defaultOptions = { - enabled: true, - icons: true, - iconSize: 20, - iconSpacing: 6, - left: { - visible: true, - position: 'top-left' // top/bottom - left,center,right - }, - right: { - visible: true, - position: 'top-left' // top/bottom - left,center,right - } - } - this.side = side; - this.options = util.extend({},this.defaultOptions); - this.linegraphOptions = linegraphOptions; + ctx.save(); + ctx.circle(this.x, this.y, radius); + ctx.stroke(); + ctx.clip(); - this.svgElements = {}; - this.dom = {}; - this.groups = {}; - this.amountOfGroups = 0; - this._create(); + this._drawImageAtPosition(ctx); - this.setOptions(options); - } + ctx.restore(); - Legend.prototype = new Component(); + this.boundingBox.top = this.y - this.options.radius; + this.boundingBox.left = this.x - this.options.radius; + this.boundingBox.right = this.x + this.options.radius; + this.boundingBox.bottom = this.y + this.options.radius; - Legend.prototype.clear = function() { - this.groups = {}; - this.amountOfGroups = 0; - } + this._drawImageLabel(ctx); + + this.boundingBox.left = Math.min(this.boundingBox.left, this.labelDimensions.left); + this.boundingBox.right = Math.max(this.boundingBox.right, this.labelDimensions.left + this.labelDimensions.width); + this.boundingBox.bottom = Math.max(this.boundingBox.bottom, this.boundingBox.bottom + this.labelDimensions.height); + }; - Legend.prototype.addGroup = function(label, graphOptions) { + Node.prototype._resizeBox = function (ctx) { + if (!this.width) { + var margin = 5; + var textSize = this.getTextSize(ctx); + this.width = textSize.width + 2 * margin; + this.height = textSize.height + 2 * margin; + + this.width += Math.min(this.clusterSize - 1, this.maxNodeSizeIncrements) * 0.5 * this.clusterSizeWidthFactor; + this.height += Math.min(this.clusterSize - 1, this.maxNodeSizeIncrements) * 0.5 * this.clusterSizeHeightFactor; + this.growthIndicator = this.width - (textSize.width + 2 * margin); + // this.options.radius+= Math.min(this.clusterSize - 1, this.maxNodeSizeIncrements) * 0.5 * this.clusterSizeRadiusFactor; - if (!this.groups.hasOwnProperty(label)) { - this.groups[label] = graphOptions; } - this.amountOfGroups += 1; }; - Legend.prototype.updateGroup = function(label, graphOptions) { - this.groups[label] = graphOptions; - }; + Node.prototype._drawBox = function (ctx) { + this._resizeBox(ctx); - Legend.prototype.removeGroup = function(label) { - if (this.groups.hasOwnProperty(label)) { - delete this.groups[label]; - this.amountOfGroups -= 1; + this.left = this.x - this.width / 2; + this.top = this.y - this.height / 2; + + var clusterLineWidth = 2.5; + var borderWidth = this.options.borderWidth; + var selectionLineWidth = this.options.borderWidthSelected || 2 * this.options.borderWidth; + + ctx.strokeStyle = this.selected ? this.options.color.highlight.border : this.hover ? this.options.color.hover.border : this.options.color.border; + + // draw the outer border + if (this.clusterSize > 1) { + ctx.lineWidth = (this.selected ? selectionLineWidth : borderWidth) + ((this.clusterSize > 1) ? clusterLineWidth : 0.0); + ctx.lineWidth *= this.networkScaleInv; + ctx.lineWidth = Math.min(this.width,ctx.lineWidth); + + ctx.roundRect(this.left-2*ctx.lineWidth, this.top-2*ctx.lineWidth, this.width+4*ctx.lineWidth, this.height+4*ctx.lineWidth, this.options.radius); + ctx.stroke(); } - }; + ctx.lineWidth = (this.selected ? selectionLineWidth : borderWidth) + ((this.clusterSize > 1) ? clusterLineWidth : 0.0); + ctx.lineWidth *= this.networkScaleInv; + ctx.lineWidth = Math.min(this.width,ctx.lineWidth); - Legend.prototype._create = function() { - this.dom.frame = document.createElement('div'); - this.dom.frame.className = 'legend'; - this.dom.frame.style.position = "absolute"; - this.dom.frame.style.top = "10px"; - this.dom.frame.style.display = "block"; + ctx.fillStyle = this.selected ? this.options.color.highlight.background : this.hover ? this.options.color.hover.background : this.options.color.background; - this.dom.textArea = document.createElement('div'); - this.dom.textArea.className = 'legendText'; - this.dom.textArea.style.position = "relative"; - this.dom.textArea.style.top = "0px"; + ctx.roundRect(this.left, this.top, this.width, this.height, this.options.radius); + ctx.fill(); + ctx.stroke(); - this.svg = document.createElementNS('http://www.w3.org/2000/svg',"svg"); - this.svg.style.position = 'absolute'; - this.svg.style.top = 0 +'px'; - this.svg.style.width = this.options.iconSize + 5 + 'px'; - this.svg.style.height = '100%'; + this.boundingBox.top = this.top; + this.boundingBox.left = this.left; + this.boundingBox.right = this.left + this.width; + this.boundingBox.bottom = this.top + this.height; - this.dom.frame.appendChild(this.svg); - this.dom.frame.appendChild(this.dom.textArea); + this._label(ctx, this.label, this.x, this.y); }; - /** - * Hide the component from the DOM - */ - Legend.prototype.hide = function() { - // remove the frame containing the items - if (this.dom.frame.parentNode) { - this.dom.frame.parentNode.removeChild(this.dom.frame); + + Node.prototype._resizeDatabase = function (ctx) { + if (!this.width) { + var margin = 5; + var textSize = this.getTextSize(ctx); + var size = textSize.width + 2 * margin; + this.width = size; + this.height = size; + + // scaling used for clustering + this.width += Math.min(this.clusterSize - 1, this.maxNodeSizeIncrements) * this.clusterSizeWidthFactor; + this.height += Math.min(this.clusterSize - 1, this.maxNodeSizeIncrements) * this.clusterSizeHeightFactor; + this.options.radius+= Math.min(this.clusterSize - 1, this.maxNodeSizeIncrements) * this.clusterSizeRadiusFactor; + this.growthIndicator = this.width - size; } }; - /** - * Show the component in the DOM (when not already visible). - * @return {Boolean} changed - */ - Legend.prototype.show = function() { - // show frame containing the items - if (!this.dom.frame.parentNode) { - this.body.dom.center.appendChild(this.dom.frame); + Node.prototype._drawDatabase = function (ctx) { + this._resizeDatabase(ctx); + this.left = this.x - this.width / 2; + this.top = this.y - this.height / 2; + + var clusterLineWidth = 2.5; + var borderWidth = this.options.borderWidth; + var selectionLineWidth = this.options.borderWidthSelected || 2 * this.options.borderWidth; + + ctx.strokeStyle = this.selected ? this.options.color.highlight.border : this.hover ? this.options.color.hover.border : this.options.color.border; + + // draw the outer border + if (this.clusterSize > 1) { + ctx.lineWidth = (this.selected ? selectionLineWidth : borderWidth) + ((this.clusterSize > 1) ? clusterLineWidth : 0.0); + ctx.lineWidth *= this.networkScaleInv; + ctx.lineWidth = Math.min(this.width,ctx.lineWidth); + + ctx.database(this.x - this.width/2 - 2*ctx.lineWidth, this.y - this.height*0.5 - 2*ctx.lineWidth, this.width + 4*ctx.lineWidth, this.height + 4*ctx.lineWidth); + ctx.stroke(); } - }; + ctx.lineWidth = (this.selected ? selectionLineWidth : borderWidth) + ((this.clusterSize > 1) ? clusterLineWidth : 0.0); + ctx.lineWidth *= this.networkScaleInv; + ctx.lineWidth = Math.min(this.width,ctx.lineWidth); - Legend.prototype.setOptions = function(options) { - var fields = ['enabled','orientation','icons','left','right']; - util.selectiveDeepExtend(fields, this.options, options); + ctx.fillStyle = this.selected ? this.options.color.highlight.background : this.hover ? this.options.color.hover.background : this.options.color.background; + ctx.database(this.x - this.width/2, this.y - this.height*0.5, this.width, this.height); + ctx.fill(); + ctx.stroke(); + + this.boundingBox.top = this.top; + this.boundingBox.left = this.left; + this.boundingBox.right = this.left + this.width; + this.boundingBox.bottom = this.top + this.height; + + this._label(ctx, this.label, this.x, this.y); }; - Legend.prototype.redraw = function() { - var activeGroups = 0; - for (var groupId in this.groups) { - if (this.groups.hasOwnProperty(groupId)) { - if (this.groups[groupId].visible == true && (this.linegraphOptions.visibility[groupId] === undefined || this.linegraphOptions.visibility[groupId] == true)) { - activeGroups++; - } - } - } - if (this.options[this.side].visible == false || this.amountOfGroups == 0 || this.options.enabled == false || activeGroups == 0) { - this.hide(); + Node.prototype._resizeCircle = function (ctx) { + if (!this.width) { + var margin = 5; + var textSize = this.getTextSize(ctx); + var diameter = Math.max(textSize.width, textSize.height) + 2 * margin; + this.options.radius = diameter / 2; + + this.width = diameter; + this.height = diameter; + + // scaling used for clustering + // this.width += Math.min(this.clusterSize - 1, this.maxNodeSizeIncrements) * 0.5 * this.clusterSizeWidthFactor; + // this.height += Math.min(this.clusterSize - 1, this.maxNodeSizeIncrements) * 0.5 * this.clusterSizeHeightFactor; + this.options.radius += Math.min(this.clusterSize - 1, this.maxNodeSizeIncrements) * 0.5 * this.clusterSizeRadiusFactor; + this.growthIndicator = this.options.radius- 0.5*diameter; } - else { - this.show(); - if (this.options[this.side].position == 'top-left' || this.options[this.side].position == 'bottom-left') { - this.dom.frame.style.left = '4px'; - this.dom.frame.style.textAlign = "left"; - this.dom.textArea.style.textAlign = "left"; - this.dom.textArea.style.left = (this.options.iconSize + 15) + 'px'; - this.dom.textArea.style.right = ''; - this.svg.style.left = 0 +'px'; - this.svg.style.right = ''; - } - else { - this.dom.frame.style.right = '4px'; - this.dom.frame.style.textAlign = "right"; - this.dom.textArea.style.textAlign = "right"; - this.dom.textArea.style.right = (this.options.iconSize + 15) + 'px'; - this.dom.textArea.style.left = ''; - this.svg.style.right = 0 +'px'; - this.svg.style.left = ''; - } + }; - if (this.options[this.side].position == 'top-left' || this.options[this.side].position == 'top-right') { - this.dom.frame.style.top = 4 - Number(this.body.dom.center.style.top.replace("px","")) + 'px'; - this.dom.frame.style.bottom = ''; - } - else { - var scrollableHeight = this.body.domProps.center.height - this.body.domProps.centerContainer.height; - this.dom.frame.style.bottom = 4 + scrollableHeight + Number(this.body.dom.center.style.top.replace("px","")) + 'px'; - this.dom.frame.style.top = ''; - } + Node.prototype._drawRawCircle = function (ctx, x, y, radius) { + var clusterLineWidth = 2.5; + var borderWidth = this.options.borderWidth; + var selectionLineWidth = this.options.borderWidthSelected || 2 * this.options.borderWidth; + + ctx.strokeStyle = this.selected ? this.options.color.highlight.border : this.hover ? this.options.color.hover.border : this.options.color.border; - if (this.options.icons == false) { - this.dom.frame.style.width = this.dom.textArea.offsetWidth + 10 + 'px'; - this.dom.textArea.style.right = ''; - this.dom.textArea.style.left = ''; - this.svg.style.width = '0px'; - } - else { - this.dom.frame.style.width = this.options.iconSize + 15 + this.dom.textArea.offsetWidth + 10 + 'px' - this.drawLegendIcons(); - } + // draw the outer border + if (this.clusterSize > 1) { + ctx.lineWidth = (this.selected ? selectionLineWidth : borderWidth) + ((this.clusterSize > 1) ? clusterLineWidth : 0.0); + ctx.lineWidth *= this.networkScaleInv; + ctx.lineWidth = Math.min(this.width,ctx.lineWidth); - var content = ''; - for (var groupId in this.groups) { - if (this.groups.hasOwnProperty(groupId)) { - if (this.groups[groupId].visible == true && (this.linegraphOptions.visibility[groupId] === undefined || this.linegraphOptions.visibility[groupId] == true)) { - content += this.groups[groupId].content + '
'; - } - } - } - this.dom.textArea.innerHTML = content; - this.dom.textArea.style.lineHeight = ((0.75 * this.options.iconSize) + this.options.iconSpacing) + 'px'; + ctx.circle(x, y, radius+2*ctx.lineWidth); + ctx.stroke(); } + ctx.lineWidth = (this.selected ? selectionLineWidth : borderWidth) + ((this.clusterSize > 1) ? clusterLineWidth : 0.0); + ctx.lineWidth *= this.networkScaleInv; + ctx.lineWidth = Math.min(this.width,ctx.lineWidth); + + ctx.fillStyle = this.selected ? this.options.color.highlight.background : this.hover ? this.options.color.hover.background : this.options.color.background; + ctx.circle(this.x, this.y, radius); + ctx.fill(); + ctx.stroke(); }; - Legend.prototype.drawLegendIcons = function() { - if (this.dom.frame.parentNode) { - DOMutil.prepareElements(this.svgElements); - var padding = window.getComputedStyle(this.dom.frame).paddingTop; - var iconOffset = Number(padding.replace('px','')); - var x = iconOffset; - var iconWidth = this.options.iconSize; - var iconHeight = 0.75 * this.options.iconSize; - var y = iconOffset + 0.5 * iconHeight + 3; + Node.prototype._drawCircle = function (ctx) { + this._resizeCircle(ctx); + this.left = this.x - this.width / 2; + this.top = this.y - this.height / 2; - this.svg.style.width = iconWidth + 5 + iconOffset + 'px'; + this._drawRawCircle(ctx, this.x, this.y, this.options.radius); - for (var groupId in this.groups) { - if (this.groups.hasOwnProperty(groupId)) { - if (this.groups[groupId].visible == true && (this.linegraphOptions.visibility[groupId] === undefined || this.linegraphOptions.visibility[groupId] == true)) { - this.groups[groupId].drawIcon(x, y, this.svgElements, this.svg, iconWidth, iconHeight); - y += iconHeight + this.options.iconSpacing; - } - } + this.boundingBox.top = this.y - this.options.radius; + this.boundingBox.left = this.x - this.options.radius; + this.boundingBox.right = this.x + this.options.radius; + this.boundingBox.bottom = this.y + this.options.radius; + + this._label(ctx, this.label, this.x, this.y); + }; + + Node.prototype._resizeEllipse = function (ctx) { + if (!this.width) { + var textSize = this.getTextSize(ctx); + + this.width = textSize.width * 1.5; + this.height = textSize.height * 2; + if (this.width < this.height) { + this.width = this.height; } + var defaultSize = this.width; - DOMutil.cleanupElements(this.svgElements); + // scaling used for clustering + this.width += Math.min(this.clusterSize - 1, this.maxNodeSizeIncrements) * this.clusterSizeWidthFactor; + this.height += Math.min(this.clusterSize - 1, this.maxNodeSizeIncrements) * this.clusterSizeHeightFactor; + this.options.radius += Math.min(this.clusterSize - 1, this.maxNodeSizeIncrements) * this.clusterSizeRadiusFactor; + this.growthIndicator = this.width - defaultSize; } }; - module.exports = Legend; - + Node.prototype._drawEllipse = function (ctx) { + this._resizeEllipse(ctx); + this.left = this.x - this.width / 2; + this.top = this.y - this.height / 2; -/***/ }, -/* 51 */ -/***/ function(module, exports, __webpack_require__) { + var clusterLineWidth = 2.5; + var borderWidth = this.options.borderWidth; + var selectionLineWidth = this.options.borderWidthSelected || 2 * this.options.borderWidth; - var Emitter = __webpack_require__(11); - var Hammer = __webpack_require__(19); - var keycharm = __webpack_require__(36); - var util = __webpack_require__(1); - var hammerUtil = __webpack_require__(22); - var DataSet = __webpack_require__(7); - var DataView = __webpack_require__(9); - var dotparser = __webpack_require__(52); - var gephiParser = __webpack_require__(53); - var Groups = __webpack_require__(54); - var Images = __webpack_require__(55); - var Node = __webpack_require__(56); - var Edge = __webpack_require__(57); - var Popup = __webpack_require__(58); - var MixinLoader = __webpack_require__(59); - var Activator = __webpack_require__(35); - var locales = __webpack_require__(70); + ctx.strokeStyle = this.selected ? this.options.color.highlight.border : this.hover ? this.options.color.hover.border : this.options.color.border; - // Load custom shapes into CanvasRenderingContext2D - __webpack_require__(71); + // draw the outer border + if (this.clusterSize > 1) { + ctx.lineWidth = (this.selected ? selectionLineWidth : borderWidth) + ((this.clusterSize > 1) ? clusterLineWidth : 0.0); + ctx.lineWidth *= this.networkScaleInv; + ctx.lineWidth = Math.min(this.width,ctx.lineWidth); - /** - * @constructor Network - * Create a network visualization, displaying nodes and edges. - * - * @param {Element} container The DOM element in which the Network will - * be created. Normally a div element. - * @param {Object} data An object containing parameters - * {Array} nodes - * {Array} edges - * @param {Object} options Options - */ - function Network (container, data, options) { - if (!(this instanceof Network)) { - throw new SyntaxError('Constructor must be called with the new operator'); + ctx.ellipse(this.left-2*ctx.lineWidth, this.top-2*ctx.lineWidth, this.width+4*ctx.lineWidth, this.height+4*ctx.lineWidth); + ctx.stroke(); } + ctx.lineWidth = (this.selected ? selectionLineWidth : borderWidth) + ((this.clusterSize > 1) ? clusterLineWidth : 0.0); + ctx.lineWidth *= this.networkScaleInv; + ctx.lineWidth = Math.min(this.width,ctx.lineWidth); - this._determineBrowserMethod(); - this._initializeMixinLoaders(); + ctx.fillStyle = this.selected ? this.options.color.highlight.background : this.hover ? this.options.color.hover.background : this.options.color.background; - // create variables and set default values - this.containerElement = container; + ctx.ellipse(this.left, this.top, this.width, this.height); + ctx.fill(); + ctx.stroke(); - // render and calculation settings - this.renderRefreshRate = 60; // hz (fps) - this.renderTimestep = 1000 / this.renderRefreshRate; // ms -- saves calculation later on - this.renderTime = 0; // measured time it takes to render a frame - this.physicsTime = 0; // measured time it takes to render a frame - this.runDoubleSpeed = false; - this.physicsDiscreteStepsize = 0.50; // discrete stepsize of the simulation + this.boundingBox.top = this.top; + this.boundingBox.left = this.left; + this.boundingBox.right = this.left + this.width; + this.boundingBox.bottom = this.top + this.height; - this.initializing = true; + this._label(ctx, this.label, this.x, this.y); + }; - this.triggerFunctions = {add:null,edit:null,editEdge:null,connect:null,del:null}; + Node.prototype._drawDot = function (ctx) { + this._drawShape(ctx, 'circle'); + }; - // set constant values - this.defaultOptions = { - nodes: { - mass: 1, - radiusMin: 10, - radiusMax: 30, - radius: 10, - shape: 'ellipse', - image: undefined, - widthMin: 16, // px - widthMax: 64, // px - fontColor: 'black', - fontSize: 14, // px - fontFace: 'verdana', - fontFill: undefined, - fontStrokeWidth: 0, // px - fontStrokeColor: 'white', - level: -1, - color: { - border: '#2B7CE9', - background: '#97C2FC', - highlight: { - border: '#2B7CE9', - background: '#D2E5FF' - }, - hover: { - border: '#2B7CE9', - background: '#D2E5FF' - } - }, - group: undefined, - borderWidth: 1, - borderWidthSelected: undefined - }, - edges: { - widthMin: 1, // - widthMax: 15,// - width: 1, - widthSelectionMultiplier: 2, - hoverWidth: 1.5, - style: 'line', - color: { - color:'#848484', - highlight:'#848484', - hover: '#848484' - }, - fontColor: '#343434', - fontSize: 14, // px - fontFace: 'arial', - fontFill: 'white', - fontStrokeWidth: 0, // px - fontStrokeColor: 'white', - labelAlignment:'horizontal', - arrowScaleFactor: 1, - dash: { - length: 10, - gap: 5, - altLength: undefined - }, - inheritColor: "from" // to, from, false, true (== from) - }, - configurePhysics:false, - physics: { - barnesHut: { - enabled: true, - thetaInverted: 1 / 0.5, // inverted to save time during calculation - gravitationalConstant: -2000, - centralGravity: 0.3, - springLength: 95, - springConstant: 0.04, - damping: 0.09 - }, - repulsion: { - centralGravity: 0.0, - springLength: 200, - springConstant: 0.05, - nodeDistance: 100, - damping: 0.09 - }, - hierarchicalRepulsion: { - enabled: false, - centralGravity: 0.0, - springLength: 100, - springConstant: 0.01, - nodeDistance: 150, - damping: 0.09 - }, - damping: null, - centralGravity: null, - springLength: null, - springConstant: null - }, - clustering: { // Per Node in Cluster = PNiC - enabled: false, // (Boolean) | global on/off switch for clustering. - initialMaxNodes: 100, // (# nodes) | if the initial amount of nodes is larger than this, we cluster until the total number is less than this threshold. - clusterThreshold:500, // (# nodes) | during calculate forces, we check if the total number of nodes is larger than this. If it is, cluster until reduced to reduceToNodes - reduceToNodes:300, // (# nodes) | during calculate forces, we check if the total number of nodes is larger than clusterThreshold. If it is, cluster until reduced to this - chainThreshold: 0.4, // (% of all drawn nodes)| maximum percentage of allowed chainnodes (long strings of connected nodes) within all nodes. (lower means less chains). - clusterEdgeThreshold: 20, // (px) | edge length threshold. if smaller, this node is clustered. - sectorThreshold: 100, // (# nodes in cluster) | cluster size threshold. If larger, expanding in own sector. - screenSizeThreshold: 0.2, // (% of canvas) | relative size threshold. If the width or height of a clusternode takes up this much of the screen, decluster node. - fontSizeMultiplier: 4.0, // (px PNiC) | how much the cluster font size grows per node in cluster (in px). - maxFontSize: 1000, - forceAmplification: 0.1, // (multiplier PNiC) | factor of increase fo the repulsion force of a cluster (per node in cluster). - distanceAmplification: 0.1, // (multiplier PNiC) | factor how much the repulsion distance of a cluster increases (per node in cluster). - edgeGrowth: 20, // (px PNiC) | amount of clusterSize connected to the edge is multiplied with this and added to edgeLength. - nodeScaling: {width: 1, // (px PNiC) | growth of the width per node in cluster. - height: 1, // (px PNiC) | growth of the height per node in cluster. - radius: 1}, // (px PNiC) | growth of the radius per node in cluster. - maxNodeSizeIncrements: 600, // (# increments) | max growth of the width per node in cluster. - activeAreaBoxSize: 80, // (px) | box area around the curser where clusters are popped open. - clusterLevelDifference: 2, - clusterByZoom: true // enable clustering through zooming in and out - }, - navigation: { - enabled: false - }, - keyboard: { - enabled: false, - speed: {x: 10, y: 10, zoom: 0.02}, - bindToWindow: true - }, - dataManipulation: { - enabled: false, - initiallyVisible: false - }, - hierarchicalLayout: { - enabled:false, - levelSeparation: 150, - nodeSpacing: 100, - direction: "UD", // UD, DU, LR, RL - layout: "hubsize" // hubsize, directed - }, - freezeForStabilization: false, - smoothCurves: { - enabled: true, - dynamic: true, - type: "continuous", - roundness: 0.5 - }, - maxVelocity: 30, - minVelocity: 0.1, // px/s - stabilize: true, // stabilize before displaying the network - stabilizationIterations: 1000, // maximum number of iteration to stabilize - zoomExtentOnStabilize: true, - locale: 'en', - locales: locales, - tooltip: { - delay: 300, - fontColor: 'black', - fontSize: 14, // px - fontFace: 'verdana', - color: { - border: '#666', - background: '#FFFFC6' - } - }, - dragNetwork: true, - dragNodes: true, - zoomable: true, - hover: false, - hideEdgesOnDrag: false, - hideNodesOnDrag: false, - width : '100%', - height : '100%', - selectable: true - }; - this.constants = util.extend({}, this.defaultOptions); - this.pixelRatio = 1; - - - this.hoverObj = {nodes:{},edges:{}}; - this.controlNodesActive = false; - this.navigationHammers = {existing:[], _new: []}; + Node.prototype._drawTriangle = function (ctx) { + this._drawShape(ctx, 'triangle'); + }; - // animation properties - this.animationSpeed = 1/this.renderRefreshRate; - this.animationEasingFunction = "easeInOutQuint"; - this.animating = false; - this.easingTime = 0; - this.sourceScale = 0; - this.targetScale = 0; - this.sourceTranslation = 0; - this.targetTranslation = 0; - this.lockedOnNodeId = null; - this.lockedOnNodeOffset = null; - this.touchTime = 0; + Node.prototype._drawTriangleDown = function (ctx) { + this._drawShape(ctx, 'triangleDown'); + }; - // Node variables - var network = this; - this.groups = new Groups(); // object with groups - this.images = new Images(); // object with images - this.images.setOnloadCallback(function (status) { - network._redraw(); - }); + Node.prototype._drawSquare = function (ctx) { + this._drawShape(ctx, 'square'); + }; - // keyboard navigation variables - this.xIncrement = 0; - this.yIncrement = 0; - this.zoomIncrement = 0; + Node.prototype._drawStar = function (ctx) { + this._drawShape(ctx, 'star'); + }; - // loading all the mixins: - // load the force calculation functions, grouped under the physics system. - this._loadPhysicsSystem(); - // create a frame and canvas - this._create(); - // load the sector system. (mandatory, fully integrated with Network) - this._loadSectorSystem(); - // load the cluster system. (mandatory, even when not using the cluster system, there are function calls to it) - this._loadClusterSystem(); - // load the selection system. (mandatory, required by Network) - this._loadSelectionSystem(); - // load the selection system. (mandatory, required by Network) - this._loadHierarchySystem(); + Node.prototype._resizeShape = function (ctx) { + if (!this.width) { + this.options.radius= this.baseRadiusValue; + var size = 2 * this.options.radius; + this.width = size; + this.height = size; + // scaling used for clustering + this.width += Math.min(this.clusterSize - 1, this.maxNodeSizeIncrements) * this.clusterSizeWidthFactor; + this.height += Math.min(this.clusterSize - 1, this.maxNodeSizeIncrements) * this.clusterSizeHeightFactor; + this.options.radius+= Math.min(this.clusterSize - 1, this.maxNodeSizeIncrements) * 0.5 * this.clusterSizeRadiusFactor; + this.growthIndicator = this.width - size; + } + }; - // apply options - this._setTranslation(this.frame.clientWidth / 2, this.frame.clientHeight / 2); - this._setScale(1); - this.setOptions(options); + Node.prototype._drawShape = function (ctx, shape) { + this._resizeShape(ctx); - // other vars - this.freezeSimulation = false;// freeze the simulation - this.cachedFunctions = {}; - this.startedStabilization = false; - this.stabilized = false; - this.stabilizationIterations = null; - this.draggingNodes = false; + this.left = this.x - this.width / 2; + this.top = this.y - this.height / 2; - // containers for nodes and edges - this.calculationNodes = {}; - this.calculationNodeIndices = []; - this.nodeIndices = []; // array with all the indices of the nodes. Used to speed up forces calculation - this.nodes = {}; // object with Node objects - this.edges = {}; // object with Edge objects + var clusterLineWidth = 2.5; + var borderWidth = this.options.borderWidth; + var selectionLineWidth = this.options.borderWidthSelected || 2 * this.options.borderWidth; + var radiusMultiplier = 2; - // position and scale variables and objects - this.canvasTopLeft = {"x": 0,"y": 0}; // coordinates of the top left of the canvas. they will be set during _redraw. - this.canvasBottomRight = {"x": 0,"y": 0}; // coordinates of the bottom right of the canvas. they will be set during _redraw - this.pointerPosition = {"x": 0,"y": 0}; // coordinates of the bottom right of the canvas. they will be set during _redraw - this.areaCenter = {}; // object with x and y elements used for determining the center of the zoom action - this.scale = 1; // defining the global scale variable in the constructor - this.previousScale = this.scale; // this is used to check if the zoom operation is zooming in or out + // choose draw method depending on the shape + switch (shape) { + case 'dot': radiusMultiplier = 2; break; + case 'square': radiusMultiplier = 2; break; + case 'triangle': radiusMultiplier = 3; break; + case 'triangleDown': radiusMultiplier = 3; break; + case 'star': radiusMultiplier = 4; break; + } - // datasets or dataviews - this.nodesData = null; // A DataSet or DataView - this.edgesData = null; // A DataSet or DataView + ctx.strokeStyle = this.selected ? this.options.color.highlight.border : this.hover ? this.options.color.hover.border : this.options.color.border; + // draw the outer border + if (this.clusterSize > 1) { + ctx.lineWidth = (this.selected ? selectionLineWidth : borderWidth) + ((this.clusterSize > 1) ? clusterLineWidth : 0.0); + ctx.lineWidth *= this.networkScaleInv; + ctx.lineWidth = Math.min(this.width,ctx.lineWidth); - // create event listeners used to subscribe on the DataSets of the nodes and edges - this.nodesListeners = { - 'add': function (event, params) { - network._addNodes(params.items); - network.start(); - }, - 'update': function (event, params) { - network._updateNodes(params.items, params.data); - network.start(); - }, - 'remove': function (event, params) { - network._removeNodes(params.items); - network.start(); - } - }; - this.edgesListeners = { - 'add': function (event, params) { - network._addEdges(params.items); - network.start(); - }, - 'update': function (event, params) { - network._updateEdges(params.items); - network.start(); - }, - 'remove': function (event, params) { - network._removeEdges(params.items); - network.start(); - } - }; + ctx[shape](this.x, this.y, this.options.radius+ radiusMultiplier * ctx.lineWidth); + ctx.stroke(); + } + ctx.lineWidth = (this.selected ? selectionLineWidth : borderWidth) + ((this.clusterSize > 1) ? clusterLineWidth : 0.0); + ctx.lineWidth *= this.networkScaleInv; + ctx.lineWidth = Math.min(this.width,ctx.lineWidth); - // properties for the animation - this.moving = true; - this.timer = undefined; // Scheduling function. Is definded in this.start(); + ctx.fillStyle = this.selected ? this.options.color.highlight.background : this.hover ? this.options.color.hover.background : this.options.color.background; + ctx[shape](this.x, this.y, this.options.radius); + ctx.fill(); + ctx.stroke(); - // load data (the disable start variable will be the same as the enabled clustering) - this.setData(data,this.constants.clustering.enabled || this.constants.hierarchicalLayout.enabled); + this.boundingBox.top = this.y - this.options.radius; + this.boundingBox.left = this.x - this.options.radius; + this.boundingBox.right = this.x + this.options.radius; + this.boundingBox.bottom = this.y + this.options.radius; - // hierarchical layout - this.initializing = false; - if (this.constants.hierarchicalLayout.enabled == true) { - this._setupHierarchicalLayout(); + if (this.label) { + this._label(ctx, this.label, this.x, this.y + this.height / 2, undefined, 'hanging',true); + this.boundingBox.left = Math.min(this.boundingBox.left, this.labelDimensions.left); + this.boundingBox.right = Math.max(this.boundingBox.right, this.labelDimensions.left + this.labelDimensions.width); + this.boundingBox.bottom = Math.max(this.boundingBox.bottom, this.boundingBox.bottom + this.labelDimensions.height); } - else { - // zoom so all data will fit on the screen, if clustering is enabled, we do not want start to be called here. - if (this.constants.stabilize == false) { - this.zoomExtent(undefined, true,this.constants.clustering.enabled); - } + }; + + Node.prototype._resizeText = function (ctx) { + if (!this.width) { + var margin = 5; + var textSize = this.getTextSize(ctx); + this.width = textSize.width + 2 * margin; + this.height = textSize.height + 2 * margin; + + // scaling used for clustering + this.width += Math.min(this.clusterSize - 1, this.maxNodeSizeIncrements) * this.clusterSizeWidthFactor; + this.height += Math.min(this.clusterSize - 1, this.maxNodeSizeIncrements) * this.clusterSizeHeightFactor; + this.options.radius+= Math.min(this.clusterSize - 1, this.maxNodeSizeIncrements) * this.clusterSizeRadiusFactor; + this.growthIndicator = this.width - (textSize.width + 2 * margin); } + }; - // if clustering is disabled, the simulation will have started in the setData function - if (this.constants.clustering.enabled) { - this.startWithClustering(); + Node.prototype._drawText = function (ctx) { + this._resizeText(ctx); + this.left = this.x - this.width / 2; + this.top = this.y - this.height / 2; + + this._label(ctx, this.label, this.x, this.y); + + this.boundingBox.top = this.top; + this.boundingBox.left = this.left; + this.boundingBox.right = this.left + this.width; + this.boundingBox.bottom = this.top + this.height; + }; + + + Node.prototype._label = function (ctx, text, x, y, align, baseline, labelUnderNode) { + if (text && Number(this.options.fontSize) * this.networkScale > this.fontDrawThreshold) { + ctx.font = (this.selected ? "bold " : "") + this.options.fontSize + "px " + this.options.fontFace; + + var lines = text.split('\n'); + var lineCount = lines.length; + var fontSize = Number(this.options.fontSize); + var yLine = y + (1 - lineCount) / 2 * fontSize; + if (labelUnderNode == true) { + yLine = y + (1 - lineCount) / (2 * fontSize); + } + + // font fill from edges now for nodes! + var 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; + } + var height = this.options.fontSize * lineCount; + var left = x - width / 2; + var top = y - height / 2; + if (baseline == "hanging") { + top += 0.5 * fontSize; + top += 4; // distance from node, required because we use hanging. Hanging has less difference between browsers + yLine += 4; // distance from node + } + this.labelDimensions = {top:top,left:left,width:width,height:height,yLine:yLine}; + + // create the fontfill background + if (this.options.fontFill !== undefined && this.options.fontFill !== null && this.options.fontFill !== "none") { + ctx.fillStyle = this.options.fontFill; + ctx.fillRect(left, top, width, height); + } + + // draw text + ctx.fillStyle = this.options.fontColor || "black"; + ctx.textAlign = align || "center"; + ctx.textBaseline = baseline || "middle"; + if (this.options.fontStrokeWidth > 0){ + ctx.lineWidth = this.options.fontStrokeWidth; + ctx.strokeStyle = this.options.fontStrokeColor; + ctx.lineJoin = 'round'; + } + for (var i = 0; i < lineCount; i++) { + if(this.options.fontStrokeWidth){ + ctx.strokeText(lines[i], x, yLine); + } + ctx.fillText(lines[i], x, yLine); + yLine += fontSize; + } } - } + }; - // Extend Network with an Emitter mixin - Emitter(Network.prototype); + + Node.prototype.getTextSize = function(ctx) { + if (this.label !== undefined) { + ctx.font = (this.selected ? "bold " : "") + this.options.fontSize + "px " + this.options.fontFace; + + var lines = this.label.split('\n'), + height = (Number(this.options.fontSize) + 4) * lines.length, + width = 0; + + for (var i = 0, iMax = lines.length; i < iMax; i++) { + width = Math.max(width, ctx.measureText(lines[i]).width); + } + + return {"width": width, "height": height, lineCount: lines.length}; + } + else { + return {"width": 0, "height": 0, lineCount: 0}; + } + }; /** - * Determine if the browser requires a setTimeout or a requestAnimationFrame. This was required because - * some implementations (safari and IE9) did not support requestAnimationFrame - * @private + * this is used to determine if a node is visible at all. this is used to determine when it needs to be drawn. + * there is a safety margin of 0.3 * width; + * + * @returns {boolean} */ - Network.prototype._determineBrowserMethod = function() { - var browserType = navigator.userAgent.toLowerCase(); - this.requiresTimeout = false; - if (browserType.indexOf('msie 9.0') != -1) { // IE 9 - this.requiresTimeout = true; + Node.prototype.inArea = function() { + if (this.width !== undefined) { + return (this.x + this.width *this.networkScaleInv >= this.canvasTopLeft.x && + this.x - this.width *this.networkScaleInv < this.canvasBottomRight.x && + this.y + this.height*this.networkScaleInv >= this.canvasTopLeft.y && + this.y - this.height*this.networkScaleInv < this.canvasBottomRight.y); } - else if (browserType.indexOf('safari') != -1) { // safari - if (browserType.indexOf('chrome') <= -1) { - this.requiresTimeout = true; - } + else { + return true; } - } + }; + /** + * checks if the core of the node is in the display area, this is used for opening clusters around zoom + * @returns {boolean} + */ + Node.prototype.inView = function() { + return (this.x >= this.canvasTopLeft.x && + this.x < this.canvasBottomRight.x && + this.y >= this.canvasTopLeft.y && + this.y < this.canvasBottomRight.y); + }; /** - * Get the script path where the vis.js library is located + * This allows the zoom level of the network to influence the rendering + * We store the inverted scale and the coordinates of the top left, and bottom right points of the canvas * - * @returns {string | null} path Path or null when not found. Path does not - * end with a slash. - * @private + * @param scale + * @param canvasTopLeft + * @param canvasBottomRight */ - Network.prototype._getScriptPath = function() { - var scripts = document.getElementsByTagName( 'script' ); - - // find script named vis.js or vis.min.js - for (var i = 0; i < scripts.length; i++) { - var src = scripts[i].src; - var match = src && /\/?vis(.min)?\.js$/.exec(src); - if (match) { - // return path without the script name - return src.substring(0, src.length - match[0].length); - } - } - - return null; + Node.prototype.setScaleAndPos = function(scale,canvasTopLeft,canvasBottomRight) { + this.networkScaleInv = 1.0/scale; + this.networkScale = scale; + this.canvasTopLeft = canvasTopLeft; + this.canvasBottomRight = canvasBottomRight; }; /** - * Find the center position of the network - * @private + * This allows the zoom level of the network to influence the rendering + * + * @param scale */ - Network.prototype._getRange = function() { - var minY = 1e9, maxY = -1e9, minX = 1e9, maxX = -1e9, node; - for (var nodeId in this.nodes) { - if (this.nodes.hasOwnProperty(nodeId)) { - node = this.nodes[nodeId]; - if (minX > (node.boundingBox.left)) {minX = node.boundingBox.left;} - if (maxX < (node.boundingBox.right)) {maxX = node.boundingBox.right;} - if (minY > (node.boundingBox.bottom)) {minY = node.boundingBox.top;} // top is negative, bottom is positive - if (maxY < (node.boundingBox.top)) {maxY = node.boundingBox.bottom;} // top is negative, bottom is positive - } - } - if (minX == 1e9 && maxX == -1e9 && minY == 1e9 && maxY == -1e9) { - minY = 0, maxY = 0, minX = 0, maxX = 0; - } - return {minX: minX, maxX: maxX, minY: minY, maxY: maxY}; + Node.prototype.setScale = function(scale) { + this.networkScaleInv = 1.0/scale; + this.networkScale = scale; }; + /** - * @param {object} range = {minX: minX, maxX: maxX, minY: minY, maxY: maxY}; - * @returns {{x: number, y: number}} - * @private + * set the velocity at 0. Is called when this node is contained in another during clustering */ - Network.prototype._findCenter = function(range) { - return {x: (0.5 * (range.maxX + range.minX)), - y: (0.5 * (range.maxY + range.minY))}; + Node.prototype.clearVelocity = function() { + this.vx = 0; + this.vy = 0; }; /** - * This function zooms out to fit all data on screen based on amount of nodes + * Basic preservation of (kinectic) energy * - * @param {Boolean} [initialZoom] | zoom based on fitted formula or range, true = fitted, default = false; - * @param {Boolean} [disableStart] | If true, start is not called. + * @param massBeforeClustering */ - Network.prototype.zoomExtent = function(animationOptions, initialZoom, disableStart) { - this._redraw(true); + Node.prototype.updateVelocity = function(massBeforeClustering) { + var energyBefore = this.vx * this.vx * massBeforeClustering; + //this.vx = (this.vx < 0) ? -Math.sqrt(energyBefore/this.options.mass) : Math.sqrt(energyBefore/this.options.mass); + this.vx = Math.sqrt(energyBefore/this.options.mass); + energyBefore = this.vy * this.vy * massBeforeClustering; + //this.vy = (this.vy < 0) ? -Math.sqrt(energyBefore/this.options.mass) : Math.sqrt(energyBefore/this.options.mass); + this.vy = Math.sqrt(energyBefore/this.options.mass); + }; - if (initialZoom === undefined) {initialZoom = false;} - if (disableStart === undefined) {disableStart = false;} - if (animationOptions === undefined) {animationOptions = false;} + module.exports = Node; - var range = this._getRange(); - var zoomLevel; - if (initialZoom == true) { - var numberOfNodes = this.nodeIndices.length; - if (this.constants.smoothCurves == true) { - if (this.constants.clustering.enabled == true && - numberOfNodes >= this.constants.clustering.initialMaxNodes) { - zoomLevel = 49.07548 / (numberOfNodes + 142.05338) + 9.1444e-04; // this is obtained from fitting a dataset from 5 points with scale levels that looked good. - } - else { - zoomLevel = 12.662 / (numberOfNodes + 7.4147) + 0.0964822; // this is obtained from fitting a dataset from 5 points with scale levels that looked good. - } - } - else { - if (this.constants.clustering.enabled == true && - numberOfNodes >= this.constants.clustering.initialMaxNodes) { - zoomLevel = 77.5271985 / (numberOfNodes + 187.266146) + 4.76710517e-05; // this is obtained from fitting a dataset from 5 points with scale levels that looked good. - } - else { - zoomLevel = 30.5062972 / (numberOfNodes + 19.93597763) + 0.08413486; // this is obtained from fitting a dataset from 5 points with scale levels that looked good. - } - } +/***/ }, +/* 41 */ +/***/ function(module, exports, __webpack_require__) { - // correct for larger canvasses. - var factor = Math.min(this.frame.canvas.clientWidth / 600, this.frame.canvas.clientHeight / 600); - zoomLevel *= factor; + /** + * 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 Popup(container, x, y, text, style) { + if (container) { + this.container = container; } else { - var xDistance = Math.abs(range.maxX - range.minX) * 1.1; - var yDistance = Math.abs(range.maxY - range.minY) * 1.1; - - var xZoomLevel = this.frame.canvas.clientWidth / xDistance; - var yZoomLevel = this.frame.canvas.clientHeight / yDistance; - zoomLevel = (xZoomLevel <= yZoomLevel) ? xZoomLevel : yZoomLevel; + this.container = document.body; } - if (zoomLevel > 1.0) { - zoomLevel = 1.0; + // x, y and text are optional, see if a style object was passed in their place + if (style === undefined) { + if (typeof x === "object") { + style = x; + x = undefined; + } else if (typeof text === "object") { + style = text; + text = undefined; + } else { + // for backwards compatibility, in case clients other than Network are creating Popup directly + style = { + fontColor: 'black', + fontSize: 14, // px + fontFace: 'verdana', + color: { + border: '#666', + background: '#FFFFC6' + } + } + } } + this.x = 0; + this.y = 0; + this.padding = 5; - var center = this._findCenter(range); - if (disableStart == false) { - var options = {position: center, scale: zoomLevel, animation: animationOptions}; - this.moveTo(options); - this.moving = true; - this.start(); + if (x !== undefined && y !== undefined ) { + this.setPosition(x, y); } - else { - center.x *= zoomLevel; - center.y *= zoomLevel; - center.x -= 0.5 * this.frame.canvas.clientWidth; - center.y -= 0.5 * this.frame.canvas.clientHeight; - this._setScale(zoomLevel); - this._setTranslation(-center.x,-center.y); + if (text !== undefined) { + this.setText(text); } - }; + // create the frame + this.frame = document.createElement("div"); + var styleAttr = this.frame.style; + styleAttr.position = "absolute"; + styleAttr.visibility = "hidden"; + styleAttr.border = "1px solid " + style.color.border; + styleAttr.color = style.fontColor; + styleAttr.fontSize = style.fontSize + "px"; + styleAttr.fontFamily = style.fontFace; + styleAttr.padding = this.padding + "px"; + styleAttr.backgroundColor = style.color.background; + styleAttr.borderRadius = "3px"; + styleAttr.MozBorderRadius = "3px"; + styleAttr.WebkitBorderRadius = "3px"; + styleAttr.boxShadow = "3px 3px 10px rgba(128, 128, 128, 0.5)"; + styleAttr.whiteSpace = "nowrap"; + this.container.appendChild(this.frame); + } /** - * Update the this.nodeIndices with the most recent node index list - * @private + * @param {number} x Horizontal position of the popup window + * @param {number} y Vertical position of the popup window */ - Network.prototype._updateNodeIndexList = function() { - this._clearNodeIndexList(); - for (var idx in this.nodes) { - if (this.nodes.hasOwnProperty(idx)) { - this.nodeIndices.push(idx); - } - } + Popup.prototype.setPosition = function(x, y) { + this.x = parseInt(x); + this.y = parseInt(y); }; - /** - * Set nodes and edges, and optionally options as well. - * - * @param {Object} data Object containing parameters: - * {Array | DataSet | DataView} [nodes] Array with nodes - * {Array | DataSet | DataView} [edges] Array with edges - * {String} [dot] String containing data in DOT format - * {String} [gephi] String containing data in gephi JSON format - * {Options} [options] Object with options - * @param {Boolean} [disableStart] | optional: disable the calling of the start function. + * Set the content for the popup window. This can be HTML code or text. + * @param {string | Element} content */ - Network.prototype.setData = function(data, disableStart) { - if (disableStart === undefined) { - disableStart = false; + Popup.prototype.setText = function(content) { + if (content instanceof Element) { + this.frame.innerHTML = ''; + this.frame.appendChild(content); } - // we set initializing to true to ensure that the hierarchical layout is not performed until both nodes and edges are added. - this.initializing = true; - - if (data && data.dot && (data.nodes || data.edges)) { - throw new SyntaxError('Data must contain either parameter "dot" or ' + - ' parameter pair "nodes" and "edges", but not both.'); + else { + this.frame.innerHTML = content; // string containing text or HTML } + }; - // clean up in case there is anyone in an active mode of the manipulation. This is the same option as bound to the escape button. - if (this.constants.dataManipulation.enabled == true) { - this._createManipulatorBar(); + /** + * Show the popup window + * @param {boolean} show Optional. Show or hide the window + */ + Popup.prototype.show = function (show) { + if (show === undefined) { + show = true; } - // set options - this.setOptions(data && data.options); - // set all data - if (data && data.dot) { - // parse DOT file - if(data && data.dot) { - var dotData = dotparser.DOTToGraph(data.dot); - this.setData(dotData); - return; + if (show) { + 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; } - } - else if (data && data.gephi) { - // parse DOT file - if(data && data.gephi) { - var gephiData = gephiParser.parseGephi(data.gephi); - this.setData(gephiData); - return; + if (top < this.padding) { + top = this.padding; } - } - else { - this._setNodes(data && data.nodes); - this._setEdges(data && data.edges); - } - this._putDataInSector(); - if (disableStart == false) { - if (this.constants.hierarchicalLayout.enabled == true) { - this._resetLevels(); - this._setupHierarchicalLayout(); + + var left = this.x; + if (left + width + this.padding > maxWidth) { + left = maxWidth - width - this.padding; } - else { - // find a stable position or start animating to a stable position - if (this.constants.stabilize) { - this._stabilize(); - } + if (left < this.padding) { + left = this.padding; } - this.start(); + + this.frame.style.left = left + "px"; + this.frame.style.top = top + "px"; + this.frame.style.visibility = "visible"; + } + else { + this.hide(); } - this.initializing = false; }; /** - * Set options - * @param {Object} options + * Hide the popup window */ - Network.prototype.setOptions = function (options) { - if (options) { - var prop; - var fields = ['nodes','edges','smoothCurves','hierarchicalLayout','clustering','navigation', - 'keyboard','dataManipulation','onAdd','onEdit','onEditEdge','onConnect','onDelete','clickToUse' - ]; - // extend all but the values in fields - util.selectiveNotDeepExtend(fields,this.constants, options); - util.selectiveNotDeepExtend(['color'],this.constants.nodes, options.nodes); - util.selectiveNotDeepExtend(['color','length'],this.constants.edges, options.edges); - - if (options.physics) { - util.mergeOptions(this.constants.physics, options.physics,'barnesHut'); - util.mergeOptions(this.constants.physics, options.physics,'repulsion'); + Popup.prototype.hide = function () { + this.frame.style.visibility = "hidden"; + }; - if (options.physics.hierarchicalRepulsion) { - this.constants.hierarchicalLayout.enabled = true; - this.constants.physics.hierarchicalRepulsion.enabled = true; - this.constants.physics.barnesHut.enabled = false; - for (prop in options.physics.hierarchicalRepulsion) { - if (options.physics.hierarchicalRepulsion.hasOwnProperty(prop)) { - this.constants.physics.hierarchicalRepulsion[prop] = options.physics.hierarchicalRepulsion[prop]; - } - } - } - } + module.exports = Popup; - if (options.onAdd) {this.triggerFunctions.add = options.onAdd;} - if (options.onEdit) {this.triggerFunctions.edit = options.onEdit;} - if (options.onEditEdge) {this.triggerFunctions.editEdge = options.onEditEdge;} - if (options.onConnect) {this.triggerFunctions.connect = options.onConnect;} - if (options.onDelete) {this.triggerFunctions.del = options.onDelete;} - util.mergeOptions(this.constants, options,'smoothCurves'); - util.mergeOptions(this.constants, options,'hierarchicalLayout'); - util.mergeOptions(this.constants, options,'clustering'); - util.mergeOptions(this.constants, options,'navigation'); - util.mergeOptions(this.constants, options,'keyboard'); - util.mergeOptions(this.constants, options,'dataManipulation'); +/***/ }, +/* 42 */ +/***/ function(module, exports, __webpack_require__) { + /** + * Parse a text source containing data in DOT language into a JSON object. + * The object contains two lists: one with nodes and one with edges. + * + * DOT language reference: http://www.graphviz.org/doc/info/lang.html + * + * @param {String} data Text containing a graph in DOT-notation + * @return {Object} graph An object containing two parameters: + * {Object[]} nodes + * {Object[]} edges + */ + function parseDOT (data) { + dot = data; + return parseGraph(); + } - if (options.dataManipulation) { - this.editMode = this.constants.dataManipulation.initiallyVisible; - } + // token types enumeration + var TOKENTYPE = { + NULL : 0, + DELIMITER : 1, + IDENTIFIER: 2, + UNKNOWN : 3 + }; + // map with all delimiters + var DELIMITERS = { + '{': true, + '}': true, + '[': true, + ']': true, + ';': true, + '=': true, + ',': true, - // TODO: work out these options and document them - if (options.edges) { - if (options.edges.color !== undefined) { - if (util.isString(options.edges.color)) { - this.constants.edges.color = {}; - this.constants.edges.color.color = options.edges.color; - this.constants.edges.color.highlight = options.edges.color; - this.constants.edges.color.hover = options.edges.color; - } - else { - if (options.edges.color.color !== undefined) {this.constants.edges.color.color = options.edges.color.color;} - if (options.edges.color.highlight !== undefined) {this.constants.edges.color.highlight = options.edges.color.highlight;} - if (options.edges.color.hover !== undefined) {this.constants.edges.color.hover = options.edges.color.hover;} - } - this.constants.edges.inheritColor = false; - } + '->': true, + '--': true + }; - if (!options.edges.fontColor) { - if (options.edges.color !== undefined) { - if (util.isString(options.edges.color)) {this.constants.edges.fontColor = options.edges.color;} - else if (options.edges.color.color !== undefined) {this.constants.edges.fontColor = options.edges.color.color;} - } + var dot = ''; // current dot file + var index = 0; // current index in dot file + var c = ''; // current token character in expr + var token = ''; // current token + var tokenType = TOKENTYPE.NULL; // type of the token + + /** + * Get the first character from the dot file. + * The character is stored into the char c. If the end of the dot file is + * reached, the function puts an empty string in c. + */ + function first() { + index = 0; + c = dot.charAt(0); + } + + /** + * Get the next character from the dot file. + * The character is stored into the char c. If the end of the dot file is + * reached, the function puts an empty string in c. + */ + function next() { + index++; + c = dot.charAt(index); + } + + /** + * Preview the next character from the dot file. + * @return {String} cNext + */ + function nextPreview() { + return dot.charAt(index + 1); + } + + /** + * Test whether given character is alphabetic or numeric + * @param {String} c + * @return {Boolean} isAlphaNumeric + */ + var regexAlphaNumeric = /[a-zA-Z_0-9.:#]/; + function isAlphaNumeric(c) { + return regexAlphaNumeric.test(c); + } + + /** + * Merge all properties of object b into object b + * @param {Object} a + * @param {Object} b + * @return {Object} a + */ + function merge (a, b) { + if (!a) { + a = {}; + } + + if (b) { + for (var name in b) { + if (b.hasOwnProperty(name)) { + a[name] = b[name]; } } + } + return a; + } - if (options.nodes) { - if (options.nodes.color) { - var newColorObj = util.parseColor(options.nodes.color); - this.constants.nodes.color.background = newColorObj.background; - this.constants.nodes.color.border = newColorObj.border; - this.constants.nodes.color.highlight.background = newColorObj.highlight.background; - this.constants.nodes.color.highlight.border = newColorObj.highlight.border; - this.constants.nodes.color.hover.background = newColorObj.hover.background; - this.constants.nodes.color.hover.border = newColorObj.hover.border; + /** + * Set a value in an object, where the provided parameter name can be a + * path with nested parameters. For example: + * + * var obj = {a: 2}; + * setValue(obj, 'b.c', 3); // obj = {a: 2, b: {c: 3}} + * + * @param {Object} obj + * @param {String} path A parameter name or dot-separated parameter path, + * like "color.highlight.border". + * @param {*} value + */ + function setValue(obj, path, value) { + var keys = path.split('.'); + var o = obj; + while (keys.length) { + var key = keys.shift(); + if (keys.length) { + // this isn't the end point + if (!o[key]) { + o[key] = {}; } + o = o[key]; } - if (options.groups) { - for (var groupname in options.groups) { - if (options.groups.hasOwnProperty(groupname)) { - var group = options.groups[groupname]; - this.groups.add(groupname, group); - } - } + else { + // this is the end point + o[key] = value; } + } + } - if (options.tooltip) { - for (prop in options.tooltip) { - if (options.tooltip.hasOwnProperty(prop)) { - this.constants.tooltip[prop] = options.tooltip[prop]; - } - } - if (options.tooltip.color) { - this.constants.tooltip.color = util.parseColor(options.tooltip.color); - } - } + /** + * Add a node to a graph object. If there is already a node with + * the same id, their attributes will be merged. + * @param {Object} graph + * @param {Object} node + */ + function addNode(graph, node) { + var i, len; + var current = null; - if ('clickToUse' in options) { - if (options.clickToUse) { - if (!this.activator) { - this.activator = new Activator(this.frame); - this.activator.on('change', this._createKeyBinds.bind(this)); - } - } - else { - if (this.activator) { - this.activator.destroy(); - delete this.activator; - } + // find root graph (in case of subgraph) + var graphs = [graph]; // list with all graphs from current graph to root graph + var root = graph; + while (root.parent) { + graphs.push(root.parent); + root = root.parent; + } + + // find existing node (at root level) by its id + if (root.nodes) { + for (i = 0, len = root.nodes.length; i < len; i++) { + if (node.id === root.nodes[i].id) { + current = root.nodes[i]; + break; } } + } - if (options.labels) { - throw new Error('Option "labels" is deprecated. Use options "locale" and "locales" instead.'); + if (!current) { + // this is a new node + current = { + id: node.id + }; + if (graph.node) { + // clone default attributes + current.attr = merge(current.attr, graph.node); } + } + // add node to this (sub)graph and all its parent graphs + for (i = graphs.length - 1; i >= 0; i--) { + var g = graphs[i]; - // (Re)loading the mixins that can be enabled or disabled in the options. - // load the force calculation functions, grouped under the physics system. - this._loadPhysicsSystem(); - // load the navigation system. - this._loadNavigationControls(); - // load the data manipulation system - this._loadManipulationSystem(); - // configure the smooth curves - this._configureSmoothCurves(); + if (!g.nodes) { + g.nodes = []; + } + if (g.nodes.indexOf(current) == -1) { + g.nodes.push(current); + } + } - // bind hammer - this._bindHammer(); + // merge attributes + if (node.attr) { + current.attr = merge(current.attr, node.attr); + } + } - // bind keys. If disabled, this will not do anything; - this._createKeyBinds(); + /** + * Add an edge to a graph object + * @param {Object} graph + * @param {Object} edge + */ + function addEdge(graph, edge) { + if (!graph.edges) { + graph.edges = []; + } + graph.edges.push(edge); + if (graph.edge) { + var attr = merge({}, graph.edge); // clone default attributes + edge.attr = merge(attr, edge.attr); // merge attributes + } + } + /** + * Create an edge to a graph object + * @param {Object} graph + * @param {String | Number | Object} from + * @param {String | Number | Object} to + * @param {String} type + * @param {Object | null} attr + * @return {Object} edge + */ + function createEdge(graph, from, to, type, attr) { + var edge = { + from: from, + to: to, + type: type + }; - this.setSize(this.constants.width, this.constants.height); - this.moving = true; - this.start(); + if (graph.edge) { + edge.attr = merge({}, graph.edge); // clone default attributes } - }; - + edge.attr = merge(edge.attr || {}, attr); // merge attributes + return edge; + } /** - * Create the main frame for the Network. - * This function is executed once when a Network object is created. The frame - * contains a canvas, and this canvas contains all objects like the axis and - * nodes. - * @private + * Get next token in the current dot file. + * The token and token type are available as token and tokenType */ - Network.prototype._create = function () { - // remove all elements from the container element. - while (this.containerElement.hasChildNodes()) { - this.containerElement.removeChild(this.containerElement.firstChild); - } + function getToken() { + tokenType = TOKENTYPE.NULL; + token = ''; - this.frame = document.createElement('div'); - this.frame.className = 'vis network-frame'; - this.frame.style.position = 'relative'; - this.frame.style.overflow = 'hidden'; - this.frame.tabIndex = 900; + // skip over whitespaces + while (c == ' ' || c == '\t' || c == '\n' || c == '\r') { // space, tab, enter + next(); + } + do { + var isComment = false; - ////////////////////////////////////////////////////////////////// + // skip comment + if (c == '#') { + // find the previous non-space character + var i = index - 1; + while (dot.charAt(i) == ' ' || dot.charAt(i) == '\t') { + i--; + } + if (dot.charAt(i) == '\n' || dot.charAt(i) == '') { + // the # is at the start of a line, this is indeed a line comment + while (c != '' && c != '\n') { + next(); + } + isComment = true; + } + } + if (c == '/' && nextPreview() == '/') { + // skip line comment + while (c != '' && c != '\n') { + next(); + } + isComment = true; + } + if (c == '/' && nextPreview() == '*') { + // skip block comment + while (c != '') { + if (c == '*' && nextPreview() == '/') { + // end of block comment found. skip these last two characters + next(); + next(); + break; + } + else { + next(); + } + } + isComment = true; + } - this.frame.canvas = document.createElement("canvas"); - this.frame.canvas.style.position = 'relative'; - this.frame.appendChild(this.frame.canvas); + // skip over whitespaces + while (c == ' ' || c == '\t' || c == '\n' || c == '\r') { // space, tab, enter + next(); + } + } + while (isComment); - if (!this.frame.canvas.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.frame.canvas.appendChild(noCanvas); + // check for end of dot file + if (c == '') { + // token is still empty + tokenType = TOKENTYPE.DELIMITER; + return; } - else { - var ctx = this.frame.canvas.getContext("2d"); - this.pixelRatio = (window.devicePixelRatio || 1) / (ctx.webkitBackingStorePixelRatio || - ctx.mozBackingStorePixelRatio || - ctx.msBackingStorePixelRatio || - ctx.oBackingStorePixelRatio || - ctx.backingStorePixelRatio || 1); - this.frame.canvas.getContext("2d").setTransform(this.pixelRatio, 0, 0, this.pixelRatio, 0, 0); + // check for delimiters consisting of 2 characters + var c2 = c + nextPreview(); + if (DELIMITERS[c2]) { + tokenType = TOKENTYPE.DELIMITER; + token = c2; + next(); + next(); + return; } - this._bindHammer(); - }; + // check for delimiters consisting of 1 character + if (DELIMITERS[c]) { + tokenType = TOKENTYPE.DELIMITER; + token = c; + next(); + return; + } + // check for an identifier (number or string) + // TODO: more precise parsing of numbers/strings (and the port separator ':') + if (isAlphaNumeric(c) || c == '-') { + token += c; + next(); - /** - * This function binds hammer, it can be repeated over and over due to the uniqueness check. - * @private - */ - Network.prototype._bindHammer = function() { - var me = this; - if (this.hammer !== undefined) { - this.hammer.dispose(); + while (isAlphaNumeric(c)) { + token += c; + next(); + } + if (token == 'false') { + token = false; // convert to boolean + } + else if (token == 'true') { + token = true; // convert to boolean + } + else if (!isNaN(Number(token))) { + token = Number(token); // convert to number + } + tokenType = TOKENTYPE.IDENTIFIER; + return; } - this.drag = {}; - this.pinch = {}; - this.hammer = Hammer(this.frame.canvas, { - prevent_default: true - }); - this.hammer.on('tap', me._onTap.bind(me) ); - this.hammer.on('doubletap', me._onDoubleTap.bind(me) ); - this.hammer.on('hold', me._onHold.bind(me) ); - this.hammer.on('touch', me._onTouch.bind(me) ); - this.hammer.on('dragstart', me._onDragStart.bind(me) ); - this.hammer.on('drag', me._onDrag.bind(me) ); - this.hammer.on('dragend', me._onDragEnd.bind(me) ); - if (this.constants.zoomable == true) { - this.hammer.on('mousewheel', me._onMouseWheel.bind(me)); - this.hammer.on('DOMMouseScroll', me._onMouseWheel.bind(me)); // for FF - this.hammer.on('pinch', me._onPinch.bind(me) ); + // check for a string enclosed by double quotes + if (c == '"') { + next(); + while (c != '' && (c != '"' || (c == '"' && nextPreview() == '"'))) { + token += c; + if (c == '"') { // skip the escape character + next(); + } + next(); + } + if (c != '"') { + throw newSyntaxError('End of string " expected'); + } + next(); + tokenType = TOKENTYPE.IDENTIFIER; + return; } - this.hammer.on('mousemove', me._onMouseMoveTitle.bind(me) ); - - this.hammerFrame = Hammer(this.frame, { - prevent_default: true - }); - this.hammerFrame.on('release', me._onRelease.bind(me) ); - - // add the frame to the container element - this.containerElement.appendChild(this.frame); + // something unknown is found, wrong characters, a syntax error + tokenType = TOKENTYPE.UNKNOWN; + while (c != '') { + token += c; + next(); + } + throw new SyntaxError('Syntax error in part "' + chop(token, 30) + '"'); } /** - * Binding the keys for keyboard navigation. These functions are defined in the NavigationMixin - * @private + * Parse a graph. + * @returns {Object} graph */ - Network.prototype._createKeyBinds = function() { - var me = this; - if (this.keycharm !== undefined) { - this.keycharm.destroy(); + function parseGraph() { + var graph = {}; + + first(); + getToken(); + + // optional strict keyword + if (token == 'strict') { + graph.strict = true; + getToken(); } - if (this.constants.keyboard.bindToWindow == true) { - this.keycharm = keycharm({container: window, preventDefault: false}); + // graph or digraph keyword + if (token == 'graph' || token == 'digraph') { + graph.type = token; + getToken(); } - else { - this.keycharm = keycharm({container: this.frame, preventDefault: false}); + + // optional graph id + if (tokenType == TOKENTYPE.IDENTIFIER) { + graph.id = token; + getToken(); } - this.keycharm.reset(); + // open angle bracket + if (token != '{') { + throw newSyntaxError('Angle bracket { expected'); + } + getToken(); - if (this.constants.keyboard.enabled && this.isActive()) { - this.keycharm.bind("up", this._moveUp.bind(me) , "keydown"); - this.keycharm.bind("up", this._yStopMoving.bind(me), "keyup"); - this.keycharm.bind("down", this._moveDown.bind(me) , "keydown"); - this.keycharm.bind("down", this._yStopMoving.bind(me), "keyup"); - this.keycharm.bind("left", this._moveLeft.bind(me) , "keydown"); - this.keycharm.bind("left", this._xStopMoving.bind(me), "keyup"); - this.keycharm.bind("right",this._moveRight.bind(me), "keydown"); - this.keycharm.bind("right",this._xStopMoving.bind(me), "keyup"); - this.keycharm.bind("=", this._zoomIn.bind(me), "keydown"); - this.keycharm.bind("=", this._stopZoom.bind(me), "keyup"); - this.keycharm.bind("num+", this._zoomIn.bind(me), "keydown"); - this.keycharm.bind("num+", this._stopZoom.bind(me), "keyup"); - this.keycharm.bind("num-", this._zoomOut.bind(me), "keydown"); - this.keycharm.bind("num-", this._stopZoom.bind(me), "keyup"); - this.keycharm.bind("-", this._zoomOut.bind(me), "keydown"); - this.keycharm.bind("-", this._stopZoom.bind(me), "keyup"); - this.keycharm.bind("[", this._zoomIn.bind(me), "keydown"); - this.keycharm.bind("[", this._stopZoom.bind(me), "keyup"); - this.keycharm.bind("]", this._zoomOut.bind(me), "keydown"); - this.keycharm.bind("]", this._stopZoom.bind(me), "keyup"); - this.keycharm.bind("pageup",this._zoomIn.bind(me), "keydown"); - this.keycharm.bind("pageup",this._stopZoom.bind(me), "keyup"); - this.keycharm.bind("pagedown",this._zoomOut.bind(me),"keydown"); - this.keycharm.bind("pagedown",this._stopZoom.bind(me), "keyup"); - } - this.keycharm.bind("1",this.increaseClusterLevel.bind(me), "keydown"); - this.keycharm.bind("2",this.decreaseClusterLevel.bind(me), "keydown"); - this.keycharm.bind("3",this.forceAggregateHubs.bind(me,true),"keydown"); - this.keycharm.bind("4",this.normalizeClusterLevels.bind(me), "keydown"); - if (this.constants.dataManipulation.enabled == true) { - this.keycharm.bind("esc",this._createManipulatorBar.bind(me)); - this.keycharm.bind("delete",this._deleteSelected.bind(me)); - } - }; - - /** - * Cleans up all bindings of the network, removing it fully from the memory IF the variable is set to null after calling this function. - * var network = new vis.Network(..); - * network.destroy(); - * network = null; - */ - Network.prototype.destroy = function() { - this.start = function () {}; - this.redraw = function () {}; - this.timer = false; - - // cleanup physicsConfiguration if it exists - this._cleanupPhysicsConfiguration(); + // statements + parseStatements(graph); - // remove keybindings - this.keycharm.reset(); + // close angle bracket + if (token != '}') { + throw newSyntaxError('Angle bracket } expected'); + } + getToken(); - // clear hammer bindings - this.hammer.dispose(); + // end of file + if (token !== '') { + throw newSyntaxError('End of file expected'); + } + getToken(); - // clear events - this.off(); + // remove temporary default properties + delete graph.node; + delete graph.edge; + delete graph.graph; - this._recursiveDOMDelete(this.containerElement); + return graph; } - Network.prototype._recursiveDOMDelete = function(DOMobject) { - while (DOMobject.hasChildNodes() == true) { - this._recursiveDOMDelete(DOMobject.firstChild); - DOMobject.removeChild(DOMobject.firstChild); + /** + * Parse a list with statements. + * @param {Object} graph + */ + function parseStatements (graph) { + while (token !== '' && token != '}') { + parseStatement(graph); + if (token == ';') { + getToken(); + } } } /** - * Get the pointer location from a touch location - * @param {{pageX: Number, pageY: Number}} touch - * @return {{x: Number, y: Number}} pointer - * @private + * Parse a single statement. Can be a an attribute statement, node + * statement, a series of node statements and edge statements, or a + * parameter. + * @param {Object} graph */ - Network.prototype._getPointer = function (touch) { - return { - x: touch.pageX - util.getAbsoluteLeft(this.frame.canvas), - y: touch.pageY - util.getAbsoluteTop(this.frame.canvas) - }; - }; + function parseStatement(graph) { + // parse subgraph + var subgraph = parseSubgraph(graph); + if (subgraph) { + // edge statements + parseEdge(graph, subgraph); - /** - * On start of a touch gesture, store the pointer - * @param event - * @private - */ - Network.prototype._onTouch = function (event) { - if (new Date().valueOf() - this.touchTime > 100) { - this.drag.pointer = this._getPointer(event.gesture.center); - this.drag.pinched = false; - this.pinch.scale = this._getScale(); + return; + } - // to avoid double fireing of this event because we have two hammer instances. (on canvas and on frame) - this.touchTime = new Date().valueOf(); + // parse an attribute statement + var attr = parseAttributeStatement(graph); + if (attr) { + return; + } - this._handleTouch(this.drag.pointer); + // parse node + if (tokenType != TOKENTYPE.IDENTIFIER) { + throw newSyntaxError('Identifier expected'); } - }; + var id = token; // id can be a string or a number + getToken(); + + if (token == '=') { + // id statement + getToken(); + if (tokenType != TOKENTYPE.IDENTIFIER) { + throw newSyntaxError('Identifier expected'); + } + graph[id] = token; + getToken(); + // TODO: implement comma separated list with "a_list: ID=ID [','] [a_list] " + } + else { + parseNodeStatement(graph, id); + } + } /** - * handle drag start event - * @private + * Parse a subgraph + * @param {Object} graph parent graph object + * @return {Object | null} subgraph */ - Network.prototype._onDragStart = function (event) { - this._handleDragStart(event); - }; + function parseSubgraph (graph) { + var subgraph = null; + // optional subgraph keyword + if (token == 'subgraph') { + subgraph = {}; + subgraph.type = 'subgraph'; + getToken(); - /** - * This function is called by _onDragStart. - * It is separated out because we can then overload it for the datamanipulation system. - * - * @private - */ - Network.prototype._handleDragStart = function(event) { - // in case the touch event was triggered on an external div, do the initial touch now. - if (this.drag.pointer === undefined) { - this._onTouch(event); + // optional graph id + if (tokenType == TOKENTYPE.IDENTIFIER) { + subgraph.id = token; + getToken(); + } } - var node = this._getNodeAt(this.drag.pointer); - // note: drag.pointer is set in _onTouch to get the initial touch location - - this.drag.dragging = true; - this.drag.selection = []; - this.drag.translation = this._getTranslation(); - this.drag.nodeId = null; - this.draggingNodes = false; + // open angle bracket + if (token == '{') { + getToken(); - if (node != null && this.constants.dragNodes == true) { - this.draggingNodes = true; - this.drag.nodeId = node.id; - // select the clicked node if not yet selected - if (!node.isSelected()) { - this._selectObject(node,false); + if (!subgraph) { + subgraph = {}; } + subgraph.parent = graph; + subgraph.node = graph.node; + subgraph.edge = graph.edge; + subgraph.graph = graph.graph; - this.emit("dragStart",{nodeIds:this.getSelection().nodes}); - - // create an array with the selected nodes and their original location and status - for (var objectId in this.selectionObj.nodes) { - if (this.selectionObj.nodes.hasOwnProperty(objectId)) { - var object = this.selectionObj.nodes[objectId]; - var s = { - id: object.id, - node: object, + // statements + parseStatements(subgraph); - // store original x, y, xFixed and yFixed, make the node temporarily Fixed - x: object.x, - y: object.y, - xFixed: object.xFixed, - yFixed: object.yFixed - }; + // close angle bracket + if (token != '}') { + throw newSyntaxError('Angle bracket } expected'); + } + getToken(); - object.xFixed = true; - object.yFixed = true; + // remove temporary default properties + delete subgraph.node; + delete subgraph.edge; + delete subgraph.graph; + delete subgraph.parent; - this.drag.selection.push(s); - } + // register at the parent graph + if (!graph.subgraphs) { + graph.subgraphs = []; } + graph.subgraphs.push(subgraph); } - }; + return subgraph; + } /** - * handle drag event - * @private + * parse an attribute statement like "node [shape=circle fontSize=16]". + * Available keywords are 'node', 'edge', 'graph'. + * The previous list with default attributes will be replaced + * @param {Object} graph + * @returns {String | null} keyword Returns the name of the parsed attribute + * (node, edge, graph), or null if nothing + * is parsed. */ - Network.prototype._onDrag = function (event) { - this._handleOnDrag(event) - }; - + function parseAttributeStatement (graph) { + // attribute statements + if (token == 'node') { + getToken(); - /** - * This function is called by _onDrag. - * It is separated out because we can then overload it for the datamanipulation system. - * - * @private - */ - Network.prototype._handleOnDrag = function(event) { - if (this.drag.pinched) { - return; + // node attributes + graph.node = parseAttributeList(); + return 'node'; } + else if (token == 'edge') { + getToken(); - // remove the focus on node if it is focussed on by the focusOnNode - this.releaseNode(); + // edge attributes + graph.edge = parseAttributeList(); + return 'edge'; + } + else if (token == 'graph') { + getToken(); - var pointer = this._getPointer(event.gesture.center); - var me = this; - var drag = this.drag; - var selection = drag.selection; - if (selection && selection.length && this.constants.dragNodes == true) { - // calculate delta's and new location - var deltaX = pointer.x - drag.pointer.x; - var deltaY = pointer.y - drag.pointer.y; + // graph attributes + graph.graph = parseAttributeList(); + return 'graph'; + } - // update position of all selected nodes - selection.forEach(function (s) { - var node = s.node; + return null; + } - if (!s.xFixed) { - node.x = me._XconvertDOMtoCanvas(me._XconvertCanvasToDOM(s.x) + deltaX); - } + /** + * parse a node statement + * @param {Object} graph + * @param {String | Number} id + */ + function parseNodeStatement(graph, id) { + // node statement + var node = { + id: id + }; + var attr = parseAttributeList(); + if (attr) { + node.attr = attr; + } + addNode(graph, node); - if (!s.yFixed) { - node.y = me._YconvertDOMtoCanvas(me._YconvertCanvasToDOM(s.y) + deltaY); - } - }); + // edge statements + parseEdge(graph, id); + } + /** + * Parse an edge or a series of edges + * @param {Object} graph + * @param {String | Number} from Id of the from node + */ + function parseEdge(graph, from) { + while (token == '->' || token == '--') { + var to; + var type = token; + getToken(); - // start _animationStep if not yet running - if (!this.moving) { - this.moving = true; - this.start(); + var subgraph = parseSubgraph(graph); + if (subgraph) { + to = subgraph; } - } - else { - // move the network - if (this.constants.dragNetwork == true) { - // if the drag was not started properly because the click started outside the network div, start it now. - if (this.drag.pointer === undefined) { - this._handleDragStart(event); - return; + else { + if (tokenType != TOKENTYPE.IDENTIFIER) { + throw newSyntaxError('Identifier or subgraph expected'); } - var diffX = pointer.x - this.drag.pointer.x; - var diffY = pointer.y - this.drag.pointer.y; - - this._setTranslation( - this.drag.translation.x + diffX, - this.drag.translation.y + diffY - ); - this._redraw(); + to = token; + addNode(graph, { + id: to + }); + getToken(); } - } - }; - /** - * handle drag start event - * @private - */ - Network.prototype._onDragEnd = function (event) { - this._handleDragEnd(event); - }; + // parse edge attributes + var attr = parseAttributeList(); + // create edge + var edge = createEdge(graph, from, to, type, attr); + addEdge(graph, edge); - Network.prototype._handleDragEnd = function(event) { - this.drag.dragging = false; - var selection = this.drag.selection; - if (selection && selection.length) { - selection.forEach(function (s) { - // restore original xFixed and yFixed - s.node.xFixed = s.xFixed; - s.node.yFixed = s.yFixed; - }); - this.moving = true; - this.start(); - } - else { - this._redraw(); - } - if (this.draggingNodes == false) { - this.emit("dragEnd",{nodeIds:[]}); - } - else { - this.emit("dragEnd",{nodeIds:this.getSelection().nodes}); + from = to; } - } + /** - * handle tap/click event: select/unselect a node - * @private + * Parse a set with attributes, + * for example [label="1.000", shape=solid] + * @return {Object | null} attr */ - Network.prototype._onTap = function (event) { - var pointer = this._getPointer(event.gesture.center); - this.pointerPosition = pointer; - this._handleTap(pointer); + function parseAttributeList() { + var attr = null; - }; + while (token == '[') { + getToken(); + attr = {}; + while (token !== '' && token != ']') { + if (tokenType != TOKENTYPE.IDENTIFIER) { + throw newSyntaxError('Attribute name expected'); + } + var name = token; + getToken(); + if (token != '=') { + throw newSyntaxError('Equal sign = expected'); + } + getToken(); - /** - * handle doubletap event - * @private - */ - Network.prototype._onDoubleTap = function (event) { - var pointer = this._getPointer(event.gesture.center); - this._handleDoubleTap(pointer); - }; + if (tokenType != TOKENTYPE.IDENTIFIER) { + throw newSyntaxError('Attribute value expected'); + } + var value = token; + setValue(attr, name, value); // name can be a path + + getToken(); + if (token ==',') { + getToken(); + } + } + + if (token != ']') { + throw newSyntaxError('Bracket ] expected'); + } + getToken(); + } + return attr; + } /** - * handle long tap event: multi select nodes - * @private + * Create a syntax error with extra information on current token and index. + * @param {String} message + * @returns {SyntaxError} err */ - Network.prototype._onHold = function (event) { - var pointer = this._getPointer(event.gesture.center); - this.pointerPosition = pointer; - this._handleOnHold(pointer); - }; + function newSyntaxError(message) { + return new SyntaxError(message + ', got "' + chop(token, 30) + '" (char ' + index + ')'); + } /** - * handle the release of the screen - * - * @private + * Chop off text after a maximum length + * @param {String} text + * @param {Number} maxLength + * @returns {String} */ - Network.prototype._onRelease = function (event) { - var pointer = this._getPointer(event.gesture.center); - this._handleOnRelease(pointer); - }; + function chop (text, maxLength) { + return (text.length <= maxLength) ? text : (text.substr(0, 27) + '...'); + } /** - * Handle pinch event - * @param event - * @private + * Execute a function fn for each pair of elements in two arrays + * @param {Array | *} array1 + * @param {Array | *} array2 + * @param {function} fn */ - Network.prototype._onPinch = function (event) { - var pointer = this._getPointer(event.gesture.center); - - this.drag.pinched = true; - if (!('scale' in this.pinch)) { - this.pinch.scale = 1; + function forEach2(array1, array2, fn) { + if (Array.isArray(array1)) { + array1.forEach(function (elem1) { + if (Array.isArray(array2)) { + array2.forEach(function (elem2) { + fn(elem1, elem2); + }); + } + else { + fn(elem1, array2); + } + }); } - - // TODO: enabled moving while pinching? - var scale = this.pinch.scale * event.gesture.scale; - this._zoom(scale, pointer) - }; + else { + if (Array.isArray(array2)) { + array2.forEach(function (elem2) { + fn(array1, elem2); + }); + } + else { + fn(array1, array2); + } + } + } /** - * Zoom the network in or out - * @param {Number} scale a number around 1, and between 0.01 and 10 - * @param {{x: Number, y: Number}} pointer Position on screen - * @return {Number} appliedScale scale is limited within the boundaries - * @private + * Convert a string containing a graph in DOT language into a map containing + * with nodes and edges in the format of graph. + * @param {String} data Text containing a graph in DOT-notation + * @return {Object} graphData */ - Network.prototype._zoom = function(scale, pointer) { - if (this.constants.zoomable == true) { - var scaleOld = this._getScale(); - if (scale < 0.00001) { - scale = 0.00001; - } - if (scale > 10) { - scale = 10; - } + function DOTToGraph (data) { + // parse the DOT file + var dotData = parseDOT(data); + var graphData = { + nodes: [], + edges: [], + options: {} + }; - var preScaleDragPointer = null; - if (this.drag !== undefined) { - if (this.drag.dragging == true) { - preScaleDragPointer = this.DOMtoCanvas(this.drag.pointer); + // copy the nodes + if (dotData.nodes) { + dotData.nodes.forEach(function (dotNode) { + var graphNode = { + id: dotNode.id, + label: String(dotNode.label || dotNode.id) + }; + merge(graphNode, dotNode.attr); + if (graphNode.image) { + graphNode.shape = 'image'; } - } - // + this.frame.canvas.clientHeight / 2 - var translation = this._getTranslation(); - - var scaleFrac = scale / scaleOld; - var tx = (1 - scaleFrac) * pointer.x + translation.x * scaleFrac; - var ty = (1 - scaleFrac) * pointer.y + translation.y * scaleFrac; - - this.areaCenter = {"x" : this._XconvertDOMtoCanvas(pointer.x), - "y" : this._YconvertDOMtoCanvas(pointer.y)}; - - this._setScale(scale); - this._setTranslation(tx, ty); - this.updateClustersDefault(); + graphData.nodes.push(graphNode); + }); + } - if (preScaleDragPointer != null) { - var postScaleDragPointer = this.canvasToDOM(preScaleDragPointer); - this.drag.pointer.x = postScaleDragPointer.x; - this.drag.pointer.y = postScaleDragPointer.y; + // copy the edges + if (dotData.edges) { + /** + * Convert an edge in DOT format to an edge with VisGraph format + * @param {Object} dotEdge + * @returns {Object} graphEdge + */ + var convertEdge = function (dotEdge) { + var graphEdge = { + from: dotEdge.from, + to: dotEdge.to + }; + merge(graphEdge, dotEdge.attr); + graphEdge.style = (dotEdge.type == '->') ? 'arrow' : 'line'; + return graphEdge; } - this._redraw(); + dotData.edges.forEach(function (dotEdge) { + var from, to; + if (dotEdge.from instanceof Object) { + from = dotEdge.from.nodes; + } + else { + from = { + id: dotEdge.from + } + } - if (scaleOld < scale) { - this.emit("zoom", {direction:"+"}); - } - else { - this.emit("zoom", {direction:"-"}); - } + if (dotEdge.to instanceof Object) { + to = dotEdge.to.nodes; + } + else { + to = { + id: dotEdge.to + } + } - return scale; - } - }; + if (dotEdge.from instanceof Object && dotEdge.from.edges) { + dotEdge.from.edges.forEach(function (subEdge) { + var graphEdge = convertEdge(subEdge); + graphData.edges.push(graphEdge); + }); + } + forEach2(from, to, function (from, to) { + var subEdge = createEdge(graphData, from.id, to.id, dotEdge.type, dotEdge.attr); + var graphEdge = convertEdge(subEdge); + graphData.edges.push(graphEdge); + }); - /** - * Event handler for mouse wheel event, used to zoom the timeline - * See http://adomas.org/javascript-mouse-wheel/ - * https://github.com/EightMedia/hammer.js/issues/256 - * @param {MouseEvent} event - * @private - */ - Network.prototype._onMouseWheel = function(event) { - // retrieve delta - var delta = 0; - if (event.wheelDelta) { /* IE/Opera. */ - delta = event.wheelDelta/120; - } else if (event.detail) { /* Mozilla case. */ - // In Mozilla, sign of delta is different than in IE. - // Also, delta is multiple of 3. - delta = -event.detail/3; + if (dotEdge.to instanceof Object && dotEdge.to.edges) { + dotEdge.to.edges.forEach(function (subEdge) { + var graphEdge = convertEdge(subEdge); + graphData.edges.push(graphEdge); + }); + } + }); } - // If delta is nonzero, handle it. - // Basically, delta is now positive if wheel was scrolled up, - // and negative, if wheel was scrolled down. - if (delta) { - - // calculate the new scale - var scale = this._getScale(); - var zoom = delta / 10; - if (delta < 0) { - zoom = zoom / (1 - zoom); - } - scale *= (1 + zoom); - - // calculate the pointer location - var gesture = hammerUtil.fakeGesture(this, event); - var pointer = this._getPointer(gesture.center); - - // apply the new scale - this._zoom(scale, pointer); + // copy the options + if (dotData.attr) { + graphData.options = dotData.attr; } - // Prevent default actions caused by mouse wheel. - event.preventDefault(); - }; - + return graphData; + } - /** - * Mouse move handler for checking whether the title moves over a node with a title. - * @param {Event} event - * @private - */ - Network.prototype._onMouseMoveTitle = function (event) { - var gesture = hammerUtil.fakeGesture(this, event); - var pointer = this._getPointer(gesture.center); + // exports + exports.parseDOT = parseDOT; + exports.DOTToGraph = DOTToGraph; - // check if the previously selected node is still selected - if (this.popupObj) { - this._checkHidePopup(pointer); - } - // if we bind the keyboard to the div, we have to highlight it to use it. This highlights it on mouse over - if (this.constants.keyboard.bindToWindow == false && this.constants.keyboard.enabled == true) { - this.frame.focus(); - } +/***/ }, +/* 43 */ +/***/ function(module, exports, __webpack_require__) { - // start a timeout that will check if the mouse is positioned above - // an element - var me = this; - var checkShow = function() { - me._checkShowPopup(pointer); + + function parseGephi(gephiJSON, options) { + var edges = []; + var nodes = []; + this.options = { + edges: { + inheritColor: true + }, + nodes: { + allowedToMove: false, + parseColor: false + } }; - if (this.popupTimer) { - clearInterval(this.popupTimer); // stop any running calculationTimer - } - if (!this.drag.dragging) { - this.popupTimer = setTimeout(checkShow, this.constants.tooltip.delay); - } + if (options !== undefined) { + this.options.nodes['allowedToMove'] = options.allowedToMove | false; + this.options.nodes['parseColor'] = options.parseColor | false; + this.options.edges['inheritColor'] = options.inheritColor | true; + } - /** - * Adding hover highlights - */ - if (this.constants.hover == true) { - // removing all hover highlights - for (var edgeId in this.hoverObj.edges) { - if (this.hoverObj.edges.hasOwnProperty(edgeId)) { - this.hoverObj.edges[edgeId].hover = false; - delete this.hoverObj.edges[edgeId]; - } - } + var gEdges = gephiJSON.edges; + var gNodes = gephiJSON.nodes; + for (var i = 0; i < gEdges.length; i++) { + var edge = {}; + var gEdge = gEdges[i]; + edge['id'] = gEdge.id; + edge['from'] = gEdge.source; + edge['to'] = gEdge.target; + edge['attributes'] = gEdge.attributes; + // edge['value'] = gEdge.attributes !== undefined ? gEdge.attributes.Weight : undefined; + // edge['width'] = edge['value'] !== undefined ? undefined : edgegEdge.size; + edge['color'] = gEdge.color; + edge['inheritColor'] = edge['color'] !== undefined ? false : this.options.inheritColor; + edges.push(edge); + } - // adding hover highlights - var obj = this._getNodeAt(pointer); - if (obj == null) { - obj = this._getEdgeAt(pointer); - } - if (obj != null) { - this._hoverObject(obj); + for (var i = 0; i < gNodes.length; i++) { + var node = {}; + var gNode = gNodes[i]; + node['id'] = gNode.id; + node['attributes'] = gNode.attributes; + node['x'] = gNode.x; + node['y'] = gNode.y; + node['label'] = gNode.label; + if (this.options.nodes.parseColor == true) { + node['color'] = gNode.color; } - - // removing all node hover highlights except for the selected one. - for (var nodeId in this.hoverObj.nodes) { - if (this.hoverObj.nodes.hasOwnProperty(nodeId)) { - if (obj instanceof Node && obj.id != nodeId || obj instanceof Edge || obj == null) { - this._blurObject(this.hoverObj.nodes[nodeId]); - delete this.hoverObj.nodes[nodeId]; - } - } + else { + node['color'] = gNode.color !== undefined ? {background:gNode.color, border:gNode.color} : undefined; } - this.redraw(); + node['radius'] = gNode.size; + node['allowedToMoveX'] = this.options.nodes.allowedToMove; + node['allowedToMoveY'] = this.options.nodes.allowedToMove; + nodes.push(node); } - }; - /** - * Check if there is an element on the given position in the network - * (a node or edge). If so, and if this element has a title, - * show a popup window with its title. - * - * @param {{x:Number, y:Number}} pointer - * @private - */ - Network.prototype._checkShowPopup = function (pointer) { - var obj = { - left: this._XconvertDOMtoCanvas(pointer.x), - top: this._YconvertDOMtoCanvas(pointer.y), - right: this._XconvertDOMtoCanvas(pointer.x), - bottom: this._YconvertDOMtoCanvas(pointer.y) - }; + return {nodes:nodes, edges:edges}; + } - var id; - var lastPopupNode = this.popupObj; - var nodeUnderCursor = false; + exports.parseGephi = parseGephi; - if (this.popupObj == undefined) { - // search the nodes for overlap, select the top one in case of multiple nodes - var nodes = this.nodes; - var overlappingNodes = []; - for (id in nodes) { - if (nodes.hasOwnProperty(id)) { - var node = nodes[id]; - if (node.isOverlappingWith(obj)) { - if (node.getTitle() !== undefined) { - overlappingNodes.push(id); - } - } - } - } +/***/ }, +/* 44 */ +/***/ function(module, exports, __webpack_require__) { - if (overlappingNodes.length > 0) { - // if there are overlapping nodes, select the last one, this is the - // one which is drawn on top of the others - this.popupObj = this.nodes[overlappingNodes[overlappingNodes.length - 1]]; - // if you hover over a node, the title of the edge is not supposed to be shown. - nodeUnderCursor = true; - } - } + // first check if moment.js is already loaded in the browser window, if so, + // use this instance. Else, load via commonjs. + module.exports = (typeof window !== 'undefined') && window['moment'] || __webpack_require__(58); - if (this.popupObj === undefined && nodeUnderCursor == false) { - // search the edges for overlap - var edges = this.edges; - var overlappingEdges = []; - for (id in edges) { - if (edges.hasOwnProperty(id)) { - var edge = edges[id]; - if (edge.connected && (edge.getTitle() !== undefined) && - edge.isOverlappingWith(obj)) { - overlappingEdges.push(id); - } - } - } - if (overlappingEdges.length > 0) { - this.popupObj = this.edges[overlappingEdges[overlappingEdges.length - 1]]; - } +/***/ }, +/* 45 */ +/***/ function(module, exports, __webpack_require__) { + + // Only load hammer.js when in a browser environment + // (loading hammer.js in a node.js environment gives errors) + if (typeof window !== 'undefined') { + module.exports = window['Hammer'] || __webpack_require__(59); + } + else { + module.exports = function () { + throw Error('hammer.js is only available in a browser, not in node.js.'); } + } - if (this.popupObj) { - // show popup message window - if (this.popupObj != lastPopupNode) { - var me = this; - if (!me.popup) { - me.popup = new Popup(me.frame, me.constants.tooltip); - } - // adjust a small offset such that the mouse cursor is located in the - // bottom left location of the popup, and you can easily move over the - // popup area - me.popup.setPosition(pointer.x - 3, pointer.y - 3); - me.popup.setText(me.popupObj.getTitle()); - me.popup.show(); - } - } - else { - if (this.popup) { - this.popup.hide(); - } - } - }; +/***/ }, +/* 46 */ +/***/ function(module, exports, __webpack_require__) { + var Emitter = __webpack_require__(56); + var Hammer = __webpack_require__(45); + 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 Activator = __webpack_require__(53); + var DateUtil = __webpack_require__(15); /** - * Check if the popup must be hided, which is the case when the mouse is no - * longer hovering on the object - * @param {{x:Number, y:Number}} pointer - * @private + * Create a timeline visualization + * @param {HTMLElement} container + * @param {vis.DataSet | Array | google.visualization.DataTable} [items] + * @param {Object} [options] See Core.setOptions for the available options. + * @constructor */ - Network.prototype._checkHidePopup = function (pointer) { - if (!this.popupObj || !this._getNodeAt(pointer) ) { - this.popupObj = undefined; - if (this.popup) { - this.popup.hide(); - } - } - }; + function Core () {} + // turn Core into an event emitter + Emitter(Core.prototype); /** - * Set a new size for the network - * @param {string} width Width in pixels or percentage (for example '800px' - * or '50%') - * @param {string} height Height in pixels or percentage (for example '400px' - * or '30%') + * Create the main DOM for the Core: a root panel containing left, right, + * top, bottom, content, and background panel. + * @param {Element} container The container element where the Core will + * be attached. + * @private */ - Network.prototype.setSize = function(width, height) { - var emitEvent = false; - var oldWidth = this.frame.canvas.width; - var oldHeight = this.frame.canvas.height; - if (width != this.constants.width || height != this.constants.height || this.frame.style.width != width || this.frame.style.height != height) { - this.frame.style.width = width; - this.frame.style.height = height; + Core.prototype._create = function (container) { + this.dom = {}; - this.frame.canvas.style.width = '100%'; - this.frame.canvas.style.height = '100%'; + this.dom.root = document.createElement('div'); + this.dom.background = document.createElement('div'); + this.dom.backgroundVertical = document.createElement('div'); + this.dom.backgroundHorizontal = document.createElement('div'); + this.dom.centerContainer = document.createElement('div'); + this.dom.leftContainer = document.createElement('div'); + this.dom.rightContainer = document.createElement('div'); + this.dom.center = document.createElement('div'); + this.dom.left = document.createElement('div'); + this.dom.right = document.createElement('div'); + this.dom.top = document.createElement('div'); + this.dom.bottom = document.createElement('div'); + this.dom.shadowTop = document.createElement('div'); + this.dom.shadowBottom = document.createElement('div'); + this.dom.shadowTopLeft = document.createElement('div'); + this.dom.shadowBottomLeft = document.createElement('div'); + this.dom.shadowTopRight = document.createElement('div'); + this.dom.shadowBottomRight = document.createElement('div'); - this.frame.canvas.width = this.frame.canvas.clientWidth * this.pixelRatio; - this.frame.canvas.height = this.frame.canvas.clientHeight * this.pixelRatio; + this.dom.root.className = 'vis timeline root'; + this.dom.background.className = 'vispanel background'; + this.dom.backgroundVertical.className = 'vispanel background vertical'; + this.dom.backgroundHorizontal.className = 'vispanel background horizontal'; + this.dom.centerContainer.className = 'vispanel center'; + this.dom.leftContainer.className = 'vispanel left'; + this.dom.rightContainer.className = 'vispanel right'; + this.dom.top.className = 'vispanel top'; + this.dom.bottom.className = 'vispanel bottom'; + this.dom.left.className = 'content'; + this.dom.center.className = 'content'; + this.dom.right.className = 'content'; + this.dom.shadowTop.className = 'shadow top'; + this.dom.shadowBottom.className = 'shadow bottom'; + this.dom.shadowTopLeft.className = 'shadow top'; + this.dom.shadowBottomLeft.className = 'shadow bottom'; + this.dom.shadowTopRight.className = 'shadow top'; + this.dom.shadowBottomRight.className = 'shadow bottom'; - this.constants.width = width; - this.constants.height = height; + this.dom.root.appendChild(this.dom.background); + this.dom.root.appendChild(this.dom.backgroundVertical); + this.dom.root.appendChild(this.dom.backgroundHorizontal); + this.dom.root.appendChild(this.dom.centerContainer); + this.dom.root.appendChild(this.dom.leftContainer); + this.dom.root.appendChild(this.dom.rightContainer); + this.dom.root.appendChild(this.dom.top); + this.dom.root.appendChild(this.dom.bottom); - emitEvent = true; - } - else { - // this would adapt the width of the canvas to the width from 100% if and only if - // there is a change. + this.dom.centerContainer.appendChild(this.dom.center); + this.dom.leftContainer.appendChild(this.dom.left); + this.dom.rightContainer.appendChild(this.dom.right); - if (this.frame.canvas.width != this.frame.canvas.clientWidth * this.pixelRatio) { - this.frame.canvas.width = this.frame.canvas.clientWidth * this.pixelRatio; - emitEvent = true; + this.dom.centerContainer.appendChild(this.dom.shadowTop); + this.dom.centerContainer.appendChild(this.dom.shadowBottom); + this.dom.leftContainer.appendChild(this.dom.shadowTopLeft); + this.dom.leftContainer.appendChild(this.dom.shadowBottomLeft); + this.dom.rightContainer.appendChild(this.dom.shadowTopRight); + this.dom.rightContainer.appendChild(this.dom.shadowBottomRight); + + this.on('rangechange', this.redraw.bind(this)); + this.on('touch', this._onTouch.bind(this)); + this.on('pinch', this._onPinch.bind(this)); + this.on('dragstart', this._onDragStart.bind(this)); + this.on('drag', this._onDrag.bind(this)); + + var me = this; + this.on('change', function (properties) { + if (properties && properties.queue == true) { + // redraw once on next tick + if (!me._redrawTimer) { + me._redrawTimer = setTimeout(function () { + me._redrawTimer = null; + me.redraw(); + }, 0) + } } - if (this.frame.canvas.height != this.frame.canvas.clientHeight * this.pixelRatio) { - this.frame.canvas.height = this.frame.canvas.clientHeight * this.pixelRatio; - emitEvent = true; + else { + // redraw immediately + me.redraw(); } - } + }); - if (emitEvent == true) { - this.emit('resize', {width:this.frame.canvas.width * this.pixelRatio,height:this.frame.canvas.height * this.pixelRatio, oldWidth: oldWidth * this.pixelRatio, oldHeight: oldHeight * this.pixelRatio}); - } - }; + // create event listeners for all interesting events, these events will be + // emitted via emitter + this.hammer = Hammer(this.dom.root, { + preventDefault: true + }); + this.listeners = {}; - /** - * Set a data set with nodes for the network - * @param {Array | DataSet | DataView} nodes The data containing the nodes. - * @private - */ - Network.prototype._setNodes = function(nodes) { - var oldNodesData = this.nodesData; - - if (nodes instanceof DataSet || nodes instanceof DataView) { - this.nodesData = nodes; - } - else if (Array.isArray(nodes)) { - this.nodesData = new DataSet(); - this.nodesData.add(nodes); - } - else if (!nodes) { - this.nodesData = new DataSet(); - } - else { - throw new TypeError('Array or DataSet expected'); - } - - if (oldNodesData) { - // unsubscribe from old dataset - util.forEach(this.nodesListeners, function (callback, event) { - oldNodesData.off(event, callback); - }); - } + var events = [ + 'touch', 'pinch', + 'tap', 'doubletap', 'hold', + 'dragstart', 'drag', 'dragend', + 'mousewheel', 'DOMMouseScroll' // DOMMouseScroll is needed for Firefox + ]; + events.forEach(function (event) { + var listener = function () { + var args = [event].concat(Array.prototype.slice.call(arguments, 0)); + if (me.isActive()) { + me.emit.apply(me, args); + } + }; + me.hammer.on(event, listener); + me.listeners[event] = listener; + }); - // remove drawn nodes - this.nodes = {}; + // size properties of each of the panels + this.props = { + root: {}, + background: {}, + centerContainer: {}, + leftContainer: {}, + rightContainer: {}, + center: {}, + left: {}, + right: {}, + top: {}, + bottom: {}, + border: {}, + scrollTop: 0, + scrollTopMin: 0 + }; + this.touch = {}; // store state information needed for touch events - if (this.nodesData) { - // subscribe to new dataset - var me = this; - util.forEach(this.nodesListeners, function (callback, event) { - me.nodesData.on(event, callback); - }); + this.redrawCount = 0; - // draw all new nodes - var ids = this.nodesData.getIds(); - this._addNodes(ids); - } - this._updateSelection(); + // attach the root panel to the provided container + if (!container) throw new Error('No container provided'); + container.appendChild(this.dom.root); }; /** - * Add nodes - * @param {Number[] | String[]} ids - * @private + * Set options. Options will be passed to all components loaded in the Timeline. + * @param {Object} [options] + * {String} orientation + * Vertical orientation for the Timeline, + * can be 'bottom' (default) or 'top'. + * {String | Number} width + * Width for the timeline, a number in pixels or + * a css string like '1000px' or '75%'. '100%' by default. + * {String | Number} height + * Fixed height for the Timeline, a number in pixels or + * a css string like '400px' or '75%'. If undefined, + * The Timeline will automatically size such that + * its contents fit. + * {String | Number} minHeight + * Minimum height for the Timeline, a number in pixels or + * a css string like '400px' or '75%'. + * {String | Number} maxHeight + * Maximum height for the Timeline, a number in pixels or + * a css string like '400px' or '75%'. + * {Number | Date | String} start + * Start date for the visible window + * {Number | Date | String} end + * End date for the visible window */ - Network.prototype._addNodes = function(ids) { - var id; - for (var i = 0, len = ids.length; i < len; i++) { - id = ids[i]; - var data = this.nodesData.get(id); - var node = new Node(data, this.images, this.groups, this.constants); - this.nodes[id] = node; // note: this may replace an existing node - if ((node.xFixed == false || node.yFixed == false) && (node.x === null || node.y === null)) { - var radius = 10 * 0.1*ids.length + 10; - var angle = 2 * Math.PI * Math.random(); - if (node.xFixed == false) {node.x = radius * Math.cos(angle);} - if (node.yFixed == false) {node.y = radius * Math.sin(angle);} - } - this.moving = true; - } - - this._updateNodeIndexList(); - if (this.constants.hierarchicalLayout.enabled == true && this.initializing == false) { - this._resetLevels(); - this._setupHierarchicalLayout(); - } - this._updateCalculationNodes(); - this._reconnectEdges(); - this._updateValueRange(this.nodes); - this.updateLabels(); - }; + Core.prototype.setOptions = function (options) { + if (options) { + // copy the known options + var fields = ['width', 'height', 'minHeight', 'maxHeight', 'autoResize', 'start', 'end', 'orientation', 'clickToUse', 'dataAttributes', 'hiddenDates']; + util.selectiveExtend(fields, this.options, options); - /** - * Update existing nodes, or create them when not yet existing - * @param {Number[] | String[]} ids - * @private - */ - Network.prototype._updateNodes = function(ids,changedData) { - var nodes = this.nodes; - for (var i = 0, len = ids.length; i < len; i++) { - var id = ids[i]; - var node = nodes[id]; - var data = changedData[i]; - if (node) { - // update node - node.setProperties(data, this.constants); + if ('hiddenDates' in this.options) { + DateUtil.convertHiddenOptions(this.body, this.options.hiddenDates); } - else { - // create node - node = new Node(properties, this.images, this.groups, this.constants); - nodes[id] = node; + + if ('clickToUse' in options) { + if (options.clickToUse) { + if (!this.activator) { + this.activator = new Activator(this.dom.root); + } + } + else { + if (this.activator) { + this.activator.destroy(); + delete this.activator; + } + } } + + // enable/disable autoResize + this._initAutoResize(); } - this.moving = true; - if (this.constants.hierarchicalLayout.enabled == true && this.initializing == false) { - this._resetLevels(); - this._setupHierarchicalLayout(); + + // propagate options to all components + this.components.forEach(function (component) { + component.setOptions(options); + }); + + // TODO: remove deprecation error one day (deprecated since version 0.8.0) + if (options && options.order) { + throw new Error('Option order is deprecated. There is no replacement for this feature.'); } - this._updateNodeIndexList(); - this._updateValueRange(nodes); + + // redraw everything + this.redraw(); }; /** - * Remove existing nodes. If nodes do not exist, the method will just ignore it. - * @param {Number[] | String[]} ids - * @private + * Returns true when the Timeline is active. + * @returns {boolean} */ - Network.prototype._removeNodes = function(ids) { - var nodes = this.nodes; - for (var i = 0, len = ids.length; i < len; i++) { - var id = ids[i]; - delete nodes[id]; - } - this._updateNodeIndexList(); - if (this.constants.hierarchicalLayout.enabled == true && this.initializing == false) { - this._resetLevels(); - this._setupHierarchicalLayout(); - } - this._updateCalculationNodes(); - this._reconnectEdges(); - this._updateSelection(); - this._updateValueRange(nodes); + Core.prototype.isActive = function () { + return !this.activator || this.activator.active; }; /** - * Load edges by reading the data table - * @param {Array | DataSet | DataView} edges The data containing the edges. - * @private - * @private + * Destroy the Core, clean up all DOM elements and event listeners. */ - Network.prototype._setEdges = function(edges) { - var oldEdgesData = this.edgesData; + Core.prototype.destroy = function () { + // unbind datasets + this.clear(); - if (edges instanceof DataSet || edges instanceof DataView) { - this.edgesData = edges; - } - else if (Array.isArray(edges)) { - this.edgesData = new DataSet(); - this.edgesData.add(edges); - } - else if (!edges) { - this.edgesData = new DataSet(); + // remove all event listeners + this.off(); + + // stop checking for changed size + this._stopAutoResize(); + + // remove from DOM + if (this.dom.root.parentNode) { + this.dom.root.parentNode.removeChild(this.dom.root); } - else { - throw new TypeError('Array or DataSet expected'); + this.dom = null; + + // remove Activator + if (this.activator) { + this.activator.destroy(); + delete this.activator; } - if (oldEdgesData) { - // unsubscribe from old dataset - util.forEach(this.edgesListeners, function (callback, event) { - oldEdgesData.off(event, callback); - }); + // cleanup hammer touch events + for (var event in this.listeners) { + if (this.listeners.hasOwnProperty(event)) { + delete this.listeners[event]; + } } + this.listeners = null; + this.hammer = null; - // remove drawn edges - this.edges = {}; + // give all components the opportunity to cleanup + this.components.forEach(function (component) { + component.destroy(); + }); - if (this.edgesData) { - // subscribe to new dataset - var me = this; - util.forEach(this.edgesListeners, function (callback, event) { - me.edgesData.on(event, callback); - }); + this.body = null; + }; - // draw all new nodes - var ids = this.edgesData.getIds(); - this._addEdges(ids); + + /** + * Set a custom time bar + * @param {Date} time + */ + Core.prototype.setCustomTime = function (time) { + if (!this.customTime) { + throw new Error('Cannot get custom time: Custom time bar is not enabled'); } - this._reconnectEdges(); + this.customTime.setCustomTime(time); }; /** - * Add edges - * @param {Number[] | String[]} ids - * @private + * Retrieve the current custom time. + * @return {Date} customTime */ - Network.prototype._addEdges = function (ids) { - var edges = this.edges, - edgesData = this.edgesData; - - for (var i = 0, len = ids.length; i < len; i++) { - var id = ids[i]; - - var oldEdge = edges[id]; - if (oldEdge) { - oldEdge.disconnect(); - } - - var data = edgesData.get(id, {"showInternalIds" : true}); - edges[id] = new Edge(data, this, this.constants); - } - this.moving = true; - this._updateValueRange(edges); - this._createBezierNodes(); - this._updateCalculationNodes(); - if (this.constants.hierarchicalLayout.enabled == true && this.initializing == false) { - this._resetLevels(); - this._setupHierarchicalLayout(); + Core.prototype.getCustomTime = function() { + if (!this.customTime) { + throw new Error('Cannot get custom time: Custom time bar is not enabled'); } + + return this.customTime.getCustomTime(); }; + /** - * Update existing edges, or create them when not yet existing - * @param {Number[] | String[]} ids - * @private + * Get the id's of the currently visible items. + * @returns {Array} The ids of the visible items */ - Network.prototype._updateEdges = function (ids) { - var edges = this.edges, - edgesData = this.edgesData; - for (var i = 0, len = ids.length; i < len; i++) { - var id = ids[i]; + Core.prototype.getVisibleItems = function() { + return this.itemSet && this.itemSet.getVisibleItems() || []; + }; - var data = edgesData.get(id); - var edge = edges[id]; - if (edge) { - // update edge - edge.disconnect(); - edge.setProperties(data, this.constants); - edge.connect(); - } - else { - // create edge - edge = new Edge(data, this, this.constants); - this.edges[id] = edge; - } - } - this._createBezierNodes(); - if (this.constants.hierarchicalLayout.enabled == true && this.initializing == false) { - this._resetLevels(); - this._setupHierarchicalLayout(); - } - this.moving = true; - this._updateValueRange(edges); - }; /** - * Remove existing edges. Non existing ids will be ignored - * @param {Number[] | String[]} ids - * @private + * Clear the Core. By Default, items, groups and options are cleared. + * Example usage: + * + * timeline.clear(); // clear items, groups, and options + * timeline.clear({options: true}); // clear options only + * + * @param {Object} [what] Optionally specify what to clear. By default: + * {items: true, groups: true, options: true} */ - Network.prototype._removeEdges = function (ids) { - var edges = this.edges; - for (var i = 0, len = ids.length; i < len; i++) { - var id = ids[i]; - var edge = edges[id]; - if (edge) { - if (edge.via != null) { - delete this.sectors['support']['nodes'][edge.via.id]; - } - edge.disconnect(); - delete edges[id]; - } + Core.prototype.clear = function(what) { + // clear items + if (!what || what.items) { + this.setItems(null); } - this.moving = true; - this._updateValueRange(edges); - if (this.constants.hierarchicalLayout.enabled == true && this.initializing == false) { - this._resetLevels(); - this._setupHierarchicalLayout(); + // clear groups + if (!what || what.groups) { + this.setGroups(null); + } + + // clear options of timeline and of each of the components + if (!what || what.options) { + this.components.forEach(function (component) { + component.setOptions(component.defaultOptions); + }); + + this.setOptions(this.defaultOptions); // this will also do a redraw } - this._updateCalculationNodes(); }; /** - * Reconnect all edges - * @private + * Set Core window such that it fits all items + * @param {Object} [options] Available options: + * `animate: boolean | number` + * If true (default), the range is animated + * smoothly to the new window. + * If a number, the number is taken as duration + * for the animation. Default duration is 500 ms. */ - Network.prototype._reconnectEdges = function() { - var id, - nodes = this.nodes, - edges = this.edges; - for (id in nodes) { - if (nodes.hasOwnProperty(id)) { - nodes[id].edges = []; - nodes[id].dynamicEdges = []; - } - } + Core.prototype.fit = function(options) { + var range = this._getDataRange(); - for (id in edges) { - if (edges.hasOwnProperty(id)) { - var edge = edges[id]; - edge.from = null; - edge.to = null; - edge.connect(); - } + // skip range set if there is no start and end date + if (range.start === null && range.end === null) { + return; } + + var animate = (options && options.animate !== undefined) ? options.animate : true; + this.range.setRange(range.start, range.end, animate); }; /** - * Update the values of all object in the given array according to the current - * value range of the objects in the array. - * @param {Object} obj An object containing a set of Edges or Nodes - * The objects must have a method getValue() and - * setValueRange(min, max). - * @private + * Calculate the data range of the items and applies a 5% window around it. + * @returns {{start: Date | null, end: Date | null}} + * @protected */ - Network.prototype._updateValueRange = function(obj) { - var id; + Core.prototype._getDataRange = function() { + // apply the data range as range + var dataRange = this.getItemRange(); - // determine the range of the objects - var valueMin = undefined; - var valueMax = undefined; - for (id in obj) { - if (obj.hasOwnProperty(id)) { - var value = obj[id].getValue(); - if (value !== undefined) { - valueMin = (valueMin === undefined) ? value : Math.min(value, valueMin); - valueMax = (valueMax === undefined) ? value : Math.max(value, valueMax); - } + // add 5% space on both sides + var start = dataRange.min; + var end = dataRange.max; + if (start != null && end != null) { + var interval = (end.valueOf() - start.valueOf()); + if (interval <= 0) { + // prevent an empty interval + interval = 24 * 60 * 60 * 1000; // 1 day } + start = new Date(start.valueOf() - interval * 0.05); + end = new Date(end.valueOf() + interval * 0.05); } - // adjust the range of all objects - if (valueMin !== undefined && valueMax !== undefined) { - for (id in obj) { - if (obj.hasOwnProperty(id)) { - obj[id].setValueRange(valueMin, valueMax); - } - } + return { + start: start, + end: end } }; /** - * Redraw the network with the current data - * chart will be resized too. + * Set the visible window. Both parameters are optional, you can change only + * start or only end. Syntax: + * + * TimeLine.setWindow(start, end) + * TimeLine.setWindow(range) + * + * Where start and end can be a Date, number, or string, and range is an + * object with properties start and end. + * + * @param {Date | Number | String | Object} [start] Start date of visible window + * @param {Date | Number | String} [end] End date of visible window + * @param {Object} [options] Available options: + * `animate: boolean | number` + * If true (default), the range is animated + * smoothly to the new window. + * If a number, the number is taken as duration + * for the animation. Default duration is 500 ms. */ - Network.prototype.redraw = function() { - this.setSize(this.constants.width, this.constants.height); - this._redraw(); + Core.prototype.setWindow = function(start, end, options) { + var animate = (options && options.animate !== undefined) ? options.animate : true; + if (arguments.length == 1) { + var range = arguments[0]; + this.range.setRange(range.start, range.end, animate); + } + else { + this.range.setRange(start, end, animate); + } }; /** - * Redraw the network with the current data - * @param hidden | used to get the first estimate of the node sizes. only the nodes are drawn after which they are quickly drawn over. - * @private - */ - Network.prototype._redraw = function(hidden) { - var ctx = this.frame.canvas.getContext('2d'); - - ctx.setTransform(this.pixelRatio, 0, 0, this.pixelRatio, 0, 0); + * Move the window such that given time is centered on screen. + * @param {Date | Number | String} time + * @param {Object} [options] Available options: + * `animate: boolean | number` + * If true (default), the range is animated + * smoothly to the new window. + * If a number, the number is taken as duration + * for the animation. Default duration is 500 ms. + */ + Core.prototype.moveTo = function(time, options) { + var interval = this.range.end - this.range.start; + var t = util.convert(time, 'Date').valueOf(); - // clear the canvas - var w = this.frame.canvas.width * this.pixelRatio; - var h = this.frame.canvas.height * this.pixelRatio; - ctx.clearRect(0, 0, w, h); + var start = t - interval / 2; + var end = t + interval / 2; + var animate = (options && options.animate !== undefined) ? options.animate : true; - // set scaling and translation - ctx.save(); - ctx.translate(this.translation.x, this.translation.y); - ctx.scale(this.scale, this.scale); + this.range.setRange(start, end, animate); + }; - this.canvasTopLeft = { - "x": this._XconvertDOMtoCanvas(0), - "y": this._YconvertDOMtoCanvas(0) - }; - this.canvasBottomRight = { - "x": this._XconvertDOMtoCanvas(this.frame.canvas.clientWidth * this.pixelRatio), - "y": this._YconvertDOMtoCanvas(this.frame.canvas.clientHeight * this.pixelRatio) + /** + * Get the visible window + * @return {{start: Date, end: Date}} Visible range + */ + Core.prototype.getWindow = function() { + var range = this.range.getRange(); + return { + start: new Date(range.start), + end: new Date(range.end) }; + }; - if (!(hidden == true)) { - this._doInAllSectors("_drawAllSectorNodes", ctx); - if (this.drag.dragging == false || this.drag.dragging === undefined || this.constants.hideEdgesOnDrag == false) { - this._doInAllSectors("_drawEdges", ctx); - } - } + /** + * Force a redraw of the Core. Can be useful to manually redraw when + * option autoResize=false + */ + Core.prototype.redraw = function() { + var resized = false; + var options = this.options; + var props = this.props; + var dom = this.dom; - if (this.drag.dragging == false || this.drag.dragging === undefined || this.constants.hideNodesOnDrag == false) { - this._doInAllSectors("_drawNodes",ctx,false); + if (!dom) return; // when destroyed + + DateUtil.updateHiddenDates(this.body, this.options.hiddenDates); + + // update class names + if (options.orientation == 'top') { + util.addClassName(dom.root, 'top'); + util.removeClassName(dom.root, 'bottom'); + } + else { + util.removeClassName(dom.root, 'top'); + util.addClassName(dom.root, 'bottom'); } - if (!(hidden == true)) { - if (this.controlNodesActive == true) { - this._doInAllSectors("_drawControlNodes", ctx); - } + // update root width and height options + dom.root.style.maxHeight = util.option.asSize(options.maxHeight, ''); + dom.root.style.minHeight = util.option.asSize(options.minHeight, ''); + dom.root.style.width = util.option.asSize(options.width, ''); + + // calculate border widths + props.border.left = (dom.centerContainer.offsetWidth - dom.centerContainer.clientWidth) / 2; + props.border.right = props.border.left; + props.border.top = (dom.centerContainer.offsetHeight - dom.centerContainer.clientHeight) / 2; + props.border.bottom = props.border.top; + var borderRootHeight= dom.root.offsetHeight - dom.root.clientHeight; + var borderRootWidth = dom.root.offsetWidth - dom.root.clientWidth; + + // workaround for a bug in IE: the clientWidth of an element with + // a height:0px and overflow:hidden is not calculated and always has value 0 + if (dom.centerContainer.clientHeight === 0) { + props.border.left = props.border.top; + props.border.right = props.border.left; + } + if (dom.root.clientHeight === 0) { + borderRootWidth = borderRootHeight; } - // this._doInSupportSector("_drawNodes",ctx,true); - // this._drawTree(ctx,"#F00F0F"); + // calculate the heights. If any of the side panels is empty, we set the height to + // minus the border width, such that the border will be invisible + props.center.height = dom.center.offsetHeight; + props.left.height = dom.left.offsetHeight; + props.right.height = dom.right.offsetHeight; + props.top.height = dom.top.clientHeight || -props.border.top; + props.bottom.height = dom.bottom.clientHeight || -props.border.bottom; - // restore original scaling and translation - ctx.restore(); + // TODO: compensate borders when any of the panels is empty. - if (hidden == true) { - ctx.clearRect(0, 0, w, h); + // apply auto height + // TODO: only calculate autoHeight when needed (else we cause an extra reflow/repaint of the DOM) + var contentHeight = Math.max(props.left.height, props.center.height, props.right.height); + var autoHeight = props.top.height + contentHeight + props.bottom.height + + borderRootHeight + props.border.top + props.border.bottom; + dom.root.style.height = util.option.asSize(options.height, autoHeight + 'px'); + + // calculate heights of the content panels + props.root.height = dom.root.offsetHeight; + props.background.height = props.root.height - borderRootHeight; + var containerHeight = props.root.height - props.top.height - props.bottom.height - + borderRootHeight; + props.centerContainer.height = containerHeight; + props.leftContainer.height = containerHeight; + props.rightContainer.height = props.leftContainer.height; + + // calculate the widths of the panels + props.root.width = dom.root.offsetWidth; + props.background.width = props.root.width - borderRootWidth; + props.left.width = dom.leftContainer.clientWidth || -props.border.left; + props.leftContainer.width = props.left.width; + props.right.width = dom.rightContainer.clientWidth || -props.border.right; + props.rightContainer.width = props.right.width; + var centerWidth = props.root.width - props.left.width - props.right.width - borderRootWidth; + props.center.width = centerWidth; + props.centerContainer.width = centerWidth; + props.top.width = centerWidth; + props.bottom.width = centerWidth; + + // resize the panels + dom.background.style.height = props.background.height + 'px'; + dom.backgroundVertical.style.height = props.background.height + 'px'; + dom.backgroundHorizontal.style.height = props.centerContainer.height + 'px'; + dom.centerContainer.style.height = props.centerContainer.height + 'px'; + dom.leftContainer.style.height = props.leftContainer.height + 'px'; + dom.rightContainer.style.height = props.rightContainer.height + 'px'; + + dom.background.style.width = props.background.width + 'px'; + dom.backgroundVertical.style.width = props.centerContainer.width + 'px'; + dom.backgroundHorizontal.style.width = props.background.width + 'px'; + dom.centerContainer.style.width = props.center.width + 'px'; + dom.top.style.width = props.top.width + 'px'; + dom.bottom.style.width = props.bottom.width + 'px'; + + // reposition the panels + dom.background.style.left = '0'; + dom.background.style.top = '0'; + dom.backgroundVertical.style.left = (props.left.width + props.border.left) + 'px'; + dom.backgroundVertical.style.top = '0'; + dom.backgroundHorizontal.style.left = '0'; + dom.backgroundHorizontal.style.top = props.top.height + 'px'; + dom.centerContainer.style.left = props.left.width + 'px'; + dom.centerContainer.style.top = props.top.height + 'px'; + dom.leftContainer.style.left = '0'; + dom.leftContainer.style.top = props.top.height + 'px'; + dom.rightContainer.style.left = (props.left.width + props.center.width) + 'px'; + dom.rightContainer.style.top = props.top.height + 'px'; + dom.top.style.left = props.left.width + 'px'; + dom.top.style.top = '0'; + dom.bottom.style.left = props.left.width + 'px'; + dom.bottom.style.top = (props.top.height + props.centerContainer.height) + 'px'; + + // update the scrollTop, feasible range for the offset can be changed + // when the height of the Core or of the contents of the center changed + this._updateScrollTop(); + + // reposition the scrollable contents + var offset = this.props.scrollTop; + if (options.orientation == 'bottom') { + offset += Math.max(this.props.centerContainer.height - this.props.center.height - + this.props.border.top - this.props.border.bottom, 0); + } + dom.center.style.left = '0'; + dom.center.style.top = offset + 'px'; + dom.left.style.left = '0'; + dom.left.style.top = offset + 'px'; + dom.right.style.left = '0'; + dom.right.style.top = offset + 'px'; + + // show shadows when vertical scrolling is available + var visibilityTop = this.props.scrollTop == 0 ? 'hidden' : ''; + var visibilityBottom = this.props.scrollTop == this.props.scrollTopMin ? 'hidden' : ''; + dom.shadowTop.style.visibility = visibilityTop; + dom.shadowBottom.style.visibility = visibilityBottom; + dom.shadowTopLeft.style.visibility = visibilityTop; + dom.shadowBottomLeft.style.visibility = visibilityBottom; + dom.shadowTopRight.style.visibility = visibilityTop; + dom.shadowBottomRight.style.visibility = visibilityBottom; + + // redraw all components + this.components.forEach(function (component) { + resized = component.redraw() || resized; + }); + if (resized) { + // keep repainting until all sizes are settled + var MAX_REDRAWS = 3; // maximum number of consecutive redraws + if (this.redrawCount < MAX_REDRAWS) { + this.redrawCount++; + this.redraw(); + } + else { + console.log('WARNING: infinite loop in redraw?'); + } + this.redrawCount = 0; } + + this.emit("finishedRedraw"); + }; + + // TODO: deprecated since version 1.1.0, remove some day + Core.prototype.repaint = function () { + throw new Error('Function repaint is deprecated. Use redraw instead.'); }; /** - * Set the translation of the network - * @param {Number} offsetX Horizontal offset - * @param {Number} offsetY Vertical offset - * @private + * Set a current time. This can be used for example to ensure that a client's + * time is synchronized with a shared server time. + * Only applicable when option `showCurrentTime` is true. + * @param {Date | String | Number} time A Date, unix timestamp, or + * ISO date string. */ - Network.prototype._setTranslation = function(offsetX, offsetY) { - if (this.translation === undefined) { - this.translation = { - x: 0, - y: 0 - }; + Core.prototype.setCurrentTime = function(time) { + if (!this.currentTime) { + throw new Error('Option showCurrentTime must be true'); } - if (offsetX !== undefined) { - this.translation.x = offsetX; - } - if (offsetY !== undefined) { - this.translation.y = offsetY; + this.currentTime.setCurrentTime(time); + }; + + /** + * Get the current time. + * Only applicable when option `showCurrentTime` is true. + * @return {Date} Returns the current time. + */ + Core.prototype.getCurrentTime = function() { + if (!this.currentTime) { + throw new Error('Option showCurrentTime must be true'); } - this.emit('viewChanged'); + return this.currentTime.getCurrentTime(); }; /** - * Get the translation of the network - * @return {Object} translation An object with parameters x and y, both a number + * Convert a position on screen (pixels) to a datetime + * @param {int} x Position on the screen in pixels + * @return {Date} time The datetime the corresponds with given position x * @private */ - Network.prototype._getTranslation = function() { - return { - x: this.translation.x, - y: this.translation.y - }; + // TODO: move this function to Range + Core.prototype._toTime = function(x) { + return DateUtil.toTime(this, x, this.props.center.width); }; /** - * Scale the network - * @param {Number} scale Scaling factor 1.0 is unscaled + * Convert a position on the global screen (pixels) to a datetime + * @param {int} x Position on the screen in pixels + * @return {Date} time The datetime the corresponds with given position x * @private */ - Network.prototype._setScale = function(scale) { - this.scale = scale; + // TODO: move this function to Range + Core.prototype._toGlobalTime = function(x) { + return DateUtil.toTime(this, x, this.props.root.width); + //var conversion = this.range.conversion(this.props.root.width); + //return new Date(x / conversion.scale + conversion.offset); }; /** - * Get the current scale of the network - * @return {Number} scale Scaling factor 1.0 is unscaled + * Convert a datetime (Date object) into a position on the screen + * @param {Date} time A date + * @return {int} x The position on the screen in pixels which corresponds + * with the given date. * @private */ - Network.prototype._getScale = function() { - return this.scale; + // TODO: move this function to Range + Core.prototype._toScreen = function(time) { + return DateUtil.toScreen(this, time, this.props.center.width); }; + + /** - * Convert the X coordinate in DOM-space (coordinate point in browser relative to the container div) to - * the X coordinate in canvas-space (the simulation sandbox, which the camera looks upon) - * @param {number} x - * @returns {number} + * Convert a datetime (Date object) into a position on the root + * This is used to get the pixel density estimate for the screen, not the center panel + * @param {Date} time A date + * @return {int} x The position on root in pixels which corresponds + * with the given date. * @private */ - Network.prototype._XconvertDOMtoCanvas = function(x) { - return (x - this.translation.x) / this.scale; + // TODO: move this function to Range + Core.prototype._toGlobalScreen = function(time) { + return DateUtil.toScreen(this, time, this.props.root.width); + //var conversion = this.range.conversion(this.props.root.width); + //return (time.valueOf() - conversion.offset) * conversion.scale; }; + /** - * Convert the X coordinate in canvas-space (the simulation sandbox, which the camera looks upon) to - * the X coordinate in DOM-space (coordinate point in browser relative to the container div) - * @param {number} x - * @returns {number} + * Initialize watching when option autoResize is true * @private */ - Network.prototype._XconvertCanvasToDOM = function(x) { - return x * this.scale + this.translation.x; + Core.prototype._initAutoResize = function () { + if (this.options.autoResize == true) { + this._startAutoResize(); + } + else { + this._stopAutoResize(); + } }; /** - * Convert the Y coordinate in DOM-space (coordinate point in browser relative to the container div) to - * the Y coordinate in canvas-space (the simulation sandbox, which the camera looks upon) - * @param {number} y - * @returns {number} + * Watch for changes in the size of the container. On resize, the Panel will + * automatically redraw itself. * @private */ - Network.prototype._YconvertDOMtoCanvas = function(y) { - return (y - this.translation.y) / this.scale; + Core.prototype._startAutoResize = function () { + var me = this; + + this._stopAutoResize(); + + this._onResize = function() { + if (me.options.autoResize != true) { + // stop watching when the option autoResize is changed to false + me._stopAutoResize(); + return; + } + + if (me.dom.root) { + // check whether the frame is resized + // Note: we compare offsetWidth here, not clientWidth. For some reason, + // IE does not restore the clientWidth from 0 to the actual width after + // changing the timeline's container display style from none to visible + if ((me.dom.root.offsetWidth != me.props.lastWidth) || + (me.dom.root.offsetHeight != me.props.lastHeight)) { + me.props.lastWidth = me.dom.root.offsetWidth; + me.props.lastHeight = me.dom.root.offsetHeight; + + me.emit('change'); + } + } + }; + + // add event listener to window resize + util.addEventListener(window, 'resize', this._onResize); + + this.watchTimer = setInterval(this._onResize, 1000); }; /** - * Convert the Y coordinate in canvas-space (the simulation sandbox, which the camera looks upon) to - * the Y coordinate in DOM-space (coordinate point in browser relative to the container div) - * @param {number} y - * @returns {number} + * Stop watching for a resize of the frame. * @private */ - Network.prototype._YconvertCanvasToDOM = function(y) { - return y * this.scale + this.translation.y ; + Core.prototype._stopAutoResize = function () { + if (this.watchTimer) { + clearInterval(this.watchTimer); + this.watchTimer = undefined; + } + + // remove event listener on window.resize + util.removeEventListener(window, 'resize', this._onResize); + this._onResize = null; }; + /** + * Start moving the timeline vertically + * @param {Event} event + * @private + */ + Core.prototype._onTouch = function (event) { + this.touch.allowDragging = true; + }; /** - * - * @param {object} pos = {x: number, y: number} - * @returns {{x: number, y: number}} - * @constructor + * Start moving the timeline vertically + * @param {Event} event + * @private */ - Network.prototype.canvasToDOM = function (pos) { - return {x: this._XconvertCanvasToDOM(pos.x), y: this._YconvertCanvasToDOM(pos.y)}; + Core.prototype._onPinch = function (event) { + this.touch.allowDragging = false; }; /** - * - * @param {object} pos = {x: number, y: number} - * @returns {{x: number, y: number}} - * @constructor + * Start moving the timeline vertically + * @param {Event} event + * @private */ - Network.prototype.DOMtoCanvas = function (pos) { - return {x: this._XconvertDOMtoCanvas(pos.x), y: this._YconvertDOMtoCanvas(pos.y)}; + Core.prototype._onDragStart = function (event) { + this.touch.initialScrollTop = this.props.scrollTop; }; /** - * Redraw all nodes - * The 2d context of a HTML canvas can be retrieved by canvas.getContext('2d'); - * @param {CanvasRenderingContext2D} ctx - * @param {Boolean} [alwaysShow] + * Move the timeline vertically + * @param {Event} event * @private */ - Network.prototype._drawNodes = function(ctx,alwaysShow) { - if (alwaysShow === undefined) { - alwaysShow = false; - } + Core.prototype._onDrag = function (event) { + // refuse to drag when we where pinching to prevent the timeline make a jump + // when releasing the fingers in opposite order from the touch screen + if (!this.touch.allowDragging) return; - // first draw the unselected nodes - var nodes = this.nodes; - var selected = []; + var delta = event.gesture.deltaY; - for (var id in nodes) { - if (nodes.hasOwnProperty(id)) { - nodes[id].setScaleAndPos(this.scale,this.canvasTopLeft,this.canvasBottomRight); - if (nodes[id].isSelected()) { - selected.push(id); - } - else { - if (nodes[id].inArea() || alwaysShow) { - nodes[id].draw(ctx); - } - } - } - } + var oldScrollTop = this._getScrollTop(); + var newScrollTop = this._setScrollTop(this.touch.initialScrollTop + delta); - // draw the selected nodes on top - for (var s = 0, sMax = selected.length; s < sMax; s++) { - if (nodes[selected[s]].inArea() || alwaysShow) { - nodes[selected[s]].draw(ctx); - } + + if (newScrollTop != oldScrollTop) { + this.redraw(); // TODO: this causes two redraws when dragging, the other is triggered by rangechange already + this.emit("verticalDrag"); } }; /** - * Redraw all edges - * The 2d context of a HTML canvas can be retrieved by canvas.getContext('2d'); - * @param {CanvasRenderingContext2D} ctx + * Apply a scrollTop + * @param {Number} scrollTop + * @returns {Number} scrollTop Returns the applied scrollTop * @private */ - Network.prototype._drawEdges = function(ctx) { - var edges = this.edges; - for (var id in edges) { - if (edges.hasOwnProperty(id)) { - var edge = edges[id]; - edge.setScale(this.scale); - if (edge.connected) { - edges[id].draw(ctx); - } - } - } + Core.prototype._setScrollTop = function (scrollTop) { + this.props.scrollTop = scrollTop; + this._updateScrollTop(); + return this.props.scrollTop; }; /** - * Redraw all edges - * The 2d context of a HTML canvas can be retrieved by canvas.getContext('2d'); - * @param {CanvasRenderingContext2D} ctx + * Update the current scrollTop when the height of the containers has been changed + * @returns {Number} scrollTop Returns the applied scrollTop * @private */ - Network.prototype._drawControlNodes = function(ctx) { - var edges = this.edges; - for (var id in edges) { - if (edges.hasOwnProperty(id)) { - edges[id]._drawControlNodes(ctx); + Core.prototype._updateScrollTop = function () { + // recalculate the scrollTopMin + var scrollTopMin = Math.min(this.props.centerContainer.height - this.props.center.height, 0); // is negative or zero + if (scrollTopMin != this.props.scrollTopMin) { + // in case of bottom orientation, change the scrollTop such that the contents + // do not move relative to the time axis at the bottom + if (this.options.orientation == 'bottom') { + this.props.scrollTop += (scrollTopMin - this.props.scrollTopMin); } + this.props.scrollTopMin = scrollTopMin; } + + // limit the scrollTop to the feasible scroll range + if (this.props.scrollTop > 0) this.props.scrollTop = 0; + if (this.props.scrollTop < scrollTopMin) this.props.scrollTop = scrollTopMin; + + return this.props.scrollTop; }; /** - * Find a stable position for all nodes + * Get the current scrollTop + * @returns {number} scrollTop * @private */ - Network.prototype._stabilize = function() { - if (this.constants.freezeForStabilization == true) { - this._freezeDefinedNodes(); - } + Core.prototype._getScrollTop = function () { + return this.props.scrollTop; + }; - // find stable position - var count = 0; - while (this.moving && count < this.constants.stabilizationIterations) { - this._physicsTick(); - count++; - } + module.exports = Core; - if (this.constants.zoomExtentOnStabilize == true) { - this.zoomExtent(undefined, false, true); - } - if (this.constants.freezeForStabilization == true) { - this._restoreFrozenNodes(); - } - }; +/***/ }, +/* 47 */ +/***/ function(module, exports, __webpack_require__) { + + var Hammer = __webpack_require__(45); /** - * When initializing and stabilizing, we can freeze nodes with a predefined position. This greatly speeds up stabilization - * because only the supportnodes for the smoothCurves have to settle. - * - * @private + * Fake a hammer.js gesture. Event can be a ScrollEvent or MouseMoveEvent + * @param {Element} element + * @param {Event} event */ - Network.prototype._freezeDefinedNodes = function() { - var nodes = this.nodes; - for (var id in nodes) { - if (nodes.hasOwnProperty(id)) { - if (nodes[id].x != null && nodes[id].y != null) { - nodes[id].fixedData.x = nodes[id].xFixed; - nodes[id].fixedData.y = nodes[id].yFixed; - nodes[id].xFixed = true; - nodes[id].yFixed = true; - } - } + exports.fakeGesture = function(element, event) { + var eventType = null; + + // for hammer.js 1.0.5 + // var gesture = Hammer.event.collectEventData(this, eventType, event); + + // for hammer.js 1.0.6+ + var touches = Hammer.event.getTouchList(event, eventType); + var gesture = Hammer.event.collectEventData(this, eventType, touches, event); + + // on IE in standards mode, no touches are recognized by hammer.js, + // resulting in NaN values for center.pageX and center.pageY + if (isNaN(gesture.center.pageX)) { + gesture.center.pageX = event.pageX; + } + if (isNaN(gesture.center.pageY)) { + gesture.center.pageY = event.pageY; } + + return gesture; }; - /** - * Unfreezes the nodes that have been frozen by _freezeDefinedNodes. - * - * @private - */ - Network.prototype._restoreFrozenNodes = function() { - var nodes = this.nodes; - for (var id in nodes) { - if (nodes.hasOwnProperty(id)) { - if (nodes[id].fixedData.x != null) { - nodes[id].xFixed = nodes[id].fixedData.x; - nodes[id].yFixed = nodes[id].fixedData.y; - } - } - } + +/***/ }, +/* 48 */ +/***/ function(module, exports, __webpack_require__) { + + // English + exports['en'] = { + current: 'current', + time: 'time' + }; + exports['en_EN'] = exports['en']; + exports['en_US'] = exports['en']; + + // Dutch + exports['nl'] = { + custom: 'aangepaste', + time: 'tijd' }; + exports['nl_NL'] = exports['nl']; + exports['nl_BE'] = exports['nl']; +/***/ }, +/* 49 */ +/***/ function(module, exports, __webpack_require__) { + /** - * Check if any of the nodes is still moving - * @param {number} vmin the minimum velocity considered as 'moving' - * @return {boolean} true if moving, false if non of the nodes is moving - * @private + * Created by Alex on 11/11/2014. */ - Network.prototype._isMoving = function(vmin) { - var nodes = this.nodes; - for (var id in nodes) { - if (nodes.hasOwnProperty(id) && nodes[id].isMoving(vmin)) { - return true; - } + var DOMutil = __webpack_require__(2); + var Points = __webpack_require__(51); + + function Line(groupId, options) { + this.groupId = groupId; + this.options = options; + } + + Line.prototype.getYRange = function(groupData) { + var yMin = groupData[0].y; + var yMax = groupData[0].y; + for (var j = 0; j < groupData.length; j++) { + yMin = yMin > groupData[j].y ? groupData[j].y : yMin; + yMax = yMax < groupData[j].y ? groupData[j].y : yMax; } - return false; + return {min: yMin, max: yMax, yAxisOrientation: this.options.yAxisOrientation}; }; /** - * /** - * Perform one discrete step for all nodes + * draw a line graph * - * @private + * @param dataset + * @param group */ - Network.prototype._discreteStepNodes = function() { - var interval = this.physicsDiscreteStepsize; - var nodes = this.nodes; - var nodeId; - var nodesPresent = false; + Line.prototype.draw = function (dataset, group, framework) { + if (dataset != null) { + if (dataset.length > 0) { + var path, d; + var svgHeight = Number(framework.svg.style.height.replace('px','')); + path = DOMutil.getSVGElement('path', framework.svgElements, framework.svg); + path.setAttributeNS(null, "class", group.className); + if(group.style !== undefined) { + path.setAttributeNS(null, "style", group.style); + } - if (this.constants.maxVelocity > 0) { - for (nodeId in nodes) { - if (nodes.hasOwnProperty(nodeId)) { - nodes[nodeId].discreteStepLimited(interval, this.constants.maxVelocity); - nodesPresent = true; + // construct path from dataset + if (group.options.catmullRom.enabled == true) { + d = Line._catmullRom(dataset, group); } - } - } - else { - for (nodeId in nodes) { - if (nodes.hasOwnProperty(nodeId)) { - nodes[nodeId].discreteStep(interval); - nodesPresent = true; + else { + d = Line._linear(dataset); } - } - } - if (nodesPresent == true) { - var vminCorrected = this.constants.minVelocity / Math.max(this.scale,0.05); - if (vminCorrected > 0.5*this.constants.maxVelocity) { - return true; - } - else { - return this._isMoving(vminCorrected); + // append with points for fill and finalize the path + if (group.options.shaded.enabled == true) { + var fillPath = DOMutil.getSVGElement('path', framework.svgElements, framework.svg); + var dFill; + if (group.options.shaded.orientation == 'top') { + dFill = 'M' + dataset[0].x + ',' + 0 + ' ' + d + 'L' + dataset[dataset.length - 1].x + ',' + 0; + } + else { + dFill = 'M' + dataset[0].x + ',' + svgHeight + ' ' + d + 'L' + dataset[dataset.length - 1].x + ',' + svgHeight; + } + fillPath.setAttributeNS(null, "class", group.className + " fill"); + if(group.options.shaded.style !== undefined) { + fillPath.setAttributeNS(null, "style", group.options.shaded.style); + } + fillPath.setAttributeNS(null, "d", dFill); + } + // copy properties to path for drawing. + path.setAttributeNS(null, 'd', 'M' + d); + + // draw points + if (group.options.drawPoints.enabled == true) { + Points.draw(dataset, group, framework); + } } } - return false; }; - Network.prototype._revertPhysicsState = function() { - var nodes = this.nodes; - for (var nodeId in nodes) { - if (nodes.hasOwnProperty(nodeId)) { - nodes[nodeId].revertPosition(); - } - } - } - - Network.prototype._revertPhysicsTick = function() { - this._doInAllActiveSectors("_revertPhysicsState"); - if (this.constants.smoothCurves.enabled == true && this.constants.smoothCurves.dynamic == true) { - this._doInSupportSector("_revertPhysicsState"); - } - } /** - * A single simulation step (or "tick") in the physics simulation - * + * This uses an uniform parametrization of the CatmullRom algorithm: + * 'On the Parameterization of Catmull-Rom Curves' by Cem Yuksel et al. + * @param data + * @returns {string} * @private */ - Network.prototype._physicsTick = function() { - if (!this.freezeSimulation) { - if (this.moving == true) { - var mainMovingStatus = false; - var supportMovingStatus = false; + Line._catmullRomUniform = function(data) { + // catmull rom + var p0, p1, p2, p3, bp1, bp2; + var d = Math.round(data[0].x) + ',' + Math.round(data[0].y) + ' '; + var normalization = 1/6; + var length = data.length; + for (var i = 0; i < length - 1; i++) { - this._doInAllActiveSectors("_initializeForceCalculation"); - var mainMoving = this._doInAllActiveSectors("_discreteStepNodes"); - if (this.constants.smoothCurves.enabled == true && this.constants.smoothCurves.dynamic == true) { - supportMovingStatus = this._doInSupportSector("_discreteStepNodes"); - } + p0 = (i == 0) ? data[0] : data[i-1]; + p1 = data[i]; + p2 = data[i+1]; + p3 = (i + 2 < length) ? data[i+2] : p2; - // gather movement data from all sectors, if one moves, we are NOT stabilzied - for (var i = 0; i < mainMoving.length; i++) {mainMovingStatus = mainMoving[0] || mainMovingStatus;} - // determine if the network has stabilzied - this.moving = mainMovingStatus || supportMovingStatus; + // Catmull-Rom to Cubic Bezier conversion matrix + // 0 1 0 0 + // -1/6 1 1/6 0 + // 0 1/6 1 -1/6 + // 0 0 1 0 - if (this.moving == false) { - this._revertPhysicsTick(); - } - else { - // this is here to ensure that there is no start event when the network is already stable. - if (this.startedStabilization == false) { - this.emit("startStabilization"); - this.startedStabilization = true; - } - } + // bp0 = { x: p1.x, y: p1.y }; + bp1 = { x: ((-p0.x + 6*p1.x + p2.x) *normalization), y: ((-p0.y + 6*p1.y + p2.y) *normalization)}; + bp2 = { x: (( p1.x + 6*p2.x - p3.x) *normalization), y: (( p1.y + 6*p2.y - p3.y) *normalization)}; + // bp0 = { x: p2.x, y: p2.y }; - this.stabilizationIterations++; - } + d += 'C' + + bp1.x + ',' + + bp1.y + ' ' + + bp2.x + ',' + + bp2.y + ' ' + + p2.x + ',' + + p2.y + ' '; } - }; + return d; + }; /** - * This function runs one step of the animation. It calls an x amount of physics ticks and one render tick. - * It reschedules itself at the beginning of the function + * This uses either the chordal or centripetal parameterization of the catmull-rom algorithm. + * By default, the centripetal parameterization is used because this gives the nicest results. + * These parameterizations are relatively heavy because the distance between 4 points have to be calculated. * + * One optimization can be used to reuse distances since this is a sliding window approach. + * @param data + * @param group + * @returns {string} * @private */ - Network.prototype._animationStep = function() { - // reset the timer so a new scheduled animation step can be set - this.timer = undefined; + Line._catmullRom = function(data, group) { + var alpha = group.options.catmullRom.alpha; + if (alpha == 0 || alpha === undefined) { + return this._catmullRomUniform(data); + } + else { + var p0, p1, p2, p3, bp1, bp2, d1,d2,d3, A, B, N, M; + var d3powA, d2powA, d3pow2A, d2pow2A, d1pow2A, d1powA; + var d = Math.round(data[0].x) + ',' + Math.round(data[0].y) + ' '; + var length = data.length; + for (var i = 0; i < length - 1; i++) { - // handle the keyboad movement - this._handleNavigation(); + p0 = (i == 0) ? data[0] : data[i-1]; + p1 = data[i]; + p2 = data[i+1]; + p3 = (i + 2 < length) ? data[i+2] : p2; - // check if the physics have settled - if (this.moving == true) { - var startTime = Date.now(); - this._physicsTick(); - var physicsTime = Date.now() - startTime; + d1 = Math.sqrt(Math.pow(p0.x - p1.x,2) + Math.pow(p0.y - p1.y,2)); + d2 = Math.sqrt(Math.pow(p1.x - p2.x,2) + Math.pow(p1.y - p2.y,2)); + d3 = Math.sqrt(Math.pow(p2.x - p3.x,2) + Math.pow(p2.y - p3.y,2)); - // run double speed if it is a little graph - if ((this.renderTimestep - this.renderTime > 2 * physicsTime || this.runDoubleSpeed == true) && this.moving == true) { - this._physicsTick(); + // Catmull-Rom to Cubic Bezier conversion matrix - // this makes sure there is no jitter. The decision is taken once to run it at double speed. - if (this.renderTime != 0) { - this.runDoubleSpeed = true - } - } - } + // A = 2d1^2a + 3d1^a * d2^a + d3^2a + // B = 2d3^2a + 3d3^a * d2^a + d2^2a - var renderStartTime = Date.now(); - this._redraw(); - this.renderTime = Date.now() - renderStartTime; + // [ 0 1 0 0 ] + // [ -d2^2a /N A/N d1^2a /N 0 ] + // [ 0 d3^2a /M B/M -d2^2a /M ] + // [ 0 0 1 0 ] - // this schedules a new animation step - this.start(); - }; + d3powA = Math.pow(d3, alpha); + d3pow2A = Math.pow(d3,2*alpha); + d2powA = Math.pow(d2, alpha); + d2pow2A = Math.pow(d2,2*alpha); + d1powA = Math.pow(d1, alpha); + d1pow2A = Math.pow(d1,2*alpha); - if (typeof window !== 'undefined') { - window.requestAnimationFrame = window.requestAnimationFrame || window.mozRequestAnimationFrame || - window.webkitRequestAnimationFrame || window.msRequestAnimationFrame; - } + A = 2*d1pow2A + 3*d1powA * d2powA + d2pow2A; + B = 2*d3pow2A + 3*d3powA * d2powA + d2pow2A; + N = 3*d1powA * (d1powA + d2powA); + if (N > 0) {N = 1 / N;} + M = 3*d3powA * (d3powA + d2powA); + if (M > 0) {M = 1 / M;} - /** - * Schedule a animation step with the refreshrate interval. - */ - Network.prototype.start = function() { - if (this.moving == true || this.xIncrement != 0 || this.yIncrement != 0 || this.zoomIncrement != 0 || this.animating == true) { - if (!this.timer) { - if (this.requiresTimeout == true) { - this.timer = window.setTimeout(this._animationStep.bind(this), this.renderTimestep); // wait this.renderTimeStep milliseconds and perform the animation step function - } - else { - this.timer = window.requestAnimationFrame(this._animationStep.bind(this)); // wait this.renderTimeStep milliseconds and perform the animation step function - } - } - } - else { - this._redraw(); - // this check is to ensure that the network does not emit these events if it was already stabilized and setOptions is called (setting moving to true and calling start()) - if (this.stabilizationIterations > 1) { - // trigger the "stabilized" event. - // The event is triggered on the next tick, to prevent the case that - // it is fired while initializing the Network, in which case you would not - // be able to catch it - var me = this; - var params = { - iterations: me.stabilizationIterations - }; - this.stabilizationIterations = 0; - this.startedStabilization = false; - setTimeout(function () { - me.emit("stabilized", params); - }, 0); - } - else { - this.stabilizationIterations = 0; + bp1 = { x: ((-d2pow2A * p0.x + A*p1.x + d1pow2A * p2.x) * N), + y: ((-d2pow2A * p0.y + A*p1.y + d1pow2A * p2.y) * N)}; + + bp2 = { x: (( d3pow2A * p1.x + B*p2.x - d2pow2A * p3.x) * M), + y: (( d3pow2A * p1.y + B*p2.y - d2pow2A * p3.y) * M)}; + + if (bp1.x == 0 && bp1.y == 0) {bp1 = p1;} + if (bp2.x == 0 && bp2.y == 0) {bp2 = p2;} + d += 'C' + + bp1.x + ',' + + bp1.y + ' ' + + bp2.x + ',' + + bp2.y + ' ' + + p2.x + ',' + + p2.y + ' '; } + + return d; } }; - /** - * Move the network according to the keyboard presses. - * + * this generates the SVG path for a linear drawing between datapoints. + * @param data + * @returns {string} * @private */ - Network.prototype._handleNavigation = function() { - if (this.xIncrement != 0 || this.yIncrement != 0) { - var translation = this._getTranslation(); - this._setTranslation(translation.x+this.xIncrement, translation.y+this.yIncrement); - } - if (this.zoomIncrement != 0) { - var center = { - x: this.frame.canvas.clientWidth / 2, - y: this.frame.canvas.clientHeight / 2 - }; - this._zoom(this.scale*(1 + this.zoomIncrement), center); + Line._linear = function(data) { + // linear + var d = ''; + for (var i = 0; i < data.length; i++) { + if (i == 0) { + d += data[i].x + ',' + data[i].y; + } + else { + d += ' ' + data[i].x + ',' + data[i].y; + } } + return d; }; + module.exports = Line; + + +/***/ }, +/* 50 */ +/***/ function(module, exports, __webpack_require__) { /** - * Freeze the _animationStep + * Created by Alex on 11/11/2014. */ - Network.prototype.toggleFreeze = function() { - if (this.freezeSimulation == false) { - this.freezeSimulation = true; + var DOMutil = __webpack_require__(2); + var Points = __webpack_require__(51); + + function Bargraph(groupId, options) { + this.groupId = groupId; + this.options = options; + } + + Bargraph.prototype.getYRange = function(groupData) { + if (this.options.barChart.handleOverlap != 'stack') { + var yMin = groupData[0].y; + var yMax = groupData[0].y; + for (var j = 0; j < groupData.length; j++) { + yMin = yMin > groupData[j].y ? groupData[j].y : yMin; + yMax = yMax < groupData[j].y ? groupData[j].y : yMax; + } + return {min: yMin, max: yMax, yAxisOrientation: this.options.yAxisOrientation}; } else { - this.freezeSimulation = false; - this.start(); + var barCombinedData = []; + for (var j = 0; j < groupData.length; j++) { + barCombinedData.push({ + x: groupData[j].x, + y: groupData[j].y, + groupId: this.groupId + }); + } + return barCombinedData; } }; + /** - * This function cleans the support nodes if they are not needed and adds them when they are. + * draw a bar graph * - * @param {boolean} [disableStart] - * @private + * @param groupIds + * @param processedGroupData */ - Network.prototype._configureSmoothCurves = function(disableStart) { - if (disableStart === undefined) { - disableStart = true; - } - if (this.constants.smoothCurves.enabled == true && this.constants.smoothCurves.dynamic == true) { - this._createBezierNodes(); - // cleanup unused support nodes - for (var nodeId in this.sectors['support']['nodes']) { - if (this.sectors['support']['nodes'].hasOwnProperty(nodeId)) { - if (this.edges[this.sectors['support']['nodes'][nodeId].parentEdgeId] === undefined) { - delete this.sectors['support']['nodes'][nodeId]; + Bargraph.draw = function (groupIds, processedGroupData, framework) { + var combinedData = []; + var intersections = {}; + var coreDistance; + var key, drawData; + var group; + var i,j; + var barPoints = 0; + + // combine all barchart data + for (i = 0; i < groupIds.length; i++) { + group = framework.groups[groupIds[i]]; + if (group.options.style == 'bar') { + if (group.visible == true && (framework.options.groups.visibility[groupIds[i]] === undefined || framework.options.groups.visibility[groupIds[i]] == true)) { + for (j = 0; j < processedGroupData[groupIds[i]].length; j++) { + combinedData.push({ + x: processedGroupData[groupIds[i]][j].x, + y: processedGroupData[groupIds[i]][j].y, + groupId: groupIds[i] + }); + barPoints += 1; } } } } - else { - // delete the support nodes - this.sectors['support']['nodes'] = {}; - for (var edgeId in this.edges) { - if (this.edges.hasOwnProperty(edgeId)) { - this.edges[edgeId].via = null; - } - } - } - - - this._updateCalculationNodes(); - if (!disableStart) { - this.moving = true; - this.start(); - } - }; + if (barPoints == 0) {return;} - /** - * 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. - * - * @private - */ - Network.prototype._createBezierNodes = function() { - if (this.constants.smoothCurves.enabled == true && this.constants.smoothCurves.dynamic == true) { - for (var edgeId in this.edges) { - if (this.edges.hasOwnProperty(edgeId)) { - var edge = this.edges[edgeId]; - if (edge.via == null) { - var nodeId = "edgeId:".concat(edge.id); - this.sectors['support']['nodes'][nodeId] = new Node( - {id:nodeId, - mass:1, - shape:'circle', - image:"", - internalMultiplier:1 - },{},{},this.constants); - edge.via = this.sectors['support']['nodes'][nodeId]; - edge.via.parentEdgeId = edge.id; - edge.positionBezierNode(); - } - } + // sort by time and by group + combinedData.sort(function (a, b) { + if (a.x == b.x) { + return a.groupId - b.groupId; + } else { + return a.x - b.x; } - } - }; + }); - /** - * load the functions that load the mixins into the prototype. - * - * @private - */ - Network.prototype._initializeMixinLoaders = function () { - for (var mixin in MixinLoader) { - if (MixinLoader.hasOwnProperty(mixin)) { - Network.prototype[mixin] = MixinLoader[mixin]; - } - } - }; + // get intersections + Bargraph._getDataIntersections(intersections, combinedData); - /** - * Load the XY positions of the nodes into the dataset. - */ - Network.prototype.storePosition = function() { - console.log("storePosition is depricated: use .storePositions() from now on.") - this.storePositions(); - }; + // plot barchart + for (i = 0; i < combinedData.length; i++) { + group = framework.groups[combinedData[i].groupId]; + var minWidth = 0.1 * group.options.barChart.width; - /** - * Load the XY positions of the nodes into the dataset. - */ - Network.prototype.storePositions = function() { - var dataArray = []; - for (var nodeId in this.nodes) { - if (this.nodes.hasOwnProperty(nodeId)) { - var node = this.nodes[nodeId]; - var allowedToMoveX = !this.nodes.xFixed; - var allowedToMoveY = !this.nodes.yFixed; - if (this.nodesData._data[nodeId].x != Math.round(node.x) || this.nodesData._data[nodeId].y != Math.round(node.y)) { - dataArray.push({id:nodeId,x:Math.round(node.x),y:Math.round(node.y),allowedToMoveX:allowedToMoveX,allowedToMoveY:allowedToMoveY}); + key = combinedData[i].x; + var heightOffset = 0; + if (intersections[key] === undefined) { + if (i+1 < combinedData.length) {coreDistance = Math.abs(combinedData[i+1].x - key);} + if (i > 0) {coreDistance = Math.min(coreDistance,Math.abs(combinedData[i-1].x - key));} + drawData = Bargraph._getSafeDrawData(coreDistance, group, minWidth); + } + else { + var nextKey = i + (intersections[key].amount - intersections[key].resolved); + var prevKey = i - (intersections[key].resolved + 1); + if (nextKey < combinedData.length) {coreDistance = Math.abs(combinedData[nextKey].x - key);} + if (prevKey > 0) {coreDistance = Math.min(coreDistance,Math.abs(combinedData[prevKey].x - key));} + drawData = Bargraph._getSafeDrawData(coreDistance, group, minWidth); + intersections[key].resolved += 1; + + if (group.options.barChart.handleOverlap == 'stack') { + heightOffset = intersections[key].accumulated; + intersections[key].accumulated += group.zeroPosition - combinedData[i].y; } + else if (group.options.barChart.handleOverlap == 'sideBySide') { + drawData.width = drawData.width / intersections[key].amount; + drawData.offset += (intersections[key].resolved) * drawData.width - (0.5*drawData.width * (intersections[key].amount+1)); + if (group.options.barChart.align == 'left') {drawData.offset -= 0.5*drawData.width;} + else if (group.options.barChart.align == 'right') {drawData.offset += 0.5*drawData.width;} + } + } + DOMutil.drawBar(combinedData[i].x + drawData.offset, combinedData[i].y - heightOffset, drawData.width, group.zeroPosition - combinedData[i].y, group.className + ' bar', framework.svgElements, framework.svg); + // draw points + if (group.options.drawPoints.enabled == true) { + DOMutil.drawPoint(combinedData[i].x + drawData.offset, combinedData[i].y, group, framework.svgElements, framework.svg); } } - this.nodesData.update(dataArray); }; + /** - * Return the positions of the nodes. + * Fill the intersections object with counters of how many datapoints share the same x coordinates + * @param intersections + * @param combinedData + * @private */ - Network.prototype.getPositions = function(ids) { - var dataArray = {}; - if (ids !== undefined) { - if (Array.isArray(ids) == true) { - for (var i = 0; i < ids.length; i++) { - if (this.nodes[ids[i]] !== undefined) { - var node = this.nodes[ids[i]]; - dataArray[ids[i]] = {x: Math.round(node.x), y: Math.round(node.y)}; - } - } + Bargraph._getDataIntersections = function (intersections, combinedData) { + // get intersections + var coreDistance; + for (var i = 0; i < combinedData.length; i++) { + if (i + 1 < combinedData.length) { + coreDistance = Math.abs(combinedData[i + 1].x - combinedData[i].x); } - else { - if (this.nodes[ids] !== undefined) { - var node = this.nodes[ids]; - dataArray[ids] = {x: Math.round(node.x), y: Math.round(node.y)}; - } + if (i > 0) { + coreDistance = Math.min(coreDistance, Math.abs(combinedData[i - 1].x - combinedData[i].x)); } - } - else { - for (var nodeId in this.nodes) { - if (this.nodes.hasOwnProperty(nodeId)) { - var node = this.nodes[nodeId]; - dataArray[nodeId] = {x: Math.round(node.x), y: Math.round(node.y)}; + if (coreDistance == 0) { + if (intersections[combinedData[i].x] === undefined) { + intersections[combinedData[i].x] = {amount: 0, resolved: 0, accumulated: 0}; } + intersections[combinedData[i].x].amount += 1; } } - return dataArray; }; - /** - * Center a node in view. + * Get the width and offset for bargraphs based on the coredistance between datapoints * - * @param {Number} nodeId - * @param {Number} [options] + * @param coreDistance + * @param group + * @param minWidth + * @returns {{width: Number, offset: Number}} + * @private */ - Network.prototype.focusOnNode = function (nodeId, options) { - if (this.nodes.hasOwnProperty(nodeId)) { - if (options === undefined) { - options = {}; - } - var nodePosition = {x: this.nodes[nodeId].x, y: this.nodes[nodeId].y}; - options.position = nodePosition; - options.lockedOnNode = nodeId; + Bargraph._getSafeDrawData = function (coreDistance, group, minWidth) { + var width, offset; + if (coreDistance < group.options.barChart.width && coreDistance > 0) { + width = coreDistance < minWidth ? minWidth : coreDistance; - this.moveTo(options) + offset = 0; // recalculate offset with the new width; + if (group.options.barChart.align == 'left') { + offset -= 0.5 * coreDistance; + } + else if (group.options.barChart.align == 'right') { + offset += 0.5 * coreDistance; + } } else { - console.log("This nodeId cannot be found."); - } - }; - - /** - * - * @param {Object} options | options.offset = {x:Number, y:Number} // offset from the center in DOM pixels - * | options.scale = Number // scale to move to - * | options.position = {x:Number, y:Number} // position to move to - * | options.animation = {duration:Number, easingFunction:String} || Boolean // position to move to - */ - Network.prototype.moveTo = function (options) { - if (options === undefined) { - options = {}; - return; + // default settings + width = group.options.barChart.width; + offset = 0; + if (group.options.barChart.align == 'left') { + offset -= 0.5 * group.options.barChart.width; + } + else if (group.options.barChart.align == 'right') { + offset += 0.5 * group.options.barChart.width; + } } - if (options.offset === undefined) {options.offset = {x: 0, y: 0}; } - if (options.offset.x === undefined) {options.offset.x = 0; } - if (options.offset.y === undefined) {options.offset.y = 0; } - if (options.scale === undefined) {options.scale = this._getScale(); } - if (options.position === undefined) {options.position = this._getTranslation();} - if (options.animation === undefined) {options.animation = {duration:0}; } - if (options.animation === false ) {options.animation = {duration:0}; } - if (options.animation === true ) {options.animation = {}; } - if (options.animation.duration === undefined) {options.animation.duration = 1000; } // default duration - if (options.animation.easingFunction === undefined) {options.animation.easingFunction = "easeInOutQuad"; } // default easing function - this.animateView(options); + return {width: width, offset: offset}; }; - /** - * - * @param {Object} options | options.offset = {x:Number, y:Number} // offset from the center in DOM pixels - * | options.time = Number // animation time in milliseconds - * | options.scale = Number // scale to animate to - * | options.position = {x:Number, y:Number} // position to animate to - * | options.easingFunction = String // linear, easeInQuad, easeOutQuad, easeInOutQuad, - * // easeInCubic, easeOutCubic, easeInOutCubic, - * // easeInQuart, easeOutQuart, easeInOutQuart, - * // easeInQuint, easeOutQuint, easeInOutQuint - */ - Network.prototype.animateView = function (options) { - if (options === undefined) { - options = {}; - return; - } - - // release if something focussed on the node - this.releaseNode(); - if (options.locked == true) { - this.lockedOnNodeId = options.lockedOnNode; - this.lockedOnNodeOffset = options.offset; - } + Bargraph.getStackedBarYRange = function(barCombinedData, groupRanges, groupIds, groupLabel, orientation) { + if (barCombinedData.length > 0) { + // sort by time and by group + barCombinedData.sort(function (a, b) { + if (a.x == b.x) { + return a.groupId - b.groupId; + } else { + return a.x - b.x; + } + }); + var intersections = {}; - // forcefully complete the old animation if it was still running - if (this.easingTime != 0) { - this._transitionRedraw(1); // by setting easingtime to 1, we finish the animation. + Bargraph._getDataIntersections(intersections, barCombinedData); + groupRanges[groupLabel] = Bargraph._getStackedBarYRange(intersections, barCombinedData); + groupRanges[groupLabel].yAxisOrientation = orientation; + groupIds.push(groupLabel); } + } - this.sourceScale = this._getScale(); - this.sourceTranslation = this._getTranslation(); - this.targetScale = options.scale; - - // set the scale so the viewCenter is based on the correct zoom level. This is overridden in the transitionRedraw - // but at least then we'll have the target transition - this._setScale(this.targetScale); - var viewCenter = this.DOMtoCanvas({x: 0.5 * this.frame.canvas.clientWidth, y: 0.5 * this.frame.canvas.clientHeight}); - var distanceFromCenter = { // offset from view, distance view has to change by these x and y to center the node - x: viewCenter.x - options.position.x, - y: viewCenter.y - options.position.y - }; - this.targetTranslation = { - x: this.sourceTranslation.x + distanceFromCenter.x * this.targetScale + options.offset.x, - y: this.sourceTranslation.y + distanceFromCenter.y * this.targetScale + options.offset.y - }; - - // if the time is set to 0, don't do an animation - if (options.animation.duration == 0) { - if (this.lockedOnNodeId != null) { - this._classicRedraw = this._redraw; - this._redraw = this._lockedRedraw; + Bargraph._getStackedBarYRange = function (intersections, combinedData) { + var key; + var yMin = combinedData[0].y; + var yMax = combinedData[0].y; + for (var i = 0; i < combinedData.length; i++) { + key = combinedData[i].x; + if (intersections[key] === undefined) { + yMin = yMin > combinedData[i].y ? combinedData[i].y : yMin; + yMax = yMax < combinedData[i].y ? combinedData[i].y : yMax; } else { - this._setScale(this.targetScale); - this._setTranslation(this.targetTranslation.x, this.targetTranslation.y); - this._redraw(); + intersections[key].accumulated += combinedData[i].y; } } - else { - this.animating = true; - this.animationSpeed = 1 / (this.renderRefreshRate * options.animation.duration * 0.001) || 1 / this.renderRefreshRate; - this.animationEasingFunction = options.animation.easingFunction; - this._classicRedraw = this._redraw; - this._redraw = this._transitionRedraw; - this._redraw(); - this.start(); + for (var xpos in intersections) { + if (intersections.hasOwnProperty(xpos)) { + yMin = yMin > intersections[xpos].accumulated ? intersections[xpos].accumulated : yMin; + yMax = yMax < intersections[xpos].accumulated ? intersections[xpos].accumulated : yMax; + } } + + return {min: yMin, max: yMax}; }; + module.exports = Bargraph; + +/***/ }, +/* 51 */ +/***/ function(module, exports, __webpack_require__) { + /** - * used to animate smoothly by hijacking the redraw function. - * @private + * Created by Alex on 11/11/2014. */ - Network.prototype._lockedRedraw = function () { - var nodePosition = {x: this.nodes[this.lockedOnNodeId].x, y: this.nodes[this.lockedOnNodeId].y}; - var viewCenter = this.DOMtoCanvas({x: 0.5 * this.frame.canvas.clientWidth, y: 0.5 * this.frame.canvas.clientHeight}); - var distanceFromCenter = { // offset from view, distance view has to change by these x and y to center the node - x: viewCenter.x - nodePosition.x, - y: viewCenter.y - nodePosition.y - }; - var sourceTranslation = this._getTranslation(); - var targetTranslation = { - x: sourceTranslation.x + distanceFromCenter.x * this.scale + this.lockedOnNodeOffset.x, - y: sourceTranslation.y + distanceFromCenter.y * this.scale + this.lockedOnNodeOffset.y - }; + var DOMutil = __webpack_require__(2); - this._setTranslation(targetTranslation.x,targetTranslation.y); - this._classicRedraw(); + function Points(groupId, options) { + this.groupId = groupId; + this.options = options; } - Network.prototype.releaseNode = function () { - if (this.lockedOnNodeId != null) { - this._redraw = this._classicRedraw; - this.lockedOnNodeId = null; - this.lockedOnNodeOffset = null; + + Points.prototype.getYRange = function(groupData) { + var yMin = groupData[0].y; + var yMax = groupData[0].y; + for (var j = 0; j < groupData.length; j++) { + yMin = yMin > groupData[j].y ? groupData[j].y : yMin; + yMax = yMax < groupData[j].y ? groupData[j].y : yMax; } + return {min: yMin, max: yMax, yAxisOrientation: this.options.yAxisOrientation}; + }; + + Points.prototype.draw = function(dataset, group, framework, offset) { + Points.draw(dataset, group, framework, offset); } /** + * draw the data points * - * @param easingTime - * @private + * @param {Array} dataset + * @param {Object} JSONcontainer + * @param {Object} svg | SVG DOM element + * @param {GraphGroup} group + * @param {Number} [offset] */ - Network.prototype._transitionRedraw = function (easingTime) { - this.easingTime = easingTime || this.easingTime + this.animationSpeed; - this.easingTime += this.animationSpeed; + Points.draw = function (dataset, group, framework, offset) { + if (offset === undefined) {offset = 0;} + for (var i = 0; i < dataset.length; i++) { + DOMutil.drawPoint(dataset[i].x + offset, dataset[i].y, group, framework.svgElements, framework.svg); + } + }; - var progress = util.easingFunctions[this.animationEasingFunction](this.easingTime); - this._setScale(this.sourceScale + (this.targetScale - this.sourceScale) * progress); - this._setTranslation( - this.sourceTranslation.x + (this.targetTranslation.x - this.sourceTranslation.x) * progress, - this.sourceTranslation.y + (this.targetTranslation.y - this.sourceTranslation.y) * progress - ); + module.exports = Points; - this._classicRedraw(); +/***/ }, +/* 52 */ +/***/ function(module, exports, __webpack_require__) { - // cleanup - if (this.easingTime >= 1.0) { - this.animating = false; - this.easingTime = 0; - if (this.lockedOnNodeId != null) { - this._redraw = this._lockedRedraw; - } - else { - this._redraw = this._classicRedraw; + var PhysicsMixin = __webpack_require__(60); + var ClusterMixin = __webpack_require__(61); + var SectorsMixin = __webpack_require__(62); + var SelectionMixin = __webpack_require__(63); + var ManipulationMixin = __webpack_require__(64); + var NavigationMixin = __webpack_require__(65); + var HierarchicalLayoutMixin = __webpack_require__(66); + + /** + * Load a mixin into the network object + * + * @param {Object} sourceVariable | this object has to contain functions. + * @private + */ + exports._loadMixin = function (sourceVariable) { + for (var mixinFunction in sourceVariable) { + if (sourceVariable.hasOwnProperty(mixinFunction)) { + this[mixinFunction] = sourceVariable[mixinFunction]; } - this.emit("animationFinished"); } }; - Network.prototype._classicRedraw = function () { - // placeholder function to be overloaded by animations; - }; /** - * Returns true when the Network is active. - * @returns {boolean} + * removes a mixin from the network object. + * + * @param {Object} sourceVariable | this object has to contain functions. + * @private */ - Network.prototype.isActive = function () { - return !this.activator || this.activator.active; + exports._clearMixin = function (sourceVariable) { + for (var mixinFunction in sourceVariable) { + if (sourceVariable.hasOwnProperty(mixinFunction)) { + this[mixinFunction] = undefined; + } + } }; /** - * Sets the scale - * @returns {Number} + * Mixin the physics system and initialize the parameters required. + * + * @private */ - Network.prototype.setScale = function () { - return this._setScale(); + exports._loadPhysicsSystem = function () { + this._loadMixin(PhysicsMixin); + this._loadSelectedForceSolver(); + if (this.constants.configurePhysics == true) { + this._loadPhysicsConfiguration(); + } + else { + this._cleanupPhysicsConfiguration(); + } }; /** - * Returns the scale - * @returns {Number} + * Mixin the cluster system and initialize the parameters required. + * + * @private */ - Network.prototype.getScale = function () { - return this._getScale(); + exports._loadClusterSystem = function () { + this.clusterSession = 0; + this.hubThreshold = 5; + this._loadMixin(ClusterMixin); }; /** - * Returns the scale - * @returns {Number} + * Mixin the sector system and initialize the parameters required + * + * @private */ - Network.prototype.getCenterCoordinates = function () { - return this.DOMtoCanvas({x: 0.5 * this.frame.canvas.clientWidth, y: 0.5 * this.frame.canvas.clientHeight}); - }; - - - Network.prototype.getBoundingBox = function(nodeId) { - if (this.nodes[nodeId] !== undefined) { - return this.nodes[nodeId].boundingBox; - } - } + exports._loadSectorSystem = function () { + this.sectors = {}; + this.activeSector = ["default"]; + this.sectors["active"] = {}; + this.sectors["active"]["default"] = {"nodes": {}, + "edges": {}, + "nodeIndices": [], + "formationScale": 1.0, + "drawingNode": undefined }; + this.sectors["frozen"] = {}; + this.sectors["support"] = {"nodes": {}, + "edges": {}, + "nodeIndices": [], + "formationScale": 1.0, + "drawingNode": undefined }; - module.exports = Network; + this.nodeIndices = this.sectors["active"]["default"]["nodeIndices"]; // the node indices list is used to speed up the computation of the repulsion fields + this._loadMixin(SectorsMixin); + }; -/***/ }, -/* 52 */ -/***/ function(module, exports, __webpack_require__) { /** - * Parse a text source containing data in DOT language into a JSON object. - * The object contains two lists: one with nodes and one with edges. - * - * DOT language reference: http://www.graphviz.org/doc/info/lang.html + * Mixin the selection system and initialize the parameters required * - * @param {String} data Text containing a graph in DOT-notation - * @return {Object} graph An object containing two parameters: - * {Object[]} nodes - * {Object[]} edges + * @private */ - function parseDOT (data) { - dot = data; - return parseGraph(); - } + exports._loadSelectionSystem = function () { + this.selectionObj = {nodes: {}, edges: {}}; - // token types enumeration - var TOKENTYPE = { - NULL : 0, - DELIMITER : 1, - IDENTIFIER: 2, - UNKNOWN : 3 + this._loadMixin(SelectionMixin); }; - // map with all delimiters - var DELIMITERS = { - '{': true, - '}': true, - '[': true, - ']': true, - ';': true, - '=': true, - ',': true, - - '->': true, - '--': true - }; - - var dot = ''; // current dot file - var index = 0; // current index in dot file - var c = ''; // current token character in expr - var token = ''; // current token - var tokenType = TOKENTYPE.NULL; // type of the token - - /** - * Get the first character from the dot file. - * The character is stored into the char c. If the end of the dot file is - * reached, the function puts an empty string in c. - */ - function first() { - index = 0; - c = dot.charAt(0); - } - - /** - * Get the next character from the dot file. - * The character is stored into the char c. If the end of the dot file is - * reached, the function puts an empty string in c. - */ - function next() { - index++; - c = dot.charAt(index); - } - - /** - * Preview the next character from the dot file. - * @return {String} cNext - */ - function nextPreview() { - return dot.charAt(index + 1); - } - - /** - * Test whether given character is alphabetic or numeric - * @param {String} c - * @return {Boolean} isAlphaNumeric - */ - var regexAlphaNumeric = /[a-zA-Z_0-9.:#]/; - function isAlphaNumeric(c) { - return regexAlphaNumeric.test(c); - } /** - * Merge all properties of object b into object b - * @param {Object} a - * @param {Object} b - * @return {Object} a + * Mixin the navigationUI (User Interface) system and initialize the parameters required + * + * @private */ - function merge (a, b) { - if (!a) { - a = {}; - } + exports._loadManipulationSystem = function () { + // reset global variables -- these are used by the selection of nodes and edges. + this.blockConnectingEdgeSelection = false; + this.forceAppendSelection = false; - if (b) { - for (var name in b) { - if (b.hasOwnProperty(name)) { - a[name] = b[name]; + if (this.constants.dataManipulation.enabled == true) { + // load the manipulator HTML elements. All styling done in css. + if (this.manipulationDiv === undefined) { + this.manipulationDiv = document.createElement('div'); + this.manipulationDiv.className = 'network-manipulationDiv'; + if (this.editMode == true) { + this.manipulationDiv.style.display = "block"; } - } - } - return a; - } - - /** - * Set a value in an object, where the provided parameter name can be a - * path with nested parameters. For example: - * - * var obj = {a: 2}; - * setValue(obj, 'b.c', 3); // obj = {a: 2, b: {c: 3}} - * - * @param {Object} obj - * @param {String} path A parameter name or dot-separated parameter path, - * like "color.highlight.border". - * @param {*} value - */ - function setValue(obj, path, value) { - var keys = path.split('.'); - var o = obj; - while (keys.length) { - var key = keys.shift(); - if (keys.length) { - // this isn't the end point - if (!o[key]) { - o[key] = {}; + else { + this.manipulationDiv.style.display = "none"; } - o = o[key]; - } - else { - // this is the end point - o[key] = value; + this.frame.appendChild(this.manipulationDiv); } - } - } - - /** - * Add a node to a graph object. If there is already a node with - * the same id, their attributes will be merged. - * @param {Object} graph - * @param {Object} node - */ - function addNode(graph, node) { - var i, len; - var current = null; - // find root graph (in case of subgraph) - var graphs = [graph]; // list with all graphs from current graph to root graph - var root = graph; - while (root.parent) { - graphs.push(root.parent); - root = root.parent; - } - - // find existing node (at root level) by its id - if (root.nodes) { - for (i = 0, len = root.nodes.length; i < len; i++) { - if (node.id === root.nodes[i].id) { - current = root.nodes[i]; - break; + if (this.editModeDiv === undefined) { + this.editModeDiv = document.createElement('div'); + this.editModeDiv.className = 'network-manipulation-editMode'; + if (this.editMode == true) { + this.editModeDiv.style.display = "none"; + } + else { + this.editModeDiv.style.display = "block"; } + this.frame.appendChild(this.editModeDiv); } - } - if (!current) { - // this is a new node - current = { - id: node.id - }; - if (graph.node) { - // clone default attributes - current.attr = merge(current.attr, graph.node); + if (this.closeDiv === undefined) { + this.closeDiv = document.createElement('div'); + this.closeDiv.className = 'network-manipulation-closeDiv'; + this.closeDiv.style.display = this.manipulationDiv.style.display; + this.frame.appendChild(this.closeDiv); } + + // load the manipulation functions + this._loadMixin(ManipulationMixin); + + // create the manipulator toolbar + this._createManipulatorBar(); } + else { + if (this.manipulationDiv !== undefined) { + // removes all the bindings and overloads + this._createManipulatorBar(); - // add node to this (sub)graph and all its parent graphs - for (i = graphs.length - 1; i >= 0; i--) { - var g = graphs[i]; + // remove the manipulation divs + this.frame.removeChild(this.manipulationDiv); + this.frame.removeChild(this.editModeDiv); + this.frame.removeChild(this.closeDiv); - if (!g.nodes) { - g.nodes = []; - } - if (g.nodes.indexOf(current) == -1) { - g.nodes.push(current); + this.manipulationDiv = undefined; + this.editModeDiv = undefined; + this.closeDiv = undefined; + // remove the mixin functions + this._clearMixin(ManipulationMixin); } } + }; - // merge attributes - if (node.attr) { - current.attr = merge(current.attr, node.attr); - } - } /** - * Add an edge to a graph object - * @param {Object} graph - * @param {Object} edge + * Mixin the navigation (User Interface) system and initialize the parameters required + * + * @private */ - function addEdge(graph, edge) { - if (!graph.edges) { - graph.edges = []; - } - graph.edges.push(edge); - if (graph.edge) { - var attr = merge({}, graph.edge); // clone default attributes - edge.attr = merge(attr, edge.attr); // merge attributes + exports._loadNavigationControls = function () { + this._loadMixin(NavigationMixin); + // the clean function removes the button divs, this is done to remove the bindings. + this._cleanNavigation(); + if (this.constants.navigation.enabled == true) { + this._loadNavigationElements(); } - } + }; + /** - * Create an edge to a graph object - * @param {Object} graph - * @param {String | Number | Object} from - * @param {String | Number | Object} to - * @param {String} type - * @param {Object | null} attr - * @return {Object} edge + * Mixin the hierarchical layout system. + * + * @private */ - function createEdge(graph, from, to, type, attr) { - var edge = { - from: from, - to: to, - type: type - }; + exports._loadHierarchySystem = function () { + this._loadMixin(HierarchicalLayoutMixin); + }; - if (graph.edge) { - edge.attr = merge({}, graph.edge); // clone default attributes - } - edge.attr = merge(edge.attr || {}, attr); // merge attributes - return edge; - } +/***/ }, +/* 53 */ +/***/ function(module, exports, __webpack_require__) { + + var keycharm = __webpack_require__(57); + var Emitter = __webpack_require__(56); + var Hammer = __webpack_require__(45); + var util = __webpack_require__(1); /** - * Get next token in the current dot file. - * The token and token type are available as token and tokenType + * Turn an element into an clickToUse element. + * When not active, the element has a transparent overlay. When the overlay is + * clicked, the mode is changed to active. + * When active, the element is displayed with a blue border around it, and + * the interactive contents of the element can be used. When clicked outside + * the element, the elements mode is changed to inactive. + * @param {Element} container + * @constructor */ - function getToken() { - tokenType = TOKENTYPE.NULL; - token = ''; - - // skip over whitespaces - while (c == ' ' || c == '\t' || c == '\n' || c == '\r') { // space, tab, enter - next(); - } - - do { - var isComment = false; - - // skip comment - if (c == '#') { - // find the previous non-space character - var i = index - 1; - while (dot.charAt(i) == ' ' || dot.charAt(i) == '\t') { - i--; - } - if (dot.charAt(i) == '\n' || dot.charAt(i) == '') { - // the # is at the start of a line, this is indeed a line comment - while (c != '' && c != '\n') { - next(); - } - isComment = true; - } - } - if (c == '/' && nextPreview() == '/') { - // skip line comment - while (c != '' && c != '\n') { - next(); - } - isComment = true; - } - if (c == '/' && nextPreview() == '*') { - // skip block comment - while (c != '') { - if (c == '*' && nextPreview() == '/') { - // end of block comment found. skip these last two characters - next(); - next(); - break; - } - else { - next(); - } - } - isComment = true; - } + function Activator(container) { + this.active = false; - // skip over whitespaces - while (c == ' ' || c == '\t' || c == '\n' || c == '\r') { // space, tab, enter - next(); - } - } - while (isComment); + this.dom = { + container: container + }; - // check for end of dot file - if (c == '') { - // token is still empty - tokenType = TOKENTYPE.DELIMITER; - return; - } + this.dom.overlay = document.createElement('div'); + this.dom.overlay.className = 'overlay'; - // check for delimiters consisting of 2 characters - var c2 = c + nextPreview(); - if (DELIMITERS[c2]) { - tokenType = TOKENTYPE.DELIMITER; - token = c2; - next(); - next(); - return; - } + this.dom.container.appendChild(this.dom.overlay); - // check for delimiters consisting of 1 character - if (DELIMITERS[c]) { - tokenType = TOKENTYPE.DELIMITER; - token = c; - next(); - return; - } + this.hammer = Hammer(this.dom.overlay, {prevent_default: false}); + this.hammer.on('tap', this._onTapOverlay.bind(this)); - // check for an identifier (number or string) - // TODO: more precise parsing of numbers/strings (and the port separator ':') - if (isAlphaNumeric(c) || c == '-') { - token += c; - next(); + // block all touch events (except tap) + var me = this; + var events = [ + 'touch', 'pinch', + 'doubletap', 'hold', + 'dragstart', 'drag', 'dragend', + 'mousewheel', 'DOMMouseScroll' // DOMMouseScroll is needed for Firefox + ]; + events.forEach(function (event) { + me.hammer.on(event, function (event) { + event.stopPropagation(); + }); + }); - while (isAlphaNumeric(c)) { - token += c; - next(); - } - if (token == 'false') { - token = false; // convert to boolean - } - else if (token == 'true') { - token = true; // convert to boolean - } - else if (!isNaN(Number(token))) { - token = Number(token); // convert to number + // attach a tap event to the window, in order to deactivate when clicking outside the timeline + this.windowHammer = Hammer(window, {prevent_default: false}); + this.windowHammer.on('tap', function (event) { + // deactivate when clicked outside the container + if (!_hasParent(event.target, container)) { + me.deactivate(); } - tokenType = TOKENTYPE.IDENTIFIER; - return; - } + }); - // check for a string enclosed by double quotes - if (c == '"') { - next(); - while (c != '' && (c != '"' || (c == '"' && nextPreview() == '"'))) { - token += c; - if (c == '"') { // skip the escape character - next(); - } - next(); - } - if (c != '"') { - throw newSyntaxError('End of string " expected'); - } - next(); - tokenType = TOKENTYPE.IDENTIFIER; - return; + if (this.keycharm !== undefined) { + this.keycharm.destroy(); } + this.keycharm = keycharm(); - // something unknown is found, wrong characters, a syntax error - tokenType = TOKENTYPE.UNKNOWN; - while (c != '') { - token += c; - next(); - } - throw new SyntaxError('Syntax error in part "' + chop(token, 30) + '"'); + // keycharm listener only bounded when active) + this.escListener = this.deactivate.bind(this); } - /** - * Parse a graph. - * @returns {Object} graph - */ - function parseGraph() { - var graph = {}; + // turn into an event emitter + Emitter(Activator.prototype); - first(); - getToken(); + // The currently active activator + Activator.current = null; - // optional strict keyword - if (token == 'strict') { - graph.strict = true; - getToken(); - } + /** + * Destroy the activator. Cleans up all created DOM and event listeners + */ + Activator.prototype.destroy = function () { + this.deactivate(); - // graph or digraph keyword - if (token == 'graph' || token == 'digraph') { - graph.type = token; - getToken(); - } + // remove dom + this.dom.overlay.parentNode.removeChild(this.dom.overlay); - // optional graph id - if (tokenType == TOKENTYPE.IDENTIFIER) { - graph.id = token; - getToken(); - } + // cleanup hammer instances + this.hammer = null; + this.windowHammer = null; + // FIXME: cleaning up hammer instances doesn't work (Timeline not removed from memory) + }; - // open angle bracket - if (token != '{') { - throw newSyntaxError('Angle bracket { expected'); + /** + * Activate the element + * Overlay is hidden, element is decorated with a blue shadow border + */ + Activator.prototype.activate = function () { + // we allow only one active activator at a time + if (Activator.current) { + Activator.current.deactivate(); } - getToken(); + Activator.current = this; - // statements - parseStatements(graph); + this.active = true; + this.dom.overlay.style.display = 'none'; + util.addClassName(this.dom.container, 'vis-active'); - // close angle bracket - if (token != '}') { - throw newSyntaxError('Angle bracket } expected'); - } - getToken(); + this.emit('change'); + this.emit('activate'); - // end of file - if (token !== '') { - throw newSyntaxError('End of file expected'); - } - getToken(); + // ugly hack: bind ESC after emitting the events, as the Network rebinds all + // keyboard events on a 'change' event + this.keycharm.bind('esc', this.escListener); + }; - // remove temporary default properties - delete graph.node; - delete graph.edge; - delete graph.graph; + /** + * Deactivate the element + * Overlay is displayed on top of the element + */ + Activator.prototype.deactivate = function () { + this.active = false; + this.dom.overlay.style.display = ''; + util.removeClassName(this.dom.container, 'vis-active'); + this.keycharm.unbind('esc', this.escListener); - return graph; - } + this.emit('change'); + this.emit('deactivate'); + }; /** - * Parse a list with statements. - * @param {Object} graph + * Handle a tap event: activate the container + * @param event + * @private */ - function parseStatements (graph) { - while (token !== '' && token != '}') { - parseStatement(graph); - if (token == ';') { - getToken(); - } - } - } + Activator.prototype._onTapOverlay = function (event) { + // activate the container + this.activate(); + event.stopPropagation(); + }; /** - * Parse a single statement. Can be a an attribute statement, node - * statement, a series of node statements and edge statements, or a - * parameter. - * @param {Object} graph + * Test whether the element has the requested parent element somewhere in + * its chain of parent nodes. + * @param {HTMLElement} element + * @param {HTMLElement} parent + * @returns {boolean} Returns true when the parent is found somewhere in the + * chain of parent nodes. + * @private */ - function parseStatement(graph) { - // parse subgraph - var subgraph = parseSubgraph(graph); - if (subgraph) { - // edge statements - parseEdge(graph, subgraph); - - return; + function _hasParent(element, parent) { + while (element) { + if (element === parent) { + return true + } + element = element.parentNode; } + return false; + } - // parse an attribute statement - var attr = parseAttributeStatement(graph); - if (attr) { - return; - } + module.exports = Activator; - // parse node - if (tokenType != TOKENTYPE.IDENTIFIER) { - throw newSyntaxError('Identifier expected'); - } - var id = token; // id can be a string or a number - getToken(); - if (token == '=') { - // id statement - getToken(); - if (tokenType != TOKENTYPE.IDENTIFIER) { - throw newSyntaxError('Identifier expected'); - } - graph[id] = token; - getToken(); - // TODO: implement comma separated list with "a_list: ID=ID [','] [a_list] " - } - else { - parseNodeStatement(graph, id); - } - } +/***/ }, +/* 54 */ +/***/ function(module, exports, __webpack_require__) { + + // English + exports['en'] = { + edit: 'Edit', + del: 'Delete selected', + back: 'Back', + addNode: 'Add Node', + addEdge: 'Add Edge', + editNode: 'Edit Node', + editEdge: 'Edit Edge', + addDescription: 'Click in an empty space to place a new node.', + edgeDescription: 'Click on a node and drag the edge to another node to connect them.', + editEdgeDescription: 'Click on the control points and drag them to a node to connect to it.', + createEdgeError: 'Cannot link edges to a cluster.', + deleteClusterError: 'Clusters cannot be deleted.' + }; + exports['en_EN'] = exports['en']; + exports['en_US'] = exports['en']; + + // Dutch + exports['nl'] = { + edit: 'Wijzigen', + del: 'Selectie verwijderen', + back: 'Terug', + addNode: 'Node toevoegen', + addEdge: 'Link toevoegen', + editNode: 'Node wijzigen', + editEdge: 'Link wijzigen', + addDescription: 'Klik op een leeg gebied om een nieuwe node te maken.', + edgeDescription: 'Klik op een node en sleep de link naar een andere node om ze te verbinden.', + editEdgeDescription: 'Klik op de verbindingspunten en sleep ze naar een node om daarmee te verbinden.', + createEdgeError: 'Kan geen link maken naar een cluster.', + deleteClusterError: 'Clusters kunnen niet worden verwijderd.' + }; + exports['nl_NL'] = exports['nl']; + exports['nl_BE'] = exports['nl']; + + +/***/ }, +/* 55 */ +/***/ function(module, exports, __webpack_require__) { /** - * Parse a subgraph - * @param {Object} graph parent graph object - * @return {Object | null} subgraph + * Canvas shapes used by Network */ - function parseSubgraph (graph) { - var subgraph = null; + if (typeof CanvasRenderingContext2D !== 'undefined') { - // optional subgraph keyword - if (token == 'subgraph') { - subgraph = {}; - subgraph.type = 'subgraph'; - getToken(); + /** + * Draw a circle shape + */ + CanvasRenderingContext2D.prototype.circle = function(x, y, r) { + this.beginPath(); + this.arc(x, y, r, 0, 2*Math.PI, false); + }; - // optional graph id - if (tokenType == TOKENTYPE.IDENTIFIER) { - subgraph.id = token; - getToken(); - } - } + /** + * Draw a square shape + * @param {Number} x horizontal center + * @param {Number} y vertical center + * @param {Number} r size, width and height of the square + */ + CanvasRenderingContext2D.prototype.square = function(x, y, r) { + this.beginPath(); + this.rect(x - r, y - r, r * 2, r * 2); + }; - // open angle bracket - if (token == '{') { - getToken(); + /** + * Draw a triangle shape + * @param {Number} x horizontal center + * @param {Number} y vertical center + * @param {Number} r radius, half the length of the sides of the triangle + */ + CanvasRenderingContext2D.prototype.triangle = function(x, y, r) { + // http://en.wikipedia.org/wiki/Equilateral_triangle + this.beginPath(); - if (!subgraph) { - subgraph = {}; - } - subgraph.parent = graph; - subgraph.node = graph.node; - subgraph.edge = graph.edge; - subgraph.graph = graph.graph; + var s = r * 2; + var s2 = s / 2; + var ir = Math.sqrt(3) / 6 * s; // radius of inner circle + var h = Math.sqrt(s * s - s2 * s2); // height - // statements - parseStatements(subgraph); + this.moveTo(x, y - (h - ir)); + this.lineTo(x + s2, y + ir); + this.lineTo(x - s2, y + ir); + this.lineTo(x, y - (h - ir)); + this.closePath(); + }; - // close angle bracket - if (token != '}') { - throw newSyntaxError('Angle bracket } expected'); - } - getToken(); + /** + * Draw a triangle shape in downward orientation + * @param {Number} x horizontal center + * @param {Number} y vertical center + * @param {Number} r radius + */ + CanvasRenderingContext2D.prototype.triangleDown = function(x, y, r) { + // http://en.wikipedia.org/wiki/Equilateral_triangle + this.beginPath(); - // remove temporary default properties - delete subgraph.node; - delete subgraph.edge; - delete subgraph.graph; - delete subgraph.parent; + var s = r * 2; + var s2 = s / 2; + var ir = Math.sqrt(3) / 6 * s; // radius of inner circle + var h = Math.sqrt(s * s - s2 * s2); // height - // register at the parent graph - if (!graph.subgraphs) { - graph.subgraphs = []; + this.moveTo(x, y + (h - ir)); + this.lineTo(x + s2, y - ir); + this.lineTo(x - s2, y - ir); + this.lineTo(x, y + (h - ir)); + this.closePath(); + }; + + /** + * Draw a star shape, a star with 5 points + * @param {Number} x horizontal center + * @param {Number} y vertical center + * @param {Number} r radius, half the length of the sides of the triangle + */ + CanvasRenderingContext2D.prototype.star = function(x, y, r) { + // http://www.html5canvastutorials.com/labs/html5-canvas-star-spinner/ + this.beginPath(); + + for (var n = 0; n < 10; n++) { + var radius = (n % 2 === 0) ? r * 1.3 : r * 0.5; + this.lineTo( + x + radius * Math.sin(n * 2 * Math.PI / 10), + y - radius * Math.cos(n * 2 * Math.PI / 10) + ); } - graph.subgraphs.push(subgraph); - } - return subgraph; - } + this.closePath(); + }; - /** - * parse an attribute statement like "node [shape=circle fontSize=16]". - * Available keywords are 'node', 'edge', 'graph'. - * The previous list with default attributes will be replaced - * @param {Object} graph - * @returns {String | null} keyword Returns the name of the parsed attribute - * (node, edge, graph), or null if nothing - * is parsed. - */ - function parseAttributeStatement (graph) { - // attribute statements - if (token == 'node') { - getToken(); + /** + * http://stackoverflow.com/questions/1255512/how-to-draw-a-rounded-rectangle-on-html-canvas + */ + CanvasRenderingContext2D.prototype.roundRect = function(x, y, w, h, r) { + var r2d = Math.PI/180; + if( w - ( 2 * r ) < 0 ) { r = ( w / 2 ); } //ensure that the radius isn't too large for x + if( h - ( 2 * r ) < 0 ) { r = ( h / 2 ); } //ensure that the radius isn't too large for y + this.beginPath(); + this.moveTo(x+r,y); + this.lineTo(x+w-r,y); + this.arc(x+w-r,y+r,r,r2d*270,r2d*360,false); + this.lineTo(x+w,y+h-r); + this.arc(x+w-r,y+h-r,r,0,r2d*90,false); + this.lineTo(x+r,y+h); + this.arc(x+r,y+h-r,r,r2d*90,r2d*180,false); + this.lineTo(x,y+r); + this.arc(x+r,y+r,r,r2d*180,r2d*270,false); + }; - // node attributes - graph.node = parseAttributeList(); - return 'node'; - } - else if (token == 'edge') { - getToken(); + /** + * http://stackoverflow.com/questions/2172798/how-to-draw-an-oval-in-html5-canvas + */ + CanvasRenderingContext2D.prototype.ellipse = function(x, y, w, h) { + var kappa = .5522848, + ox = (w / 2) * kappa, // control point offset horizontal + oy = (h / 2) * kappa, // control point offset vertical + xe = x + w, // x-end + ye = y + h, // y-end + xm = x + w / 2, // x-middle + ym = y + h / 2; // y-middle - // edge attributes - graph.edge = parseAttributeList(); - return 'edge'; - } - else if (token == 'graph') { - getToken(); + this.beginPath(); + this.moveTo(x, ym); + this.bezierCurveTo(x, ym - oy, xm - ox, y, xm, y); + this.bezierCurveTo(xm + ox, y, xe, ym - oy, xe, ym); + this.bezierCurveTo(xe, ym + oy, xm + ox, ye, xm, ye); + this.bezierCurveTo(xm - ox, ye, x, ym + oy, x, ym); + }; - // graph attributes - graph.graph = parseAttributeList(); - return 'graph'; - } - return null; - } - /** - * parse a node statement - * @param {Object} graph - * @param {String | Number} id - */ - function parseNodeStatement(graph, id) { - // node statement - var node = { - id: id - }; - var attr = parseAttributeList(); - if (attr) { - node.attr = attr; - } - addNode(graph, node); + /** + * http://stackoverflow.com/questions/2172798/how-to-draw-an-oval-in-html5-canvas + */ + CanvasRenderingContext2D.prototype.database = function(x, y, w, h) { + var f = 1/3; + var wEllipse = w; + var hEllipse = h * f; - // edge statements - parseEdge(graph, id); - } + var kappa = .5522848, + ox = (wEllipse / 2) * kappa, // control point offset horizontal + oy = (hEllipse / 2) * kappa, // control point offset vertical + xe = x + wEllipse, // x-end + ye = y + hEllipse, // y-end + xm = x + wEllipse / 2, // x-middle + ym = y + hEllipse / 2, // y-middle + ymb = y + (h - hEllipse/2), // y-midlle, bottom ellipse + yeb = y + h; // y-end, bottom ellipse - /** - * Parse an edge or a series of edges - * @param {Object} graph - * @param {String | Number} from Id of the from node - */ - function parseEdge(graph, from) { - while (token == '->' || token == '--') { - var to; - var type = token; - getToken(); + this.beginPath(); + this.moveTo(xe, ym); - var subgraph = parseSubgraph(graph); - if (subgraph) { - to = subgraph; - } - else { - if (tokenType != TOKENTYPE.IDENTIFIER) { - throw newSyntaxError('Identifier or subgraph expected'); - } - to = token; - addNode(graph, { - id: to - }); - getToken(); - } + this.bezierCurveTo(xe, ym + oy, xm + ox, ye, xm, ye); + this.bezierCurveTo(xm - ox, ye, x, ym + oy, x, ym); - // parse edge attributes - var attr = parseAttributeList(); + this.bezierCurveTo(x, ym - oy, xm - ox, y, xm, y); + this.bezierCurveTo(xm + ox, y, xe, ym - oy, xe, ym); - // create edge - var edge = createEdge(graph, from, to, type, attr); - addEdge(graph, edge); + this.lineTo(xe, ymb); - from = to; - } - } + this.bezierCurveTo(xe, ymb + oy, xm + ox, yeb, xm, yeb); + this.bezierCurveTo(xm - ox, yeb, x, ymb + oy, x, ymb); - /** - * Parse a set with attributes, - * for example [label="1.000", shape=solid] - * @return {Object | null} attr - */ - function parseAttributeList() { - var attr = null; + this.lineTo(x, ym); + }; - while (token == '[') { - getToken(); - attr = {}; - while (token !== '' && token != ']') { - if (tokenType != TOKENTYPE.IDENTIFIER) { - throw newSyntaxError('Attribute name expected'); - } - var name = token; - getToken(); - if (token != '=') { - throw newSyntaxError('Equal sign = expected'); - } - getToken(); + /** + * Draw an arrow point (no line) + */ + CanvasRenderingContext2D.prototype.arrow = function(x, y, angle, length) { + // tail + var xt = x - length * Math.cos(angle); + var yt = y - length * Math.sin(angle); - if (tokenType != TOKENTYPE.IDENTIFIER) { - throw newSyntaxError('Attribute value expected'); - } - var value = token; - setValue(attr, name, value); // name can be a path + // inner tail + // TODO: allow to customize different shapes + var xi = x - length * 0.9 * Math.cos(angle); + var yi = y - length * 0.9 * Math.sin(angle); - getToken(); - if (token ==',') { - getToken(); - } - } + // left + var xl = xt + length / 3 * Math.cos(angle + 0.5 * Math.PI); + var yl = yt + length / 3 * Math.sin(angle + 0.5 * Math.PI); - if (token != ']') { - throw newSyntaxError('Bracket ] expected'); + // right + var xr = xt + length / 3 * Math.cos(angle - 0.5 * Math.PI); + var yr = yt + length / 3 * Math.sin(angle - 0.5 * Math.PI); + + this.beginPath(); + this.moveTo(x, y); + this.lineTo(xl, yl); + this.lineTo(xi, yi); + this.lineTo(xr, yr); + this.closePath(); + }; + + /** + * Sets up the dashedLine functionality for drawing + * Original code came from http://stackoverflow.com/questions/4576724/dotted-stroke-in-canvas + * @author David Jordan + * @date 2012-08-08 + */ + CanvasRenderingContext2D.prototype.dashedLine = function(x,y,x2,y2,dashArray){ + if (!dashArray) dashArray=[10,5]; + if (dashLength==0) dashLength = 0.001; // Hack for Safari + var dashCount = dashArray.length; + this.moveTo(x, y); + var dx = (x2-x), dy = (y2-y); + var slope = dy/dx; + var distRemaining = Math.sqrt( dx*dx + dy*dy ); + var dashIndex=0, draw=true; + while (distRemaining>=0.1){ + var dashLength = dashArray[dashIndex++%dashCount]; + if (dashLength > distRemaining) dashLength = distRemaining; + var xStep = Math.sqrt( dashLength*dashLength / (1 + slope*slope) ); + if (dx<0) xStep = -xStep; + x += xStep; + y += slope*xStep; + this[draw ? 'lineTo' : 'moveTo'](x,y); + distRemaining -= dashLength; + draw = !draw; } - getToken(); - } + }; - return attr; + // TODO: add diamond shape } + +/***/ }, +/* 56 */ +/***/ function(module, exports, __webpack_require__) { + + /** - * Create a syntax error with extra information on current token and index. - * @param {String} message - * @returns {SyntaxError} err + * Expose `Emitter`. */ - function newSyntaxError(message) { - return new SyntaxError(message + ', got "' + chop(token, 30) + '" (char ' + index + ')'); - } + + module.exports = Emitter; /** - * Chop off text after a maximum length - * @param {String} text - * @param {Number} maxLength - * @returns {String} + * Initialize a new `Emitter`. + * + * @api public */ - function chop (text, maxLength) { - return (text.length <= maxLength) ? text : (text.substr(0, 27) + '...'); - } + + function Emitter(obj) { + if (obj) return mixin(obj); + }; /** - * Execute a function fn for each pair of elements in two arrays - * @param {Array | *} array1 - * @param {Array | *} array2 - * @param {function} fn + * Mixin the emitter properties. + * + * @param {Object} obj + * @return {Object} + * @api private */ - function forEach2(array1, array2, fn) { - if (Array.isArray(array1)) { - array1.forEach(function (elem1) { - if (Array.isArray(array2)) { - array2.forEach(function (elem2) { - fn(elem1, elem2); - }); - } - else { - fn(elem1, array2); - } - }); - } - else { - if (Array.isArray(array2)) { - array2.forEach(function (elem2) { - fn(array1, elem2); - }); - } - else { - fn(array1, array2); - } + + function mixin(obj) { + for (var key in Emitter.prototype) { + obj[key] = Emitter.prototype[key]; } + return obj; } /** - * Convert a string containing a graph in DOT language into a map containing - * with nodes and edges in the format of graph. - * @param {String} data Text containing a graph in DOT-notation - * @return {Object} graphData + * Listen on the given `event` with `fn`. + * + * @param {String} event + * @param {Function} fn + * @return {Emitter} + * @api public */ - function DOTToGraph (data) { - // parse the DOT file - var dotData = parseDOT(data); - var graphData = { - nodes: [], - edges: [], - options: {} - }; - - // copy the nodes - if (dotData.nodes) { - dotData.nodes.forEach(function (dotNode) { - var graphNode = { - id: dotNode.id, - label: String(dotNode.label || dotNode.id) - }; - merge(graphNode, dotNode.attr); - if (graphNode.image) { - graphNode.shape = 'image'; - } - graphData.nodes.push(graphNode); - }); - } - - // copy the edges - if (dotData.edges) { - /** - * Convert an edge in DOT format to an edge with VisGraph format - * @param {Object} dotEdge - * @returns {Object} graphEdge - */ - var convertEdge = function (dotEdge) { - var graphEdge = { - from: dotEdge.from, - to: dotEdge.to - }; - merge(graphEdge, dotEdge.attr); - graphEdge.style = (dotEdge.type == '->') ? 'arrow' : 'line'; - return graphEdge; - } - - dotData.edges.forEach(function (dotEdge) { - var from, to; - if (dotEdge.from instanceof Object) { - from = dotEdge.from.nodes; - } - else { - from = { - id: dotEdge.from - } - } - - if (dotEdge.to instanceof Object) { - to = dotEdge.to.nodes; - } - else { - to = { - id: dotEdge.to - } - } - if (dotEdge.from instanceof Object && dotEdge.from.edges) { - dotEdge.from.edges.forEach(function (subEdge) { - var graphEdge = convertEdge(subEdge); - graphData.edges.push(graphEdge); - }); - } + Emitter.prototype.on = + Emitter.prototype.addEventListener = function(event, fn){ + this._callbacks = this._callbacks || {}; + (this._callbacks[event] = this._callbacks[event] || []) + .push(fn); + return this; + }; - forEach2(from, to, function (from, to) { - var subEdge = createEdge(graphData, from.id, to.id, dotEdge.type, dotEdge.attr); - var graphEdge = convertEdge(subEdge); - graphData.edges.push(graphEdge); - }); + /** + * Adds an `event` listener that will be invoked a single + * time then automatically removed. + * + * @param {String} event + * @param {Function} fn + * @return {Emitter} + * @api public + */ - if (dotEdge.to instanceof Object && dotEdge.to.edges) { - dotEdge.to.edges.forEach(function (subEdge) { - var graphEdge = convertEdge(subEdge); - graphData.edges.push(graphEdge); - }); - } - }); - } + Emitter.prototype.once = function(event, fn){ + var self = this; + this._callbacks = this._callbacks || {}; - // copy the options - if (dotData.attr) { - graphData.options = dotData.attr; + function on() { + self.off(event, on); + fn.apply(this, arguments); } - return graphData; - } - - // exports - exports.parseDOT = parseDOT; - exports.DOTToGraph = DOTToGraph; - + on.fn = fn; + this.on(event, on); + return this; + }; -/***/ }, -/* 53 */ -/***/ function(module, exports, __webpack_require__) { + /** + * Remove the given callback for `event` or all + * registered callbacks. + * + * @param {String} event + * @param {Function} fn + * @return {Emitter} + * @api public + */ - - function parseGephi(gephiJSON, options) { - var edges = []; - var nodes = []; - this.options = { - edges: { - inheritColor: true - }, - nodes: { - allowedToMove: false, - parseColor: false - } - }; + Emitter.prototype.off = + Emitter.prototype.removeListener = + Emitter.prototype.removeAllListeners = + Emitter.prototype.removeEventListener = function(event, fn){ + this._callbacks = this._callbacks || {}; - if (options !== undefined) { - this.options.nodes['allowedToMove'] = options.allowedToMove | false; - this.options.nodes['parseColor'] = options.parseColor | false; - this.options.edges['inheritColor'] = options.inheritColor | true; + // all + if (0 == arguments.length) { + this._callbacks = {}; + return this; } - var gEdges = gephiJSON.edges; - var gNodes = gephiJSON.nodes; - for (var i = 0; i < gEdges.length; i++) { - var edge = {}; - var gEdge = gEdges[i]; - edge['id'] = gEdge.id; - edge['from'] = gEdge.source; - edge['to'] = gEdge.target; - edge['attributes'] = gEdge.attributes; - // edge['value'] = gEdge.attributes !== undefined ? gEdge.attributes.Weight : undefined; - // edge['width'] = edge['value'] !== undefined ? undefined : edgegEdge.size; - edge['color'] = gEdge.color; - edge['inheritColor'] = edge['color'] !== undefined ? false : this.options.inheritColor; - edges.push(edge); + // specific event + var callbacks = this._callbacks[event]; + if (!callbacks) return this; + + // remove all handlers + if (1 == arguments.length) { + delete this._callbacks[event]; + return this; } - for (var i = 0; i < gNodes.length; i++) { - var node = {}; - var gNode = gNodes[i]; - node['id'] = gNode.id; - node['attributes'] = gNode.attributes; - node['x'] = gNode.x; - node['y'] = gNode.y; - node['label'] = gNode.label; - if (this.options.nodes.parseColor == true) { - node['color'] = gNode.color; - } - else { - node['color'] = gNode.color !== undefined ? {background:gNode.color, border:gNode.color} : undefined; + // 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; } - node['radius'] = gNode.size; - node['allowedToMoveX'] = this.options.nodes.allowedToMove; - node['allowedToMoveY'] = this.options.nodes.allowedToMove; - nodes.push(node); } - - return {nodes:nodes, edges:edges}; - } - - exports.parseGephi = parseGephi; - -/***/ }, -/* 54 */ -/***/ function(module, exports, __webpack_require__) { - - var util = __webpack_require__(1); - - /** - * @class Groups - * This class can store groups and properties specific for groups. - */ - function Groups() { - this.clear(); - this.defaultIndex = 0; - } - + return this; + }; /** - * default constants for group colors + * Emit `event` with the given args. + * + * @param {String} event + * @param {Mixed} ... + * @return {Emitter} */ - Groups.DEFAULT = [ - {border: "#2B7CE9", background: "#97C2FC", highlight: {border: "#2B7CE9", background: "#D2E5FF"}, hover: {border: "#2B7CE9", background: "#D2E5FF"}}, // blue - {border: "#FFA500", background: "#FFFF00", highlight: {border: "#FFA500", background: "#FFFFA3"}, hover: {border: "#FFA500", background: "#FFFFA3"}}, // yellow - {border: "#FA0A10", background: "#FB7E81", highlight: {border: "#FA0A10", background: "#FFAFB1"}, hover: {border: "#FA0A10", background: "#FFAFB1"}}, // red - {border: "#41A906", background: "#7BE141", highlight: {border: "#41A906", background: "#A1EC76"}, hover: {border: "#41A906", background: "#A1EC76"}}, // green - {border: "#E129F0", background: "#EB7DF4", highlight: {border: "#E129F0", background: "#F0B3F5"}, hover: {border: "#E129F0", background: "#F0B3F5"}}, // magenta - {border: "#7C29F0", background: "#AD85E4", highlight: {border: "#7C29F0", background: "#D3BDF0"}, hover: {border: "#7C29F0", background: "#D3BDF0"}}, // purple - {border: "#C37F00", background: "#FFA807", highlight: {border: "#C37F00", background: "#FFCA66"}, hover: {border: "#C37F00", background: "#FFCA66"}}, // orange - {border: "#4220FB", background: "#6E6EFD", highlight: {border: "#4220FB", background: "#9B9BFD"}, hover: {border: "#4220FB", background: "#9B9BFD"}}, // darkblue - {border: "#FD5A77", background: "#FFC0CB", highlight: {border: "#FD5A77", background: "#FFD1D9"}, hover: {border: "#FD5A77", background: "#FFD1D9"}}, // pink - {border: "#4AD63A", background: "#C2FABC", highlight: {border: "#4AD63A", background: "#E6FFE3"}, hover: {border: "#4AD63A", background: "#E6FFE3"}} // mint - ]; + Emitter.prototype.emit = function(event){ + this._callbacks = this._callbacks || {}; + var args = [].slice.call(arguments, 1) + , callbacks = this._callbacks[event]; - /** - * Clear all groups - */ - Groups.prototype.clear = function () { - this.groups = {}; - this.groups.length = function() - { - var i = 0; - for ( var p in this ) { - if (this.hasOwnProperty(p)) { - i++; - } + if (callbacks) { + callbacks = callbacks.slice(0); + for (var i = 0, len = callbacks.length; i < len; ++i) { + callbacks[i].apply(this, args); } - return i; } - }; + return this; + }; /** - * get group properties of a groupname. If groupname is not found, a new group - * is added. - * @param {*} groupname Can be a number, string, Date, etc. - * @return {Object} group The created group, containing all group properties + * Return array of callbacks for `event`. + * + * @param {String} event + * @return {Array} + * @api public */ - Groups.prototype.get = function (groupname) { - var group = this.groups[groupname]; - if (group == undefined) { - // create new group - var index = this.defaultIndex % Groups.DEFAULT.length; - this.defaultIndex++; - group = {}; - group.color = Groups.DEFAULT[index]; - this.groups[groupname] = group; - } - return group; + Emitter.prototype.listeners = function(event){ + this._callbacks = this._callbacks || {}; + return this._callbacks[event] || []; }; /** - * Add a custom group style - * @param {String} groupname - * @param {Object} style An object containing borderColor, - * backgroundColor, etc. - * @return {Object} group The created group object + * Check if this emitter has `event` handlers. + * + * @param {String} event + * @return {Boolean} + * @api public */ - Groups.prototype.add = function (groupname, style) { - this.groups[groupname] = style; - return style; - }; - module.exports = Groups; + Emitter.prototype.hasListeners = function(event){ + return !! this.listeners(event).length; + }; /***/ }, -/* 55 */ +/* 57 */ /***/ function(module, exports, __webpack_require__) { + var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;"use strict"; /** - * @class Images - * This class loads images and keeps them stored. + * Created by Alex on 11/6/2014. */ - function Images() { - this.images = {}; - this.imageBroken = {}; - this.callback = undefined; - } - /** - * Set an onload callback function. This will be called each time an image - * is loaded - * @param {function} callback - */ - Images.prototype.setOnloadCallback = function(callback) { - this.callback = callback; - }; + // 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 () { - /** - * - * @param {string} url Url of the image - * @param {string} url Url of an image to use if the url image is not found - * @return {Image} img The image object - */ - Images.prototype.load = function(url, brokenUrl) { - var img = this.images[url]; // make a pointer - if (img === undefined) { - // create the image - var me = this; - img = new Image(); - img.onload = function () { - // IE11 fix -- thanks dponch! - if (this.width == 0) { - document.body.appendChild(this); - this.width = this.offsetWidth; - this.height = this.offsetHeight; - document.body.removeChild(this); - } + function keycharm(options) { + var preventDefault = options && options.preventDefault || false; - if (me.callback) { - me.images[url] = img; - me.callback(this); - } - }; + var container = options && options.container || window; - img.onerror = function () { - if (brokenUrl === undefined) { - console.error("Could not load image:", url); - delete this.src; - if (me.callback) { - me.callback(this); - } - } - else { - if (me.imageBroken[url] === true) { - if (this.src == brokenUrl) { - console.error("Could not load brokenImage:", brokenUrl); - delete this.src; - if (me.callback) { - me.callback(this); - } + var _exportFunctions = {}; + var _bound = {keydown:{}, keyup:{}}; + var _keys = {}; + var i; + + // a - z + for (i = 97; i <= 122; i++) {_keys[String.fromCharCode(i)] = {code:65 + (i - 97), shift: false};} + // A - Z + for (i = 65; i <= 90; i++) {_keys[String.fromCharCode(i)] = {code:i, shift: true};} + // 0 - 9 + for (i = 0; i <= 9; i++) {_keys['' + i] = {code:48 + i, shift: false};} + // F1 - F12 + for (i = 1; i <= 12; i++) {_keys['F' + i] = {code:111 + i, shift: false};} + // num0 - num9 + for (i = 0; i <= 9; i++) {_keys['num' + i] = {code:96 + i, shift: false};} + + // numpad misc + _keys['num*'] = {code:106, shift: false}; + _keys['num+'] = {code:107, shift: false}; + _keys['num-'] = {code:109, shift: false}; + _keys['num/'] = {code:111, shift: false}; + _keys['num.'] = {code:110, shift: false}; + // arrows + _keys['left'] = {code:37, shift: false}; + _keys['up'] = {code:38, shift: false}; + _keys['right'] = {code:39, shift: false}; + _keys['down'] = {code:40, shift: false}; + // extra keys + _keys['space'] = {code:32, shift: false}; + _keys['enter'] = {code:13, shift: false}; + _keys['shift'] = {code:16, shift: undefined}; + _keys['esc'] = {code:27, shift: false}; + _keys['backspace'] = {code:8, shift: false}; + _keys['tab'] = {code:9, shift: false}; + _keys['ctrl'] = {code:17, shift: false}; + _keys['alt'] = {code:18, shift: false}; + _keys['delete'] = {code:46, shift: false}; + _keys['pageup'] = {code:33, shift: false}; + _keys['pagedown'] = {code:34, shift: false}; + // symbols + _keys['='] = {code:187, shift: false}; + _keys['-'] = {code:189, shift: false}; + _keys[']'] = {code:221, shift: false}; + _keys['['] = {code:219, shift: false}; + + + + var down = function(event) {handleEvent(event,'keydown');}; + var up = function(event) {handleEvent(event,'keyup');}; + + // handle the actualy bound key with the event + var handleEvent = function(event,type) { + if (_bound[type][event.keyCode] !== undefined) { + var bound = _bound[type][event.keyCode]; + for (var i = 0; i < bound.length; i++) { + if (bound[i].shift === undefined) { + bound[i].fn(event); } - else { - console.error("Could not load image:", url); - this.src = brokenUrl; + 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); } } - else { - console.error("Could not load image:", url); - this.src = brokenUrl; - me.imageBroken[url] = true; + + if (preventDefault == true) { + event.preventDefault(); } } }; - img.src = url; - } + // 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}); + }; - return img; - }; - module.exports = Images; + // bind all keys to a call back (demo purposes) + _exportFunctions.bindAll = function(callback, type) { + if (type === undefined) { + type = 'keydown'; + } + for (var key in _keys) { + if (_keys.hasOwnProperty(key)) { + _exportFunctions.bind(key,callback,type); + } + } + }; + // get the key label from an event + _exportFunctions.getKey = function(event) { + for (var key in _keys) { + if (_keys.hasOwnProperty(key)) { + if (event.shiftKey == true && _keys[key].shift == true && event.keyCode == _keys[key].code) { + return key; + } + else if (event.shiftKey == false && _keys[key].shift == false && event.keyCode == _keys[key].code) { + return key; + } + else if (event.keyCode == _keys[key].code && key == 'shift') { + return key; + } + } + } + return "unknown key, currently not supported"; + }; -/***/ }, -/* 56 */ -/***/ function(module, exports, __webpack_require__) { - - var util = __webpack_require__(1); - - /** - * @class Node - * A node. A node can be connected to other nodes via one or multiple edges. - * @param {object} properties An object containing properties for the node. All - * properties 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" - * {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 properties - * @param {Object} constants An object with default values for - * example for the color - * - */ - function Node(properties, imagelist, grouplist, networkConstants) { - var constants = util.selectiveBridgeObject(['nodes'],networkConstants); - this.options = constants.nodes; - - this.selected = false; - this.hover = false; - - this.edges = []; // all edges connected to this node - this.dynamicEdges = []; - this.reroutedEdges = {}; - - this.fontDrawThreshold = 3; - - // set defaults for the properties - this.id = undefined; - this.allowedToMoveX = false; - this.allowedToMoveY = false; - this.xFixed = false; - this.yFixed = false; - this.horizontalAlignLeft = true; // these are for the navigation controls - this.verticalAlignTop = true; // these are for the navigation controls - this.baseRadiusValue = networkConstants.nodes.radius; - this.radiusFixed = false; - this.level = -1; - this.preassignedLevel = false; - this.hierarchyEnumerated = false; - this.labelDimensions = {top:0, left:0, width:0, height:0, yLine:0}; // could be cached - this.boundingBox = {top:0, left:0, right:0, bottom:0}; - - this.imagelist = imagelist; - this.grouplist = grouplist; - - // physics properties - this.fx = 0.0; // external force x - this.fy = 0.0; // external force y - this.vx = 0.0; // velocity x - this.vy = 0.0; // velocity y - this.x = null; - this.y = null; - - // used for reverting to previous position on stabilization - this.previousState = {vx:0,vy:0,x:0,y:0}; - - this.damping = networkConstants.physics.damping; // written every time gravity is calculated - this.fixedData = {x:null,y:null}; - - this.setProperties(properties, constants); - - // creating the variables for clustering - this.resetCluster(); - this.dynamicEdgesLength = 0; - this.clusterSession = 0; - this.clusterSizeWidthFactor = networkConstants.clustering.nodeScaling.width; - this.clusterSizeHeightFactor = networkConstants.clustering.nodeScaling.height; - this.clusterSizeRadiusFactor = networkConstants.clustering.nodeScaling.radius; - this.maxNodeSizeIncrements = networkConstants.clustering.maxNodeSizeIncrements; - this.growthIndicator = 0; - - // variables to tell the node about the network. - this.networkScaleInv = 1; - this.networkScale = 1; - this.canvasTopLeft = {"x": -300, "y": -300}; - this.canvasBottomRight = {"x": 300, "y": 300}; - this.parentEdgeId = null; - } - - - /** - * Revert the position and velocity of the previous step. - */ - Node.prototype.revertPosition = function() { - this.x = this.previousState.x; - this.y = this.previousState.y; - this.vx = this.previousState.vx; - this.vy = this.previousState.vy; - } - - - /** - * (re)setting the clustering variables and objects - */ - Node.prototype.resetCluster = function() { - // clustering variables - this.formationScale = undefined; // this is used to determine when to open the cluster - this.clusterSize = 1; // this signifies the total amount of nodes in this cluster - this.containedNodes = {}; - this.containedEdges = {}; - this.clusterSessions = []; - }; + // 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] = []; + } + }; - /** - * Attach a edge to the node - * @param {Edge} edge - */ - Node.prototype.attachEdge = function(edge) { - if (this.edges.indexOf(edge) == -1) { - this.edges.push(edge); - } - if (this.dynamicEdges.indexOf(edge) == -1) { - this.dynamicEdges.push(edge); - } - this.dynamicEdgesLength = this.dynamicEdges.length; - }; + // reset all bound variables. + _exportFunctions.reset = function() { + _bound = {keydown:{}, keyup:{}}; + }; - /** - * Detach a edge from the node - * @param {Edge} edge - */ - Node.prototype.detachEdge = function(edge) { - var index = this.edges.indexOf(edge); - if (index != -1) { - this.edges.splice(index, 1); - } - index = this.dynamicEdges.indexOf(edge); - if (index != -1) { - this.dynamicEdges.splice(index, 1); - } - this.dynamicEdgesLength = this.dynamicEdges.length; - }; + // unbind all listeners and reset all variables. + _exportFunctions.destroy = function() { + _bound = {keydown:{}, keyup:{}}; + container.removeEventListener('keydown', down, true); + container.removeEventListener('keyup', up, true); + }; + // create listeners. + container.addEventListener('keydown',down,true); + container.addEventListener('keyup',up,true); - /** - * Set or overwrite properties for the node - * @param {Object} properties an object with properties - * @param {Object} constants and object with default, global properties - */ - Node.prototype.setProperties = function(properties, constants) { - if (!properties) { - return; + // return the public functions. + return _exportFunctions; } - var fields = ['borderWidth','borderWidthSelected','shape','image','brokenImage','radius','fontColor', - 'fontSize','fontFace','fontFill','fontStrokeWidth','fontStrokeColor','group','mass' - ]; - util.selectiveDeepExtend(fields, this.options, properties); - - // basic properties - if (properties.id !== undefined) {this.id = properties.id;} - if (properties.label !== undefined) {this.label = properties.label; this.originalLabel = properties.label;} - if (properties.title !== undefined) {this.title = properties.title;} - if (properties.x !== undefined) {this.x = properties.x;} - if (properties.y !== undefined) {this.y = properties.y;} - if (properties.value !== undefined) {this.value = properties.value;} - if (properties.level !== undefined) {this.level = properties.level; this.preassignedLevel = true;} + return keycharm; + })); - // navigation controls properties - if (properties.horizontalAlignLeft !== undefined) {this.horizontalAlignLeft = properties.horizontalAlignLeft;} - if (properties.verticalAlignTop !== undefined) {this.verticalAlignTop = properties.verticalAlignTop;} - if (properties.triggerFunction !== undefined) {this.triggerFunction = properties.triggerFunction;} - if (this.id === undefined) { - throw "Node must have an id"; - } - // copy group properties - if (typeof properties.group === 'number' || (typeof properties.group === 'string' && properties.group != '')) { - var groupObj = this.grouplist.get(properties.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); - } - // individual shape properties - if (properties.radius !== undefined) {this.baseRadiusValue = this.options.radius;} - if (properties.color !== undefined) {this.options.color = util.parseColor(properties.color);} - 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"; - } - } +/***/ }, +/* 58 */ +/***/ function(module, exports, __webpack_require__) { - if (properties.allowedToMoveX !== undefined) { - this.xFixed = !properties.allowedToMoveX; - this.allowedToMoveX = properties.allowedToMoveX; - } - else if (properties.x !== undefined && this.allowedToMoveX == false) { - this.xFixed = true; - } + var __WEBPACK_AMD_DEFINE_RESULT__;/* WEBPACK VAR INJECTION */(function(global, module) {//! moment.js + //! version : 2.9.0 + //! authors : Tim Wood, Iskren Chernev, Moment.js contributors + //! license : MIT + //! momentjs.com + (function (undefined) { + /************************************ + Constants + ************************************/ - if (properties.allowedToMoveY !== undefined) { - this.yFixed = !properties.allowedToMoveY; - this.allowedToMoveY = properties.allowedToMoveY; - } - else if (properties.y !== undefined && this.allowedToMoveY == false) { - this.yFixed = true; - } + var moment, + VERSION = '2.9.0', + // the global-scope this is NOT the global object in Node.js + globalScope = (typeof global !== 'undefined' && (typeof window === 'undefined' || window === global.window)) ? global : this, + oldGlobalMoment, + round = Math.round, + hasOwnProperty = Object.prototype.hasOwnProperty, + i, - this.radiusFixed = this.radiusFixed || (properties.radius !== undefined); + YEAR = 0, + MONTH = 1, + DATE = 2, + HOUR = 3, + MINUTE = 4, + SECOND = 5, + MILLISECOND = 6, - if (this.options.shape === 'image' || this.options.shape === 'circularImage') { - this.options.radiusMin = constants.nodes.widthMin; - this.options.radiusMax = constants.nodes.widthMax; - } + // internal storage for locale config files + locales = {}, - // choose draw method depending on the shape - switch (this.options.shape) { - case 'database': this.draw = this._drawDatabase; this.resize = this._resizeDatabase; break; - case 'box': this.draw = this._drawBox; this.resize = this._resizeBox; break; - case 'circle': this.draw = this._drawCircle; this.resize = this._resizeCircle; break; - case 'ellipse': this.draw = this._drawEllipse; this.resize = this._resizeEllipse; break; - // TODO: add diamond shape - case 'image': this.draw = this._drawImage; this.resize = this._resizeImage; break; - case 'circularImage': this.draw = this._drawCircularImage; this.resize = this._resizeCircularImage; break; - case 'text': this.draw = this._drawText; this.resize = this._resizeText; break; - case 'dot': this.draw = this._drawDot; this.resize = this._resizeShape; break; - case 'square': this.draw = this._drawSquare; this.resize = this._resizeShape; break; - case 'triangle': this.draw = this._drawTriangle; this.resize = this._resizeShape; break; - case 'triangleDown': this.draw = this._drawTriangleDown; this.resize = this._resizeShape; break; - case 'star': this.draw = this._drawStar; this.resize = this._resizeShape; break; - default: this.draw = this._drawEllipse; this.resize = this._resizeEllipse; break; - } - // reset the size of the node, this can be changed - this._reset(); + // extra moment internal properties (plugins register props here) + momentProperties = [], - }; + // check for nodeJS + hasModule = (typeof module !== 'undefined' && module && module.exports), - /** - * select this node - */ - Node.prototype.select = function() { - this.selected = true; - this._reset(); - }; + // ASP.NET json date format regex + aspNetJsonRegex = /^\/?Date\((\-?\d+)/i, + aspNetTimeSpanJsonRegex = /(\-)?(?:(\d*)\.)?(\d+)\:(\d+)(?:\:(\d+)\.?(\d{3})?)?/, - /** - * unselect this node - */ - Node.prototype.unselect = function() { - this.selected = false; - this._reset(); - }; + // from http://docs.closure-library.googlecode.com/git/closure_goog_date_date.js.source.html + // somewhat more in line with 4.4.3.2 2004 spec, but allows decimal anywhere + isoDurationRegex = /^(-)?P(?:(?:([0-9,.]*)Y)?(?:([0-9,.]*)M)?(?:([0-9,.]*)D)?(?:T(?:([0-9,.]*)H)?(?:([0-9,.]*)M)?(?:([0-9,.]*)S)?)?|([0-9,.]*)W)$/, + // format tokens + formattingTokens = /(\[[^\[]*\])|(\\)?(Mo|MM?M?M?|Do|DDDo|DD?D?D?|ddd?d?|do?|w[o|w]?|W[o|W]?|Q|YYYYYY|YYYYY|YYYY|YY|gg(ggg?)?|GG(GGG?)?|e|E|a|A|hh?|HH?|mm?|ss?|S{1,4}|x|X|zz?|ZZ?|.)/g, + localFormattingTokens = /(\[[^\[]*\])|(\\)?(LTS|LT|LL?L?L?|l{1,4})/g, - /** - * Reset the calculated size of the node, forces it to recalculate its size - */ - Node.prototype.clearSizeCache = function() { - this._reset(); - }; + // parsing token regexes + parseTokenOneOrTwoDigits = /\d\d?/, // 0 - 99 + parseTokenOneToThreeDigits = /\d{1,3}/, // 0 - 999 + parseTokenOneToFourDigits = /\d{1,4}/, // 0 - 9999 + parseTokenOneToSixDigits = /[+\-]?\d{1,6}/, // -999,999 - 999,999 + parseTokenDigits = /\d+/, // nonzero number of digits + parseTokenWord = /[0-9]*['a-z\u00A0-\u05FF\u0700-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]+|[\u0600-\u06FF\/]+(\s*?[\u0600-\u06FF]+){1,2}/i, // any word (or two) characters or numbers including two/three word month in arabic. + parseTokenTimezone = /Z|[\+\-]\d\d:?\d\d/gi, // +00:00 -00:00 +0000 -0000 or Z + parseTokenT = /T/i, // T (ISO separator) + parseTokenOffsetMs = /[\+\-]?\d+/, // 1234567890123 + parseTokenTimestampMs = /[\+\-]?\d+(\.\d{1,3})?/, // 123456789 123456789.123 - /** - * Reset the calculated size of the node, forces it to recalculate its size - * @private - */ - Node.prototype._reset = function() { - this.width = undefined; - this.height = undefined; - }; + //strict parsing regexes + parseTokenOneDigit = /\d/, // 0 - 9 + parseTokenTwoDigits = /\d\d/, // 00 - 99 + parseTokenThreeDigits = /\d{3}/, // 000 - 999 + parseTokenFourDigits = /\d{4}/, // 0000 - 9999 + parseTokenSixDigits = /[+-]?\d{6}/, // -999,999 - 999,999 + parseTokenSignedNumber = /[+-]?\d+/, // -inf - inf - /** - * get the title of this node. - * @return {string} title The title of the node, or undefined when no title - * has been set. - */ - Node.prototype.getTitle = function() { - return typeof this.title === "function" ? this.title() : this.title; - }; + // iso 8601 regex + // 0000-00-00 0000-W00 or 0000-W00-0 + T + 00 or 00:00 or 00:00:00 or 00:00:00.000 + +00:00 or +0000 or +00) + isoRegex = /^\s*(?:[+-]\d{6}|\d{4})-(?:(\d\d-\d\d)|(W\d\d$)|(W\d\d-\d)|(\d\d\d))((T| )(\d\d(:\d\d(:\d\d(\.\d+)?)?)?)?([\+\-]\d\d(?::?\d\d)?|\s*Z)?)?$/, - /** - * 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 - */ - Node.prototype.distanceToBorder = function (ctx, angle) { - var borderWidth = 1; + isoFormat = 'YYYY-MM-DDTHH:mm:ssZ', - if (!this.width) { - this.resize(ctx); - } + isoDates = [ + ['YYYYYY-MM-DD', /[+-]\d{6}-\d{2}-\d{2}/], + ['YYYY-MM-DD', /\d{4}-\d{2}-\d{2}/], + ['GGGG-[W]WW-E', /\d{4}-W\d{2}-\d/], + ['GGGG-[W]WW', /\d{4}-W\d{2}/], + ['YYYY-DDD', /\d{4}-\d{3}/] + ], - switch (this.options.shape) { - case 'circle': - case 'dot': - return this.options.radius+ borderWidth; + // iso time formats and regexes + isoTimes = [ + ['HH:mm:ss.SSSS', /(T| )\d\d:\d\d:\d\d\.\d+/], + ['HH:mm:ss', /(T| )\d\d:\d\d:\d\d/], + ['HH:mm', /(T| )\d\d:\d\d/], + ['HH', /(T| )\d\d/] + ], - case 'ellipse': - 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); + // timezone chunker '+10:00' > ['10', '00'] or '-1530' > ['-', '15', '30'] + parseTimezoneChunker = /([\+\-]|\d\d)/gi, - // TODO: implement distanceToBorder for database - // TODO: implement distanceToBorder for triangle - // TODO: implement distanceToBorder for triangleDown + // getter and setter names + proxyGettersAndSetters = 'Date|Hours|Minutes|Seconds|Milliseconds'.split('|'), + unitMillisecondFactors = { + 'Milliseconds' : 1, + 'Seconds' : 1e3, + 'Minutes' : 6e4, + 'Hours' : 36e5, + 'Days' : 864e5, + 'Months' : 2592e6, + 'Years' : 31536e6 + }, - case 'box': - case 'image': - case 'text': - default: - if (this.width) { - return Math.min( - Math.abs(this.width / 2 / Math.cos(angle)), - Math.abs(this.height / 2 / Math.sin(angle))) + borderWidth; - // TODO: reckon with border radius too in case of box - } - else { - return 0; - } + unitAliases = { + ms : 'millisecond', + s : 'second', + m : 'minute', + h : 'hour', + d : 'day', + D : 'date', + w : 'week', + W : 'isoWeek', + M : 'month', + Q : 'quarter', + y : 'year', + DDD : 'dayOfYear', + e : 'weekday', + E : 'isoWeekday', + gg: 'weekYear', + GG: 'isoWeekYear' + }, - } - // TODO: implement calculation of distance to border for all shapes - }; + camelFunctions = { + dayofyear : 'dayOfYear', + isoweekday : 'isoWeekday', + isoweek : 'isoWeek', + weekyear : 'weekYear', + isoweekyear : 'isoWeekYear' + }, - /** - * Set forces acting on the node - * @param {number} fx Force in horizontal direction - * @param {number} fy Force in vertical direction - */ - Node.prototype._setForce = function(fx, fy) { - this.fx = fx; - this.fy = fy; - }; + // format function strings + formatFunctions = {}, - /** - * Add forces acting on the node - * @param {number} fx Force in horizontal direction - * @param {number} fy Force in vertical direction - * @private - */ - Node.prototype._addForce = function(fx, fy) { - this.fx += fx; - this.fy += fy; - }; + // default relative time thresholds + relativeTimeThresholds = { + s: 45, // seconds to minute + m: 45, // minutes to hour + h: 22, // hours to day + d: 26, // days to month + M: 11 // months to year + }, - /** - * Store the state before the next step - */ - Node.prototype.storeState = function() { - this.previousState.x = this.x; - this.previousState.y = this.y; - this.previousState.vx = this.vx; - this.previousState.vy = this.vy; - } - - /** - * Perform one discrete step for the node - * @param {number} interval Time interval in seconds - */ - Node.prototype.discreteStep = function(interval) { - this.storeState(); - if (!this.xFixed) { - var dx = this.damping * this.vx; // damping force - var ax = (this.fx - dx) / this.options.mass; // acceleration - this.vx += ax * interval; // velocity - this.x += this.vx * interval; // position - } - else { - this.fx = 0; - this.vx = 0; - } - - if (!this.yFixed) { - var dy = this.damping * this.vy; // damping force - var ay = (this.fy - dy) / this.options.mass; // acceleration - this.vy += ay * interval; // velocity - this.y += this.vy * interval; // position - } - else { - this.fy = 0; - this.vy = 0; - } - }; + // tokens to ordinalize and pad + ordinalizeTokens = 'DDD w W M D d'.split(' '), + paddedTokens = 'M D H h m s w W'.split(' '), + formatTokenFunctions = { + M : function () { + return this.month() + 1; + }, + MMM : function (format) { + return this.localeData().monthsShort(this, format); + }, + MMMM : function (format) { + return this.localeData().months(this, format); + }, + D : function () { + return this.date(); + }, + DDD : function () { + return this.dayOfYear(); + }, + d : function () { + return this.day(); + }, + dd : function (format) { + return this.localeData().weekdaysMin(this, format); + }, + ddd : function (format) { + return this.localeData().weekdaysShort(this, format); + }, + dddd : function (format) { + return this.localeData().weekdays(this, format); + }, + w : function () { + return this.week(); + }, + W : function () { + return this.isoWeek(); + }, + YY : function () { + return leftZeroFill(this.year() % 100, 2); + }, + YYYY : function () { + return leftZeroFill(this.year(), 4); + }, + YYYYY : function () { + return leftZeroFill(this.year(), 5); + }, + YYYYYY : function () { + var y = this.year(), sign = y >= 0 ? '+' : '-'; + return sign + leftZeroFill(Math.abs(y), 6); + }, + gg : function () { + return leftZeroFill(this.weekYear() % 100, 2); + }, + gggg : function () { + return leftZeroFill(this.weekYear(), 4); + }, + ggggg : function () { + return leftZeroFill(this.weekYear(), 5); + }, + GG : function () { + return leftZeroFill(this.isoWeekYear() % 100, 2); + }, + GGGG : function () { + return leftZeroFill(this.isoWeekYear(), 4); + }, + GGGGG : function () { + return leftZeroFill(this.isoWeekYear(), 5); + }, + e : function () { + return this.weekday(); + }, + E : function () { + return this.isoWeekday(); + }, + a : function () { + return this.localeData().meridiem(this.hours(), this.minutes(), true); + }, + A : function () { + return this.localeData().meridiem(this.hours(), this.minutes(), false); + }, + H : function () { + return this.hours(); + }, + h : function () { + return this.hours() % 12 || 12; + }, + m : function () { + return this.minutes(); + }, + s : function () { + return this.seconds(); + }, + S : function () { + return toInt(this.milliseconds() / 100); + }, + SS : function () { + return leftZeroFill(toInt(this.milliseconds() / 10), 2); + }, + SSS : function () { + return leftZeroFill(this.milliseconds(), 3); + }, + SSSS : function () { + return leftZeroFill(this.milliseconds(), 3); + }, + Z : function () { + var a = this.utcOffset(), + b = '+'; + if (a < 0) { + a = -a; + b = '-'; + } + return b + leftZeroFill(toInt(a / 60), 2) + ':' + leftZeroFill(toInt(a) % 60, 2); + }, + ZZ : function () { + var a = this.utcOffset(), + b = '+'; + if (a < 0) { + a = -a; + b = '-'; + } + return b + leftZeroFill(toInt(a / 60), 2) + leftZeroFill(toInt(a) % 60, 2); + }, + z : function () { + return this.zoneAbbr(); + }, + zz : function () { + return this.zoneName(); + }, + x : function () { + return this.valueOf(); + }, + X : function () { + return this.unix(); + }, + Q : function () { + return this.quarter(); + } + }, + deprecations = {}, - /** - * Perform one discrete step for the node - * @param {number} interval Time interval in seconds - * @param {number} maxVelocity The speed limit imposed on the velocity - */ - Node.prototype.discreteStepLimited = function(interval, maxVelocity) { - this.storeState(); - if (!this.xFixed) { - var dx = this.damping * this.vx; // damping force - var ax = (this.fx - dx) / this.options.mass; // acceleration - this.vx += ax * interval; // velocity - this.vx = (Math.abs(this.vx) > maxVelocity) ? ((this.vx > 0) ? maxVelocity : -maxVelocity) : this.vx; - this.x += this.vx * interval; // position - } - else { - this.fx = 0; - this.vx = 0; - } + lists = ['months', 'monthsShort', 'weekdays', 'weekdaysShort', 'weekdaysMin'], - if (!this.yFixed) { - var dy = this.damping * this.vy; // damping force - var ay = (this.fy - dy) / this.options.mass; // acceleration - this.vy += ay * interval; // velocity - this.vy = (Math.abs(this.vy) > maxVelocity) ? ((this.vy > 0) ? maxVelocity : -maxVelocity) : this.vy; - this.y += this.vy * interval; // position - } - else { - this.fy = 0; - this.vy = 0; - } - }; + updateInProgress = false; - /** - * Check if this node has a fixed x and y position - * @return {boolean} true if fixed, false if not - */ - Node.prototype.isFixed = function() { - return (this.xFixed && this.yFixed); - }; + // Pick the first defined of two or three arguments. dfl comes from + // default. + function dfl(a, b, c) { + switch (arguments.length) { + case 2: return a != null ? a : b; + case 3: return a != null ? a : b != null ? b : c; + default: throw new Error('Implement me'); + } + } - /** - * Check if this node is moving - * @param {number} vmin the minimum velocity considered as "moving" - * @return {boolean} true if moving, false if it has no velocity - */ - Node.prototype.isMoving = function(vmin) { - var velocity = Math.sqrt(Math.pow(this.vx,2) + Math.pow(this.vy,2)); - // this.velocity = Math.sqrt(Math.pow(this.vx,2) + Math.pow(this.vy,2)) - return (velocity > vmin); - }; + function hasOwnProp(a, b) { + return hasOwnProperty.call(a, b); + } - /** - * check if this node is selecte - * @return {boolean} selected True if node is selected, else false - */ - Node.prototype.isSelected = function() { - return this.selected; - }; + function defaultParsingFlags() { + // We need to deep clone this object, and es5 standard is not very + // helpful. + return { + empty : false, + unusedTokens : [], + unusedInput : [], + overflow : -2, + charsLeftOver : 0, + nullInput : false, + invalidMonth : null, + invalidFormat : false, + userInvalidated : false, + iso: false + }; + } - /** - * Retrieve the value of the node. Can be undefined - * @return {Number} value - */ - Node.prototype.getValue = function() { - return this.value; - }; + function printMsg(msg) { + if (moment.suppressDeprecationWarnings === false && + typeof console !== 'undefined' && console.warn) { + console.warn('Deprecation warning: ' + msg); + } + } - /** - * Calculate the distance from the nodes location to the given location (x,y) - * @param {Number} x - * @param {Number} y - * @return {Number} value - */ - Node.prototype.getDistance = function(x, y) { - var dx = this.x - x, - dy = this.y - y; - return Math.sqrt(dx * dx + dy * dy); - }; + function deprecate(msg, fn) { + var firstTime = true; + return extend(function () { + if (firstTime) { + printMsg(msg); + firstTime = false; + } + return fn.apply(this, arguments); + }, fn); + } + function deprecateSimple(name, msg) { + if (!deprecations[name]) { + printMsg(msg); + deprecations[name] = true; + } + } - /** - * Adjust the value range of the node. The node will adjust it's radius - * based on its value. - * @param {Number} min - * @param {Number} max - */ - Node.prototype.setValueRange = function(min, max) { - if (!this.radiusFixed && this.value !== undefined) { - if (max == min) { - this.options.radius= (this.options.radiusMin + this.options.radiusMax) / 2; + function padToken(func, count) { + return function (a) { + return leftZeroFill(func.call(this, a), count); + }; } - else { - var scale = (this.options.radiusMax - this.options.radiusMin) / (max - min); - this.options.radius= (this.value - min) * scale + this.options.radiusMin; + function ordinalizeToken(func, period) { + return function (a) { + return this.localeData().ordinal(func.call(this, a), period); + }; } - } - this.baseRadiusValue = this.options.radius; - }; - - /** - * Draw this node in the given canvas - * The 2d context of a HTML canvas can be retrieved by canvas.getContext("2d"); - * @param {CanvasRenderingContext2D} ctx - */ - Node.prototype.draw = function(ctx) { - throw "Draw method not initialized for node"; - }; - /** - * 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 - */ - Node.prototype.resize = function(ctx) { - throw "Resize method not initialized for node"; - }; + function monthDiff(a, b) { + // difference in months + var wholeMonthDiff = ((b.year() - a.year()) * 12) + (b.month() - a.month()), + // b is in (anchor - 1 month, anchor + 1 month) + anchor = a.clone().add(wholeMonthDiff, 'months'), + anchor2, adjust; - /** - * 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 - */ - Node.prototype.isOverlappingWith = function(obj) { - return (this.left < obj.right && - this.left + this.width > obj.left && - this.top < obj.bottom && - this.top + this.height > obj.top); - }; + if (b - anchor < 0) { + anchor2 = a.clone().add(wholeMonthDiff - 1, 'months'); + // linear across the month + adjust = (b - anchor) / (anchor - anchor2); + } else { + anchor2 = a.clone().add(wholeMonthDiff + 1, 'months'); + // linear across the month + adjust = (b - anchor) / (anchor2 - anchor); + } - Node.prototype._resizeImage = function (ctx) { - // TODO: pre calculate the image size + return -(wholeMonthDiff + adjust); + } - if (!this.width || !this.height) { // undefined or 0 - var width, height; - if (this.value) { - this.options.radius= this.baseRadiusValue; - var scale = this.imageObj.height / this.imageObj.width; - if (scale !== undefined) { - width = this.options.radius|| this.imageObj.width; - height = this.options.radius* scale || this.imageObj.height; - } - else { - width = 0; - height = 0; - } + while (ordinalizeTokens.length) { + i = ordinalizeTokens.pop(); + formatTokenFunctions[i + 'o'] = ordinalizeToken(formatTokenFunctions[i], i); } - else { - width = this.imageObj.width; - height = this.imageObj.height; + while (paddedTokens.length) { + i = paddedTokens.pop(); + formatTokenFunctions[i + i] = padToken(formatTokenFunctions[i], 2); } - this.width = width; - this.height = height; + formatTokenFunctions.DDDD = padToken(formatTokenFunctions.DDD, 3); - this.growthIndicator = 0; - if (this.width > 0 && this.height > 0) { - this.width += Math.min(this.clusterSize - 1, this.maxNodeSizeIncrements) * this.clusterSizeWidthFactor; - this.height += Math.min(this.clusterSize - 1, this.maxNodeSizeIncrements) * this.clusterSizeHeightFactor; - this.options.radius+= Math.min(this.clusterSize - 1, this.maxNodeSizeIncrements) * this.clusterSizeRadiusFactor; - this.growthIndicator = this.width - width; - } - } - }; - Node.prototype._drawImageAtPosition = function (ctx) { - if (this.imageObj.width != 0 ) { - // draw the shade - if (this.clusterSize > 1) { - var lineWidth = ((this.clusterSize > 1) ? 10 : 0.0); - lineWidth *= this.networkScaleInv; - lineWidth = Math.min(0.2 * this.width,lineWidth); + function meridiemFixWrap(locale, hour, meridiem) { + var isPm; - ctx.globalAlpha = 0.5; - ctx.drawImage(this.imageObj, this.left - lineWidth, this.top - lineWidth, this.width + 2*lineWidth, this.height + 2*lineWidth); + if (meridiem == null) { + // nothing to do + return hour; + } + if (locale.meridiemHour != null) { + return locale.meridiemHour(hour, meridiem); + } else if (locale.isPM != null) { + // Fallback + isPm = locale.isPM(meridiem); + if (isPm && hour < 12) { + hour += 12; + } + if (!isPm && hour === 12) { + hour = 0; + } + return hour; + } else { + // thie is not supposed to happen + return hour; + } } - // draw the image - ctx.globalAlpha = 1.0; - ctx.drawImage(this.imageObj, this.left, this.top, this.width, this.height); - } - }; + /************************************ + Constructors + ************************************/ - Node.prototype._drawImageLabel = function (ctx) { - var yLabel; - var offset = 0; - - if (this.height){ - offset = this.height / 2; - var labelDimensions = this.getTextSize(ctx); - - if (labelDimensions.lineCount >= 1){ - offset += labelDimensions.height / 2; - offset += 3; + function Locale() { } - } - - yLabel = this.y + offset; - this._label(ctx, this.label, this.x, yLabel, undefined); - }; + // Moment prototype object + function Moment(config, skipOverflow) { + if (skipOverflow !== false) { + checkOverflow(config); + } + copyConfig(this, config); + this._d = new Date(+config._d); + // Prevent infinite loop in case updateOffset creates new moment + // objects. + if (updateInProgress === false) { + updateInProgress = true; + moment.updateOffset(this); + updateInProgress = false; + } + } - Node.prototype._drawImage = function (ctx) { - this._resizeImage(ctx); - this.left = this.x - this.width / 2; - this.top = this.y - this.height / 2; + // Duration Constructor + function Duration(duration) { + var normalizedInput = normalizeObjectUnits(duration), + years = normalizedInput.year || 0, + quarters = normalizedInput.quarter || 0, + months = normalizedInput.month || 0, + weeks = normalizedInput.week || 0, + days = normalizedInput.day || 0, + hours = normalizedInput.hour || 0, + minutes = normalizedInput.minute || 0, + seconds = normalizedInput.second || 0, + milliseconds = normalizedInput.millisecond || 0; - this._drawImageAtPosition(ctx); + // representation for dateAddRemove + this._milliseconds = +milliseconds + + seconds * 1e3 + // 1000 + minutes * 6e4 + // 1000 * 60 + hours * 36e5; // 1000 * 60 * 60 + // Because of dateAddRemove treats 24 hours as different from a + // day when working around DST, we need to store them separately + this._days = +days + + weeks * 7; + // It is impossible translate months into days without knowing + // which months you are are talking about, so we have to store + // it separately. + this._months = +months + + quarters * 3 + + years * 12; - this.boundingBox.top = this.top; - this.boundingBox.left = this.left; - this.boundingBox.right = this.left + this.width; - this.boundingBox.bottom = this.top + this.height; + this._data = {}; - this._drawImageLabel(ctx); - this.boundingBox.left = Math.min(this.boundingBox.left, this.labelDimensions.left); - this.boundingBox.right = Math.max(this.boundingBox.right, this.labelDimensions.left + this.labelDimensions.width); - this.boundingBox.bottom = Math.max(this.boundingBox.bottom, this.boundingBox.bottom + this.labelDimensions.height); - }; + this._locale = moment.localeData(); - Node.prototype._resizeCircularImage = function (ctx) { - if(!this.imageObj.src || !this.imageObj.width || !this.imageObj.height){ - if (!this.width) { - var diameter = this.options.radius * 2; - this.width = diameter; - this.height = diameter; + this._bubble(); + } - // scaling used for clustering - //this.width += Math.min(this.clusterSize - 1, this.maxNodeSizeIncrements) * 0.5 * this.clusterSizeWidthFactor; - //this.height += Math.min(this.clusterSize - 1, this.maxNodeSizeIncrements) * 0.5 * this.clusterSizeHeightFactor; - this.options.radius += Math.min(this.clusterSize - 1, this.maxNodeSizeIncrements) * 0.5 * this.clusterSizeRadiusFactor; - this.growthIndicator = this.options.radius- 0.5*diameter; - this._swapToImageResizeWhenImageLoaded = true; - } - } - else { - if (this._swapToImageResizeWhenImageLoaded) { - this.width = 0; - this.height = 0; - delete this._swapToImageResizeWhenImageLoaded; - } - this._resizeImage(ctx); - } + /************************************ + Helpers + ************************************/ - }; - Node.prototype._drawCircularImage = function (ctx) { - this._resizeCircularImage(ctx); + function extend(a, b) { + for (var i in b) { + if (hasOwnProp(b, i)) { + a[i] = b[i]; + } + } - this.left = this.x - this.width / 2; - this.top = this.y - this.height / 2; - - var centerX = this.left + (this.width / 2); - var centerY = this.top + (this.height / 2); - var radius = Math.abs(this.height / 2); + if (hasOwnProp(b, 'toString')) { + a.toString = b.toString; + } - this._drawRawCircle(ctx, centerX, centerY, radius); + if (hasOwnProp(b, 'valueOf')) { + a.valueOf = b.valueOf; + } - ctx.save(); - ctx.circle(this.x, this.y, radius); - ctx.stroke(); - ctx.clip(); + return a; + } - this._drawImageAtPosition(ctx); + function copyConfig(to, from) { + var i, prop, val; - ctx.restore(); + if (typeof from._isAMomentObject !== 'undefined') { + to._isAMomentObject = from._isAMomentObject; + } + if (typeof from._i !== 'undefined') { + to._i = from._i; + } + if (typeof from._f !== 'undefined') { + to._f = from._f; + } + if (typeof from._l !== 'undefined') { + to._l = from._l; + } + if (typeof from._strict !== 'undefined') { + to._strict = from._strict; + } + if (typeof from._tzm !== 'undefined') { + to._tzm = from._tzm; + } + if (typeof from._isUTC !== 'undefined') { + to._isUTC = from._isUTC; + } + if (typeof from._offset !== 'undefined') { + to._offset = from._offset; + } + if (typeof from._pf !== 'undefined') { + to._pf = from._pf; + } + if (typeof from._locale !== 'undefined') { + to._locale = from._locale; + } - this.boundingBox.top = this.y - this.options.radius; - this.boundingBox.left = this.x - this.options.radius; - this.boundingBox.right = this.x + this.options.radius; - this.boundingBox.bottom = this.y + this.options.radius; + if (momentProperties.length > 0) { + for (i in momentProperties) { + prop = momentProperties[i]; + val = from[prop]; + if (typeof val !== 'undefined') { + to[prop] = val; + } + } + } - this._drawImageLabel(ctx); - - this.boundingBox.left = Math.min(this.boundingBox.left, this.labelDimensions.left); - this.boundingBox.right = Math.max(this.boundingBox.right, this.labelDimensions.left + this.labelDimensions.width); - this.boundingBox.bottom = Math.max(this.boundingBox.bottom, this.boundingBox.bottom + this.labelDimensions.height); - }; + return to; + } - Node.prototype._resizeBox = function (ctx) { - if (!this.width) { - var margin = 5; - var textSize = this.getTextSize(ctx); - this.width = textSize.width + 2 * margin; - this.height = textSize.height + 2 * margin; + function absRound(number) { + if (number < 0) { + return Math.ceil(number); + } else { + return Math.floor(number); + } + } - this.width += Math.min(this.clusterSize - 1, this.maxNodeSizeIncrements) * 0.5 * this.clusterSizeWidthFactor; - this.height += Math.min(this.clusterSize - 1, this.maxNodeSizeIncrements) * 0.5 * this.clusterSizeHeightFactor; - this.growthIndicator = this.width - (textSize.width + 2 * margin); - // this.options.radius+= Math.min(this.clusterSize - 1, this.maxNodeSizeIncrements) * 0.5 * this.clusterSizeRadiusFactor; + // left zero fill a number + // see http://jsperf.com/left-zero-filling for performance comparison + function leftZeroFill(number, targetLength, forceSign) { + var output = '' + Math.abs(number), + sign = number >= 0; - } - }; + while (output.length < targetLength) { + output = '0' + output; + } + return (sign ? (forceSign ? '+' : '') : '-') + output; + } - Node.prototype._drawBox = function (ctx) { - this._resizeBox(ctx); + function positiveMomentsDifference(base, other) { + var res = {milliseconds: 0, months: 0}; - this.left = this.x - this.width / 2; - this.top = this.y - this.height / 2; + res.months = other.month() - base.month() + + (other.year() - base.year()) * 12; + if (base.clone().add(res.months, 'M').isAfter(other)) { + --res.months; + } - var clusterLineWidth = 2.5; - var borderWidth = this.options.borderWidth; - var selectionLineWidth = this.options.borderWidthSelected || 2 * this.options.borderWidth; + res.milliseconds = +other - +(base.clone().add(res.months, 'M')); - ctx.strokeStyle = this.selected ? this.options.color.highlight.border : this.hover ? this.options.color.hover.border : this.options.color.border; + return res; + } - // draw the outer border - if (this.clusterSize > 1) { - ctx.lineWidth = (this.selected ? selectionLineWidth : borderWidth) + ((this.clusterSize > 1) ? clusterLineWidth : 0.0); - ctx.lineWidth *= this.networkScaleInv; - ctx.lineWidth = Math.min(this.width,ctx.lineWidth); + function momentsDifference(base, other) { + var res; + other = makeAs(other, base); + if (base.isBefore(other)) { + res = positiveMomentsDifference(base, other); + } else { + res = positiveMomentsDifference(other, base); + res.milliseconds = -res.milliseconds; + res.months = -res.months; + } - ctx.roundRect(this.left-2*ctx.lineWidth, this.top-2*ctx.lineWidth, this.width+4*ctx.lineWidth, this.height+4*ctx.lineWidth, this.options.radius); - ctx.stroke(); - } - ctx.lineWidth = (this.selected ? selectionLineWidth : borderWidth) + ((this.clusterSize > 1) ? clusterLineWidth : 0.0); - ctx.lineWidth *= this.networkScaleInv; - ctx.lineWidth = Math.min(this.width,ctx.lineWidth); + return res; + } - ctx.fillStyle = this.selected ? this.options.color.highlight.background : this.hover ? this.options.color.hover.background : this.options.color.background; + // TODO: remove 'name' arg after deprecation is removed + function createAdder(direction, name) { + return function (val, period) { + var dur, tmp; + //invert the arguments, but complain about it + if (period !== null && !isNaN(+period)) { + deprecateSimple(name, 'moment().' + name + '(period, number) is deprecated. Please use moment().' + name + '(number, period).'); + tmp = val; val = period; period = tmp; + } - ctx.roundRect(this.left, this.top, this.width, this.height, this.options.radius); - ctx.fill(); - ctx.stroke(); + val = typeof val === 'string' ? +val : val; + dur = moment.duration(val, period); + addOrSubtractDurationFromMoment(this, dur, direction); + return this; + }; + } - this.boundingBox.top = this.top; - this.boundingBox.left = this.left; - this.boundingBox.right = this.left + this.width; - this.boundingBox.bottom = this.top + this.height; + function addOrSubtractDurationFromMoment(mom, duration, isAdding, updateOffset) { + var milliseconds = duration._milliseconds, + days = duration._days, + months = duration._months; + updateOffset = updateOffset == null ? true : updateOffset; - this._label(ctx, this.label, this.x, this.y); - }; + if (milliseconds) { + mom._d.setTime(+mom._d + milliseconds * isAdding); + } + if (days) { + rawSetter(mom, 'Date', rawGetter(mom, 'Date') + days * isAdding); + } + if (months) { + rawMonthSetter(mom, rawGetter(mom, 'Month') + months * isAdding); + } + if (updateOffset) { + moment.updateOffset(mom, days || months); + } + } + // check if is an array + function isArray(input) { + return Object.prototype.toString.call(input) === '[object Array]'; + } - Node.prototype._resizeDatabase = function (ctx) { - if (!this.width) { - var margin = 5; - var textSize = this.getTextSize(ctx); - var size = textSize.width + 2 * margin; - this.width = size; - this.height = size; + function isDate(input) { + return Object.prototype.toString.call(input) === '[object Date]' || + input instanceof Date; + } - // scaling used for clustering - this.width += Math.min(this.clusterSize - 1, this.maxNodeSizeIncrements) * this.clusterSizeWidthFactor; - this.height += Math.min(this.clusterSize - 1, this.maxNodeSizeIncrements) * this.clusterSizeHeightFactor; - this.options.radius+= Math.min(this.clusterSize - 1, this.maxNodeSizeIncrements) * this.clusterSizeRadiusFactor; - this.growthIndicator = this.width - size; - } - }; + // compare two arrays, return the number of differences + function compareArrays(array1, array2, dontConvert) { + var len = Math.min(array1.length, array2.length), + lengthDiff = Math.abs(array1.length - array2.length), + diffs = 0, + i; + for (i = 0; i < len; i++) { + if ((dontConvert && array1[i] !== array2[i]) || + (!dontConvert && toInt(array1[i]) !== toInt(array2[i]))) { + diffs++; + } + } + return diffs + lengthDiff; + } - Node.prototype._drawDatabase = function (ctx) { - this._resizeDatabase(ctx); - this.left = this.x - this.width / 2; - this.top = this.y - this.height / 2; + function normalizeUnits(units) { + if (units) { + var lowered = units.toLowerCase().replace(/(.)s$/, '$1'); + units = unitAliases[units] || camelFunctions[lowered] || lowered; + } + return units; + } - var clusterLineWidth = 2.5; - var borderWidth = this.options.borderWidth; - var selectionLineWidth = this.options.borderWidthSelected || 2 * this.options.borderWidth; + function normalizeObjectUnits(inputObject) { + var normalizedInput = {}, + normalizedProp, + prop; - ctx.strokeStyle = this.selected ? this.options.color.highlight.border : this.hover ? this.options.color.hover.border : this.options.color.border; + for (prop in inputObject) { + if (hasOwnProp(inputObject, prop)) { + normalizedProp = normalizeUnits(prop); + if (normalizedProp) { + normalizedInput[normalizedProp] = inputObject[prop]; + } + } + } - // draw the outer border - if (this.clusterSize > 1) { - ctx.lineWidth = (this.selected ? selectionLineWidth : borderWidth) + ((this.clusterSize > 1) ? clusterLineWidth : 0.0); - ctx.lineWidth *= this.networkScaleInv; - ctx.lineWidth = Math.min(this.width,ctx.lineWidth); + return normalizedInput; + } - ctx.database(this.x - this.width/2 - 2*ctx.lineWidth, this.y - this.height*0.5 - 2*ctx.lineWidth, this.width + 4*ctx.lineWidth, this.height + 4*ctx.lineWidth); - ctx.stroke(); - } - ctx.lineWidth = (this.selected ? selectionLineWidth : borderWidth) + ((this.clusterSize > 1) ? clusterLineWidth : 0.0); - ctx.lineWidth *= this.networkScaleInv; - ctx.lineWidth = Math.min(this.width,ctx.lineWidth); + function makeList(field) { + var count, setter; - ctx.fillStyle = this.selected ? this.options.color.highlight.background : this.hover ? this.options.color.hover.background : this.options.color.background; - ctx.database(this.x - this.width/2, this.y - this.height*0.5, this.width, this.height); - ctx.fill(); - ctx.stroke(); + if (field.indexOf('week') === 0) { + count = 7; + setter = 'day'; + } + else if (field.indexOf('month') === 0) { + count = 12; + setter = 'month'; + } + else { + return; + } - this.boundingBox.top = this.top; - this.boundingBox.left = this.left; - this.boundingBox.right = this.left + this.width; - this.boundingBox.bottom = this.top + this.height; + moment[field] = function (format, index) { + var i, getter, + method = moment._locale[field], + results = []; - this._label(ctx, this.label, this.x, this.y); - }; + if (typeof format === 'number') { + index = format; + format = undefined; + } + getter = function (i) { + var m = moment().utc().set(setter, i); + return method.call(moment._locale, m, format || ''); + }; - Node.prototype._resizeCircle = function (ctx) { - if (!this.width) { - var margin = 5; - var textSize = this.getTextSize(ctx); - var diameter = Math.max(textSize.width, textSize.height) + 2 * margin; - this.options.radius = diameter / 2; + if (index != null) { + return getter(index); + } + else { + for (i = 0; i < count; i++) { + results.push(getter(i)); + } + return results; + } + }; + } - this.width = diameter; - this.height = diameter; + function toInt(argumentForCoercion) { + var coercedNumber = +argumentForCoercion, + value = 0; - // scaling used for clustering - // this.width += Math.min(this.clusterSize - 1, this.maxNodeSizeIncrements) * 0.5 * this.clusterSizeWidthFactor; - // this.height += Math.min(this.clusterSize - 1, this.maxNodeSizeIncrements) * 0.5 * this.clusterSizeHeightFactor; - this.options.radius += Math.min(this.clusterSize - 1, this.maxNodeSizeIncrements) * 0.5 * this.clusterSizeRadiusFactor; - this.growthIndicator = this.options.radius- 0.5*diameter; - } - }; + if (coercedNumber !== 0 && isFinite(coercedNumber)) { + if (coercedNumber >= 0) { + value = Math.floor(coercedNumber); + } else { + value = Math.ceil(coercedNumber); + } + } - Node.prototype._drawRawCircle = function (ctx, x, y, radius) { - var clusterLineWidth = 2.5; - var borderWidth = this.options.borderWidth; - var selectionLineWidth = this.options.borderWidthSelected || 2 * this.options.borderWidth; - - ctx.strokeStyle = this.selected ? this.options.color.highlight.border : this.hover ? this.options.color.hover.border : this.options.color.border; + return value; + } - // draw the outer border - if (this.clusterSize > 1) { - ctx.lineWidth = (this.selected ? selectionLineWidth : borderWidth) + ((this.clusterSize > 1) ? clusterLineWidth : 0.0); - ctx.lineWidth *= this.networkScaleInv; - ctx.lineWidth = Math.min(this.width,ctx.lineWidth); + function daysInMonth(year, month) { + return new Date(Date.UTC(year, month + 1, 0)).getUTCDate(); + } - ctx.circle(x, y, radius+2*ctx.lineWidth); - ctx.stroke(); - } - ctx.lineWidth = (this.selected ? selectionLineWidth : borderWidth) + ((this.clusterSize > 1) ? clusterLineWidth : 0.0); - ctx.lineWidth *= this.networkScaleInv; - ctx.lineWidth = Math.min(this.width,ctx.lineWidth); + function weeksInYear(year, dow, doy) { + return weekOfYear(moment([year, 11, 31 + dow - doy]), dow, doy).week; + } - ctx.fillStyle = this.selected ? this.options.color.highlight.background : this.hover ? this.options.color.hover.background : this.options.color.background; - ctx.circle(this.x, this.y, radius); - ctx.fill(); - ctx.stroke(); - }; + function daysInYear(year) { + return isLeapYear(year) ? 366 : 365; + } - Node.prototype._drawCircle = function (ctx) { - this._resizeCircle(ctx); - this.left = this.x - this.width / 2; - this.top = this.y - this.height / 2; + function isLeapYear(year) { + return (year % 4 === 0 && year % 100 !== 0) || year % 400 === 0; + } - this._drawRawCircle(ctx, this.x, this.y, this.options.radius); + function checkOverflow(m) { + var overflow; + if (m._a && m._pf.overflow === -2) { + overflow = + m._a[MONTH] < 0 || m._a[MONTH] > 11 ? MONTH : + m._a[DATE] < 1 || m._a[DATE] > daysInMonth(m._a[YEAR], m._a[MONTH]) ? DATE : + m._a[HOUR] < 0 || m._a[HOUR] > 24 || + (m._a[HOUR] === 24 && (m._a[MINUTE] !== 0 || + m._a[SECOND] !== 0 || + m._a[MILLISECOND] !== 0)) ? HOUR : + m._a[MINUTE] < 0 || m._a[MINUTE] > 59 ? MINUTE : + m._a[SECOND] < 0 || m._a[SECOND] > 59 ? SECOND : + m._a[MILLISECOND] < 0 || m._a[MILLISECOND] > 999 ? MILLISECOND : + -1; - this.boundingBox.top = this.y - this.options.radius; - this.boundingBox.left = this.x - this.options.radius; - this.boundingBox.right = this.x + this.options.radius; - this.boundingBox.bottom = this.y + this.options.radius; + if (m._pf._overflowDayOfYear && (overflow < YEAR || overflow > DATE)) { + overflow = DATE; + } - this._label(ctx, this.label, this.x, this.y); - }; + m._pf.overflow = overflow; + } + } - Node.prototype._resizeEllipse = function (ctx) { - if (!this.width) { - var textSize = this.getTextSize(ctx); + function isValid(m) { + if (m._isValid == null) { + m._isValid = !isNaN(m._d.getTime()) && + m._pf.overflow < 0 && + !m._pf.empty && + !m._pf.invalidMonth && + !m._pf.nullInput && + !m._pf.invalidFormat && + !m._pf.userInvalidated; - this.width = textSize.width * 1.5; - this.height = textSize.height * 2; - if (this.width < this.height) { - this.width = this.height; + if (m._strict) { + m._isValid = m._isValid && + m._pf.charsLeftOver === 0 && + m._pf.unusedTokens.length === 0 && + m._pf.bigHour === undefined; + } + } + return m._isValid; } - var defaultSize = this.width; - // scaling used for clustering - this.width += Math.min(this.clusterSize - 1, this.maxNodeSizeIncrements) * this.clusterSizeWidthFactor; - this.height += Math.min(this.clusterSize - 1, this.maxNodeSizeIncrements) * this.clusterSizeHeightFactor; - this.options.radius += Math.min(this.clusterSize - 1, this.maxNodeSizeIncrements) * this.clusterSizeRadiusFactor; - this.growthIndicator = this.width - defaultSize; - } - }; + function normalizeLocale(key) { + return key ? key.toLowerCase().replace('_', '-') : key; + } - Node.prototype._drawEllipse = function (ctx) { - this._resizeEllipse(ctx); - this.left = this.x - this.width / 2; - this.top = this.y - this.height / 2; + // pick the locale from the array + // try ['en-au', 'en-gb'] as 'en-au', 'en-gb', 'en', as in move through the list trying each + // substring from most specific to least, but move to the next array item if it's a more specific variant than the current root + function chooseLocale(names) { + var i = 0, j, next, locale, split; - var clusterLineWidth = 2.5; - var borderWidth = this.options.borderWidth; - var selectionLineWidth = this.options.borderWidthSelected || 2 * this.options.borderWidth; + while (i < names.length) { + split = normalizeLocale(names[i]).split('-'); + j = split.length; + next = normalizeLocale(names[i + 1]); + next = next ? next.split('-') : null; + while (j > 0) { + locale = loadLocale(split.slice(0, j).join('-')); + if (locale) { + return locale; + } + if (next && next.length >= j && compareArrays(split, next, true) >= j - 1) { + //the next array item is better than a shallower substring of this one + break; + } + j--; + } + i++; + } + return null; + } - ctx.strokeStyle = this.selected ? this.options.color.highlight.border : this.hover ? this.options.color.hover.border : this.options.color.border; + function loadLocale(name) { + var oldLocale = null; + if (!locales[name] && hasModule) { + try { + oldLocale = moment.locale(); + !(function webpackMissingModule() { var e = new Error("Cannot find module \"./locale\""); e.code = 'MODULE_NOT_FOUND'; throw e; }()); + // because defineLocale currently also sets the global locale, we want to undo that for lazy loaded locales + moment.locale(oldLocale); + } catch (e) { } + } + return locales[name]; + } - // draw the outer border - if (this.clusterSize > 1) { - ctx.lineWidth = (this.selected ? selectionLineWidth : borderWidth) + ((this.clusterSize > 1) ? clusterLineWidth : 0.0); - ctx.lineWidth *= this.networkScaleInv; - ctx.lineWidth = Math.min(this.width,ctx.lineWidth); - - ctx.ellipse(this.left-2*ctx.lineWidth, this.top-2*ctx.lineWidth, this.width+4*ctx.lineWidth, this.height+4*ctx.lineWidth); - ctx.stroke(); - } - ctx.lineWidth = (this.selected ? selectionLineWidth : borderWidth) + ((this.clusterSize > 1) ? clusterLineWidth : 0.0); - ctx.lineWidth *= this.networkScaleInv; - ctx.lineWidth = Math.min(this.width,ctx.lineWidth); - - ctx.fillStyle = this.selected ? this.options.color.highlight.background : this.hover ? this.options.color.hover.background : this.options.color.background; + // Return a moment from input, that is local/utc/utcOffset equivalent to + // model. + function makeAs(input, model) { + var res, diff; + if (model._isUTC) { + res = model.clone(); + diff = (moment.isMoment(input) || isDate(input) ? + +input : +moment(input)) - (+res); + // Use low-level api, because this fn is low-level api. + res._d.setTime(+res._d + diff); + moment.updateOffset(res, false); + return res; + } else { + return moment(input).local(); + } + } - ctx.ellipse(this.left, this.top, this.width, this.height); - ctx.fill(); - ctx.stroke(); + /************************************ + Locale + ************************************/ - this.boundingBox.top = this.top; - this.boundingBox.left = this.left; - this.boundingBox.right = this.left + this.width; - this.boundingBox.bottom = this.top + this.height; - this._label(ctx, this.label, this.x, this.y); - }; + extend(Locale.prototype, { - Node.prototype._drawDot = function (ctx) { - this._drawShape(ctx, 'circle'); - }; + set : function (config) { + var prop, i; + for (i in config) { + prop = config[i]; + if (typeof prop === 'function') { + this[i] = prop; + } else { + this['_' + i] = prop; + } + } + // Lenient ordinal parsing accepts just a number in addition to + // number + (possibly) stuff coming from _ordinalParseLenient. + this._ordinalParseLenient = new RegExp(this._ordinalParse.source + '|' + /\d{1,2}/.source); + }, - Node.prototype._drawTriangle = function (ctx) { - this._drawShape(ctx, 'triangle'); - }; + _months : 'January_February_March_April_May_June_July_August_September_October_November_December'.split('_'), + months : function (m) { + return this._months[m.month()]; + }, - Node.prototype._drawTriangleDown = function (ctx) { - this._drawShape(ctx, 'triangleDown'); - }; + _monthsShort : 'Jan_Feb_Mar_Apr_May_Jun_Jul_Aug_Sep_Oct_Nov_Dec'.split('_'), + monthsShort : function (m) { + return this._monthsShort[m.month()]; + }, - Node.prototype._drawSquare = function (ctx) { - this._drawShape(ctx, 'square'); - }; + monthsParse : function (monthName, format, strict) { + var i, mom, regex; - Node.prototype._drawStar = function (ctx) { - this._drawShape(ctx, 'star'); - }; + if (!this._monthsParse) { + this._monthsParse = []; + this._longMonthsParse = []; + this._shortMonthsParse = []; + } - Node.prototype._resizeShape = function (ctx) { - if (!this.width) { - this.options.radius= this.baseRadiusValue; - var size = 2 * this.options.radius; - this.width = size; - this.height = size; + for (i = 0; i < 12; i++) { + // make the regex if we don't have it already + mom = moment.utc([2000, i]); + if (strict && !this._longMonthsParse[i]) { + this._longMonthsParse[i] = new RegExp('^' + this.months(mom, '').replace('.', '') + '$', 'i'); + this._shortMonthsParse[i] = new RegExp('^' + this.monthsShort(mom, '').replace('.', '') + '$', 'i'); + } + if (!strict && !this._monthsParse[i]) { + regex = '^' + this.months(mom, '') + '|^' + this.monthsShort(mom, ''); + this._monthsParse[i] = new RegExp(regex.replace('.', ''), 'i'); + } + // test the regex + if (strict && format === 'MMMM' && this._longMonthsParse[i].test(monthName)) { + return i; + } else if (strict && format === 'MMM' && this._shortMonthsParse[i].test(monthName)) { + return i; + } else if (!strict && this._monthsParse[i].test(monthName)) { + return i; + } + } + }, - // scaling used for clustering - this.width += Math.min(this.clusterSize - 1, this.maxNodeSizeIncrements) * this.clusterSizeWidthFactor; - this.height += Math.min(this.clusterSize - 1, this.maxNodeSizeIncrements) * this.clusterSizeHeightFactor; - this.options.radius+= Math.min(this.clusterSize - 1, this.maxNodeSizeIncrements) * 0.5 * this.clusterSizeRadiusFactor; - this.growthIndicator = this.width - size; - } - }; + _weekdays : 'Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday'.split('_'), + weekdays : function (m) { + return this._weekdays[m.day()]; + }, - Node.prototype._drawShape = function (ctx, shape) { - this._resizeShape(ctx); + _weekdaysShort : 'Sun_Mon_Tue_Wed_Thu_Fri_Sat'.split('_'), + weekdaysShort : function (m) { + return this._weekdaysShort[m.day()]; + }, - this.left = this.x - this.width / 2; - this.top = this.y - this.height / 2; + _weekdaysMin : 'Su_Mo_Tu_We_Th_Fr_Sa'.split('_'), + weekdaysMin : function (m) { + return this._weekdaysMin[m.day()]; + }, - var clusterLineWidth = 2.5; - var borderWidth = this.options.borderWidth; - var selectionLineWidth = this.options.borderWidthSelected || 2 * this.options.borderWidth; - var radiusMultiplier = 2; + weekdaysParse : function (weekdayName) { + var i, mom, regex; - // choose draw method depending on the shape - switch (shape) { - case 'dot': radiusMultiplier = 2; break; - case 'square': radiusMultiplier = 2; break; - case 'triangle': radiusMultiplier = 3; break; - case 'triangleDown': radiusMultiplier = 3; break; - case 'star': radiusMultiplier = 4; break; - } + if (!this._weekdaysParse) { + this._weekdaysParse = []; + } - ctx.strokeStyle = this.selected ? this.options.color.highlight.border : this.hover ? this.options.color.hover.border : this.options.color.border; - // draw the outer border - if (this.clusterSize > 1) { - ctx.lineWidth = (this.selected ? selectionLineWidth : borderWidth) + ((this.clusterSize > 1) ? clusterLineWidth : 0.0); - ctx.lineWidth *= this.networkScaleInv; - ctx.lineWidth = Math.min(this.width,ctx.lineWidth); + for (i = 0; i < 7; i++) { + // make the regex if we don't have it already + if (!this._weekdaysParse[i]) { + mom = moment([2000, 1]).day(i); + regex = '^' + this.weekdays(mom, '') + '|^' + this.weekdaysShort(mom, '') + '|^' + this.weekdaysMin(mom, ''); + this._weekdaysParse[i] = new RegExp(regex.replace('.', ''), 'i'); + } + // test the regex + if (this._weekdaysParse[i].test(weekdayName)) { + return i; + } + } + }, - ctx[shape](this.x, this.y, this.options.radius+ radiusMultiplier * ctx.lineWidth); - ctx.stroke(); - } - ctx.lineWidth = (this.selected ? selectionLineWidth : borderWidth) + ((this.clusterSize > 1) ? clusterLineWidth : 0.0); - ctx.lineWidth *= this.networkScaleInv; - ctx.lineWidth = Math.min(this.width,ctx.lineWidth); + _longDateFormat : { + LTS : 'h:mm:ss A', + LT : 'h:mm A', + L : 'MM/DD/YYYY', + LL : 'MMMM D, YYYY', + LLL : 'MMMM D, YYYY LT', + LLLL : 'dddd, MMMM D, YYYY LT' + }, + longDateFormat : function (key) { + var output = this._longDateFormat[key]; + if (!output && this._longDateFormat[key.toUpperCase()]) { + output = this._longDateFormat[key.toUpperCase()].replace(/MMMM|MM|DD|dddd/g, function (val) { + return val.slice(1); + }); + this._longDateFormat[key] = output; + } + return output; + }, - ctx.fillStyle = this.selected ? this.options.color.highlight.background : this.hover ? this.options.color.hover.background : this.options.color.background; - ctx[shape](this.x, this.y, this.options.radius); - ctx.fill(); - ctx.stroke(); + isPM : function (input) { + // IE8 Quirks Mode & IE7 Standards Mode do not allow accessing strings like arrays + // Using charAt should be more compatible. + return ((input + '').toLowerCase().charAt(0) === 'p'); + }, - this.boundingBox.top = this.y - this.options.radius; - this.boundingBox.left = this.x - this.options.radius; - this.boundingBox.right = this.x + this.options.radius; - this.boundingBox.bottom = this.y + this.options.radius; + _meridiemParse : /[ap]\.?m?\.?/i, + meridiem : function (hours, minutes, isLower) { + if (hours > 11) { + return isLower ? 'pm' : 'PM'; + } else { + return isLower ? 'am' : 'AM'; + } + }, - if (this.label) { - this._label(ctx, this.label, this.x, this.y + this.height / 2, undefined, 'hanging',true); - this.boundingBox.left = Math.min(this.boundingBox.left, this.labelDimensions.left); - this.boundingBox.right = Math.max(this.boundingBox.right, this.labelDimensions.left + this.labelDimensions.width); - this.boundingBox.bottom = Math.max(this.boundingBox.bottom, this.boundingBox.bottom + this.labelDimensions.height); - } - }; - Node.prototype._resizeText = function (ctx) { - if (!this.width) { - var margin = 5; - var textSize = this.getTextSize(ctx); - this.width = textSize.width + 2 * margin; - this.height = textSize.height + 2 * margin; + _calendar : { + sameDay : '[Today at] LT', + nextDay : '[Tomorrow at] LT', + nextWeek : 'dddd [at] LT', + lastDay : '[Yesterday at] LT', + lastWeek : '[Last] dddd [at] LT', + sameElse : 'L' + }, + calendar : function (key, mom, now) { + var output = this._calendar[key]; + return typeof output === 'function' ? output.apply(mom, [now]) : output; + }, - // scaling used for clustering - this.width += Math.min(this.clusterSize - 1, this.maxNodeSizeIncrements) * this.clusterSizeWidthFactor; - this.height += Math.min(this.clusterSize - 1, this.maxNodeSizeIncrements) * this.clusterSizeHeightFactor; - this.options.radius+= Math.min(this.clusterSize - 1, this.maxNodeSizeIncrements) * this.clusterSizeRadiusFactor; - this.growthIndicator = this.width - (textSize.width + 2 * margin); - } - }; + _relativeTime : { + future : 'in %s', + past : '%s ago', + s : 'a few seconds', + m : 'a minute', + mm : '%d minutes', + h : 'an hour', + hh : '%d hours', + d : 'a day', + dd : '%d days', + M : 'a month', + MM : '%d months', + y : 'a year', + yy : '%d years' + }, - Node.prototype._drawText = function (ctx) { - this._resizeText(ctx); - this.left = this.x - this.width / 2; - this.top = this.y - this.height / 2; + relativeTime : function (number, withoutSuffix, string, isFuture) { + var output = this._relativeTime[string]; + return (typeof output === 'function') ? + output(number, withoutSuffix, string, isFuture) : + output.replace(/%d/i, number); + }, - this._label(ctx, this.label, this.x, this.y); + pastFuture : function (diff, output) { + var format = this._relativeTime[diff > 0 ? 'future' : 'past']; + return typeof format === 'function' ? format(output) : format.replace(/%s/i, output); + }, - this.boundingBox.top = this.top; - this.boundingBox.left = this.left; - this.boundingBox.right = this.left + this.width; - this.boundingBox.bottom = this.top + this.height; - }; + ordinal : function (number) { + return this._ordinal.replace('%d', number); + }, + _ordinal : '%d', + _ordinalParse : /\d{1,2}/, + preparse : function (string) { + return string; + }, - Node.prototype._label = function (ctx, text, x, y, align, baseline, labelUnderNode) { - if (text && Number(this.options.fontSize) * this.networkScale > this.fontDrawThreshold) { - ctx.font = (this.selected ? "bold " : "") + this.options.fontSize + "px " + this.options.fontFace; + postformat : function (string) { + return string; + }, - var lines = text.split('\n'); - var lineCount = lines.length; - var fontSize = Number(this.options.fontSize); - var yLine = y + (1 - lineCount) / 2 * fontSize; - if (labelUnderNode == true) { - yLine = y + (1 - lineCount) / (2 * fontSize); - } + week : function (mom) { + return weekOfYear(mom, this._week.dow, this._week.doy).week; + }, - // font fill from edges now for nodes! - var 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; - } - var height = this.options.fontSize * lineCount; - var left = x - width / 2; - var top = y - height / 2; - if (baseline == "hanging") { - top += 0.5 * fontSize; - top += 4; // distance from node, required because we use hanging. Hanging has less difference between browsers - yLine += 4; // distance from node - } - this.labelDimensions = {top:top,left:left,width:width,height:height,yLine:yLine}; + _week : { + dow : 0, // Sunday is the first day of the week. + doy : 6 // The week that contains Jan 1st is the first week of the year. + }, - // create the fontfill background - if (this.options.fontFill !== undefined && this.options.fontFill !== null && this.options.fontFill !== "none") { - ctx.fillStyle = this.options.fontFill; - ctx.fillRect(left, top, width, height); - } + firstDayOfWeek : function () { + return this._week.dow; + }, - // draw text - ctx.fillStyle = this.options.fontColor || "black"; - ctx.textAlign = align || "center"; - ctx.textBaseline = baseline || "middle"; - if (this.options.fontStrokeWidth > 0){ - ctx.lineWidth = this.options.fontStrokeWidth; - ctx.strokeStyle = this.options.fontStrokeColor; - ctx.lineJoin = 'round'; - } - for (var i = 0; i < lineCount; i++) { - if(this.options.fontStrokeWidth){ - ctx.strokeText(lines[i], x, yLine); - } - ctx.fillText(lines[i], x, yLine); - yLine += fontSize; - } - } - }; + firstDayOfYear : function () { + return this._week.doy; + }, + _invalidDate: 'Invalid date', + invalidDate: function () { + return this._invalidDate; + } + }); - Node.prototype.getTextSize = function(ctx) { - if (this.label !== undefined) { - ctx.font = (this.selected ? "bold " : "") + this.options.fontSize + "px " + this.options.fontFace; + /************************************ + Formatting + ************************************/ - var lines = this.label.split('\n'), - height = (Number(this.options.fontSize) + 4) * lines.length, - width = 0; - for (var i = 0, iMax = lines.length; i < iMax; i++) { - width = Math.max(width, ctx.measureText(lines[i]).width); + function removeFormattingTokens(input) { + if (input.match(/\[[\s\S]/)) { + return input.replace(/^\[|\]$/g, ''); + } + return input.replace(/\\/g, ''); } - return {"width": width, "height": height, lineCount: lines.length}; - } - else { - return {"width": 0, "height": 0, lineCount: 0}; - } - }; - - /** - * this is used to determine if a node is visible at all. this is used to determine when it needs to be drawn. - * there is a safety margin of 0.3 * width; - * - * @returns {boolean} - */ - Node.prototype.inArea = function() { - if (this.width !== undefined) { - return (this.x + this.width *this.networkScaleInv >= this.canvasTopLeft.x && - this.x - this.width *this.networkScaleInv < this.canvasBottomRight.x && - this.y + this.height*this.networkScaleInv >= this.canvasTopLeft.y && - this.y - this.height*this.networkScaleInv < this.canvasBottomRight.y); - } - else { - return true; - } - }; + function makeFormatFunction(format) { + var array = format.match(formattingTokens), i, length; - /** - * checks if the core of the node is in the display area, this is used for opening clusters around zoom - * @returns {boolean} - */ - Node.prototype.inView = function() { - return (this.x >= this.canvasTopLeft.x && - this.x < this.canvasBottomRight.x && - this.y >= this.canvasTopLeft.y && - this.y < this.canvasBottomRight.y); - }; + for (i = 0, length = array.length; i < length; i++) { + if (formatTokenFunctions[array[i]]) { + array[i] = formatTokenFunctions[array[i]]; + } else { + array[i] = removeFormattingTokens(array[i]); + } + } - /** - * This allows the zoom level of the network to influence the rendering - * We store the inverted scale and the coordinates of the top left, and bottom right points of the canvas - * - * @param scale - * @param canvasTopLeft - * @param canvasBottomRight - */ - Node.prototype.setScaleAndPos = function(scale,canvasTopLeft,canvasBottomRight) { - this.networkScaleInv = 1.0/scale; - this.networkScale = scale; - this.canvasTopLeft = canvasTopLeft; - this.canvasBottomRight = canvasBottomRight; - }; + return function (mom) { + var output = ''; + for (i = 0; i < length; i++) { + output += array[i] instanceof Function ? array[i].call(mom, format) : array[i]; + } + return output; + }; + } + // format date using native date object + function formatMoment(m, format) { + if (!m.isValid()) { + return m.localeData().invalidDate(); + } - /** - * This allows the zoom level of the network to influence the rendering - * - * @param scale - */ - Node.prototype.setScale = function(scale) { - this.networkScaleInv = 1.0/scale; - this.networkScale = scale; - }; + format = expandFormat(format, m.localeData()); + if (!formatFunctions[format]) { + formatFunctions[format] = makeFormatFunction(format); + } + return formatFunctions[format](m); + } - /** - * set the velocity at 0. Is called when this node is contained in another during clustering - */ - Node.prototype.clearVelocity = function() { - this.vx = 0; - this.vy = 0; - }; + function expandFormat(format, locale) { + var i = 5; + function replaceLongDateFormatTokens(input) { + return locale.longDateFormat(input) || input; + } - /** - * Basic preservation of (kinectic) energy - * - * @param massBeforeClustering - */ - Node.prototype.updateVelocity = function(massBeforeClustering) { - var energyBefore = this.vx * this.vx * massBeforeClustering; - //this.vx = (this.vx < 0) ? -Math.sqrt(energyBefore/this.options.mass) : Math.sqrt(energyBefore/this.options.mass); - this.vx = Math.sqrt(energyBefore/this.options.mass); - energyBefore = this.vy * this.vy * massBeforeClustering; - //this.vy = (this.vy < 0) ? -Math.sqrt(energyBefore/this.options.mass) : Math.sqrt(energyBefore/this.options.mass); - this.vy = Math.sqrt(energyBefore/this.options.mass); - }; + localFormattingTokens.lastIndex = 0; + while (i >= 0 && localFormattingTokens.test(format)) { + format = format.replace(localFormattingTokens, replaceLongDateFormatTokens); + localFormattingTokens.lastIndex = 0; + i -= 1; + } - module.exports = Node; + return format; + } -/***/ }, -/* 57 */ -/***/ function(module, exports, __webpack_require__) { + /************************************ + Parsing + ************************************/ - var util = __webpack_require__(1); - var Node = __webpack_require__(56); - /** - * @class Edge - * - * A edge connects two nodes - * @param {Object} properties Object with properties. Must contain - * At least properties from and to. - * Available properties: 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 - */ - function Edge (properties, network, networkConstants) { - if (!network) { - throw "No network provided"; - } - var fields = ['edges','physics']; - var constants = util.selectiveBridgeObject(fields,networkConstants); - this.options = constants.edges; - this.physics = constants.physics; - this.options['smoothCurves'] = networkConstants['smoothCurves']; + // get the regex to find the next token + function getParseRegexForToken(token, config) { + var a, strict = config._strict; + switch (token) { + case 'Q': + return parseTokenOneDigit; + case 'DDDD': + return parseTokenThreeDigits; + case 'YYYY': + case 'GGGG': + case 'gggg': + return strict ? parseTokenFourDigits : parseTokenOneToFourDigits; + case 'Y': + case 'G': + case 'g': + return parseTokenSignedNumber; + case 'YYYYYY': + case 'YYYYY': + case 'GGGGG': + case 'ggggg': + return strict ? parseTokenSixDigits : parseTokenOneToSixDigits; + case 'S': + if (strict) { + return parseTokenOneDigit; + } + /* falls through */ + case 'SS': + if (strict) { + return parseTokenTwoDigits; + } + /* falls through */ + case 'SSS': + if (strict) { + return parseTokenThreeDigits; + } + /* falls through */ + case 'DDD': + return parseTokenOneToThreeDigits; + case 'MMM': + case 'MMMM': + case 'dd': + case 'ddd': + case 'dddd': + return parseTokenWord; + case 'a': + case 'A': + return config._locale._meridiemParse; + case 'x': + return parseTokenOffsetMs; + case 'X': + return parseTokenTimestampMs; + case 'Z': + case 'ZZ': + return parseTokenTimezone; + case 'T': + return parseTokenT; + case 'SSSS': + return parseTokenDigits; + case 'MM': + case 'DD': + case 'YY': + case 'GG': + case 'gg': + case 'HH': + case 'hh': + case 'mm': + case 'ss': + case 'ww': + case 'WW': + return strict ? parseTokenTwoDigits : parseTokenOneOrTwoDigits; + case 'M': + case 'D': + case 'd': + case 'H': + case 'h': + case 'm': + case 's': + case 'w': + case 'W': + case 'e': + case 'E': + return parseTokenOneOrTwoDigits; + case 'Do': + return strict ? config._locale._ordinalParse : config._locale._ordinalParseLenient; + default : + a = new RegExp(regexpEscape(unescapeFormat(token.replace('\\', '')), 'i')); + return a; + } + } + function utcOffsetFromString(string) { + string = string || ''; + var possibleTzMatches = (string.match(parseTokenTimezone) || []), + tzChunk = possibleTzMatches[possibleTzMatches.length - 1] || [], + parts = (tzChunk + '').match(parseTimezoneChunker) || ['-', 0, 0], + minutes = +(parts[1] * 60) + toInt(parts[2]); - this.network = network; + return parts[0] === '+' ? minutes : -minutes; + } - // initialize variables - this.id = undefined; - this.fromId = undefined; - this.toId = undefined; - this.title = undefined; - this.widthSelected = this.options.width * this.options.widthSelectionMultiplier; - this.value = undefined; - this.selected = false; - this.hover = false; - this.labelDimensions = {top:0,left:0,width:0,height:0,yLine:0}; // could be cached - this.dirtyLabel = true; + // function to convert string input to date + function addTimeToArrayFromToken(token, input, config) { + var a, datePartArray = config._a; - this.from = null; // a node - this.to = null; // a node - this.via = null; // a temp node + switch (token) { + // QUARTER + case 'Q': + if (input != null) { + datePartArray[MONTH] = (toInt(input) - 1) * 3; + } + break; + // MONTH + case 'M' : // fall through to MM + case 'MM' : + if (input != null) { + datePartArray[MONTH] = toInt(input) - 1; + } + break; + case 'MMM' : // fall through to MMMM + case 'MMMM' : + a = config._locale.monthsParse(input, token, config._strict); + // if we didn't find a month name, mark the date as invalid. + if (a != null) { + datePartArray[MONTH] = a; + } else { + config._pf.invalidMonth = input; + } + break; + // DAY OF MONTH + case 'D' : // fall through to DD + case 'DD' : + if (input != null) { + datePartArray[DATE] = toInt(input); + } + break; + case 'Do' : + if (input != null) { + datePartArray[DATE] = toInt(parseInt( + input.match(/\d{1,2}/)[0], 10)); + } + break; + // DAY OF YEAR + case 'DDD' : // fall through to DDDD + case 'DDDD' : + if (input != null) { + config._dayOfYear = toInt(input); + } - this.fromBackup = null; // used to clean up after reconnect - this.toBackup = null;; // used to clean up after reconnect + break; + // YEAR + case 'YY' : + datePartArray[YEAR] = moment.parseTwoDigitYear(input); + break; + case 'YYYY' : + case 'YYYYY' : + case 'YYYYYY' : + datePartArray[YEAR] = toInt(input); + break; + // AM / PM + case 'a' : // fall through to A + case 'A' : + config._meridiem = input; + // config._isPm = config._locale.isPM(input); + break; + // HOUR + case 'h' : // fall through to hh + case 'hh' : + config._pf.bigHour = true; + /* falls through */ + case 'H' : // fall through to HH + case 'HH' : + datePartArray[HOUR] = toInt(input); + break; + // MINUTE + case 'm' : // fall through to mm + case 'mm' : + datePartArray[MINUTE] = toInt(input); + break; + // SECOND + case 's' : // fall through to ss + case 'ss' : + datePartArray[SECOND] = toInt(input); + break; + // MILLISECOND + case 'S' : + case 'SS' : + case 'SSS' : + case 'SSSS' : + datePartArray[MILLISECOND] = toInt(('0.' + input) * 1000); + break; + // UNIX OFFSET (MILLISECONDS) + case 'x': + config._d = new Date(toInt(input)); + break; + // UNIX TIMESTAMP WITH MS + case 'X': + config._d = new Date(parseFloat(input) * 1000); + break; + // TIMEZONE + case 'Z' : // fall through to ZZ + case 'ZZ' : + config._useUTC = true; + config._tzm = utcOffsetFromString(input); + break; + // WEEKDAY - human + case 'dd': + case 'ddd': + case 'dddd': + a = config._locale.weekdaysParse(input); + // if we didn't get a weekday name, mark the date as invalid + if (a != null) { + config._w = config._w || {}; + config._w['d'] = a; + } else { + config._pf.invalidWeekday = input; + } + break; + // WEEK, WEEK DAY - numeric + case 'w': + case 'ww': + case 'W': + case 'WW': + case 'd': + case 'e': + case 'E': + token = token.substr(0, 1); + /* falls through */ + case 'gggg': + case 'GGGG': + case 'GGGGG': + token = token.substr(0, 2); + if (input) { + config._w = config._w || {}; + config._w[token] = toInt(input); + } + break; + case 'gg': + case 'GG': + config._w = config._w || {}; + config._w[token] = moment.parseTwoDigitYear(input); + } + } - // we use this to be able to reconnect the edge to a cluster if its node is put into a cluster - // by storing the original information we can revert to the original connection when the cluser is opened. - this.originalFromId = []; - this.originalToId = []; + function dayOfYearFromWeekInfo(config) { + var w, weekYear, week, weekday, dow, doy, temp; - this.connected = false; + w = config._w; + if (w.GG != null || w.W != null || w.E != null) { + dow = 1; + doy = 4; - this.widthFixed = false; - this.lengthFixed = false; + // TODO: We need to take the current isoWeekYear, but that depends on + // how we interpret now (local, utc, fixed offset). So create + // a now version of current config (take local/utc/offset flags, and + // create now). + weekYear = dfl(w.GG, config._a[YEAR], weekOfYear(moment(), 1, 4).year); + week = dfl(w.W, 1); + weekday = dfl(w.E, 1); + } else { + dow = config._locale._week.dow; + doy = config._locale._week.doy; - this.setProperties(properties); + weekYear = dfl(w.gg, config._a[YEAR], weekOfYear(moment(), dow, doy).year); + week = dfl(w.w, 1); - this.controlNodesEnabled = false; - this.controlNodes = {from:null, to:null, positions:{}}; - this.connectedNode = null; - } + if (w.d != null) { + // weekday -- low day numbers are considered next week + weekday = w.d; + if (weekday < dow) { + ++week; + } + } else if (w.e != null) { + // local weekday -- counting starts from begining of week + weekday = w.e + dow; + } else { + // default to begining of week + weekday = dow; + } + } + temp = dayOfYearFromWeeks(weekYear, week, weekday, doy, dow); - /** - * Set or overwrite properties for the edge - * @param {Object} properties an object with properties - * @param {Object} constants and object with default, global properties - */ - Edge.prototype.setProperties = function(properties) { - if (!properties) { - return; - } + config._a[YEAR] = temp.year; + config._dayOfYear = temp.dayOfYear; + } - var fields = ['style','fontSize','fontFace','fontColor','fontFill','fontStrokeWidth','fontStrokeColor','width', - 'widthSelectionMultiplier','hoverWidth','arrowScaleFactor','dash','inheritColor','labelAlignment' - ]; - util.selectiveDeepExtend(fields, this.options, properties); + // convert an array to a date. + // the array should mirror the parameters below + // note: all values past the year are optional and will default to the lowest possible value. + // [year, month, day , hour, minute, second, millisecond] + function dateFromConfig(config) { + var i, date, input = [], currentDate, yearToUse; - if (properties.from !== undefined) {this.fromId = properties.from;} - if (properties.to !== undefined) {this.toId = properties.to;} + if (config._d) { + return; + } - if (properties.id !== undefined) {this.id = properties.id;} - if (properties.label !== undefined) {this.label = properties.label; this.dirtyLabel = true;} + currentDate = currentDateArray(config); - if (properties.title !== undefined) {this.title = properties.title;} - if (properties.value !== undefined) {this.value = properties.value;} - if (properties.length !== undefined) {this.physics.springLength = properties.length;} + //compute day of the year from weeks and weekdays + if (config._w && config._a[DATE] == null && config._a[MONTH] == null) { + dayOfYearFromWeekInfo(config); + } - if (properties.color !== undefined) { - this.options.inheritColor = false; - if (util.isString(properties.color)) { - this.options.color.color = properties.color; - this.options.color.highlight = properties.color; - } - else { - if (properties.color.color !== undefined) {this.options.color.color = properties.color.color;} - if (properties.color.highlight !== undefined) {this.options.color.highlight = properties.color.highlight;} - if (properties.color.hover !== undefined) {this.options.color.hover = properties.color.hover;} - } - } + //if the day of the year is set, figure out what it is + if (config._dayOfYear) { + yearToUse = dfl(config._a[YEAR], currentDate[YEAR]); - // A node is connected when it has a from and to node. - this.connect(); + if (config._dayOfYear > daysInYear(yearToUse)) { + config._pf._overflowDayOfYear = true; + } - this.widthFixed = this.widthFixed || (properties.width !== undefined); - this.lengthFixed = this.lengthFixed || (properties.length !== undefined); + date = makeUTCDate(yearToUse, 0, config._dayOfYear); + config._a[MONTH] = date.getUTCMonth(); + config._a[DATE] = date.getUTCDate(); + } - this.widthSelected = this.options.width* this.options.widthSelectionMultiplier; + // Default to current date. + // * if no year, month, day of month are given, default to today + // * if day of month is given, default month and year + // * if month is given, default only year + // * if year is given, don't default anything + for (i = 0; i < 3 && config._a[i] == null; ++i) { + config._a[i] = input[i] = currentDate[i]; + } - // set draw method based on style - switch (this.options.style) { - case 'line': this.draw = this._drawLine; break; - case 'arrow': this.draw = this._drawArrow; break; - case 'arrow-center': this.draw = this._drawArrowCenter; break; - case 'dash-line': this.draw = this._drawDashLine; break; - default: this.draw = this._drawLine; break; - } - - }; + // Zero out whatever was not defaulted, including time + for (; i < 7; i++) { + config._a[i] = input[i] = (config._a[i] == null) ? (i === 2 ? 1 : 0) : config._a[i]; + } - /** - * Connect an edge to its nodes - */ - Edge.prototype.connect = function () { - this.disconnect(); + // Check for 24:00:00.000 + if (config._a[HOUR] === 24 && + config._a[MINUTE] === 0 && + config._a[SECOND] === 0 && + config._a[MILLISECOND] === 0) { + config._nextDay = true; + config._a[HOUR] = 0; + } - this.from = this.network.nodes[this.fromId] || null; - this.to = this.network.nodes[this.toId] || null; - this.connected = (this.from && this.to); + config._d = (config._useUTC ? makeUTCDate : makeDate).apply(null, input); + // Apply timezone offset from input. The actual utcOffset can be changed + // with parseZone. + if (config._tzm != null) { + config._d.setUTCMinutes(config._d.getUTCMinutes() - config._tzm); + } - if (this.connected) { - this.from.attachEdge(this); - this.to.attachEdge(this); - } - else { - if (this.from) { - this.from.detachEdge(this); - } - if (this.to) { - this.to.detachEdge(this); + if (config._nextDay) { + config._a[HOUR] = 24; + } } - } - }; - /** - * Disconnect an edge from its nodes - */ - Edge.prototype.disconnect = function () { - if (this.from) { - this.from.detachEdge(this); - this.from = null; - } - if (this.to) { - this.to.detachEdge(this); - this.to = null; - } + function dateFromObject(config) { + var normalizedInput; - this.connected = false; - }; + if (config._d) { + return; + } - /** - * get the title of this edge. - * @return {string} title The title of the edge, or undefined when no title - * has been set. - */ - Edge.prototype.getTitle = function() { - return typeof this.title === "function" ? this.title() : this.title; - }; + normalizedInput = normalizeObjectUnits(config._i); + config._a = [ + normalizedInput.year, + normalizedInput.month, + normalizedInput.day || normalizedInput.date, + normalizedInput.hour, + normalizedInput.minute, + normalizedInput.second, + normalizedInput.millisecond + ]; + dateFromConfig(config); + } - /** - * Retrieve the value of the edge. Can be undefined - * @return {Number} value - */ - Edge.prototype.getValue = function() { - return this.value; - }; + function currentDateArray(config) { + var now = new Date(); + if (config._useUTC) { + return [ + now.getUTCFullYear(), + now.getUTCMonth(), + now.getUTCDate() + ]; + } else { + return [now.getFullYear(), now.getMonth(), now.getDate()]; + } + } - /** - * Adjust the value range of the edge. The edge will adjust it's width - * based on its value. - * @param {Number} min - * @param {Number} max - */ - Edge.prototype.setValueRange = function(min, max) { - if (!this.widthFixed && this.value !== undefined) { - var scale = (this.options.widthMax - this.options.widthMin) / (max - min); - this.options.width= (this.value - min) * scale + this.options.widthMin; - this.widthSelected = this.options.width* this.options.widthSelectionMultiplier; - } - }; + // date from string and format string + function makeDateFromStringAndFormat(config) { + if (config._f === moment.ISO_8601) { + parseISO(config); + return; + } - /** - * 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 - */ - Edge.prototype.draw = function(ctx) { - throw "Method draw not initialized in edge"; - }; - - /** - * 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 - */ - Edge.prototype.isOverlappingWith = function(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._getDistanceToEdge(xFrom, yFrom, xTo, yTo, xObj, yObj); - - return (dist < distMax); - } - else { - return false - } - }; - - Edge.prototype._getColor = function() { - var colorObj = this.options.color; - if (this.options.inheritColor == "to") { - colorObj = { - highlight: this.to.options.color.highlight.border, - hover: this.to.options.color.hover.border, - color: this.to.options.color.border - }; - } - else if (this.options.inheritColor == "from" || this.options.inheritColor == true) { - colorObj = { - highlight: this.from.options.color.highlight.border, - hover: this.from.options.color.hover.border, - color: this.from.options.color.border - }; - } + config._a = []; + config._pf.empty = true; - if (this.selected == true) {return colorObj.highlight;} - else if (this.hover == true) {return colorObj.hover;} - else {return colorObj.color;} - }; + // This array is used to make a Date, either with `new Date` or `Date.UTC` + var string = '' + config._i, + i, parsedInput, tokens, token, skipped, + stringLength = string.length, + totalParsedInputLength = 0; + tokens = expandFormat(config._f, config._locale).match(formattingTokens) || []; - /** - * Redraw a edge as a line - * Draw this edge in the given canvas - * The 2d context of a HTML canvas can be retrieved by canvas.getContext("2d"); - * @param {CanvasRenderingContext2D} ctx - * @private - */ - Edge.prototype._drawLine = function(ctx) { - // set style - ctx.strokeStyle = this._getColor(); - ctx.lineWidth = this._getLineWidth(); + for (i = 0; i < tokens.length; i++) { + token = tokens[i]; + parsedInput = (string.match(getParseRegexForToken(token, config)) || [])[0]; + if (parsedInput) { + skipped = string.substr(0, string.indexOf(parsedInput)); + if (skipped.length > 0) { + config._pf.unusedInput.push(skipped); + } + string = string.slice(string.indexOf(parsedInput) + parsedInput.length); + totalParsedInputLength += parsedInput.length; + } + // don't parse if it's not a known token + if (formatTokenFunctions[token]) { + if (parsedInput) { + config._pf.empty = false; + } + else { + config._pf.unusedTokens.push(token); + } + addTimeToArrayFromToken(token, parsedInput, config); + } + else if (config._strict && !parsedInput) { + config._pf.unusedTokens.push(token); + } + } - if (this.from != this.to) { - // draw line - var via = this._line(ctx); + // add remaining unparsed input length to the string + config._pf.charsLeftOver = stringLength - totalParsedInputLength; + if (string.length > 0) { + config._pf.unusedInput.push(string); + } - // draw label - var point; - if (this.label) { - if (this.options.smoothCurves.enabled == true && via != null) { - var midpointX = 0.5*(0.5*(this.from.x + via.x) + 0.5*(this.to.x + via.x)); - var midpointY = 0.5*(0.5*(this.from.y + via.y) + 0.5*(this.to.y + via.y)); - point = {x:midpointX, y:midpointY}; - } - else { - point = this._pointOnLine(0.5); - } - this._label(ctx, this.label, point.x, point.y); - } - } - else { - var x, y; - var radius = this.physics.springLength / 4; - var node = this.from; - if (!node.width) { - node.resize(ctx); - } - if (node.width > node.height) { - x = node.x + node.width / 2; - y = node.y - radius; - } - else { - x = node.x + radius; - y = node.y - node.height / 2; + // clear _12h flag if hour is <= 12 + if (config._pf.bigHour === true && config._a[HOUR] <= 12) { + config._pf.bigHour = undefined; + } + // handle meridiem + config._a[HOUR] = meridiemFixWrap(config._locale, config._a[HOUR], + config._meridiem); + dateFromConfig(config); + checkOverflow(config); } - this._circle(ctx, x, y, radius); - point = this._pointOnCircle(x, y, radius, 0.5); - this._label(ctx, this.label, point.x, point.y); - } - }; - /** - * Get the line width of the edge. Depends on width and whether one of the - * connected nodes is selected. - * @return {Number} width - * @private - */ - Edge.prototype._getLineWidth = function() { - if (this.selected == true) { - return Math.max(Math.min(this.widthSelected, this.options.widthMax), 0.3*this.networkScaleInv); - } - else { - if (this.hover == true) { - return Math.max(Math.min(this.options.hoverWidth, this.options.widthMax), 0.3*this.networkScaleInv); + function unescapeFormat(s) { + return s.replace(/\\(\[)|\\(\])|\[([^\]\[]*)\]|\\(.)/g, function (matched, p1, p2, p3, p4) { + return p1 || p2 || p3 || p4; + }); } - else { - return Math.max(this.options.width, 0.3*this.networkScaleInv); + + // Code from http://stackoverflow.com/questions/3561493/is-there-a-regexp-escape-function-in-javascript + function regexpEscape(s) { + return s.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&'); } - } - }; - Edge.prototype._getViaCoordinates = function () { - if (this.options.smoothCurves.dynamic == true && this.options.smoothCurves.enabled == true ) { - return this.via; - } - else if (this.options.smoothCurves.enabled == false) { - return {x:0,y:0}; - } - else { - var xVia = null; - var yVia = null; - var factor = this.options.smoothCurves.roundness; - var type = this.options.smoothCurves.type; + // date from string and array of format strings + function makeDateFromStringAndArray(config) { + var tempConfig, + bestMoment, - var dx = Math.abs(this.from.x - this.to.x); - var dy = Math.abs(this.from.y - this.to.y); - if (type == 'discrete' || type == 'diagonalCross') { - if (Math.abs(this.from.x - this.to.x) < Math.abs(this.from.y - this.to.y)) { - if (this.from.y > this.to.y) { - if (this.from.x < this.to.x) { - xVia = this.from.x + factor * dy; - yVia = this.from.y - factor * dy; - } - else if (this.from.x > this.to.x) { - xVia = this.from.x - factor * dy; - yVia = this.from.y - factor * dy; - } - } - else if (this.from.y < this.to.y) { - if (this.from.x < this.to.x) { - xVia = this.from.x + factor * dy; - yVia = this.from.y + factor * dy; - } - else if (this.from.x > this.to.x) { - xVia = this.from.x - factor * dy; - yVia = this.from.y + factor * dy; - } - } - if (type == "discrete") { - xVia = dx < factor * dy ? this.from.x : xVia; - } - } - else if (Math.abs(this.from.x - this.to.x) > Math.abs(this.from.y - this.to.y)) { - if (this.from.y > this.to.y) { - if (this.from.x < this.to.x) { - xVia = this.from.x + factor * dx; - yVia = this.from.y - factor * dx; - } - else if (this.from.x > this.to.x) { - xVia = this.from.x - factor * dx; - yVia = this.from.y - factor * dx; - } - } - else if (this.from.y < this.to.y) { - if (this.from.x < this.to.x) { - xVia = this.from.x + factor * dx; - yVia = this.from.y + factor * dx; - } - else if (this.from.x > this.to.x) { - xVia = this.from.x - factor * dx; - yVia = this.from.y + factor * dx; - } + scoreToBeat, + i, + currentScore; + + if (config._f.length === 0) { + config._pf.invalidFormat = true; + config._d = new Date(NaN); + return; } - if (type == "discrete") { - yVia = dy < factor * dx ? this.from.y : yVia; + + for (i = 0; i < config._f.length; i++) { + currentScore = 0; + tempConfig = copyConfig({}, config); + if (config._useUTC != null) { + tempConfig._useUTC = config._useUTC; + } + tempConfig._pf = defaultParsingFlags(); + tempConfig._f = config._f[i]; + makeDateFromStringAndFormat(tempConfig); + + if (!isValid(tempConfig)) { + continue; + } + + // if there is any input that was not parsed add a penalty for that format + currentScore += tempConfig._pf.charsLeftOver; + + //or tokens + currentScore += tempConfig._pf.unusedTokens.length * 10; + + tempConfig._pf.score = currentScore; + + if (scoreToBeat == null || currentScore < scoreToBeat) { + scoreToBeat = currentScore; + bestMoment = tempConfig; + } } - } + + extend(config, bestMoment || tempConfig); } - else if (type == "straightCross") { - if (Math.abs(this.from.x - this.to.x) < Math.abs(this.from.y - this.to.y)) { // up - down - xVia = this.from.x; - if (this.from.y < this.to.y) { - yVia = this.to.y - (1 - factor) * dy; - } - else { - yVia = this.to.y + (1 - factor) * dy; - } - } - else if (Math.abs(this.from.x - this.to.x) > Math.abs(this.from.y - this.to.y)) { // left - right - if (this.from.x < this.to.x) { - xVia = this.to.x - (1 - factor) * dx; - } - else { - xVia = this.to.x + (1 - factor) * dx; + + // date from iso format + function parseISO(config) { + var i, l, + string = config._i, + match = isoRegex.exec(string); + + if (match) { + config._pf.iso = true; + for (i = 0, l = isoDates.length; i < l; i++) { + if (isoDates[i][1].exec(string)) { + // match[5] should be 'T' or undefined + config._f = isoDates[i][0] + (match[6] || ' '); + break; + } + } + for (i = 0, l = isoTimes.length; i < l; i++) { + if (isoTimes[i][1].exec(string)) { + config._f += isoTimes[i][0]; + break; + } + } + if (string.match(parseTokenTimezone)) { + config._f += 'Z'; + } + makeDateFromStringAndFormat(config); + } else { + config._isValid = false; } - yVia = this.from.y; - } } - else if (type == 'horizontal') { - if (this.from.x < this.to.x) { - xVia = this.to.x - (1 - factor) * dx; - } - else { - xVia = this.to.x + (1 - factor) * dx; - } - yVia = this.from.y; + + // date from iso format or fallback + function makeDateFromString(config) { + parseISO(config); + if (config._isValid === false) { + delete config._isValid; + moment.createFromInputFallback(config); + } } - else if (type == 'vertical') { - xVia = this.from.x; - if (this.from.y < this.to.y) { - yVia = this.to.y - (1 - factor) * dy; - } - else { - yVia = this.to.y + (1 - factor) * dy; - } + + function map(arr, fn) { + var res = [], i; + for (i = 0; i < arr.length; ++i) { + res.push(fn(arr[i], i)); + } + return res; } - else { // continuous - if (Math.abs(this.from.x - this.to.x) < Math.abs(this.from.y - this.to.y)) { - if (this.from.y > this.to.y) { - if (this.from.x < this.to.x) { - // console.log(1) - xVia = this.from.x + factor * dy; - yVia = this.from.y - factor * dy; - xVia = this.to.x < xVia ? this.to.x : xVia; - } - else if (this.from.x > this.to.x) { - // console.log(2) - xVia = this.from.x - factor * dy; - yVia = this.from.y - factor * dy; - xVia = this.to.x > xVia ? this.to.x : xVia; - } + + function makeDateFromInput(config) { + var input = config._i, matched; + if (input === undefined) { + config._d = new Date(); + } else if (isDate(input)) { + config._d = new Date(+input); + } else if ((matched = aspNetJsonRegex.exec(input)) !== null) { + config._d = new Date(+matched[1]); + } else if (typeof input === 'string') { + makeDateFromString(config); + } else if (isArray(input)) { + config._a = map(input.slice(0), function (obj) { + return parseInt(obj, 10); + }); + dateFromConfig(config); + } else if (typeof(input) === 'object') { + dateFromObject(config); + } else if (typeof(input) === 'number') { + // from milliseconds + config._d = new Date(input); + } else { + moment.createFromInputFallback(config); } - else if (this.from.y < this.to.y) { - if (this.from.x < this.to.x) { - // console.log(3) - xVia = this.from.x + factor * dy; - yVia = this.from.y + factor * dy; - xVia = this.to.x < xVia ? this.to.x : xVia; - } - else if (this.from.x > this.to.x) { - // console.log(4, this.from.x, this.to.x) - xVia = this.from.x - factor * dy; - yVia = this.from.y + factor * dy; - xVia = this.to.x > xVia ? this.to.x : xVia; - } + } + + function makeDate(y, m, d, h, M, s, ms) { + //can't just apply() to create a date: + //http://stackoverflow.com/questions/181348/instantiating-a-javascript-object-by-calling-prototype-constructor-apply + var date = new Date(y, m, d, h, M, s, ms); + + //the date constructor doesn't accept years < 1970 + if (y < 1970) { + date.setFullYear(y); } - } - else if (Math.abs(this.from.x - this.to.x) > Math.abs(this.from.y - this.to.y)) { - if (this.from.y > this.to.y) { - if (this.from.x < this.to.x) { - // console.log(5) - xVia = this.from.x + factor * dx; - yVia = this.from.y - factor * dx; - yVia = this.to.y > yVia ? this.to.y : yVia; - } - else if (this.from.x > this.to.x) { - // console.log(6) - xVia = this.from.x - factor * dx; - yVia = this.from.y - factor * dx; - yVia = this.to.y > yVia ? this.to.y : yVia; - } + return date; + } + + function makeUTCDate(y) { + var date = new Date(Date.UTC.apply(null, arguments)); + if (y < 1970) { + date.setUTCFullYear(y); } - else if (this.from.y < this.to.y) { - if (this.from.x < this.to.x) { - // console.log(7) - xVia = this.from.x + factor * dx; - yVia = this.from.y + factor * dx; - yVia = this.to.y < yVia ? this.to.y : yVia; - } - else if (this.from.x > this.to.x) { - // console.log(8) - xVia = this.from.x - factor * dx; - yVia = this.from.y + factor * dx; - yVia = this.to.y < yVia ? this.to.y : yVia; - } + return date; + } + + function parseWeekday(input, locale) { + if (typeof input === 'string') { + if (!isNaN(input)) { + input = parseInt(input, 10); + } + else { + input = locale.weekdaysParse(input); + if (typeof input !== 'number') { + return null; + } + } } - } + return input; } + /************************************ + Relative Time + ************************************/ - return {x: xVia, y: yVia}; - } - }; - /** - * Draw a line between two nodes - * @param {CanvasRenderingContext2D} ctx - * @private - */ - Edge.prototype._line = function (ctx) { - // draw a straight line - ctx.beginPath(); - ctx.moveTo(this.from.x, this.from.y); - if (this.options.smoothCurves.enabled == true) { - if (this.options.smoothCurves.dynamic == false) { - var via = this._getViaCoordinates(); - if (via.x == null) { - ctx.lineTo(this.to.x, this.to.y); - ctx.stroke(); - return null; - } - else { - // this.via.x = via.x; - // this.via.y = via.y; - ctx.quadraticCurveTo(via.x,via.y,this.to.x, this.to.y); - ctx.stroke(); - return via; - } - } - else { - ctx.quadraticCurveTo(this.via.x,this.via.y,this.to.x, this.to.y); - ctx.stroke(); - return this.via; + // helper function for moment.fn.from, moment.fn.fromNow, and moment.duration.fn.humanize + function substituteTimeAgo(string, number, withoutSuffix, isFuture, locale) { + return locale.relativeTime(number || 1, !!withoutSuffix, string, isFuture); } - } - else { - ctx.lineTo(this.to.x, this.to.y); - ctx.stroke(); - return null; - } - }; - /** - * Draw a line from a node to itself, a circle - * @param {CanvasRenderingContext2D} ctx - * @param {Number} x - * @param {Number} y - * @param {Number} radius - * @private + function relativeTime(posNegDuration, withoutSuffix, locale) { + var duration = moment.duration(posNegDuration).abs(), + seconds = round(duration.as('s')), + minutes = round(duration.as('m')), + hours = round(duration.as('h')), + days = round(duration.as('d')), + months = round(duration.as('M')), + years = round(duration.as('y')), + + args = seconds < relativeTimeThresholds.s && ['s', seconds] || + minutes === 1 && ['m'] || + minutes < relativeTimeThresholds.m && ['mm', minutes] || + hours === 1 && ['h'] || + hours < relativeTimeThresholds.h && ['hh', hours] || + days === 1 && ['d'] || + days < relativeTimeThresholds.d && ['dd', days] || + months === 1 && ['M'] || + months < relativeTimeThresholds.M && ['MM', months] || + years === 1 && ['y'] || ['yy', years]; + + args[2] = withoutSuffix; + args[3] = +posNegDuration > 0; + args[4] = locale; + return substituteTimeAgo.apply({}, args); + } + + + /************************************ + Week of Year + ************************************/ + + + // firstDayOfWeek 0 = sun, 6 = sat + // the day of the week that starts the week + // (usually sunday or monday) + // firstDayOfWeekOfYear 0 = sun, 6 = sat + // the first week is the week that contains the first + // of this day of the week + // (eg. ISO weeks use thursday (4)) + function weekOfYear(mom, firstDayOfWeek, firstDayOfWeekOfYear) { + var end = firstDayOfWeekOfYear - firstDayOfWeek, + daysToDayOfWeek = firstDayOfWeekOfYear - mom.day(), + adjustedMoment; + + + if (daysToDayOfWeek > end) { + daysToDayOfWeek -= 7; + } + + if (daysToDayOfWeek < end - 7) { + daysToDayOfWeek += 7; + } + + adjustedMoment = moment(mom).add(daysToDayOfWeek, 'd'); + return { + week: Math.ceil(adjustedMoment.dayOfYear() / 7), + year: adjustedMoment.year() + }; + } + + //http://en.wikipedia.org/wiki/ISO_week_date#Calculating_a_date_given_the_year.2C_week_number_and_weekday + function dayOfYearFromWeeks(year, week, weekday, firstDayOfWeekOfYear, firstDayOfWeek) { + var d = makeUTCDate(year, 0, 1).getUTCDay(), daysToAdd, dayOfYear; + + d = d === 0 ? 7 : d; + weekday = weekday != null ? weekday : firstDayOfWeek; + daysToAdd = firstDayOfWeek - d + (d > firstDayOfWeekOfYear ? 7 : 0) - (d < firstDayOfWeek ? 7 : 0); + dayOfYear = 7 * (week - 1) + (weekday - firstDayOfWeek) + daysToAdd + 1; + + return { + year: dayOfYear > 0 ? year : year - 1, + dayOfYear: dayOfYear > 0 ? dayOfYear : daysInYear(year - 1) + dayOfYear + }; + } + + /************************************ + Top Level Functions + ************************************/ + + function makeMoment(config) { + var input = config._i, + format = config._f, + res; + + config._locale = config._locale || moment.localeData(config._l); + + if (input === null || (format === undefined && input === '')) { + return moment.invalid({nullInput: true}); + } + + if (typeof input === 'string') { + config._i = input = config._locale.preparse(input); + } + + if (moment.isMoment(input)) { + return new Moment(input, true); + } else if (format) { + if (isArray(format)) { + makeDateFromStringAndArray(config); + } else { + makeDateFromStringAndFormat(config); + } + } else { + makeDateFromInput(config); + } + + res = new Moment(config); + if (res._nextDay) { + // Adding is smart enough around DST + res.add(1, 'd'); + res._nextDay = undefined; + } + + return res; + } + + moment = function (input, format, locale, strict) { + var c; + + if (typeof(locale) === 'boolean') { + strict = locale; + locale = undefined; + } + // object construction must be done this way. + // https://github.com/moment/moment/issues/1423 + c = {}; + c._isAMomentObject = true; + c._i = input; + c._f = format; + c._l = locale; + c._strict = strict; + c._isUTC = false; + c._pf = defaultParsingFlags(); + + return makeMoment(c); + }; + + moment.suppressDeprecationWarnings = false; + + moment.createFromInputFallback = deprecate( + 'moment construction falls back to js Date. This is ' + + 'discouraged and will be removed in upcoming major ' + + 'release. Please refer to ' + + 'https://github.com/moment/moment/issues/1407 for more info.', + function (config) { + config._d = new Date(config._i + (config._useUTC ? ' UTC' : '')); + } + ); + + // Pick a moment m from moments so that m[fn](other) is true for all + // other. This relies on the function fn to be transitive. + // + // moments should either be an array of moment objects or an array, whose + // first element is an array of moment objects. + function pickBy(fn, moments) { + var res, i; + if (moments.length === 1 && isArray(moments[0])) { + moments = moments[0]; + } + if (!moments.length) { + return moment(); + } + res = moments[0]; + for (i = 1; i < moments.length; ++i) { + if (moments[i][fn](res)) { + res = moments[i]; + } + } + return res; + } + + moment.min = function () { + var args = [].slice.call(arguments, 0); + + return pickBy('isBefore', args); + }; + + moment.max = function () { + var args = [].slice.call(arguments, 0); + + return pickBy('isAfter', args); + }; + + // creating with utc + moment.utc = function (input, format, locale, strict) { + var c; + + if (typeof(locale) === 'boolean') { + strict = locale; + locale = undefined; + } + // object construction must be done this way. + // https://github.com/moment/moment/issues/1423 + c = {}; + c._isAMomentObject = true; + c._useUTC = true; + c._isUTC = true; + c._l = locale; + c._i = input; + c._f = format; + c._strict = strict; + c._pf = defaultParsingFlags(); + + return makeMoment(c).utc(); + }; + + // creating with unix timestamp (in seconds) + moment.unix = function (input) { + return moment(input * 1000); + }; + + // duration + moment.duration = function (input, key) { + var duration = input, + // matching against regexp is expensive, do it on demand + match = null, + sign, + ret, + parseIso, + diffRes; + + if (moment.isDuration(input)) { + duration = { + ms: input._milliseconds, + d: input._days, + M: input._months + }; + } else if (typeof input === 'number') { + duration = {}; + if (key) { + duration[key] = input; + } else { + duration.milliseconds = input; + } + } else if (!!(match = aspNetTimeSpanJsonRegex.exec(input))) { + sign = (match[1] === '-') ? -1 : 1; + duration = { + y: 0, + d: toInt(match[DATE]) * sign, + h: toInt(match[HOUR]) * sign, + m: toInt(match[MINUTE]) * sign, + s: toInt(match[SECOND]) * sign, + ms: toInt(match[MILLISECOND]) * sign + }; + } else if (!!(match = isoDurationRegex.exec(input))) { + sign = (match[1] === '-') ? -1 : 1; + parseIso = function (inp) { + // We'd normally use ~~inp for this, but unfortunately it also + // converts floats to ints. + // inp may be undefined, so careful calling replace on it. + var res = inp && parseFloat(inp.replace(',', '.')); + // apply sign while we're at it + return (isNaN(res) ? 0 : res) * sign; + }; + duration = { + y: parseIso(match[2]), + M: parseIso(match[3]), + d: parseIso(match[4]), + h: parseIso(match[5]), + m: parseIso(match[6]), + s: parseIso(match[7]), + w: parseIso(match[8]) + }; + } else if (duration == null) {// checks for null or undefined + duration = {}; + } else if (typeof duration === 'object' && + ('from' in duration || 'to' in duration)) { + diffRes = momentsDifference(moment(duration.from), moment(duration.to)); + + duration = {}; + duration.ms = diffRes.milliseconds; + duration.M = diffRes.months; + } + + ret = new Duration(duration); + + if (moment.isDuration(input) && hasOwnProp(input, '_locale')) { + ret._locale = input._locale; + } + + return ret; + }; + + // version number + moment.version = VERSION; + + // default format + moment.defaultFormat = isoFormat; + + // constant that refers to the ISO standard + moment.ISO_8601 = function () {}; + + // Plugins that add properties should also add the key here (null value), + // so we can properly clone ourselves. + moment.momentProperties = momentProperties; + + // This function will be called whenever a moment is mutated. + // It is intended to keep the offset in sync with the timezone. + moment.updateOffset = function () {}; + + // This function allows you to set a threshold for relative time strings + moment.relativeTimeThreshold = function (threshold, limit) { + if (relativeTimeThresholds[threshold] === undefined) { + return false; + } + if (limit === undefined) { + return relativeTimeThresholds[threshold]; + } + relativeTimeThresholds[threshold] = limit; + return true; + }; + + moment.lang = deprecate( + 'moment.lang is deprecated. Use moment.locale instead.', + function (key, value) { + return moment.locale(key, value); + } + ); + + // This function will load locale and then set the global locale. If + // no arguments are passed in, it will simply return the current global + // locale key. + moment.locale = function (key, values) { + var data; + if (key) { + if (typeof(values) !== 'undefined') { + data = moment.defineLocale(key, values); + } + else { + data = moment.localeData(key); + } + + if (data) { + moment.duration._locale = moment._locale = data; + } + } + + return moment._locale._abbr; + }; + + moment.defineLocale = function (name, values) { + if (values !== null) { + values.abbr = name; + if (!locales[name]) { + locales[name] = new Locale(); + } + locales[name].set(values); + + // backwards compat for now: also set the locale + moment.locale(name); + + return locales[name]; + } else { + // useful for testing + delete locales[name]; + return null; + } + }; + + moment.langData = deprecate( + 'moment.langData is deprecated. Use moment.localeData instead.', + function (key) { + return moment.localeData(key); + } + ); + + // returns locale data + moment.localeData = function (key) { + var locale; + + if (key && key._locale && key._locale._abbr) { + key = key._locale._abbr; + } + + if (!key) { + return moment._locale; + } + + if (!isArray(key)) { + //short-circuit everything else + locale = loadLocale(key); + if (locale) { + return locale; + } + key = [key]; + } + + return chooseLocale(key); + }; + + // compare moment object + moment.isMoment = function (obj) { + return obj instanceof Moment || + (obj != null && hasOwnProp(obj, '_isAMomentObject')); + }; + + // for typechecking Duration objects + moment.isDuration = function (obj) { + return obj instanceof Duration; + }; + + for (i = lists.length - 1; i >= 0; --i) { + makeList(lists[i]); + } + + moment.normalizeUnits = function (units) { + return normalizeUnits(units); + }; + + moment.invalid = function (flags) { + var m = moment.utc(NaN); + if (flags != null) { + extend(m._pf, flags); + } + else { + m._pf.userInvalidated = true; + } + + return m; + }; + + moment.parseZone = function () { + return moment.apply(null, arguments).parseZone(); + }; + + moment.parseTwoDigitYear = function (input) { + return toInt(input) + (toInt(input) > 68 ? 1900 : 2000); + }; + + moment.isDate = isDate; + + /************************************ + Moment Prototype + ************************************/ + + + extend(moment.fn = Moment.prototype, { + + clone : function () { + return moment(this); + }, + + valueOf : function () { + return +this._d - ((this._offset || 0) * 60000); + }, + + unix : function () { + return Math.floor(+this / 1000); + }, + + toString : function () { + return this.clone().locale('en').format('ddd MMM DD YYYY HH:mm:ss [GMT]ZZ'); + }, + + toDate : function () { + return this._offset ? new Date(+this) : this._d; + }, + + toISOString : function () { + var m = moment(this).utc(); + if (0 < m.year() && m.year() <= 9999) { + if ('function' === typeof Date.prototype.toISOString) { + // native implementation is ~50x faster, use it when we can + return this.toDate().toISOString(); + } else { + return formatMoment(m, 'YYYY-MM-DD[T]HH:mm:ss.SSS[Z]'); + } + } else { + return formatMoment(m, 'YYYYYY-MM-DD[T]HH:mm:ss.SSS[Z]'); + } + }, + + toArray : function () { + var m = this; + return [ + m.year(), + m.month(), + m.date(), + m.hours(), + m.minutes(), + m.seconds(), + m.milliseconds() + ]; + }, + + isValid : function () { + return isValid(this); + }, + + isDSTShifted : function () { + if (this._a) { + return this.isValid() && compareArrays(this._a, (this._isUTC ? moment.utc(this._a) : moment(this._a)).toArray()) > 0; + } + + return false; + }, + + parsingFlags : function () { + return extend({}, this._pf); + }, + + invalidAt: function () { + return this._pf.overflow; + }, + + utc : function (keepLocalTime) { + return this.utcOffset(0, keepLocalTime); + }, + + local : function (keepLocalTime) { + if (this._isUTC) { + this.utcOffset(0, keepLocalTime); + this._isUTC = false; + + if (keepLocalTime) { + this.subtract(this._dateUtcOffset(), 'm'); + } + } + return this; + }, + + format : function (inputString) { + var output = formatMoment(this, inputString || moment.defaultFormat); + return this.localeData().postformat(output); + }, + + add : createAdder(1, 'add'), + + subtract : createAdder(-1, 'subtract'), + + diff : function (input, units, asFloat) { + var that = makeAs(input, this), + zoneDiff = (that.utcOffset() - this.utcOffset()) * 6e4, + anchor, diff, output, daysAdjust; + + units = normalizeUnits(units); + + if (units === 'year' || units === 'month' || units === 'quarter') { + output = monthDiff(this, that); + if (units === 'quarter') { + output = output / 3; + } else if (units === 'year') { + output = output / 12; + } + } else { + diff = this - that; + output = units === 'second' ? diff / 1e3 : // 1000 + units === 'minute' ? diff / 6e4 : // 1000 * 60 + units === 'hour' ? diff / 36e5 : // 1000 * 60 * 60 + units === 'day' ? (diff - zoneDiff) / 864e5 : // 1000 * 60 * 60 * 24, negate dst + units === 'week' ? (diff - zoneDiff) / 6048e5 : // 1000 * 60 * 60 * 24 * 7, negate dst + diff; + } + return asFloat ? output : absRound(output); + }, + + from : function (time, withoutSuffix) { + return moment.duration({to: this, from: time}).locale(this.locale()).humanize(!withoutSuffix); + }, + + fromNow : function (withoutSuffix) { + return this.from(moment(), withoutSuffix); + }, + + calendar : function (time) { + // We want to compare the start of today, vs this. + // Getting start-of-today depends on whether we're locat/utc/offset + // or not. + var now = time || moment(), + sod = makeAs(now, this).startOf('day'), + diff = this.diff(sod, 'days', true), + format = diff < -6 ? 'sameElse' : + diff < -1 ? 'lastWeek' : + diff < 0 ? 'lastDay' : + diff < 1 ? 'sameDay' : + diff < 2 ? 'nextDay' : + diff < 7 ? 'nextWeek' : 'sameElse'; + return this.format(this.localeData().calendar(format, this, moment(now))); + }, + + isLeapYear : function () { + return isLeapYear(this.year()); + }, + + isDST : function () { + return (this.utcOffset() > this.clone().month(0).utcOffset() || + this.utcOffset() > this.clone().month(5).utcOffset()); + }, + + day : function (input) { + var day = this._isUTC ? this._d.getUTCDay() : this._d.getDay(); + if (input != null) { + input = parseWeekday(input, this.localeData()); + return this.add(input - day, 'd'); + } else { + return day; + } + }, + + month : makeAccessor('Month', true), + + startOf : function (units) { + units = normalizeUnits(units); + // the following switch intentionally omits break keywords + // to utilize falling through the cases. + switch (units) { + case 'year': + this.month(0); + /* falls through */ + case 'quarter': + case 'month': + this.date(1); + /* falls through */ + case 'week': + case 'isoWeek': + case 'day': + this.hours(0); + /* falls through */ + case 'hour': + this.minutes(0); + /* falls through */ + case 'minute': + this.seconds(0); + /* falls through */ + case 'second': + this.milliseconds(0); + /* falls through */ + } + + // weeks are a special case + if (units === 'week') { + this.weekday(0); + } else if (units === 'isoWeek') { + this.isoWeekday(1); + } + + // quarters are also special + if (units === 'quarter') { + this.month(Math.floor(this.month() / 3) * 3); + } + + return this; + }, + + endOf: function (units) { + units = normalizeUnits(units); + if (units === undefined || units === 'millisecond') { + return this; + } + return this.startOf(units).add(1, (units === 'isoWeek' ? 'week' : units)).subtract(1, 'ms'); + }, + + isAfter: function (input, units) { + var inputMs; + units = normalizeUnits(typeof units !== 'undefined' ? units : 'millisecond'); + if (units === 'millisecond') { + input = moment.isMoment(input) ? input : moment(input); + return +this > +input; + } else { + inputMs = moment.isMoment(input) ? +input : +moment(input); + return inputMs < +this.clone().startOf(units); + } + }, + + isBefore: function (input, units) { + var inputMs; + units = normalizeUnits(typeof units !== 'undefined' ? units : 'millisecond'); + if (units === 'millisecond') { + input = moment.isMoment(input) ? input : moment(input); + return +this < +input; + } else { + inputMs = moment.isMoment(input) ? +input : +moment(input); + return +this.clone().endOf(units) < inputMs; + } + }, + + isBetween: function (from, to, units) { + return this.isAfter(from, units) && this.isBefore(to, units); + }, + + isSame: function (input, units) { + var inputMs; + units = normalizeUnits(units || 'millisecond'); + if (units === 'millisecond') { + input = moment.isMoment(input) ? input : moment(input); + return +this === +input; + } else { + inputMs = +moment(input); + return +(this.clone().startOf(units)) <= inputMs && inputMs <= +(this.clone().endOf(units)); + } + }, + + min: deprecate( + 'moment().min is deprecated, use moment.min instead. https://github.com/moment/moment/issues/1548', + function (other) { + other = moment.apply(null, arguments); + return other < this ? this : other; + } + ), + + max: deprecate( + 'moment().max is deprecated, use moment.max instead. https://github.com/moment/moment/issues/1548', + function (other) { + other = moment.apply(null, arguments); + return other > this ? this : other; + } + ), + + zone : deprecate( + 'moment().zone is deprecated, use moment().utcOffset instead. ' + + 'https://github.com/moment/moment/issues/1779', + function (input, keepLocalTime) { + if (input != null) { + if (typeof input !== 'string') { + input = -input; + } + + this.utcOffset(input, keepLocalTime); + + return this; + } else { + return -this.utcOffset(); + } + } + ), + + // keepLocalTime = true means only change the timezone, without + // affecting the local hour. So 5:31:26 +0300 --[utcOffset(2, true)]--> + // 5:31:26 +0200 It is possible that 5:31:26 doesn't exist with offset + // +0200, so we adjust the time as needed, to be valid. + // + // Keeping the time actually adds/subtracts (one hour) + // from the actual represented time. That is why we call updateOffset + // a second time. In case it wants us to change the offset again + // _changeInProgress == true case, then we have to adjust, because + // there is no such time in the given timezone. + utcOffset : function (input, keepLocalTime) { + var offset = this._offset || 0, + localAdjust; + if (input != null) { + if (typeof input === 'string') { + input = utcOffsetFromString(input); + } + if (Math.abs(input) < 16) { + input = input * 60; + } + if (!this._isUTC && keepLocalTime) { + localAdjust = this._dateUtcOffset(); + } + this._offset = input; + this._isUTC = true; + if (localAdjust != null) { + this.add(localAdjust, 'm'); + } + if (offset !== input) { + if (!keepLocalTime || this._changeInProgress) { + addOrSubtractDurationFromMoment(this, + moment.duration(input - offset, 'm'), 1, false); + } else if (!this._changeInProgress) { + this._changeInProgress = true; + moment.updateOffset(this, true); + this._changeInProgress = null; + } + } + + return this; + } else { + return this._isUTC ? offset : this._dateUtcOffset(); + } + }, + + isLocal : function () { + return !this._isUTC; + }, + + isUtcOffset : function () { + return this._isUTC; + }, + + isUtc : function () { + return this._isUTC && this._offset === 0; + }, + + zoneAbbr : function () { + return this._isUTC ? 'UTC' : ''; + }, + + zoneName : function () { + return this._isUTC ? 'Coordinated Universal Time' : ''; + }, + + parseZone : function () { + if (this._tzm) { + this.utcOffset(this._tzm); + } else if (typeof this._i === 'string') { + this.utcOffset(utcOffsetFromString(this._i)); + } + return this; + }, + + hasAlignedHourOffset : function (input) { + if (!input) { + input = 0; + } + else { + input = moment(input).utcOffset(); + } + + return (this.utcOffset() - input) % 60 === 0; + }, + + daysInMonth : function () { + return daysInMonth(this.year(), this.month()); + }, + + dayOfYear : function (input) { + var dayOfYear = round((moment(this).startOf('day') - moment(this).startOf('year')) / 864e5) + 1; + return input == null ? dayOfYear : this.add((input - dayOfYear), 'd'); + }, + + quarter : function (input) { + return input == null ? Math.ceil((this.month() + 1) / 3) : this.month((input - 1) * 3 + this.month() % 3); + }, + + weekYear : function (input) { + var year = weekOfYear(this, this.localeData()._week.dow, this.localeData()._week.doy).year; + return input == null ? year : this.add((input - year), 'y'); + }, + + isoWeekYear : function (input) { + var year = weekOfYear(this, 1, 4).year; + return input == null ? year : this.add((input - year), 'y'); + }, + + week : function (input) { + var week = this.localeData().week(this); + return input == null ? week : this.add((input - week) * 7, 'd'); + }, + + isoWeek : function (input) { + var week = weekOfYear(this, 1, 4).week; + return input == null ? week : this.add((input - week) * 7, 'd'); + }, + + weekday : function (input) { + var weekday = (this.day() + 7 - this.localeData()._week.dow) % 7; + return input == null ? weekday : this.add(input - weekday, 'd'); + }, + + isoWeekday : function (input) { + // behaves the same as moment#day except + // as a getter, returns 7 instead of 0 (1-7 range instead of 0-6) + // as a setter, sunday should belong to the previous week. + return input == null ? this.day() || 7 : this.day(this.day() % 7 ? input : input - 7); + }, + + isoWeeksInYear : function () { + return weeksInYear(this.year(), 1, 4); + }, + + weeksInYear : function () { + var weekInfo = this.localeData()._week; + return weeksInYear(this.year(), weekInfo.dow, weekInfo.doy); + }, + + get : function (units) { + units = normalizeUnits(units); + return this[units](); + }, + + set : function (units, value) { + var unit; + if (typeof units === 'object') { + for (unit in units) { + this.set(unit, units[unit]); + } + } + else { + units = normalizeUnits(units); + if (typeof this[units] === 'function') { + this[units](value); + } + } + return this; + }, + + // If passed a locale key, it will set the locale for this + // instance. Otherwise, it will return the locale configuration + // variables for this instance. + locale : function (key) { + var newLocaleData; + + if (key === undefined) { + return this._locale._abbr; + } else { + newLocaleData = moment.localeData(key); + if (newLocaleData != null) { + this._locale = newLocaleData; + } + return this; + } + }, + + lang : deprecate( + 'moment().lang() is deprecated. Instead, use moment().localeData() to get the language configuration. Use moment().locale() to change languages.', + function (key) { + if (key === undefined) { + return this.localeData(); + } else { + return this.locale(key); + } + } + ), + + localeData : function () { + return this._locale; + }, + + _dateUtcOffset : function () { + // On Firefox.24 Date#getTimezoneOffset returns a floating point. + // https://github.com/moment/moment/pull/1871 + return -Math.round(this._d.getTimezoneOffset() / 15) * 15; + } + + }); + + function rawMonthSetter(mom, value) { + var dayOfMonth; + + // TODO: Move this out of here! + if (typeof value === 'string') { + value = mom.localeData().monthsParse(value); + // TODO: Another silent failure? + if (typeof value !== 'number') { + return mom; + } + } + + dayOfMonth = Math.min(mom.date(), + daysInMonth(mom.year(), value)); + mom._d['set' + (mom._isUTC ? 'UTC' : '') + 'Month'](value, dayOfMonth); + return mom; + } + + function rawGetter(mom, unit) { + return mom._d['get' + (mom._isUTC ? 'UTC' : '') + unit](); + } + + function rawSetter(mom, unit, value) { + if (unit === 'Month') { + return rawMonthSetter(mom, value); + } else { + return mom._d['set' + (mom._isUTC ? 'UTC' : '') + unit](value); + } + } + + function makeAccessor(unit, keepTime) { + return function (value) { + if (value != null) { + rawSetter(this, unit, value); + moment.updateOffset(this, keepTime); + return this; + } else { + return rawGetter(this, unit); + } + }; + } + + moment.fn.millisecond = moment.fn.milliseconds = makeAccessor('Milliseconds', false); + moment.fn.second = moment.fn.seconds = makeAccessor('Seconds', false); + moment.fn.minute = moment.fn.minutes = makeAccessor('Minutes', false); + // Setting the hour should keep the time, because the user explicitly + // specified which hour he wants. So trying to maintain the same hour (in + // a new timezone) makes sense. Adding/subtracting hours does not follow + // this rule. + moment.fn.hour = moment.fn.hours = makeAccessor('Hours', true); + // moment.fn.month is defined separately + moment.fn.date = makeAccessor('Date', true); + moment.fn.dates = deprecate('dates accessor is deprecated. Use date instead.', makeAccessor('Date', true)); + moment.fn.year = makeAccessor('FullYear', true); + moment.fn.years = deprecate('years accessor is deprecated. Use year instead.', makeAccessor('FullYear', true)); + + // add plural methods + moment.fn.days = moment.fn.day; + moment.fn.months = moment.fn.month; + moment.fn.weeks = moment.fn.week; + moment.fn.isoWeeks = moment.fn.isoWeek; + moment.fn.quarters = moment.fn.quarter; + + // add aliased format methods + moment.fn.toJSON = moment.fn.toISOString; + + // alias isUtc for dev-friendliness + moment.fn.isUTC = moment.fn.isUtc; + + /************************************ + Duration Prototype + ************************************/ + + + function daysToYears (days) { + // 400 years have 146097 days (taking into account leap year rules) + return days * 400 / 146097; + } + + function yearsToDays (years) { + // years * 365 + absRound(years / 4) - + // absRound(years / 100) + absRound(years / 400); + return years * 146097 / 400; + } + + extend(moment.duration.fn = Duration.prototype, { + + _bubble : function () { + var milliseconds = this._milliseconds, + days = this._days, + months = this._months, + data = this._data, + seconds, minutes, hours, years = 0; + + // The following code bubbles up values, see the tests for + // examples of what that means. + data.milliseconds = milliseconds % 1000; + + seconds = absRound(milliseconds / 1000); + data.seconds = seconds % 60; + + minutes = absRound(seconds / 60); + data.minutes = minutes % 60; + + hours = absRound(minutes / 60); + data.hours = hours % 24; + + days += absRound(hours / 24); + + // Accurately convert days to years, assume start from year 0. + years = absRound(daysToYears(days)); + days -= absRound(yearsToDays(years)); + + // 30 days to a month + // TODO (iskren): Use anchor date (like 1st Jan) to compute this. + months += absRound(days / 30); + days %= 30; + + // 12 months -> 1 year + years += absRound(months / 12); + months %= 12; + + data.days = days; + data.months = months; + data.years = years; + }, + + abs : function () { + this._milliseconds = Math.abs(this._milliseconds); + this._days = Math.abs(this._days); + this._months = Math.abs(this._months); + + this._data.milliseconds = Math.abs(this._data.milliseconds); + this._data.seconds = Math.abs(this._data.seconds); + this._data.minutes = Math.abs(this._data.minutes); + this._data.hours = Math.abs(this._data.hours); + this._data.months = Math.abs(this._data.months); + this._data.years = Math.abs(this._data.years); + + return this; + }, + + weeks : function () { + return absRound(this.days() / 7); + }, + + valueOf : function () { + return this._milliseconds + + this._days * 864e5 + + (this._months % 12) * 2592e6 + + toInt(this._months / 12) * 31536e6; + }, + + humanize : function (withSuffix) { + var output = relativeTime(this, !withSuffix, this.localeData()); + + if (withSuffix) { + output = this.localeData().pastFuture(+this, output); + } + + return this.localeData().postformat(output); + }, + + add : function (input, val) { + // supports only 2.0-style add(1, 's') or add(moment) + var dur = moment.duration(input, val); + + this._milliseconds += dur._milliseconds; + this._days += dur._days; + this._months += dur._months; + + this._bubble(); + + return this; + }, + + subtract : function (input, val) { + var dur = moment.duration(input, val); + + this._milliseconds -= dur._milliseconds; + this._days -= dur._days; + this._months -= dur._months; + + this._bubble(); + + return this; + }, + + get : function (units) { + units = normalizeUnits(units); + return this[units.toLowerCase() + 's'](); + }, + + as : function (units) { + var days, months; + units = normalizeUnits(units); + + if (units === 'month' || units === 'year') { + days = this._days + this._milliseconds / 864e5; + months = this._months + daysToYears(days) * 12; + return units === 'month' ? months : months / 12; + } else { + // handle milliseconds separately because of floating point math errors (issue #1867) + days = this._days + Math.round(yearsToDays(this._months / 12)); + switch (units) { + case 'week': return days / 7 + this._milliseconds / 6048e5; + case 'day': return days + this._milliseconds / 864e5; + case 'hour': return days * 24 + this._milliseconds / 36e5; + case 'minute': return days * 24 * 60 + this._milliseconds / 6e4; + case 'second': return days * 24 * 60 * 60 + this._milliseconds / 1000; + // Math.floor prevents floating point math errors here + case 'millisecond': return Math.floor(days * 24 * 60 * 60 * 1000) + this._milliseconds; + default: throw new Error('Unknown unit ' + units); + } + } + }, + + lang : moment.fn.lang, + locale : moment.fn.locale, + + toIsoString : deprecate( + 'toIsoString() is deprecated. Please use toISOString() instead ' + + '(notice the capitals)', + function () { + return this.toISOString(); + } + ), + + toISOString : function () { + // inspired by https://github.com/dordille/moment-isoduration/blob/master/moment.isoduration.js + var years = Math.abs(this.years()), + months = Math.abs(this.months()), + days = Math.abs(this.days()), + hours = Math.abs(this.hours()), + minutes = Math.abs(this.minutes()), + seconds = Math.abs(this.seconds() + this.milliseconds() / 1000); + + if (!this.asSeconds()) { + // this is the same as C#'s (Noda) and python (isodate)... + // but not other JS (goog.date) + return 'P0D'; + } + + return (this.asSeconds() < 0 ? '-' : '') + + 'P' + + (years ? years + 'Y' : '') + + (months ? months + 'M' : '') + + (days ? days + 'D' : '') + + ((hours || minutes || seconds) ? 'T' : '') + + (hours ? hours + 'H' : '') + + (minutes ? minutes + 'M' : '') + + (seconds ? seconds + 'S' : ''); + }, + + localeData : function () { + return this._locale; + }, + + toJSON : function () { + return this.toISOString(); + } + }); + + moment.duration.fn.toString = moment.duration.fn.toISOString; + + function makeDurationGetter(name) { + moment.duration.fn[name] = function () { + return this._data[name]; + }; + } + + for (i in unitMillisecondFactors) { + if (hasOwnProp(unitMillisecondFactors, i)) { + makeDurationGetter(i.toLowerCase()); + } + } + + moment.duration.fn.asMilliseconds = function () { + return this.as('ms'); + }; + moment.duration.fn.asSeconds = function () { + return this.as('s'); + }; + moment.duration.fn.asMinutes = function () { + return this.as('m'); + }; + moment.duration.fn.asHours = function () { + return this.as('h'); + }; + moment.duration.fn.asDays = function () { + return this.as('d'); + }; + moment.duration.fn.asWeeks = function () { + return this.as('weeks'); + }; + moment.duration.fn.asMonths = function () { + return this.as('M'); + }; + moment.duration.fn.asYears = function () { + return this.as('y'); + }; + + /************************************ + Default Locale + ************************************/ + + + // Set default locale, other locale will inherit from English. + moment.locale('en', { + ordinalParse: /\d{1,2}(th|st|nd|rd)/, + ordinal : function (number) { + var b = number % 10, + output = (toInt(number % 100 / 10) === 1) ? 'th' : + (b === 1) ? 'st' : + (b === 2) ? 'nd' : + (b === 3) ? 'rd' : 'th'; + return number + output; + } + }); + + /* EMBED_LOCALES */ + + /************************************ + Exposing Moment + ************************************/ + + function makeGlobal(shouldDeprecate) { + /*global ender:false */ + if (typeof ender !== 'undefined') { + return; + } + oldGlobalMoment = globalScope.moment; + if (shouldDeprecate) { + globalScope.moment = deprecate( + 'Accessing Moment through the global scope is ' + + 'deprecated, and will be removed in an upcoming ' + + 'release.', + moment); + } else { + globalScope.moment = moment; + } + } + + // CommonJS module is defined + if (hasModule) { + module.exports = moment; + } else if (true) { + !(__WEBPACK_AMD_DEFINE_RESULT__ = function (require, exports, module) { + if (module.config && module.config() && module.config().noGlobal === true) { + // release the global variable + globalScope.moment = oldGlobalMoment; + } + + return moment; + }.call(exports, __webpack_require__, exports, module), __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__)); + makeGlobal(true); + } else { + makeGlobal(); + } + }).call(this); + + /* WEBPACK VAR INJECTION */}.call(exports, (function() { return this; }()), __webpack_require__(71)(module))) + +/***/ }, +/* 59 */ +/***/ function(module, exports, __webpack_require__) { + + var __WEBPACK_AMD_DEFINE_RESULT__;/*! Hammer.JS - v1.1.3 - 2014-05-20 + * http://eightmedia.github.io/hammer.js + * + * Copyright (c) 2014 Jorik Tangelder ; + * Licensed under the MIT license */ + + (function(window, undefined) { + 'use strict'; + + /** + * @main + * @module hammer + * + * @class Hammer + * @static */ - Edge.prototype._circle = function (ctx, x, y, radius) { - // draw a circle - ctx.beginPath(); - ctx.arc(x, y, radius, 0, 2 * Math.PI, false); - ctx.stroke(); + + /** + * Hammer, use this to create instances + * ```` + * var hammertime = new Hammer(myElement); + * ```` + * + * @method Hammer + * @param {HTMLElement} element + * @param {Object} [options={}] + * @return {Hammer.Instance} + */ + var Hammer = function Hammer(element, options) { + return new Hammer.Instance(element, options || {}); + }; + + /** + * version, as defined in package.json + * the value will be set at each build + * @property VERSION + * @final + * @type {String} + */ + Hammer.VERSION = '1.1.3'; + + /** + * default settings. + * more settings are defined per gesture at `/gestures`. Each gesture can be disabled/enabled + * by setting it's name (like `swipe`) to false. + * You can set the defaults for all instances by changing this object before creating an instance. + * @example + * ```` + * Hammer.defaults.drag = false; + * Hammer.defaults.behavior.touchAction = 'pan-y'; + * delete Hammer.defaults.behavior.userSelect; + * ```` + * @property defaults + * @type {Object} + */ + Hammer.defaults = { + /** + * this setting object adds styles and attributes to the element to prevent the browser from doing + * its native behavior. The css properties are auto prefixed for the browsers when needed. + * @property defaults.behavior + * @type {Object} + */ + behavior: { + /** + * Disables text selection to improve the dragging gesture. When the value is `none` it also sets + * `onselectstart=false` for IE on the element. Mainly for desktop browsers. + * @property defaults.behavior.userSelect + * @type {String} + * @default 'none' + */ + userSelect: 'none', + + /** + * Specifies whether and how a given region can be manipulated by the user (for instance, by panning or zooming). + * Used by Chrome 35> and IE10>. By default this makes the element blocking any touch event. + * @property defaults.behavior.touchAction + * @type {String} + * @default: 'pan-y' + */ + touchAction: 'pan-y', + + /** + * Disables the default callout shown when you touch and hold a touch target. + * On iOS, when you touch and hold a touch target such as a link, Safari displays + * a callout containing information about the link. This property allows you to disable that callout. + * @property defaults.behavior.touchCallout + * @type {String} + * @default 'none' + */ + touchCallout: 'none', + + /** + * Specifies whether zooming is enabled. Used by IE10> + * @property defaults.behavior.contentZooming + * @type {String} + * @default 'none' + */ + contentZooming: 'none', + + /** + * Specifies that an entire element should be draggable instead of its contents. + * Mainly for desktop browsers. + * @property defaults.behavior.userDrag + * @type {String} + * @default 'none' + */ + userDrag: 'none', + + /** + * Overrides the highlight color shown when the user taps a link or a JavaScript + * clickable element in Safari on iPhone. This property obeys the alpha value, if specified. + * + * If you don't specify an alpha value, Safari on iPhone applies a default alpha value + * to the color. To disable tap highlighting, set the alpha value to 0 (invisible). + * If you set the alpha value to 1.0 (opaque), the element is not visible when tapped. + * @property defaults.behavior.tapHighlightColor + * @type {String} + * @default 'rgba(0,0,0,0)' + */ + tapHighlightColor: 'rgba(0,0,0,0)' + } }; - /** - * Draw label with white background and with the middle at (x, y) - * @param {CanvasRenderingContext2D} ctx - * @param {String} text - * @param {Number} x - * @param {Number} y - * @private - */ - Edge.prototype._label = function (ctx, text, x, y) { - if (text) { - ctx.font = ((this.from.selected || this.to.selected) ? "bold " : "") + - this.options.fontSize + "px " + this.options.fontFace; - var yLine; + /** + * hammer document where the base events are added at + * @property DOCUMENT + * @type {HTMLElement} + * @default window.document + */ + Hammer.DOCUMENT = document; + + /** + * detect support for pointer events + * @property HAS_POINTEREVENTS + * @type {Boolean} + */ + Hammer.HAS_POINTEREVENTS = navigator.pointerEnabled || navigator.msPointerEnabled; + + /** + * detect support for touch events + * @property HAS_TOUCHEVENTS + * @type {Boolean} + */ + Hammer.HAS_TOUCHEVENTS = ('ontouchstart' in window); + + /** + * detect mobile browsers + * @property IS_MOBILE + * @type {Boolean} + */ + Hammer.IS_MOBILE = /mobile|tablet|ip(ad|hone|od)|android|silk/i.test(navigator.userAgent); + + /** + * detect if we want to support mouseevents at all + * @property NO_MOUSEEVENTS + * @type {Boolean} + */ + Hammer.NO_MOUSEEVENTS = (Hammer.HAS_TOUCHEVENTS && Hammer.IS_MOBILE) || Hammer.HAS_POINTEREVENTS; + + /** + * interval in which Hammer recalculates current velocity/direction/angle in ms + * @property CALCULATE_INTERVAL + * @type {Number} + * @default 25 + */ + Hammer.CALCULATE_INTERVAL = 25; + + /** + * eventtypes per touchevent (start, move, end) are filled by `Event.determineEventTypes` on `setup` + * the object contains the DOM event names per type (`EVENT_START`, `EVENT_MOVE`, `EVENT_END`) + * @property EVENT_TYPES + * @private + * @writeOnce + * @type {Object} + */ + var EVENT_TYPES = {}; + + /** + * direction strings, for safe comparisons + * @property DIRECTION_DOWN|LEFT|UP|RIGHT + * @final + * @type {String} + * @default 'down' 'left' 'up' 'right' + */ + var DIRECTION_DOWN = Hammer.DIRECTION_DOWN = 'down'; + var DIRECTION_LEFT = Hammer.DIRECTION_LEFT = 'left'; + var DIRECTION_UP = Hammer.DIRECTION_UP = 'up'; + var DIRECTION_RIGHT = Hammer.DIRECTION_RIGHT = 'right'; + + /** + * pointertype strings, for safe comparisons + * @property POINTER_MOUSE|TOUCH|PEN + * @final + * @type {String} + * @default 'mouse' 'touch' 'pen' + */ + var POINTER_MOUSE = Hammer.POINTER_MOUSE = 'mouse'; + var POINTER_TOUCH = Hammer.POINTER_TOUCH = 'touch'; + var POINTER_PEN = Hammer.POINTER_PEN = 'pen'; + + /** + * eventtypes + * @property EVENT_START|MOVE|END|RELEASE|TOUCH + * @final + * @type {String} + * @default 'start' 'change' 'move' 'end' 'release' 'touch' + */ + var EVENT_START = Hammer.EVENT_START = 'start'; + var EVENT_MOVE = Hammer.EVENT_MOVE = 'move'; + var EVENT_END = Hammer.EVENT_END = 'end'; + var EVENT_RELEASE = Hammer.EVENT_RELEASE = 'release'; + var EVENT_TOUCH = Hammer.EVENT_TOUCH = 'touch'; + + /** + * if the window events are set... + * @property READY + * @writeOnce + * @type {Boolean} + * @default false + */ + Hammer.READY = false; + + /** + * plugins namespace + * @property plugins + * @type {Object} + */ + Hammer.plugins = Hammer.plugins || {}; + + /** + * gestures namespace + * see `/gestures` for the definitions + * @property gestures + * @type {Object} + */ + Hammer.gestures = Hammer.gestures || {}; + + /** + * setup events to detect gestures on the document + * this function is called when creating an new instance + * @private + */ + function setup() { + if(Hammer.READY) { + return; + } + + // find what eventtypes we add listeners to + Event.determineEventTypes(); + + // Register all gestures inside Hammer.gestures + Utils.each(Hammer.gestures, function(gesture) { + Detection.register(gesture); + }); + + // Add touch events on the document + Event.onTouch(Hammer.DOCUMENT, EVENT_MOVE, Detection.detect); + Event.onTouch(Hammer.DOCUMENT, EVENT_END, Detection.detect); + + // Hammer is ready...! + Hammer.READY = true; + } + + /** + * @module hammer + * + * @class Utils + * @static + */ + var Utils = Hammer.utils = { + /** + * extend method, could also be used for cloning when `dest` is an empty object. + * changes the dest object + * @method extend + * @param {Object} dest + * @param {Object} src + * @param {Boolean} [merge=false] do a merge + * @return {Object} dest + */ + extend: function extend(dest, src, merge) { + for(var key in src) { + if(!src.hasOwnProperty(key) || (dest[key] !== undefined && merge)) { + continue; + } + dest[key] = src[key]; + } + return dest; + }, + + /** + * simple addEventListener wrapper + * @method on + * @param {HTMLElement} element + * @param {String} type + * @param {Function} handler + */ + on: function on(element, type, handler) { + element.addEventListener(type, handler, false); + }, + + /** + * simple removeEventListener wrapper + * @method off + * @param {HTMLElement} element + * @param {String} type + * @param {Function} handler + */ + off: function off(element, type, handler) { + element.removeEventListener(type, handler, false); + }, + + /** + * forEach over arrays and objects + * @method each + * @param {Object|Array} obj + * @param {Function} iterator + * @param {any} iterator.item + * @param {Number} iterator.index + * @param {Object|Array} iterator.obj the source object + * @param {Object} context value to use as `this` in the iterator + */ + each: function each(obj, iterator, context) { + var i, len; + + // native forEach on arrays + if('forEach' in obj) { + obj.forEach(iterator, context); + // arrays + } else if(obj.length !== undefined) { + for(i = 0, len = obj.length; i < len; i++) { + if(iterator.call(context, obj[i], i, obj) === false) { + return; + } + } + // objects + } else { + for(i in obj) { + if(obj.hasOwnProperty(i) && + iterator.call(context, obj[i], i, obj) === false) { + return; + } + } + } + }, + + /** + * find if a string contains the string using indexOf + * @method inStr + * @param {String} src + * @param {String} find + * @return {Boolean} found + */ + inStr: function inStr(src, find) { + return src.indexOf(find) > -1; + }, + + /** + * find if a array contains the object using indexOf or a simple polyfill + * @method inArray + * @param {String} src + * @param {String} find + * @return {Boolean|Number} false when not found, or the index + */ + inArray: function inArray(src, find) { + if(src.indexOf) { + var index = src.indexOf(find); + return (index === -1) ? false : index; + } else { + for(var i = 0, len = src.length; i < len; i++) { + if(src[i] === find) { + return i; + } + } + return false; + } + }, + + /** + * convert an array-like object (`arguments`, `touchlist`) to an array + * @method toArray + * @param {Object} obj + * @return {Array} + */ + toArray: function toArray(obj) { + return Array.prototype.slice.call(obj, 0); + }, + + /** + * find if a node is in the given parent + * @method hasParent + * @param {HTMLElement} node + * @param {HTMLElement} parent + * @return {Boolean} found + */ + hasParent: function hasParent(node, parent) { + while(node) { + if(node == parent) { + return true; + } + node = node.parentNode; + } + return false; + }, + + /** + * get the center of all the touches + * @method getCenter + * @param {Array} touches + * @return {Object} center contains `pageX`, `pageY`, `clientX` and `clientY` properties + */ + getCenter: function getCenter(touches) { + var pageX = [], + pageY = [], + clientX = [], + clientY = [], + min = Math.min, + max = Math.max; + + // no need to loop when only one touch + if(touches.length === 1) { + return { + pageX: touches[0].pageX, + pageY: touches[0].pageY, + clientX: touches[0].clientX, + clientY: touches[0].clientY + }; + } + + Utils.each(touches, function(touch) { + pageX.push(touch.pageX); + pageY.push(touch.pageY); + clientX.push(touch.clientX); + clientY.push(touch.clientY); + }); + + return { + pageX: (min.apply(Math, pageX) + max.apply(Math, pageX)) / 2, + pageY: (min.apply(Math, pageY) + max.apply(Math, pageY)) / 2, + clientX: (min.apply(Math, clientX) + max.apply(Math, clientX)) / 2, + clientY: (min.apply(Math, clientY) + max.apply(Math, clientY)) / 2 + }; + }, + + /** + * calculate the velocity between two points. unit is in px per ms. + * @method getVelocity + * @param {Number} deltaTime + * @param {Number} deltaX + * @param {Number} deltaY + * @return {Object} velocity `x` and `y` + */ + getVelocity: function getVelocity(deltaTime, deltaX, deltaY) { + return { + x: Math.abs(deltaX / deltaTime) || 0, + y: Math.abs(deltaY / deltaTime) || 0 + }; + }, + + /** + * calculate the angle between two coordinates + * @method getAngle + * @param {Touch} touch1 + * @param {Touch} touch2 + * @return {Number} angle + */ + getAngle: function getAngle(touch1, touch2) { + var x = touch2.clientX - touch1.clientX, + y = touch2.clientY - touch1.clientY; + + return Math.atan2(y, x) * 180 / Math.PI; + }, + + /** + * do a small comparision to get the direction between two touches. + * @method getDirection + * @param {Touch} touch1 + * @param {Touch} touch2 + * @return {String} direction matches `DIRECTION_LEFT|RIGHT|UP|DOWN` + */ + getDirection: function getDirection(touch1, touch2) { + var x = Math.abs(touch1.clientX - touch2.clientX), + y = Math.abs(touch1.clientY - touch2.clientY); + + if(x >= y) { + return touch1.clientX - touch2.clientX > 0 ? DIRECTION_LEFT : DIRECTION_RIGHT; + } + return touch1.clientY - touch2.clientY > 0 ? DIRECTION_UP : DIRECTION_DOWN; + }, + + /** + * calculate the distance between two touches + * @method getDistance + * @param {Touch}touch1 + * @param {Touch} touch2 + * @return {Number} distance + */ + getDistance: function getDistance(touch1, touch2) { + var x = touch2.clientX - touch1.clientX, + y = touch2.clientY - touch1.clientY; + + return Math.sqrt((x * x) + (y * y)); + }, + + /** + * calculate the scale factor between two touchLists + * no scale is 1, and goes down to 0 when pinched together, and bigger when pinched out + * @method getScale + * @param {Array} start array of touches + * @param {Array} end array of touches + * @return {Number} scale + */ + getScale: function getScale(start, end) { + // need two fingers... + if(start.length >= 2 && end.length >= 2) { + return this.getDistance(end[0], end[1]) / this.getDistance(start[0], start[1]); + } + return 1; + }, + + /** + * calculate the rotation degrees between two touchLists + * @method getRotation + * @param {Array} start array of touches + * @param {Array} end array of touches + * @return {Number} rotation + */ + getRotation: function getRotation(start, end) { + // need two fingers + if(start.length >= 2 && end.length >= 2) { + return this.getAngle(end[1], end[0]) - this.getAngle(start[1], start[0]); + } + return 0; + }, - if (this.dirtyLabel == true) { - var lines = String(text).split('\n'); - var lineCount = lines.length; - var fontSize = Number(this.options.fontSize); - yLine = y + (1 - lineCount) / 2 * fontSize; + /** + * find out if the direction is vertical * + * @method isVertical + * @param {String} direction matches `DIRECTION_UP|DOWN` + * @return {Boolean} is_vertical + */ + isVertical: function isVertical(direction) { + return direction == DIRECTION_UP || direction == DIRECTION_DOWN; + }, - var 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; - } - var height = this.options.fontSize * lineCount; - var left = x - width / 2; - var top = y - height / 2; + /** + * set css properties with their prefixes + * @param {HTMLElement} element + * @param {String} prop + * @param {String} value + * @param {Boolean} [toggle=true] + * @return {Boolean} + */ + setPrefixedCss: function setPrefixedCss(element, prop, value, toggle) { + var prefixes = ['', 'Webkit', 'Moz', 'O', 'ms']; + prop = Utils.toCamelCase(prop); - // cache - this.labelDimensions = {top:top,left:left,width:width,height:height,yLine:yLine}; - } + for(var i = 0; i < prefixes.length; i++) { + var p = prop; + // prefixes + if(prefixes[i]) { + p = prefixes[i] + p.slice(0, 1).toUpperCase() + p.slice(1); + } - var yLine = this.labelDimensions.yLine; - - ctx.save(); - - if (this.options.labelAlignment != "horizontal"){ - ctx.translate(x, yLine); - this._rotateForLabelAlignment(ctx); - x = 0; - yLine = 0; - } + // test the style + if(p in element.style) { + element.style[p] = (toggle == null || toggle) && value || ''; + break; + } + } + }, - - this._drawLabelRect(ctx); - this._drawLabelText(ctx,x,yLine, lines, lineCount, fontSize); - - ctx.restore(); - } - }; + /** + * toggle browser default behavior by setting css properties. + * `userSelect='none'` also sets `element.onselectstart` to false + * `userDrag='none'` also sets `element.ondragstart` to false + * + * @method toggleBehavior + * @param {HtmlElement} element + * @param {Object} props + * @param {Boolean} [toggle=true] + */ + toggleBehavior: function toggleBehavior(element, props, toggle) { + if(!props || !element || !element.style) { + return; + } - /** - * Rotates the canvas so the text is most readable - * @param {CanvasRenderingContext2D} ctx - * @private - */ - Edge.prototype._rotateForLabelAlignment = function(ctx) { - var dy = this.from.y - this.to.y; - var dx = this.from.x - this.to.x; - var angleInDegrees = Math.atan2(dy, dx); + // set the css properties + Utils.each(props, function(value, prop) { + Utils.setPrefixedCss(element, prop, value, toggle); + }); - // rotate so label it is readable - if((angleInDegrees < -1 && dx < 0) || (angleInDegrees > 0 && dx < 0)){ - angleInDegrees = angleInDegrees + Math.PI; - } - - ctx.rotate(angleInDegrees); - }; + var falseFn = toggle && function() { + return false; + }; - /** - * Draws the label rectangle - * @param {CanvasRenderingContext2D} ctx - * @param {String} labelAlignment - * @private - */ - Edge.prototype._drawLabelRect = function(ctx) { - if (this.options.fontFill !== undefined && this.options.fontFill !== null && this.options.fontFill !== "none") { - ctx.fillStyle = this.options.fontFill; - - var lineMargin = 2; + // also the disable onselectstart + if(props.userSelect == 'none') { + element.onselectstart = falseFn; + } + // and disable ondragstart + if(props.userDrag == 'none') { + element.ondragstart = falseFn; + } + }, - if (this.options.labelAlignment == 'line-center') { - ctx.fillRect(-this.labelDimensions.width * 0.5, -this.labelDimensions.height * 0.5, this.labelDimensions.width, this.labelDimensions.height); - } - else if (this.options.labelAlignment == 'line-above') { - ctx.fillRect(-this.labelDimensions.width * 0.5, -(this.labelDimensions.height + lineMargin), this.labelDimensions.width, this.labelDimensions.height); - } - else if (this.options.labelAlignment == 'line-below') { - ctx.fillRect(-this.labelDimensions.width * 0.5, lineMargin, this.labelDimensions.width, this.labelDimensions.height); - } - else { - ctx.fillRect(this.labelDimensions.left, this.labelDimensions.top, this.labelDimensions.width, this.labelDimensions.height); + /** + * convert a string with underscores to camelCase + * so prevent_default becomes preventDefault + * @param {String} str + * @return {String} camelCaseStr + */ + toCamelCase: function toCamelCase(str) { + return str.replace(/[_-]([a-z])/g, function(s) { + return s[1].toUpperCase(); + }); } - } }; + /** - * Draws the label text - * @param {CanvasRenderingContext2D} ctx - * @param {Number} x - * @param {Number} yLine - * @param {Array} lines - * @param {Number} lineCount - * @param {Number} fontSize - * @private + * @module hammer */ - Edge.prototype._drawLabelText = function(ctx, x, yLine, lines, lineCount, fontSize) { - // draw text - ctx.fillStyle = this.options.fontColor || "black"; - ctx.textAlign = "center"; + /** + * @class Event + * @static + */ + var Event = Hammer.event = { + /** + * when touch events have been fired, this is true + * this is used to stop mouse events + * @property prevent_mouseevents + * @private + * @type {Boolean} + */ + preventMouseEvents: false, - // check for label alignment - if (this.options.labelAlignment != 'horizontal') { - var lineMargin = 2; - if (this.options.labelAlignment == 'line-above') { - ctx.textBaseline = "alphabetic"; - yLine -= 2 * lineMargin; // distance from edge, required because we use alphabetic. Alphabetic has less difference between browsers - } - else if (this.options.labelAlignment == 'line-below') { - 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 = "middle"; - } + /** + * if EVENT_START has been fired + * @property started + * @private + * @type {Boolean} + */ + started: false, - // check for strokeWidth - if (this.options.fontStrokeWidth > 0){ - ctx.lineWidth = this.options.fontStrokeWidth; - ctx.strokeStyle = this.options.fontStrokeColor; - ctx.lineJoin = 'round'; - } - for (var i = 0; i < lineCount; i++) { - if(this.options.fontStrokeWidth > 0){ - ctx.strokeText(lines[i], x, yLine); - } - ctx.fillText(lines[i], x, yLine); - yLine += fontSize; - } - }; + /** + * when the mouse is hold down, this is true + * @property should_detect + * @private + * @type {Boolean} + */ + shouldDetect: false, - /** - * Redraw a edge as a dashed line - * Draw this edge in the given canvas - * @author David Jordan - * @date 2012-08-08 - * The 2d context of a HTML canvas can be retrieved by canvas.getContext("2d"); - * @param {CanvasRenderingContext2D} ctx - * @private - */ - Edge.prototype._drawDashLine = function(ctx) { - // set style - ctx.strokeStyle = this._getColor(); - ctx.lineWidth = this._getLineWidth(); + /** + * simple event binder with a hook and support for multiple types + * @method on + * @param {HTMLElement} element + * @param {String} type + * @param {Function} handler + * @param {Function} [hook] + * @param {Object} hook.type + */ + on: function on(element, type, handler, hook) { + var types = type.split(' '); + Utils.each(types, function(type) { + Utils.on(element, type, handler); + hook && hook(type); + }); + }, + + /** + * simple event unbinder with a hook and support for multiple types + * @method off + * @param {HTMLElement} element + * @param {String} type + * @param {Function} handler + * @param {Function} [hook] + * @param {Object} hook.type + */ + off: function off(element, type, handler, hook) { + var types = type.split(' '); + Utils.each(types, function(type) { + Utils.off(element, type, handler); + hook && hook(type); + }); + }, + + /** + * the core touch event handler. + * this finds out if we should to detect gestures + * @method onTouch + * @param {HTMLElement} element + * @param {String} eventType matches `EVENT_START|MOVE|END` + * @param {Function} handler + * @return onTouchHandler {Function} the core event handler + */ + onTouch: function onTouch(element, eventType, handler) { + var self = this; + + var onTouchHandler = function onTouchHandler(ev) { + var srcType = ev.type.toLowerCase(), + isPointer = Hammer.HAS_POINTEREVENTS, + isMouse = Utils.inStr(srcType, 'mouse'), + triggerType; + + // if we are in a mouseevent, but there has been a touchevent triggered in this session + // we want to do nothing. simply break out of the event. + if(isMouse && self.preventMouseEvents) { + return; + + // mousebutton must be down + } else if(isMouse && eventType == EVENT_START && ev.button === 0) { + self.preventMouseEvents = false; + self.shouldDetect = true; + } else if(isPointer && eventType == EVENT_START) { + self.shouldDetect = (ev.buttons === 1 || PointerEvent.matchType(POINTER_TOUCH, ev)); + // just a valid start event, but no mouse + } else if(!isMouse && eventType == EVENT_START) { + self.preventMouseEvents = true; + self.shouldDetect = true; + } + + // update the pointer event before entering the detection + if(isPointer && eventType != EVENT_END) { + PointerEvent.updatePointer(eventType, ev); + } + + // we are in a touch/down state, so allowed detection of gestures + if(self.shouldDetect) { + triggerType = self.doDetect.call(self, ev, eventType, element, handler); + } + + // ...and we are done with the detection + // so reset everything to start each detection totally fresh + if(triggerType == EVENT_END) { + self.preventMouseEvents = false; + self.shouldDetect = false; + PointerEvent.reset(); + // update the pointerevent object after the detection + } + + if(isPointer && eventType == EVENT_END) { + PointerEvent.updatePointer(eventType, ev); + } + }; + + this.on(element, EVENT_TYPES[eventType], onTouchHandler); + return onTouchHandler; + }, + + /** + * the core detection method + * this finds out what hammer-touch-events to trigger + * @method doDetect + * @param {Object} ev + * @param {String} eventType matches `EVENT_START|MOVE|END` + * @param {HTMLElement} element + * @param {Function} handler + * @return {String} triggerType matches `EVENT_START|MOVE|END` + */ + doDetect: function doDetect(ev, eventType, element, handler) { + var touchList = this.getTouchList(ev, eventType); + var touchListLength = touchList.length; + var triggerType = eventType; + var triggerChange = touchList.trigger; // used by fakeMultitouch plugin + var changedLength = touchListLength; + + // at each touchstart-like event we want also want to trigger a TOUCH event... + if(eventType == EVENT_START) { + triggerChange = EVENT_TOUCH; + // ...the same for a touchend-like event + } else if(eventType == EVENT_END) { + triggerChange = EVENT_RELEASE; + + // keep track of how many touches have been removed + changedLength = touchList.length - ((ev.changedTouches) ? ev.changedTouches.length : 1); + } + + // after there are still touches on the screen, + // we just want to trigger a MOVE event. so change the START or END to a MOVE + // but only after detection has been started, the first time we actualy want a START + if(changedLength > 0 && this.started) { + triggerType = EVENT_MOVE; + } + + // detection has been started, we keep track of this, see above + this.started = true; + + // generate some event data, some basic information + var evData = this.collectEventData(element, triggerType, touchList, ev); + + // trigger the triggerType event before the change (TOUCH, RELEASE) events + // but the END event should be at last + if(eventType != EVENT_END) { + handler.call(Detection, evData); + } + + // trigger a change (TOUCH, RELEASE) event, this means the length of the touches changed + if(triggerChange) { + evData.changedLength = changedLength; + evData.eventType = triggerChange; + + handler.call(Detection, evData); + + evData.eventType = triggerType; + delete evData.changedLength; + } + + // trigger the END event + if(triggerType == EVENT_END) { + handler.call(Detection, evData); + + // ...and we are done with the detection + // so reset everything to start each detection totally fresh + this.started = false; + } + + return triggerType; + }, + + /** + * we have different events for each device/browser + * determine what we need and set them in the EVENT_TYPES constant + * the `onTouch` method is bind to these properties. + * @method determineEventTypes + * @return {Object} events + */ + determineEventTypes: function determineEventTypes() { + var types; + if(Hammer.HAS_POINTEREVENTS) { + if(window.PointerEvent) { + types = [ + 'pointerdown', + 'pointermove', + 'pointerup pointercancel lostpointercapture' + ]; + } else { + types = [ + 'MSPointerDown', + 'MSPointerMove', + 'MSPointerUp MSPointerCancel MSLostPointerCapture' + ]; + } + } else if(Hammer.NO_MOUSEEVENTS) { + types = [ + 'touchstart', + 'touchmove', + 'touchend touchcancel' + ]; + } else { + types = [ + 'touchstart mousedown', + 'touchmove mousemove', + 'touchend touchcancel mouseup' + ]; + } + + EVENT_TYPES[EVENT_START] = types[0]; + EVENT_TYPES[EVENT_MOVE] = types[1]; + EVENT_TYPES[EVENT_END] = types[2]; + return EVENT_TYPES; + }, + + /** + * create touchList depending on the event + * @method getTouchList + * @param {Object} ev + * @param {String} eventType + * @return {Array} touches + */ + getTouchList: function getTouchList(ev, eventType) { + // get the fake pointerEvent touchlist + if(Hammer.HAS_POINTEREVENTS) { + return PointerEvent.getTouchList(); + } + + // get the touchlist + if(ev.touches) { + if(eventType == EVENT_MOVE) { + return ev.touches; + } + + var identifiers = []; + var concat = [].concat(Utils.toArray(ev.touches), Utils.toArray(ev.changedTouches)); + var touchList = []; + + Utils.each(concat, function(touch) { + if(Utils.inArray(identifiers, touch.identifier) === false) { + touchList.push(touch); + } + identifiers.push(touch.identifier); + }); + + return touchList; + } - var via = null; - // only firefox and chrome support this method, else we use the legacy one. - if (ctx.setLineDash !== undefined) { - ctx.save(); - // configure the dash pattern - var pattern = [0]; - if (this.options.dash.length !== undefined && this.options.dash.gap !== undefined) { - pattern = [this.options.dash.length,this.options.dash.gap]; - } - else { - pattern = [5,5]; - } + // make fake touchList from mouse position + ev.identifier = 1; + return [ev]; + }, - // set dash settings for chrome or firefox - ctx.setLineDash(pattern); - ctx.lineDashOffset = 0; + /** + * collect basic event data + * @method collectEventData + * @param {HTMLElement} element + * @param {String} eventType matches `EVENT_START|MOVE|END` + * @param {Array} touches + * @param {Object} ev + * @return {Object} ev + */ + collectEventData: function collectEventData(element, eventType, touches, ev) { + // find out pointerType + var pointerType = POINTER_TOUCH; + if(Utils.inStr(ev.type, 'mouse') || PointerEvent.matchType(POINTER_MOUSE, ev)) { + pointerType = POINTER_MOUSE; + } else if(PointerEvent.matchType(POINTER_PEN, ev)) { + pointerType = POINTER_PEN; + } - // draw the line - via = this._line(ctx); + return { + center: Utils.getCenter(touches), + timeStamp: Date.now(), + target: ev.target, + touches: touches, + eventType: eventType, + pointerType: pointerType, + srcEvent: ev, - // restore the dash settings. - ctx.setLineDash([0]); - ctx.lineDashOffset = 0; - ctx.restore(); - } - else { // unsupporting smooth lines - // draw dashed line - ctx.beginPath(); - ctx.lineCap = 'round'; - if (this.options.dash.altLength !== undefined) //If an alt dash value has been set add to the array this value - { - ctx.dashedLine(this.from.x,this.from.y,this.to.x,this.to.y, - [this.options.dash.length,this.options.dash.gap,this.options.dash.altLength,this.options.dash.gap]); - } - else if (this.options.dash.length !== undefined && this.options.dash.gap !== undefined) //If a dash and gap value has been set add to the array this value - { - ctx.dashedLine(this.from.x,this.from.y,this.to.x,this.to.y, - [this.options.dash.length,this.options.dash.gap]); - } - else //If all else fails draw a line - { - ctx.moveTo(this.from.x, this.from.y); - ctx.lineTo(this.to.x, this.to.y); - } - ctx.stroke(); - } + /** + * prevent the browser default actions + * mostly used to disable scrolling of the browser + */ + preventDefault: function() { + var srcEvent = this.srcEvent; + srcEvent.preventManipulation && srcEvent.preventManipulation(); + srcEvent.preventDefault && srcEvent.preventDefault(); + }, - // draw label - if (this.label) { - var point; - if (this.options.smoothCurves.enabled == true && via != null) { - var midpointX = 0.5*(0.5*(this.from.x + via.x) + 0.5*(this.to.x + via.x)); - var midpointY = 0.5*(0.5*(this.from.y + via.y) + 0.5*(this.to.y + via.y)); - point = {x:midpointX, y:midpointY}; - } - else { - point = this._pointOnLine(0.5); - } - this._label(ctx, this.label, point.x, point.y); - } - }; + /** + * stop bubbling the event up to its parents + */ + stopPropagation: function() { + this.srcEvent.stopPropagation(); + }, - /** - * Get a point on a line - * @param {Number} percentage. Value between 0 (line start) and 1 (line end) - * @return {Object} point - * @private - */ - Edge.prototype._pointOnLine = function (percentage) { - return { - x: (1 - percentage) * this.from.x + percentage * this.to.x, - y: (1 - percentage) * this.from.y + percentage * this.to.y - } + /** + * immediately stop gesture detection + * might be useful after a swipe was detected + * @return {*} + */ + stopDetect: function() { + return Detection.stopDetect(); + } + }; + } }; - /** - * 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 - */ - Edge.prototype._pointOnCircle = function (x, y, radius, percentage) { - var angle = (percentage - 3/8) * 2 * Math.PI; - return { - x: x + radius * Math.cos(angle), - y: y - radius * Math.sin(angle) - } - }; /** - * Redraw a edge as a line with an arrow halfway the line - * Draw this edge in the given canvas - * The 2d context of a HTML canvas can be retrieved by canvas.getContext("2d"); - * @param {CanvasRenderingContext2D} ctx - * @private + * @module hammer + * + * @class PointerEvent + * @static */ - Edge.prototype._drawArrowCenter = function(ctx) { - var point; - // set style - ctx.strokeStyle = this._getColor(); - ctx.fillStyle = ctx.strokeStyle; - ctx.lineWidth = this._getLineWidth(); + var PointerEvent = Hammer.PointerEvent = { + /** + * holds all pointers, by `identifier` + * @property pointers + * @type {Object} + */ + pointers: {}, - if (this.from != this.to) { - // draw line - var via = this._line(ctx); + /** + * get the pointers as an array + * @method getTouchList + * @return {Array} touchlist + */ + getTouchList: function getTouchList() { + var touchlist = []; + // we can use forEach since pointerEvents only is in IE10 + Utils.each(this.pointers, function(pointer) { + touchlist.push(pointer); + }); + return touchlist; + }, - var angle = Math.atan2((this.to.y - this.from.y), (this.to.x - this.from.x)); - var length = (10 + 5 * this.options.width) * this.options.arrowScaleFactor; - // draw an arrow halfway the line - if (this.options.smoothCurves.enabled == true && via != null) { - var midpointX = 0.5*(0.5*(this.from.x + via.x) + 0.5*(this.to.x + via.x)); - var midpointY = 0.5*(0.5*(this.from.y + via.y) + 0.5*(this.to.y + via.y)); - point = {x:midpointX, y:midpointY}; - } - else { - point = this._pointOnLine(0.5); - } + /** + * update the position of a pointer + * @method updatePointer + * @param {String} eventType matches `EVENT_START|MOVE|END` + * @param {Object} pointerEvent + */ + updatePointer: function updatePointer(eventType, pointerEvent) { + if(eventType == EVENT_END || (eventType != EVENT_END && pointerEvent.buttons !== 1)) { + delete this.pointers[pointerEvent.pointerId]; + } else { + pointerEvent.identifier = pointerEvent.pointerId; + this.pointers[pointerEvent.pointerId] = pointerEvent; + } + }, - ctx.arrow(point.x, point.y, angle, length); - ctx.fill(); - ctx.stroke(); + /** + * check if ev matches pointertype + * @method matchType + * @param {String} pointerType matches `POINTER_MOUSE|TOUCH|PEN` + * @param {PointerEvent} ev + */ + matchType: function matchType(pointerType, ev) { + if(!ev.pointerType) { + return false; + } - // draw label - if (this.label) { - this._label(ctx, this.label, point.x, point.y); - } - } - else { - // draw circle - var x, y; - var radius = 0.25 * Math.max(100,this.physics.springLength); - var node = this.from; - if (!node.width) { - node.resize(ctx); - } - if (node.width > node.height) { - x = node.x + node.width * 0.5; - y = node.y - radius; - } - else { - x = node.x + radius; - y = node.y - node.height * 0.5; - } - this._circle(ctx, x, y, radius); + var pt = ev.pointerType, + types = {}; - // draw all arrows - var angle = 0.2 * Math.PI; - var length = (10 + 5 * this.options.width) * this.options.arrowScaleFactor; - point = this._pointOnCircle(x, y, radius, 0.5); - ctx.arrow(point.x, point.y, angle, length); - ctx.fill(); - ctx.stroke(); + types[POINTER_MOUSE] = (pt === (ev.MSPOINTER_TYPE_MOUSE || POINTER_MOUSE)); + types[POINTER_TOUCH] = (pt === (ev.MSPOINTER_TYPE_TOUCH || POINTER_TOUCH)); + types[POINTER_PEN] = (pt === (ev.MSPOINTER_TYPE_PEN || POINTER_PEN)); + return types[pointerType]; + }, - // draw label - if (this.label) { - point = this._pointOnCircle(x, y, radius, 0.5); - this._label(ctx, this.label, point.x, point.y); + /** + * reset the stored pointers + * @method reset + */ + reset: function resetList() { + this.pointers = {}; } - } }; - Edge.prototype._pointOnBezier = function(t) { - var via = this._getViaCoordinates(); - - var x = Math.pow(1-t,2)*this.from.x + (2*t*(1 - t))*via.x + Math.pow(t,2)*this.to.x; - var y = Math.pow(1-t,2)*this.from.y + (2*t*(1 - t))*via.y + Math.pow(t,2)*this.to.y; - - return {x:x,y:y}; - } /** - * This function uses binary search to look for the point where the bezier curve crosses the border of the node. + * @module hammer * - * @param from - * @param ctx - * @returns {*} - * @private + * @class Detection + * @static */ - Edge.prototype._findBorderPosition = function(from,ctx) { - var maxIterations = 10; - var iteration = 0; - var low = 0; - var high = 1; - var pos,angle,distanceToBorder, distanceToNodes, difference; - var threshold = 0.2; - var node = this.to; - if (from == true) { - node = this.from; - } - - while (low <= high && iteration < maxIterations) { - var middle = (low + high) * 0.5; - - pos = this._pointOnBezier(middle); - angle = Math.atan2((node.y - pos.y), (node.x - pos.x)); - distanceToBorder = node.distanceToBorder(ctx,angle); - distanceToNodes = Math.sqrt(Math.pow(pos.x-node.x,2) + Math.pow(pos.y-node.y,2)); - difference = distanceToBorder - distanceToNodes; - if (Math.abs(difference) < threshold) { - break; // found - } - else if (difference < 0) { // distance to nodes is larger than distance to border --> t needs to be bigger if we're looking at the to node. - if (from == false) { - low = middle; - } - else { - high = middle; - } - } - else { - if (from == false) { - high = middle; - } - else { - low = middle; - } - } + var Detection = Hammer.detection = { + // contains all registred Hammer.gestures in the correct order + gestures: [], - iteration++; - } - pos.t = middle; + // data of the current Hammer.gesture detection session + current: null, - return pos; - }; + // the previous Hammer.gesture session data + // is a full clone of the previous gesture.current object + previous: null, - /** - * Redraw a edge as a line with an arrow - * Draw this edge in the given canvas - * The 2d context of a HTML canvas can be retrieved by canvas.getContext("2d"); - * @param {CanvasRenderingContext2D} ctx - * @private - */ - Edge.prototype._drawArrow = function(ctx) { - // set style - ctx.strokeStyle = this._getColor(); - ctx.fillStyle = ctx.strokeStyle; - ctx.lineWidth = this._getLineWidth(); + // when this becomes true, no gestures are fired + stopped: false, - // set vars - var angle, length, arrowPos; + /** + * start Hammer.gesture detection + * @method startDetect + * @param {Hammer.Instance} inst + * @param {Object} eventData + */ + startDetect: function startDetect(inst, eventData) { + // already busy with a Hammer.gesture detection on an element + if(this.current) { + return; + } - // if not connected to itself - if (this.from != this.to) { - // draw line - this._line(ctx); + this.stopped = false; - // draw arrow head - if (this.options.smoothCurves.enabled == true) { - var via = this._getViaCoordinates(); - arrowPos = this._findBorderPosition(false, ctx); - var guidePos = this._pointOnBezier(Math.max(0.0, arrowPos.t - 0.1)) - angle = Math.atan2((arrowPos.y - guidePos.y), (arrowPos.x - guidePos.x)); - } - else { - angle = Math.atan2((this.to.y - this.from.y), (this.to.x - this.from.x)); - var dx = (this.to.x - this.from.x); - var dy = (this.to.y - this.from.y); - var edgeSegmentLength = Math.sqrt(dx * dx + dy * dy); - var toBorderDist = this.to.distanceToBorder(ctx, angle); - var toBorderPoint = (edgeSegmentLength - toBorderDist) / edgeSegmentLength; + // holds current session + this.current = { + inst: inst, // reference to HammerInstance we're working for + startEvent: Utils.extend({}, eventData), // start eventData for distances, timing etc + lastEvent: false, // last eventData + lastCalcEvent: false, // last eventData for calculations. + futureCalcEvent: false, // last eventData for calculations. + lastCalcData: {}, // last lastCalcData + name: '' // current gesture we're in/detected, can be 'tap', 'hold' etc + }; - arrowPos = {}; - arrowPos.x = (1 - toBorderPoint) * this.from.x + toBorderPoint * this.to.x; - arrowPos.y = (1 - toBorderPoint) * this.from.y + toBorderPoint * this.to.y; - } + this.detect(eventData); + }, - // draw arrow at the end of the line - length = (10 + 5 * this.options.width) * this.options.arrowScaleFactor; - ctx.arrow(arrowPos.x,arrowPos.y, angle, length); - ctx.fill(); - ctx.stroke(); + /** + * Hammer.gesture detection + * @method detect + * @param {Object} eventData + * @return {any} + */ + detect: function detect(eventData) { + if(!this.current || this.stopped) { + return; + } - // draw label - if (this.label) { - var point; - if (this.options.smoothCurves.enabled == true && via != null) { - point = this._pointOnBezier(0.5); - } - else { - point = this._pointOnLine(0.5); - } - this._label(ctx, this.label, point.x, point.y); - } - } - else { - // draw circle - var node = this.from; - var x, y, arrow; - var radius = 0.25 * Math.max(100,this.physics.springLength); - if (!node.width) { - node.resize(ctx); - } - if (node.width > node.height) { - x = node.x + node.width * 0.5; - y = node.y - radius; - arrow = { - x: x, - y: node.y, - angle: 0.9 * Math.PI - }; - } - else { - x = node.x + radius; - y = node.y - node.height * 0.5; - arrow = { - x: node.x, - y: y, - angle: 0.6 * Math.PI - }; - } - ctx.beginPath(); - // TODO: similarly, for a line without arrows, draw to the border of the nodes instead of the center - ctx.arc(x, y, radius, 0, 2 * Math.PI, false); - ctx.stroke(); + // extend event data with calculations about scale, distance etc + eventData = this.extendEventData(eventData); - // draw all arrows - var length = (10 + 5 * this.options.width) * this.options.arrowScaleFactor; - ctx.arrow(arrow.x, arrow.y, arrow.angle, length); - ctx.fill(); - ctx.stroke(); + // hammer instance and instance options + var inst = this.current.inst, + instOptions = inst.options; - // draw label - if (this.label) { - point = this._pointOnCircle(x, y, radius, 0.5); - this._label(ctx, this.label, point.x, point.y); - } - } - }; + // call Hammer.gesture handlers + Utils.each(this.gestures, function triggerGesture(gesture) { + // only when the instance options have enabled this gesture + if(!this.stopped && inst.enabled && instOptions[gesture.name]) { + gesture.handler.call(gesture, eventData, inst); + } + }, this); - /** - * Calculate the distance between a point (x3,y3) and a line segment from - * (x1,y1) to (x2,y2). - * http://stackoverflow.com/questions/849211/shortest-distancae-between-a-point-and-a-line-segment - * @param {number} x1 - * @param {number} y1 - * @param {number} x2 - * @param {number} y2 - * @param {number} x3 - * @param {number} y3 - * @private - */ - Edge.prototype._getDistanceToEdge = function (x1,y1, x2,y2, x3,y3) { // x3,y3 is the point - var returnValue = 0; - if (this.from != this.to) { - if (this.options.smoothCurves.enabled == true) { - var xVia, yVia; - if (this.options.smoothCurves.enabled == true && this.options.smoothCurves.dynamic == true) { - xVia = this.via.x; - yVia = this.via.y; - } - else { - var via = this._getViaCoordinates(); - xVia = via.x; - yVia = via.y; - } - var minDistance = 1e9; - var distance; - var i,t,x,y, lastX, lastY; - for (i = 0; i < 10; i++) { - t = 0.1*i; - x = Math.pow(1-t,2)*x1 + (2*t*(1 - t))*xVia + Math.pow(t,2)*x2; - y = Math.pow(1-t,2)*y1 + (2*t*(1 - t))*yVia + Math.pow(t,2)*y2; - if (i > 0) { - distance = this._getDistanceToLine(lastX,lastY,x,y, x3,y3); - minDistance = distance < minDistance ? distance : minDistance; + // store as previous event event + if(this.current) { + this.current.lastEvent = eventData; } - lastX = x; lastY = y; - } - returnValue = minDistance; - } - else { - returnValue = this._getDistanceToLine(x1,y1,x2,y2,x3,y3); - } - } - else { - var x, y, dx, dy; - var radius = 0.25 * this.physics.springLength; - var node = this.from; - if (node.width > node.height) { - x = node.x + 0.5 * node.width; - y = node.y - radius; - } - else { - x = node.x + radius; - y = node.y - 0.5 * node.height; - } - dx = x - x3; - dy = y - y3; - returnValue = Math.abs(Math.sqrt(dx*dx + dy*dy) - radius); - } - if (this.labelDimensions.left < x3 && - this.labelDimensions.left + this.labelDimensions.width > x3 && - this.labelDimensions.top < y3 && - this.labelDimensions.top + this.labelDimensions.height > y3) { - return 0; - } - else { - return returnValue; - } - }; + if(eventData.eventType == EVENT_END) { + this.stopDetect(); + } - Edge.prototype._getDistanceToLine = function(x1,y1,x2,y2,x3,y3) { - var px = x2-x1, - py = y2-y1, - something = px*px + py*py, - u = ((x3 - x1) * px + (y3 - y1) * py) / something; + return eventData; + }, - if (u > 1) { - u = 1; - } - else if (u < 0) { - u = 0; - } + /** + * clear the Hammer.gesture vars + * this is called on endDetect, but can also be used when a final Hammer.gesture has been detected + * to stop other Hammer.gestures from being fired + * @method stopDetect + */ + stopDetect: function stopDetect() { + // clone current data to the store as the previous gesture + // used for the double tap gesture, since this is an other gesture detect session + this.previous = Utils.extend({}, this.current); - var x = x1 + u * px, - y = y1 + u * py, - dx = x - x3, - dy = y - y3; + // reset the current + this.current = null; + this.stopped = true; + }, - //# Note: If the actual distance does not matter, - //# if you only want to compare what this function - //# returns to other results of this function, you - //# can just return the squared distance instead - //# (i.e. remove the sqrt) to gain a little performance + /** + * calculate velocity, angle and direction + * @method getVelocityData + * @param {Object} ev + * @param {Object} center + * @param {Number} deltaTime + * @param {Number} deltaX + * @param {Number} deltaY + */ + getCalculatedData: function getCalculatedData(ev, center, deltaTime, deltaX, deltaY) { + var cur = this.current, + recalc = false, + calcEv = cur.lastCalcEvent, + calcData = cur.lastCalcData; - return Math.sqrt(dx*dx + dy*dy); - }; + if(calcEv && ev.timeStamp - calcEv.timeStamp > Hammer.CALCULATE_INTERVAL) { + center = calcEv.center; + deltaTime = ev.timeStamp - calcEv.timeStamp; + deltaX = ev.center.clientX - calcEv.center.clientX; + deltaY = ev.center.clientY - calcEv.center.clientY; + recalc = true; + } - /** - * This allows the zoom level of the network to influence the rendering - * - * @param scale - */ - Edge.prototype.setScale = function(scale) { - this.networkScaleInv = 1.0/scale; - }; + if(ev.eventType == EVENT_TOUCH || ev.eventType == EVENT_RELEASE) { + cur.futureCalcEvent = ev; + } + if(!cur.lastCalcEvent || recalc) { + calcData.velocity = Utils.getVelocity(deltaTime, deltaX, deltaY); + calcData.angle = Utils.getAngle(center, ev.center); + calcData.direction = Utils.getDirection(center, ev.center); - Edge.prototype.select = function() { - this.selected = true; - }; + cur.lastCalcEvent = cur.futureCalcEvent || ev; + cur.futureCalcEvent = ev; + } - Edge.prototype.unselect = function() { - this.selected = false; - }; + ev.velocityX = calcData.velocity.x; + ev.velocityY = calcData.velocity.y; + ev.interimAngle = calcData.angle; + ev.interimDirection = calcData.direction; + }, - Edge.prototype.positionBezierNode = function() { - if (this.via !== null && this.from !== null && this.to !== null) { - 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 !== null) { - this.via.x = 0; - this.via.y = 0; - } - }; + /** + * extend eventData for Hammer.gestures + * @method extendEventData + * @param {Object} ev + * @return {Object} ev + */ + extendEventData: function extendEventData(ev) { + var cur = this.current, + startEv = cur.startEvent, + lastEv = cur.lastEvent || startEv; - /** - * This function draws the control nodes for the manipulator. - * In order to enable this, only set the this.controlNodesEnabled to true. - * @param ctx - */ - Edge.prototype._drawControlNodes = function(ctx) { - if (this.controlNodesEnabled == true) { - if (this.controlNodes.from === null && this.controlNodes.to === null) { - var nodeIdFrom = "edgeIdFrom:".concat(this.id); - var nodeIdTo = "edgeIdTo:".concat(this.id); - var constants = { - nodes:{group:'', radius:7, borderWidth:2, borderWidthSelected: 2}, - physics:{damping:0}, - clustering: {maxNodeSizeIncrements: 0 ,nodeScaling: {width:0, height: 0, radius:0}} - }; - this.controlNodes.from = new Node( - {id:nodeIdFrom, - shape:'dot', - color:{background:'#ff0000', border:'#3c3c3c', highlight: {background:'#07f968'}} - },{},{},constants); - this.controlNodes.to = new Node( - {id:nodeIdTo, - shape:'dot', - color:{background:'#ff0000', border:'#3c3c3c', highlight: {background:'#07f968'}} - },{},{},constants); - } + // update the start touchlist to calculate the scale/rotation + if(ev.eventType == EVENT_TOUCH || ev.eventType == EVENT_RELEASE) { + startEv.touches = []; + Utils.each(ev.touches, function(touch) { + startEv.touches.push({ + clientX: touch.clientX, + clientY: touch.clientY + }); + }); + } - this.controlNodes.positions = {}; - if (this.controlNodes.from.selected == false) { - this.controlNodes.positions.from = this.getControlNodeFromPosition(ctx); - this.controlNodes.from.x = this.controlNodes.positions.from.x; - this.controlNodes.from.y = this.controlNodes.positions.from.y; - } - if (this.controlNodes.to.selected == false) { - this.controlNodes.positions.to = this.getControlNodeToPosition(ctx); - this.controlNodes.to.x = this.controlNodes.positions.to.x; - this.controlNodes.to.y = this.controlNodes.positions.to.y; - } + var deltaTime = ev.timeStamp - startEv.timeStamp, + deltaX = ev.center.clientX - startEv.center.clientX, + deltaY = ev.center.clientY - startEv.center.clientY; - this.controlNodes.from.draw(ctx); - this.controlNodes.to.draw(ctx); - } - else { - this.controlNodes = {from:null, to:null, positions:{}}; - } - }; + this.getCalculatedData(ev, lastEv.center, deltaTime, deltaX, deltaY); - /** - * Enable control nodes. - * @private - */ - Edge.prototype._enableControlNodes = function() { - this.fromBackup = this.from; - this.toBackup = this.to; - this.controlNodesEnabled = true; - }; + Utils.extend(ev, { + startEvent: startEv, - /** - * disable control nodes and remove from dynamicEdges from old node - * @private - */ - Edge.prototype._disableControlNodes = function() { - this.fromId = this.from.id; - this.toId = this.to.id; - if (this.fromId != this.fromBackup.id) { // from was changed, remove edge from old 'from' node dynamic edges - this.fromBackup.detachEdge(this); - } - else if (this.toId != this.toBackup.id) { // to was changed, remove edge from old 'to' node dynamic edges - this.toBackup.detachEdge(this); - } + deltaTime: deltaTime, + deltaX: deltaX, + deltaY: deltaY, - this.fromBackup = null; - this.toBackup = null; - this.controlNodesEnabled = false; - }; + distance: Utils.getDistance(startEv.center, ev.center), + angle: Utils.getAngle(startEv.center, ev.center), + direction: Utils.getDirection(startEv.center, ev.center), + scale: Utils.getScale(startEv.touches, ev.touches), + rotation: Utils.getRotation(startEv.touches, ev.touches) + }); + return ev; + }, - /** - * This checks if one of the control nodes is selected and if so, returns the control node object. Else it returns null. - * @param x - * @param y - * @returns {null} - * @private - */ - Edge.prototype._getSelectedControlNode = function(x,y) { - var positions = this.controlNodes.positions; - var fromDistance = Math.sqrt(Math.pow(x - positions.from.x,2) + Math.pow(y - positions.from.y,2)); - var toDistance = Math.sqrt(Math.pow(x - positions.to.x ,2) + Math.pow(y - positions.to.y ,2)); + /** + * register new gesture + * @method register + * @param {Object} gesture object, see `gestures/` for documentation + * @return {Array} gestures + */ + register: function register(gesture) { + // add an enable gesture options if there is no given + var options = gesture.defaults || {}; + if(options[gesture.name] === undefined) { + options[gesture.name] = true; + } - if (fromDistance < 15) { - this.connectedNode = this.from; - this.from = this.controlNodes.from; - return this.controlNodes.from; - } - else if (toDistance < 15) { - this.connectedNode = this.to; - this.to = this.controlNodes.to; - return this.controlNodes.to; - } - else { - return null; - } - }; + // extend Hammer default options with the Hammer.gesture options + Utils.extend(Hammer.defaults, options, true); + // set its index + gesture.index = gesture.index || 1000; - /** - * this resets the control nodes to their original position. - * @private - */ - Edge.prototype._restoreControlNodes = function() { - if (this.controlNodes.from.selected == true) { - this.from = this.connectedNode; - this.connectedNode = null; - this.controlNodes.from.unselect(); - } - else if (this.controlNodes.to.selected == true) { - this.to = this.connectedNode; - this.connectedNode = null; - this.controlNodes.to.unselect(); - } + // add Hammer.gesture to the list + this.gestures.push(gesture); + + // sort the list by index + this.gestures.sort(function(a, b) { + if(a.index < b.index) { + return -1; + } + if(a.index > b.index) { + return 1; + } + return 0; + }); + + return this.gestures; + } }; + /** - * this calculates the position of the control nodes on the edges of the parent nodes. - * - * @param ctx - * @returns {x: *, y: *} + * @module hammer */ - Edge.prototype.getControlNodeFromPosition = function(ctx) { - // draw arrow head - var controlnodeFromPos; - if (this.options.smoothCurves.enabled == true) { - controlnodeFromPos = this._findBorderPosition(true, ctx); - } - else { - var angle = Math.atan2((this.to.y - this.from.y), (this.to.x - this.from.x)); - var dx = (this.to.x - this.from.x); - var dy = (this.to.y - this.from.y); - var edgeSegmentLength = Math.sqrt(dx * dx + dy * dy); - - var fromBorderDist = this.from.distanceToBorder(ctx, angle + Math.PI); - var fromBorderPoint = (edgeSegmentLength - fromBorderDist) / edgeSegmentLength; - controlnodeFromPos = {}; - controlnodeFromPos.x = (fromBorderPoint) * this.from.x + (1 - fromBorderPoint) * this.to.x; - controlnodeFromPos.y = (fromBorderPoint) * this.from.y + (1 - fromBorderPoint) * this.to.y; - } - - return controlnodeFromPos; - }; /** - * this calculates the position of the control nodes on the edges of the parent nodes. + * create new hammer instance + * all methods should return the instance itself, so it is chainable. * - * @param ctx - * @returns {{from: {x: number, y: number}, to: {x: *, y: *}}} + * @class Instance + * @constructor + * @param {HTMLElement} element + * @param {Object} [options={}] options are merged with `Hammer.defaults` + * @return {Hammer.Instance} */ - Edge.prototype.getControlNodeToPosition = function(ctx) { - // draw arrow head - var controlnodeFromPos,controlnodeToPos; - if (this.options.smoothCurves.enabled == true) { - controlnodeToPos = this._findBorderPosition(false, ctx); - } - else { - var angle = Math.atan2((this.to.y - this.from.y), (this.to.x - this.from.x)); - var dx = (this.to.x - this.from.x); - var dy = (this.to.y - this.from.y); - var edgeSegmentLength = Math.sqrt(dx * dx + dy * dy); - var toBorderDist = this.to.distanceToBorder(ctx, angle); - var toBorderPoint = (edgeSegmentLength - toBorderDist) / edgeSegmentLength; + Hammer.Instance = function(element, options) { + var self = this; - controlnodeToPos = {}; - controlnodeToPos.x = (1 - toBorderPoint) * this.from.x + toBorderPoint * this.to.x; - controlnodeToPos.y = (1 - toBorderPoint) * this.from.y + toBorderPoint * this.to.y; - } + // setup HammerJS window events and register all gestures + // this also sets up the default options + setup(); - return controlnodeToPos; - }; + /** + * @property element + * @type {HTMLElement} + */ + this.element = element; - module.exports = Edge; + /** + * @property enabled + * @type {Boolean} + * @protected + */ + this.enabled = true; -/***/ }, -/* 58 */ -/***/ function(module, exports, __webpack_require__) { + /** + * options, merged with the defaults + * options with an _ are converted to camelCase + * @property options + * @type {Object} + */ + Utils.each(options, function(value, name) { + delete options[name]; + options[Utils.toCamelCase(name)] = value; + }); - /** - * 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 Popup(container, x, y, text, style) { - if (container) { - this.container = container; - } - else { - this.container = document.body; - } + this.options = Utils.extend(Utils.extend({}, Hammer.defaults), options || {}); - // x, y and text are optional, see if a style object was passed in their place - if (style === undefined) { - if (typeof x === "object") { - style = x; - x = undefined; - } else if (typeof text === "object") { - style = text; - text = undefined; - } else { - // for backwards compatibility, in case clients other than Network are creating Popup directly - style = { - fontColor: 'black', - fontSize: 14, // px - fontFace: 'verdana', - color: { - border: '#666', - background: '#FFFFC6' - } - } + // add some css to the element to prevent the browser from doing its native behavoir + if(this.options.behavior) { + Utils.toggleBehavior(this.element, this.options.behavior, true); } - } - - this.x = 0; - this.y = 0; - this.padding = 5; - - if (x !== undefined && y !== undefined ) { - this.setPosition(x, y); - } - if (text !== undefined) { - this.setText(text); - } - // create the frame - this.frame = document.createElement("div"); - var styleAttr = this.frame.style; - styleAttr.position = "absolute"; - styleAttr.visibility = "hidden"; - styleAttr.border = "1px solid " + style.color.border; - styleAttr.color = style.fontColor; - styleAttr.fontSize = style.fontSize + "px"; - styleAttr.fontFamily = style.fontFace; - styleAttr.padding = this.padding + "px"; - styleAttr.backgroundColor = style.color.background; - styleAttr.borderRadius = "3px"; - styleAttr.MozBorderRadius = "3px"; - styleAttr.WebkitBorderRadius = "3px"; - styleAttr.boxShadow = "3px 3px 10px rgba(128, 128, 128, 0.5)"; - styleAttr.whiteSpace = "nowrap"; - this.container.appendChild(this.frame); - } + /** + * event start handler on the element to start the detection + * @property eventStartHandler + * @type {Object} + */ + this.eventStartHandler = Event.onTouch(element, EVENT_START, function(ev) { + if(self.enabled && ev.eventType == EVENT_START) { + Detection.startDetect(self, ev); + } else if(ev.eventType == EVENT_TOUCH) { + Detection.detect(ev); + } + }); - /** - * @param {number} x Horizontal position of the popup window - * @param {number} y Vertical position of the popup window - */ - Popup.prototype.setPosition = function(x, y) { - this.x = parseInt(x); - this.y = parseInt(y); + /** + * keep a list of user event handlers which needs to be removed when calling 'dispose' + * @property eventHandlers + * @type {Array} + */ + this.eventHandlers = []; }; - /** - * Set the content for the popup window. This can be HTML code or text. - * @param {string | Element} content - */ - Popup.prototype.setText = function(content) { - if (content instanceof Element) { - this.frame.innerHTML = ''; - this.frame.appendChild(content); - } - else { - this.frame.innerHTML = content; // string containing text or HTML - } - }; + Hammer.Instance.prototype = { + /** + * bind events to the instance + * @method on + * @chainable + * @param {String} gestures multiple gestures by splitting with a space + * @param {Function} handler + * @param {Object} handler.ev event object + */ + on: function onEvent(gestures, handler) { + var self = this; + Event.on(self.element, gestures, handler, function(type) { + self.eventHandlers.push({ gesture: type, handler: handler }); + }); + return self; + }, + + /** + * unbind events to the instance + * @method off + * @chainable + * @param {String} gestures + * @param {Function} handler + */ + off: function offEvent(gestures, handler) { + var self = this; - /** - * Show the popup window - * @param {boolean} show Optional. Show or hide the window - */ - Popup.prototype.show = function (show) { - if (show === undefined) { - show = true; - } + Event.off(self.element, gestures, handler, function(type) { + var index = Utils.inArray({ gesture: type, handler: handler }); + if(index !== false) { + self.eventHandlers.splice(index, 1); + } + }); + return self; + }, - if (show) { - var height = this.frame.clientHeight; - var width = this.frame.clientWidth; - var maxHeight = this.frame.parentNode.clientHeight; - var maxWidth = this.frame.parentNode.clientWidth; + /** + * trigger gesture event + * @method trigger + * @chainable + * @param {String} gesture + * @param {Object} [eventData] + */ + trigger: function triggerEvent(gesture, eventData) { + // optional + if(!eventData) { + eventData = {}; + } - var top = (this.y - height); - if (top + height + this.padding > maxHeight) { - top = maxHeight - height - this.padding; - } - if (top < this.padding) { - top = this.padding; - } + // create DOM event + var event = Hammer.DOCUMENT.createEvent('Event'); + event.initEvent(gesture, true, true); + event.gesture = eventData; - var left = this.x; - if (left + width + this.padding > maxWidth) { - left = maxWidth - width - this.padding; - } - if (left < this.padding) { - left = this.padding; - } + // trigger on the target if it is in the instance element, + // this is for event delegation tricks + var element = this.element; + if(Utils.hasParent(eventData.target, element)) { + element = eventData.target; + } - this.frame.style.left = left + "px"; - this.frame.style.top = top + "px"; - this.frame.style.visibility = "visible"; - } - else { - this.hide(); - } - }; + element.dispatchEvent(event); + return this; + }, - /** - * Hide the popup window - */ - Popup.prototype.hide = function () { - this.frame.style.visibility = "hidden"; - }; + /** + * enable of disable hammer.js detection + * @method enable + * @chainable + * @param {Boolean} state + */ + enable: function enable(state) { + this.enabled = state; + return this; + }, - module.exports = Popup; + /** + * dispose this hammer instance + * @method dispose + * @return {Null} + */ + dispose: function dispose() { + var i, eh; + // undo all changes made by stop_browser_behavior + Utils.toggleBehavior(this.element, this.options.behavior, false); -/***/ }, -/* 59 */ -/***/ function(module, exports, __webpack_require__) { + // unbind all custom event handlers + for(i = -1; (eh = this.eventHandlers[++i]);) { + Utils.off(this.element, eh.gesture, eh.handler); + } - var PhysicsMixin = __webpack_require__(60); - var ClusterMixin = __webpack_require__(64); - var SectorsMixin = __webpack_require__(65); - var SelectionMixin = __webpack_require__(66); - var ManipulationMixin = __webpack_require__(67); - var NavigationMixin = __webpack_require__(68); - var HierarchicalLayoutMixin = __webpack_require__(69); + this.eventHandlers = []; - /** - * Load a mixin into the network object - * - * @param {Object} sourceVariable | this object has to contain functions. - * @private - */ - exports._loadMixin = function (sourceVariable) { - for (var mixinFunction in sourceVariable) { - if (sourceVariable.hasOwnProperty(mixinFunction)) { - this[mixinFunction] = sourceVariable[mixinFunction]; + // unbind the start event listener + Event.off(this.element, EVENT_TYPES[EVENT_START], this.eventStartHandler); + + return null; } - } }; /** - * removes a mixin from the network object. - * - * @param {Object} sourceVariable | this object has to contain functions. - * @private + * @module gestures */ - exports._clearMixin = function (sourceVariable) { - for (var mixinFunction in sourceVariable) { - if (sourceVariable.hasOwnProperty(mixinFunction)) { - this[mixinFunction] = undefined; - } - } - }; - - /** - * Mixin the physics system and initialize the parameters required. + * Move with x fingers (default 1) around on the page. + * Preventing the default browser behavior is a good way to improve feel and working. + * ```` + * hammertime.on("drag", function(ev) { + * console.log(ev); + * ev.gesture.preventDefault(); + * }); + * ```` * - * @private + * @class Drag + * @static */ - exports._loadPhysicsSystem = function () { - this._loadMixin(PhysicsMixin); - this._loadSelectedForceSolver(); - if (this.constants.configurePhysics == true) { - this._loadPhysicsConfiguration(); - } - else { - this._cleanupPhysicsConfiguration(); - } - }; - - /** - * Mixin the cluster system and initialize the parameters required. - * - * @private + * @event drag + * @param {Object} ev */ - exports._loadClusterSystem = function () { - this.clusterSession = 0; - this.hubThreshold = 5; - this._loadMixin(ClusterMixin); - }; - - /** - * Mixin the sector system and initialize the parameters required - * - * @private + * @event dragstart + * @param {Object} ev */ - exports._loadSectorSystem = function () { - this.sectors = {}; - this.activeSector = ["default"]; - this.sectors["active"] = {}; - this.sectors["active"]["default"] = {"nodes": {}, - "edges": {}, - "nodeIndices": [], - "formationScale": 1.0, - "drawingNode": undefined }; - this.sectors["frozen"] = {}; - this.sectors["support"] = {"nodes": {}, - "edges": {}, - "nodeIndices": [], - "formationScale": 1.0, - "drawingNode": undefined }; - - this.nodeIndices = this.sectors["active"]["default"]["nodeIndices"]; // the node indices list is used to speed up the computation of the repulsion fields - - this._loadMixin(SectorsMixin); - }; - - /** - * Mixin the selection system and initialize the parameters required - * - * @private + * @event dragend + * @param {Object} ev */ - exports._loadSelectionSystem = function () { - this.selectionObj = {nodes: {}, edges: {}}; - - this._loadMixin(SelectionMixin); - }; - - /** - * Mixin the navigationUI (User Interface) system and initialize the parameters required - * - * @private + * @event drapleft + * @param {Object} ev + */ + /** + * @event dragright + * @param {Object} ev + */ + /** + * @event dragup + * @param {Object} ev + */ + /** + * @event dragdown + * @param {Object} ev */ - exports._loadManipulationSystem = function () { - // reset global variables -- these are used by the selection of nodes and edges. - this.blockConnectingEdgeSelection = false; - this.forceAppendSelection = false; - if (this.constants.dataManipulation.enabled == true) { - // load the manipulator HTML elements. All styling done in css. - if (this.manipulationDiv === undefined) { - this.manipulationDiv = document.createElement('div'); - this.manipulationDiv.className = 'network-manipulationDiv'; - if (this.editMode == true) { - this.manipulationDiv.style.display = "block"; - } - else { - this.manipulationDiv.style.display = "none"; - } - this.frame.appendChild(this.manipulationDiv); - } + /** + * @param {String} name + */ + (function(name) { + var triggered = false; - if (this.editModeDiv === undefined) { - this.editModeDiv = document.createElement('div'); - this.editModeDiv.className = 'network-manipulation-editMode'; - if (this.editMode == true) { - this.editModeDiv.style.display = "none"; - } - else { - this.editModeDiv.style.display = "block"; - } - this.frame.appendChild(this.editModeDiv); - } + function dragGesture(ev, inst) { + var cur = Detection.current; - if (this.closeDiv === undefined) { - this.closeDiv = document.createElement('div'); - this.closeDiv.className = 'network-manipulation-closeDiv'; - this.closeDiv.style.display = this.manipulationDiv.style.display; - this.frame.appendChild(this.closeDiv); - } + // max touches + if(inst.options.dragMaxTouches > 0 && + ev.touches.length > inst.options.dragMaxTouches) { + return; + } - // load the manipulation functions - this._loadMixin(ManipulationMixin); + switch(ev.eventType) { + case EVENT_START: + triggered = false; + break; - // create the manipulator toolbar - this._createManipulatorBar(); - } - else { - if (this.manipulationDiv !== undefined) { - // removes all the bindings and overloads - this._createManipulatorBar(); + case EVENT_MOVE: + // when the distance we moved is too small we skip this gesture + // or we can be already in dragging + if(ev.distance < inst.options.dragMinDistance && + cur.name != name) { + return; + } - // remove the manipulation divs - this.frame.removeChild(this.manipulationDiv); - this.frame.removeChild(this.editModeDiv); - this.frame.removeChild(this.closeDiv); + var startCenter = cur.startEvent.center; - this.manipulationDiv = undefined; - this.editModeDiv = undefined; - this.closeDiv = undefined; - // remove the mixin functions - this._clearMixin(ManipulationMixin); - } - } - }; + // we are dragging! + if(cur.name != name) { + cur.name = name; + if(inst.options.dragDistanceCorrection && ev.distance > 0) { + // When a drag is triggered, set the event center to dragMinDistance pixels from the original event center. + // Without this correction, the dragged distance would jumpstart at dragMinDistance pixels instead of at 0. + // It might be useful to save the original start point somewhere + var factor = Math.abs(inst.options.dragMinDistance / ev.distance); + startCenter.pageX += ev.deltaX * factor; + startCenter.pageY += ev.deltaY * factor; + startCenter.clientX += ev.deltaX * factor; + startCenter.clientY += ev.deltaY * factor; + // recalculate event data using new start point + ev = Detection.extendEventData(ev); + } + } - /** - * Mixin the navigation (User Interface) system and initialize the parameters required - * - * @private - */ - exports._loadNavigationControls = function () { - this._loadMixin(NavigationMixin); - // the clean function removes the button divs, this is done to remove the bindings. - this._cleanNavigation(); - if (this.constants.navigation.enabled == true) { - this._loadNavigationElements(); - } - }; + // lock drag to axis? + if(cur.lastEvent.dragLockToAxis || + ( inst.options.dragLockToAxis && + inst.options.dragLockMinDistance <= ev.distance + )) { + ev.dragLockToAxis = true; + } + // keep direction on the axis that the drag gesture started on + var lastDirection = cur.lastEvent.direction; + if(ev.dragLockToAxis && lastDirection !== ev.direction) { + if(Utils.isVertical(lastDirection)) { + ev.direction = (ev.deltaY < 0) ? DIRECTION_UP : DIRECTION_DOWN; + } else { + ev.direction = (ev.deltaX < 0) ? DIRECTION_LEFT : DIRECTION_RIGHT; + } + } - /** - * Mixin the hierarchical layout system. - * - * @private - */ - exports._loadHierarchySystem = function () { - this._loadMixin(HierarchicalLayoutMixin); - }; + // first time, trigger dragstart event + if(!triggered) { + inst.trigger(name + 'start', ev); + triggered = true; + } + // trigger events + inst.trigger(name, ev); + inst.trigger(name + ev.direction, ev); -/***/ }, -/* 60 */ -/***/ function(module, exports, __webpack_require__) { + var isVertical = Utils.isVertical(ev.direction); - var util = __webpack_require__(1); - var RepulsionMixin = __webpack_require__(61); - var HierarchialRepulsionMixin = __webpack_require__(62); - var BarnesHutMixin = __webpack_require__(63); + // block the browser events + if((inst.options.dragBlockVertical && isVertical) || + (inst.options.dragBlockHorizontal && !isVertical)) { + ev.preventDefault(); + } + break; - /** - * Toggling barnes Hut calculation on and off. - * - * @private - */ - exports._toggleBarnesHut = function () { - this.constants.physics.barnesHut.enabled = !this.constants.physics.barnesHut.enabled; - this._loadSelectedForceSolver(); - this.moving = true; - this.start(); - }; + case EVENT_RELEASE: + if(triggered && ev.changedLength <= inst.options.dragMaxTouches) { + inst.trigger(name + 'end', ev); + triggered = false; + } + break; + case EVENT_END: + triggered = false; + break; + } + } - /** - * This loads the node force solver based on the barnes hut or repulsion algorithm - * - * @private - */ - exports._loadSelectedForceSolver = function () { - // this overloads the this._calculateNodeForces - if (this.constants.physics.barnesHut.enabled == true) { - this._clearMixin(RepulsionMixin); - this._clearMixin(HierarchialRepulsionMixin); + Hammer.gestures.Drag = { + name: name, + index: 50, + handler: dragGesture, + defaults: { + /** + * minimal movement that have to be made before the drag event gets triggered + * @property dragMinDistance + * @type {Number} + * @default 10 + */ + dragMinDistance: 10, - this.constants.physics.centralGravity = this.constants.physics.barnesHut.centralGravity; - this.constants.physics.springLength = this.constants.physics.barnesHut.springLength; - this.constants.physics.springConstant = this.constants.physics.barnesHut.springConstant; - this.constants.physics.damping = this.constants.physics.barnesHut.damping; + /** + * Set dragDistanceCorrection to true to make the starting point of the drag + * be calculated from where the drag was triggered, not from where the touch started. + * Useful to avoid a jerk-starting drag, which can make fine-adjustments + * through dragging difficult, and be visually unappealing. + * @property dragDistanceCorrection + * @type {Boolean} + * @default true + */ + dragDistanceCorrection: true, - this._loadMixin(BarnesHutMixin); - } - else if (this.constants.physics.hierarchicalRepulsion.enabled == true) { - this._clearMixin(BarnesHutMixin); - this._clearMixin(RepulsionMixin); + /** + * set 0 for unlimited, but this can conflict with transform + * @property dragMaxTouches + * @type {Number} + * @default 1 + */ + dragMaxTouches: 1, - this.constants.physics.centralGravity = this.constants.physics.hierarchicalRepulsion.centralGravity; - this.constants.physics.springLength = this.constants.physics.hierarchicalRepulsion.springLength; - this.constants.physics.springConstant = this.constants.physics.hierarchicalRepulsion.springConstant; - this.constants.physics.damping = this.constants.physics.hierarchicalRepulsion.damping; + /** + * prevent default browser behavior when dragging occurs + * be careful with it, it makes the element a blocking element + * when you are using the drag gesture, it is a good practice to set this true + * @property dragBlockHorizontal + * @type {Boolean} + * @default false + */ + dragBlockHorizontal: false, - this._loadMixin(HierarchialRepulsionMixin); - } - else { - this._clearMixin(BarnesHutMixin); - this._clearMixin(HierarchialRepulsionMixin); - this.barnesHutTree = undefined; + /** + * same as `dragBlockHorizontal`, but for vertical movement + * @property dragBlockVertical + * @type {Boolean} + * @default false + */ + dragBlockVertical: false, - this.constants.physics.centralGravity = this.constants.physics.repulsion.centralGravity; - this.constants.physics.springLength = this.constants.physics.repulsion.springLength; - this.constants.physics.springConstant = this.constants.physics.repulsion.springConstant; - this.constants.physics.damping = this.constants.physics.repulsion.damping; + /** + * dragLockToAxis keeps the drag gesture on the axis that it started on, + * It disallows vertical directions if the initial direction was horizontal, and vice versa. + * @property dragLockToAxis + * @type {Boolean} + * @default false + */ + dragLockToAxis: false, - this._loadMixin(RepulsionMixin); - } - }; + /** + * drag lock only kicks in when distance > dragLockMinDistance + * This way, locking occurs only when the distance has become large enough to reliably determine the direction + * @property dragLockMinDistance + * @type {Number} + * @default 25 + */ + dragLockMinDistance: 25 + } + }; + })('drag'); /** - * Before calculating the forces, we check if we need to cluster to keep up performance and we check - * if there is more than one node. If it is just one node, we dont calculate anything. + * @module gestures + */ + /** + * trigger a simple gesture event, so you can do anything in your handler. + * only usable if you know what your doing... * - * @private + * @class Gesture + * @static */ - exports._initializeForceCalculation = function () { - // stop calculation if there is only one node - if (this.nodeIndices.length == 1) { - this.nodes[this.nodeIndices[0]]._setForce(0, 0); - } - else { - // if there are too many nodes on screen, we cluster without repositioning - if (this.nodeIndices.length > this.constants.clustering.clusterThreshold && this.constants.clustering.enabled == true) { - this.clusterToFit(this.constants.clustering.reduceToNodes, false); + /** + * @event gesture + * @param {Object} ev + */ + Hammer.gestures.Gesture = { + name: 'gesture', + index: 1337, + handler: function releaseGesture(ev, inst) { + inst.trigger(this.name, ev); } - - // we now start the force calculation - this._calculateForces(); - } }; + /** + * @module gestures + */ + /** + * Touch stays at the same place for x time + * + * @class Hold + * @static + */ + /** + * @event hold + * @param {Object} ev + */ /** - * Calculate the external forces acting on the nodes - * Forces are caused by: edges, repulsing forces between nodes, gravity - * @private + * @param {String} name */ - exports._calculateForces = function () { - // Gravity is required to keep separated groups from floating off - // the forces are reset to zero in this loop by using _setForce instead - // of _addForce + (function(name) { + var timer; - this._calculateGravitationalForces(); - this._calculateNodeForces(); + function holdGesture(ev, inst) { + var options = inst.options, + current = Detection.current; - if (this.constants.physics.springConstant > 0) { - if (this.constants.smoothCurves.enabled == true && this.constants.smoothCurves.dynamic == true) { - this._calculateSpringForcesWithSupport(); - } - else { - if (this.constants.physics.hierarchicalRepulsion.enabled == true) { - this._calculateHierarchicalSpringForces(); - } - else { - this._calculateSpringForces(); - } + switch(ev.eventType) { + case EVENT_START: + clearTimeout(timer); + + // set the gesture so we can check in the timeout if it still is + current.name = name; + + // set timer and if after the timeout it still is hold, + // we trigger the hold event + timer = setTimeout(function() { + if(current && current.name == name) { + inst.trigger(name, ev); + } + }, options.holdTimeout); + break; + + case EVENT_MOVE: + if(ev.distance > options.holdThreshold) { + clearTimeout(timer); + } + break; + + case EVENT_RELEASE: + clearTimeout(timer); + break; + } } - } - }; + Hammer.gestures.Hold = { + name: name, + index: 10, + defaults: { + /** + * @property holdTimeout + * @type {Number} + * @default 500 + */ + holdTimeout: 500, + + /** + * movement allowed while holding + * @property holdThreshold + * @type {Number} + * @default 2 + */ + holdThreshold: 2 + }, + handler: holdGesture + }; + })('hold'); /** - * Smooth curves are created by adding invisible nodes in the center of the edges. These nodes are also - * handled in the calculateForces function. We then use a quadratic curve with the center node as control. - * This function joins the datanodes and invisible (called support) nodes into one object. - * We do this so we do not contaminate this.nodes with the support nodes. + * @module gestures + */ + /** + * when a touch is being released from the page * - * @private + * @class Release + * @static */ - exports._updateCalculationNodes = function () { - if (this.constants.smoothCurves.enabled == true && this.constants.smoothCurves.dynamic == true) { - this.calculationNodes = {}; - this.calculationNodeIndices = []; - - for (var nodeId in this.nodes) { - if (this.nodes.hasOwnProperty(nodeId)) { - this.calculationNodes[nodeId] = this.nodes[nodeId]; - } - } - var supportNodes = this.sectors['support']['nodes']; - for (var supportNodeId in supportNodes) { - if (supportNodes.hasOwnProperty(supportNodeId)) { - if (this.edges.hasOwnProperty(supportNodes[supportNodeId].parentEdgeId)) { - this.calculationNodes[supportNodeId] = supportNodes[supportNodeId]; - } - else { - supportNodes[supportNodeId]._setForce(0, 0); + /** + * @event release + * @param {Object} ev + */ + Hammer.gestures.Release = { + name: 'release', + index: Infinity, + handler: function releaseGesture(ev, inst) { + if(ev.eventType == EVENT_RELEASE) { + inst.trigger(this.name, ev); } - } } - - for (var idx in this.calculationNodes) { - if (this.calculationNodes.hasOwnProperty(idx)) { - this.calculationNodeIndices.push(idx); - } - } - } - else { - this.calculationNodes = this.nodes; - this.calculationNodeIndices = this.nodeIndices; - } }; - /** - * this function applies the central gravity effect to keep groups from floating off + * @module gestures + */ + /** + * triggers swipe events when the end velocity is above the threshold + * for best usage, set `preventDefault` (on the drag gesture) to `true` + * ```` + * hammertime.on("dragleft swipeleft", function(ev) { + * console.log(ev); + * ev.gesture.preventDefault(); + * }); + * ```` * - * @private + * @class Swipe + * @static */ - exports._calculateGravitationalForces = function () { - var dx, dy, distance, node, i; - var nodes = this.calculationNodes; - var gravity = this.constants.physics.centralGravity; - var gravityForce = 0; + /** + * @event swipe + * @param {Object} ev + */ + /** + * @event swipeleft + * @param {Object} ev + */ + /** + * @event swiperight + * @param {Object} ev + */ + /** + * @event swipeup + * @param {Object} ev + */ + /** + * @event swipedown + * @param {Object} ev + */ + Hammer.gestures.Swipe = { + name: 'swipe', + index: 40, + defaults: { + /** + * @property swipeMinTouches + * @type {Number} + * @default 1 + */ + swipeMinTouches: 1, - for (i = 0; i < this.calculationNodeIndices.length; i++) { - node = nodes[this.calculationNodeIndices[i]]; - node.damping = this.constants.physics.damping; // possibly add function to alter damping properties of clusters. - // gravity does not apply when we are in a pocket sector - if (this._sector() == "default" && gravity != 0) { - dx = -node.x; - dy = -node.y; - distance = Math.sqrt(dx * dx + dy * dy); + /** + * @property swipeMaxTouches + * @type {Number} + * @default 1 + */ + swipeMaxTouches: 1, - gravityForce = (distance == 0) ? 0 : (gravity / distance); - node.fx = dx * gravityForce; - node.fy = dy * gravityForce; - } - else { - node.fx = 0; - node.fy = 0; - } - } - }; + /** + * horizontal swipe velocity + * @property swipeVelocityX + * @type {Number} + * @default 0.6 + */ + swipeVelocityX: 0.6, + + /** + * vertical swipe velocity + * @property swipeVelocityY + * @type {Number} + * @default 0.6 + */ + swipeVelocityY: 0.6 + }, + handler: function swipeGesture(ev, inst) { + if(ev.eventType == EVENT_RELEASE) { + var touches = ev.touches.length, + options = inst.options; + // max touches + if(touches < options.swipeMinTouches || + touches > options.swipeMaxTouches) { + return; + } + // when the distance we moved is too small we skip this gesture + // or we can be already in dragging + if(ev.velocityX > options.swipeVelocityX || + ev.velocityY > options.swipeVelocityY) { + // trigger swipe events + inst.trigger(this.name, ev); + inst.trigger(this.name + ev.direction, ev); + } + } + } + }; /** - * this function calculates the effects of the springs in the case of unsmooth curves. + * @module gestures + */ + /** + * Single tap and a double tap on a place * - * @private + * @class Tap + * @static + */ + /** + * @event tap + * @param {Object} ev + */ + /** + * @event doubletap + * @param {Object} ev */ - exports._calculateSpringForces = function () { - var edgeLength, edge, edgeId; - var dx, dy, fx, fy, springForce, distance; - var edges = this.edges; - // forces caused by the edges, modelled as springs - for (edgeId in edges) { - if (edges.hasOwnProperty(edgeId)) { - edge = edges[edgeId]; - if (edge.connected) { - // only calculate forces if nodes are in the same sector - if (this.nodes.hasOwnProperty(edge.toId) && this.nodes.hasOwnProperty(edge.fromId)) { - edgeLength = edge.physics.springLength; - // this implies that the edges between big clusters are longer - edgeLength += (edge.to.clusterSize + edge.from.clusterSize - 2) * this.constants.clustering.edgeGrowth; + /** + * @param {String} name + */ + (function(name) { + var hasMoved = false; - dx = (edge.from.x - edge.to.x); - dy = (edge.from.y - edge.to.y); - distance = Math.sqrt(dx * dx + dy * dy); + function tapGesture(ev, inst) { + var options = inst.options, + current = Detection.current, + prev = Detection.previous, + sincePrev, + didDoubleTap; - if (distance == 0) { - distance = 0.01; - } + switch(ev.eventType) { + case EVENT_START: + hasMoved = false; + break; - // the 1/distance is so the fx and fy can be calculated without sine or cosine. - springForce = this.constants.physics.springConstant * (edgeLength - distance) / distance; + case EVENT_MOVE: + hasMoved = hasMoved || (ev.distance > options.tapMaxDistance); + break; - fx = dx * springForce; - fy = dy * springForce; + case EVENT_END: + if(!Utils.inStr(ev.srcEvent.type, 'cancel') && ev.deltaTime < options.tapMaxTime && !hasMoved) { + // previous gesture, for the double tap since these are two different gesture detections + sincePrev = prev && prev.lastEvent && ev.timeStamp - prev.lastEvent.timeStamp; + didDoubleTap = false; - edge.from.fx += fx; - edge.from.fy += fy; - edge.to.fx -= fx; - edge.to.fy -= fy; + // check if double tap + if(prev && prev.name == name && + (sincePrev && sincePrev < options.doubleTapInterval) && + ev.distance < options.doubleTapDistance) { + inst.trigger('doubletap', ev); + didDoubleTap = true; + } + + // do a single tap + if(!didDoubleTap || options.tapAlways) { + current.name = name; + inst.trigger(current.name, ev); + } + } + break; } - } } - } - }; + Hammer.gestures.Tap = { + name: name, + index: 100, + handler: tapGesture, + defaults: { + /** + * max time of a tap, this is for the slow tappers + * @property tapMaxTime + * @type {Number} + * @default 250 + */ + tapMaxTime: 250, + + /** + * max distance of movement of a tap, this is for the slow tappers + * @property tapMaxDistance + * @type {Number} + * @default 10 + */ + tapMaxDistance: 10, + + /** + * always trigger the `tap` event, even while double-tapping + * @property tapAlways + * @type {Boolean} + * @default true + */ + tapAlways: true, + /** + * max distance between two taps + * @property doubleTapDistance + * @type {Number} + * @default 20 + */ + doubleTapDistance: 20, + /** + * max time between two taps + * @property doubleTapInterval + * @type {Number} + * @default 300 + */ + doubleTapInterval: 300 + } + }; + })('tap'); /** - * This function calculates the springforces on the nodes, accounting for the support nodes. + * @module gestures + */ + /** + * when a touch is being touched at the page * - * @private + * @class Touch + * @static */ - exports._calculateSpringForcesWithSupport = function () { - var edgeLength, edge, edgeId, combinedClusterSize; - var edges = this.edges; - - // forces caused by the edges, modelled as springs - for (edgeId in edges) { - if (edges.hasOwnProperty(edgeId)) { - edge = edges[edgeId]; - if (edge.connected) { - // only calculate forces if nodes are in the same sector - if (this.nodes.hasOwnProperty(edge.toId) && this.nodes.hasOwnProperty(edge.fromId)) { - if (edge.via != null) { - var node1 = edge.to; - var node2 = edge.via; - var node3 = edge.from; + /** + * @event touch + * @param {Object} ev + */ + Hammer.gestures.Touch = { + name: 'touch', + index: -Infinity, + defaults: { + /** + * call preventDefault at touchstart, and makes the element blocking by disabling the scrolling of the page, + * but it improves gestures like transforming and dragging. + * be careful with using this, it can be very annoying for users to be stuck on the page + * @property preventDefault + * @type {Boolean} + * @default false + */ + preventDefault: false, - edgeLength = edge.physics.springLength; + /** + * disable mouse events, so only touch (or pen!) input triggers events + * @property preventMouse + * @type {Boolean} + * @default false + */ + preventMouse: false + }, + handler: function touchGesture(ev, inst) { + if(inst.options.preventMouse && ev.pointerType == POINTER_MOUSE) { + ev.stopDetect(); + return; + } - combinedClusterSize = node1.clusterSize + node3.clusterSize - 2; + if(inst.options.preventDefault) { + ev.preventDefault(); + } - // this implies that the edges between big clusters are longer - edgeLength += combinedClusterSize * this.constants.clustering.edgeGrowth; - this._calculateSpringForce(node1, node2, 0.5 * edgeLength); - this._calculateSpringForce(node2, node3, 0.5 * edgeLength); - } + if(ev.eventType == EVENT_TOUCH) { + inst.trigger('touch', ev); } - } } - } }; - /** - * This is the code actually performing the calculation for the function above. It is split out to avoid repetition. - * - * @param node1 - * @param node2 - * @param edgeLength - * @private + * @module gestures + */ + /** + * User want to scale or rotate with 2 fingers + * Preventing the default browser behavior is a good way to improve feel and working. This can be done with the + * `preventDefault` option. + * + * @class Transform + * @static + */ + /** + * @event transform + * @param {Object} ev + */ + /** + * @event transformstart + * @param {Object} ev + */ + /** + * @event transformend + * @param {Object} ev + */ + /** + * @event pinchin + * @param {Object} ev + */ + /** + * @event pinchout + * @param {Object} ev + */ + /** + * @event rotate + * @param {Object} ev */ - exports._calculateSpringForce = function (node1, node2, edgeLength) { - var dx, dy, fx, fy, springForce, distance; - dx = (node1.x - node2.x); - dy = (node1.y - node2.y); - distance = Math.sqrt(dx * dx + dy * dy); + /** + * @param {String} name + */ + (function(name) { + var triggered = false; - if (distance == 0) { - distance = 0.01; - } + function transformGesture(ev, inst) { + switch(ev.eventType) { + case EVENT_START: + triggered = false; + break; - // the 1/distance is so the fx and fy can be calculated without sine or cosine. - springForce = this.constants.physics.springConstant * (edgeLength - distance) / distance; + case EVENT_MOVE: + // at least multitouch + if(ev.touches.length < 2) { + return; + } - fx = dx * springForce; - fy = dy * springForce; + var scaleThreshold = Math.abs(1 - ev.scale); + var rotationThreshold = Math.abs(ev.rotation); - node1.fx += fx; - node1.fy += fy; - node2.fx -= fx; - node2.fy -= fy; - }; + // when the distance we moved is too small we skip this gesture + // or we can be already in dragging + if(scaleThreshold < inst.options.transformMinScale && + rotationThreshold < inst.options.transformMinRotation) { + return; + } + // we are transforming! + Detection.current.name = name; - exports._cleanupPhysicsConfiguration = function() { - if (this.physicsConfiguration !== undefined) { - while (this.physicsConfiguration.hasChildNodes()) { - this.physicsConfiguration.removeChild(this.physicsConfiguration.firstChild); - } + // first time, trigger dragstart event + if(!triggered) { + inst.trigger(name + 'start', ev); + triggered = true; + } - this.physicsConfiguration.parentNode.removeChild(this.physicsConfiguration); - this.physicsConfiguration = undefined; - } - } + inst.trigger(name, ev); // basic transform event - /** - * Load the HTML for the physics config and bind it - * @private - */ - exports._loadPhysicsConfiguration = function () { - if (this.physicsConfiguration === undefined) { - this.backupConstants = {}; - util.deepExtend(this.backupConstants,this.constants); + // trigger rotate event + if(rotationThreshold > inst.options.transformMinRotation) { + inst.trigger('rotate', ev); + } - var hierarchicalLayoutDirections = ["LR", "RL", "UD", "DU"]; - this.physicsConfiguration = document.createElement('div'); - this.physicsConfiguration.className = "PhysicsConfiguration"; - this.physicsConfiguration.innerHTML = '' + - '' + - '' + - '' + - '' + - '' + - '' + - '
Simulation Mode:
Barnes HutRepulsionHierarchical
' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '
Options:
' - this.containerElement.parentElement.insertBefore(this.physicsConfiguration, this.containerElement); - this.optionsDiv = document.createElement("div"); - this.optionsDiv.style.fontSize = "14px"; - this.optionsDiv.style.fontFamily = "verdana"; - this.containerElement.parentElement.insertBefore(this.optionsDiv, this.containerElement); + // trigger pinch event + if(scaleThreshold > inst.options.transformMinScale) { + inst.trigger('pinch', ev); + inst.trigger('pinch' + (ev.scale < 1 ? 'in' : 'out'), ev); + } + break; - var rangeElement; - rangeElement = document.getElementById('graph_BH_gc'); - rangeElement.onchange = showValueOfRange.bind(this, 'graph_BH_gc', -1, "physics_barnesHut_gravitationalConstant"); - rangeElement = document.getElementById('graph_BH_cg'); - rangeElement.onchange = showValueOfRange.bind(this, 'graph_BH_cg', 1, "physics_centralGravity"); - rangeElement = document.getElementById('graph_BH_sc'); - rangeElement.onchange = showValueOfRange.bind(this, 'graph_BH_sc', 1, "physics_springConstant"); - rangeElement = document.getElementById('graph_BH_sl'); - rangeElement.onchange = showValueOfRange.bind(this, 'graph_BH_sl', 1, "physics_springLength"); - rangeElement = document.getElementById('graph_BH_damp'); - rangeElement.onchange = showValueOfRange.bind(this, 'graph_BH_damp', 1, "physics_damping"); + case EVENT_RELEASE: + if(triggered && ev.changedLength < 2) { + inst.trigger(name + 'end', ev); + triggered = false; + } + break; + } + } - rangeElement = document.getElementById('graph_R_nd'); - rangeElement.onchange = showValueOfRange.bind(this, 'graph_R_nd', 1, "physics_repulsion_nodeDistance"); - rangeElement = document.getElementById('graph_R_cg'); - rangeElement.onchange = showValueOfRange.bind(this, 'graph_R_cg', 1, "physics_centralGravity"); - rangeElement = document.getElementById('graph_R_sc'); - rangeElement.onchange = showValueOfRange.bind(this, 'graph_R_sc', 1, "physics_springConstant"); - rangeElement = document.getElementById('graph_R_sl'); - rangeElement.onchange = showValueOfRange.bind(this, 'graph_R_sl', 1, "physics_springLength"); - rangeElement = document.getElementById('graph_R_damp'); - rangeElement.onchange = showValueOfRange.bind(this, 'graph_R_damp', 1, "physics_damping"); + Hammer.gestures.Transform = { + name: name, + index: 45, + defaults: { + /** + * minimal scale factor, no scale is 1, zoomin is to 0 and zoomout until higher then 1 + * @property transformMinScale + * @type {Number} + * @default 0.01 + */ + transformMinScale: 0.01, - rangeElement = document.getElementById('graph_H_nd'); - rangeElement.onchange = showValueOfRange.bind(this, 'graph_H_nd', 1, "physics_hierarchicalRepulsion_nodeDistance"); - rangeElement = document.getElementById('graph_H_cg'); - rangeElement.onchange = showValueOfRange.bind(this, 'graph_H_cg', 1, "physics_centralGravity"); - rangeElement = document.getElementById('graph_H_sc'); - rangeElement.onchange = showValueOfRange.bind(this, 'graph_H_sc', 1, "physics_springConstant"); - rangeElement = document.getElementById('graph_H_sl'); - rangeElement.onchange = showValueOfRange.bind(this, 'graph_H_sl', 1, "physics_springLength"); - rangeElement = document.getElementById('graph_H_damp'); - rangeElement.onchange = showValueOfRange.bind(this, 'graph_H_damp', 1, "physics_damping"); - rangeElement = document.getElementById('graph_H_direction'); - rangeElement.onchange = showValueOfRange.bind(this, 'graph_H_direction', hierarchicalLayoutDirections, "hierarchicalLayout_direction"); - rangeElement = document.getElementById('graph_H_levsep'); - rangeElement.onchange = showValueOfRange.bind(this, 'graph_H_levsep', 1, "hierarchicalLayout_levelSeparation"); - rangeElement = document.getElementById('graph_H_nspac'); - rangeElement.onchange = showValueOfRange.bind(this, 'graph_H_nspac', 1, "hierarchicalLayout_nodeSpacing"); + /** + * rotation in degrees + * @property transformMinRotation + * @type {Number} + * @default 1 + */ + transformMinRotation: 1 + }, - var radioButton1 = document.getElementById("graph_physicsMethod1"); - var radioButton2 = document.getElementById("graph_physicsMethod2"); - var radioButton3 = document.getElementById("graph_physicsMethod3"); - radioButton2.checked = true; - if (this.constants.physics.barnesHut.enabled) { - radioButton1.checked = true; - } - if (this.constants.hierarchicalLayout.enabled) { - radioButton3.checked = true; - } + handler: transformGesture + }; + })('transform'); - var graph_toggleSmooth = document.getElementById("graph_toggleSmooth"); - var graph_repositionNodes = document.getElementById("graph_repositionNodes"); - var graph_generateOptions = document.getElementById("graph_generateOptions"); + /** + * @module hammer + */ - graph_toggleSmooth.onclick = graphToggleSmoothCurves.bind(this); - graph_repositionNodes.onclick = graphRepositionNodes.bind(this); - graph_generateOptions.onclick = graphGenerateOptions.bind(this); - if (this.constants.smoothCurves == true && this.constants.dynamicSmoothCurves == false) { - graph_toggleSmooth.style.background = "#A4FF56"; - } - else { - graph_toggleSmooth.style.background = "#FF8532"; - } + // AMD export + if(true) { + !(__WEBPACK_AMD_DEFINE_RESULT__ = function() { + return Hammer; + }.call(exports, __webpack_require__, exports, module), __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__)); + // commonjs export + } else if(typeof module !== 'undefined' && module.exports) { + module.exports = Hammer; + // browser export + } else { + window.Hammer = Hammer; + } + })(window); - switchConfigurations.apply(this); +/***/ }, +/* 60 */ +/***/ function(module, exports, __webpack_require__) { - radioButton1.onchange = switchConfigurations.bind(this); - radioButton2.onchange = switchConfigurations.bind(this); - radioButton3.onchange = switchConfigurations.bind(this); - } - }; + var util = __webpack_require__(1); + var RepulsionMixin = __webpack_require__(68); + var HierarchialRepulsionMixin = __webpack_require__(69); + var BarnesHutMixin = __webpack_require__(70); /** - * This overwrites the this.constants. + * Toggling barnes Hut calculation on and off. * - * @param constantsVariableName - * @param value * @private */ - exports._overWriteGraphConstants = function (constantsVariableName, value) { - var nameArray = constantsVariableName.split("_"); - if (nameArray.length == 1) { - this.constants[nameArray[0]] = value; - } - else if (nameArray.length == 2) { - this.constants[nameArray[0]][nameArray[1]] = value; - } - else if (nameArray.length == 3) { - this.constants[nameArray[0]][nameArray[1]][nameArray[2]] = value; - } + exports._toggleBarnesHut = function () { + this.constants.physics.barnesHut.enabled = !this.constants.physics.barnesHut.enabled; + this._loadSelectedForceSolver(); + this.moving = true; + this.start(); }; /** - * this function is bound to the toggle smooth curves button. That is also why it is not in the prototype. - */ - function graphToggleSmoothCurves () { - this.constants.smoothCurves.enabled = !this.constants.smoothCurves.enabled; - var graph_toggleSmooth = document.getElementById("graph_toggleSmooth"); - if (this.constants.smoothCurves.enabled == true) {graph_toggleSmooth.style.background = "#A4FF56";} - else {graph_toggleSmooth.style.background = "#FF8532";} - - this._configureSmoothCurves(false); - } - - /** - * this function is used to scramble the nodes + * This loads the node force solver based on the barnes hut or repulsion algorithm * + * @private */ - function graphRepositionNodes () { - for (var nodeId in this.calculationNodes) { - if (this.calculationNodes.hasOwnProperty(nodeId)) { - this.calculationNodes[nodeId].vx = 0; this.calculationNodes[nodeId].vy = 0; - this.calculationNodes[nodeId].fx = 0; this.calculationNodes[nodeId].fy = 0; - } - } - if (this.constants.hierarchicalLayout.enabled == true) { - this._setupHierarchicalLayout(); - showValueOfRange.call(this, 'graph_H_nd', 1, "physics_hierarchicalRepulsion_nodeDistance"); - showValueOfRange.call(this, 'graph_H_cg', 1, "physics_centralGravity"); - showValueOfRange.call(this, 'graph_H_sc', 1, "physics_springConstant"); - showValueOfRange.call(this, 'graph_H_sl', 1, "physics_springLength"); - showValueOfRange.call(this, 'graph_H_damp', 1, "physics_damping"); - } - else { - this.repositionNodes(); - } - this.moving = true; - this.start(); - } + exports._loadSelectedForceSolver = function () { + // this overloads the this._calculateNodeForces + if (this.constants.physics.barnesHut.enabled == true) { + this._clearMixin(RepulsionMixin); + this._clearMixin(HierarchialRepulsionMixin); - /** - * this is used to generate an options file from the playing with physics system. - */ - function graphGenerateOptions () { - var options = "No options are required, default values used."; - var optionsSpecific = []; - var radioButton1 = document.getElementById("graph_physicsMethod1"); - var radioButton2 = document.getElementById("graph_physicsMethod2"); - if (radioButton1.checked == true) { - if (this.constants.physics.barnesHut.gravitationalConstant != this.backupConstants.physics.barnesHut.gravitationalConstant) {optionsSpecific.push("gravitationalConstant: " + this.constants.physics.barnesHut.gravitationalConstant);} - if (this.constants.physics.centralGravity != this.backupConstants.physics.barnesHut.centralGravity) {optionsSpecific.push("centralGravity: " + this.constants.physics.centralGravity);} - if (this.constants.physics.springLength != this.backupConstants.physics.barnesHut.springLength) {optionsSpecific.push("springLength: " + this.constants.physics.springLength);} - if (this.constants.physics.springConstant != this.backupConstants.physics.barnesHut.springConstant) {optionsSpecific.push("springConstant: " + this.constants.physics.springConstant);} - if (this.constants.physics.damping != this.backupConstants.physics.barnesHut.damping) {optionsSpecific.push("damping: " + this.constants.physics.damping);} - if (optionsSpecific.length != 0) { - options = "var options = {"; - options += "physics: {barnesHut: {"; - for (var i = 0; i < optionsSpecific.length; i++) { - options += optionsSpecific[i]; - if (i < optionsSpecific.length - 1) { - options += ", " - } - } - options += '}}' - } - if (this.constants.smoothCurves.enabled != this.backupConstants.smoothCurves.enabled) { - if (optionsSpecific.length == 0) {options = "var options = {";} - else {options += ", "} - options += "smoothCurves: " + this.constants.smoothCurves.enabled; - } - if (options != "No options are required, default values used.") { - options += '};' - } - } - else if (radioButton2.checked == true) { - options = "var options = {"; - options += "physics: {barnesHut: {enabled: false}"; - if (this.constants.physics.repulsion.nodeDistance != this.backupConstants.physics.repulsion.nodeDistance) {optionsSpecific.push("nodeDistance: " + this.constants.physics.repulsion.nodeDistance);} - if (this.constants.physics.centralGravity != this.backupConstants.physics.repulsion.centralGravity) {optionsSpecific.push("centralGravity: " + this.constants.physics.centralGravity);} - if (this.constants.physics.springLength != this.backupConstants.physics.repulsion.springLength) {optionsSpecific.push("springLength: " + this.constants.physics.springLength);} - if (this.constants.physics.springConstant != this.backupConstants.physics.repulsion.springConstant) {optionsSpecific.push("springConstant: " + this.constants.physics.springConstant);} - if (this.constants.physics.damping != this.backupConstants.physics.repulsion.damping) {optionsSpecific.push("damping: " + this.constants.physics.damping);} - if (optionsSpecific.length != 0) { - options += ", repulsion: {"; - for (var i = 0; i < optionsSpecific.length; i++) { - options += optionsSpecific[i]; - if (i < optionsSpecific.length - 1) { - options += ", " - } - } - options += '}}' - } - if (optionsSpecific.length == 0) {options += "}"} - if (this.constants.smoothCurves != this.backupConstants.smoothCurves) { - options += ", smoothCurves: " + this.constants.smoothCurves; - } - options += '};' - } - else { - options = "var options = {"; - if (this.constants.physics.hierarchicalRepulsion.nodeDistance != this.backupConstants.physics.hierarchicalRepulsion.nodeDistance) {optionsSpecific.push("nodeDistance: " + this.constants.physics.hierarchicalRepulsion.nodeDistance);} - if (this.constants.physics.centralGravity != this.backupConstants.physics.hierarchicalRepulsion.centralGravity) {optionsSpecific.push("centralGravity: " + this.constants.physics.centralGravity);} - if (this.constants.physics.springLength != this.backupConstants.physics.hierarchicalRepulsion.springLength) {optionsSpecific.push("springLength: " + this.constants.physics.springLength);} - if (this.constants.physics.springConstant != this.backupConstants.physics.hierarchicalRepulsion.springConstant) {optionsSpecific.push("springConstant: " + this.constants.physics.springConstant);} - if (this.constants.physics.damping != this.backupConstants.physics.hierarchicalRepulsion.damping) {optionsSpecific.push("damping: " + this.constants.physics.damping);} - if (optionsSpecific.length != 0) { - options += "physics: {hierarchicalRepulsion: {"; - for (var i = 0; i < optionsSpecific.length; i++) { - options += optionsSpecific[i]; - if (i < optionsSpecific.length - 1) { - options += ", "; - } - } - options += '}},'; - } - options += 'hierarchicalLayout: {'; - optionsSpecific = []; - if (this.constants.hierarchicalLayout.direction != this.backupConstants.hierarchicalLayout.direction) {optionsSpecific.push("direction: " + this.constants.hierarchicalLayout.direction);} - if (Math.abs(this.constants.hierarchicalLayout.levelSeparation) != this.backupConstants.hierarchicalLayout.levelSeparation) {optionsSpecific.push("levelSeparation: " + this.constants.hierarchicalLayout.levelSeparation);} - if (this.constants.hierarchicalLayout.nodeSpacing != this.backupConstants.hierarchicalLayout.nodeSpacing) {optionsSpecific.push("nodeSpacing: " + this.constants.hierarchicalLayout.nodeSpacing);} - if (optionsSpecific.length != 0) { - for (var i = 0; i < optionsSpecific.length; i++) { - options += optionsSpecific[i]; - if (i < optionsSpecific.length - 1) { - options += ", " - } - } - options += '}' - } - else { - options += "enabled:true}"; - } - options += '};' - } + this.constants.physics.centralGravity = this.constants.physics.barnesHut.centralGravity; + this.constants.physics.springLength = this.constants.physics.barnesHut.springLength; + this.constants.physics.springConstant = this.constants.physics.barnesHut.springConstant; + this.constants.physics.damping = this.constants.physics.barnesHut.damping; + this._loadMixin(BarnesHutMixin); + } + else if (this.constants.physics.hierarchicalRepulsion.enabled == true) { + this._clearMixin(BarnesHutMixin); + this._clearMixin(RepulsionMixin); - this.optionsDiv.innerHTML = options; - } + this.constants.physics.centralGravity = this.constants.physics.hierarchicalRepulsion.centralGravity; + this.constants.physics.springLength = this.constants.physics.hierarchicalRepulsion.springLength; + this.constants.physics.springConstant = this.constants.physics.hierarchicalRepulsion.springConstant; + this.constants.physics.damping = this.constants.physics.hierarchicalRepulsion.damping; - /** - * this is used to switch between barnesHut, repulsion and hierarchical. - * - */ - function switchConfigurations () { - var ids = ["graph_BH_table", "graph_R_table", "graph_H_table"]; - var radioButton = document.querySelector('input[name="graph_physicsMethod"]:checked').value; - var tableId = "graph_" + radioButton + "_table"; - var table = document.getElementById(tableId); - table.style.display = "block"; - for (var i = 0; i < ids.length; i++) { - if (ids[i] != tableId) { - table = document.getElementById(ids[i]); - table.style.display = "none"; - } - } - this._restoreNodes(); - if (radioButton == "R") { - this.constants.hierarchicalLayout.enabled = false; - this.constants.physics.hierarchicalRepulsion.enabled = false; - this.constants.physics.barnesHut.enabled = false; - } - else if (radioButton == "H") { - if (this.constants.hierarchicalLayout.enabled == false) { - this.constants.hierarchicalLayout.enabled = true; - this.constants.physics.hierarchicalRepulsion.enabled = true; - this.constants.physics.barnesHut.enabled = false; - this.constants.smoothCurves.enabled = false; - this._setupHierarchicalLayout(); - } + this._loadMixin(HierarchialRepulsionMixin); } else { - this.constants.hierarchicalLayout.enabled = false; - this.constants.physics.hierarchicalRepulsion.enabled = false; - this.constants.physics.barnesHut.enabled = true; - } - this._loadSelectedForceSolver(); - var graph_toggleSmooth = document.getElementById("graph_toggleSmooth"); - if (this.constants.smoothCurves.enabled == true) {graph_toggleSmooth.style.background = "#A4FF56";} - else {graph_toggleSmooth.style.background = "#FF8532";} - this.moving = true; - this.start(); - } + this._clearMixin(BarnesHutMixin); + this._clearMixin(HierarchialRepulsionMixin); + this.barnesHutTree = undefined; + + this.constants.physics.centralGravity = this.constants.physics.repulsion.centralGravity; + this.constants.physics.springLength = this.constants.physics.repulsion.springLength; + this.constants.physics.springConstant = this.constants.physics.repulsion.springConstant; + this.constants.physics.damping = this.constants.physics.repulsion.damping; + this._loadMixin(RepulsionMixin); + } + }; /** - * this generates the ranges depending on the iniital values. + * Before calculating the forces, we check if we need to cluster to keep up performance and we check + * if there is more than one node. If it is just one node, we dont calculate anything. * - * @param id - * @param map - * @param constantsVariableName + * @private */ - function showValueOfRange (id,map,constantsVariableName) { - var valueId = id + "_value"; - var rangeValue = document.getElementById(id).value; - - if (Array.isArray(map)) { - document.getElementById(valueId).value = map[parseInt(rangeValue)]; - this._overWriteGraphConstants(constantsVariableName,map[parseInt(rangeValue)]); + exports._initializeForceCalculation = function () { + // stop calculation if there is only one node + if (this.nodeIndices.length == 1) { + this.nodes[this.nodeIndices[0]]._setForce(0, 0); } else { - document.getElementById(valueId).value = parseInt(map) * parseFloat(rangeValue); - this._overWriteGraphConstants(constantsVariableName, parseInt(map) * parseFloat(rangeValue)); - } + // if there are too many nodes on screen, we cluster without repositioning + if (this.nodeIndices.length > this.constants.clustering.clusterThreshold && this.constants.clustering.enabled == true) { + this.clusterToFit(this.constants.clustering.reduceToNodes, false); + } - if (constantsVariableName == "hierarchicalLayout_direction" || - constantsVariableName == "hierarchicalLayout_levelSeparation" || - constantsVariableName == "hierarchicalLayout_nodeSpacing") { - this._setupHierarchicalLayout(); + // we now start the force calculation + this._calculateForces(); } - this.moving = true; - this.start(); - } - - - + }; -/***/ }, -/* 61 */ -/***/ function(module, exports, __webpack_require__) { /** - * Calculate the forces the nodes apply on each other based on a repulsion field. - * This field is linearly approximated. - * + * Calculate the external forces acting on the nodes + * Forces are caused by: edges, repulsing forces between nodes, gravity * @private */ - exports._calculateNodeForces = function () { - var dx, dy, angle, distance, fx, fy, combinedClusterSize, - repulsingForce, node1, node2, i, j; - - var nodes = this.calculationNodes; - var nodeIndices = this.calculationNodeIndices; + exports._calculateForces = function () { + // Gravity is required to keep separated groups from floating off + // the forces are reset to zero in this loop by using _setForce instead + // of _addForce - // approximation constants - var a_base = -2 / 3; - var b = 4 / 3; + this._calculateGravitationalForces(); + this._calculateNodeForces(); - // repulsing forces between nodes - var nodeDistance = this.constants.physics.repulsion.nodeDistance; - var minimumDistance = nodeDistance; + if (this.constants.physics.springConstant > 0) { + if (this.constants.smoothCurves.enabled == true && this.constants.smoothCurves.dynamic == true) { + this._calculateSpringForcesWithSupport(); + } + else { + if (this.constants.physics.hierarchicalRepulsion.enabled == true) { + this._calculateHierarchicalSpringForces(); + } + else { + this._calculateSpringForces(); + } + } + } + }; - // 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]]; - combinedClusterSize = node1.clusterSize + node2.clusterSize - 2; - dx = node2.x - node1.x; - dy = node2.y - node1.y; - distance = Math.sqrt(dx * dx + dy * dy); + /** + * Smooth curves are created by adding invisible nodes in the center of the edges. These nodes are also + * handled in the calculateForces function. We then use a quadratic curve with the center node as control. + * This function joins the datanodes and invisible (called support) nodes into one object. + * We do this so we do not contaminate this.nodes with the support nodes. + * + * @private + */ + exports._updateCalculationNodes = function () { + if (this.constants.smoothCurves.enabled == true && this.constants.smoothCurves.dynamic == true) { + this.calculationNodes = {}; + this.calculationNodeIndices = []; - // same condition as BarnesHut, making sure nodes are never 100% overlapping. - if (distance == 0) { - distance = 0.1*Math.random(); - dx = distance; + for (var nodeId in this.nodes) { + if (this.nodes.hasOwnProperty(nodeId)) { + this.calculationNodes[nodeId] = this.nodes[nodeId]; } - - minimumDistance = (combinedClusterSize == 0) ? nodeDistance : (nodeDistance * (1 + combinedClusterSize * this.constants.clustering.distanceAmplification)); - var a = a_base / minimumDistance; - if (distance < 2 * minimumDistance) { - if (distance < 0.5 * minimumDistance) { - repulsingForce = 1.0; + } + var supportNodes = this.sectors['support']['nodes']; + for (var supportNodeId in supportNodes) { + if (supportNodes.hasOwnProperty(supportNodeId)) { + if (this.edges.hasOwnProperty(supportNodes[supportNodeId].parentEdgeId)) { + this.calculationNodes[supportNodeId] = supportNodes[supportNodeId]; } else { - repulsingForce = a * distance + b; // linear approx of 1 / (1 + Math.exp((distance / minimumDistance - 1) * steepness)) + supportNodes[supportNodeId]._setForce(0, 0); } + } + } - // amplify the repulsion for clusters. - repulsingForce *= (combinedClusterSize == 0) ? 1 : 1 + combinedClusterSize * this.constants.clustering.forceAmplification; - repulsingForce = repulsingForce / Math.max(distance,0.01*minimumDistance); - - fx = dx * repulsingForce; - fy = dy * repulsingForce; - node1.fx -= fx; - node1.fy -= fy; - node2.fx += fx; - node2.fy += fy; - + for (var idx in this.calculationNodes) { + if (this.calculationNodes.hasOwnProperty(idx)) { + this.calculationNodeIndices.push(idx); } } } + else { + this.calculationNodes = this.nodes; + this.calculationNodeIndices = this.nodeIndices; + } }; -/***/ }, -/* 62 */ -/***/ function(module, exports, __webpack_require__) { - /** - * Calculate the forces the nodes apply on eachother based on a repulsion field. - * This field is linearly approximated. + * this function applies the central gravity effect to keep groups from floating off * * @private */ - exports._calculateNodeForces = function () { - var dx, dy, distance, fx, fy, - repulsingForce, node1, node2, i, j; - + exports._calculateGravitationalForces = function () { + var dx, dy, distance, node, i; var nodes = this.calculationNodes; - var nodeIndices = this.calculationNodeIndices; - - // repulsing forces between nodes - var nodeDistance = this.constants.physics.hierarchicalRepulsion.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 gravity = this.constants.physics.centralGravity; + var gravityForce = 0; - 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; + for (i = 0; i < this.calculationNodeIndices.length; i++) { + node = nodes[this.calculationNodeIndices[i]]; + node.damping = this.constants.physics.damping; // possibly add function to alter damping properties of clusters. + // gravity does not apply when we are in a pocket sector + if (this._sector() == "default" && gravity != 0) { + dx = -node.x; + dy = -node.y; + distance = Math.sqrt(dx * dx + dy * dy); - node1.fx -= fx; - node1.fy -= fy; - node2.fx += fx; - node2.fy += fy; - } + gravityForce = (distance == 0) ? 0 : (gravity / distance); + node.fx = dx * gravityForce; + node.fy = dy * gravityForce; + } + else { + node.fx = 0; + node.fy = 0; } } }; + + /** * this function calculates the effects of the springs in the case of unsmooth curves. * * @private */ - exports._calculateHierarchicalSpringForces = function () { + exports._calculateSpringForces = function () { var edgeLength, edge, edgeId; var dx, dy, fx, fy, springForce, distance; var edges = this.edges; - var nodes = this.calculationNodes; - var nodeIndices = this.calculationNodeIndices; - - - for (var i = 0; i < nodeIndices.length; i++) { - var node1 = nodes[nodeIndices[i]]; - node1.springFx = 0; - node1.springFy = 0; - } - - // forces caused by the edges, modelled as springs for (edgeId in edges) { if (edges.hasOwnProperty(edgeId)) { @@ -30260,87 +29874,50 @@ return /******/ (function(modules) { // webpackBootstrap fx = dx * springForce; fy = dy * springForce; - - - if (edge.to.level != edge.from.level) { - edge.to.springFx -= fx; - edge.to.springFy -= fy; - edge.from.springFx += fx; - edge.from.springFy += fy; - } - else { - var factor = 0.5; - edge.to.fx -= factor*fx; - edge.to.fy -= factor*fy; - edge.from.fx += factor*fx; - edge.from.fy += factor*fy; - } + edge.from.fx += fx; + edge.from.fy += fy; + edge.to.fx -= fx; + edge.to.fy -= fy; } } } } + }; - // normalize spring forces - var springForce = 1; - var springFx, springFy; - for (i = 0; i < nodeIndices.length; i++) { - var node = nodes[nodeIndices[i]]; - springFx = Math.min(springForce,Math.max(-springForce,node.springFx)); - springFy = Math.min(springForce,Math.max(-springForce,node.springFy)); - - node.fx += springFx; - node.fy += springFy; - } - - // retain energy balance - var totalFx = 0; - var totalFy = 0; - for (i = 0; i < nodeIndices.length; i++) { - var node = nodes[nodeIndices[i]]; - totalFx += node.fx; - totalFy += node.fy; - } - var correctionFx = totalFx / nodeIndices.length; - var correctionFy = totalFy / nodeIndices.length; - - for (i = 0; i < nodeIndices.length; i++) { - var node = nodes[nodeIndices[i]]; - node.fx -= correctionFx; - node.fy -= correctionFy; - } - }; -/***/ }, -/* 63 */ -/***/ function(module, exports, __webpack_require__) { /** - * 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. + * This function calculates the springforces on the nodes, accounting for the support nodes. * * @private */ - exports._calculateNodeForces = function() { - if (this.constants.physics.barnesHut.gravitationalConstant != 0) { - var node; - var nodes = this.calculationNodes; - var nodeIndices = this.calculationNodeIndices; - var nodeCount = nodeIndices.length; + exports._calculateSpringForcesWithSupport = function () { + var edgeLength, edge, edgeId, combinedClusterSize; + var edges = this.edges; - this._formBarnesHutTree(nodes,nodeIndices); + // forces caused by the edges, modelled as springs + for (edgeId in edges) { + if (edges.hasOwnProperty(edgeId)) { + edge = edges[edgeId]; + if (edge.connected) { + // only calculate forces if nodes are in the same sector + if (this.nodes.hasOwnProperty(edge.toId) && this.nodes.hasOwnProperty(edge.fromId)) { + if (edge.via != null) { + var node1 = edge.to; + var node2 = edge.via; + var node3 = edge.from; - var barnesHutTree = this.barnesHutTree; + edgeLength = edge.physics.springLength; - // 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 BarnesHut 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); + combinedClusterSize = node1.clusterSize + node3.clusterSize - 2; + + // this implies that the edges between big clusters are longer + edgeLength += combinedClusterSize * this.constants.clustering.edgeGrowth; + this._calculateSpringForce(node1, node2, 0.5 * edgeLength); + this._calculateSpringForce(node2, node3, 0.5 * edgeLength); + } + } } } } @@ -30348,376 +29925,455 @@ return /******/ (function(modules) { // webpackBootstrap /** - * 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. + * This is the code actually performing the calculation for the function above. It is split out to avoid repetition. * - * @param parentBranch - * @param node + * @param node1 + * @param node2 + * @param edgeLength * @private */ - exports._getForceContribution = function(parentBranch,node) { - // we get no force contribution from an empty region - if (parentBranch.childrenCount > 0) { - var dx,dy,distance; + exports._calculateSpringForce = function (node1, node2, edgeLength) { + var dx, dy, fx, fy, springForce, distance; + + dx = (node1.x - node2.x); + dy = (node1.y - node2.y); + distance = Math.sqrt(dx * dx + dy * dy); + + if (distance == 0) { + distance = 0.01; + } + + // the 1/distance is so the fx and fy can be calculated without sine or cosine. + springForce = this.constants.physics.springConstant * (edgeLength - distance) / distance; + + fx = dx * springForce; + fy = dy * springForce; + + node1.fx += fx; + node1.fy += fy; + node2.fx -= fx; + node2.fy -= fy; + }; + + + exports._cleanupPhysicsConfiguration = function() { + if (this.physicsConfiguration !== undefined) { + while (this.physicsConfiguration.hasChildNodes()) { + this.physicsConfiguration.removeChild(this.physicsConfiguration.firstChild); + } + + this.physicsConfiguration.parentNode.removeChild(this.physicsConfiguration); + this.physicsConfiguration = undefined; + } + } + + /** + * Load the HTML for the physics config and bind it + * @private + */ + exports._loadPhysicsConfiguration = function () { + if (this.physicsConfiguration === undefined) { + this.backupConstants = {}; + util.deepExtend(this.backupConstants,this.constants); + + var hierarchicalLayoutDirections = ["LR", "RL", "UD", "DU"]; + this.physicsConfiguration = document.createElement('div'); + this.physicsConfiguration.className = "PhysicsConfiguration"; + this.physicsConfiguration.innerHTML = '' + + '' + + '' + + '' + + '' + + '' + + '' + + '
Simulation Mode:
Barnes HutRepulsionHierarchical
' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '
Options:
' + this.containerElement.parentElement.insertBefore(this.physicsConfiguration, this.containerElement); + this.optionsDiv = document.createElement("div"); + this.optionsDiv.style.fontSize = "14px"; + this.optionsDiv.style.fontFamily = "verdana"; + this.containerElement.parentElement.insertBefore(this.optionsDiv, this.containerElement); + + var rangeElement; + rangeElement = document.getElementById('graph_BH_gc'); + rangeElement.onchange = showValueOfRange.bind(this, 'graph_BH_gc', -1, "physics_barnesHut_gravitationalConstant"); + rangeElement = document.getElementById('graph_BH_cg'); + rangeElement.onchange = showValueOfRange.bind(this, 'graph_BH_cg', 1, "physics_centralGravity"); + rangeElement = document.getElementById('graph_BH_sc'); + rangeElement.onchange = showValueOfRange.bind(this, 'graph_BH_sc', 1, "physics_springConstant"); + rangeElement = document.getElementById('graph_BH_sl'); + rangeElement.onchange = showValueOfRange.bind(this, 'graph_BH_sl', 1, "physics_springLength"); + rangeElement = document.getElementById('graph_BH_damp'); + rangeElement.onchange = showValueOfRange.bind(this, 'graph_BH_damp', 1, "physics_damping"); + + rangeElement = document.getElementById('graph_R_nd'); + rangeElement.onchange = showValueOfRange.bind(this, 'graph_R_nd', 1, "physics_repulsion_nodeDistance"); + rangeElement = document.getElementById('graph_R_cg'); + rangeElement.onchange = showValueOfRange.bind(this, 'graph_R_cg', 1, "physics_centralGravity"); + rangeElement = document.getElementById('graph_R_sc'); + rangeElement.onchange = showValueOfRange.bind(this, 'graph_R_sc', 1, "physics_springConstant"); + rangeElement = document.getElementById('graph_R_sl'); + rangeElement.onchange = showValueOfRange.bind(this, 'graph_R_sl', 1, "physics_springLength"); + rangeElement = document.getElementById('graph_R_damp'); + rangeElement.onchange = showValueOfRange.bind(this, 'graph_R_damp', 1, "physics_damping"); + + rangeElement = document.getElementById('graph_H_nd'); + rangeElement.onchange = showValueOfRange.bind(this, 'graph_H_nd', 1, "physics_hierarchicalRepulsion_nodeDistance"); + rangeElement = document.getElementById('graph_H_cg'); + rangeElement.onchange = showValueOfRange.bind(this, 'graph_H_cg', 1, "physics_centralGravity"); + rangeElement = document.getElementById('graph_H_sc'); + rangeElement.onchange = showValueOfRange.bind(this, 'graph_H_sc', 1, "physics_springConstant"); + rangeElement = document.getElementById('graph_H_sl'); + rangeElement.onchange = showValueOfRange.bind(this, 'graph_H_sl', 1, "physics_springLength"); + rangeElement = document.getElementById('graph_H_damp'); + rangeElement.onchange = showValueOfRange.bind(this, 'graph_H_damp', 1, "physics_damping"); + rangeElement = document.getElementById('graph_H_direction'); + rangeElement.onchange = showValueOfRange.bind(this, 'graph_H_direction', hierarchicalLayoutDirections, "hierarchicalLayout_direction"); + rangeElement = document.getElementById('graph_H_levsep'); + rangeElement.onchange = showValueOfRange.bind(this, 'graph_H_levsep', 1, "hierarchicalLayout_levelSeparation"); + rangeElement = document.getElementById('graph_H_nspac'); + rangeElement.onchange = showValueOfRange.bind(this, 'graph_H_nspac', 1, "hierarchicalLayout_nodeSpacing"); + + var radioButton1 = document.getElementById("graph_physicsMethod1"); + var radioButton2 = document.getElementById("graph_physicsMethod2"); + var radioButton3 = document.getElementById("graph_physicsMethod3"); + radioButton2.checked = true; + if (this.constants.physics.barnesHut.enabled) { + radioButton1.checked = true; + } + if (this.constants.hierarchicalLayout.enabled) { + radioButton3.checked = true; + } - // 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); + var graph_toggleSmooth = document.getElementById("graph_toggleSmooth"); + var graph_repositionNodes = document.getElementById("graph_repositionNodes"); + var graph_generateOptions = document.getElementById("graph_generateOptions"); - // BarnesHut condition - // original condition : s/d < thetaInverted = passed === d/s > 1/theta = passed - // calcSize = 1/s --> d * 1/s > 1/theta = passed - if (distance * parentBranch.calcSize > this.constants.physics.barnesHut.thetaInverted) { - // duplicate code to reduce function calls to speed up program - if (distance == 0) { - distance = 0.1*Math.random(); - dx = distance; - } - var gravityForce = this.constants.physics.barnesHut.gravitationalConstant * parentBranch.mass * node.options.mass / (distance * distance * distance); - var fx = dx * gravityForce; - var fy = dy * gravityForce; - node.fx += fx; - node.fy += fy; + graph_toggleSmooth.onclick = graphToggleSmoothCurves.bind(this); + graph_repositionNodes.onclick = graphRepositionNodes.bind(this); + graph_generateOptions.onclick = graphGenerateOptions.bind(this); + if (this.constants.smoothCurves == true && this.constants.dynamicSmoothCurves == false) { + graph_toggleSmooth.style.background = "#A4FF56"; } 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 - // duplicate code to reduce function calls to speed up program - if (distance == 0) { - distance = 0.5*Math.random(); - dx = distance; - } - var gravityForce = this.constants.physics.barnesHut.gravitationalConstant * parentBranch.mass * node.options.mass / (distance * distance * distance); - var fx = dx * gravityForce; - var fy = dy * gravityForce; - node.fx += fx; - node.fy += fy; - } - } + graph_toggleSmooth.style.background = "#FF8532"; } + + + switchConfigurations.apply(this); + + radioButton1.onchange = switchConfigurations.bind(this); + radioButton2.onchange = switchConfigurations.bind(this); + radioButton3.onchange = switchConfigurations.bind(this); } }; /** - * This function constructs the barnesHut tree recursively. It creates the root, splits it and starts placing the nodes. + * This overwrites the this.constants. * - * @param nodes - * @param nodeIndices + * @param constantsVariableName + * @param value * @private */ - exports._formBarnesHutTree = function(nodes,nodeIndices) { - var node; - var nodeCount = nodeIndices.length; - - var minX = Number.MAX_VALUE, - minY = Number.MAX_VALUE, - maxX =-Number.MAX_VALUE, - maxY =-Number.MAX_VALUE; - - // get the range of the nodes - for (var i = 0; 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; } - } + exports._overWriteGraphConstants = function (constantsVariableName, value) { + var nameArray = constantsVariableName.split("_"); + if (nameArray.length == 1) { + this.constants[nameArray[0]] = value; } - // make the range a square - var sizeDiff = Math.abs(maxX - minX) - Math.abs(maxY - minY); // difference between X and Y - if (sizeDiff > 0) {minY -= 0.5 * sizeDiff; maxY += 0.5 * sizeDiff;} // xSize > ySize - else {minX += 0.5 * sizeDiff; maxX -= 0.5 * sizeDiff;} // xSize < ySize - - - var minimumTreeSize = 1e-5; - var rootSize = Math.max(minimumTreeSize,Math.abs(maxX - minX)); - var halfRootSize = 0.5 * rootSize; - var centerX = 0.5 * (minX + maxX), centerY = 0.5 * (minY + maxY); - - // construct the barnesHutTree - var barnesHutTree = { - root:{ - centerOfMass: {x:0, y:0}, - mass:0, - range: { - minX: centerX-halfRootSize,maxX:centerX+halfRootSize, - minY: centerY-halfRootSize,maxY:centerY+halfRootSize - }, - size: rootSize, - calcSize: 1 / rootSize, - children: { data:null}, - maxWidth: 0, - level: 0, - childrenCount: 4 - } - }; - this._splitBranch(barnesHutTree.root); - - // place the nodes one by one recursively - for (i = 0; i < nodeCount; i++) { - node = nodes[nodeIndices[i]]; - if (node.options.mass > 0) { - this._placeInTree(barnesHutTree.root,node); - } + else if (nameArray.length == 2) { + this.constants[nameArray[0]][nameArray[1]] = value; + } + else if (nameArray.length == 3) { + this.constants[nameArray[0]][nameArray[1]][nameArray[2]] = value; } - - // make global - this.barnesHutTree = barnesHutTree }; /** - * this updates the mass of a branch. this is increased by adding a node. - * - * @param parentBranch - * @param node - * @private + * this function is bound to the toggle smooth curves button. That is also why it is not in the prototype. */ - exports._updateBranchMass = function(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; - - }; + function graphToggleSmoothCurves () { + this.constants.smoothCurves.enabled = !this.constants.smoothCurves.enabled; + var graph_toggleSmooth = document.getElementById("graph_toggleSmooth"); + if (this.constants.smoothCurves.enabled == true) {graph_toggleSmooth.style.background = "#A4FF56";} + else {graph_toggleSmooth.style.background = "#FF8532";} + this._configureSmoothCurves(false); + } /** - * determine in which branch the node will be placed. + * this function is used to scramble the nodes * - * @param parentBranch - * @param node - * @param skipMassUpdate - * @private */ - exports._placeInTree = function(parentBranch,node,skipMassUpdate) { - if (skipMassUpdate != true || skipMassUpdate === undefined) { - // update the mass of the branch. - this._updateBranchMass(parentBranch,node); + function graphRepositionNodes () { + for (var nodeId in this.calculationNodes) { + if (this.calculationNodes.hasOwnProperty(nodeId)) { + this.calculationNodes[nodeId].vx = 0; this.calculationNodes[nodeId].vy = 0; + this.calculationNodes[nodeId].fx = 0; this.calculationNodes[nodeId].fy = 0; + } } + if (this.constants.hierarchicalLayout.enabled == true) { + this._setupHierarchicalLayout(); + showValueOfRange.call(this, 'graph_H_nd', 1, "physics_hierarchicalRepulsion_nodeDistance"); + showValueOfRange.call(this, 'graph_H_cg', 1, "physics_centralGravity"); + showValueOfRange.call(this, 'graph_H_sc', 1, "physics_springConstant"); + showValueOfRange.call(this, 'graph_H_sl', 1, "physics_springLength"); + showValueOfRange.call(this, 'graph_H_damp', 1, "physics_damping"); + } + else { + this.repositionNodes(); + } + this.moving = true; + this.start(); + } - 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"); + /** + * this is used to generate an options file from the playing with physics system. + */ + function graphGenerateOptions () { + var options = "No options are required, default values used."; + var optionsSpecific = []; + var radioButton1 = document.getElementById("graph_physicsMethod1"); + var radioButton2 = document.getElementById("graph_physicsMethod2"); + if (radioButton1.checked == true) { + if (this.constants.physics.barnesHut.gravitationalConstant != this.backupConstants.physics.barnesHut.gravitationalConstant) {optionsSpecific.push("gravitationalConstant: " + this.constants.physics.barnesHut.gravitationalConstant);} + if (this.constants.physics.centralGravity != this.backupConstants.physics.barnesHut.centralGravity) {optionsSpecific.push("centralGravity: " + this.constants.physics.centralGravity);} + if (this.constants.physics.springLength != this.backupConstants.physics.barnesHut.springLength) {optionsSpecific.push("springLength: " + this.constants.physics.springLength);} + if (this.constants.physics.springConstant != this.backupConstants.physics.barnesHut.springConstant) {optionsSpecific.push("springConstant: " + this.constants.physics.springConstant);} + if (this.constants.physics.damping != this.backupConstants.physics.barnesHut.damping) {optionsSpecific.push("damping: " + this.constants.physics.damping);} + if (optionsSpecific.length != 0) { + options = "var options = {"; + options += "physics: {barnesHut: {"; + for (var i = 0; i < optionsSpecific.length; i++) { + options += optionsSpecific[i]; + if (i < optionsSpecific.length - 1) { + options += ", " + } + } + options += '}}' } - else { // in SW - this._placeInRegion(parentBranch,node,"SW"); + if (this.constants.smoothCurves.enabled != this.backupConstants.smoothCurves.enabled) { + if (optionsSpecific.length == 0) {options = "var options = {";} + else {options += ", "} + options += "smoothCurves: " + this.constants.smoothCurves.enabled; + } + if (options != "No options are required, default values used.") { + options += '};' } } - else { // in NE or SE - if (parentBranch.children.NW.range.maxY > node.y) { // in NE - this._placeInRegion(parentBranch,node,"NE"); + else if (radioButton2.checked == true) { + options = "var options = {"; + options += "physics: {barnesHut: {enabled: false}"; + if (this.constants.physics.repulsion.nodeDistance != this.backupConstants.physics.repulsion.nodeDistance) {optionsSpecific.push("nodeDistance: " + this.constants.physics.repulsion.nodeDistance);} + if (this.constants.physics.centralGravity != this.backupConstants.physics.repulsion.centralGravity) {optionsSpecific.push("centralGravity: " + this.constants.physics.centralGravity);} + if (this.constants.physics.springLength != this.backupConstants.physics.repulsion.springLength) {optionsSpecific.push("springLength: " + this.constants.physics.springLength);} + if (this.constants.physics.springConstant != this.backupConstants.physics.repulsion.springConstant) {optionsSpecific.push("springConstant: " + this.constants.physics.springConstant);} + if (this.constants.physics.damping != this.backupConstants.physics.repulsion.damping) {optionsSpecific.push("damping: " + this.constants.physics.damping);} + if (optionsSpecific.length != 0) { + options += ", repulsion: {"; + for (var i = 0; i < optionsSpecific.length; i++) { + options += optionsSpecific[i]; + if (i < optionsSpecific.length - 1) { + options += ", " + } + } + options += '}}' } - else { // in SE - this._placeInRegion(parentBranch,node,"SE"); + if (optionsSpecific.length == 0) {options += "}"} + if (this.constants.smoothCurves != this.backupConstants.smoothCurves) { + options += ", smoothCurves: " + this.constants.smoothCurves; } + options += '};' } - }; - - - /** - * actually place the node in a region (or branch) - * - * @param parentBranch - * @param node - * @param region - * @private - */ - exports._placeInRegion = function(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 { + options = "var options = {"; + if (this.constants.physics.hierarchicalRepulsion.nodeDistance != this.backupConstants.physics.hierarchicalRepulsion.nodeDistance) {optionsSpecific.push("nodeDistance: " + this.constants.physics.hierarchicalRepulsion.nodeDistance);} + if (this.constants.physics.centralGravity != this.backupConstants.physics.hierarchicalRepulsion.centralGravity) {optionsSpecific.push("centralGravity: " + this.constants.physics.centralGravity);} + if (this.constants.physics.springLength != this.backupConstants.physics.hierarchicalRepulsion.springLength) {optionsSpecific.push("springLength: " + this.constants.physics.springLength);} + if (this.constants.physics.springConstant != this.backupConstants.physics.hierarchicalRepulsion.springConstant) {optionsSpecific.push("springConstant: " + this.constants.physics.springConstant);} + if (this.constants.physics.damping != this.backupConstants.physics.hierarchicalRepulsion.damping) {optionsSpecific.push("damping: " + this.constants.physics.damping);} + if (optionsSpecific.length != 0) { + options += "physics: {hierarchicalRepulsion: {"; + for (var i = 0; i < optionsSpecific.length; i++) { + options += optionsSpecific[i]; + if (i < optionsSpecific.length - 1) { + options += ", "; + } } - else { - this._splitBranch(parentBranch.children[region]); - this._placeInTree(parentBranch.children[region],node); + options += '}},'; + } + options += 'hierarchicalLayout: {'; + optionsSpecific = []; + if (this.constants.hierarchicalLayout.direction != this.backupConstants.hierarchicalLayout.direction) {optionsSpecific.push("direction: " + this.constants.hierarchicalLayout.direction);} + if (Math.abs(this.constants.hierarchicalLayout.levelSeparation) != this.backupConstants.hierarchicalLayout.levelSeparation) {optionsSpecific.push("levelSeparation: " + this.constants.hierarchicalLayout.levelSeparation);} + if (this.constants.hierarchicalLayout.nodeSpacing != this.backupConstants.hierarchicalLayout.nodeSpacing) {optionsSpecific.push("nodeSpacing: " + this.constants.hierarchicalLayout.nodeSpacing);} + if (optionsSpecific.length != 0) { + for (var i = 0; i < optionsSpecific.length; i++) { + options += optionsSpecific[i]; + if (i < optionsSpecific.length - 1) { + options += ", " + } } - break; - case 4: // place in branch - this._placeInTree(parentBranch.children[region],node); - break; + options += '}' + } + else { + options += "enabled:true}"; + } + options += '};' } - }; + this.optionsDiv.innerHTML = options; + } + /** - * 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. + * this is used to switch between barnesHut, repulsion and hierarchical. * - * @param parentBranch - * @private */ - exports._splitBranch = function(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; + function switchConfigurations () { + var ids = ["graph_BH_table", "graph_R_table", "graph_H_table"]; + var radioButton = document.querySelector('input[name="graph_physicsMethod"]:checked').value; + var tableId = "graph_" + radioButton + "_table"; + var table = document.getElementById(tableId); + table.style.display = "block"; + for (var i = 0; i < ids.length; i++) { + if (ids[i] != tableId) { + table = document.getElementById(ids[i]); + table.style.display = "none"; + } } - parentBranch.childrenCount = 4; - parentBranch.children.data = null; - this._insertRegion(parentBranch,"NW"); - this._insertRegion(parentBranch,"NE"); - this._insertRegion(parentBranch,"SW"); - this._insertRegion(parentBranch,"SE"); - - if (containedNode != null) { - this._placeInTree(parentBranch,containedNode); + this._restoreNodes(); + if (radioButton == "R") { + this.constants.hierarchicalLayout.enabled = false; + this.constants.physics.hierarchicalRepulsion.enabled = false; + this.constants.physics.barnesHut.enabled = false; } - }; - - - /** - * 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 - */ - exports._insertRegion = function(parentBranch, region) { - var minX,maxX,minY,maxY; - 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; + else if (radioButton == "H") { + if (this.constants.hierarchicalLayout.enabled == false) { + this.constants.hierarchicalLayout.enabled = true; + this.constants.physics.hierarchicalRepulsion.enabled = true; + this.constants.physics.barnesHut.enabled = false; + this.constants.smoothCurves.enabled = false; + this._setupHierarchicalLayout(); + } } - - - 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 - }; - }; + else { + this.constants.hierarchicalLayout.enabled = false; + this.constants.physics.hierarchicalRepulsion.enabled = false; + this.constants.physics.barnesHut.enabled = true; + } + this._loadSelectedForceSolver(); + var graph_toggleSmooth = document.getElementById("graph_toggleSmooth"); + if (this.constants.smoothCurves.enabled == true) {graph_toggleSmooth.style.background = "#A4FF56";} + else {graph_toggleSmooth.style.background = "#FF8532";} + this.moving = true; + this.start(); + } /** - * This function is for debugging purposed, it draws the tree. + * this generates the ranges depending on the iniital values. * - * @param ctx - * @param color - * @private + * @param id + * @param map + * @param constantsVariableName */ - exports._drawTree = function(ctx,color) { - if (this.barnesHutTree !== undefined) { - - ctx.lineWidth = 1; + function showValueOfRange (id,map,constantsVariableName) { + var valueId = id + "_value"; + var rangeValue = document.getElementById(id).value; - this._drawBranch(this.barnesHutTree.root,ctx,color); + if (Array.isArray(map)) { + document.getElementById(valueId).value = map[parseInt(rangeValue)]; + this._overWriteGraphConstants(constantsVariableName,map[parseInt(rangeValue)]); } - }; - - - /** - * This function is for debugging purposes. It draws the branches recursively. - * - * @param branch - * @param ctx - * @param color - * @private - */ - exports._drawBranch = function(branch,ctx,color) { - if (color === undefined) { - color = "#FF0000"; + else { + document.getElementById(valueId).value = parseInt(map) * parseFloat(rangeValue); + this._overWriteGraphConstants(constantsVariableName, parseInt(map) * parseFloat(rangeValue)); } - 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); + if (constantsVariableName == "hierarchicalLayout_direction" || + constantsVariableName == "hierarchicalLayout_levelSeparation" || + constantsVariableName == "hierarchicalLayout_nodeSpacing") { + this._setupHierarchicalLayout(); } - 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(); + this.moving = true; + this.start(); + } - 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(); - } - */ - }; /***/ }, -/* 64 */ +/* 61 */ /***/ function(module, exports, __webpack_require__) { /** @@ -31882,11 +31538,11 @@ return /******/ (function(modules) { // webpackBootstrap /***/ }, -/* 65 */ +/* 62 */ /***/ function(module, exports, __webpack_require__) { var util = __webpack_require__(1); - var Node = __webpack_require__(56); + var Node = __webpack_require__(40); /** * Creation of the SectorMixin var. @@ -32441,10 +32097,10 @@ return /******/ (function(modules) { // webpackBootstrap /***/ }, -/* 66 */ +/* 63 */ /***/ function(module, exports, __webpack_require__) { - var Node = __webpack_require__(56); + var Node = __webpack_require__(40); /** * This function can be called from the _doInAllSectors function @@ -33155,12 +32811,12 @@ return /******/ (function(modules) { // webpackBootstrap /***/ }, -/* 67 */ +/* 64 */ /***/ function(module, exports, __webpack_require__) { var util = __webpack_require__(1); - var Node = __webpack_require__(56); - var Edge = __webpack_require__(57); + var Node = __webpack_require__(40); + var Edge = __webpack_require__(37); /** * clears the toolbar div element of children @@ -33854,11 +33510,11 @@ return /******/ (function(modules) { // webpackBootstrap /***/ }, -/* 68 */ +/* 65 */ /***/ function(module, exports, __webpack_require__) { var util = __webpack_require__(1); - var Hammer = __webpack_require__(19); + var Hammer = __webpack_require__(45); exports._cleanNavigation = function() { // clean hammer bindings @@ -34003,33 +33659,521 @@ return /******/ (function(modules) { // webpackBootstrap }; - /** - * Stop zooming and unhighlight the zoom controls - * @private - */ - exports._stopZoom = function(event) { - this.zoomIncrement = 0; - event && event.preventDefault(); - }; + /** + * Stop zooming and unhighlight the zoom controls + * @private + */ + exports._stopZoom = function(event) { + this.zoomIncrement = 0; + event && event.preventDefault(); + }; + + + /** + * Stop moving in the Y direction and unHighlight the up and down + * @private + */ + exports._yStopMoving = function(event) { + this.yIncrement = 0; + event && event.preventDefault(); + }; + + + /** + * Stop moving in the X direction and unHighlight left and right. + * @private + */ + exports._xStopMoving = function(event) { + this.xIncrement = 0; + event && event.preventDefault(); + }; + + +/***/ }, +/* 66 */ +/***/ function(module, exports, __webpack_require__) { + + exports._resetLevels = function() { + for (var nodeId in this.nodes) { + if (this.nodes.hasOwnProperty(nodeId)) { + var node = this.nodes[nodeId]; + if (node.preassignedLevel == false) { + node.level = -1; + node.hierarchyEnumerated = false; + } + } + } + }; + + /** + * This is the main function to layout the nodes in a hierarchical way. + * It checks if the node details are supplied correctly + * + * @private + */ + exports._setupHierarchicalLayout = function() { + if (this.constants.hierarchicalLayout.enabled == true && this.nodeIndices.length > 0) { + // get the size of the largest hubs and check if the user has defined a level for a node. + var hubsize = 0; + var node, nodeId; + var definedLevel = false; + var undefinedLevel = false; + + for (nodeId in this.nodes) { + if (this.nodes.hasOwnProperty(nodeId)) { + node = this.nodes[nodeId]; + if (node.level != -1) { + definedLevel = true; + } + else { + undefinedLevel = true; + } + if (hubsize < node.edges.length) { + hubsize = node.edges.length; + } + } + } + + // if the user defined some levels but not all, alert and run without hierarchical layout + if (undefinedLevel == true && definedLevel == true) { + throw new Error("To use the hierarchical layout, nodes require either no predefined levels or levels have to be defined for all nodes."); + this.zoomExtent(undefined,true,this.constants.clustering.enabled); + if (!this.constants.clustering.enabled) { + this.start(); + } + } + else { + // setup the system to use hierarchical method. + this._changeConstants(); + + // define levels if undefined by the users. Based on hubsize + if (undefinedLevel == true) { + if (this.constants.hierarchicalLayout.layout == "hubsize") { + this._determineLevels(hubsize); + } + else { + this._determineLevelsDirected(false); + } + + } + // check the distribution of the nodes per level. + var distribution = this._getDistribution(); + + // place the nodes on the canvas. This also stablilizes the system. + this._placeNodesByHierarchy(distribution); + + // start the simulation. + this.start(); + } + } + }; + + + /** + * This function places the nodes on the canvas based on the hierarchial distribution. + * + * @param {Object} distribution | obtained by the function this._getDistribution() + * @private + */ + exports._placeNodesByHierarchy = function(distribution) { + var nodeId, node; + + // start placing all the level 0 nodes first. Then recursively position their branches. + for (var level in distribution) { + if (distribution.hasOwnProperty(level)) { + + for (nodeId in distribution[level].nodes) { + if (distribution[level].nodes.hasOwnProperty(nodeId)) { + node = distribution[level].nodes[nodeId]; + if (this.constants.hierarchicalLayout.direction == "UD" || this.constants.hierarchicalLayout.direction == "DU") { + if (node.xFixed) { + node.x = distribution[level].minPos; + node.xFixed = false; + + distribution[level].minPos += distribution[level].nodeSpacing; + } + } + else { + if (node.yFixed) { + node.y = distribution[level].minPos; + node.yFixed = false; + + distribution[level].minPos += distribution[level].nodeSpacing; + } + } + this._placeBranchNodes(node.edges,node.id,distribution,node.level); + } + } + } + } + + // stabilize the system after positioning. This function calls zoomExtent. + this._stabilize(); + }; + + + /** + * This function get the distribution of levels based on hubsize + * + * @returns {Object} + * @private + */ + exports._getDistribution = function() { + var distribution = {}; + var nodeId, node, level; + + // we fix Y because the hierarchy is vertical, we fix X so we do not give a node an x position for a second time. + // the fix of X is removed after the x value has been set. + for (nodeId in this.nodes) { + if (this.nodes.hasOwnProperty(nodeId)) { + node = this.nodes[nodeId]; + node.xFixed = true; + node.yFixed = true; + if (this.constants.hierarchicalLayout.direction == "UD" || this.constants.hierarchicalLayout.direction == "DU") { + node.y = this.constants.hierarchicalLayout.levelSeparation*node.level; + } + else { + node.x = this.constants.hierarchicalLayout.levelSeparation*node.level; + } + if (distribution[node.level] === undefined) { + distribution[node.level] = {amount: 0, nodes: {}, minPos:0, nodeSpacing:0}; + } + distribution[node.level].amount += 1; + distribution[node.level].nodes[nodeId] = node; + } + } + + // determine the largest amount of nodes of all levels + var maxCount = 0; + for (level in distribution) { + if (distribution.hasOwnProperty(level)) { + if (maxCount < distribution[level].amount) { + maxCount = distribution[level].amount; + } + } + } + + // set the initial position and spacing of each nodes accordingly + for (level in distribution) { + if (distribution.hasOwnProperty(level)) { + distribution[level].nodeSpacing = (maxCount + 1) * this.constants.hierarchicalLayout.nodeSpacing; + distribution[level].nodeSpacing /= (distribution[level].amount + 1); + distribution[level].minPos = distribution[level].nodeSpacing - (0.5 * (distribution[level].amount + 1) * distribution[level].nodeSpacing); + } + } + + return distribution; + }; + + + /** + * this function allocates nodes in levels based on the recursive branching from the largest hubs. + * + * @param hubsize + * @private + */ + exports._determineLevels = function(hubsize) { + var nodeId, node; + + // determine hubs + for (nodeId in this.nodes) { + if (this.nodes.hasOwnProperty(nodeId)) { + node = this.nodes[nodeId]; + if (node.edges.length == hubsize) { + node.level = 0; + } + } + } + + // branch from hubs + for (nodeId in this.nodes) { + if (this.nodes.hasOwnProperty(nodeId)) { + node = this.nodes[nodeId]; + if (node.level == 0) { + this._setLevel(1,node.edges,node.id); + } + } + } + }; + + + + /** + * this function allocates nodes in levels based on the direction of the edges + * + * @param hubsize + * @private + */ + exports._determineLevelsDirected = function() { + var nodeId, node, firstNode; + var minLevel = 10000; + + // set first node to source + firstNode = this.nodes[this.nodeIndices[0]]; + firstNode.level = minLevel; + this._setLevelDirected(minLevel,firstNode.edges,firstNode.id); + + // get the minimum level + for (nodeId in this.nodes) { + if (this.nodes.hasOwnProperty(nodeId)) { + node = this.nodes[nodeId]; + minLevel = node.level < minLevel ? node.level : minLevel; + } + } + + // subtract the minimum from the set so we have a range starting from 0 + for (nodeId in this.nodes) { + if (this.nodes.hasOwnProperty(nodeId)) { + node = this.nodes[nodeId]; + node.level -= minLevel; + } + } + }; + + + /** + * Since hierarchical layout does not support: + * - smooth curves (based on the physics), + * - clustering (based on dynamic node counts) + * + * We disable both features so there will be no problems. + * + * @private + */ + exports._changeConstants = function() { + this.constants.clustering.enabled = false; + this.constants.physics.barnesHut.enabled = false; + this.constants.physics.hierarchicalRepulsion.enabled = true; + this._loadSelectedForceSolver(); + if (this.constants.smoothCurves.enabled == true) { + this.constants.smoothCurves.dynamic = false; + } + this._configureSmoothCurves(); + + var config = this.constants.hierarchicalLayout; + config.levelSeparation = Math.abs(config.levelSeparation); + if (config.direction == "RL" || config.direction == "DU") { + config.levelSeparation *= -1; + } + + if (config.direction == "RL" || config.direction == "LR") { + if (this.constants.smoothCurves.enabled == true) { + this.constants.smoothCurves.type = "vertical"; + } + } + else { + if (this.constants.smoothCurves.enabled == true) { + this.constants.smoothCurves.type = "horizontal"; + } + } + }; + + + /** + * This is a recursively called function to enumerate the branches from the largest hubs and place the nodes + * on a X position that ensures there will be no overlap. + * + * @param edges + * @param parentId + * @param distribution + * @param parentLevel + * @private + */ + exports._placeBranchNodes = function(edges, parentId, distribution, parentLevel) { + for (var i = 0; i < edges.length; i++) { + var childNode = null; + if (edges[i].toId == parentId) { + childNode = edges[i].from; + } + else { + childNode = edges[i].to; + } + + // if a node is conneceted to another node on the same level (or higher (means lower level))!, this is not handled here. + var nodeMoved = false; + if (this.constants.hierarchicalLayout.direction == "UD" || this.constants.hierarchicalLayout.direction == "DU") { + if (childNode.xFixed && childNode.level > parentLevel) { + childNode.xFixed = false; + childNode.x = distribution[childNode.level].minPos; + nodeMoved = true; + } + } + else { + if (childNode.yFixed && childNode.level > parentLevel) { + childNode.yFixed = false; + childNode.y = distribution[childNode.level].minPos; + nodeMoved = true; + } + } + + if (nodeMoved == true) { + distribution[childNode.level].minPos += distribution[childNode.level].nodeSpacing; + if (childNode.edges.length > 1) { + this._placeBranchNodes(childNode.edges,childNode.id,distribution,childNode.level); + } + } + } + }; + + + /** + * this function is called recursively to enumerate the barnches of the largest hubs and give each node a level. + * + * @param level + * @param edges + * @param parentId + * @private + */ + exports._setLevel = function(level, edges, parentId) { + for (var i = 0; i < edges.length; i++) { + var childNode = null; + if (edges[i].toId == parentId) { + childNode = edges[i].from; + } + else { + childNode = edges[i].to; + } + if (childNode.level == -1 || childNode.level > level) { + childNode.level = level; + if (childNode.edges.length > 1) { + this._setLevel(level+1, childNode.edges, childNode.id); + } + } + } + }; + + + /** + * this function is called recursively to enumerate the branched of the first node and give each node a level based on edge direction + * + * @param level + * @param edges + * @param parentId + * @private + */ + exports._setLevelDirected = function(level, edges, parentId) { + this.nodes[parentId].hierarchyEnumerated = true; + var childNode, direction; + for (var i = 0; i < edges.length; i++) { + direction = 1; + if (edges[i].toId == parentId) { + childNode = edges[i].from; + direction = -1; + } + else { + childNode = edges[i].to; + } + if (childNode.level == -1) { + childNode.level = level + direction; + } + } + + for (var i = 0; i < edges.length; i++) { + if (edges[i].toId == parentId) {childNode = edges[i].from;} + else {childNode = edges[i].to;} + + if (childNode.edges.length > 1 && childNode.hierarchyEnumerated === false) { + this._setLevelDirected(childNode.level, childNode.edges, childNode.id); + } + } + }; + + + /** + * Unfix nodes + * + * @private + */ + exports._restoreNodes = function() { + for (var nodeId in this.nodes) { + if (this.nodes.hasOwnProperty(nodeId)) { + this.nodes[nodeId].xFixed = false; + this.nodes[nodeId].yFixed = false; + } + } + }; + + +/***/ }, +/* 67 */ +/***/ function(module, exports, __webpack_require__) { + + function webpackContext(req) { + throw new Error("Cannot find module '" + req + "'."); + } + webpackContext.keys = function() { return []; }; + webpackContext.resolve = webpackContext; + module.exports = webpackContext; + webpackContext.id = 67; + + +/***/ }, +/* 68 */ +/***/ function(module, exports, __webpack_require__) { + + /** + * Calculate the forces the nodes apply on each other based on a repulsion field. + * This field is linearly approximated. + * + * @private + */ + exports._calculateNodeForces = function () { + var dx, dy, angle, distance, fx, fy, combinedClusterSize, + repulsingForce, node1, node2, i, j; + + var nodes = this.calculationNodes; + var nodeIndices = this.calculationNodeIndices; + + // approximation constants + var a_base = -2 / 3; + var b = 4 / 3; + + // repulsing forces between nodes + var nodeDistance = this.constants.physics.repulsion.nodeDistance; + var minimumDistance = 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]]; + combinedClusterSize = node1.clusterSize + node2.clusterSize - 2; + + dx = node2.x - node1.x; + dy = node2.y - node1.y; + distance = Math.sqrt(dx * dx + dy * dy); + + // same condition as BarnesHut, making sure nodes are never 100% overlapping. + if (distance == 0) { + distance = 0.1*Math.random(); + dx = distance; + } + minimumDistance = (combinedClusterSize == 0) ? nodeDistance : (nodeDistance * (1 + combinedClusterSize * this.constants.clustering.distanceAmplification)); + var a = a_base / minimumDistance; + if (distance < 2 * minimumDistance) { + if (distance < 0.5 * minimumDistance) { + repulsingForce = 1.0; + } + else { + repulsingForce = a * distance + b; // linear approx of 1 / (1 + Math.exp((distance / minimumDistance - 1) * steepness)) + } - /** - * Stop moving in the Y direction and unHighlight the up and down - * @private - */ - exports._yStopMoving = function(event) { - this.yIncrement = 0; - event && event.preventDefault(); - }; + // amplify the repulsion for clusters. + repulsingForce *= (combinedClusterSize == 0) ? 1 : 1 + combinedClusterSize * this.constants.clustering.forceAmplification; + repulsingForce = repulsingForce / Math.max(distance,0.01*minimumDistance); + fx = dx * repulsingForce; + fy = dy * repulsingForce; + node1.fx -= fx; + node1.fy -= fy; + node2.fx += fx; + node2.fy += fy; - /** - * Stop moving in the X direction and unHighlight left and right. - * @private - */ - exports._xStopMoving = function(event) { - this.xIncrement = 0; - event && event.preventDefault(); + } + } + } }; @@ -34037,676 +34181,579 @@ return /******/ (function(modules) { // webpackBootstrap /* 69 */ /***/ function(module, exports, __webpack_require__) { - exports._resetLevels = function() { - for (var nodeId in this.nodes) { - if (this.nodes.hasOwnProperty(nodeId)) { - var node = this.nodes[nodeId]; - if (node.preassignedLevel == false) { - node.level = -1; - node.hierarchyEnumerated = false; - } - } - } - }; - /** - * This is the main function to layout the nodes in a hierarchical way. - * It checks if the node details are supplied correctly + * Calculate the forces the nodes apply on eachother based on a repulsion field. + * This field is linearly approximated. * * @private */ - exports._setupHierarchicalLayout = function() { - if (this.constants.hierarchicalLayout.enabled == true && this.nodeIndices.length > 0) { - // get the size of the largest hubs and check if the user has defined a level for a node. - var hubsize = 0; - var node, nodeId; - var definedLevel = false; - var undefinedLevel = false; + exports._calculateNodeForces = function () { + var dx, dy, distance, fx, fy, + repulsingForce, node1, node2, i, j; - for (nodeId in this.nodes) { - if (this.nodes.hasOwnProperty(nodeId)) { - node = this.nodes[nodeId]; - if (node.level != -1) { - definedLevel = true; - } - else { - undefinedLevel = true; - } - if (hubsize < node.edges.length) { - hubsize = node.edges.length; - } - } - } + var nodes = this.calculationNodes; + var nodeIndices = this.calculationNodeIndices; - // if the user defined some levels but not all, alert and run without hierarchical layout - if (undefinedLevel == true && definedLevel == true) { - throw new Error("To use the hierarchical layout, nodes require either no predefined levels or levels have to be defined for all nodes."); - this.zoomExtent(undefined,true,this.constants.clustering.enabled); - if (!this.constants.clustering.enabled) { - this.start(); - } - } - else { - // setup the system to use hierarchical method. - this._changeConstants(); + // repulsing forces between nodes + var nodeDistance = this.constants.physics.hierarchicalRepulsion.nodeDistance; - // define levels if undefined by the users. Based on hubsize - if (undefinedLevel == true) { - if (this.constants.hierarchicalLayout.layout == "hubsize") { - this._determineLevels(hubsize); + // 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 { - this._determineLevelsDirected(false); + repulsingForce = 0; } + // normalize force with + if (distance == 0) { + distance = 0.01; + } + else { + repulsingForce = repulsingForce / distance; + } + fx = dx * repulsingForce; + fy = dy * repulsingForce; + node1.fx -= fx; + node1.fy -= fy; + node2.fx += fx; + node2.fy += fy; } - // check the distribution of the nodes per level. - var distribution = this._getDistribution(); - - // place the nodes on the canvas. This also stablilizes the system. - this._placeNodesByHierarchy(distribution); - - // start the simulation. - this.start(); } } }; /** - * This function places the nodes on the canvas based on the hierarchial distribution. + * this function calculates the effects of the springs in the case of unsmooth curves. * - * @param {Object} distribution | obtained by the function this._getDistribution() * @private */ - exports._placeNodesByHierarchy = function(distribution) { - var nodeId, node; + exports._calculateHierarchicalSpringForces = function () { + var edgeLength, edge, edgeId; + var dx, dy, fx, fy, springForce, distance; + var edges = this.edges; - // start placing all the level 0 nodes first. Then recursively position their branches. - for (var level in distribution) { - if (distribution.hasOwnProperty(level)) { + var nodes = this.calculationNodes; + var nodeIndices = this.calculationNodeIndices; - for (nodeId in distribution[level].nodes) { - if (distribution[level].nodes.hasOwnProperty(nodeId)) { - node = distribution[level].nodes[nodeId]; - if (this.constants.hierarchicalLayout.direction == "UD" || this.constants.hierarchicalLayout.direction == "DU") { - if (node.xFixed) { - node.x = distribution[level].minPos; - node.xFixed = false; - distribution[level].minPos += distribution[level].nodeSpacing; - } + for (var i = 0; i < nodeIndices.length; i++) { + var node1 = nodes[nodeIndices[i]]; + node1.springFx = 0; + node1.springFy = 0; + } + + + // forces caused by the edges, modelled as springs + for (edgeId in edges) { + if (edges.hasOwnProperty(edgeId)) { + edge = edges[edgeId]; + if (edge.connected) { + // only calculate forces if nodes are in the same sector + if (this.nodes.hasOwnProperty(edge.toId) && this.nodes.hasOwnProperty(edge.fromId)) { + edgeLength = edge.physics.springLength; + // this implies that the edges between big clusters are longer + edgeLength += (edge.to.clusterSize + edge.from.clusterSize - 2) * this.constants.clustering.edgeGrowth; + + dx = (edge.from.x - edge.to.x); + dy = (edge.from.y - edge.to.y); + distance = Math.sqrt(dx * dx + dy * dy); + + if (distance == 0) { + distance = 0.01; } - else { - if (node.yFixed) { - node.y = distribution[level].minPos; - node.yFixed = false; - distribution[level].minPos += distribution[level].nodeSpacing; - } + // the 1/distance is so the fx and fy can be calculated without sine or cosine. + springForce = this.constants.physics.springConstant * (edgeLength - distance) / distance; + + fx = dx * springForce; + fy = dy * springForce; + + + + if (edge.to.level != edge.from.level) { + edge.to.springFx -= fx; + edge.to.springFy -= fy; + edge.from.springFx += fx; + edge.from.springFy += fy; + } + else { + var factor = 0.5; + edge.to.fx -= factor*fx; + edge.to.fy -= factor*fy; + edge.from.fx += factor*fx; + edge.from.fy += factor*fy; } - this._placeBranchNodes(node.edges,node.id,distribution,node.level); } } } } - // stabilize the system after positioning. This function calls zoomExtent. - this._stabilize(); + // normalize spring forces + var springForce = 1; + var springFx, springFy; + for (i = 0; i < nodeIndices.length; i++) { + var node = nodes[nodeIndices[i]]; + springFx = Math.min(springForce,Math.max(-springForce,node.springFx)); + springFy = Math.min(springForce,Math.max(-springForce,node.springFy)); + + node.fx += springFx; + node.fy += springFy; + } + + // retain energy balance + var totalFx = 0; + var totalFy = 0; + for (i = 0; i < nodeIndices.length; i++) { + var node = nodes[nodeIndices[i]]; + totalFx += node.fx; + totalFy += node.fy; + } + var correctionFx = totalFx / nodeIndices.length; + var correctionFy = totalFy / nodeIndices.length; + + for (i = 0; i < nodeIndices.length; i++) { + var node = nodes[nodeIndices[i]]; + node.fx -= correctionFx; + node.fy -= correctionFy; + } + }; +/***/ }, +/* 70 */ +/***/ function(module, exports, __webpack_require__) { /** - * This function get the distribution of levels based on hubsize + * 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. * - * @returns {Object} * @private */ - exports._getDistribution = function() { - var distribution = {}; - var nodeId, node, level; + exports._calculateNodeForces = function() { + if (this.constants.physics.barnesHut.gravitationalConstant != 0) { + var node; + var nodes = this.calculationNodes; + var nodeIndices = this.calculationNodeIndices; + var nodeCount = nodeIndices.length; - // we fix Y because the hierarchy is vertical, we fix X so we do not give a node an x position for a second time. - // the fix of X is removed after the x value has been set. - for (nodeId in this.nodes) { - if (this.nodes.hasOwnProperty(nodeId)) { - node = this.nodes[nodeId]; - node.xFixed = true; - node.yFixed = true; - if (this.constants.hierarchicalLayout.direction == "UD" || this.constants.hierarchicalLayout.direction == "DU") { - node.y = this.constants.hierarchicalLayout.levelSeparation*node.level; - } - else { - node.x = this.constants.hierarchicalLayout.levelSeparation*node.level; - } - if (distribution[node.level] === undefined) { - distribution[node.level] = {amount: 0, nodes: {}, minPos:0, nodeSpacing:0}; - } - distribution[node.level].amount += 1; - distribution[node.level].nodes[nodeId] = node; - } - } + this._formBarnesHutTree(nodes,nodeIndices); - // determine the largest amount of nodes of all levels - var maxCount = 0; - for (level in distribution) { - if (distribution.hasOwnProperty(level)) { - if (maxCount < distribution[level].amount) { - maxCount = distribution[level].amount; - } - } - } + var barnesHutTree = this.barnesHutTree; - // set the initial position and spacing of each nodes accordingly - for (level in distribution) { - if (distribution.hasOwnProperty(level)) { - distribution[level].nodeSpacing = (maxCount + 1) * this.constants.hierarchicalLayout.nodeSpacing; - distribution[level].nodeSpacing /= (distribution[level].amount + 1); - distribution[level].minPos = distribution[level].nodeSpacing - (0.5 * (distribution[level].amount + 1) * distribution[level].nodeSpacing); + // 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 BarnesHut 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); + } } } - - return distribution; }; /** - * this function allocates nodes in levels based on the recursive branching from the largest hubs. + * 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 hubsize + * @param parentBranch + * @param node * @private */ - exports._determineLevels = function(hubsize) { - var nodeId, node; + exports._getForceContribution = function(parentBranch,node) { + // we get no force contribution from an empty region + if (parentBranch.childrenCount > 0) { + var dx,dy,distance; - // determine hubs - for (nodeId in this.nodes) { - if (this.nodes.hasOwnProperty(nodeId)) { - node = this.nodes[nodeId]; - if (node.edges.length == hubsize) { - node.level = 0; + // 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); + + // BarnesHut condition + // original condition : s/d < thetaInverted = passed === d/s > 1/theta = passed + // calcSize = 1/s --> d * 1/s > 1/theta = passed + if (distance * parentBranch.calcSize > this.constants.physics.barnesHut.thetaInverted) { + // duplicate code to reduce function calls to speed up program + if (distance == 0) { + distance = 0.1*Math.random(); + dx = distance; } + var gravityForce = this.constants.physics.barnesHut.gravitationalConstant * parentBranch.mass * node.options.mass / (distance * distance * distance); + var fx = dx * gravityForce; + var fy = dy * gravityForce; + node.fx += fx; + node.fy += fy; } - } - - // branch from hubs - for (nodeId in this.nodes) { - if (this.nodes.hasOwnProperty(nodeId)) { - node = this.nodes[nodeId]; - if (node.level == 0) { - this._setLevel(1,node.edges,node.id); + 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 + // duplicate code to reduce function calls to speed up program + if (distance == 0) { + distance = 0.5*Math.random(); + dx = distance; + } + var gravityForce = this.constants.physics.barnesHut.gravitationalConstant * parentBranch.mass * node.options.mass / (distance * distance * distance); + var fx = dx * gravityForce; + var fy = dy * gravityForce; + node.fx += fx; + node.fy += fy; + } } } } }; - - /** - * this function allocates nodes in levels based on the direction of the edges + * This function constructs the barnesHut tree recursively. It creates the root, splits it and starts placing the nodes. * - * @param hubsize + * @param nodes + * @param nodeIndices * @private */ - exports._determineLevelsDirected = function() { - var nodeId, node, firstNode; - var minLevel = 10000; + exports._formBarnesHutTree = function(nodes,nodeIndices) { + var node; + var nodeCount = nodeIndices.length; - // set first node to source - firstNode = this.nodes[this.nodeIndices[0]]; - firstNode.level = minLevel; - this._setLevelDirected(minLevel,firstNode.edges,firstNode.id); + var minX = Number.MAX_VALUE, + minY = Number.MAX_VALUE, + maxX =-Number.MAX_VALUE, + maxY =-Number.MAX_VALUE; - // get the minimum level - for (nodeId in this.nodes) { - if (this.nodes.hasOwnProperty(nodeId)) { - node = this.nodes[nodeId]; - minLevel = node.level < minLevel ? node.level : minLevel; + // get the range of the nodes + for (var i = 0; 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 - // subtract the minimum from the set so we have a range starting from 0 - for (nodeId in this.nodes) { - if (this.nodes.hasOwnProperty(nodeId)) { - node = this.nodes[nodeId]; - node.level -= minLevel; + + var minimumTreeSize = 1e-5; + var rootSize = Math.max(minimumTreeSize,Math.abs(maxX - minX)); + var halfRootSize = 0.5 * rootSize; + var centerX = 0.5 * (minX + maxX), centerY = 0.5 * (minY + maxY); + + // construct the barnesHutTree + var barnesHutTree = { + root:{ + centerOfMass: {x:0, y:0}, + mass:0, + range: { + minX: centerX-halfRootSize,maxX:centerX+halfRootSize, + minY: centerY-halfRootSize,maxY:centerY+halfRootSize + }, + size: rootSize, + calcSize: 1 / rootSize, + children: { data:null}, + maxWidth: 0, + level: 0, + childrenCount: 4 + } + }; + this._splitBranch(barnesHutTree.root); + + // place the nodes one by one recursively + for (i = 0; i < nodeCount; i++) { + node = nodes[nodeIndices[i]]; + if (node.options.mass > 0) { + this._placeInTree(barnesHutTree.root,node); } } + + // make global + this.barnesHutTree = barnesHutTree }; /** - * Since hierarchical layout does not support: - * - smooth curves (based on the physics), - * - clustering (based on dynamic node counts) - * - * We disable both features so there will be no problems. + * this updates the mass of a branch. this is increased by adding a node. * + * @param parentBranch + * @param node * @private */ - exports._changeConstants = function() { - this.constants.clustering.enabled = false; - this.constants.physics.barnesHut.enabled = false; - this.constants.physics.hierarchicalRepulsion.enabled = true; - this._loadSelectedForceSolver(); - if (this.constants.smoothCurves.enabled == true) { - this.constants.smoothCurves.dynamic = false; - } - this._configureSmoothCurves(); + exports._updateBranchMass = function(parentBranch, node) { + var totalMass = parentBranch.mass + node.options.mass; + var totalMassInv = 1/totalMass; - var config = this.constants.hierarchicalLayout; - config.levelSeparation = Math.abs(config.levelSeparation); - if (config.direction == "RL" || config.direction == "DU") { - config.levelSeparation *= -1; - } + 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; - if (config.direction == "RL" || config.direction == "LR") { - if (this.constants.smoothCurves.enabled == true) { - this.constants.smoothCurves.type = "vertical"; - } - } - else { - if (this.constants.smoothCurves.enabled == true) { - this.constants.smoothCurves.type = "horizontal"; - } - } }; /** - * This is a recursively called function to enumerate the branches from the largest hubs and place the nodes - * on a X position that ensures there will be no overlap. + * determine in which branch the node will be placed. * - * @param edges - * @param parentId - * @param distribution - * @param parentLevel + * @param parentBranch + * @param node + * @param skipMassUpdate * @private */ - exports._placeBranchNodes = function(edges, parentId, distribution, parentLevel) { - for (var i = 0; i < edges.length; i++) { - var childNode = null; - if (edges[i].toId == parentId) { - childNode = edges[i].from; - } - else { - childNode = edges[i].to; - } + exports._placeInTree = function(parentBranch,node,skipMassUpdate) { + if (skipMassUpdate != true || skipMassUpdate === undefined) { + // update the mass of the branch. + this._updateBranchMass(parentBranch,node); + } - // if a node is conneceted to another node on the same level (or higher (means lower level))!, this is not handled here. - var nodeMoved = false; - if (this.constants.hierarchicalLayout.direction == "UD" || this.constants.hierarchicalLayout.direction == "DU") { - if (childNode.xFixed && childNode.level > parentLevel) { - childNode.xFixed = false; - childNode.x = distribution[childNode.level].minPos; - nodeMoved = true; - } + 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 { - if (childNode.yFixed && childNode.level > parentLevel) { - childNode.yFixed = false; - childNode.y = distribution[childNode.level].minPos; - nodeMoved = true; - } + else { // in SW + this._placeInRegion(parentBranch,node,"SW"); } - - if (nodeMoved == true) { - distribution[childNode.level].minPos += distribution[childNode.level].nodeSpacing; - if (childNode.edges.length > 1) { - this._placeBranchNodes(childNode.edges,childNode.id,distribution,childNode.level); - } + } + 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"); } } }; /** - * this function is called recursively to enumerate the barnches of the largest hubs and give each node a level. + * actually place the node in a region (or branch) * - * @param level - * @param edges - * @param parentId + * @param parentBranch + * @param node + * @param region * @private */ - exports._setLevel = function(level, edges, parentId) { - for (var i = 0; i < edges.length; i++) { - var childNode = null; - if (edges[i].toId == parentId) { - childNode = edges[i].from; - } - else { - childNode = edges[i].to; - } - if (childNode.level == -1 || childNode.level > level) { - childNode.level = level; - if (childNode.edges.length > 1) { - this._setLevel(level+1, childNode.edges, childNode.id); + exports._placeInRegion = function(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; } }; /** - * this function is called recursively to enumerate the branched of the first node and give each node a level based on edge direction + * 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 level - * @param edges - * @param parentId + * @param parentBranch * @private */ - exports._setLevelDirected = function(level, edges, parentId) { - this.nodes[parentId].hierarchyEnumerated = true; - var childNode, direction; - for (var i = 0; i < edges.length; i++) { - direction = 1; - if (edges[i].toId == parentId) { - childNode = edges[i].from; - direction = -1; - } - else { - childNode = edges[i].to; - } - if (childNode.level == -1) { - childNode.level = level + direction; - } + exports._splitBranch = function(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"); - for (var i = 0; i < edges.length; i++) { - if (edges[i].toId == parentId) {childNode = edges[i].from;} - else {childNode = edges[i].to;} - - if (childNode.edges.length > 1 && childNode.hierarchyEnumerated === false) { - this._setLevelDirected(childNode.level, childNode.edges, childNode.id); - } + if (containedNode != null) { + this._placeInTree(parentBranch,containedNode); } }; /** - * Unfix nodes + * 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 */ - exports._restoreNodes = function() { - for (var nodeId in this.nodes) { - if (this.nodes.hasOwnProperty(nodeId)) { - this.nodes[nodeId].xFixed = false; - this.nodes[nodeId].yFixed = false; - } + exports._insertRegion = function(parentBranch, region) { + var minX,maxX,minY,maxY; + 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; } - }; - - -/***/ }, -/* 70 */ -/***/ function(module, exports, __webpack_require__) { - // English - exports['en'] = { - edit: 'Edit', - del: 'Delete selected', - back: 'Back', - addNode: 'Add Node', - addEdge: 'Add Edge', - editNode: 'Edit Node', - editEdge: 'Edit Edge', - addDescription: 'Click in an empty space to place a new node.', - edgeDescription: 'Click on a node and drag the edge to another node to connect them.', - editEdgeDescription: 'Click on the control points and drag them to a node to connect to it.', - createEdgeError: 'Cannot link edges to a cluster.', - deleteClusterError: 'Clusters cannot be deleted.' - }; - exports['en_EN'] = exports['en']; - exports['en_US'] = exports['en']; - // Dutch - exports['nl'] = { - edit: 'Wijzigen', - del: 'Selectie verwijderen', - back: 'Terug', - addNode: 'Node toevoegen', - addEdge: 'Link toevoegen', - editNode: 'Node wijzigen', - editEdge: 'Link wijzigen', - addDescription: 'Klik op een leeg gebied om een nieuwe node te maken.', - edgeDescription: 'Klik op een node en sleep de link naar een andere node om ze te verbinden.', - editEdgeDescription: 'Klik op de verbindingspunten en sleep ze naar een node om daarmee te verbinden.', - createEdgeError: 'Kan geen link maken naar een cluster.', - deleteClusterError: 'Clusters kunnen niet worden verwijderd.' + 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 + }; }; - exports['nl_NL'] = exports['nl']; - exports['nl_BE'] = exports['nl']; - -/***/ }, -/* 71 */ -/***/ function(module, exports, __webpack_require__) { /** - * Canvas shapes used by Network + * This function is for debugging purposed, it draws the tree. + * + * @param ctx + * @param color + * @private */ - if (typeof CanvasRenderingContext2D !== 'undefined') { - - /** - * Draw a circle shape - */ - CanvasRenderingContext2D.prototype.circle = function(x, y, r) { - this.beginPath(); - this.arc(x, y, r, 0, 2*Math.PI, false); - }; - - /** - * Draw a square shape - * @param {Number} x horizontal center - * @param {Number} y vertical center - * @param {Number} r size, width and height of the square - */ - CanvasRenderingContext2D.prototype.square = function(x, y, r) { - this.beginPath(); - this.rect(x - r, y - r, r * 2, r * 2); - }; - - /** - * Draw a triangle shape - * @param {Number} x horizontal center - * @param {Number} y vertical center - * @param {Number} r radius, half the length of the sides of the triangle - */ - CanvasRenderingContext2D.prototype.triangle = function(x, y, r) { - // http://en.wikipedia.org/wiki/Equilateral_triangle - this.beginPath(); - - var s = r * 2; - var s2 = s / 2; - var ir = Math.sqrt(3) / 6 * s; // radius of inner circle - var h = Math.sqrt(s * s - s2 * s2); // height - - this.moveTo(x, y - (h - ir)); - this.lineTo(x + s2, y + ir); - this.lineTo(x - s2, y + ir); - this.lineTo(x, y - (h - ir)); - this.closePath(); - }; - - /** - * Draw a triangle shape in downward orientation - * @param {Number} x horizontal center - * @param {Number} y vertical center - * @param {Number} r radius - */ - CanvasRenderingContext2D.prototype.triangleDown = function(x, y, r) { - // http://en.wikipedia.org/wiki/Equilateral_triangle - this.beginPath(); - - var s = r * 2; - var s2 = s / 2; - var ir = Math.sqrt(3) / 6 * s; // radius of inner circle - var h = Math.sqrt(s * s - s2 * s2); // height - - this.moveTo(x, y + (h - ir)); - this.lineTo(x + s2, y - ir); - this.lineTo(x - s2, y - ir); - this.lineTo(x, y + (h - ir)); - this.closePath(); - }; - - /** - * Draw a star shape, a star with 5 points - * @param {Number} x horizontal center - * @param {Number} y vertical center - * @param {Number} r radius, half the length of the sides of the triangle - */ - CanvasRenderingContext2D.prototype.star = function(x, y, r) { - // http://www.html5canvastutorials.com/labs/html5-canvas-star-spinner/ - this.beginPath(); - - for (var n = 0; n < 10; n++) { - var radius = (n % 2 === 0) ? r * 1.3 : r * 0.5; - this.lineTo( - x + radius * Math.sin(n * 2 * Math.PI / 10), - y - radius * Math.cos(n * 2 * Math.PI / 10) - ); - } - - this.closePath(); - }; - - /** - * http://stackoverflow.com/questions/1255512/how-to-draw-a-rounded-rectangle-on-html-canvas - */ - CanvasRenderingContext2D.prototype.roundRect = function(x, y, w, h, r) { - var r2d = Math.PI/180; - if( w - ( 2 * r ) < 0 ) { r = ( w / 2 ); } //ensure that the radius isn't too large for x - if( h - ( 2 * r ) < 0 ) { r = ( h / 2 ); } //ensure that the radius isn't too large for y - this.beginPath(); - this.moveTo(x+r,y); - this.lineTo(x+w-r,y); - this.arc(x+w-r,y+r,r,r2d*270,r2d*360,false); - this.lineTo(x+w,y+h-r); - this.arc(x+w-r,y+h-r,r,0,r2d*90,false); - this.lineTo(x+r,y+h); - this.arc(x+r,y+h-r,r,r2d*90,r2d*180,false); - this.lineTo(x,y+r); - this.arc(x+r,y+r,r,r2d*180,r2d*270,false); - }; - - /** - * http://stackoverflow.com/questions/2172798/how-to-draw-an-oval-in-html5-canvas - */ - CanvasRenderingContext2D.prototype.ellipse = function(x, y, w, h) { - var kappa = .5522848, - ox = (w / 2) * kappa, // control point offset horizontal - oy = (h / 2) * kappa, // control point offset vertical - xe = x + w, // x-end - ye = y + h, // y-end - xm = x + w / 2, // x-middle - ym = y + h / 2; // y-middle - - this.beginPath(); - this.moveTo(x, ym); - this.bezierCurveTo(x, ym - oy, xm - ox, y, xm, y); - this.bezierCurveTo(xm + ox, y, xe, ym - oy, xe, ym); - this.bezierCurveTo(xe, ym + oy, xm + ox, ye, xm, ye); - this.bezierCurveTo(xm - ox, ye, x, ym + oy, x, ym); - }; - - - - /** - * http://stackoverflow.com/questions/2172798/how-to-draw-an-oval-in-html5-canvas - */ - CanvasRenderingContext2D.prototype.database = function(x, y, w, h) { - var f = 1/3; - var wEllipse = w; - var hEllipse = h * f; + exports._drawTree = function(ctx,color) { + if (this.barnesHutTree !== undefined) { - var kappa = .5522848, - ox = (wEllipse / 2) * kappa, // control point offset horizontal - oy = (hEllipse / 2) * kappa, // control point offset vertical - xe = x + wEllipse, // x-end - ye = y + hEllipse, // y-end - xm = x + wEllipse / 2, // x-middle - ym = y + hEllipse / 2, // y-middle - ymb = y + (h - hEllipse/2), // y-midlle, bottom ellipse - yeb = y + h; // y-end, bottom ellipse + ctx.lineWidth = 1; - this.beginPath(); - this.moveTo(xe, ym); + this._drawBranch(this.barnesHutTree.root,ctx,color); + } + }; - this.bezierCurveTo(xe, ym + oy, xm + ox, ye, xm, ye); - this.bezierCurveTo(xm - ox, ye, x, ym + oy, x, ym); - this.bezierCurveTo(x, ym - oy, xm - ox, y, xm, y); - this.bezierCurveTo(xm + ox, y, xe, ym - oy, xe, ym); + /** + * This function is for debugging purposes. It draws the branches recursively. + * + * @param branch + * @param ctx + * @param color + * @private + */ + exports._drawBranch = function(branch,ctx,color) { + if (color === undefined) { + color = "#FF0000"; + } - this.lineTo(xe, ymb); + 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(); - this.bezierCurveTo(xe, ymb + oy, xm + ox, yeb, xm, yeb); - this.bezierCurveTo(xm - ox, yeb, x, ymb + oy, x, ymb); + ctx.beginPath(); + ctx.moveTo(branch.range.maxX,branch.range.minY); + ctx.lineTo(branch.range.maxX,branch.range.maxY); + ctx.stroke(); - this.lineTo(x, ym); - }; + 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(); - /** - * Draw an arrow point (no line) + /* + if (branch.mass > 0) { + ctx.circle(branch.centerOfMass.x, branch.centerOfMass.y, 3*branch.mass); + ctx.stroke(); + } */ - CanvasRenderingContext2D.prototype.arrow = function(x, y, angle, length) { - // tail - var xt = x - length * Math.cos(angle); - var yt = y - length * Math.sin(angle); - - // inner tail - // TODO: allow to customize different shapes - var xi = x - length * 0.9 * Math.cos(angle); - var yi = y - length * 0.9 * Math.sin(angle); - - // left - var xl = xt + length / 3 * Math.cos(angle + 0.5 * Math.PI); - var yl = yt + length / 3 * Math.sin(angle + 0.5 * Math.PI); - - // right - var xr = xt + length / 3 * Math.cos(angle - 0.5 * Math.PI); - var yr = yt + length / 3 * Math.sin(angle - 0.5 * Math.PI); + }; - this.beginPath(); - this.moveTo(x, y); - this.lineTo(xl, yl); - this.lineTo(xi, yi); - this.lineTo(xr, yr); - this.closePath(); - }; - /** - * Sets up the dashedLine functionality for drawing - * Original code came from http://stackoverflow.com/questions/4576724/dotted-stroke-in-canvas - * @author David Jordan - * @date 2012-08-08 - */ - CanvasRenderingContext2D.prototype.dashedLine = function(x,y,x2,y2,dashArray){ - if (!dashArray) dashArray=[10,5]; - if (dashLength==0) dashLength = 0.001; // Hack for Safari - var dashCount = dashArray.length; - this.moveTo(x, y); - var dx = (x2-x), dy = (y2-y); - var slope = dy/dx; - var distRemaining = Math.sqrt( dx*dx + dy*dy ); - var dashIndex=0, draw=true; - while (distRemaining>=0.1){ - var dashLength = dashArray[dashIndex++%dashCount]; - if (dashLength > distRemaining) dashLength = distRemaining; - var xStep = Math.sqrt( dashLength*dashLength / (1 + slope*slope) ); - if (dx<0) xStep = -xStep; - x += xStep; - y += slope*xStep; - this[draw ? 'lineTo' : 'moveTo'](x,y); - distRemaining -= dashLength; - draw = !draw; - } - }; +/***/ }, +/* 71 */ +/***/ function(module, exports, __webpack_require__) { - // TODO: add diamond shape + module.exports = function(module) { + if(!module.webpackPolyfill) { + module.deprecate = function() {}; + module.paths = []; + // module.parent = undefined by default + module.children = []; + module.webpackPolyfill = 1; + } + return module; } diff --git a/dist/vis.map b/dist/vis.map index 89ce19b4..ff704cf5 100644 --- a/dist/vis.map +++ b/dist/vis.map @@ -1 +1 @@ -{"version":3,"file":"vis.map","sources":["./dist/vis.js"],"names":["root","factory","exports","module","define","amd","this","modules","__webpack_require__","moduleId","installedModules","id","loaded","call","m","c","p","util","DOMutil","DataSet","DataView","Queue","Graph3d","graph3d","Camera","Filter","Point2d","Point3d","Slider","StepNumber","Timeline","Graph2d","timeline","DateUtil","DataStep","Range","stack","TimeStep","components","items","Item","BackgroundItem","BoxItem","PointItem","RangeItem","Component","CurrentTime","CustomTime","DataAxis","GraphGroup","Group","BackgroundGroup","ItemSet","Legend","LineGraph","TimeAxis","Network","network","Edge","Groups","Images","Node","Popup","dotparser","gephiParser","Graph","Error","moment","hammer","isNumber","object","Number","isString","String","isDate","Date","match","ASPDateRegex","exec","isNaN","parse","isDataTable","google","visualization","DataTable","randomUUID","S4","Math","floor","random","toString","extend","a","i","len","arguments","length","other","prop","hasOwnProperty","selectiveExtend","props","Array","isArray","selectiveDeepExtend","b","TypeError","constructor","Object","undefined","deepExtend","selectiveNotDeepExtend","indexOf","equalArray","convert","type","Boolean","valueOf","isMoment","toDate","getType","toISOString","value","getAbsoluteLeft","elem","getBoundingClientRect","left","window","pageXOffset","getAbsoluteTop","top","pageYOffset","addClassName","className","classes","split","push","join","removeClassName","index","splice","forEach","callback","toArray","array","updateProperty","key","addEventListener","element","action","listener","useCapture","navigator","userAgent","attachEvent","removeEventListener","detachEvent","preventDefault","event","returnValue","getTarget","target","srcElement","nodeType","parentNode","option","asBoolean","defaultValue","asNumber","asString","asSize","asElement","hexToRGB","hex","shorthandRegex","replace","r","g","result","parseInt","RGBToHex","red","green","blue","slice","parseColor","color","isValidRGB","rgb","substr","isValidHex","hsv","hexToHSV","lighterColorHSV","h","s","v","min","darkerColorHSV","darkerColorHex","HSVToHex","lighterColorHex","background","border","highlight","hover","RGBToHSV","minRGB","maxRGB","max","d","hue","saturation","cssUtil","cssText","styles","style","trim","parts","keys","map","addCssText","currentStyles","newStyles","removeCssText","removeStyles","HSVToRGB","f","q","t","isOk","test","selectiveBridgeObject","fields","referenceObject","objectTo","create","bridgeObject","mergeOptions","mergeTarget","options","enabled","binarySearchCustom","orderedItems","searchFunction","field","field2","maxIterations","iteration","low","high","middle","item","searchResult","binarySearchValue","sidePreference","prevValue","nextValue","easeInOutQuad","start","end","duration","change","easingFunctions","linear","easeInQuad","easeOutQuad","easeInCubic","easeOutCubic","easeInOutCubic","easeInQuart","easeOutQuart","easeInOutQuart","easeInQuint","easeOutQuint","easeInOutQuint","prepareElements","JSONcontainer","elementType","redundant","used","cleanupElements","removeChild","getSVGElement","svgContainer","shift","document","createElementNS","appendChild","getDOMElement","DOMContainer","insertBefore","createElement","drawPoint","x","y","group","point","drawPoints","setAttributeNS","size","drawBar","width","height","rect","data","_options","_data","_fieldId","fieldId","_type","_subscribers","add","setOptions","prototype","queue","_queue","destroy","on","subscribers","subscribe","off","filter","unsubscribe","_trigger","params","senderId","concat","subscriber","addedIds","me","_addItem","columns","_getColumnNames","row","rows","getNumberOfRows","col","cols","getValue","update","updatedIds","updatedData","addOrUpdate","_updateItem","get","ids","firstType","returnType","allowedValues","itemId","_getItem","order","_sort","_filterFields","_appendRow","getIds","getDataSet","mappedItems","filteredItem","name","sort","av","bv","remove","removedId","removedIds","_remove","clear","maxField","itemField","minField","distinct","values","fieldType","count","exists","types","raw","converted","JSON","stringify","dataTable","getNumberOfColumns","getColumnId","getColumnLabel","addRow","setValue","_ids","_onEvent","apply","setData","viewOptions","getArguments","defaultFilter","dataSet","added","updated","removed","delay","Infinity","_timeout","_extended","_flushIfNeeded","flush","methods","original","method","args","fn","context","entry","clearTimeout","setTimeout","container","SyntaxError","containerElement","margin","defaultXCenter","defaultYCenter","xLabel","yLabel","zLabel","passValueFn","xValueLabel","yValueLabel","zValueLabel","filterLabel","legendLabel","STYLE","DOT","showPerspective","showGrid","keepAspectRatio","showShadow","showGrayBottom","showTooltip","verticalRatio","animationInterval","animationPreload","camera","eye","dataPoints","colX","colY","colZ","colValue","colFilter","xMin","xStep","xMax","yMin","yStep","yMax","zMin","zStep","zMax","valueMin","valueMax","xBarWidth","yBarWidth","colorAxis","colorGrid","colorDot","colorDotBorder","getMouseX","clientX","targetTouches","getMouseY","clientY","Emitter","_setScale","scale","z","xCenter","yCenter","zCenter","setArmLocation","_convert3Dto2D","point3d","translation","_convertPointToTranslation","_convertTranslationToScreen","ax","ay","az","cx","getCameraLocation","cy","cz","sinTx","sin","getCameraRotation","cosTx","cos","sinTy","cosTy","sinTz","cosTz","dx","dy","dz","bx","by","ex","ey","ez","getArmLength","xcenter","frame","canvas","clientWidth","ycenter","_setBackgroundColor","backgroundColor","fill","stroke","strokeWidth","borderColor","borderWidth","borderStyle","BAR","BARCOLOR","BARSIZE","DOTLINE","DOTCOLOR","DOTSIZE","GRID","LINE","SURFACE","_getStyleNumber","styleName","_determineColumnIndexes","counter","column","getDistinctValues","distinctValues","getColumnRange","minMax","_dataInitialize","rawData","_onChange","dataFilter","setOnLoadCallback","redraw","withBars","defaultXBarWidth","dataX","defaultYBarWidth","dataY","xRange","defaultXMin","defaultXMax","defaultXStep","yRange","defaultYMin","defaultYMax","defaultYStep","zRange","defaultZMin","defaultZMax","defaultZStep","valueRange","defaultValueMin","defaultValueMax","_getDataPoints","obj","sortNumber","dataMatrix","xIndex","yIndex","trans","screen","bottom","pointRight","pointTop","pointCross","hasChildNodes","firstChild","position","overflow","noCanvas","fontWeight","padding","innerHTML","onmousedown","_onMouseDown","ontouchstart","_onTouchStart","onmousewheel","_onWheel","ontooltip","_onTooltip","onkeydown","setSize","_resizeCanvas","clientHeight","animationStart","slider","play","animationStop","stop","_resizeCenter","charAt","parseFloat","setCameraPosition","pos","horizontal","vertical","setArmRotation","distance","setArmLength","getCameraPosition","getArmRotation","_readData","_redrawFilter","animationAutoStart","cameraPosition","styleNumber","tooltip","showAnimationControls","_redrawSlider","_redrawClear","_redrawAxis","_redrawDataGrid","_redrawDataLine","_redrawDataBar","_redrawDataDot","_redrawInfo","_redrawLegend","ctx","getContext","clearRect","widthMin","widthMax","dotSize","right","lineWidth","font","ymin","ymax","_hsv2rgb","strokeStyle","beginPath","moveTo","lineTo","strokeRect","fillStyle","closePath","gridLineLen","step","getCurrent","next","textAlign","textBaseline","fillText","label","visible","setValues","setPlayInterval","onchange","getIndex","selectValue","setOnChangeCallback","lineStyle","getLabel","getSelectedValue","from","to","prettyStep","text","xText","yText","zText","offset","xOffset","yOffset","xMin2d","xMax2d","gridLenX","gridLenY","textMargin","armAngle","H","S","V","R","G","B","C","Hi","X","abs","cross","topSideVisible","zAvg","transBottom","dist","sortDepth","aDiff","subtract","bDiff","crossproduct","crossProduct","radius","arc","PI","j","surface","corners","xWidth","yWidth","surfaces","center","avg","transCenter","diff","leftButtonDown","_onMouseUp","which","button","touchDown","startMouseX","startMouseY","startStart","startEnd","startArmRotation","cursor","onmousemove","_onMouseMove","onmouseup","diffX","diffY","horizontalNew","verticalNew","snapAngle","snapValue","round","parameters","emit","boundingRect","mouseX","mouseY","tooltipTimeout","_hideTooltip","dataPoint","_dataPointFromXY","_showTooltip","ontouchmove","_onTouchMove","ontouchend","_onTouchEnd","delta","wheelDelta","detail","oldLength","newLength","_insideTriangle","triangle","sign","as","bs","cs","distMax","closestDataPoint","closestDist","triangle1","triangle2","distX","distY","sqrt","content","line","dot","dom","borderRadius","boxShadow","borderLeft","contentWidth","offsetWidth","contentHeight","offsetHeight","lineHeight","dotWidth","dotHeight","armLocation","armRotation","armLength","cameraLocation","cameraRotation","calculateCameraOrientation","rot","graph","onLoadCallback","loadInBackground","isLoaded","getLoadedProgress","getColumn","getValues","dataView","progress","sub","sum","prev","bar","MozBorderRadius","slide","onclick","togglePlay","onChangeCallback","playTimeout","playInterval","playLoop","setIndex","playNext","interval","clearInterval","getPlayInterval","setPlayLoop","doLoop","onChange","indexToLeft","startClientX","startSlideX","leftToIndex","_start","_end","_step","precision","_current","setRange","setStep","calculatePrettyStep","log10","log","LN10","step1","pow","step2","step5","toPrecision","getStep","groups","forthArgument","defaultOptions","autoResize","orientation","maxHeight","minHeight","_create","body","domProps","emitter","bind","hiddenDates","snap","toScreen","_toScreen","toGlobalScreen","_toGlobalScreen","toTime","_toTime","toGlobalTime","_toGlobalTime","range","timeAxis","currentTime","customTime","itemSet","itemsData","groupsData","setGroups","setItems","Core","newDataSet","initialLoad","dataRange","_getDataRange","setWindow","animate","fit","setSelection","focus","getSelection","itemData","e","getItemRange","dataset","minItem","maxStartItem","maxEndItem","linegraph","getLegend","groupId","isGroupVisible","visibility","convertHiddenOptions","repeat","dateItem","updateHiddenDates","centerContainer","totalRange","pixelTime","startDate","endDate","_d","runUntil","clone","day","dayOfYear","year","dayOffset","date","month","console","removeDuplicates","startHidden","isHidden","endHidden","rangeStart","rangeEnd","hidden","startToFront","endToFront","_applyRange","safeDates","printDates","dates","stepOverHiddenDates","timeStep","previousTime","stepInHidden","currentValue","current","newValue","switchedYear","switchedMonth","switchedDay","time","conversion","getHiddenDurationBetween","correctTimeForHidden","hiddenDuration","totalDuration","partialDuration","accumulatedHiddenDuration","getAccumulatedHiddenDuration","newTime","getHiddenDurationBefore","timeOffset","requiredDuration","previousPoint","snapAwayFromHidden","direction","correctionEnabled","minimumStep","containerHeight","customRange","alignZeros","autoScale","stepIndex","marginStart","marginEnd","deadSpace","majorSteps","minorSteps","setMinimumStep","setFirst","safeSize","minimumStepValue","orderOfMagnitude","minorStepIdx","magnitudefactor","solutionFound","stepSize","niceStart","niceEnd","roundToMinor","marginRange","rounded","hasNext","previous","decimals","exp","cnt","isMajor","now","hours","minutes","seconds","milliseconds","deltaDifference","scaleOffset","moveable","zoomable","zoomMin","zoomMax","touch","animateTimer","_onDragStart","_onDrag","_onDragEnd","_onHold","_onMouseWheel","_onTouch","_onPinch","validateDirection","getPointer","pageX","pageY","hammerUtil","byUser","_cancelAnimation","initStart","initEnd","initTime","anyChanged","dragging","done","changed","newStart","newEnd","getRange","totalHidden","previousDelta","allowDragging","gesture","deltaX","deltaY","diffRange","safeStart","safeEnd","fakeGesture","pointer","pointerDate","_pointerToDate","zoom","touches","centerDate","hiddenDurationBefore","hiddenDurationAfter","move","EPSILON","orderByStart","orderByEnd","aTime","bTime","force","iMax","axis","collidingItem","jj","collision","nostack","subgroups","newTop","subgroup","format","FORMAT","minorLabels","millisecond","second","minute","hour","weekday","majorLabels","setFormat","defaultFormat","first","setFullYear","getFullYear","setMonth","setDate","setHours","setMinutes","setSeconds","setMilliseconds","getMilliseconds","getSeconds","getMinutes","getHours","getDate","getMonth","setScale","setAutoScale","enable","stepYear","stepMonth","stepDay","stepHour","stepMinute","stepSecond","stepMillisecond","getLabelMinor","getLabelMajor","getClassName","even","today","isSame","currentWeek","currentMonth","currentYear","locale","lang","toLowerCase","_isResized","resized","_previousWidth","_previousHeight","showCurrentTime","locales","parent","backgroundVertical","title","toUpperCase","substring","currentTimeTimer","setCurrentTime","getCurrentTime","showCustomTime","eventParams","Hammer","drag","prevent_default","setCustomTime","getCustomTime","stopPropagation","svg","linegraphOptions","showMinorLabels","showMajorLabels","icons","majorLinesOffset","minorLinesOffset","labelOffsetX","labelOffsetY","iconWidth","linegraphSVG","DOMelements","lines","labels","conversionFactor","minWidth","stepPixels","stepPixelsForced","zeroCrossing","lineOffset","master","svgElements","iconsRemoved","amountOfGroups","lineContainer","scrollTop","addGroup","graphOptions","updateGroup","removeGroup","hide","show","display","_redrawGroupIcons","iconHeight","iconOffset","drawIcon","_cleanupIcons","backgroundHorizontal","activeGroups","_calculateCharSize","minorLabelHeight","minorCharHeight","majorLabelHeight","majorCharHeight","minorLineWidth","minorLineHeight","majorLineWidth","majorLineHeight","_redrawLabels","_redrawTitle","amountOfSteps","stepDifference","zeroStepDifference","valueAtZero","marginStartPos","maxLabelSize","_redrawLabel","_redrawLine","titleWidth","titleCharHeight","convertValue","invertedValue","convertedValue","characterHeight","largestWidth","majorCharWidth","minorCharWidth","textMinor","createTextNode","measureCharMinor","textMajor","measureCharMajor","textTitle","measureCharTitle","titleCharWidth","groupsUsingDefaultStyles","usingDefaultStyle","zeroPosition","Line","Bar","Points","setZeroPosition","catmullRom","parametrization","alpha","SVGcontainer","path","fillPath","fillHeight","outline","shaded","barWidth","bar1Height","bar2Height","icon","yAxisOrientation","getYRange","groupData","draw","framework","subgroupIndex","subgroupOrderer","subgroupOrder","visibleItems","byStart","byEnd","checkRangedItems","inner","foreground","marker","Element","getLabelWidth","restack","_updateVisibleItems","markerHeight","lastMarkerHeight","dirty","displayed","_calculateHeight","offsetTop","offsetLeft","ii","repositionY","resetSubgroups","labelSet","setParent","orderSubgroups","_checkIfVisible","sortArray","sortField","removeFromDataSet","removeItem","startArray","endArray","oldVisibleItems","visibleItemsLookup","lowerBound","upperBound","_checkIfVisibleWithReference","initialPosByStart","_traceVisible","initialPosByEnd","repositionX","initialPos","breakCondition","isVisible","align","groupOrder","selectable","editable","updateTime","onAdd","onUpdate","onMove","onRemove","onMoving","itemOptions","itemListeners","_onAdd","_onUpdate","_onRemove","groupListeners","_onAddGroups","_onUpdateGroups","_onRemoveGroups","groupIds","selection","stackDirty","touchParams","UNGROUPED","BACKGROUND","box","_updateUngrouped","backgroundGroup","_onSelectItem","_onMultiSelectItem","_onAddItem","addCallback","Function","markDirty","unselect","select","getVisibleItems","rawVisibleItems","_deselect","_orderGroups","visibleInterval","zoomed","lastVisibleInterval","lastWidth","firstGroup","_firstGroup","firstMargin","nonFirstMargin","groupMargin","groupResized","firstGroupIndex","firstGroupId","ungrouped","_getGroupId","getLabelSet","oldItemsData","getItems","_order","getGroups","_getType","_removeItem","groupOptions","oldGroupId","oldGroup","_constructByEndArray","itemFromTarget","selected","dragLeftItem","dragRightItem","initialX","itemProps","newProps","initial","groupFromTarget","_updateItemProps","_moveToGroup","changes","ctrlKey","srcEvent","shiftKey","oldSelection","newSelection","xAbs","newItem","_getItemRange","_item","itemSetFromTarget","side","iconSize","iconSpacing","textArea","scrollableHeight","drawLegendIcons","getComputedStyle","paddingTop","defaultGroup","sampling","graphHeight","barChart","handleOverlap","dataAxis","legend","abortedGraphUpdate","updateSVGheight","updateSVGheightOnResize","lastStart","COUNTER","BarGraphFunctions","yAxisLeft","yAxisRight","legendLeft","legendRight","_updateAllGroupData","_updateGroup","groupsContent","ungroupedCounter","forceGraphUpdate","_updateGraph","rangePerPixelInv","preprocessedGroupData","processedGroupData","groupRanges","changeCalled","minDate","maxDate","_getRelevantData","_applySampling","_convertXcoordinates","_getYRanges","_updateYAxis","MAX_CYCLES","_convertYcoordinates","dataContainer","guess","increment","amountOfPoints","xDistance","pointsPerPixel","ceil","sampledData","barCombinedDataLeft","barCombinedDataRight","getStackedBarYRange","minVal","maxVal","yAxisLeftUsed","yAxisRightUsed","minLeft","minRight","maxLeft","maxRight","ignore","_toggleAxisVisiblity","drawIcons","axisUsed","datapoints","xValue","yValue","extractedData","svgHeight","majorTexts","minorTexts","lineTop","parentChanged","foregroundNextSibling","nextSibling","backgroundNextSibling","_repaintLabels","timeLabelsize","cur","prevLine","xPrev","xFirstMajorLabel","_repaintMinorText","_repaintMajorText","_repaintMajorLine","_repaintMinorLine","leftTime","leftText","widthText","arr","pop","childNodes","nodeValue","_repaintDeleteButton","anchor","deleteButton","_updateContents","template","_updateTitle","removeAttribute","_updateDataAttributes","dataAttributes","attributes","setAttribute","_updateStyle","emptyContent","baseClassName","onTop","itemSubgroup","itemSetHeight","marginLeft","maxWidth","_repaintDragLeft","_repaintDragRight","contentLeft","parentWidth","boxWidth","dragLeft","dragRight","_determineBrowserMethod","_initializeMixinLoaders","renderRefreshRate","renderTimestep","renderTime","physicsTime","runDoubleSpeed","physicsDiscreteStepsize","initializing","triggerFunctions","edit","editEdge","connect","del","nodes","mass","radiusMin","radiusMax","shape","image","fontColor","fontSize","fontFace","fontFill","fontStrokeWidth","fontStrokeColor","level","borderWidthSelected","edges","widthSelectionMultiplier","hoverWidth","labelAlignment","arrowScaleFactor","dash","gap","altLength","inheritColor","configurePhysics","physics","barnesHut","thetaInverted","gravitationalConstant","centralGravity","springLength","springConstant","damping","repulsion","nodeDistance","hierarchicalRepulsion","clustering","initialMaxNodes","clusterThreshold","reduceToNodes","chainThreshold","clusterEdgeThreshold","sectorThreshold","screenSizeThreshold","fontSizeMultiplier","maxFontSize","forceAmplification","distanceAmplification","edgeGrowth","nodeScaling","maxNodeSizeIncrements","activeAreaBoxSize","clusterLevelDifference","navigation","keyboard","speed","bindToWindow","dataManipulation","initiallyVisible","hierarchicalLayout","levelSeparation","nodeSpacing","layout","freezeForStabilization","smoothCurves","dynamic","roundness","maxVelocity","minVelocity","stabilize","stabilizationIterations","zoomExtentOnStabilize","dragNetwork","dragNodes","hideEdgesOnDrag","hideNodesOnDrag","constants","pixelRatio","hoverObj","controlNodesActive","navigationHammers","existing","_new","animationSpeed","animationEasingFunction","animating","easingTime","sourceScale","targetScale","sourceTranslation","targetTranslation","lockedOnNodeId","lockedOnNodeOffset","touchTime","images","setOnloadCallback","_redraw","xIncrement","yIncrement","zoomIncrement","_loadPhysicsSystem","_loadSectorSystem","_loadClusterSystem","_loadSelectionSystem","_loadHierarchySystem","_setTranslation","freezeSimulation","cachedFunctions","startedStabilization","stabilized","draggingNodes","calculationNodes","calculationNodeIndices","nodeIndices","canvasTopLeft","canvasBottomRight","pointerPosition","areaCenter","previousScale","nodesData","edgesData","nodesListeners","_addNodes","_updateNodes","_removeNodes","edgesListeners","_addEdges","_updateEdges","_removeEdges","moving","timer","_setupHierarchicalLayout","zoomExtent","startWithClustering","keycharm","MixinLoader","Activator","browserType","requiresTimeout","_getScriptPath","scripts","getElementsByTagName","src","_getRange","node","minY","maxY","minX","maxX","nodeId","boundingBox","_findCenter","animationOptions","initialZoom","disableStart","zoomLevel","numberOfNodes","factor","yDistance","xZoomLevel","yZoomLevel","animation","_updateNodeIndexList","_clearNodeIndexList","idx","_createManipulatorBar","dotData","DOTToGraph","gephi","gephiData","parseGephi","_setNodes","_setEdges","_putDataInSector","_resetLevels","_stabilize","onEdit","onEditEdge","onConnect","onDelete","editMode","newColorObj","groupname","clickToUse","activator","_createKeyBinds","_loadNavigationControls","_loadManipulationSystem","_configureSmoothCurves","_bindHammer","tabIndex","devicePixelRatio","webkitBackingStorePixelRatio","mozBackingStorePixelRatio","msBackingStorePixelRatio","oBackingStorePixelRatio","backingStorePixelRatio","setTransform","dispose","pinch","_onTap","_onDoubleTap","_onMouseMoveTitle","hammerFrame","_onRelease","reset","isActive","_moveUp","_yStopMoving","_moveDown","_moveLeft","_xStopMoving","_moveRight","_zoomIn","_stopZoom","_zoomOut","_deleteSelected","_cleanupPhysicsConfiguration","_recursiveDOMDelete","DOMobject","_getPointer","pinched","_getScale","_handleTouch","_handleDragStart","_getNodeAt","_getTranslation","isSelected","_selectObject","nodeIds","objectId","selectionObj","xFixed","yFixed","_handleOnDrag","releaseNode","_XconvertDOMtoCanvas","_XconvertCanvasToDOM","_YconvertDOMtoCanvas","_YconvertCanvasToDOM","_handleDragEnd","_handleTap","_handleDoubleTap","_handleOnHold","_handleOnRelease","_zoom","scaleOld","preScaleDragPointer","DOMtoCanvas","scaleFrac","tx","ty","updateClustersDefault","postScaleDragPointer","canvasToDOM","popupObj","_checkHidePopup","checkShow","_checkShowPopup","popupTimer","edgeId","_getEdgeAt","_hoverObject","_blurObject","lastPopupNode","nodeUnderCursor","overlappingNodes","isOverlappingWith","getTitle","overlappingEdges","edge","connected","popup","setPosition","setText","emitEvent","oldWidth","oldHeight","oldNodesData","_updateSelection","angle","_updateCalculationNodes","_reconnectEdges","_updateValueRange","updateLabels","changedData","setProperties","properties","oldEdgesData","oldEdge","disconnect","showInternalIds","_createBezierNodes","via","sectors","dynamicEdges","setValueRange","w","save","translate","_doInAllSectors","restore","offsetX","offsetY","_drawNodes","alwaysShow","setScaleAndPos","inArea","sMax","_drawEdges","_drawControlNodes","_freezeDefinedNodes","_physicsTick","_restoreFrozenNodes","fixedData","_isMoving","vmin","isMoving","_discreteStepNodes","nodesPresent","discreteStepLimited","discreteStep","vminCorrected","_revertPhysicsState","revertPosition","_revertPhysicsTick","_doInAllActiveSectors","_doInSupportSector","mainMovingStatus","supportMovingStatus","mainMoving","_animationStep","_handleNavigation","startTime","renderStartTime","requestAnimationFrame","mozRequestAnimationFrame","webkitRequestAnimationFrame","msRequestAnimationFrame","iterations","toggleFreeze","parentEdgeId","internalMultiplier","positionBezierNode","mixin","storePosition","storePositions","dataArray","allowedToMoveX","allowedToMoveY","getPositions","focusOnNode","nodePosition","lockedOnNode","easingFunction","animateView","locked","_transitionRedraw","viewCenter","distanceFromCenter","_classicRedraw","_lockedRedraw","active","getScale","getCenterCoordinates","getBoundingBox","networkConstants","fromId","toId","widthSelected","labelDimensions","yLine","dirtyLabel","fromBackup","toBackup","originalFromId","originalToId","widthFixed","lengthFixed","controlNodesEnabled","controlNodes","positions","connectedNode","_drawLine","_drawArrow","_drawArrowCenter","_drawDashLine","attachEdge","detachEdge","xFrom","yFrom","xTo","yTo","xObj","yObj","_getDistanceToEdge","_getColor","colorObj","_getLineWidth","_line","midpointX","midpointY","_pointOnLine","_label","resize","_circle","_pointOnCircle","networkScaleInv","_getViaCoordinates","xVia","yVia","quadraticCurveTo","lineCount","measureText","_rotateForLabelAlignment","_drawLabelRect","_drawLabelText","angleInDegrees","atan2","rotate","lineMargin","fillRect","lineJoin","strokeText","setLineDash","pattern","lineDashOffset","lineCap","dashedLine","percentage","arrow","_pointOnBezier","_findBorderPosition","distanceToBorder","distanceToNodes","difference","threshold","arrowPos","guidePos","edgeSegmentLength","toBorderDist","toBorderPoint","x1","y1","x2","y2","x3","y3","lastX","lastY","minDistance","_getDistanceToLine","px","py","something","u","nodeIdFrom","nodeIdTo","getControlNodeFromPosition","getControlNodeToPosition","_enableControlNodes","_disableControlNodes","_getSelectedControlNode","fromDistance","toDistance","_restoreControlNodes","controlnodeFromPos","fromBorderDist","fromBorderPoint","controlnodeToPos","defaultIndex","DEFAULT","imageBroken","load","url","brokenUrl","img","Image","onload","onerror","error","imagelist","grouplist","reroutedEdges","fontDrawThreshold","horizontalAlignLeft","verticalAlignTop","baseRadiusValue","radiusFixed","preassignedLevel","hierarchyEnumerated","fx","fy","vx","vy","previousState","resetCluster","dynamicEdgesLength","clusterSession","clusterSizeWidthFactor","clusterSizeHeightFactor","clusterSizeRadiusFactor","growthIndicator","networkScale","formationScale","clusterSize","containedNodes","containedEdges","clusterSessions","originalLabel","triggerFunction","groupObj","imageObj","brokenImage","_drawDatabase","_resizeDatabase","_drawBox","_resizeBox","_drawCircle","_resizeCircle","_drawEllipse","_resizeEllipse","_drawImage","_resizeImage","_drawCircularImage","_resizeCircularImage","_drawText","_resizeText","_drawDot","_resizeShape","_drawSquare","_drawTriangle","_drawTriangleDown","_drawStar","_reset","clearSizeCache","_setForce","_addForce","storeState","isFixed","velocity","getDistance","_drawImageAtPosition","globalAlpha","drawImage","_drawImageLabel","getTextSize","_swapToImageResizeWhenImageLoaded","diameter","centerX","centerY","_drawRawCircle","circle","clip","textSize","clusterLineWidth","selectionLineWidth","roundRect","database","defaultSize","ellipse","_drawShape","radiusMultiplier","baseline","labelUnderNode","inView","clearVelocity","updateVelocity","massBeforeClustering","energyBefore","styleAttr","fontFamily","WebkitBorderRadius","whiteSpace","parseDOT","parseGraph","nextPreview","isAlphaNumeric","regexAlphaNumeric","merge","o","addNode","graphs","attr","addEdge","createEdge","getToken","tokenType","TOKENTYPE","NULL","token","isComment","DELIMITER","c2","DELIMITERS","IDENTIFIER","newSyntaxError","UNKNOWN","chop","strict","parseStatements","parseStatement","subgraph","parseSubgraph","parseEdge","parseAttributeStatement","parseNodeStatement","subgraphs","parseAttributeList","message","maxLength","forEach2","array1","array2","elem1","elem2","graphData","dotNode","graphNode","convertEdge","dotEdge","graphEdge","subEdge","{","}","[","]",";","=",",","->","--","gephiJSON","allowedToMove","gEdges","gNodes","gEdge","source","gNode","leftContainer","rightContainer","shadowTop","shadowBottom","shadowTopLeft","shadowBottomLeft","shadowTopRight","shadowBottomRight","_redrawTimer","listeners","events","scrollTopMin","redrawCount","_initAutoResize","component","_stopAutoResize","what","getWindow","borderRootHeight","borderRootWidth","autoHeight","centerWidth","_updateScrollTop","visibilityTop","visibilityBottom","MAX_REDRAWS","repaint","_startAutoResize","_onResize","lastHeight","watchTimer","setInterval","initialScrollTop","oldScrollTop","_getScrollTop","newScrollTop","_setScrollTop","eventType","getTouchList","collectEventData","custom","back","editNode","addDescription","edgeDescription","editEdgeDescription","createEdgeError","deleteClusterError","CanvasRenderingContext2D","square","s2","ir","triangleDown","star","n","r2d","kappa","ox","oy","xe","ye","xm","ym","bezierCurveTo","wEllipse","hEllipse","ymb","yeb","xt","yt","xi","yi","xl","yl","xr","yr","dashArray","dashLength","dashCount","slope","distRemaining","dashIndex","_catmullRom","_linear","dFill","_catmullRomUniform","p0","p1","p2","p3","bp1","bp2","normalization","d1","d2","d3","A","N","M","d3powA","d2powA","d3pow2A","d2pow2A","d1pow2A","d1powA","Bargraph","barCombinedData","coreDistance","drawData","combinedData","intersections","barPoints","_getDataIntersections","heightOffset","_getSafeDrawData","nextKey","amount","resolved","prevKey","accumulated","groupLabel","_getStackedBarYRange","xpos","PhysicsMixin","ClusterMixin","SectorsMixin","SelectionMixin","ManipulationMixin","NavigationMixin","HierarchicalLayoutMixin","_loadMixin","sourceVariable","mixinFunction","_clearMixin","_loadSelectedForceSolver","_loadPhysicsConfiguration","hubThreshold","activeSector","drawingNode","blockConnectingEdgeSelection","forceAppendSelection","manipulationDiv","editModeDiv","closeDiv","_cleanNavigation","_loadNavigationElements","overlay","_onTapOverlay","windowHammer","_hasParent","deactivate","escListener","activate","unbind","_callbacks","once","self","removeListener","removeAllListeners","callbacks","cb","hasListeners","clusterToFit","maxNumberOfNodes","reposition","maxLevels","forceAggregateHubs","normalizeClusterLevels","increaseClusterLevel","repositionNodes","openCluster","isMovingBeforeClustering","_nodeInActiveArea","_sector","_addSector","decreaseClusterLevel","_expandClusterNode","_updateDynamicEdges","updateClusters","zoomDirection","recursive","doNotStart","amountOfNodes","_collapseSector","_formClusters","_openClusters","_openClustersBySize","_aggregateHubs","handleChains","chainPercentage","_getChainFraction","_reduceAmountOfChains","_getHubSize","_formClustersByHub","openAll","containedNodeId","childNode","_expelChildFromParent","_unselectAll","_releaseContainedEdges","_connectEdgeBackToChild","_validateEdges","othersPresent","childNodeId","_repositionBezierNodes","_formClustersByZoom","_forceClustersByZoom","minLength","_addToCluster","_clusterToSmallestNeighbour","smallestNeighbour","smallestNeighbourNode","neighbour","onlyEqual","_formClusterFromHub","hubNode","absorptionSizeOffset","allowCluster","edgesIdarray","amountOfInitialEdges","_addToContainedEdges","_connectEdgeToCluster","_containCircularEdgesFromNode","massBefore","correction","edgeToId","edgeFromId","k","_addToReroutedEdges","maxLevel","minLevel","clusterLevel","targetLevel","average","averageSquared","hubCounter","largestHub","variance","standardDeviation","fraction","reduceAmount","chains","total","_switchToSector","sectorId","sectorType","_switchToActiveSector","_switchToFrozenSector","_switchToSupportSector","_loadLatestSector","_previousSector","_setActiveSector","newId","_forgetLastSector","_createNewSector","_deleteActiveSector","_deleteFrozenSector","_freezeSector","_activateSector","_mergeThisWithFrozen","_collapseThisToSingleCluster","sector","unqiueIdentifier","previousSector","runFunction","argument","returnValues","_doInAllFrozenSectors","_drawSectorNodes","_drawAllSectorNodes","_getNodesOverlappingWith","_getAllNodesOverlappingWith","_pointerToPositionObject","positionObject","_getEdgesOverlappingWith","_getAllEdgesOverlappingWith","_addToSelection","_addToHover","_removeFromSelection","doNotTrigger","_unselectClusters","_getSelectedNodeCount","_getSelectedNode","_getSelectedEdge","_getSelectedEdgeCount","_getSelectedObjectCount","_selectionIsEmpty","_clusterInSelection","_selectConnectedEdges","_hoverConnectedEdges","_unselectConnectedEdges","append","highlightEdges","overrideSelectable","DOM","_manipulationReleaseOverload","_navigationReleaseOverload","getSelectedNodes","edgeIds","getSelectedEdges","idArray","selectNodes","RangeError","selectEdges","_clearManipulatorBar","manipulationDOM","_restoreOverloadedFunctions","functionName","_toggleEditMode","toolbar","boundFunction","edgeBeingEdited","selectedControlNode","_createAddNodeToolbar","_createAddEdgeToolbar","_editNode","_createEditEdgeToolbar","_addNode","_handleConnect","_finishConnect","_selectControlNode","_controlNodeDrag","_releaseControlNode","newNode","_editEdge","alert","supportNodes","targetNode","connectionEdge","connectFromId","_createEdge","defaultData","finalizedData","sourceNodeId","targetNodeId","selectedNodes","selectedEdges","navigationDivs","navigationDivActions","_stopMovement","_zoomExtent","hubsize","definedLevel","undefinedLevel","_changeConstants","_determineLevels","_determineLevelsDirected","distribution","_getDistribution","_placeNodesByHierarchy","minPos","_placeBranchNodes","maxCount","_setLevel","firstNode","_setLevelDirected","config","parentId","parentLevel","nodeMoved","_restoreNodes","__WEBPACK_AMD_DEFINE_FACTORY__","__WEBPACK_AMD_DEFINE_ARRAY__","__WEBPACK_AMD_DEFINE_RESULT__","_exportFunctions","_bound","keydown","keyup","_keys","fromCharCode","code","down","handleEvent","up","keyCode","bound","bindAll","getKey","newBindings","setup","READY","Event","determineEventTypes","Utils","each","gestures","Detection","register","onTouch","DOCUMENT","EVENT_MOVE","detect","EVENT_END","Instance","VERSION","defaults","behavior","userSelect","touchAction","touchCallout","contentZooming","userDrag","tapHighlightColor","HAS_POINTEREVENTS","pointerEnabled","msPointerEnabled","HAS_TOUCHEVENTS","IS_MOBILE","NO_MOUSEEVENTS","CALCULATE_INTERVAL","EVENT_TYPES","DIRECTION_DOWN","DIRECTION_LEFT","DIRECTION_UP","DIRECTION_RIGHT","POINTER_MOUSE","POINTER_TOUCH","POINTER_PEN","EVENT_START","EVENT_RELEASE","EVENT_TOUCH","plugins","utils","dest","handler","iterator","inStr","find","inArray","hasParent","getCenter","getVelocity","deltaTime","getAngle","touch1","touch2","getDirection","getRotation","isVertical","setPrefixedCss","toggle","prefixes","toCamelCase","toggleBehavior","falseFn","onselectstart","ondragstart","str","preventMouseEvents","started","shouldDetect","hook","onTouchHandler","ev","triggerType","srcType","isPointer","isMouse","buttons","PointerEvent","matchType","updatePointer","doDetect","touchList","touchListLength","triggerChange","trigger","changedLength","changedTouches","evData","identifiers","identifier","pointerType","timeStamp","preventManipulation","stopDetect","pointers","touchlist","pointerEvent","pointerId","pt","MSPOINTER_TYPE_MOUSE","MSPOINTER_TYPE_TOUCH","MSPOINTER_TYPE_PEN","detection","stopped","startDetect","inst","eventData","startEvent","lastEvent","lastCalcEvent","futureCalcEvent","lastCalcData","extendEventData","instOptions","getCalculatedData","recalc","calcEv","calcData","velocityX","velocityY","interimAngle","interimDirection","startEv","lastEv","rotation","eventStartHandler","eventHandlers","createEvent","initEvent","dispatchEvent","state","eh","dragGesture","dragMaxTouches","triggered","dragMinDistance","startCenter","dragDistanceCorrection","dragLockToAxis","dragLockMinDistance","lastDirection","dragBlockVertical","dragBlockHorizontal","Drag","Gesture","holdGesture","holdTimeout","holdThreshold","Hold","Release","Swipe","swipeMinTouches","swipeMaxTouches","swipeVelocityX","swipeVelocityY","tapGesture","sincePrev","didDoubleTap","hasMoved","tapMaxDistance","tapMaxTime","doubleTapInterval","doubleTapDistance","tapAlways","Tap","Touch","preventMouse","transformGesture","scaleThreshold","rotationThreshold","transformMinScale","transformMinRotation","Transform","global","dfl","hasOwnProp","defaultParsingFlags","empty","unusedTokens","unusedInput","charsLeftOver","nullInput","invalidMonth","invalidFormat","userInvalidated","iso","printMsg","msg","suppressDeprecationWarnings","warn","deprecate","firstTime","deprecateSimple","deprecations","padToken","func","leftZeroFill","ordinalizeToken","period","localeData","ordinal","monthDiff","anchor2","adjust","wholeMonthDiff","meridiemFixWrap","meridiem","isPm","meridiemHour","isPM","Locale","Moment","skipOverflow","checkOverflow","copyConfig","updateInProgress","updateOffset","Duration","normalizedInput","normalizeObjectUnits","years","quarters","quarter","months","weeks","week","days","_milliseconds","_days","_months","_locale","_bubble","val","_isAMomentObject","_i","_f","_l","_strict","_tzm","_isUTC","_offset","_pf","momentProperties","absRound","number","targetLength","forceSign","output","positiveMomentsDifference","base","res","isAfter","momentsDifference","makeAs","isBefore","createAdder","dur","tmp","addOrSubtractDurationFromMoment","mom","isAdding","setTime","rawSetter","rawGetter","rawMonthSetter","input","compareArrays","dontConvert","lengthDiff","diffs","toInt","normalizeUnits","units","lowered","unitAliases","camelFunctions","inputObject","normalizedProp","makeList","setter","getter","results","utc","set","argumentForCoercion","coercedNumber","isFinite","daysInMonth","UTC","getUTCDate","weeksInYear","dow","doy","weekOfYear","daysInYear","isLeapYear","_a","MONTH","DATE","YEAR","HOUR","MINUTE","SECOND","MILLISECOND","_overflowDayOfYear","isValid","_isValid","getTime","bigHour","normalizeLocale","chooseLocale","names","loadLocale","oldLocale","hasModule","model","local","removeFormattingTokens","makeFormatFunction","formattingTokens","formatTokenFunctions","formatMoment","expandFormat","formatFunctions","invalidDate","replaceLongDateFormatTokens","longDateFormat","localFormattingTokens","lastIndex","getParseRegexForToken","parseTokenOneDigit","parseTokenThreeDigits","parseTokenFourDigits","parseTokenOneToFourDigits","parseTokenSignedNumber","parseTokenSixDigits","parseTokenOneToSixDigits","parseTokenTwoDigits","parseTokenOneToThreeDigits","parseTokenWord","_meridiemParse","parseTokenOffsetMs","parseTokenTimestampMs","parseTokenTimezone","parseTokenT","parseTokenDigits","parseTokenOneOrTwoDigits","_ordinalParse","_ordinalParseLenient","RegExp","regexpEscape","unescapeFormat","utcOffsetFromString","string","possibleTzMatches","tzChunk","parseTimezoneChunker","addTimeToArrayFromToken","datePartArray","monthsParse","_dayOfYear","parseTwoDigitYear","_meridiem","_useUTC","weekdaysParse","_w","invalidWeekday","dayOfYearFromWeekInfo","weekYear","temp","GG","W","E","_week","gg","dayOfYearFromWeeks","dateFromConfig","currentDate","yearToUse","currentDateArray","makeUTCDate","getUTCMonth","_nextDay","makeDate","setUTCMinutes","getUTCMinutes","dateFromObject","getUTCFullYear","makeDateFromStringAndFormat","ISO_8601","parseISO","parsedInput","tokens","skipped","stringLength","totalParsedInputLength","matched","p4","makeDateFromStringAndArray","tempConfig","bestMoment","scoreToBeat","currentScore","NaN","score","l","isoRegex","isoDates","isoTimes","makeDateFromString","createFromInputFallback","makeDateFromInput","aspNetJsonRegex","ms","setUTCFullYear","parseWeekday","substituteTimeAgo","withoutSuffix","isFuture","relativeTime","posNegDuration","relativeTimeThresholds","firstDayOfWeek","firstDayOfWeekOfYear","adjustedMoment","daysToDayOfWeek","daysToAdd","getUTCDay","makeMoment","invalid","preparse","pickBy","moments","dayOfMonth","unit","makeAccessor","keepTime","daysToYears","yearsToDays","makeDurationGetter","makeGlobal","shouldDeprecate","ender","oldGlobalMoment","globalScope","aspNetTimeSpanJsonRegex","isoDurationRegex","isoFormat","unitMillisecondFactors","Milliseconds","Seconds","Minutes","Hours","Days","Months","Years","D","Q","DDD","dayofyear","isoweekday","isoweek","weekyear","isoweekyear","ordinalizeTokens","paddedTokens","MMM","monthsShort","MMMM","dd","weekdaysMin","ddd","weekdaysShort","dddd","weekdays","isoWeek","YY","YYYY","YYYYY","YYYYYY","gggg","ggggg","isoWeekYear","GGGG","GGGGG","isoWeekday","SS","SSS","SSSS","Z","utcOffset","ZZ","zoneAbbr","zz","zoneName","unix","lists","DDDD","_monthsShort","monthName","regex","_monthsParse","_longMonthsParse","_shortMonthsParse","_weekdays","_weekdaysShort","_weekdaysMin","weekdayName","_weekdaysParse","_longDateFormat","LTS","LT","L","LL","LLL","LLLL","isLower","_calendar","sameDay","nextDay","nextWeek","lastDay","lastWeek","sameElse","calendar","_relativeTime","future","past","mm","hh","MM","yy","pastFuture","_ordinal","postformat","firstDayOfYear","_invalidDate","ret","parseIso","diffRes","isDuration","inp","version","relativeTimeThreshold","limit","defineLocale","_abbr","abbr","langData","flags","parseZone","isDSTShifted","parsingFlags","invalidAt","keepLocalTime","_dateUtcOffset","inputString","asFloat","that","zoneDiff","humanize","fromNow","sod","startOf","isDST","getDay","endOf","inputMs","isBetween","zone","localAdjust","_changeInProgress","isLocal","isUtcOffset","isUtc","hasAlignedHourOffset","isoWeeksInYear","weekInfo","newLocaleData","getTimezoneOffset","isoWeeks","toJSON","isUTC","withSuffix","toIsoString","asSeconds","asMilliseconds","asMinutes","asHours","asDays","asWeeks","asMonths","asYears","ordinalParse","require","noGlobal","graphToggleSmoothCurves","graph_toggleSmooth","getElementById","graphRepositionNodes","showValueOfRange","graphGenerateOptions","optionsSpecific","radioButton1","radioButton2","checked","backupConstants","optionsDiv","switchConfigurations","radioButton","querySelector","tableId","table","constantsVariableName","valueId","rangeValue","_overWriteGraphConstants","RepulsionMixin","HierarchialRepulsionMixin","BarnesHutMixin","_toggleBarnesHut","barnesHutTree","_initializeForceCalculation","_calculateForces","_calculateGravitationalForces","_calculateNodeForces","_calculateSpringForcesWithSupport","_calculateHierarchicalSpringForces","_calculateSpringForces","supportNodeId","gravity","gravityForce","edgeLength","springForce","combinedClusterSize","node1","node2","node3","_calculateSpringForce","physicsConfiguration","hierarchicalLayoutDirections","parentElement","rangeElement","radioButton3","graph_repositionNodes","graph_generateOptions","dynamicSmoothCurves","nameArray","webpackContext","req","resolve","repulsingForce","a_base","minimumDistance","steepness","springFx","springFy","totalFx","totalFy","correctionFx","correctionFy","nodeCount","_formBarnesHutTree","_getForceContribution","children","NW","NE","SW","SE","parentBranch","childrenCount","centerOfMass","calcSize","MAX_VALUE","sizeDiff","minimumTreeSize","rootSize","halfRootSize","_splitBranch","_placeInTree","_updateBranchMass","totalMass","totalMassInv","biggestSize","skipMassUpdate","_placeInRegion","region","containedNode","_insertRegion","childSize","_drawTree","_drawBranch","branch","webpackPolyfill","paths"],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"vis.map","sources":["./dist/vis.js"],"names":["root","factory","exports","module","define","amd","this","modules","__webpack_require__","moduleId","installedModules","id","loaded","call","m","c","p","util","DOMutil","DataSet","DataView","Queue","Graph3d","graph3d","Camera","Filter","Point2d","Point3d","Slider","StepNumber","Timeline","Graph2d","timeline","DateUtil","DataStep","Range","stack","TimeStep","components","items","Item","BackgroundItem","BoxItem","PointItem","RangeItem","Component","CurrentTime","CustomTime","DataAxis","GraphGroup","Group","BackgroundGroup","ItemSet","Legend","LineGraph","TimeAxis","Network","network","Edge","Groups","Images","Node","Popup","dotparser","gephiParser","Graph","Error","moment","hammer","isNumber","object","Number","isString","String","isDate","Date","match","ASPDateRegex","exec","isNaN","parse","isDataTable","google","visualization","DataTable","randomUUID","S4","Math","floor","random","toString","extend","a","i","len","arguments","length","other","prop","hasOwnProperty","selectiveExtend","props","Array","isArray","selectiveDeepExtend","b","TypeError","constructor","Object","undefined","deepExtend","selectiveNotDeepExtend","indexOf","equalArray","convert","type","Boolean","valueOf","isMoment","toDate","getType","toISOString","value","getAbsoluteLeft","elem","getBoundingClientRect","left","window","pageXOffset","getAbsoluteTop","top","pageYOffset","addClassName","className","classes","split","push","join","removeClassName","index","splice","forEach","callback","toArray","array","updateProperty","key","addEventListener","element","action","listener","useCapture","navigator","userAgent","attachEvent","removeEventListener","detachEvent","preventDefault","event","returnValue","getTarget","target","srcElement","nodeType","parentNode","option","asBoolean","defaultValue","asNumber","asString","asSize","asElement","hexToRGB","hex","shorthandRegex","replace","r","g","result","parseInt","RGBToHex","red","green","blue","slice","parseColor","color","isValidRGB","rgb","substr","isValidHex","hsv","hexToHSV","lighterColorHSV","h","s","v","min","darkerColorHSV","darkerColorHex","HSVToHex","lighterColorHex","background","border","highlight","hover","RGBToHSV","minRGB","maxRGB","max","d","hue","saturation","cssUtil","cssText","styles","style","trim","parts","keys","map","addCssText","currentStyles","newStyles","removeCssText","removeStyles","HSVToRGB","f","q","t","isOk","test","selectiveBridgeObject","fields","referenceObject","objectTo","create","bridgeObject","mergeOptions","mergeTarget","options","enabled","binarySearchCustom","orderedItems","searchFunction","field","field2","maxIterations","iteration","low","high","middle","item","searchResult","binarySearchValue","sidePreference","prevValue","nextValue","easeInOutQuad","start","end","duration","change","easingFunctions","linear","easeInQuad","easeOutQuad","easeInCubic","easeOutCubic","easeInOutCubic","easeInQuart","easeOutQuart","easeInOutQuart","easeInQuint","easeOutQuint","easeInOutQuint","prepareElements","JSONcontainer","elementType","redundant","used","cleanupElements","removeChild","getSVGElement","svgContainer","shift","document","createElementNS","appendChild","getDOMElement","DOMContainer","insertBefore","createElement","drawPoint","x","y","group","point","drawPoints","setAttributeNS","size","drawBar","width","height","rect","data","_options","_data","_fieldId","fieldId","_type","_subscribers","add","setOptions","prototype","queue","_queue","destroy","on","subscribers","subscribe","off","filter","unsubscribe","_trigger","params","senderId","concat","subscriber","addedIds","me","_addItem","columns","_getColumnNames","row","rows","getNumberOfRows","col","cols","getValue","update","updatedIds","updatedData","addOrUpdate","_updateItem","get","ids","firstType","returnType","allowedValues","itemId","_getItem","order","_sort","_filterFields","_appendRow","getIds","getDataSet","mappedItems","filteredItem","name","sort","av","bv","remove","removedId","removedIds","_remove","clear","maxField","itemField","minField","distinct","values","fieldType","count","exists","types","raw","converted","JSON","stringify","dataTable","getNumberOfColumns","getColumnId","getColumnLabel","addRow","setValue","_ids","_onEvent","apply","setData","refresh","newIds","added","removed","viewOptions","getArguments","defaultFilter","dataSet","updated","delay","Infinity","_timeout","_extended","_flushIfNeeded","flush","methods","original","method","args","fn","context","entry","clearTimeout","setTimeout","container","SyntaxError","containerElement","margin","defaultXCenter","defaultYCenter","xLabel","yLabel","zLabel","passValueFn","xValueLabel","yValueLabel","zValueLabel","filterLabel","legendLabel","STYLE","DOT","showPerspective","showGrid","keepAspectRatio","showShadow","showGrayBottom","showTooltip","verticalRatio","animationInterval","animationPreload","camera","eye","dataPoints","colX","colY","colZ","colValue","colFilter","xMin","xStep","xMax","yMin","yStep","yMax","zMin","zStep","zMax","valueMin","valueMax","xBarWidth","yBarWidth","colorAxis","colorGrid","colorDot","colorDotBorder","getMouseX","clientX","targetTouches","getMouseY","clientY","Emitter","_setScale","scale","z","xCenter","yCenter","zCenter","setArmLocation","_convert3Dto2D","point3d","translation","_convertPointToTranslation","_convertTranslationToScreen","ax","ay","az","cx","getCameraLocation","cy","cz","sinTx","sin","getCameraRotation","cosTx","cos","sinTy","cosTy","sinTz","cosTz","dx","dy","dz","bx","by","ex","ey","ez","getArmLength","xcenter","frame","canvas","clientWidth","ycenter","_setBackgroundColor","backgroundColor","fill","stroke","strokeWidth","borderColor","borderWidth","borderStyle","BAR","BARCOLOR","BARSIZE","DOTLINE","DOTCOLOR","DOTSIZE","GRID","LINE","SURFACE","_getStyleNumber","styleName","_determineColumnIndexes","counter","column","getDistinctValues","distinctValues","getColumnRange","minMax","_dataInitialize","rawData","_onChange","dataFilter","setOnLoadCallback","redraw","withBars","defaultXBarWidth","dataX","defaultYBarWidth","dataY","xRange","defaultXMin","defaultXMax","defaultXStep","yRange","defaultYMin","defaultYMax","defaultYStep","zRange","defaultZMin","defaultZMax","defaultZStep","valueRange","defaultValueMin","defaultValueMax","_getDataPoints","obj","sortNumber","dataMatrix","xIndex","yIndex","trans","screen","bottom","pointRight","pointTop","pointCross","hasChildNodes","firstChild","position","overflow","noCanvas","fontWeight","padding","innerHTML","onmousedown","_onMouseDown","ontouchstart","_onTouchStart","onmousewheel","_onWheel","ontooltip","_onTooltip","onkeydown","setSize","_resizeCanvas","clientHeight","animationStart","slider","play","animationStop","stop","_resizeCenter","charAt","parseFloat","setCameraPosition","pos","horizontal","vertical","setArmRotation","distance","setArmLength","getCameraPosition","getArmRotation","_readData","_redrawFilter","animationAutoStart","cameraPosition","styleNumber","tooltip","showAnimationControls","_redrawSlider","_redrawClear","_redrawAxis","_redrawDataGrid","_redrawDataLine","_redrawDataBar","_redrawDataDot","_redrawInfo","_redrawLegend","ctx","getContext","clearRect","widthMin","widthMax","dotSize","right","lineWidth","font","ymin","ymax","_hsv2rgb","strokeStyle","beginPath","moveTo","lineTo","strokeRect","fillStyle","closePath","gridLineLen","step","getCurrent","next","textAlign","textBaseline","fillText","label","visible","setValues","setPlayInterval","onchange","getIndex","selectValue","setOnChangeCallback","lineStyle","getLabel","getSelectedValue","from","to","prettyStep","text","xText","yText","zText","offset","xOffset","yOffset","xMin2d","xMax2d","gridLenX","gridLenY","textMargin","armAngle","H","S","V","R","G","B","C","Hi","X","abs","cross","topSideVisible","zAvg","transBottom","dist","sortDepth","aDiff","subtract","bDiff","crossproduct","crossProduct","radius","arc","PI","j","surface","corners","xWidth","yWidth","surfaces","center","avg","transCenter","diff","leftButtonDown","_onMouseUp","which","button","touchDown","startMouseX","startMouseY","startStart","startEnd","startArmRotation","cursor","onmousemove","_onMouseMove","onmouseup","diffX","diffY","horizontalNew","verticalNew","snapAngle","snapValue","round","parameters","emit","boundingRect","mouseX","mouseY","tooltipTimeout","_hideTooltip","dataPoint","_dataPointFromXY","_showTooltip","ontouchmove","_onTouchMove","ontouchend","_onTouchEnd","delta","wheelDelta","detail","oldLength","newLength","_insideTriangle","triangle","sign","as","bs","cs","distMax","closestDataPoint","closestDist","triangle1","triangle2","distX","distY","sqrt","content","line","dot","dom","borderRadius","boxShadow","borderLeft","contentWidth","offsetWidth","contentHeight","offsetHeight","lineHeight","dotWidth","dotHeight","armLocation","armRotation","armLength","cameraLocation","cameraRotation","calculateCameraOrientation","rot","graph","onLoadCallback","loadInBackground","isLoaded","getLoadedProgress","getColumn","getValues","dataView","progress","sub","sum","prev","bar","MozBorderRadius","slide","onclick","togglePlay","onChangeCallback","playTimeout","playInterval","playLoop","setIndex","playNext","interval","clearInterval","getPlayInterval","setPlayLoop","doLoop","onChange","indexToLeft","startClientX","startSlideX","leftToIndex","_start","_end","_step","precision","_current","setRange","setStep","calculatePrettyStep","log10","log","LN10","step1","pow","step2","step5","toPrecision","getStep","groups","forthArgument","defaultOptions","autoResize","orientation","maxHeight","minHeight","_create","body","domProps","emitter","bind","hiddenDates","snap","toScreen","_toScreen","toGlobalScreen","_toGlobalScreen","toTime","_toTime","toGlobalTime","_toGlobalTime","range","timeAxis","currentTime","customTime","itemSet","itemsData","groupsData","setGroups","setItems","Core","newDataSet","initialLoad","dataRange","_getDataRange","setWindow","animate","fit","setSelection","focus","getSelection","itemData","e","getItemRange","dataset","minItem","maxStartItem","maxEndItem","linegraph","getLegend","groupId","isGroupVisible","visibility","convertHiddenOptions","repeat","dateItem","updateHiddenDates","centerContainer","totalRange","pixelTime","startDate","endDate","_d","runUntil","clone","day","dayOfYear","year","dayOffset","date","month","console","removeDuplicates","startHidden","isHidden","endHidden","rangeStart","rangeEnd","hidden","startToFront","endToFront","_applyRange","safeDates","printDates","dates","stepOverHiddenDates","timeStep","previousTime","stepInHidden","currentValue","current","newValue","switchedYear","switchedMonth","switchedDay","time","conversion","getHiddenDurationBetween","correctTimeForHidden","hiddenDuration","totalDuration","partialDuration","accumulatedHiddenDuration","getAccumulatedHiddenDuration","newTime","getHiddenDurationBefore","timeOffset","requiredDuration","previousPoint","snapAwayFromHidden","direction","correctionEnabled","minimumStep","containerHeight","customRange","alignZeros","autoScale","stepIndex","marginStart","marginEnd","deadSpace","majorSteps","minorSteps","setMinimumStep","setFirst","safeSize","minimumStepValue","orderOfMagnitude","minorStepIdx","magnitudefactor","solutionFound","stepSize","niceStart","niceEnd","roundToMinor","marginRange","rounded","hasNext","previous","decimals","exp","cnt","isMajor","now","hours","minutes","seconds","milliseconds","deltaDifference","scaleOffset","moveable","zoomable","zoomMin","zoomMax","touch","animateTimer","_onDragStart","_onDrag","_onDragEnd","_onHold","_onMouseWheel","_onTouch","_onPinch","validateDirection","getPointer","pageX","pageY","hammerUtil","byUser","_cancelAnimation","initStart","initEnd","initTime","anyChanged","dragging","done","changed","newStart","newEnd","getRange","totalHidden","previousDelta","allowDragging","gesture","deltaX","deltaY","diffRange","safeStart","safeEnd","fakeGesture","pointer","pointerDate","_pointerToDate","zoom","touches","centerDate","hiddenDurationBefore","hiddenDurationAfter","move","EPSILON","orderByStart","orderByEnd","aTime","bTime","force","iMax","axis","collidingItem","jj","collision","nostack","subgroups","newTop","subgroup","format","FORMAT","minorLabels","millisecond","second","minute","hour","weekday","majorLabels","setFormat","defaultFormat","first","setFullYear","getFullYear","setMonth","setDate","setHours","setMinutes","setSeconds","setMilliseconds","getMilliseconds","getSeconds","getMinutes","getHours","getDate","getMonth","setScale","setAutoScale","enable","stepYear","stepMonth","stepDay","stepHour","stepMinute","stepSecond","stepMillisecond","getLabelMinor","getLabelMajor","getClassName","even","today","isSame","currentWeek","currentMonth","currentYear","locale","lang","toLowerCase","parent","selected","displayed","dirty","Hammer","select","unselect","setParent","hide","show","isVisible","repositionX","repositionY","_repaintDeleteButton","anchor","editable","deleteButton","title","removeFromDataSet","stopPropagation","_updateContents","template","Element","_updateTitle","removeAttribute","_updateDataAttributes","dataAttributes","attributes","setAttribute","_updateStyle","emptyContent","baseClassName","box","getComputedStyle","onTop","itemSubgroup","subgroupIndex","foreground","align","itemSetHeight","marginLeft","maxWidth","_repaintDragLeft","_repaintDragRight","contentLeft","parentWidth","boxWidth","updateTime","dragLeft","dragLeftItem","dragRight","dragRightItem","_isResized","resized","_previousWidth","_previousHeight","showCurrentTime","locales","backgroundVertical","toUpperCase","substring","currentTimeTimer","setCurrentTime","getCurrentTime","showCustomTime","eventParams","drag","prevent_default","setCustomTime","getCustomTime","svg","linegraphOptions","showMinorLabels","showMajorLabels","icons","majorLinesOffset","minorLinesOffset","labelOffsetX","labelOffsetY","iconWidth","linegraphSVG","DOMelements","lines","labels","conversionFactor","minWidth","stepPixels","stepPixelsForced","zeroCrossing","lineOffset","master","svgElements","iconsRemoved","amountOfGroups","lineContainer","scrollTop","addGroup","graphOptions","updateGroup","removeGroup","display","_redrawGroupIcons","iconHeight","iconOffset","drawIcon","_cleanupIcons","backgroundHorizontal","activeGroups","_calculateCharSize","minorLabelHeight","minorCharHeight","majorLabelHeight","majorCharHeight","minorLineWidth","minorLineHeight","majorLineWidth","majorLineHeight","_redrawLabels","_redrawTitle","amountOfSteps","stepDifference","zeroStepDifference","valueAtZero","marginStartPos","maxLabelSize","_redrawLabel","_redrawLine","titleWidth","titleCharHeight","convertValue","invertedValue","convertedValue","characterHeight","largestWidth","majorCharWidth","minorCharWidth","textMinor","createTextNode","measureCharMinor","textMajor","measureCharMajor","textTitle","measureCharTitle","titleCharWidth","groupsUsingDefaultStyles","usingDefaultStyle","zeroPosition","Line","Bar","Points","setZeroPosition","catmullRom","parametrization","alpha","SVGcontainer","path","fillPath","fillHeight","outline","shaded","barWidth","bar1Height","bar2Height","icon","yAxisOrientation","getYRange","groupData","draw","framework","subgroupOrderer","subgroupOrder","visibleItems","byStart","byEnd","checkRangedItems","inner","marker","getLabelWidth","restack","_updateVisibleItems","markerHeight","lastMarkerHeight","_calculateHeight","offsetTop","offsetLeft","ii","resetSubgroups","labelSet","orderSubgroups","_checkIfVisible","sortArray","sortField","removeItem","startArray","endArray","oldVisibleItems","visibleItemsLookup","lowerBound","upperBound","_checkIfVisibleWithReference","initialPosByStart","_traceVisible","initialPosByEnd","initialPos","breakCondition","groupOrder","selectable","onAdd","onUpdate","onMove","onRemove","onMoving","itemOptions","itemListeners","_onAdd","_onUpdate","_onRemove","groupListeners","_onAddGroups","_onUpdateGroups","_onRemoveGroups","groupIds","selection","stackDirty","touchParams","UNGROUPED","BACKGROUND","_updateUngrouped","backgroundGroup","_onSelectItem","_onMultiSelectItem","_onAddItem","addCallback","Function","markDirty","getVisibleItems","rawVisibleItems","_deselect","_orderGroups","visibleInterval","zoomed","lastVisibleInterval","lastWidth","firstGroup","_firstGroup","firstMargin","nonFirstMargin","groupMargin","groupResized","firstGroupIndex","firstGroupId","ungrouped","_getGroupId","getLabelSet","oldItemsData","getItems","_order","getGroups","_getType","_removeItem","groupOptions","oldGroupId","oldGroup","_constructByEndArray","itemFromTarget","initialX","itemProps","newProps","initial","groupFromTarget","_updateItemProps","_moveToGroup","changes","ctrlKey","srcEvent","shiftKey","oldSelection","newSelection","xAbs","newItem","_getItemRange","_item","itemSetFromTarget","side","iconSize","iconSpacing","textArea","scrollableHeight","drawLegendIcons","paddingTop","defaultGroup","sampling","graphHeight","barChart","handleOverlap","dataAxis","legend","abortedGraphUpdate","updateSVGheight","updateSVGheightOnResize","lastStart","COUNTER","BarGraphFunctions","yAxisLeft","yAxisRight","legendLeft","legendRight","_updateAllGroupData","_updateGroup","groupsContent","ungroupedCounter","forceGraphUpdate","_updateGraph","rangePerPixelInv","preprocessedGroupData","processedGroupData","groupRanges","changeCalled","minDate","maxDate","_getRelevantData","_applySampling","_convertXcoordinates","_getYRanges","_updateYAxis","MAX_CYCLES","_convertYcoordinates","dataContainer","guess","increment","amountOfPoints","xDistance","pointsPerPixel","ceil","sampledData","barCombinedDataLeft","barCombinedDataRight","getStackedBarYRange","minVal","maxVal","yAxisLeftUsed","yAxisRightUsed","minLeft","minRight","maxLeft","maxRight","ignore","_toggleAxisVisiblity","drawIcons","axisUsed","datapoints","xValue","yValue","extractedData","svgHeight","majorTexts","minorTexts","lineTop","parentChanged","foregroundNextSibling","nextSibling","backgroundNextSibling","_repaintLabels","timeLabelsize","cur","prevLine","xPrev","xFirstMajorLabel","_repaintMinorText","_repaintMajorText","_repaintMajorLine","_repaintMinorLine","leftTime","leftText","widthText","arr","pop","childNodes","nodeValue","_determineBrowserMethod","_initializeMixinLoaders","renderRefreshRate","renderTimestep","renderTime","physicsTime","runDoubleSpeed","physicsDiscreteStepsize","initializing","triggerFunctions","edit","editEdge","connect","del","nodes","mass","radiusMin","radiusMax","shape","image","fontColor","fontSize","fontFace","fontFill","fontStrokeWidth","fontStrokeColor","level","borderWidthSelected","edges","widthSelectionMultiplier","hoverWidth","labelAlignment","arrowScaleFactor","dash","gap","altLength","inheritColor","configurePhysics","physics","barnesHut","thetaInverted","gravitationalConstant","centralGravity","springLength","springConstant","damping","repulsion","nodeDistance","hierarchicalRepulsion","clustering","initialMaxNodes","clusterThreshold","reduceToNodes","chainThreshold","clusterEdgeThreshold","sectorThreshold","screenSizeThreshold","fontSizeMultiplier","maxFontSize","forceAmplification","distanceAmplification","edgeGrowth","nodeScaling","maxNodeSizeIncrements","activeAreaBoxSize","clusterLevelDifference","clusterByZoom","navigation","keyboard","speed","bindToWindow","dataManipulation","initiallyVisible","hierarchicalLayout","levelSeparation","nodeSpacing","layout","freezeForStabilization","smoothCurves","dynamic","roundness","maxVelocity","minVelocity","stabilize","stabilizationIterations","zoomExtentOnStabilize","dragNetwork","dragNodes","hideEdgesOnDrag","hideNodesOnDrag","constants","pixelRatio","hoverObj","controlNodesActive","navigationHammers","existing","_new","animationSpeed","animationEasingFunction","animating","easingTime","sourceScale","targetScale","sourceTranslation","targetTranslation","lockedOnNodeId","lockedOnNodeOffset","touchTime","images","setOnloadCallback","_redraw","xIncrement","yIncrement","zoomIncrement","_loadPhysicsSystem","_loadSectorSystem","_loadClusterSystem","_loadSelectionSystem","_loadHierarchySystem","_setTranslation","freezeSimulation","cachedFunctions","startedStabilization","stabilized","draggingNodes","calculationNodes","calculationNodeIndices","nodeIndices","canvasTopLeft","canvasBottomRight","pointerPosition","areaCenter","previousScale","nodesData","edgesData","nodesListeners","_addNodes","_updateNodes","_removeNodes","edgesListeners","_addEdges","_updateEdges","_removeEdges","moving","timer","_setupHierarchicalLayout","zoomExtent","startWithClustering","keycharm","MixinLoader","Activator","browserType","requiresTimeout","_getScriptPath","scripts","getElementsByTagName","src","_getRange","node","minY","maxY","minX","maxX","nodeId","boundingBox","_findCenter","animationOptions","initialZoom","disableStart","zoomLevel","numberOfNodes","factor","yDistance","xZoomLevel","yZoomLevel","animation","_updateNodeIndexList","_clearNodeIndexList","idx","_createManipulatorBar","dotData","DOTToGraph","gephi","gephiData","parseGephi","_setNodes","_setEdges","_putDataInSector","_resetLevels","_stabilize","onEdit","onEditEdge","onConnect","onDelete","editMode","newColorObj","groupname","clickToUse","activator","_createKeyBinds","_loadNavigationControls","_loadManipulationSystem","_configureSmoothCurves","_bindHammer","tabIndex","devicePixelRatio","webkitBackingStorePixelRatio","mozBackingStorePixelRatio","msBackingStorePixelRatio","oBackingStorePixelRatio","backingStorePixelRatio","setTransform","dispose","pinch","_onTap","_onDoubleTap","_onMouseMoveTitle","hammerFrame","_onRelease","reset","isActive","_moveUp","_yStopMoving","_moveDown","_moveLeft","_xStopMoving","_moveRight","_zoomIn","_stopZoom","_zoomOut","increaseClusterLevel","decreaseClusterLevel","forceAggregateHubs","normalizeClusterLevels","_deleteSelected","_cleanupPhysicsConfiguration","_recursiveDOMDelete","DOMobject","_getPointer","pinched","_getScale","_handleTouch","_handleDragStart","_getNodeAt","_getTranslation","isSelected","_selectObject","nodeIds","objectId","selectionObj","xFixed","yFixed","_handleOnDrag","releaseNode","_XconvertDOMtoCanvas","_XconvertCanvasToDOM","_YconvertDOMtoCanvas","_YconvertCanvasToDOM","_handleDragEnd","_handleTap","_handleDoubleTap","_handleOnHold","_handleOnRelease","_zoom","scaleOld","preScaleDragPointer","DOMtoCanvas","scaleFrac","tx","ty","updateClustersDefault","postScaleDragPointer","canvasToDOM","popupObj","_checkHidePopup","checkShow","_checkShowPopup","popupTimer","edgeId","_getEdgeAt","_hoverObject","_blurObject","lastPopupNode","nodeUnderCursor","overlappingNodes","isOverlappingWith","getTitle","overlappingEdges","edge","connected","popup","setPosition","setText","emitEvent","oldWidth","oldHeight","oldNodesData","_updateSelection","angle","_updateCalculationNodes","_reconnectEdges","_updateValueRange","updateLabels","changedData","setProperties","properties","oldEdgesData","oldEdge","disconnect","showInternalIds","_createBezierNodes","via","sectors","dynamicEdges","setValueRange","w","save","translate","_doInAllSectors","restore","offsetX","offsetY","_drawNodes","alwaysShow","setScaleAndPos","inArea","sMax","_drawEdges","_drawControlNodes","_freezeDefinedNodes","_physicsTick","_restoreFrozenNodes","fixedData","_isMoving","vmin","isMoving","_discreteStepNodes","nodesPresent","discreteStepLimited","discreteStep","vminCorrected","_revertPhysicsState","revertPosition","_revertPhysicsTick","_doInAllActiveSectors","_doInSupportSector","mainMovingStatus","supportMovingStatus","mainMoving","_animationStep","_handleNavigation","startTime","renderStartTime","requestAnimationFrame","mozRequestAnimationFrame","webkitRequestAnimationFrame","msRequestAnimationFrame","iterations","toggleFreeze","parentEdgeId","internalMultiplier","positionBezierNode","mixin","storePosition","storePositions","dataArray","allowedToMoveX","allowedToMoveY","getPositions","focusOnNode","nodePosition","lockedOnNode","easingFunction","animateView","locked","_transitionRedraw","viewCenter","distanceFromCenter","_classicRedraw","_lockedRedraw","active","getScale","getCenterCoordinates","getBoundingBox","networkConstants","fromId","toId","widthSelected","labelDimensions","yLine","dirtyLabel","fromBackup","toBackup","originalFromId","originalToId","widthFixed","lengthFixed","controlNodesEnabled","controlNodes","positions","connectedNode","_drawLine","_drawArrow","_drawArrowCenter","_drawDashLine","attachEdge","detachEdge","xFrom","yFrom","xTo","yTo","xObj","yObj","_getDistanceToEdge","_getColor","colorObj","_getLineWidth","_line","midpointX","midpointY","_pointOnLine","_label","resize","_circle","_pointOnCircle","networkScaleInv","_getViaCoordinates","xVia","yVia","quadraticCurveTo","lineCount","measureText","_rotateForLabelAlignment","_drawLabelRect","_drawLabelText","angleInDegrees","atan2","rotate","lineMargin","fillRect","lineJoin","strokeText","setLineDash","pattern","lineDashOffset","lineCap","dashedLine","percentage","arrow","_pointOnBezier","_findBorderPosition","distanceToBorder","distanceToNodes","difference","threshold","arrowPos","guidePos","edgeSegmentLength","toBorderDist","toBorderPoint","x1","y1","x2","y2","x3","y3","lastX","lastY","minDistance","_getDistanceToLine","px","py","something","u","nodeIdFrom","nodeIdTo","getControlNodeFromPosition","getControlNodeToPosition","_enableControlNodes","_disableControlNodes","_getSelectedControlNode","fromDistance","toDistance","_restoreControlNodes","controlnodeFromPos","fromBorderDist","fromBorderPoint","controlnodeToPos","defaultIndex","DEFAULT","imageBroken","load","url","brokenUrl","img","Image","onload","onerror","error","imagelist","grouplist","reroutedEdges","fontDrawThreshold","horizontalAlignLeft","verticalAlignTop","baseRadiusValue","radiusFixed","preassignedLevel","hierarchyEnumerated","fx","fy","vx","vy","previousState","resetCluster","dynamicEdgesLength","clusterSession","clusterSizeWidthFactor","clusterSizeHeightFactor","clusterSizeRadiusFactor","growthIndicator","networkScale","formationScale","clusterSize","containedNodes","containedEdges","clusterSessions","originalLabel","triggerFunction","groupObj","imageObj","brokenImage","_drawDatabase","_resizeDatabase","_drawBox","_resizeBox","_drawCircle","_resizeCircle","_drawEllipse","_resizeEllipse","_drawImage","_resizeImage","_drawCircularImage","_resizeCircularImage","_drawText","_resizeText","_drawDot","_resizeShape","_drawSquare","_drawTriangle","_drawTriangleDown","_drawStar","_reset","clearSizeCache","_setForce","_addForce","storeState","isFixed","velocity","getDistance","_drawImageAtPosition","globalAlpha","drawImage","_drawImageLabel","getTextSize","_swapToImageResizeWhenImageLoaded","diameter","centerX","centerY","_drawRawCircle","circle","clip","textSize","clusterLineWidth","selectionLineWidth","roundRect","database","defaultSize","ellipse","_drawShape","radiusMultiplier","baseline","labelUnderNode","inView","clearVelocity","updateVelocity","massBeforeClustering","energyBefore","styleAttr","fontFamily","WebkitBorderRadius","whiteSpace","parseDOT","parseGraph","nextPreview","isAlphaNumeric","regexAlphaNumeric","merge","o","addNode","graphs","attr","addEdge","createEdge","getToken","tokenType","TOKENTYPE","NULL","token","isComment","DELIMITER","c2","DELIMITERS","IDENTIFIER","newSyntaxError","UNKNOWN","chop","strict","parseStatements","parseStatement","subgraph","parseSubgraph","parseEdge","parseAttributeStatement","parseNodeStatement","subgraphs","parseAttributeList","message","maxLength","forEach2","array1","array2","elem1","elem2","graphData","dotNode","graphNode","convertEdge","dotEdge","graphEdge","subEdge","{","}","[","]",";","=",",","->","--","gephiJSON","allowedToMove","gEdges","gNodes","gEdge","source","gNode","leftContainer","rightContainer","shadowTop","shadowBottom","shadowTopLeft","shadowBottomLeft","shadowTopRight","shadowBottomRight","_redrawTimer","listeners","events","scrollTopMin","redrawCount","_initAutoResize","component","_stopAutoResize","what","getWindow","borderRootHeight","borderRootWidth","autoHeight","centerWidth","_updateScrollTop","visibilityTop","visibilityBottom","MAX_REDRAWS","repaint","_startAutoResize","_onResize","lastHeight","watchTimer","setInterval","initialScrollTop","oldScrollTop","_getScrollTop","newScrollTop","_setScrollTop","eventType","getTouchList","collectEventData","custom","_catmullRom","_linear","dFill","_catmullRomUniform","p0","p1","p2","p3","bp1","bp2","normalization","d1","d2","d3","A","N","M","d3powA","d2powA","d3pow2A","d2pow2A","d1pow2A","d1powA","Bargraph","barCombinedData","coreDistance","drawData","combinedData","intersections","barPoints","_getDataIntersections","heightOffset","_getSafeDrawData","nextKey","amount","resolved","prevKey","accumulated","groupLabel","_getStackedBarYRange","xpos","PhysicsMixin","ClusterMixin","SectorsMixin","SelectionMixin","ManipulationMixin","NavigationMixin","HierarchicalLayoutMixin","_loadMixin","sourceVariable","mixinFunction","_clearMixin","_loadSelectedForceSolver","_loadPhysicsConfiguration","hubThreshold","activeSector","drawingNode","blockConnectingEdgeSelection","forceAppendSelection","manipulationDiv","editModeDiv","closeDiv","_cleanNavigation","_loadNavigationElements","overlay","_onTapOverlay","windowHammer","_hasParent","deactivate","escListener","activate","unbind","back","editNode","addDescription","edgeDescription","editEdgeDescription","createEdgeError","deleteClusterError","CanvasRenderingContext2D","square","s2","ir","triangleDown","star","n","r2d","kappa","ox","oy","xe","ye","xm","ym","bezierCurveTo","wEllipse","hEllipse","ymb","yeb","xt","yt","xi","yi","xl","yl","xr","yr","dashArray","dashLength","dashCount","slope","distRemaining","dashIndex","_callbacks","once","self","removeListener","removeAllListeners","callbacks","cb","hasListeners","__WEBPACK_AMD_DEFINE_FACTORY__","__WEBPACK_AMD_DEFINE_ARRAY__","__WEBPACK_AMD_DEFINE_RESULT__","_exportFunctions","_bound","keydown","keyup","_keys","fromCharCode","code","down","handleEvent","up","keyCode","bound","bindAll","getKey","newBindings","global","dfl","hasOwnProp","defaultParsingFlags","empty","unusedTokens","unusedInput","charsLeftOver","nullInput","invalidMonth","invalidFormat","userInvalidated","iso","printMsg","msg","suppressDeprecationWarnings","warn","deprecate","firstTime","deprecateSimple","deprecations","padToken","func","leftZeroFill","ordinalizeToken","period","localeData","ordinal","monthDiff","anchor2","adjust","wholeMonthDiff","meridiemFixWrap","meridiem","isPm","meridiemHour","isPM","Locale","Moment","config","skipOverflow","checkOverflow","copyConfig","updateInProgress","updateOffset","Duration","normalizedInput","normalizeObjectUnits","years","quarters","quarter","months","weeks","week","days","_milliseconds","_days","_months","_locale","_bubble","val","_isAMomentObject","_i","_f","_l","_strict","_tzm","_isUTC","_offset","_pf","momentProperties","absRound","number","targetLength","forceSign","output","positiveMomentsDifference","base","res","isAfter","momentsDifference","makeAs","isBefore","createAdder","dur","tmp","addOrSubtractDurationFromMoment","mom","isAdding","setTime","rawSetter","rawGetter","rawMonthSetter","input","compareArrays","dontConvert","lengthDiff","diffs","toInt","normalizeUnits","units","lowered","unitAliases","camelFunctions","inputObject","normalizedProp","makeList","setter","getter","results","utc","set","argumentForCoercion","coercedNumber","isFinite","daysInMonth","UTC","getUTCDate","weeksInYear","dow","doy","weekOfYear","daysInYear","isLeapYear","_a","MONTH","DATE","YEAR","HOUR","MINUTE","SECOND","MILLISECOND","_overflowDayOfYear","isValid","_isValid","getTime","bigHour","normalizeLocale","chooseLocale","names","loadLocale","oldLocale","hasModule","model","local","removeFormattingTokens","makeFormatFunction","formattingTokens","formatTokenFunctions","formatMoment","expandFormat","formatFunctions","invalidDate","replaceLongDateFormatTokens","longDateFormat","localFormattingTokens","lastIndex","getParseRegexForToken","parseTokenOneDigit","parseTokenThreeDigits","parseTokenFourDigits","parseTokenOneToFourDigits","parseTokenSignedNumber","parseTokenSixDigits","parseTokenOneToSixDigits","parseTokenTwoDigits","parseTokenOneToThreeDigits","parseTokenWord","_meridiemParse","parseTokenOffsetMs","parseTokenTimestampMs","parseTokenTimezone","parseTokenT","parseTokenDigits","parseTokenOneOrTwoDigits","_ordinalParse","_ordinalParseLenient","RegExp","regexpEscape","unescapeFormat","utcOffsetFromString","string","possibleTzMatches","tzChunk","parseTimezoneChunker","addTimeToArrayFromToken","datePartArray","monthsParse","_dayOfYear","parseTwoDigitYear","_meridiem","_useUTC","weekdaysParse","_w","invalidWeekday","dayOfYearFromWeekInfo","weekYear","temp","GG","W","E","_week","gg","dayOfYearFromWeeks","dateFromConfig","currentDate","yearToUse","currentDateArray","makeUTCDate","getUTCMonth","_nextDay","makeDate","setUTCMinutes","getUTCMinutes","dateFromObject","getUTCFullYear","makeDateFromStringAndFormat","ISO_8601","parseISO","parsedInput","tokens","skipped","stringLength","totalParsedInputLength","matched","p4","makeDateFromStringAndArray","tempConfig","bestMoment","scoreToBeat","currentScore","NaN","score","l","isoRegex","isoDates","isoTimes","makeDateFromString","createFromInputFallback","makeDateFromInput","aspNetJsonRegex","ms","setUTCFullYear","parseWeekday","substituteTimeAgo","withoutSuffix","isFuture","relativeTime","posNegDuration","relativeTimeThresholds","firstDayOfWeek","firstDayOfWeekOfYear","adjustedMoment","daysToDayOfWeek","daysToAdd","getUTCDay","makeMoment","invalid","preparse","pickBy","moments","dayOfMonth","unit","makeAccessor","keepTime","daysToYears","yearsToDays","makeDurationGetter","makeGlobal","shouldDeprecate","ender","oldGlobalMoment","globalScope","VERSION","aspNetTimeSpanJsonRegex","isoDurationRegex","isoFormat","unitMillisecondFactors","Milliseconds","Seconds","Minutes","Hours","Days","Months","Years","D","Q","DDD","dayofyear","isoweekday","isoweek","weekyear","isoweekyear","ordinalizeTokens","paddedTokens","MMM","monthsShort","MMMM","dd","weekdaysMin","ddd","weekdaysShort","dddd","weekdays","isoWeek","YY","YYYY","YYYYY","YYYYYY","gggg","ggggg","isoWeekYear","GGGG","GGGGG","isoWeekday","SS","SSS","SSSS","Z","utcOffset","ZZ","zoneAbbr","zz","zoneName","unix","lists","DDDD","_monthsShort","monthName","regex","_monthsParse","_longMonthsParse","_shortMonthsParse","_weekdays","_weekdaysShort","_weekdaysMin","weekdayName","_weekdaysParse","_longDateFormat","LTS","LT","L","LL","LLL","LLLL","isLower","_calendar","sameDay","nextDay","nextWeek","lastDay","lastWeek","sameElse","calendar","_relativeTime","future","past","mm","hh","MM","yy","pastFuture","_ordinal","postformat","firstDayOfYear","_invalidDate","ret","parseIso","diffRes","isDuration","inp","version","relativeTimeThreshold","limit","defineLocale","_abbr","abbr","langData","flags","parseZone","isDSTShifted","parsingFlags","invalidAt","keepLocalTime","_dateUtcOffset","inputString","asFloat","that","zoneDiff","humanize","fromNow","sod","startOf","isDST","getDay","endOf","inputMs","isBetween","zone","localAdjust","_changeInProgress","isLocal","isUtcOffset","isUtc","hasAlignedHourOffset","isoWeeksInYear","weekInfo","newLocaleData","getTimezoneOffset","isoWeeks","toJSON","isUTC","withSuffix","toIsoString","asSeconds","asMilliseconds","asMinutes","asHours","asDays","asWeeks","asMonths","asYears","ordinalParse","require","noGlobal","setup","READY","Event","determineEventTypes","Utils","each","gestures","Detection","register","onTouch","DOCUMENT","EVENT_MOVE","detect","EVENT_END","Instance","defaults","behavior","userSelect","touchAction","touchCallout","contentZooming","userDrag","tapHighlightColor","HAS_POINTEREVENTS","pointerEnabled","msPointerEnabled","HAS_TOUCHEVENTS","IS_MOBILE","NO_MOUSEEVENTS","CALCULATE_INTERVAL","EVENT_TYPES","DIRECTION_DOWN","DIRECTION_LEFT","DIRECTION_UP","DIRECTION_RIGHT","POINTER_MOUSE","POINTER_TOUCH","POINTER_PEN","EVENT_START","EVENT_RELEASE","EVENT_TOUCH","plugins","utils","dest","handler","iterator","inStr","find","inArray","hasParent","getCenter","getVelocity","deltaTime","getAngle","touch1","touch2","getDirection","getRotation","isVertical","setPrefixedCss","toggle","prefixes","toCamelCase","toggleBehavior","falseFn","onselectstart","ondragstart","str","preventMouseEvents","started","shouldDetect","hook","onTouchHandler","ev","triggerType","srcType","isPointer","isMouse","buttons","PointerEvent","matchType","updatePointer","doDetect","touchList","touchListLength","triggerChange","trigger","changedLength","changedTouches","evData","identifiers","identifier","pointerType","timeStamp","preventManipulation","stopDetect","pointers","touchlist","pointerEvent","pointerId","pt","MSPOINTER_TYPE_MOUSE","MSPOINTER_TYPE_TOUCH","MSPOINTER_TYPE_PEN","detection","stopped","startDetect","inst","eventData","startEvent","lastEvent","lastCalcEvent","futureCalcEvent","lastCalcData","extendEventData","instOptions","getCalculatedData","recalc","calcEv","calcData","velocityX","velocityY","interimAngle","interimDirection","startEv","lastEv","rotation","eventStartHandler","eventHandlers","createEvent","initEvent","dispatchEvent","state","eh","dragGesture","dragMaxTouches","triggered","dragMinDistance","startCenter","dragDistanceCorrection","dragLockToAxis","dragLockMinDistance","lastDirection","dragBlockVertical","dragBlockHorizontal","Drag","Gesture","holdGesture","holdTimeout","holdThreshold","Hold","Release","Swipe","swipeMinTouches","swipeMaxTouches","swipeVelocityX","swipeVelocityY","tapGesture","sincePrev","didDoubleTap","hasMoved","tapMaxDistance","tapMaxTime","doubleTapInterval","doubleTapDistance","tapAlways","Tap","Touch","preventMouse","transformGesture","scaleThreshold","rotationThreshold","transformMinScale","transformMinRotation","Transform","graphToggleSmoothCurves","graph_toggleSmooth","getElementById","graphRepositionNodes","showValueOfRange","repositionNodes","graphGenerateOptions","optionsSpecific","radioButton1","radioButton2","checked","backupConstants","optionsDiv","switchConfigurations","radioButton","querySelector","tableId","table","_restoreNodes","constantsVariableName","valueId","rangeValue","_overWriteGraphConstants","RepulsionMixin","HierarchialRepulsionMixin","BarnesHutMixin","_toggleBarnesHut","barnesHutTree","_initializeForceCalculation","clusterToFit","_calculateForces","_calculateGravitationalForces","_calculateNodeForces","_calculateSpringForcesWithSupport","_calculateHierarchicalSpringForces","_calculateSpringForces","supportNodes","supportNodeId","gravity","gravityForce","_sector","edgeLength","springForce","combinedClusterSize","node1","node2","node3","_calculateSpringForce","physicsConfiguration","hierarchicalLayoutDirections","parentElement","rangeElement","radioButton3","graph_repositionNodes","graph_generateOptions","dynamicSmoothCurves","nameArray","maxNumberOfNodes","reposition","maxLevels","openCluster","isMovingBeforeClustering","_nodeInActiveArea","_addSector","_expandClusterNode","_updateDynamicEdges","updateClusters","zoomDirection","recursive","doNotStart","amountOfNodes","_collapseSector","_formClusters","_openClusters","_openClustersBySize","_aggregateHubs","handleChains","chainPercentage","_getChainFraction","_reduceAmountOfChains","_getHubSize","_formClustersByHub","openAll","containedNodeId","childNode","_expelChildFromParent","_unselectAll","_releaseContainedEdges","_connectEdgeBackToChild","_validateEdges","othersPresent","childNodeId","_repositionBezierNodes","_formClustersByZoom","_forceClustersByZoom","minLength","_addToCluster","_clusterToSmallestNeighbour","smallestNeighbour","smallestNeighbourNode","neighbour","onlyEqual","_formClusterFromHub","hubNode","absorptionSizeOffset","allowCluster","edgesIdarray","amountOfInitialEdges","children","childrenIds","_addToContainedEdges","_connectEdgeToCluster","_containCircularEdgesFromNode","massBefore","correction","edgeToId","edgeFromId","k","_addToReroutedEdges","maxLevel","minLevel","clusterLevel","targetLevel","average","averageSquared","hubCounter","largestHub","variance","standardDeviation","fraction","reduceAmount","chains","total","_switchToSector","sectorId","sectorType","_switchToActiveSector","_switchToFrozenSector","_switchToSupportSector","_loadLatestSector","_previousSector","_setActiveSector","newId","_forgetLastSector","_createNewSector","_deleteActiveSector","_deleteFrozenSector","_freezeSector","_activateSector","_mergeThisWithFrozen","_collapseThisToSingleCluster","sector","unqiueIdentifier","previousSector","runFunction","argument","returnValues","_doInAllFrozenSectors","_drawSectorNodes","_drawAllSectorNodes","_getNodesOverlappingWith","_getAllNodesOverlappingWith","_pointerToPositionObject","positionObject","_getEdgesOverlappingWith","_getAllEdgesOverlappingWith","_addToSelection","_addToHover","_removeFromSelection","doNotTrigger","_unselectClusters","_getSelectedNodeCount","_getSelectedNode","_getSelectedEdge","_getSelectedEdgeCount","_getSelectedObjectCount","_selectionIsEmpty","_clusterInSelection","_selectConnectedEdges","_hoverConnectedEdges","_unselectConnectedEdges","append","highlightEdges","overrideSelectable","DOM","_manipulationReleaseOverload","_navigationReleaseOverload","getSelectedNodes","edgeIds","getSelectedEdges","idArray","selectNodes","RangeError","selectEdges","_clearManipulatorBar","manipulationDOM","_restoreOverloadedFunctions","functionName","_toggleEditMode","toolbar","boundFunction","edgeBeingEdited","selectedControlNode","_createAddNodeToolbar","_createAddEdgeToolbar","_editNode","_createEditEdgeToolbar","_addNode","_handleConnect","_finishConnect","_selectControlNode","_controlNodeDrag","_releaseControlNode","newNode","_editEdge","alert","targetNode","connectionEdge","connectFromId","_createEdge","defaultData","finalizedData","sourceNodeId","targetNodeId","selectedNodes","selectedEdges","navigationDivs","navigationDivActions","_stopMovement","_zoomExtent","hubsize","definedLevel","undefinedLevel","_changeConstants","_determineLevels","_determineLevelsDirected","distribution","_getDistribution","_placeNodesByHierarchy","minPos","_placeBranchNodes","maxCount","_setLevel","firstNode","_setLevelDirected","parentId","parentLevel","nodeMoved","webpackContext","req","resolve","repulsingForce","a_base","minimumDistance","steepness","springFx","springFy","totalFx","totalFy","correctionFx","correctionFy","nodeCount","_formBarnesHutTree","_getForceContribution","NW","NE","SW","SE","parentBranch","childrenCount","centerOfMass","calcSize","MAX_VALUE","sizeDiff","minimumTreeSize","rootSize","halfRootSize","_splitBranch","_placeInTree","_updateBranchMass","totalMass","totalMassInv","biggestSize","skipMassUpdate","_placeInRegion","region","containedNode","_insertRegion","childSize","_drawTree","_drawBranch","branch","webpackPolyfill","paths"],"mappings":"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"} \ No newline at end of file diff --git a/dist/vis.min.css b/dist/vis.min.css index 8478b04b..a390c40d 100644 --- a/dist/vis.min.css +++ b/dist/vis.min.css @@ -1 +1 @@ -.vis .overlay{position:absolute;top:0;left:0;width:100%;height:100%;z-index:10}.vis-active{box-shadow:0 0 10px #86d5f8}.vis [class*=span]{min-height:0;width:auto}.vis.timeline.root{position:relative;border:1px solid #bfbfbf;overflow:hidden;padding:0;margin:0;box-sizing:border-box}.vis.timeline .vispanel{position:absolute;padding:0;margin:0;box-sizing:border-box}.vis.timeline .vispanel.bottom,.vis.timeline .vispanel.center,.vis.timeline .vispanel.left,.vis.timeline .vispanel.right,.vis.timeline .vispanel.top{border:1px #bfbfbf}.vis.timeline .vispanel.center,.vis.timeline .vispanel.left,.vis.timeline .vispanel.right{border-top-style:solid;border-bottom-style:solid;overflow:hidden}.vis.timeline .vispanel.bottom,.vis.timeline .vispanel.center,.vis.timeline .vispanel.top{border-left-style:solid;border-right-style:solid}.vis.timeline .background{overflow:hidden}.vis.timeline .vispanel>.content{position:relative}.vis.timeline .vispanel .shadow{position:absolute;width:100%;height:1px;box-shadow:0 0 10px rgba(0,0,0,.8)}.vis.timeline .vispanel .shadow.top{top:-1px;left:0}.vis.timeline .vispanel .shadow.bottom{bottom:-1px;left:0}.vis.timeline .labelset{position:relative;overflow:hidden;box-sizing:border-box}.vis.timeline .labelset .vlabel{position:relative;left:0;top:0;width:100%;color:#4d4d4d;box-sizing:border-box;border-bottom:1px solid #bfbfbf}.vis.timeline .labelset .vlabel:last-child{border-bottom:none}.vis.timeline .labelset .vlabel .inner{display:inline-block;padding:5px}.vis.timeline .labelset .vlabel .inner.hidden{padding:0}.vis.timeline .itemset{position:relative;padding:0;margin:0;box-sizing:border-box}.vis.timeline .itemset .background,.vis.timeline .itemset .foreground{position:absolute;width:100%;height:100%;overflow:visible}.vis.timeline .axis{position:absolute;width:100%;height:0;left:0;z-index:1}.vis.timeline .foreground .group{position:relative;box-sizing:border-box;border-bottom:1px solid #bfbfbf}.vis.timeline .foreground .group:last-child{border-bottom:none}.vis.timeline .item{position:absolute;color:#1A1A1A;border-color:#97B0F8;border-width:1px;background-color:#D5DDF6;display:inline-block;padding:5px}.vis.timeline .item.selected{border-color:#FFC200;background-color:#FFF785;z-index:2}.vis.timeline .editable .item.selected{cursor:move}.vis.timeline .item.point.selected{background-color:#FFF785}.vis.timeline .item.box{text-align:center;border-style:solid;border-radius:2px}.vis.timeline .item.point{background:0 0}.vis.timeline .item.dot{position:absolute;padding:0;border-width:4px;border-style:solid;border-radius:4px}.vis.timeline .item.range{border-style:solid;border-radius:2px;box-sizing:border-box}.vis.timeline .item.background{overflow:hidden;border:none;background-color:rgba(213,221,246,.4);box-sizing:border-box;padding:0;margin:0}.vis.timeline .item.range .content{position:relative;display:inline-block;max-width:100%;overflow:hidden}.vis.timeline .item.background .content{position:absolute;display:inline-block;overflow:hidden;max-width:100%;margin:5px}.vis.timeline .item.line{padding:0;position:absolute;width:0;border-left-width:1px;border-left-style:solid}.vis.timeline .item .content{white-space:nowrap;overflow:hidden}.vis.timeline .item .delete{background:url(img/timeline/delete.png) top center no-repeat;position:absolute;width:24px;height:24px;top:0;right:-24px;cursor:pointer}.vis.timeline .item.range .drag-left{position:absolute;width:24px;height:100%;top:0;left:-4px;cursor:w-resize}.vis.timeline .item.range .drag-right{position:absolute;width:24px;height:100%;top:0;right:-4px;cursor:e-resize}.vis.timeline .timeaxis{position:relative;overflow:hidden}.vis.timeline .timeaxis.foreground{top:0;left:0;width:100%}.vis.timeline .timeaxis.background{position:absolute;top:0;left:0;width:100%;height:100%}.vis.timeline .timeaxis .text{position:absolute;color:#4d4d4d;padding:3px;white-space:nowrap}.vis.timeline .timeaxis .text.measure{position:absolute;padding-left:0;padding-right:0;margin-left:0;margin-right:0;visibility:hidden}.vis.timeline .timeaxis .grid.vertical{position:absolute;border-left:1px solid}.vis.timeline .timeaxis .grid.minor{border-color:#e5e5e5}.vis.timeline .timeaxis .grid.major{border-color:#bfbfbf}.vis.timeline .currenttime{background-color:#FF7F6E;width:2px;z-index:1}.vis.timeline .customtime{background-color:#6E94FF;width:2px;cursor:move;z-index:1}.vis.timeline .vispanel.background.horizontal .grid.horizontal{position:absolute;width:100%;height:0;border-bottom:1px solid}.vis.timeline .vispanel.background.horizontal .grid.minor{border-color:#e5e5e5}.vis.timeline .vispanel.background.horizontal .grid.major{border-color:#bfbfbf}.vis.timeline .dataaxis .yAxis.major{width:100%;position:absolute;color:#4d4d4d;white-space:nowrap}.vis.timeline .dataaxis .yAxis.major.measure{padding:0;margin:0;border:0;visibility:hidden;width:auto}.vis.timeline .dataaxis .yAxis.minor{position:absolute;width:100%;color:#bebebe;white-space:nowrap}.vis.timeline .dataaxis .yAxis.minor.measure{padding:0;margin:0;border:0;visibility:hidden;width:auto}.vis.timeline .dataaxis .yAxis.title{position:absolute;color:#4d4d4d;white-space:nowrap;bottom:20px;text-align:center}.vis.timeline .dataaxis .yAxis.title.measure{padding:0;margin:0;visibility:hidden;width:auto}.vis.timeline .dataaxis .yAxis.title.left{bottom:0;-webkit-transform-origin:left top;-moz-transform-origin:left top;-ms-transform-origin:left top;-o-transform-origin:left top;transform-origin:left bottom;-webkit-transform:rotate(-90deg);-moz-transform:rotate(-90deg);-ms-transform:rotate(-90deg);-o-transform:rotate(-90deg);transform:rotate(-90deg)}.vis.timeline .dataaxis .yAxis.title.right{bottom:0;-webkit-transform-origin:right bottom;-moz-transform-origin:right bottom;-ms-transform-origin:right bottom;-o-transform-origin:right bottom;transform-origin:right bottom;-webkit-transform:rotate(90deg);-moz-transform:rotate(90deg);-ms-transform:rotate(90deg);-o-transform:rotate(90deg);transform:rotate(90deg)}.vis.timeline .legend{background-color:rgba(247,252,255,.65);padding:5px;border-color:#b3b3b3;border-style:solid;border-width:1px;box-shadow:2px 2px 10px rgba(154,154,154,.55)}.vis.timeline .legendText{white-space:nowrap;display:inline-block}.vis.timeline .graphGroup0{fill:#4f81bd;fill-opacity:0;stroke-width:2px;stroke:#4f81bd}.vis.timeline .graphGroup1{fill:#f79646;fill-opacity:0;stroke-width:2px;stroke:#f79646}.vis.timeline .graphGroup2{fill:#8c51cf;fill-opacity:0;stroke-width:2px;stroke:#8c51cf}.vis.timeline .graphGroup3{fill:#75c841;fill-opacity:0;stroke-width:2px;stroke:#75c841}.vis.timeline .graphGroup4{fill:#ff0100;fill-opacity:0;stroke-width:2px;stroke:#ff0100}.vis.timeline .graphGroup5{fill:#37d8e6;fill-opacity:0;stroke-width:2px;stroke:#37d8e6}.vis.timeline .graphGroup6{fill:#042662;fill-opacity:0;stroke-width:2px;stroke:#042662}.vis.timeline .graphGroup7{fill:#00ff26;fill-opacity:0;stroke-width:2px;stroke:#00ff26}.vis.timeline .graphGroup8{fill:#f0f;fill-opacity:0;stroke-width:2px;stroke:#f0f}.vis.timeline .graphGroup9{fill:#8f3938;fill-opacity:0;stroke-width:2px;stroke:#8f3938}.vis.timeline .fill{fill-opacity:.1;stroke:none}.vis.timeline .bar{fill-opacity:.5;stroke-width:1px}.vis.timeline .point{stroke-width:2px;fill-opacity:1}.vis.timeline .legendBackground{stroke-width:1px;fill-opacity:.9;fill:#fff;stroke:#c2c2c2}.vis.timeline .outline{stroke-width:1px;fill-opacity:1;fill:#fff;stroke:#e5e5e5}.vis.timeline .iconFill{fill-opacity:.3;stroke:none}div.network-manipulationDiv{border-width:0;border-bottom:1px;border-style:solid;border-color:#d6d9d8;background:#fff;background:-moz-linear-gradient(top,#fff 0,#fcfcfc 48%,#fafafa 50%,#fcfcfc 100%);background:-webkit-gradient(linear,left top,left bottom,color-stop(0%,#fff),color-stop(48%,#fcfcfc),color-stop(50%,#fafafa),color-stop(100%,#fcfcfc));background:-webkit-linear-gradient(top,#fff 0,#fcfcfc 48%,#fafafa 50%,#fcfcfc 100%);background:-o-linear-gradient(top,#fff 0,#fcfcfc 48%,#fafafa 50%,#fcfcfc 100%);background:-ms-linear-gradient(top,#fff 0,#fcfcfc 48%,#fafafa 50%,#fcfcfc 100%);background:linear-gradient(to bottom,#fff 0,#fcfcfc 48%,#fafafa 50%,#fcfcfc 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffff', endColorstr='#fcfcfc', GradientType=0);position:absolute;left:0;top:0;width:100%;height:30px}div.network-manipulation-editMode{position:absolute;left:0;top:0;height:30px;margin-top:20px}div.network-manipulation-closeDiv{position:absolute;right:0;top:0;width:30px;height:30px;background-position:20px 3px;background-repeat:no-repeat;background-image:url(img/network/cross.png);cursor:pointer;-webkit-touch-callout:none;-webkit-user-select:none;-khtml-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}div.network-manipulation-closeDiv:hover{opacity:.6}span.network-manipulationUI{font-family:verdana;font-size:12px;-moz-border-radius:15px;border-radius:15px;display:inline-block;background-position:0 0;background-repeat:no-repeat;height:24px;margin:-14px 0 0 10px;vertical-align:middle;cursor:pointer;padding:0 8px;-webkit-touch-callout:none;-webkit-user-select:none;-khtml-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}span.network-manipulationUI:hover{box-shadow:1px 1px 8px rgba(0,0,0,.2)}span.network-manipulationUI:active{box-shadow:1px 1px 8px rgba(0,0,0,.5)}span.network-manipulationUI.back{background-image:url(img/network/backIcon.png)}span.network-manipulationUI.none:hover{box-shadow:1px 1px 8px transparent;cursor:default}span.network-manipulationUI.none:active{box-shadow:1px 1px 8px transparent}span.network-manipulationUI.none{padding:0}span.network-manipulationUI.notification{margin:2px;font-weight:700}span.network-manipulationUI.add{background-image:url(img/network/addNodeIcon.png)}span.network-manipulationUI.edit{background-image:url(img/network/editIcon.png)}span.network-manipulationUI.edit.editmode{background-color:#fcfcfc;border-style:solid;border-width:1px;border-color:#ccc}span.network-manipulationUI.connect{background-image:url(img/network/connectIcon.png)}span.network-manipulationUI.delete{background-image:url(img/network/deleteIcon.png)}span.network-manipulationLabel{margin:0 0 0 23px;line-height:25px}div.network-seperatorLine{display:inline-block;width:1px;height:20px;background-color:#bdbdbd;margin:5px 7px 0 15px}div.network-navigation_wrapper{position:absolute;left:0;top:0;width:100%;height:100%}div.network-navigation{width:34px;height:34px;-moz-border-radius:17px;border-radius:17px;position:absolute;display:inline-block;background-position:2px 2px;background-repeat:no-repeat;cursor:pointer;-webkit-touch-callout:none;-webkit-user-select:none;-khtml-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}div.network-navigation:hover{box-shadow:0 0 3px 3px rgba(56,207,21,.3)}div.network-navigation:active{box-shadow:0 0 1px 3px rgba(56,207,21,.95)}div.network-navigation.up{background-image:url(img/network/upArrow.png);bottom:50px;left:55px}div.network-navigation.down{background-image:url(img/network/downArrow.png);bottom:10px;left:55px}div.network-navigation.left{background-image:url(img/network/leftArrow.png);bottom:10px;left:15px}div.network-navigation.right{background-image:url(img/network/rightArrow.png);bottom:10px;left:95px}div.network-navigation.zoomIn{background-image:url(img/network/plus.png);bottom:10px;right:15px}div.network-navigation.zoomOut{background-image:url(img/network/minus.png);bottom:10px;right:55px}div.network-navigation.zoomExtends{background-image:url(img/network/zoomExtends.png);bottom:50px;right:15px} \ No newline at end of file +.vis .overlay{position:absolute;top:0;left:0;width:100%;height:100%;z-index:10}.vis-active{box-shadow:0 0 10px #86d5f8}.vis [class*=span]{min-height:0;width:auto}.vis.timeline.root{position:relative;border:1px solid #bfbfbf;overflow:hidden;padding:0;margin:0;box-sizing:border-box}.vis.timeline .vispanel{position:absolute;padding:0;margin:0;box-sizing:border-box}.vis.timeline .vispanel.bottom,.vis.timeline .vispanel.center,.vis.timeline .vispanel.left,.vis.timeline .vispanel.right,.vis.timeline .vispanel.top{border:1px #bfbfbf}.vis.timeline .vispanel.center,.vis.timeline .vispanel.left,.vis.timeline .vispanel.right{border-top-style:solid;border-bottom-style:solid;overflow:hidden}.vis.timeline .vispanel.bottom,.vis.timeline .vispanel.center,.vis.timeline .vispanel.top{border-left-style:solid;border-right-style:solid}.vis.timeline .background{overflow:hidden}.vis.timeline .vispanel>.content{position:relative}.vis.timeline .vispanel .shadow{position:absolute;width:100%;height:1px;box-shadow:0 0 10px rgba(0,0,0,.8)}.vis.timeline .vispanel .shadow.top{top:-1px;left:0}.vis.timeline .vispanel .shadow.bottom{bottom:-1px;left:0}.vis.timeline .labelset{position:relative;overflow:hidden;box-sizing:border-box}.vis.timeline .labelset .vlabel{position:relative;left:0;top:0;width:100%;color:#4d4d4d;box-sizing:border-box;border-bottom:1px solid #bfbfbf}.vis.timeline .labelset .vlabel:last-child{border-bottom:none}.vis.timeline .labelset .vlabel .inner{display:inline-block;padding:5px}.vis.timeline .labelset .vlabel .inner.hidden{padding:0}.vis.timeline .itemset{position:relative;padding:0;margin:0;box-sizing:border-box}.vis.timeline .itemset .background,.vis.timeline .itemset .foreground{position:absolute;width:100%;height:100%;overflow:visible}.vis.timeline .axis{position:absolute;width:100%;height:0;left:0;z-index:1}.vis.timeline .foreground .group{position:relative;box-sizing:border-box;border-bottom:1px solid #bfbfbf}.vis.timeline .foreground .group:last-child{border-bottom:none}.vis.timeline .item{position:absolute;color:#1A1A1A;border-color:#97B0F8;border-width:1px;background-color:#D5DDF6;display:inline-block;padding:5px}.vis.timeline .item.selected{border-color:#FFC200;background-color:#FFF785;z-index:2}.vis.timeline .editable .item.selected{cursor:move}.vis.timeline .item.point.selected{background-color:#FFF785}.vis.timeline .item.box{text-align:center;border-style:solid;border-radius:2px}.vis.timeline .item.point{background:0 0}.vis.timeline .item.dot{position:absolute;padding:0;border-width:4px;border-style:solid;border-radius:4px}.vis.timeline .item.range{border-style:solid;border-radius:2px;box-sizing:border-box}.vis.timeline .item.background{overflow:hidden;border:none;background-color:rgba(213,221,246,.4);box-sizing:border-box;padding:0;margin:0}.vis.timeline .item.range .content{position:relative;display:inline-block;max-width:100%;overflow:hidden}.vis.timeline .item.background .content{position:absolute;display:inline-block;overflow:hidden;max-width:100%;margin:5px}.vis.timeline .item.line{padding:0;position:absolute;width:0;border-left-width:1px;border-left-style:solid}.vis.timeline .item .content{white-space:nowrap;overflow:hidden}.vis.timeline .item .delete{background:url(img/timeline/delete.png) top center no-repeat;position:absolute;width:24px;height:24px;top:0;right:-24px;cursor:pointer}.vis.timeline .item.range .drag-left{position:absolute;width:24px;height:100%;top:0;left:-4px;cursor:w-resize}.vis.timeline .item.range .drag-right{position:absolute;width:24px;height:100%;top:0;right:-4px;cursor:e-resize}.vis.timeline .timeaxis{position:relative;overflow:hidden}.vis.timeline .timeaxis.foreground{top:0;left:0;width:100%}.vis.timeline .timeaxis.background{position:absolute;top:0;left:0;width:100%;height:100%}.vis.timeline .timeaxis .text{position:absolute;color:#4d4d4d;padding:3px;white-space:nowrap}.vis.timeline .timeaxis .text.measure{position:absolute;padding-left:0;padding-right:0;margin-left:0;margin-right:0;visibility:hidden}.vis.timeline .timeaxis .grid.vertical{position:absolute;border-left:1px solid}.vis.timeline .timeaxis .grid.minor{border-color:#e5e5e5}.vis.timeline .timeaxis .grid.major{border-color:#bfbfbf}.vis.timeline .currenttime{background-color:#FF7F6E;width:2px;z-index:1}.vis.timeline .customtime{background-color:#6E94FF;width:2px;cursor:move;z-index:1}.vis.timeline .vispanel.background.horizontal .grid.horizontal{position:absolute;width:100%;height:0;border-bottom:1px solid}.vis.timeline .vispanel.background.horizontal .grid.minor{border-color:#e5e5e5}.vis.timeline .vispanel.background.horizontal .grid.major{border-color:#bfbfbf}.vis.timeline .dataaxis .yAxis.major{width:100%;position:absolute;color:#4d4d4d;white-space:nowrap}.vis.timeline .dataaxis .yAxis.major.measure{padding:0;margin:0;border:0;visibility:hidden;width:auto}.vis.timeline .dataaxis .yAxis.minor{position:absolute;width:100%;color:#bebebe;white-space:nowrap}.vis.timeline .dataaxis .yAxis.minor.measure{padding:0;margin:0;border:0;visibility:hidden;width:auto}.vis.timeline .dataaxis .yAxis.title{position:absolute;color:#4d4d4d;white-space:nowrap;bottom:20px;text-align:center}.vis.timeline .dataaxis .yAxis.title.measure{padding:0;margin:0;visibility:hidden;width:auto}.vis.timeline .dataaxis .yAxis.title.left{bottom:0;-webkit-transform-origin:left top;-moz-transform-origin:left top;-ms-transform-origin:left top;-o-transform-origin:left top;transform-origin:left bottom;-webkit-transform:rotate(-90deg);-moz-transform:rotate(-90deg);-ms-transform:rotate(-90deg);-o-transform:rotate(-90deg);transform:rotate(-90deg)}.vis.timeline .dataaxis .yAxis.title.right{bottom:0;-webkit-transform-origin:right bottom;-moz-transform-origin:right bottom;-ms-transform-origin:right bottom;-o-transform-origin:right bottom;transform-origin:right bottom;-webkit-transform:rotate(90deg);-moz-transform:rotate(90deg);-ms-transform:rotate(90deg);-o-transform:rotate(90deg);transform:rotate(90deg)}.vis.timeline .legend{background-color:rgba(247,252,255,.65);padding:5px;border-color:#b3b3b3;border-style:solid;border-width:1px;box-shadow:2px 2px 10px rgba(154,154,154,.55)}.vis.timeline .legendText{white-space:nowrap;display:inline-block}.vis.timeline .graphGroup0{fill:#4f81bd;fill-opacity:0;stroke-width:2px;stroke:#4f81bd}.vis.timeline .graphGroup1{fill:#f79646;fill-opacity:0;stroke-width:2px;stroke:#f79646}.vis.timeline .graphGroup2{fill:#8c51cf;fill-opacity:0;stroke-width:2px;stroke:#8c51cf}.vis.timeline .graphGroup3{fill:#75c841;fill-opacity:0;stroke-width:2px;stroke:#75c841}.vis.timeline .graphGroup4{fill:#ff0100;fill-opacity:0;stroke-width:2px;stroke:#ff0100}.vis.timeline .graphGroup5{fill:#37d8e6;fill-opacity:0;stroke-width:2px;stroke:#37d8e6}.vis.timeline .graphGroup6{fill:#042662;fill-opacity:0;stroke-width:2px;stroke:#042662}.vis.timeline .graphGroup7{fill:#00ff26;fill-opacity:0;stroke-width:2px;stroke:#00ff26}.vis.timeline .graphGroup8{fill:#f0f;fill-opacity:0;stroke-width:2px;stroke:#f0f}.vis.timeline .graphGroup9{fill:#8f3938;fill-opacity:0;stroke-width:2px;stroke:#8f3938}.vis.timeline .fill{fill-opacity:.1;stroke:none}.vis.timeline .bar{fill-opacity:.5;stroke-width:1px}.vis.timeline .point{stroke-width:2px;fill-opacity:1}.vis.timeline .legendBackground{stroke-width:1px;fill-opacity:.9;fill:#fff;stroke:#c2c2c2}.vis.timeline .outline{stroke-width:1px;fill-opacity:1;fill:#fff;stroke:#e5e5e5}.vis.timeline .iconFill{fill-opacity:.3;stroke:none}div.network-manipulationDiv{border-width:0;border-bottom:1px;border-style:solid;border-color:#d6d9d8;background:#fff;background:-moz-linear-gradient(top,#fff 0,#fcfcfc 48%,#fafafa 50%,#fcfcfc 100%);background:-webkit-gradient(linear,left top,left bottom,color-stop(0,#fff),color-stop(48%,#fcfcfc),color-stop(50%,#fafafa),color-stop(100%,#fcfcfc));background:-webkit-linear-gradient(top,#fff 0,#fcfcfc 48%,#fafafa 50%,#fcfcfc 100%);background:-o-linear-gradient(top,#fff 0,#fcfcfc 48%,#fafafa 50%,#fcfcfc 100%);background:-ms-linear-gradient(top,#fff 0,#fcfcfc 48%,#fafafa 50%,#fcfcfc 100%);background:linear-gradient(to bottom,#fff 0,#fcfcfc 48%,#fafafa 50%,#fcfcfc 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffff', endColorstr='#fcfcfc', GradientType=0);position:absolute;left:0;top:0;width:100%;height:30px}div.network-manipulation-editMode{position:absolute;left:0;top:0;height:30px;margin-top:20px}div.network-manipulation-closeDiv{position:absolute;right:0;top:0;width:30px;height:30px;background-position:20px 3px;background-repeat:no-repeat;background-image:url(img/network/cross.png);cursor:pointer;-webkit-touch-callout:none;-webkit-user-select:none;-khtml-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}div.network-manipulation-closeDiv:hover{opacity:.6}span.network-manipulationUI{font-family:verdana;font-size:12px;-moz-border-radius:15px;border-radius:15px;display:inline-block;background-position:0 0;background-repeat:no-repeat;height:24px;margin:-14px 0 0 10px;vertical-align:middle;cursor:pointer;padding:0 8px;-webkit-touch-callout:none;-webkit-user-select:none;-khtml-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}span.network-manipulationUI:hover{box-shadow:1px 1px 8px rgba(0,0,0,.2)}span.network-manipulationUI:active{box-shadow:1px 1px 8px rgba(0,0,0,.5)}span.network-manipulationUI.back{background-image:url(img/network/backIcon.png)}span.network-manipulationUI.none:hover{box-shadow:1px 1px 8px transparent;cursor:default}span.network-manipulationUI.none:active{box-shadow:1px 1px 8px transparent}span.network-manipulationUI.none{padding:0}span.network-manipulationUI.notification{margin:2px;font-weight:700}span.network-manipulationUI.add{background-image:url(img/network/addNodeIcon.png)}span.network-manipulationUI.edit{background-image:url(img/network/editIcon.png)}span.network-manipulationUI.edit.editmode{background-color:#fcfcfc;border-style:solid;border-width:1px;border-color:#ccc}span.network-manipulationUI.connect{background-image:url(img/network/connectIcon.png)}span.network-manipulationUI.delete{background-image:url(img/network/deleteIcon.png)}span.network-manipulationLabel{margin:0 0 0 23px;line-height:25px}div.network-seperatorLine{display:inline-block;width:1px;height:20px;background-color:#bdbdbd;margin:5px 7px 0 15px}div.network-navigation_wrapper{position:absolute;left:0;top:0;width:100%;height:100%}div.network-navigation{width:34px;height:34px;-moz-border-radius:17px;border-radius:17px;position:absolute;display:inline-block;background-position:2px 2px;background-repeat:no-repeat;cursor:pointer;-webkit-touch-callout:none;-webkit-user-select:none;-khtml-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}div.network-navigation:hover{box-shadow:0 0 3px 3px rgba(56,207,21,.3)}div.network-navigation:active{box-shadow:0 0 1px 3px rgba(56,207,21,.95)}div.network-navigation.up{background-image:url(img/network/upArrow.png);bottom:50px;left:55px}div.network-navigation.down{background-image:url(img/network/downArrow.png);bottom:10px;left:55px}div.network-navigation.left{background-image:url(img/network/leftArrow.png);bottom:10px;left:15px}div.network-navigation.right{background-image:url(img/network/rightArrow.png);bottom:10px;left:95px}div.network-navigation.zoomIn{background-image:url(img/network/plus.png);bottom:10px;right:15px}div.network-navigation.zoomOut{background-image:url(img/network/minus.png);bottom:10px;right:55px}div.network-navigation.zoomExtends{background-image:url(img/network/zoomExtends.png);bottom:50px;right:15px} \ No newline at end of file diff --git a/dist/vis.min.js b/dist/vis.min.js index 32e0656e..9f87aad1 100644 --- a/dist/vis.min.js +++ b/dist/vis.min.js @@ -5,7 +5,7 @@ * A dynamic, browser-based visualization library. * * @version 3.9.2-SNAPSHOT - * @date 2015-02-02 + * @date 2015-02-05 * * @license * Copyright (C) 2011-2014 Almende B.V, http://almende.com @@ -22,18 +22,18 @@ * * Vis.js may be distributed under either license. */ -"use strict";!function(t,e){"object"==typeof exports&&"object"==typeof module?module.exports=e():"function"==typeof define&&define.amd?define(e):"object"==typeof exports?exports.vis=e():t.vis=e()}(this,function(){return function(t){function e(s){if(i[s])return i[s].exports;var o=i[s]={exports:{},id:s,loaded:!1};return t[s].call(o.exports,o,o.exports,e),o.loaded=!0,o.exports}var i={};return e.m=t,e.c=i,e.p="",e(0)}([function(t,e,i){e.util=i(1),e.DOMutil=i(2),e.DataSet=i(3),e.DataView=i(4),e.Queue=i(5),e.Graph3d=i(6),e.graph3d={Camera:i(7),Filter:i(8),Point2d:i(9),Point3d:i(10),Slider:i(11),StepNumber:i(12)},e.Timeline=i(13),e.Graph2d=i(14),e.timeline={DateUtil:i(15),DataStep:i(16),Range:i(17),stack:i(18),TimeStep:i(19),components:{items:{Item:i(31),BackgroundItem:i(32),BoxItem:i(33),PointItem:i(34),RangeItem:i(35)},Component:i(20),CurrentTime:i(21),CustomTime:i(22),DataAxis:i(23),GraphGroup:i(24),Group:i(25),BackgroundGroup:i(26),ItemSet:i(27),Legend:i(28),LineGraph:i(29),TimeAxis:i(30)}},e.Network=i(36),e.network={Edge:i(37),Groups:i(38),Images:i(39),Node:i(40),Popup:i(41),dotparser:i(42),gephiParser:i(43)},e.Graph=function(){throw new Error("Graph is renamed to Network. Please create a graph as new vis.Network(...)")},e.moment=i(44),e.hammer=i(45)},function(t,e,i){var s=i(44);e.isNumber=function(t){return t instanceof Number||"number"==typeof t},e.isString=function(t){return t instanceof String||"string"==typeof t},e.isDate=function(t){if(t instanceof Date)return!0;if(e.isString(t)){var i=o.exec(t);if(i)return!0;if(!isNaN(Date.parse(t)))return!0}return!1},e.isDataTable=function(t){return"undefined"!=typeof google&&google.visualization&&google.visualization.DataTable&&t instanceof google.visualization.DataTable},e.randomUUID=function(){var t=function(){return Math.floor(65536*Math.random()).toString(16)};return t()+t()+"-"+t()+"-"+t()+"-"+t()+"-"+t()+t()+t()},e.extend=function(t){for(var e=1,i=arguments.length;i>e;e++){var s=arguments[e];for(var o in s)s.hasOwnProperty(o)&&(t[o]=s[o])}return t},e.selectiveExtend=function(t,e){if(!Array.isArray(t))throw new Error("Array with property names expected as first argument");for(var i=2;ii;i++)if(t[i]!=e[i])return!1;return!0},e.convert=function(t,i){var n;if(void 0===t)return void 0;if(null===t)return null;if(!i)return t;if("string"!=typeof i&&!(i instanceof String))throw new Error("Type must be a string");switch(i){case"boolean":case"Boolean":return Boolean(t);case"number":case"Number":return Number(t.valueOf());case"string":case"String":return String(t);case"Date":if(e.isNumber(t))return new Date(t);if(t instanceof Date)return new Date(t.valueOf());if(s.isMoment(t))return new Date(t.valueOf());if(e.isString(t))return n=o.exec(t),n?new Date(Number(n[1])):s(t).toDate();throw new Error("Cannot convert object of type "+e.getType(t)+" to type Date");case"Moment":if(e.isNumber(t))return s(t);if(t instanceof Date)return s(t.valueOf());if(s.isMoment(t))return s(t);if(e.isString(t))return n=o.exec(t),s(n?Number(n[1]):t);throw new Error("Cannot convert object of type "+e.getType(t)+" to type Date");case"ISODate":if(e.isNumber(t))return new Date(t);if(t instanceof Date)return t.toISOString();if(s.isMoment(t))return t.toDate().toISOString();if(e.isString(t))return n=o.exec(t),n?new Date(Number(n[1])).toISOString():new Date(t).toISOString();throw new Error("Cannot convert object of type "+e.getType(t)+" to type ISODate");case"ASPDate":if(e.isNumber(t))return"/Date("+t+")/";if(t instanceof Date)return"/Date("+t.valueOf()+")/";if(e.isString(t)){n=o.exec(t);var r;return r=n?new Date(Number(n[1])).valueOf():new Date(t).valueOf(),"/Date("+r+")/"}throw new Error("Cannot convert object of type "+e.getType(t)+" to type ASPDate");default:throw new Error('Unknown type "'+i+'"')}};var o=/^\/?Date\((\-?\d+)/i;e.getType=function(t){var e=typeof t;return"object"==e?null==t?"null":t instanceof Boolean?"Boolean":t instanceof Number?"Number":t instanceof String?"String":Array.isArray(t)?"Array":t instanceof Date?"Date":"Object":"number"==e?"Number":"boolean"==e?"Boolean":"string"==e?"String":e},e.getAbsoluteLeft=function(t){return t.getBoundingClientRect().left+window.pageXOffset},e.getAbsoluteTop=function(t){return t.getBoundingClientRect().top+window.pageYOffset},e.addClassName=function(t,e){var i=t.className.split(" ");-1==i.indexOf(e)&&(i.push(e),t.className=i.join(" "))},e.removeClassName=function(t,e){var i=t.className.split(" "),s=i.indexOf(e);-1!=s&&(i.splice(s,1),t.className=i.join(" "))},e.forEach=function(t,e){var i,s;if(Array.isArray(t))for(i=0,s=t.length;s>i;i++)e(t[i],i,t);else for(i in t)t.hasOwnProperty(i)&&e(t[i],i,t)},e.toArray=function(t){var e=[];for(var i in t)t.hasOwnProperty(i)&&e.push(t[i]);return e},e.updateProperty=function(t,e,i){return t[e]!==i?(t[e]=i,!0):!1},e.addEventListener=function(t,e,i,s){t.addEventListener?(void 0===s&&(s=!1),"mousewheel"===e&&navigator.userAgent.indexOf("Firefox")>=0&&(e="DOMMouseScroll"),t.addEventListener(e,i,s)):t.attachEvent("on"+e,i)},e.removeEventListener=function(t,e,i,s){t.removeEventListener?(void 0===s&&(s=!1),"mousewheel"===e&&navigator.userAgent.indexOf("Firefox")>=0&&(e="DOMMouseScroll"),t.removeEventListener(e,i,s)):t.detachEvent("on"+e,i)},e.preventDefault=function(t){t||(t=window.event),t.preventDefault?t.preventDefault():t.returnValue=!1},e.getTarget=function(t){t||(t=window.event);var e;return t.target?e=t.target:t.srcElement&&(e=t.srcElement),void 0!=e.nodeType&&3==e.nodeType&&(e=e.parentNode),e},e.option={},e.option.asBoolean=function(t,e){return"function"==typeof t&&(t=t()),null!=t?0!=t:e||null},e.option.asNumber=function(t,e){return"function"==typeof t&&(t=t()),null!=t?Number(t)||e||null:e||null},e.option.asString=function(t,e){return"function"==typeof t&&(t=t()),null!=t?String(t):e||null},e.option.asSize=function(t,i){return"function"==typeof t&&(t=t()),e.isString(t)?t:e.isNumber(t)?t+"px":i||null},e.option.asElement=function(t,e){return"function"==typeof t&&(t=t()),t||e||null},e.hexToRGB=function(t){var e=/^#?([a-f\d])([a-f\d])([a-f\d])$/i;t=t.replace(e,function(t,e,i,s){return e+e+i+i+s+s});var i=/^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(t);return i?{r:parseInt(i[1],16),g:parseInt(i[2],16),b:parseInt(i[3],16)}:null},e.RGBToHex=function(t,e,i){return"#"+((1<<24)+(t<<16)+(e<<8)+i).toString(16).slice(1)},e.parseColor=function(t){var i;if(e.isString(t)){if(e.isValidRGB(t)){var s=t.substr(4).substr(0,t.length-5).split(",");t=e.RGBToHex(s[0],s[1],s[2])}if(e.isValidHex(t)){var o=e.hexToHSV(t),n={h:o.h,s:.45*o.s,v:Math.min(1,1.05*o.v)},r={h:o.h,s:Math.min(1,1.25*o.v),v:.6*o.v},a=e.HSVToHex(r.h,r.h,r.v),h=e.HSVToHex(n.h,n.s,n.v);i={background:t,border:a,highlight:{background:h,border:a},hover:{background:h,border:a}}}else i={background:t,border:t,highlight:{background:t,border:t},hover:{background:t,border:t}}}else i={},i.background=t.background||"white",i.border=t.border||i.background,e.isString(t.highlight)?i.highlight={border:t.highlight,background:t.highlight}:(i.highlight={},i.highlight.background=t.highlight&&t.highlight.background||i.background,i.highlight.border=t.highlight&&t.highlight.border||i.border),e.isString(t.hover)?i.hover={border:t.hover,background:t.hover}:(i.hover={},i.hover.background=t.hover&&t.hover.background||i.background,i.hover.border=t.hover&&t.hover.border||i.border);return i},e.RGBToHSV=function(t,e,i){t/=255,e/=255,i/=255;var s=Math.min(t,Math.min(e,i)),o=Math.max(t,Math.max(e,i));if(s==o)return{h:0,s:0,v:s};var n=t==s?e-i:i==s?t-e:i-t,r=t==s?3:i==s?1:5,a=60*(r-n/(o-s))/360,h=(o-s)/o,d=o;return{h:a,s:h,v:d}};var n={split:function(t){var e={};return t.split(";").forEach(function(t){if(""!=t.trim()){var i=t.split(":"),s=i[0].trim(),o=i[1].trim();e[s]=o}}),e},join:function(t){return Object.keys(t).map(function(e){return e+": "+t[e]}).join("; ")}};e.addCssText=function(t,i){var s=n.split(t.style.cssText),o=n.split(i),r=e.extend(s,o);t.style.cssText=n.join(r)},e.removeCssText=function(t,e){var i=n.split(t.style.cssText),s=n.split(e);for(var o in s)s.hasOwnProperty(o)&&delete i[o];t.style.cssText=n.join(i)},e.HSVToRGB=function(t,e,i){var s,o,n,r=Math.floor(6*t),a=6*t-r,h=i*(1-e),d=i*(1-a*e),l=i*(1-(1-a)*e);switch(r%6){case 0:s=i,o=l,n=h;break;case 1:s=d,o=i,n=h;break;case 2:s=h,o=i,n=l;break;case 3:s=h,o=d,n=i;break;case 4:s=l,o=h,n=i;break;case 5:s=i,o=h,n=d}return{r:Math.floor(255*s),g:Math.floor(255*o),b:Math.floor(255*n)}},e.HSVToHex=function(t,i,s){var o=e.HSVToRGB(t,i,s);return e.RGBToHex(o.r,o.g,o.b)},e.hexToHSV=function(t){var i=e.hexToRGB(t);return e.RGBToHSV(i.r,i.g,i.b)},e.isValidHex=function(t){var e=/(^#[0-9A-F]{6}$)|(^#[0-9A-F]{3}$)/i.test(t);return e},e.isValidRGB=function(t){t=t.replace(" ","");var e=/rgb\((\d{1,3}),(\d{1,3}),(\d{1,3})\)/i.test(t);return e},e.selectiveBridgeObject=function(t,i){if("object"==typeof i){for(var s=Object.create(i),o=0;o=r&&o>n;){var h=Math.floor((r+a)/2),d=t[h],l=void 0===s?d[i]:d[i][s],c=e(l);if(0==c)return h;-1==c?r=h+1:a=h-1,n++}return-1},e.binarySearchValue=function(t,e,i,s){for(var o,n,r,a,h=1e4,d=0,l=0,c=t.length-1;c>=l&&h>d;){if(a=Math.floor(.5*(c+l)),o=t[Math.max(0,a-1)][i],n=t[a][i],r=t[Math.min(t.length-1,a+1)][i],n==e)return a;if(e>o&&n>e)return"before"==s?Math.max(0,a-1):a;if(e>n&&r>e)return"before"==s?a:Math.min(t.length-1,a+1);e>n?l=a+1:c=a-1,d++}return-1},e.easeInOutQuad=function(t,e,i,s){var o=i-e;return t/=s/2,1>t?o/2*t*t+e:(t--,-o/2*(t*(t-2)-1)+e)},e.easingFunctions={linear:function(t){return t},easeInQuad:function(t){return t*t},easeOutQuad:function(t){return t*(2-t)},easeInOutQuad:function(t){return.5>t?2*t*t:-1+(4-2*t)*t},easeInCubic:function(t){return t*t*t},easeOutCubic:function(t){return--t*t*t+1},easeInOutCubic:function(t){return.5>t?4*t*t*t:(t-1)*(2*t-2)*(2*t-2)+1},easeInQuart:function(t){return t*t*t*t},easeOutQuart:function(t){return 1- --t*t*t*t},easeInOutQuart:function(t){return.5>t?8*t*t*t*t:1-8*--t*t*t*t},easeInQuint:function(t){return t*t*t*t*t},easeOutQuint:function(t){return 1+--t*t*t*t*t},easeInOutQuint:function(t){return.5>t?16*t*t*t*t*t:1+16*--t*t*t*t*t}}},function(t,e){e.prepareElements=function(t){for(var e in t)t.hasOwnProperty(e)&&(t[e].redundant=t[e].used,t[e].used=[])},e.cleanupElements=function(t){for(var e in t)if(t.hasOwnProperty(e)&&t[e].redundant){for(var i=0;i0?(s=e[t].redundant[0],e[t].redundant.shift()):(s=document.createElementNS("http://www.w3.org/2000/svg",t),i.appendChild(s)):(s=document.createElementNS("http://www.w3.org/2000/svg",t),e[t]={used:[],redundant:[]},i.appendChild(s)),e[t].used.push(s),s},e.getDOMElement=function(t,e,i,s){var o;return e.hasOwnProperty(t)?e[t].redundant.length>0?(o=e[t].redundant[0],e[t].redundant.shift()):(o=document.createElement(t),void 0!==s?i.insertBefore(o,s):i.appendChild(o)):(o=document.createElement(t),e[t]={used:[],redundant:[]},void 0!==s?i.insertBefore(o,s):i.appendChild(o)),e[t].used.push(o),o},e.drawPoint=function(t,i,s,o,n){var r;return"circle"==s.options.drawPoints.style?(r=e.getSVGElement("circle",o,n),r.setAttributeNS(null,"cx",t),r.setAttributeNS(null,"cy",i),r.setAttributeNS(null,"r",.5*s.options.drawPoints.size)):(r=e.getSVGElement("rect",o,n),r.setAttributeNS(null,"x",t-.5*s.options.drawPoints.size),r.setAttributeNS(null,"y",i-.5*s.options.drawPoints.size),r.setAttributeNS(null,"width",s.options.drawPoints.size),r.setAttributeNS(null,"height",s.options.drawPoints.size)),void 0!==s.options.drawPoints.styles&&r.setAttributeNS(null,"style",s.group.options.drawPoints.styles),r.setAttributeNS(null,"class",s.className+" point"),r},e.drawBar=function(t,i,s,o,n,r,a){if(0!=o){0>o&&(o*=-1,i-=o);var h=e.getSVGElement("rect",r,a);h.setAttributeNS(null,"x",t-.5*s),h.setAttributeNS(null,"y",i),h.setAttributeNS(null,"width",s),h.setAttributeNS(null,"height",o),h.setAttributeNS(null,"class",n)}}},function(t,e,i){function s(t,e){if(!t||Array.isArray(t)||o.isDataTable(t)||(e=t,t=null),this._options=e||{},this._data={},this.length=0,this._fieldId=this._options.fieldId||"id",this._type={},this._options.type)for(var i in this._options.type)if(this._options.type.hasOwnProperty(i)){var s=this._options.type[i];this._type[i]="Date"==s||"ISODate"==s||"ASPDate"==s?"Date":s}if(this._options.convert)throw new Error('Option "convert" is deprecated. Use "type" instead.');this._subscribers={},t&&this.add(t),this.setOptions(e)}var o=i(1),n=i(5);s.prototype.setOptions=function(t){t&&void 0!==t.queue&&(t.queue===!1?this._queue&&(this._queue.destroy(),delete this._queue):(this._queue||(this._queue=n.extend(this,{replace:["add","update","remove"]})),"object"==typeof t.queue&&this._queue.setOptions(t.queue)))},s.prototype.on=function(t,e){var i=this._subscribers[t];i||(i=[],this._subscribers[t]=i),i.push({callback:e})},s.prototype.subscribe=s.prototype.on,s.prototype.off=function(t,e){var i=this._subscribers[t];i&&(this._subscribers[t]=i.filter(function(t){return t.callback!=e}))},s.prototype.unsubscribe=s.prototype.off,s.prototype._trigger=function(t,e,i){if("*"==t)throw new Error("Cannot trigger event *");var s=[];t in this._subscribers&&(s=s.concat(this._subscribers[t])),"*"in this._subscribers&&(s=s.concat(this._subscribers["*"]));for(var o=0;or;r++)i=n._addItem(t[r]),s.push(i);else if(o.isDataTable(t))for(var h=this._getColumnNames(t),d=0,l=t.getNumberOfRows();l>d;d++){for(var c={},p=0,u=h.length;u>p;p++){var m=h[p];c[m]=t.getValue(d,p)}i=n._addItem(c),s.push(i)}else{if(!(t instanceof Object))throw new Error("Unknown dataType");i=n._addItem(t),s.push(i)}return s.length&&this._trigger("add",{items:s},e),s},s.prototype.update=function(t,e){var i=[],s=[],n=[],r=this,a=r._fieldId,h=function(t){var e=t[a];r._data[e]?(e=r._updateItem(t),s.push(e),n.push(t)):(e=r._addItem(t),i.push(e))};if(Array.isArray(t))for(var d=0,l=t.length;l>d;d++)h(t[d]);else if(o.isDataTable(t))for(var c=this._getColumnNames(t),p=0,u=t.getNumberOfRows();u>p;p++){for(var m={},f=0,g=c.length;g>f;f++){var v=c[f];m[v]=t.getValue(p,f)}h(m)}else{if(!(t instanceof Object))throw new Error("Unknown dataType");h(t)}return i.length&&this._trigger("add",{items:i},e),s.length&&this._trigger("update",{items:s,data:n},e),i.concat(s)},s.prototype.get=function(){var t,e,i,s,n=this,r=o.getType(arguments[0]);"String"==r||"Number"==r?(t=arguments[0],i=arguments[1],s=arguments[2]):"Array"==r?(e=arguments[0],i=arguments[1],s=arguments[2]):(i=arguments[0],s=arguments[1]);var a;if(i&&i.returnType){var h=["DataTable","Array","Object"];if(a=-1==h.indexOf(i.returnType)?"Array":i.returnType,s&&a!=o.getType(s))throw new Error('Type of parameter "data" ('+o.getType(s)+") does not correspond with specified options.type ("+i.type+")");if("DataTable"==a&&!o.isDataTable(s))throw new Error('Parameter "data" must be a DataTable when options.type is "DataTable"')}else a=s&&"DataTable"==o.getType(s)?"DataTable":"Array";var d,l,c,p,u=i&&i.type||this._options.type,m=i&&i.filter,f=[];if(void 0!=t)d=n._getItem(t,u),m&&!m(d)&&(d=null);else if(void 0!=e)for(c=0,p=e.length;p>c;c++)d=n._getItem(e[c],u),(!m||m(d))&&f.push(d);else for(l in this._data)this._data.hasOwnProperty(l)&&(d=n._getItem(l,u),(!m||m(d))&&f.push(d));if(i&&i.order&&void 0==t&&this._sort(f,i.order),i&&i.fields){var g=i.fields;if(void 0!=t)d=this._filterFields(d,g);else for(c=0,p=f.length;p>c;c++)f[c]=this._filterFields(f[c],g)}if("DataTable"==a){var v=this._getColumnNames(s);if(void 0!=t)n._appendRow(s,v,d);else for(c=0;cc;c++)s.push(f[c]);return s}return f},s.prototype.getIds=function(t){var e,i,s,o,n,r=this._data,a=t&&t.filter,h=t&&t.order,d=t&&t.type||this._options.type,l=[];if(a)if(h){n=[];for(s in r)r.hasOwnProperty(s)&&(o=this._getItem(s,d),a(o)&&n.push(o));for(this._sort(n,h),e=0,i=n.length;i>e;e++)l[e]=n[e][this._fieldId]}else for(s in r)r.hasOwnProperty(s)&&(o=this._getItem(s,d),a(o)&&l.push(o[this._fieldId]));else if(h){n=[];for(s in r)r.hasOwnProperty(s)&&n.push(r[s]);for(this._sort(n,h),e=0,i=n.length;i>e;e++)l[e]=n[e][this._fieldId]}else for(s in r)r.hasOwnProperty(s)&&(o=r[s],l.push(o[this._fieldId]));return l},s.prototype.getDataSet=function(){return this},s.prototype.forEach=function(t,e){var i,s,o=e&&e.filter,n=e&&e.type||this._options.type,r=this._data;if(e&&e.order)for(var a=this.get(e),h=0,d=a.length;d>h;h++)i=a[h],s=i[this._fieldId],t(i,s);else for(s in r)r.hasOwnProperty(s)&&(i=this._getItem(s,n),(!o||o(i))&&t(i,s))},s.prototype.map=function(t,e){var i,s=e&&e.filter,o=e&&e.type||this._options.type,n=[],r=this._data;for(var a in r)r.hasOwnProperty(a)&&(i=this._getItem(a,o),(!s||s(i))&&n.push(t(i,a)));return e&&e.order&&this._sort(n,e.order),n},s.prototype._filterFields=function(t,e){var i={};for(var s in t)t.hasOwnProperty(s)&&-1!=e.indexOf(s)&&(i[s]=t[s]);return i},s.prototype._sort=function(t,e){if(o.isString(e)){var i=e;t.sort(function(t,e){var s=t[i],o=e[i];return s>o?1:o>s?-1:0})}else{if("function"!=typeof e)throw new TypeError("Order must be a function or a string");t.sort(e)}},s.prototype.remove=function(t,e){var i,s,o,n=[];if(Array.isArray(t))for(i=0,s=t.length;s>i;i++)o=this._remove(t[i]),null!=o&&n.push(o);else o=this._remove(t),null!=o&&n.push(o);return n.length&&this._trigger("remove",{items:n},e),n},s.prototype._remove=function(t){if(o.isNumber(t)||o.isString(t)){if(this._data[t])return delete this._data[t],this.length--,t}else if(t instanceof Object){var e=t[this._fieldId];if(e&&this._data[e])return delete this._data[e],this.length--,e}return null},s.prototype.clear=function(t){var e=Object.keys(this._data);return this._data={},this.length=0,this._trigger("remove",{items:e},t),e},s.prototype.max=function(t){var e=this._data,i=null,s=null;for(var o in e)if(e.hasOwnProperty(o)){var n=e[o],r=n[t];null!=r&&(!i||r>s)&&(i=n,s=r)}return i},s.prototype.min=function(t){var e=this._data,i=null,s=null;for(var o in e)if(e.hasOwnProperty(o)){var n=e[o],r=n[t];null!=r&&(!i||s>r)&&(i=n,s=r)}return i},s.prototype.distinct=function(t){var e,i=this._data,s=[],n=this._options.type&&this._options.type[t]||null,r=0;for(var a in i)if(i.hasOwnProperty(a)){var h=i[a],d=h[t],l=!1;for(e=0;r>e;e++)if(s[e]==d){l=!0;break}l||void 0===d||(s[r]=d,r++)}if(n)for(e=0;ei;i++)e[i]=t.getColumnId(i)||t.getColumnLabel(i);return e},s.prototype._appendRow=function(t,e,i){for(var s=t.addRow(),o=0,n=e.length;n>o;o++){var r=e[o];t.setValue(s,o,i[r])}},t.exports=s},function(t,e,i){function s(t,e){this._data=null,this._ids={},this.length=0,this._options=e||{},this._fieldId="id",this._subscribers={};var i=this;this.listener=function(){i._onEvent.apply(i,arguments)},this.setData(t)}var o=i(1),n=i(3);s.prototype.setData=function(t){var e,i,s;if(this._data){this._data.unsubscribe&&this._data.unsubscribe("*",this.listener),e=[];for(var o in this._ids)this._ids.hasOwnProperty(o)&&e.push(o);this._ids={},this.length=0,this._trigger("remove",{items:e})}if(this._data=t,this._data){for(this._fieldId=this._options.fieldId||this._data&&this._data.options&&this._data.options.fieldId||"id",e=this._data.getIds({filter:this._options&&this._options.filter}),i=0,s=e.length;s>i;i++)o=e[i],this._ids[o]=!0;this.length=e.length,this._trigger("add",{items:e}),this._data.on&&this._data.on("*",this.listener)}},s.prototype.get=function(){var t,e,i,s=this,n=o.getType(arguments[0]);"String"==n||"Number"==n||"Array"==n?(t=arguments[0],e=arguments[1],i=arguments[2]):(e=arguments[0],i=arguments[1]);var r=o.extend({},this._options,e);this._options.filter&&e&&e.filter&&(r.filter=function(t){return s._options.filter(t)&&e.filter(t)});var a=[];return void 0!=t&&a.push(t),a.push(r),a.push(i),this._data&&this._data.get.apply(this._data,a)},s.prototype.getIds=function(t){var e;if(this._data){var i,s=this._options.filter;i=t&&t.filter?s?function(e){return s(e)&&t.filter(e)}:t.filter:s,e=this._data.getIds({filter:i,order:t&&t.order})}else e=[];return e},s.prototype.getDataSet=function(){for(var t=this;t instanceof s;)t=t._data;return t||null},s.prototype._onEvent=function(t,e,i){var s,o,n,r,a=e&&e.items,h=this._data,d=[],l=[],c=[];if(a&&h){switch(t){case"add":for(s=0,o=a.length;o>s;s++)n=a[s],r=this.get(n),r&&(this._ids[n]=!0,d.push(n));break;case"update":for(s=0,o=a.length;o>s;s++)n=a[s],r=this.get(n),r?this._ids[n]?l.push(n):(this._ids[n]=!0,d.push(n)):this._ids[n]&&(delete this._ids[n],c.push(n));break;case"remove":for(s=0,o=a.length;o>s;s++)n=a[s],this._ids[n]&&(delete this._ids[n],c.push(n))}this.length+=d.length-c.length,d.length&&this._trigger("add",{items:d},i),l.length&&this._trigger("update",{items:l},i),c.length&&this._trigger("remove",{items:c},i)}},s.prototype.on=n.prototype.on,s.prototype.off=n.prototype.off,s.prototype._trigger=n.prototype._trigger,s.prototype.subscribe=s.prototype.on,s.prototype.unsubscribe=s.prototype.off,t.exports=s},function(t){function e(t){this.delay=null,this.max=1/0,this._queue=[],this._timeout=null,this._extended=null,this.setOptions(t)}e.prototype.setOptions=function(t){t&&"undefined"!=typeof t.delay&&(this.delay=t.delay),t&&"undefined"!=typeof t.max&&(this.max=t.max),this._flushIfNeeded()},e.extend=function(t,i){var s=new e(i);if(void 0!==t.flush)throw new Error("Target object already has a property flush");t.flush=function(){s.flush()};var o=[{name:"flush",original:void 0}];if(i&&i.replace)for(var n=0;nthis.max&&this.flush(),clearTimeout(this._timeout),this.queue.length>0&&"number"==typeof this.delay){var t=this;this._timeout=setTimeout(function(){t.flush()},this.delay)}},e.prototype.flush=function(){for(;this._queue.length>0;){var t=this._queue.shift();t.fn.apply(t.context||t.fn,t.args||[])}},t.exports=e},function(t,e,i){function s(t,e,i){if(!(this instanceof s))throw new SyntaxError("Constructor must be called with the new operator");this.containerElement=t,this.width="400px",this.height="400px",this.margin=10,this.defaultXCenter="55%",this.defaultYCenter="50%",this.xLabel="x",this.yLabel="y",this.zLabel="z";var o=function(t){return t};this.xValueLabel=o,this.yValueLabel=o,this.zValueLabel=o,this.filterLabel="time",this.legendLabel="value",this.style=s.STYLE.DOT,this.showPerspective=!0,this.showGrid=!0,this.keepAspectRatio=!0,this.showShadow=!1,this.showGrayBottom=!1,this.showTooltip=!1,this.verticalRatio=.5,this.animationInterval=1e3,this.animationPreload=!1,this.camera=new p,this.eye=new l(0,0,-1),this.dataTable=null,this.dataPoints=null,this.colX=void 0,this.colY=void 0,this.colZ=void 0,this.colValue=void 0,this.colFilter=void 0,this.xMin=0,this.xStep=void 0,this.xMax=1,this.yMin=0,this.yStep=void 0,this.yMax=1,this.zMin=0,this.zStep=void 0,this.zMax=1,this.valueMin=0,this.valueMax=1,this.xBarWidth=1,this.yBarWidth=1,this.colorAxis="#4D4D4D",this.colorGrid="#D3D3D3",this.colorDot="#7DC1FF",this.colorDotBorder="#3267D2",this.create(),this.setOptions(i),e&&this.setData(e)}function o(t){return"clientX"in t?t.clientX:t.targetTouches[0]&&t.targetTouches[0].clientX||0}function n(t){return"clientY"in t?t.clientY:t.targetTouches[0]&&t.targetTouches[0].clientY||0}var r=i(56),a=i(3),h=i(4),d=i(1),l=i(10),c=i(9),p=i(7),u=i(8),m=i(11),f=i(12);r(s.prototype),s.prototype._setScale=function(){this.scale=new l(1/(this.xMax-this.xMin),1/(this.yMax-this.yMin),1/(this.zMax-this.zMin)),this.keepAspectRatio&&(this.scale.x3&&(this.colFilter=3);else{if(this.style!==s.STYLE.DOTCOLOR&&this.style!==s.STYLE.DOTSIZE&&this.style!==s.STYLE.BARCOLOR&&this.style!==s.STYLE.BARSIZE)throw'Unknown style "'+this.style+'"';this.colX=0,this.colY=1,this.colZ=2,this.colValue=3,t.getNumberOfColumns()>4&&(this.colFilter=4)}},s.prototype.getNumberOfRows=function(t){return t.length},s.prototype.getNumberOfColumns=function(t){var e=0;for(var i in t[0])t[0].hasOwnProperty(i)&&e++;return e},s.prototype.getDistinctValues=function(t,e){for(var i=[],s=0;st[s][e]&&(i.min=t[s][e]),i.maxt;t++){var m=(t-p)/(u-p),g=240*m,v=this._hsv2rgb(g,1,1);c.strokeStyle=v,c.beginPath(),c.moveTo(h,r+t),c.lineTo(a,r+t),c.stroke()}c.strokeStyle=this.colorAxis,c.strokeRect(h,r,i,n)}if(this.style===s.STYLE.DOTSIZE&&(c.strokeStyle=this.colorAxis,c.fillStyle=this.colorDot,c.beginPath(),c.moveTo(h,r),c.lineTo(a,r),c.lineTo(a-i+e,d),c.lineTo(h,d),c.closePath(),c.fill(),c.stroke()),this.style===s.STYLE.DOTCOLOR||this.style===s.STYLE.DOTSIZE){var y=5,b=new f(this.valueMin,this.valueMax,(this.valueMax-this.valueMin)/5,!0);for(b.start(),b.getCurrent()0?this.yMin:this.yMax,o=this._convert3Dto2D(new l(x,r,this.zMin)),Math.cos(2*_)>0?(g.textAlign="center",g.textBaseline="top",o.y+=b):Math.sin(2*_)<0?(g.textAlign="right",g.textBaseline="middle"):(g.textAlign="left",g.textBaseline="middle"),g.fillStyle=this.colorAxis,g.fillText(" "+this.xValueLabel(i.getCurrent())+" ",o.x,o.y),i.next()}for(g.lineWidth=1,s=void 0===this.defaultYStep,i=new f(this.yMin,this.yMax,this.yStep,s),i.start(),i.getCurrent()0?this.xMin:this.xMax,o=this._convert3Dto2D(new l(n,i.getCurrent(),this.zMin)),Math.cos(2*_)<0?(g.textAlign="center",g.textBaseline="top",o.y+=b):Math.sin(2*_)>0?(g.textAlign="right",g.textBaseline="middle"):(g.textAlign="left",g.textBaseline="middle"),g.fillStyle=this.colorAxis,g.fillText(" "+this.yValueLabel(i.getCurrent())+" ",o.x,o.y),i.next();for(g.lineWidth=1,s=void 0===this.defaultZStep,i=new f(this.zMin,this.zMax,this.zStep,s),i.start(),i.getCurrent()0?this.xMin:this.xMax,r=Math.sin(_)<0?this.yMin:this.yMax;!i.end();)t=this._convert3Dto2D(new l(n,r,i.getCurrent())),g.strokeStyle=this.colorAxis,g.beginPath(),g.moveTo(t.x,t.y),g.lineTo(t.x-b,t.y),g.stroke(),g.textAlign="right",g.textBaseline="middle",g.fillStyle=this.colorAxis,g.fillText(this.zValueLabel(i.getCurrent())+" ",t.x-5,t.y),i.next();g.lineWidth=1,t=this._convert3Dto2D(new l(n,r,this.zMin)),e=this._convert3Dto2D(new l(n,r,this.zMax)),g.strokeStyle=this.colorAxis,g.beginPath(),g.moveTo(t.x,t.y),g.lineTo(e.x,e.y),g.stroke(),g.lineWidth=1,p=this._convert3Dto2D(new l(this.xMin,this.yMin,this.zMin)),u=this._convert3Dto2D(new l(this.xMax,this.yMin,this.zMin)),g.strokeStyle=this.colorAxis,g.beginPath(),g.moveTo(p.x,p.y),g.lineTo(u.x,u.y),g.stroke(),p=this._convert3Dto2D(new l(this.xMin,this.yMax,this.zMin)),u=this._convert3Dto2D(new l(this.xMax,this.yMax,this.zMin)),g.strokeStyle=this.colorAxis,g.beginPath(),g.moveTo(p.x,p.y),g.lineTo(u.x,u.y),g.stroke(),g.lineWidth=1,t=this._convert3Dto2D(new l(this.xMin,this.yMin,this.zMin)),e=this._convert3Dto2D(new l(this.xMin,this.yMax,this.zMin)),g.strokeStyle=this.colorAxis,g.beginPath(),g.moveTo(t.x,t.y),g.lineTo(e.x,e.y),g.stroke(),t=this._convert3Dto2D(new l(this.xMax,this.yMin,this.zMin)),e=this._convert3Dto2D(new l(this.xMax,this.yMax,this.zMin)),g.strokeStyle=this.colorAxis,g.beginPath(),g.moveTo(t.x,t.y),g.lineTo(e.x,e.y),g.stroke();var w=this.xLabel;w.length>0&&(c=.1/this.scale.y,n=(this.xMin+this.xMax)/2,r=Math.cos(_)>0?this.yMin-c:this.yMax+c,o=this._convert3Dto2D(new l(n,r,this.zMin)),Math.cos(2*_)>0?(g.textAlign="center",g.textBaseline="top"):Math.sin(2*_)<0?(g.textAlign="right",g.textBaseline="middle"):(g.textAlign="left",g.textBaseline="middle"),g.fillStyle=this.colorAxis,g.fillText(w,o.x,o.y));var M=this.yLabel;M.length>0&&(d=.1/this.scale.x,n=Math.sin(_)>0?this.xMin-d:this.xMax+d,r=(this.yMin+this.yMax)/2,o=this._convert3Dto2D(new l(n,r,this.zMin)),Math.cos(2*_)<0?(g.textAlign="center",g.textBaseline="top"):Math.sin(2*_)>0?(g.textAlign="right",g.textBaseline="middle"):(g.textAlign="left",g.textBaseline="middle"),g.fillStyle=this.colorAxis,g.fillText(M,o.x,o.y));var S=this.zLabel;S.length>0&&(h=30,n=Math.cos(_)>0?this.xMin:this.xMax,r=Math.sin(_)<0?this.yMin:this.yMax,a=(this.zMin+this.zMax)/2,o=this._convert3Dto2D(new l(n,r,a)),g.textAlign="right",g.textBaseline="middle",g.fillStyle=this.colorAxis,g.fillText(S,o.x-h,o.y))},s.prototype._hsv2rgb=function(t,e,i){var s,o,n,r,a,h;switch(r=i*e,a=Math.floor(t/60),h=r*(1-Math.abs(t/60%2-1)),a){case 0:s=r,o=h,n=0;break;case 1:s=h,o=r,n=0;break;case 2:s=0,o=r,n=h;break;case 3:s=0,o=h,n=r;break;case 4:s=h,o=0,n=r;break;case 5:s=r,o=0,n=h;break;default:s=0,o=0,n=0}return"RGB("+parseInt(255*s)+","+parseInt(255*o)+","+parseInt(255*n)+")"},s.prototype._redrawDataGrid=function(){var t,e,i,o,n,r,a,h,d,c,p,u,m,f=this.frame.canvas,g=f.getContext("2d");if(!(void 0===this.dataPoints||this.dataPoints.length<=0)){for(n=0;n0}else r=!0;r?(m=(t.point.z+e.point.z+i.point.z+o.point.z)/4,c=240*(1-(m-this.zMin)*this.scale.z/this.verticalRatio),p=1,this.showShadow?(u=Math.min(1+M.x/S/2,1),a=this._hsv2rgb(c,p,u),h=a):(u=1,a=this._hsv2rgb(c,p,u),h=this.colorAxis)):(a="gray",h=this.colorAxis),d=.5,g.lineWidth=d,g.fillStyle=a,g.strokeStyle=h,g.beginPath(),g.moveTo(t.screen.x,t.screen.y),g.lineTo(e.screen.x,e.screen.y),g.lineTo(o.screen.x,o.screen.y),g.lineTo(i.screen.x,i.screen.y),g.closePath(),g.fill(),g.stroke()}}else for(n=0;np&&(p=0);var u,m,f;this.style===s.STYLE.DOTCOLOR?(u=240*(1-(d.point.value-this.valueMin)*this.scale.value),m=this._hsv2rgb(u,1,1),f=this._hsv2rgb(u,1,.8)):this.style===s.STYLE.DOTSIZE?(m=this.colorDot,f=this.colorDotBorder):(u=240*(1-(d.point.z-this.zMin)*this.scale.z/this.verticalRatio),m=this._hsv2rgb(u,1,1),f=this._hsv2rgb(u,1,.8)),i.lineWidth=1,i.strokeStyle=f,i.fillStyle=m,i.beginPath(),i.arc(d.screen.x,d.screen.y,p,0,2*Math.PI,!0),i.fill(),i.stroke()}}},s.prototype._redrawDataBar=function(){var t,e,i,o,n=this.frame.canvas,r=n.getContext("2d");if(!(void 0===this.dataPoints||this.dataPoints.length<=0)){for(t=0;t0&&(t=this.dataPoints[0],s.lineWidth=1,s.strokeStyle="blue",s.beginPath(),s.moveTo(t.screen.x,t.screen.y)),e=1;e0&&s.stroke()}},s.prototype._onMouseDown=function(t){if(t=t||window.event,this.leftButtonDown&&this._onMouseUp(t),this.leftButtonDown=t.which?1===t.which:1===t.button,this.leftButtonDown||this.touchDown){this.startMouseX=o(t),this.startMouseY=n(t),this.startStart=new Date(this.start),this.startEnd=new Date(this.end),this.startArmRotation=this.camera.getArmRotation(),this.frame.style.cursor="move";var e=this;this.onmousemove=function(t){e._onMouseMove(t)},this.onmouseup=function(t){e._onMouseUp(t)},d.addEventListener(document,"mousemove",e.onmousemove),d.addEventListener(document,"mouseup",e.onmouseup),d.preventDefault(t)}},s.prototype._onMouseMove=function(t){t=t||window.event;var e=parseFloat(o(t))-this.startMouseX,i=parseFloat(n(t))-this.startMouseY,s=this.startArmRotation.horizontal+e/200,r=this.startArmRotation.vertical+i/200,a=4,h=Math.sin(a/360*2*Math.PI);Math.abs(Math.sin(s))0?1:0>t?-1:0}var s=e[0],o=e[1],n=e[2],r=i((o.x-s.x)*(t.y-s.y)-(o.y-s.y)*(t.x-s.x)),a=i((n.x-o.x)*(t.y-o.y)-(n.y-o.y)*(t.x-o.x)),h=i((s.x-n.x)*(t.y-n.y)-(s.y-n.y)*(t.x-n.x));return!(0!=r&&0!=a&&r!=a||0!=a&&0!=h&&a!=h||0!=r&&0!=h&&r!=h)},s.prototype._dataPointFromXY=function(t,e){var i,o=100,n=null,r=null,a=null,h=new c(t,e);if(this.style===s.STYLE.BAR||this.style===s.STYLE.BARCOLOR||this.style===s.STYLE.BARSIZE)for(i=this.dataPoints.length-1;i>=0;i--){n=this.dataPoints[i];var d=n.surfaces;if(d)for(var l=d.length-1;l>=0;l--){var p=d[l],u=p.corners,m=[u[0].screen,u[1].screen,u[2].screen],f=[u[2].screen,u[3].screen,u[0].screen];if(this._insideTriangle(h,m)||this._insideTriangle(h,f))return n}}else for(i=0;ib)&&o>b&&(a=b,r=n)}}return r},s.prototype._showTooltip=function(t){var e,i,s;this.tooltip?(e=this.tooltip.dom.content,i=this.tooltip.dom.line,s=this.tooltip.dom.dot):(e=document.createElement("div"),e.style.position="absolute",e.style.padding="10px",e.style.border="1px solid #4d4d4d",e.style.color="#1a1a1a",e.style.background="rgba(255,255,255,0.7)",e.style.borderRadius="2px",e.style.boxShadow="5px 5px 10px rgba(128,128,128,0.5)",i=document.createElement("div"),i.style.position="absolute",i.style.height="40px",i.style.width="0",i.style.borderLeft="1px solid #4d4d4d",s=document.createElement("div"),s.style.position="absolute",s.style.height="0",s.style.width="0",s.style.border="5px solid #4d4d4d",s.style.borderRadius="5px",this.tooltip={dataPoint:null,dom:{content:e,line:i,dot:s}}),this._hideTooltip(),this.tooltip.dataPoint=t,e.innerHTML="function"==typeof this.showTooltip?this.showTooltip(t.point):"
x:"+t.point.x+"
y:"+t.point.y+"
z:"+t.point.z+"
",e.style.left="0",e.style.top="0",this.frame.appendChild(e),this.frame.appendChild(i),this.frame.appendChild(s);var o=e.offsetWidth,n=e.offsetHeight,r=i.offsetHeight,a=s.offsetWidth,h=s.offsetHeight,d=t.screen.x-o/2;d=Math.min(Math.max(d,10),this.frame.clientWidth-10-o),i.style.left=t.screen.x+"px",i.style.top=t.screen.y-r+"px",e.style.left=d+"px",e.style.top=t.screen.y-r-n+"px",s.style.left=t.screen.x-a/2+"px",s.style.top=t.screen.y-h/2+"px"},s.prototype._hideTooltip=function(){if(this.tooltip){this.tooltip.dataPoint=null;for(var t in this.tooltip.dom)if(this.tooltip.dom.hasOwnProperty(t)){var e=this.tooltip.dom[t];e&&e.parentNode&&e.parentNode.removeChild(e)}}},t.exports=s},function(t,e,i){function s(){this.armLocation=new o,this.armRotation={},this.armRotation.horizontal=0,this.armRotation.vertical=0,this.armLength=1.7,this.cameraLocation=new o,this.cameraRotation=new o(.5*Math.PI,0,0),this.calculateCameraOrientation()}var o=i(10);s.prototype.setArmLocation=function(t,e,i){this.armLocation.x=t,this.armLocation.y=e,this.armLocation.z=i,this.calculateCameraOrientation()},s.prototype.setArmRotation=function(t,e){void 0!==t&&(this.armRotation.horizontal=t),void 0!==e&&(this.armRotation.vertical=e,this.armRotation.vertical<0&&(this.armRotation.vertical=0),this.armRotation.vertical>.5*Math.PI&&(this.armRotation.vertical=.5*Math.PI)),(void 0!==t||void 0!==e)&&this.calculateCameraOrientation()},s.prototype.getArmRotation=function(){var t={};return t.horizontal=this.armRotation.horizontal,t.vertical=this.armRotation.vertical,t},s.prototype.setArmLength=function(t){void 0!==t&&(this.armLength=t,this.armLength<.71&&(this.armLength=.71),this.armLength>5&&(this.armLength=5),this.calculateCameraOrientation())},s.prototype.getArmLength=function(){return this.armLength},s.prototype.getCameraLocation=function(){return this.cameraLocation},s.prototype.getCameraRotation=function(){return this.cameraRotation},s.prototype.calculateCameraOrientation=function(){this.cameraLocation.x=this.armLocation.x-this.armLength*Math.sin(this.armRotation.horizontal)*Math.cos(this.armRotation.vertical),this.cameraLocation.y=this.armLocation.y-this.armLength*Math.cos(this.armRotation.horizontal)*Math.cos(this.armRotation.vertical),this.cameraLocation.z=this.armLocation.z+this.armLength*Math.sin(this.armRotation.vertical),this.cameraRotation.x=Math.PI/2-this.armRotation.vertical,this.cameraRotation.y=0,this.cameraRotation.z=-this.armRotation.horizontal},t.exports=s},function(t,e,i){function s(t,e,i){this.data=t,this.column=e,this.graph=i,this.index=void 0,this.value=void 0,this.values=i.getDistinctValues(t.get(),this.column),this.values.sort(function(t,e){return t>e?1:e>t?-1:0}),this.values.length>0&&this.selectValue(0),this.dataPoints=[],this.loaded=!1,this.onLoadCallback=void 0,i.animationPreload?(this.loaded=!1,this.loadInBackground()):this.loaded=!0}var o=i(4);s.prototype.isLoaded=function(){return this.loaded},s.prototype.getLoadedProgress=function(){for(var t=this.values.length,e=0;this.dataPoints[e];)e++;return Math.round(e/t*100)},s.prototype.getLabel=function(){return this.graph.filterLabel},s.prototype.getColumn=function(){return this.column},s.prototype.getSelectedValue=function(){return void 0===this.index?void 0:this.values[this.index]},s.prototype.getValues=function(){return this.values},s.prototype.getValue=function(t){if(t>=this.values.length)throw"Error: index out of range";return this.values[t]},s.prototype._getDataPoints=function(t){if(void 0===t&&(t=this.index),void 0===t)return[];var e;if(this.dataPoints[t])e=this.dataPoints[t];else{var i={};i.column=this.column,i.value=this.values[t];var s=new o(this.data,{filter:function(t){return t[i.column]==i.value}}).get();e=this.graph._getDataPoints(s),this.dataPoints[t]=e}return e},s.prototype.setOnLoadCallback=function(t){this.onLoadCallback=t},s.prototype.selectValue=function(t){if(t>=this.values.length)throw"Error: index out of range";this.index=t,this.value=this.values[t]},s.prototype.loadInBackground=function(t){void 0===t&&(t=0);var e=this.graph.frame;if(t0&&(t--,this.setIndex(t))},s.prototype.next=function(){var t=this.getIndex();t0?this.setIndex(0):this.index=void 0},s.prototype.setIndex=function(t){if(!(ts&&(s=0),s>this.values.length-1&&(s=this.values.length-1),s},s.prototype.indexToLeft=function(t){var e=parseFloat(this.frame.bar.style.width)-this.frame.slide.clientWidth-10,i=t/(this.values.length-1)*e,s=i+3;return s},s.prototype._onMouseMove=function(t){var e=t.clientX-this.startClientX,i=this.startSlideX+e,s=this.leftToIndex(i);this.setIndex(s),o.preventDefault()},s.prototype._onMouseUp=function(){this.frame.style.cursor="auto",o.removeEventListener(document,"mousemove",this.onmousemove),o.removeEventListener(document,"mouseup",this.onmouseup),o.preventDefault()},t.exports=s},function(t){function e(t,e,i,s){this._start=0,this._end=0,this._step=1,this.prettyStep=!0,this.precision=5,this._current=0,this.setRange(t,e,i,s)}e.prototype.setRange=function(t,e,i,s){this._start=t?t:0,this._end=e?e:0,this.setStep(i,s)},e.prototype.setStep=function(t,i){void 0===t||0>=t||(void 0!==i&&(this.prettyStep=i),this._step=this.prettyStep===!0?e.calculatePrettyStep(t):t)},e.calculatePrettyStep=function(t){var e=function(t){return Math.log(t)/Math.LN10},i=Math.pow(10,Math.round(e(t))),s=2*Math.pow(10,Math.round(e(t/2))),o=5*Math.pow(10,Math.round(e(t/5))),n=i;return Math.abs(s-t)<=Math.abs(n-t)&&(n=s),Math.abs(o-t)<=Math.abs(n-t)&&(n=o),0>=n&&(n=1),n},e.prototype.getCurrent=function(){return parseFloat(this._current.toPrecision(this.precision))},e.prototype.getStep=function(){return this._step},e.prototype.start=function(){this._current=this._start-this._start%this._step},e.prototype.next=function(){this._current+=this._step},e.prototype.end=function(){return this._current>this._end},t.exports=e},function(t,e,i){function s(t,e,i,r){if(!(this instanceof s))throw new SyntaxError("Constructor must be called with the new operator");if(!(Array.isArray(i)||i instanceof n)&&i instanceof Object){var h=r;r=i,i=h}var u=this;this.defaultOptions={start:null,end:null,autoResize:!0,orientation:"bottom",width:null,height:null,maxHeight:null,minHeight:null},this.options=o.deepExtend({},this.defaultOptions),this._create(t),this.components=[],this.body={dom:this.dom,domProps:this.props,emitter:{on:this.on.bind(this),off:this.off.bind(this),emit:this.emit.bind(this)},hiddenDates:[],util:{snap:null,toScreen:u._toScreen.bind(u),toGlobalScreen:u._toGlobalScreen.bind(u),toTime:u._toTime.bind(u),toGlobalTime:u._toGlobalTime.bind(u)}},this.range=new a(this.body),this.components.push(this.range),this.body.range=this.range,this.timeAxis=new d(this.body),this.components.push(this.timeAxis),this.body.util.snap=this.timeAxis.snap.bind(this.timeAxis),this.currentTime=new l(this.body),this.components.push(this.currentTime),this.customTime=new c(this.body),this.components.push(this.customTime),this.itemSet=new p(this.body),this.components.push(this.itemSet),this.itemsData=null,this.groupsData=null,r&&this.setOptions(r),i&&this.setGroups(i),e?this.setItems(e):this.redraw()}var o=(i(56),i(45),i(1)),n=i(3),r=i(4),a=i(17),h=i(46),d=i(30),l=i(21),c=i(22),p=i(27);s.prototype=new h,s.prototype.setItems=function(t){var e,i=null==this.itemsData;if(e=t?t instanceof n||t instanceof r?t:new n(t,{type:{start:"Date",end:"Date"}}):null,this.itemsData=e,this.itemSet&&this.itemSet.setItems(e),i)if(void 0!=this.options.start||void 0!=this.options.end){if(void 0==this.options.start||void 0==this.options.end)var s=this._getDataRange();var o=void 0!=this.options.start?this.options.start:s.start,a=void 0!=this.options.end?this.options.end:s.end;this.setWindow(o,a,{animate:!1})}else this.fit({animate:!1})},s.prototype.setGroups=function(t){var e;e=t?t instanceof n||t instanceof r?t:new n(t):null,this.groupsData=e,this.itemSet.setGroups(e)},s.prototype.setSelection=function(t,e){this.itemSet&&this.itemSet.setSelection(t),e&&e.focus&&this.focus(t,e)},s.prototype.getSelection=function(){return this.itemSet&&this.itemSet.getSelection()||[]},s.prototype.focus=function(t,e){if(this.itemsData&&void 0!=t){var i=Array.isArray(t)?t:[t],s=this.itemsData.getDataSet().get(i,{type:{start:"Date",end:"Date"}}),o=null,n=null;if(s.forEach(function(t){var e=t.start.valueOf(),i="end"in t?t.end.valueOf():t.start.valueOf();(null===o||o>e)&&(o=e),(null===n||i>n)&&(n=i)}),null!==o&&null!==n){var r=(o+n)/2,a=Math.max(this.range.end-this.range.start,1.1*(n-o)),h=e&&void 0!==e.animate?e.animate:!0;this.range.setRange(r-a/2,r+a/2,h)}}},s.prototype.getItemRange=function(){var t=this.itemsData.getDataSet(),e=null,i=null;if(t){var s=t.min("start");e=s?o.convert(s.start,"Date").valueOf():null;var n=t.max("start");n&&(i=o.convert(n.start,"Date").valueOf());var r=t.max("end");r&&(i=null==i?o.convert(r.end,"Date").valueOf():Math.max(i,o.convert(r.end,"Date").valueOf()))}return{min:null!=e?new Date(e):null,max:null!=i?new Date(i):null}},t.exports=s},function(t,e,i){function s(t,e,i,s){if(!(Array.isArray(i)||i instanceof n)&&i instanceof Object){var r=s;s=i,i=r}var h=this;this.defaultOptions={start:null,end:null,autoResize:!0,orientation:"bottom",width:null,height:null,maxHeight:null,minHeight:null},this.options=o.deepExtend({},this.defaultOptions),this._create(t),this.components=[],this.body={dom:this.dom,domProps:this.props,emitter:{on:this.on.bind(this),off:this.off.bind(this),emit:this.emit.bind(this)},hiddenDates:[],util:{snap:null,toScreen:h._toScreen.bind(h),toGlobalScreen:h._toGlobalScreen.bind(h),toTime:h._toTime.bind(h),toGlobalTime:h._toGlobalTime.bind(h)}},this.range=new a(this.body),this.components.push(this.range),this.body.range=this.range,this.timeAxis=new d(this.body),this.components.push(this.timeAxis),this.body.util.snap=this.timeAxis.snap.bind(this.timeAxis),this.currentTime=new l(this.body),this.components.push(this.currentTime),this.customTime=new c(this.body),this.components.push(this.customTime),this.linegraph=new p(this.body),this.components.push(this.linegraph),this.itemsData=null,this.groupsData=null,s&&this.setOptions(s),i&&this.setGroups(i),e?this.setItems(e):this.redraw()}var o=(i(56),i(45),i(1)),n=i(3),r=i(4),a=i(17),h=i(46),d=i(30),l=i(21),c=i(22),p=i(29);s.prototype=new h,s.prototype.setItems=function(t){var e,i=null==this.itemsData;if(e=t?t instanceof n||t instanceof r?t:new n(t,{type:{start:"Date",end:"Date"}}):null,this.itemsData=e,this.linegraph&&this.linegraph.setItems(e),i)if(void 0!=this.options.start||void 0!=this.options.end){var s=void 0!=this.options.start?this.options.start:null,o=void 0!=this.options.end?this.options.end:null;this.setWindow(s,o,{animate:!1})}else this.fit({animate:!1})},s.prototype.setGroups=function(t){var e;e=t?t instanceof n||t instanceof r?t:new n(t):null,this.groupsData=e,this.linegraph.setGroups(e)},s.prototype.getLegend=function(t,e,i){return void 0===e&&(e=15),void 0===i&&(i=15),void 0!==this.linegraph.groups[t]?this.linegraph.groups[t].getLegend(e,i):"cannot find group:"+t},s.prototype.isGroupVisible=function(t){return void 0!==this.linegraph.groups[t]?this.linegraph.groups[t].visible&&(void 0===this.linegraph.options.groups.visibility[t]||1==this.linegraph.options.groups.visibility[t]):!1},s.prototype.getItemRange=function(){var t=null,e=null;for(var i in this.linegraph.groups)if(this.linegraph.groups.hasOwnProperty(i)&&1==this.linegraph.groups[i].visible)for(var s=0;sr?r:t,e=null==e?r:r>e?r:e}return{min:null!=t?new Date(t):null,max:null!=e?new Date(e):null}},t.exports=s},function(t,e,i){var s=i(44);e.convertHiddenOptions=function(t,e){if(t.hiddenDates=[],e&&1==Array.isArray(e)){for(var i=0;i=4*a){var p=0,u=n.clone();switch(i[h].repeat){case"daily":d.day()!=l.day()&&(p=1),d.dayOfYear(o.dayOfYear()),d.year(o.year()),d.subtract(7,"days"),l.dayOfYear(o.dayOfYear()),l.year(o.year()),l.subtract(7-p,"days"),u.add(1,"weeks");break;case"weekly":var m=l.diff(d,"days"),f=d.day();d.date(o.date()),d.month(o.month()),d.year(o.year()),l=d.clone(),d.day(f),l.day(f),l.add(m,"days"),d.subtract(1,"weeks"),l.subtract(1,"weeks"),u.add(1,"weeks");break;case"monthly":d.month()!=l.month()&&(p=1),d.month(o.month()),d.year(o.year()),d.subtract(1,"months"),l.month(o.month()),l.year(o.year()),l.subtract(1,"months"),l.add(p,"months"),u.add(1,"months");break;case"yearly":d.year()!=l.year()&&(p=1),d.year(o.year()),d.subtract(1,"years"),l.year(o.year()),l.subtract(1,"years"),l.add(p,"years"),u.add(1,"years");break;default:return void console.log("Wrong repeat format, allowed are: daily, weekly, monthly, yearly. Given:",i[h].repeat)}for(;u>d;)switch(t.hiddenDates.push({start:d.valueOf(),end:l.valueOf()}),i[h].repeat){case"daily":d.add(1,"days"),l.add(1,"days");break;case"weekly":d.add(1,"weeks"),l.add(1,"weeks");break;case"monthly":d.add(1,"months"),l.add(1,"months");break;case"yearly":d.add(1,"y"),l.add(1,"y");break;default:return void console.log("Wrong repeat format, allowed are: daily, weekly, monthly, yearly. Given:",i[h].repeat)}t.hiddenDates.push({start:d.valueOf(),end:l.valueOf()})}}e.removeDuplicates(t);var g=e.isHidden(t.range.start,t.hiddenDates),v=e.isHidden(t.range.end,t.hiddenDates),y=t.range.start,b=t.range.end;1==g.hidden&&(y=1==t.range.startToFront?g.startDate-1:g.endDate+1),1==v.hidden&&(b=1==t.range.endToFront?v.startDate-1:v.endDate+1),(1==g.hidden||1==v.hidden)&&t.range._applyRange(y,b)}},e.removeDuplicates=function(t){for(var e=t.hiddenDates,i=[],s=0;s=e[s].start&&e[o].end<=e[s].end?e[o].remove=!0:e[o].start>=e[s].start&&e[o].start<=e[s].end?(e[s].end=e[o].end,e[o].remove=!0):e[o].end>=e[s].start&&e[o].end<=e[s].end&&(e[s].start=e[o].start,e[o].remove=!0));for(var s=0;s=r&&a>o){i=!0;break}}if(1==i&&o=e&&i>r&&(s+=r-n)}return s},e.correctTimeForHidden=function(t,i,o){return o=s(o).toDate().valueOf(),o-=e.getHiddenDurationBefore(t,i,o)},e.getHiddenDurationBefore=function(t,e,i){var o=0;i=s(i).toDate().valueOf();for(var n=0;n=e.start&&a=a&&(o+=a-r)}return o},e.getAccumulatedHiddenDuration=function(t,e,i){for(var s=0,o=0,n=e.start,r=0;r=e.start&&h=i)break;s+=h-a}}return s},e.snapAwayFromHidden=function(t,i,s,o){var n=e.isHidden(i,t);return 1==n.hidden?0>s?1==o?n.startDate-(n.endDate-i)-1:n.startDate-1:1==o?n.endDate+(i-n.startDate)+1:n.endDate+1:i},e.isHidden=function(t,e){for(var i=0;i=s&&o>t)return{hidden:!0,startDate:s,endDate:o}}return{hidden:!1,startDate:s,endDate:o}}},function(t){function e(t,e,i,s,o,n){this.current=0,this.autoScale=!0,this.stepIndex=0,this.step=1,this.scale=1,this.marginStart,this.marginEnd,this.deadSpace=0,this.majorSteps=[1,2,5,10],this.minorSteps=[.25,.5,1,2],this.alignZeros=n,this.setRange(t,e,i,s,o)}e.prototype.setRange=function(t,e,i,s,o){this._start=void 0===o.min?t:o.min,this._end=void 0===o.max?e:o.max,this._start==this._end&&(this._start-=.75,this._end+=1),1==this.autoScale&&this.setMinimumStep(i,s),this.setFirst(o)},e.prototype.setMinimumStep=function(t,e){var i=this._end-this._start,s=1.2*i,o=t*(s/e),n=Math.round(Math.log(s)/Math.LN10),r=-1,a=Math.pow(10,n),h=0;0>n&&(h=n);for(var d=!1,l=h;Math.abs(l)<=Math.abs(n);l++){a=Math.pow(10,l);for(var c=0;c=o){d=!0,r=c;break}}if(1==d)break}this.stepIndex=r,this.scale=a,this.step=a*this.minorSteps[r]},e.prototype.setFirst=function(t){void 0===t&&(t={});var e=void 0===t.min?this._start-2*this.scale*this.minorSteps[this.stepIndex]:t.min,i=void 0===t.max?this._end+this.scale*this.minorSteps[this.stepIndex]:t.max;this.marginEnd=void 0===t.max?this.roundToMinor(i):t.max,this.marginStart=void 0===t.min?this.roundToMinor(e):t.min,1==this.alignZeros&&(this.marginEnd-this.marginStart)%this.step!=0&&(this.marginEnd+=this.marginEnd%this.step),this.deadSpace=this.roundToMinor(i)-i+this.roundToMinor(e)-e,this.marginRange=this.marginEnd-this.marginStart,this.current=this.marginEnd},e.prototype.roundToMinor=function(t){var e=t-t%(this.scale*this.minorSteps[this.stepIndex]);return t%(this.scale*this.minorSteps[this.stepIndex])>.5*this.scale*this.minorSteps[this.stepIndex]?e+this.scale*this.minorSteps[this.stepIndex]:e},e.prototype.hasNext=function(){return this.current>=this.marginStart},e.prototype.next=function(){var t=this.current;this.current-=this.step,this.current==t&&(this.current=this._end)},e.prototype.previous=function(){this.current+=this.step,this.marginEnd+=this.step,this.marginRange=this.marginEnd-this.marginStart},e.prototype.getCurrent=function(t){var e=Math.abs(this.current)0;s--){if("0"!=i[s]){if("."==i[s]||","==i[s]){i=i.slice(0,s);break}break}i=i.slice(0,s)}}else{var o="",n=i.indexOf("e");if(-1!=n&&(o=i.slice(n),i=i.slice(0,n)),n=Math.max(i.indexOf(","),i.indexOf(".")),-1===n?(0!==t&&(i+="."),n=i.length+t):0!==t&&(n+=t+1),n>i.length)for(var r=n-i.length;r>0;r--)i+="0";else i=i.slice(0,n);i+=o}return i},e.prototype.snap=function(){},e.prototype.isMajor=function(){return this.current%(this.scale*this.majorSteps[this.stepIndex])==0},t.exports=e},function(t,e,i){function s(t,e){var i=h().hours(0).minutes(0).seconds(0).milliseconds(0);this.start=i.clone().add(-3,"days").valueOf(),this.end=i.clone().add(4,"days").valueOf(),this.body=t,this.deltaDifference=0,this.scaleOffset=0,this.startToFront=!1,this.endToFront=!0,this.defaultOptions={start:null,end:null,direction:"horizontal",moveable:!0,zoomable:!0,min:null,max:null,zoomMin:10,zoomMax:31536e10},this.options=r.extend({},this.defaultOptions),this.props={touch:{}},this.animateTimer=null,this.body.emitter.on("dragstart",this._onDragStart.bind(this)),this.body.emitter.on("drag",this._onDrag.bind(this)),this.body.emitter.on("dragend",this._onDragEnd.bind(this)),this.body.emitter.on("hold",this._onHold.bind(this)),this.body.emitter.on("mousewheel",this._onMouseWheel.bind(this)),this.body.emitter.on("DOMMouseScroll",this._onMouseWheel.bind(this)),this.body.emitter.on("touch",this._onTouch.bind(this)),this.body.emitter.on("pinch",this._onPinch.bind(this)),this.setOptions(e)}function o(t){if("horizontal"!=t&&"vertical"!=t)throw new TypeError('Unknown direction "'+t+'". Choose "horizontal" or "vertical".')}function n(t,e){return{x:t.pageX-r.getAbsoluteLeft(e),y:t.pageY-r.getAbsoluteTop(e)}}var r=i(1),a=i(47),h=i(44),d=i(20),l=i(15);s.prototype=new d,s.prototype.setOptions=function(t){if(t){var e=["direction","min","max","zoomMin","zoomMax","moveable","zoomable","activate","hiddenDates"];r.selectiveExtend(e,this.options,t),("start"in t||"end"in t)&&this.setRange(t.start,t.end)}},s.prototype.setRange=function(t,e,i,s){s!==!0&&(s=!1);var o=void 0!=t?r.convert(t,"Date").valueOf():null,n=void 0!=e?r.convert(e,"Date").valueOf():null;if(this._cancelAnimation(),i){var a=this,h=this.start,d=this.end,c="number"==typeof i?i:500,p=(new Date).valueOf(),u=!1,m=function(){if(!a.props.touch.dragging){var t=(new Date).valueOf(),e=t-p,i=e>c,g=i||null===o?o:r.easeInOutQuad(e,h,o,c),v=i||null===n?n:r.easeInOutQuad(e,d,n,c);f=a._applyRange(g,v),l.updateHiddenDates(a.body,a.options.hiddenDates),u=u||f,f&&a.body.emitter.emit("rangechange",{start:new Date(a.start),end:new Date(a.end),byUser:s}),i?u&&a.body.emitter.emit("rangechanged",{start:new Date(a.start),end:new Date(a.end),byUser:s}):a.animateTimer=setTimeout(m,20)}};return m()}var f=this._applyRange(o,n);if(l.updateHiddenDates(this.body,this.options.hiddenDates),f){var g={start:new Date(this.start),end:new Date(this.end),byUser:s};this.body.emitter.emit("rangechange",g),this.body.emitter.emit("rangechanged",g)}},s.prototype._cancelAnimation=function(){this.animateTimer&&(clearTimeout(this.animateTimer),this.animateTimer=null)},s.prototype._applyRange=function(t,e){var i,s=null!=t?r.convert(t,"Date").valueOf():this.start,o=null!=e?r.convert(e,"Date").valueOf():this.end,n=null!=this.options.max?r.convert(this.options.max,"Date").valueOf():null,a=null!=this.options.min?r.convert(this.options.min,"Date").valueOf():null;if(isNaN(s)||null===s)throw new Error('Invalid start "'+t+'"');if(isNaN(o)||null===o)throw new Error('Invalid end "'+e+'"');if(s>o&&(o=s),null!==a&&a>s&&(i=a-s,s+=i,o+=i,null!=n&&o>n&&(o=n)),null!==n&&o>n&&(i=o-n,s-=i,o-=i,null!=a&&a>s&&(s=a)),null!==this.options.zoomMin){var h=parseFloat(this.options.zoomMin);0>h&&(h=0),h>o-s&&(this.end-this.start===h?(s=this.start,o=this.end):(i=h-(o-s),s-=i/2,o+=i/2))}if(null!==this.options.zoomMax){var d=parseFloat(this.options.zoomMax);0>d&&(d=0),o-s>d&&(this.end-this.start===d?(s=this.start,o=this.end):(i=o-s-d,s+=i/2,o-=i/2))}var l=this.start!=s||this.end!=o;return s>=this.start&&s<=this.end||o>=this.start&&o<=this.end||this.start>=s&&this.start<=o||this.end>=s&&this.end<=o||this.body.emitter.emit("checkRangedItems"),this.start=s,this.end=o,l},s.prototype.getRange=function(){return{start:this.start,end:this.end}},s.prototype.conversion=function(t,e){return s.conversion(this.start,this.end,t,e)},s.conversion=function(t,e,i,s){return void 0===s&&(s=0),0!=i&&e-t!=0?{offset:t,scale:i/(e-t-s)}:{offset:0,scale:1}},s.prototype._onDragStart=function(){this.deltaDifference=0,this.previousDelta=0,this.options.moveable&&this.props.touch.allowDragging&&(this.props.touch.start=this.start,this.props.touch.end=this.end,this.props.touch.dragging=!0,this.body.dom.root&&(this.body.dom.root.style.cursor="move"))},s.prototype._onDrag=function(t){if(this.options.moveable&&this.props.touch.allowDragging){var e=this.options.direction;o(e);var i="horizontal"==e?t.gesture.deltaX:t.gesture.deltaY;i-=this.deltaDifference;var s=this.props.touch.end-this.props.touch.start,n=l.getHiddenDurationBetween(this.body.hiddenDates,this.start,this.end);s-=n;var r="horizontal"==e?this.body.domProps.center.width:this.body.domProps.center.height,a=-i/r*s,h=this.props.touch.start+a,d=this.props.touch.end+a,c=l.snapAwayFromHidden(this.body.hiddenDates,h,this.previousDelta-i,!0),p=l.snapAwayFromHidden(this.body.hiddenDates,d,this.previousDelta-i,!0);if(c!=h||p!=d)return this.deltaDifference+=i,this.props.touch.start=c,this.props.touch.end=p,void this._onDrag(t);this.previousDelta=i,this._applyRange(h,d),this.body.emitter.emit("rangechange",{start:new Date(this.start),end:new Date(this.end),byUser:!0})}},s.prototype._onDragEnd=function(){this.options.moveable&&this.props.touch.allowDragging&&(this.props.touch.dragging=!1,this.body.dom.root&&(this.body.dom.root.style.cursor="auto"),this.body.emitter.emit("rangechanged",{start:new Date(this.start),end:new Date(this.end),byUser:!0}))},s.prototype._onMouseWheel=function(t){if(this.options.zoomable&&this.options.moveable){var e=0;if(t.wheelDelta?e=t.wheelDelta/120:t.detail&&(e=-t.detail/3),e){var i;i=0>e?1-e/5:1/(1+e/5);var s=a.fakeGesture(this,t),o=n(s.center,this.body.dom.center),r=this._pointerToDate(o);this.zoom(i,r,e)}t.preventDefault()}},s.prototype._onTouch=function(){this.props.touch.start=this.start,this.props.touch.end=this.end,this.props.touch.allowDragging=!0,this.props.touch.center=null,this.scaleOffset=0,this.deltaDifference=0},s.prototype._onHold=function(){this.props.touch.allowDragging=!1},s.prototype._onPinch=function(t){if(this.options.zoomable&&this.options.moveable&&(this.props.touch.allowDragging=!1,t.gesture.touches.length>1)){this.props.touch.center||(this.props.touch.center=n(t.gesture.center,this.body.dom.center));var e=1/(t.gesture.scale+this.scaleOffset),i=this._pointerToDate(this.props.touch.center),s=l.getHiddenDurationBetween(this.body.hiddenDates,this.start,this.end),o=l.getHiddenDurationBefore(this.body.hiddenDates,this,i),r=s-o,a=i-o+(this.props.touch.start-(i-o))*e,h=i+r+(this.props.touch.end-(i+r))*e;this.startToFront=1-e>0?!1:!0,this.endToFront=e-1>0?!1:!0;var d=l.snapAwayFromHidden(this.body.hiddenDates,a,1-e,!0),c=l.snapAwayFromHidden(this.body.hiddenDates,h,e-1,!0);(d!=a||c!=h)&&(this.props.touch.start=d,this.props.touch.end=c,this.scaleOffset=1-t.gesture.scale,a=d,h=c),this.setRange(a,h,!1,!0),this.startToFront=!1,this.endToFront=!0}},s.prototype._pointerToDate=function(t){var e,i=this.options.direction;if(o(i),"horizontal"==i)return this.body.util.toTime(t.x).valueOf();var s=this.body.domProps.center.height;return e=this.conversion(s),t.y/e.scale+e.offset},s.prototype.zoom=function(t,e,i){null==e&&(e=(this.start+this.end)/2);var s=l.getHiddenDurationBetween(this.body.hiddenDates,this.start,this.end),o=l.getHiddenDurationBefore(this.body.hiddenDates,this,e),n=s-o,r=e-o+(this.start-(e-o))*t,a=e+n+(this.end-(e+n))*t;this.startToFront=i>0?!1:!0,this.endToFront=-i>0?!1:!0;var h=l.snapAwayFromHidden(this.body.hiddenDates,r,i,!0),d=l.snapAwayFromHidden(this.body.hiddenDates,a,-i,!0);(h!=r||d!=a)&&(r=h,a=d),this.setRange(r,a,!1,!0),this.startToFront=!1,this.endToFront=!0},s.prototype.move=function(t){var e=this.end-this.start,i=this.start+e*t,s=this.end+e*t;this.start=i,this.end=s},s.prototype.moveTo=function(t){var e=(this.start+this.end)/2,i=e-t,s=this.start-i,o=this.end-i;this.setRange(s,o)},t.exports=s},function(t,e){var i=.001;e.orderByStart=function(t){t.sort(function(t,e){return t.data.start-e.data.start})},e.orderByEnd=function(t){t.sort(function(t,e){var i="end"in t.data?t.data.end:t.data.start,s="end"in e.data?e.data.end:e.data.start;return i-s})},e.stack=function(t,i,s){var o,n;if(s)for(o=0,n=t.length;n>o;o++)t[o].top=null;for(o=0,n=t.length;n>o;o++){var r=t[o];if(r.stack&&null===r.top){r.top=i.axis;do{for(var a=null,h=0,d=t.length;d>h;h++){var l=t[h];if(null!==l.top&&l!==r&&l.stack&&e.collision(r,l,i.item)){a=l;break}}null!=a&&(r.top=a.top+a.height+i.item.vertical)}while(a)}}},e.nostack=function(t,e,i){var s,o,n;for(s=0,o=t.length;o>s;s++)if(void 0!==t[s].data.subgroup){n=e.axis;for(var r in i)i.hasOwnProperty(r)&&1==i[r].visible&&i[r].indexe.left&&t.top-s.vertical+ie.top}},function(t,e,i){function s(t,e,i,o){this.current=new Date,this._start=new Date,this._end=new Date,this.autoScale=!0,this.scale="day",this.step=1,this.setRange(t,e,i),this.switchedDay=!1,this.switchedMonth=!1,this.switchedYear=!1,this.hiddenDates=o,void 0===o&&(this.hiddenDates=[]),this.format=s.FORMAT}var o=i(44),n=i(15),r=i(1);s.FORMAT={minorLabels:{millisecond:"SSS",second:"s",minute:"HH:mm",hour:"HH:mm",weekday:"ddd D",day:"D",month:"MMM",year:"YYYY"},majorLabels:{millisecond:"HH:mm:ss",second:"D MMMM HH:mm",minute:"ddd D MMMM",hour:"ddd D MMMM",weekday:"MMMM YYYY",day:"MMMM YYYY",month:"YYYY",year:""}},s.prototype.setFormat=function(t){var e=r.deepExtend({},s.FORMAT);this.format=r.deepExtend(e,t)},s.prototype.setRange=function(t,e,i){if(!(t instanceof Date&&e instanceof Date))throw"No legal start or end date in method setRange";this._start=void 0!=t?new Date(t.valueOf()):new Date,this._end=void 0!=e?new Date(e.valueOf()):new Date,this.autoScale&&this.setMinimumStep(i)},s.prototype.first=function(){this.current=new Date(this._start.valueOf()),this.roundToMinor()},s.prototype.roundToMinor=function(){switch(this.scale){case"year":this.current.setFullYear(this.step*Math.floor(this.current.getFullYear()/this.step)),this.current.setMonth(0);case"month":this.current.setDate(1);case"day":case"weekday":this.current.setHours(0);case"hour":this.current.setMinutes(0);case"minute":this.current.setSeconds(0);case"second":this.current.setMilliseconds(0)}if(1!=this.step)switch(this.scale){case"millisecond":this.current.setMilliseconds(this.current.getMilliseconds()-this.current.getMilliseconds()%this.step);break;case"second":this.current.setSeconds(this.current.getSeconds()-this.current.getSeconds()%this.step);break;case"minute":this.current.setMinutes(this.current.getMinutes()-this.current.getMinutes()%this.step);break;case"hour":this.current.setHours(this.current.getHours()-this.current.getHours()%this.step);break;case"weekday":case"day":this.current.setDate(this.current.getDate()-1-(this.current.getDate()-1)%this.step+1);break;case"month":this.current.setMonth(this.current.getMonth()-this.current.getMonth()%this.step);break;case"year":this.current.setFullYear(this.current.getFullYear()-this.current.getFullYear()%this.step)}},s.prototype.hasNext=function(){return this.current.valueOf()<=this._end.valueOf()},s.prototype.next=function(){var t=this.current.valueOf();if(this.current.getMonth()<6)switch(this.scale){case"millisecond":this.current=new Date(this.current.valueOf()+this.step);break;case"second":this.current=new Date(this.current.valueOf()+1e3*this.step);break;case"minute":this.current=new Date(this.current.valueOf()+1e3*this.step*60);break;case"hour":this.current=new Date(this.current.valueOf()+1e3*this.step*60*60);var e=this.current.getHours();this.current.setHours(e-e%this.step);break;case"weekday":case"day":this.current.setDate(this.current.getDate()+this.step); -break;case"month":this.current.setMonth(this.current.getMonth()+this.step);break;case"year":this.current.setFullYear(this.current.getFullYear()+this.step)}else switch(this.scale){case"millisecond":this.current=new Date(this.current.valueOf()+this.step);break;case"second":this.current.setSeconds(this.current.getSeconds()+this.step);break;case"minute":this.current.setMinutes(this.current.getMinutes()+this.step);break;case"hour":this.current.setHours(this.current.getHours()+this.step);break;case"weekday":case"day":this.current.setDate(this.current.getDate()+this.step);break;case"month":this.current.setMonth(this.current.getMonth()+this.step);break;case"year":this.current.setFullYear(this.current.getFullYear()+this.step)}if(1!=this.step)switch(this.scale){case"millisecond":this.current.getMilliseconds()0?t.step:1,this.autoScale=!1)},s.prototype.setAutoScale=function(t){this.autoScale=t},s.prototype.setMinimumStep=function(t){if(void 0!=t){var e=31104e6,i=2592e6,s=864e5,o=36e5,n=6e4,r=1e3,a=1;1e3*e>t&&(this.scale="year",this.step=1e3),500*e>t&&(this.scale="year",this.step=500),100*e>t&&(this.scale="year",this.step=100),50*e>t&&(this.scale="year",this.step=50),10*e>t&&(this.scale="year",this.step=10),5*e>t&&(this.scale="year",this.step=5),e>t&&(this.scale="year",this.step=1),3*i>t&&(this.scale="month",this.step=3),i>t&&(this.scale="month",this.step=1),5*s>t&&(this.scale="day",this.step=5),2*s>t&&(this.scale="day",this.step=2),s>t&&(this.scale="day",this.step=1),s/2>t&&(this.scale="weekday",this.step=1),4*o>t&&(this.scale="hour",this.step=4),o>t&&(this.scale="hour",this.step=1),15*n>t&&(this.scale="minute",this.step=15),10*n>t&&(this.scale="minute",this.step=10),5*n>t&&(this.scale="minute",this.step=5),n>t&&(this.scale="minute",this.step=1),15*r>t&&(this.scale="second",this.step=15),10*r>t&&(this.scale="second",this.step=10),5*r>t&&(this.scale="second",this.step=5),r>t&&(this.scale="second",this.step=1),200*a>t&&(this.scale="millisecond",this.step=200),100*a>t&&(this.scale="millisecond",this.step=100),50*a>t&&(this.scale="millisecond",this.step=50),10*a>t&&(this.scale="millisecond",this.step=10),5*a>t&&(this.scale="millisecond",this.step=5),a>t&&(this.scale="millisecond",this.step=1)}},s.prototype.snap=function(t){var e=new Date(t.valueOf());if("year"==this.scale){var i=e.getFullYear()+Math.round(e.getMonth()/12);e.setFullYear(Math.round(i/this.step)*this.step),e.setMonth(0),e.setDate(0),e.setHours(0),e.setMinutes(0),e.setSeconds(0),e.setMilliseconds(0)}else if("month"==this.scale)e.getDate()>15?(e.setDate(1),e.setMonth(e.getMonth()+1)):e.setDate(1),e.setHours(0),e.setMinutes(0),e.setSeconds(0),e.setMilliseconds(0);else if("day"==this.scale){switch(this.step){case 5:case 2:e.setHours(24*Math.round(e.getHours()/24));break;default:e.setHours(12*Math.round(e.getHours()/12))}e.setMinutes(0),e.setSeconds(0),e.setMilliseconds(0)}else if("weekday"==this.scale){switch(this.step){case 5:case 2:e.setHours(12*Math.round(e.getHours()/12));break;default:e.setHours(6*Math.round(e.getHours()/6))}e.setMinutes(0),e.setSeconds(0),e.setMilliseconds(0)}else if("hour"==this.scale){switch(this.step){case 4:e.setMinutes(60*Math.round(e.getMinutes()/60));break;default:e.setMinutes(30*Math.round(e.getMinutes()/30))}e.setSeconds(0),e.setMilliseconds(0)}else if("minute"==this.scale){switch(this.step){case 15:case 10:e.setMinutes(5*Math.round(e.getMinutes()/5)),e.setSeconds(0);break;case 5:e.setSeconds(60*Math.round(e.getSeconds()/60));break;default:e.setSeconds(30*Math.round(e.getSeconds()/30))}e.setMilliseconds(0)}else if("second"==this.scale)switch(this.step){case 15:case 10:e.setSeconds(5*Math.round(e.getSeconds()/5)),e.setMilliseconds(0);break;case 5:e.setMilliseconds(1e3*Math.round(e.getMilliseconds()/1e3));break;default:e.setMilliseconds(500*Math.round(e.getMilliseconds()/500))}else if("millisecond"==this.scale){var s=this.step>5?this.step/2:1;e.setMilliseconds(Math.round(e.getMilliseconds()/s)*s)}return e},s.prototype.isMajor=function(){if(1==this.switchedYear)switch(this.switchedYear=!1,this.scale){case"year":case"month":case"weekday":case"day":case"hour":case"minute":case"second":case"millisecond":return!0;default:return!1}else if(1==this.switchedMonth)switch(this.switchedMonth=!1,this.scale){case"weekday":case"day":case"hour":case"minute":case"second":case"millisecond":return!0;default:return!1}else if(1==this.switchedDay)switch(this.switchedDay=!1,this.scale){case"millisecond":case"second":case"minute":case"hour":return!0;default:return!1}switch(this.scale){case"millisecond":return 0==this.current.getMilliseconds();case"second":return 0==this.current.getSeconds();case"minute":return 0==this.current.getHours()&&0==this.current.getMinutes();case"hour":return 0==this.current.getHours();case"weekday":case"day":return 1==this.current.getDate();case"month":return 0==this.current.getMonth();case"year":return!1;default:return!1}},s.prototype.getLabelMinor=function(t){void 0==t&&(t=this.current);var e=this.format.minorLabels[this.scale];return e&&e.length>0?o(t).format(e):""},s.prototype.getLabelMajor=function(t){void 0==t&&(t=this.current);var e=this.format.majorLabels[this.scale];return e&&e.length>0?o(t).format(e):""},s.prototype.getClassName=function(){function t(t){return t/h%2==0?" even":" odd"}function e(t){return t.isSame(new Date,"day")?" today":t.isSame(o().add(1,"day"),"day")?" tomorrow":t.isSame(o().add(-1,"day"),"day")?" yesterday":""}function i(t){return t.isSame(new Date,"week")?" current-week":""}function s(t){return t.isSame(new Date,"month")?" current-month":""}function n(t){return t.isSame(new Date,"year")?" current-year":""}var r=o(this.current),a=r.locale?r.locale("en"):r.lang("en"),h=this.step;switch(this.scale){case"millisecond":return t(a.milliseconds()).trim();case"second":return t(a.seconds()).trim();case"minute":return t(a.minutes()).trim();case"hour":var d=a.hours();return 4==this.step&&(d=d+"-"+(d+4)),d+"h"+e(a)+t(a.hours());case"weekday":return a.format("dddd").toLowerCase()+e(a)+i(a)+t(a.date());case"day":var l=a.date(),c=a.format("MMMM").toLowerCase();return"day"+l+" "+c+s(a)+t(l-1);case"month":return a.format("MMMM").toLowerCase()+s(a)+t(a.month());case"year":var p=a.year();return"year"+p+n(a)+t(p);default:return""}},t.exports=s},function(t){function e(){this.options=null,this.props=null}e.prototype.setOptions=function(t){t&&util.extend(this.options,t)},e.prototype.redraw=function(){return!1},e.prototype.destroy=function(){},e.prototype._isResized=function(){var t=this.props._previousWidth!==this.props.width||this.props._previousHeight!==this.props.height;return this.props._previousWidth=this.props.width,this.props._previousHeight=this.props.height,t},t.exports=e},function(t,e,i){function s(t,e){this.body=t,this.defaultOptions={showCurrentTime:!0,locales:a,locale:"en"},this.options=o.extend({},this.defaultOptions),this.offset=0,this._create(),this.setOptions(e)}var o=i(1),n=i(20),r=i(44),a=i(48);s.prototype=new n,s.prototype._create=function(){var t=document.createElement("div");t.className="currenttime",t.style.position="absolute",t.style.top="0px",t.style.height="100%",this.bar=t},s.prototype.destroy=function(){this.options.showCurrentTime=!1,this.redraw(),this.body=null},s.prototype.setOptions=function(t){t&&o.selectiveExtend(["showCurrentTime","locale","locales"],this.options,t)},s.prototype.redraw=function(){if(this.options.showCurrentTime){var t=this.body.dom.backgroundVertical;this.bar.parentNode!=t&&(this.bar.parentNode&&this.bar.parentNode.removeChild(this.bar),t.appendChild(this.bar),this.start());var e=new Date((new Date).valueOf()+this.offset),i=this.body.util.toScreen(e),s=this.options.locales[this.options.locale],o=s.current+" "+s.time+": "+r(e).format("dddd, MMMM Do YYYY, H:mm:ss");o=o.charAt(0).toUpperCase()+o.substring(1),this.bar.style.left=i+"px",this.bar.title=o}else this.bar.parentNode&&this.bar.parentNode.removeChild(this.bar),this.stop();return!1},s.prototype.start=function(){function t(){e.stop();var i=e.body.range.conversion(e.body.domProps.center.width).scale,s=1/i/10;30>s&&(s=30),s>1e3&&(s=1e3),e.redraw(),e.currentTimeTimer=setTimeout(t,s)}var e=this;t()},s.prototype.stop=function(){void 0!==this.currentTimeTimer&&(clearTimeout(this.currentTimeTimer),delete this.currentTimeTimer)},s.prototype.setCurrentTime=function(t){var e=o.convert(t,"Date").valueOf(),i=(new Date).valueOf();this.offset=e-i,this.redraw()},s.prototype.getCurrentTime=function(){return new Date((new Date).valueOf()+this.offset)},t.exports=s},function(t,e,i){function s(t,e){this.body=t,this.defaultOptions={showCustomTime:!1,locales:h,locale:"en"},this.options=n.extend({},this.defaultOptions),this.customTime=new Date,this.eventParams={},this._create(),this.setOptions(e)}var o=i(45),n=i(1),r=i(20),a=i(44),h=i(48);s.prototype=new r,s.prototype.setOptions=function(t){t&&n.selectiveExtend(["showCustomTime","locale","locales"],this.options,t)},s.prototype._create=function(){var t=document.createElement("div");t.className="customtime",t.style.position="absolute",t.style.top="0px",t.style.height="100%",this.bar=t;var e=document.createElement("div");e.style.position="relative",e.style.top="0px",e.style.left="-10px",e.style.height="100%",e.style.width="20px",t.appendChild(e),this.hammer=o(t,{prevent_default:!0}),this.hammer.on("dragstart",this._onDragStart.bind(this)),this.hammer.on("drag",this._onDrag.bind(this)),this.hammer.on("dragend",this._onDragEnd.bind(this))},s.prototype.destroy=function(){this.options.showCustomTime=!1,this.redraw(),this.hammer.enable(!1),this.hammer=null,this.body=null},s.prototype.redraw=function(){if(this.options.showCustomTime){var t=this.body.dom.backgroundVertical;this.bar.parentNode!=t&&(this.bar.parentNode&&this.bar.parentNode.removeChild(this.bar),t.appendChild(this.bar));var e=this.body.util.toScreen(this.customTime),i=this.options.locales[this.options.locale],s=i.time+": "+a(this.customTime).format("dddd, MMMM Do YYYY, H:mm:ss");s=s.charAt(0).toUpperCase()+s.substring(1),this.bar.style.left=e+"px",this.bar.title=s}else this.bar.parentNode&&this.bar.parentNode.removeChild(this.bar);return!1},s.prototype.setCustomTime=function(t){this.customTime=n.convert(t,"Date"),this.redraw()},s.prototype.getCustomTime=function(){return new Date(this.customTime.valueOf())},s.prototype._onDragStart=function(t){this.eventParams.dragging=!0,this.eventParams.customTime=this.customTime,t.stopPropagation(),t.preventDefault()},s.prototype._onDrag=function(t){if(this.eventParams.dragging){var e=t.gesture.deltaX,i=this.body.util.toScreen(this.eventParams.customTime)+e,s=this.body.util.toTime(i);this.setCustomTime(s),this.body.emitter.emit("timechange",{time:new Date(this.customTime.valueOf())}),t.stopPropagation(),t.preventDefault()}},s.prototype._onDragEnd=function(t){this.eventParams.dragging&&(this.body.emitter.emit("timechanged",{time:new Date(this.customTime.valueOf())}),t.stopPropagation(),t.preventDefault())},t.exports=s},function(t,e,i){function s(t,e,i,s){this.id=o.randomUUID(),this.body=t,this.defaultOptions={orientation:"left",showMinorLabels:!0,showMajorLabels:!0,icons:!0,majorLinesOffset:7,minorLinesOffset:4,labelOffsetX:10,labelOffsetY:2,iconWidth:20,width:"40px",visible:!0,alignZeros:!0,customRange:{left:{min:void 0,max:void 0},right:{min:void 0,max:void 0}},title:{left:{text:void 0},right:{text:void 0}},format:{left:{decimals:void 0},right:{decimals:void 0}}},this.linegraphOptions=s,this.linegraphSVG=i,this.props={},this.DOMelements={lines:{},labels:{},title:{}},this.dom={},this.range={start:0,end:0},this.options=o.extend({},this.defaultOptions),this.conversionFactor=1,this.setOptions(e),this.width=Number((""+this.options.width).replace("px","")),this.minWidth=this.width,this.height=this.linegraphSVG.offsetHeight,this.hidden=!1,this.stepPixels=25,this.stepPixelsForced=25,this.zeroCrossing=-1,this.lineOffset=0,this.master=!0,this.svgElements={},this.iconsRemoved=!1,this.groups={},this.amountOfGroups=0,this._create();var n=this;this.body.emitter.on("verticalDrag",function(){n.dom.lineContainer.style.top=n.body.domProps.scrollTop+"px"})}var o=i(1),n=i(2),r=i(20),a=i(16);s.prototype=new r,s.prototype.addGroup=function(t,e){this.groups.hasOwnProperty(t)||(this.groups[t]=e),this.amountOfGroups+=1},s.prototype.updateGroup=function(t,e){this.groups[t]=e},s.prototype.removeGroup=function(t){this.groups.hasOwnProperty(t)&&(delete this.groups[t],this.amountOfGroups-=1)},s.prototype.setOptions=function(t){if(t){var e=!1;this.options.orientation!=t.orientation&&void 0!==t.orientation&&(e=!0);var i=["orientation","showMinorLabels","showMajorLabels","icons","majorLinesOffset","minorLinesOffset","labelOffsetX","labelOffsetY","iconWidth","width","visible","customRange","title","format","alignZeros"];o.selectiveExtend(i,this.options,t),this.minWidth=Number((""+this.options.width).replace("px","")),1==e&&this.dom.frame&&(this.hide(),this.show())}},s.prototype._create=function(){this.dom.frame=document.createElement("div"),this.dom.frame.style.width=this.options.width,this.dom.frame.style.height=this.height,this.dom.lineContainer=document.createElement("div"),this.dom.lineContainer.style.width="100%",this.dom.lineContainer.style.height=this.height,this.dom.lineContainer.style.position="relative",this.svg=document.createElementNS("http://www.w3.org/2000/svg","svg"),this.svg.style.position="absolute",this.svg.style.top="0px",this.svg.style.height="100%",this.svg.style.width="100%",this.svg.style.display="block",this.dom.frame.appendChild(this.svg)},s.prototype._redrawGroupIcons=function(){n.prepareElements(this.svgElements);var t,e=this.options.iconWidth,i=15,s=4,o=s+.5*i;t="left"==this.options.orientation?s:this.width-e-s;for(var r in this.groups)this.groups.hasOwnProperty(r)&&(1!=this.groups[r].visible||void 0!==this.linegraphOptions.visibility[r]&&1!=this.linegraphOptions.visibility[r]||(this.groups[r].drawIcon(t,o,this.svgElements,this.svg,e,i),o+=i+s));n.cleanupElements(this.svgElements),this.iconsRemoved=!1},s.prototype._cleanupIcons=function(){0==this.iconsRemoved&&(n.prepareElements(this.svgElements),n.cleanupElements(this.svgElements),this.iconsRemoved=!0)},s.prototype.show=function(){this.hidden=!1,this.dom.frame.parentNode||("left"==this.options.orientation?this.body.dom.left.appendChild(this.dom.frame):this.body.dom.right.appendChild(this.dom.frame)),this.dom.lineContainer.parentNode||this.body.dom.backgroundHorizontal.appendChild(this.dom.lineContainer)},s.prototype.hide=function(){this.hidden=!0,this.dom.frame.parentNode&&this.dom.frame.parentNode.removeChild(this.dom.frame),this.dom.lineContainer.parentNode&&this.dom.lineContainer.parentNode.removeChild(this.dom.lineContainer)},s.prototype.setRange=function(t,e){0==this.master&&1==this.options.alignZeros&&-1!=this.zeroCrossing&&t>0&&(t=0),this.range.start=t,this.range.end=e},s.prototype.redraw=function(){var t=!1,e=0;this.dom.lineContainer.style.top=this.body.domProps.scrollTop+"px";for(var i in this.groups)this.groups.hasOwnProperty(i)&&(1!=this.groups[i].visible||void 0!==this.linegraphOptions.visibility[i]&&1!=this.linegraphOptions.visibility[i]||e++);if(0==this.amountOfGroups||0==e)this.hide();else{this.show(),this.height=Number(this.linegraphSVG.style.height.replace("px","")),this.dom.lineContainer.style.height=this.height+"px",this.width=1==this.options.visible?Number((""+this.options.width).replace("px","")):0;var s=this.props,o=this.dom.frame;o.className="dataaxis",this._calculateCharSize();var n=this.options.orientation,r=this.options.showMinorLabels,a=this.options.showMajorLabels;s.minorLabelHeight=r?s.minorCharHeight:0,s.majorLabelHeight=a?s.majorCharHeight:0,s.minorLineWidth=this.body.dom.backgroundHorizontal.offsetWidth-this.lineOffset-this.width+2*this.options.minorLinesOffset,s.minorLineHeight=1,s.majorLineWidth=this.body.dom.backgroundHorizontal.offsetWidth-this.lineOffset-this.width+2*this.options.majorLinesOffset,s.majorLineHeight=1,"left"==n?(o.style.top="0",o.style.left="0",o.style.bottom="",o.style.width=this.width+"px",o.style.height=this.height+"px",this.props.width=this.body.domProps.left.width,this.props.height=this.body.domProps.left.height):(o.style.top="",o.style.bottom="0",o.style.left="0",o.style.width=this.width+"px",o.style.height=this.height+"px",this.props.width=this.body.domProps.right.width,this.props.height=this.body.domProps.right.height),t=this._redrawLabels(),t=this._isResized()||t,1==this.options.icons?this._redrawGroupIcons():this._cleanupIcons(),this._redrawTitle(n)}return t},s.prototype._redrawLabels=function(){var t=!1;n.prepareElements(this.DOMelements.lines),n.prepareElements(this.DOMelements.labels);var e=this.options.orientation,i=this.master?this.props.majorCharHeight||10:this.stepPixelsForced,s=new a(this.range.start,this.range.end,i,this.dom.frame.offsetHeight,this.options.customRange[this.options.orientation],0==this.master&&this.options.alignZeros);this.step=s;var o=(this.dom.frame.offsetHeight-s.deadSpace*(this.dom.frame.offsetHeight/s.marginRange))/((s.marginRange-s.deadSpace)/s.step);this.stepPixels=o;var r=this.height/o,h=0;if(0==this.master){o=this.stepPixelsForced,h=Math.round(this.dom.frame.offsetHeight/o-r);for(var d=0;.5*h>d;d++)s.previous();if(r=this.height/o,-1!=this.zeroCrossing&&1==this.options.alignZeros){var l=s.marginEnd/s.step-this.zeroCrossing;if(l>0)for(var d=0;l>d;d++)s.next();else if(0>l)for(var d=0;-l>d;d++)s.previous()}}else r+=.25;this.valueAtZero=s.marginEnd;var c,p=0,u=1;void 0!==this.options.format[e]&&(c=this.options.format[e].decimals),this.maxLabelSize=0;for(var m=0;u=0&&this._redrawLabel(m-2,s.getCurrent(c),e,"yAxis major",this.props.majorCharHeight),this._redrawLine(m,e,"grid horizontal major",this.options.majorLinesOffset,this.props.majorLineWidth)):this._redrawLine(m,e,"grid horizontal minor",this.options.minorLinesOffset,this.props.minorLineWidth),1==this.master&&0==s.current&&(this.zeroCrossing=u),u++}this.conversionFactor=0==this.master?m/(this.valueAtZero-s.current):this.dom.frame.offsetHeight/s.marginRange;var g=0;void 0!==this.options.title[e]&&void 0!==this.options.title[e].text&&(g=this.props.titleCharHeight);var v=1==this.options.icons?Math.max(this.options.iconWidth,g)+this.options.labelOffsetX+15:g+this.options.labelOffsetX+15;return this.maxLabelSize>this.width-v&&1==this.options.visible?(this.width=this.maxLabelSize+v,this.options.width=this.width+"px",n.cleanupElements(this.DOMelements.lines),n.cleanupElements(this.DOMelements.labels),this.redraw(),t=!0):this.maxLabelSizethis.minWidth?(this.width=Math.max(this.minWidth,this.maxLabelSize+v),this.options.width=this.width+"px",n.cleanupElements(this.DOMelements.lines),n.cleanupElements(this.DOMelements.labels),this.redraw(),t=!0):(n.cleanupElements(this.DOMelements.lines),n.cleanupElements(this.DOMelements.labels),t=!1),t},s.prototype.convertValue=function(t){var e=this.valueAtZero-t,i=e*this.conversionFactor;return i},s.prototype._redrawLabel=function(t,e,i,s,o){var r=n.getDOMElement("div",this.DOMelements.labels,this.dom.frame);r.className=s,r.innerHTML=e,"left"==i?(r.style.left="-"+this.options.labelOffsetX+"px",r.style.textAlign="right"):(r.style.right="-"+this.options.labelOffsetX+"px",r.style.textAlign="left"),r.style.top=t-.5*o+this.options.labelOffsetY+"px",e+="";var a=Math.max(this.props.majorCharWidth,this.props.minorCharWidth);this.maxLabelSized;d++){var c=this.visibleItems[d];c.repositionY(e)}return s},s.prototype._calculateHeight=function(t){var e,i=this.visibleItems;this.resetSubgroups();var s=this;if(i.length){var n=i[0].top,r=i[0].top+i[0].height;if(o.forEach(i,function(t){n=Math.min(n,t.top),r=Math.max(r,t.top+t.height),void 0!==t.data.subgroup&&(s.subgroups[t.data.subgroup].height=Math.max(s.subgroups[t.data.subgroup].height,t.height),s.subgroups[t.data.subgroup].visible=!0)}),n>t.axis){var a=n-t.axis;r-=a,o.forEach(i,function(t){t.top-=a})}e=r+t.item.vertical/2}else e=t.axis+t.item.vertical;return e=Math.max(e,this.props.label.height)},s.prototype.show=function(){this.dom.label.parentNode||this.itemSet.dom.labelSet.appendChild(this.dom.label),this.dom.foreground.parentNode||this.itemSet.dom.foreground.appendChild(this.dom.foreground),this.dom.background.parentNode||this.itemSet.dom.background.appendChild(this.dom.background),this.dom.axis.parentNode||this.itemSet.dom.axis.appendChild(this.dom.axis)},s.prototype.hide=function(){var t=this.dom.label;t.parentNode&&t.parentNode.removeChild(t);var e=this.dom.foreground;e.parentNode&&e.parentNode.removeChild(e);var i=this.dom.background;i.parentNode&&i.parentNode.removeChild(i);var s=this.dom.axis;s.parentNode&&s.parentNode.removeChild(s)},s.prototype.add=function(t){if(this.items[t.id]=t,t.setParent(this),void 0!==t.data.subgroup&&(void 0===this.subgroups[t.data.subgroup]&&(this.subgroups[t.data.subgroup]={height:0,visible:!1,index:this.subgroupIndex,items:[]},this.subgroupIndex++),this.subgroups[t.data.subgroup].items.push(t)),this.orderSubgroups(),-1==this.visibleItems.indexOf(t)){var e=this.itemSet.body.range;this._checkIfVisible(t,this.visibleItems,e)}},s.prototype.orderSubgroups=function(){if(void 0!==this.subgroupOrderer){var t=[];if("string"==typeof this.subgroupOrderer){for(var e in this.subgroups)t.push({subgroup:e,sortField:this.subgroups[e].items[0].data[this.subgroupOrderer]});t.sort(function(t,e){return t.sortField-e.sortField})}else if("function"==typeof this.subgroupOrderer){for(var e in this.subgroups)t.push(this.subgroups[e].items[0].data);t.sort(this.subgroupOrderer)}if(t.length>0)for(var i=0;it?-1:l>=t?0:1};if(e.length>0)for(n=0;nl}),1==this.checkRangedItems)for(this.checkRangedItems=!1,n=0;nl})}for(n=0;n=0&&(n=e[r],!o(n));r--)void 0===s[n.id]&&(s[n.id]=!0,i.push(n));for(r=t+1;rs;s++){var n=this.visibleItems[s];n.repositionY(e)}return i},s.prototype.show=function(){this.dom.background.parentNode||this.itemSet.dom.background.appendChild(this.dom.background)},t.exports=s},function(t,e,i){function s(t,e){this.body=t,this.defaultOptions={type:null,orientation:"bottom",align:"auto",stack:!0,groupOrder:null,selectable:!0,editable:{updateTime:!1,updateGroup:!1,add:!1,remove:!1},onAdd:function(t,e){e(t)},onUpdate:function(t,e){e(t)},onMove:function(t,e){e(t)},onRemove:function(t,e){e(t)},onMoving:function(t,e){e(t)},margin:{item:{horizontal:10,vertical:10},axis:20},padding:5},this.options=n.extend({},this.defaultOptions),this.itemOptions={type:{start:"Date",end:"Date"}},this.conversion={toScreen:t.util.toScreen,toTime:t.util.toTime},this.dom={},this.props={},this.hammer=null;var i=this;this.itemsData=null,this.groupsData=null,this.itemListeners={add:function(t,e){i._onAdd(e.items)},update:function(t,e){i._onUpdate(e.items)},remove:function(t,e){i._onRemove(e.items)}},this.groupListeners={add:function(t,e){i._onAddGroups(e.items)},update:function(t,e){i._onUpdateGroups(e.items)},remove:function(t,e){i._onRemoveGroups(e.items)}},this.items={},this.groups={},this.groupIds=[],this.selection=[],this.stackDirty=!0,this.touchParams={},this._create(),this.setOptions(e)}var o=i(45),n=i(1),r=i(3),a=i(4),h=i(20),d=i(25),l=i(26),c=i(33),p=i(34),u=i(35),m=i(32),f="__ungrouped__",g="__background__";s.prototype=new h,s.types={background:m,box:c,range:u,point:p},s.prototype._create=function(){var t=document.createElement("div");t.className="itemset",t["timeline-itemset"]=this,this.dom.frame=t;var e=document.createElement("div");e.className="background",t.appendChild(e),this.dom.background=e;var i=document.createElement("div");i.className="foreground",t.appendChild(i),this.dom.foreground=i;var s=document.createElement("div");s.className="axis",this.dom.axis=s;var n=document.createElement("div");n.className="labelset",this.dom.labelSet=n,this._updateUngrouped();var r=new l(g,null,this);r.show(),this.groups[g]=r,this.hammer=o(this.body.dom.centerContainer,{preventDefault:!0}),this.hammer.on("touch",this._onTouch.bind(this)),this.hammer.on("dragstart",this._onDragStart.bind(this)),this.hammer.on("drag",this._onDrag.bind(this)),this.hammer.on("dragend",this._onDragEnd.bind(this)),this.hammer.on("tap",this._onSelectItem.bind(this)),this.hammer.on("hold",this._onMultiSelectItem.bind(this)),this.hammer.on("doubletap",this._onAddItem.bind(this)),this.show()},s.prototype.setOptions=function(t){if(t){var e=["type","align","orientation","padding","stack","selectable","groupOrder","dataAttributes","template","hide"];n.selectiveExtend(e,this.options,t),"margin"in t&&("number"==typeof t.margin?(this.options.margin.axis=t.margin,this.options.margin.item.horizontal=t.margin,this.options.margin.item.vertical=t.margin):"object"==typeof t.margin&&(n.selectiveExtend(["axis"],this.options.margin,t.margin),"item"in t.margin&&("number"==typeof t.margin.item?(this.options.margin.item.horizontal=t.margin.item,this.options.margin.item.vertical=t.margin.item):"object"==typeof t.margin.item&&n.selectiveExtend(["horizontal","vertical"],this.options.margin.item,t.margin.item)))),"editable"in t&&("boolean"==typeof t.editable?(this.options.editable.updateTime=t.editable,this.options.editable.updateGroup=t.editable,this.options.editable.add=t.editable,this.options.editable.remove=t.editable):"object"==typeof t.editable&&n.selectiveExtend(["updateTime","updateGroup","add","remove"],this.options.editable,t.editable));var i=function(e){var i=t[e];if(i){if(!(i instanceof Function))throw new Error("option "+e+" must be a function "+e+"(item, callback)");this.options[e]=i}}.bind(this);["onAdd","onUpdate","onRemove","onMove","onMoving"].forEach(i),this.markDirty()}},s.prototype.markDirty=function(){this.groupIds=[],this.stackDirty=!0},s.prototype.destroy=function(){this.hide(),this.setItems(null),this.setGroups(null),this.hammer=null,this.body=null,this.conversion=null},s.prototype.hide=function(){this.dom.frame.parentNode&&this.dom.frame.parentNode.removeChild(this.dom.frame),this.dom.axis.parentNode&&this.dom.axis.parentNode.removeChild(this.dom.axis),this.dom.labelSet.parentNode&&this.dom.labelSet.parentNode.removeChild(this.dom.labelSet)},s.prototype.show=function(){this.dom.frame.parentNode||this.body.dom.center.appendChild(this.dom.frame),this.dom.axis.parentNode||this.body.dom.backgroundVertical.appendChild(this.dom.axis),this.dom.labelSet.parentNode||this.body.dom.left.appendChild(this.dom.labelSet)},s.prototype.setSelection=function(t){var e,i,s,o;for(void 0==t&&(t=[]),Array.isArray(t)||(t=[t]),e=0,i=this.selection.length;i>e;e++)s=this.selection[e],o=this.items[s],o&&o.unselect();for(this.selection=[],e=0,i=t.length;i>e;e++)s=t[e],o=this.items[s],o&&(this.selection.push(s),o.select())},s.prototype.getSelection=function(){return this.selection.concat([])},s.prototype.getVisibleItems=function(){var t=this.body.range.getRange(),e=this.body.util.toScreen(t.start),i=this.body.util.toScreen(t.end),s=[];for(var o in this.groups)if(this.groups.hasOwnProperty(o))for(var n=this.groups[o],r=n.visibleItems,a=0;ae&&s.push(h.id)}return s},s.prototype._deselect=function(t){for(var e=this.selection,i=0,s=e.length;s>i;i++)if(e[i]==t){e.splice(i,1);break}},s.prototype.redraw=function(){var t=this.options.margin,e=this.body.range,i=n.option.asSize,s=this.options,o=s.orientation,r=!1,a=this.dom.frame,h=s.editable.updateTime||s.editable.updateGroup;this.props.top=this.body.domProps.top.height+this.body.domProps.border.top,this.props.left=this.body.domProps.left.width+this.body.domProps.border.left,a.className="itemset"+(h?" editable":""),r=this._orderGroups()||r;var d=e.end-e.start,l=d!=this.lastVisibleInterval||this.props.width!=this.props.lastWidth;l&&(this.stackDirty=!0),this.lastVisibleInterval=d,this.props.lastWidth=this.props.width;var c=this.stackDirty,p=this._firstGroup(),u={item:t.item,axis:t.axis},m={item:t.item,axis:t.item.vertical/2},f=0,v=t.axis+t.item.vertical;return this.groups[g].redraw(e,m,c),n.forEach(this.groups,function(t){var i=t==p?u:m,s=t.redraw(e,i,c);r=s||r,f+=t.height}),f=Math.max(f,v),this.stackDirty=!1,a.style.height=i(f),this.props.width=a.offsetWidth,this.props.height=f,this.dom.axis.style.top=i("top"==o?this.body.domProps.top.height+this.body.domProps.border.top:this.body.domProps.top.height+this.body.domProps.centerContainer.height),this.dom.axis.style.left="0",r=this._isResized()||r},s.prototype._firstGroup=function(){var t="top"==this.options.orientation?0:this.groupIds.length-1,e=this.groupIds[t],i=this.groups[e]||this.groups[f];return i||null},s.prototype._updateUngrouped=function(){{var t,e,i=this.groups[f];this.groups[g]}if(this.groupsData){if(i){i.hide(),delete this.groups[f];for(e in this.items)if(this.items.hasOwnProperty(e)){t=this.items[e],t.parent&&t.parent.remove(t);var s=this._getGroupId(t.data),o=this.groups[s];o&&o.add(t)||t.hide()}}}else if(!i){var n=null,r=null;i=new d(n,r,this),this.groups[f]=i;for(e in this.items)this.items.hasOwnProperty(e)&&(t=this.items[e],i.add(t));i.show()}},s.prototype.getLabelSet=function(){return this.dom.labelSet},s.prototype.setItems=function(t){var e,i=this,s=this.itemsData;if(t){if(!(t instanceof r||t instanceof a))throw new TypeError("Data must be an instance of DataSet or DataView");this.itemsData=t}else this.itemsData=null;if(s&&(n.forEach(this.itemListeners,function(t,e){s.off(e,t)}),e=s.getIds(),this._onRemove(e)),this.itemsData){var o=this.id;n.forEach(this.itemListeners,function(t,e){i.itemsData.on(e,t,o)}),e=this.itemsData.getIds(),this._onAdd(e),this._updateUngrouped()}},s.prototype.getItems=function(){return this.itemsData},s.prototype.setGroups=function(t){var e,i=this;if(this.groupsData&&(n.forEach(this.groupListeners,function(t,e){i.groupsData.unsubscribe(e,t)}),e=this.groupsData.getIds(),this.groupsData=null,this._onRemoveGroups(e)),t){if(!(t instanceof r||t instanceof a))throw new TypeError("Data must be an instance of DataSet or DataView");this.groupsData=t}else this.groupsData=null;if(this.groupsData){var s=this.id;n.forEach(this.groupListeners,function(t,e){i.groupsData.on(e,t,s)}),e=this.groupsData.getIds(),this._onAddGroups(e)}this._updateUngrouped(),this._order(),this.body.emitter.emit("change",{queue:!0})},s.prototype.getGroups=function(){return this.groupsData},s.prototype.removeItem=function(t){var e=this.itemsData.get(t),i=this.itemsData.getDataSet();e&&this.options.onRemove(e,function(e){e&&i.remove(t)})},s.prototype._getType=function(t){return t.type||this.options.type||(t.end?"range":"box")},s.prototype._getGroupId=function(t){var e=this._getType(t);return"background"==e&&void 0==t.group?g:this.groupsData?t.group:f},s.prototype._onUpdate=function(t){var e=this;t.forEach(function(t){var i=e.itemsData.get(t,e.itemOptions),o=e.items[t],n=e._getType(i),r=s.types[n];if(o&&(r&&o instanceof r?e._updateItem(o,i):(e._removeItem(o),o=null)),!o){if(!r)throw new TypeError("rangeoverflow"==n?'Item type "rangeoverflow" is deprecated. Use css styling instead: .vis.timeline .item.range .content {overflow: visible;}':'Unknown item type "'+n+'"');o=new r(i,e.conversion,e.options),o.id=t,e._addItem(o)}}),this._order(),this.stackDirty=!0,this.body.emitter.emit("change",{queue:!0})},s.prototype._onAdd=s.prototype._onUpdate,s.prototype._onRemove=function(t){var e=0,i=this;t.forEach(function(t){var s=i.items[t];s&&(e++,i._removeItem(s))}),e&&(this._order(),this.stackDirty=!0,this.body.emitter.emit("change",{queue:!0}))},s.prototype._order=function(){n.forEach(this.groups,function(t){t.order()})},s.prototype._onUpdateGroups=function(t){this._onAddGroups(t)},s.prototype._onAddGroups=function(t){var e=this;t.forEach(function(t){var i=e.groupsData.get(t),s=e.groups[t];if(s)s.setData(i);else{if(t==f||t==g)throw new Error("Illegal group id. "+t+" is a reserved id.");var o=Object.create(e.options);n.extend(o,{height:null}),s=new d(t,i,e),e.groups[t]=s;for(var r in e.items)if(e.items.hasOwnProperty(r)){var a=e.items[r];a.data.group==t&&s.add(a)}s.order(),s.show()}}),this.body.emitter.emit("change",{queue:!0})},s.prototype._onRemoveGroups=function(t){var e=this.groups;t.forEach(function(t){var i=e[t];i&&(i.hide(),delete e[t])}),this.markDirty(),this.body.emitter.emit("change",{queue:!0})},s.prototype._orderGroups=function(){if(this.groupsData){var t=this.groupsData.getIds({order:this.options.groupOrder}),e=!n.equalArray(t,this.groupIds);if(e){var i=this.groups;t.forEach(function(t){i[t].hide()}),t.forEach(function(t){i[t].show()}),this.groupIds=t}return e}return!1},s.prototype._addItem=function(t){this.items[t.id]=t;var e=this._getGroupId(t.data),i=this.groups[e];i&&i.add(t)},s.prototype._updateItem=function(t,e){var i=t.data.group;if(t.setData(e),i!=t.data.group){var s=this.groups[i];s&&s.remove(t);var o=this._getGroupId(t.data),n=this.groups[o];n&&n.add(t)}},s.prototype._removeItem=function(t){t.hide(),delete this.items[t.id];var e=this.selection.indexOf(t.id);-1!=e&&this.selection.splice(e,1),t.parent&&t.parent.remove(t)},s.prototype._constructByEndArray=function(t){for(var e=[],i=0;i0||o.length>0)&&this.body.emitter.emit("select",{items:a})}},s.prototype._onAddItem=function(t){if(this.options.selectable&&this.options.editable.add){var e=this,i=this.body.util.snap||null,o=s.itemFromTarget(t);if(o){var r=e.itemsData.get(o.id);this.options.onUpdate(r,function(t){t&&e.itemsData.getDataSet().update(t)})}else{var a=n.getAbsoluteLeft(this.dom.frame),h=t.gesture.center.pageX-a,d=this.body.util.toTime(h),l={start:i?i(d):d,content:"new item"};if("range"===this.options.type){var c=this.body.util.toTime(h+this.props.width/5);l.end=i?i(c):c}l[this.itemsData._fieldId]=n.randomUUID();var p=s.groupFromTarget(t);p&&(l.group=p.groupId),this.options.onAdd(l,function(t){t&&e.itemsData.getDataSet().add(t)})}}},s.prototype._onMultiSelectItem=function(t){if(this.options.selectable){var e,i=s.itemFromTarget(t);if(i){e=this.getSelection();var o=t.gesture.touches[0]&&t.gesture.touches[0].shiftKey||!1;if(o){e.push(i.id);var n=s._getItemRange(this.itemsData.get(e,this.itemOptions));e=[];for(var r in this.items)if(this.items.hasOwnProperty(r)){var a=this.items[r],h=a.data.start,d=void 0!==a.data.end?a.data.end:h;h>=n.min&&d<=n.max&&e.push(a.id)}}else{var l=e.indexOf(i.id);-1==l?e.push(i.id):e.splice(l,1)}this.setSelection(e),this.body.emitter.emit("select",{items:this.getSelection()})}}},s._getItemRange=function(t){var e=null,i=null;return t.forEach(function(t){(null==i||t.starte)&&(e=t.end):(null==e||t.start>e)&&(e=t.start)}),{min:i,max:e}},s.itemFromTarget=function(t){for(var e=t.target;e;){if(e.hasOwnProperty("timeline-item"))return e["timeline-item"];e=e.parentNode}return null},s.groupFromTarget=function(t){for(var e=t.target;e;){if(e.hasOwnProperty("timeline-group"))return e["timeline-group"];e=e.parentNode}return null},s.itemSetFromTarget=function(t){for(var e=t.target;e;){if(e.hasOwnProperty("timeline-itemset"))return e["timeline-itemset"];e=e.parentNode}return null},t.exports=s},function(t,e,i){function s(t,e,i,s){this.body=t,this.defaultOptions={enabled:!0,icons:!0,iconSize:20,iconSpacing:6,left:{visible:!0,position:"top-left"},right:{visible:!0,position:"top-left"}},this.side=i,this.options=o.extend({},this.defaultOptions),this.linegraphOptions=s,this.svgElements={},this.dom={},this.groups={},this.amountOfGroups=0,this._create(),this.setOptions(e)}var o=i(1),n=i(2),r=i(20);s.prototype=new r,s.prototype.clear=function(){this.groups={},this.amountOfGroups=0},s.prototype.addGroup=function(t,e){this.groups.hasOwnProperty(t)||(this.groups[t]=e),this.amountOfGroups+=1},s.prototype.updateGroup=function(t,e){this.groups[t]=e},s.prototype.removeGroup=function(t){this.groups.hasOwnProperty(t)&&(delete this.groups[t],this.amountOfGroups-=1)},s.prototype._create=function(){this.dom.frame=document.createElement("div"),this.dom.frame.className="legend",this.dom.frame.style.position="absolute",this.dom.frame.style.top="10px",this.dom.frame.style.display="block",this.dom.textArea=document.createElement("div"),this.dom.textArea.className="legendText",this.dom.textArea.style.position="relative",this.dom.textArea.style.top="0px",this.svg=document.createElementNS("http://www.w3.org/2000/svg","svg"),this.svg.style.position="absolute",this.svg.style.top="0px",this.svg.style.width=this.options.iconSize+5+"px",this.svg.style.height="100%",this.dom.frame.appendChild(this.svg),this.dom.frame.appendChild(this.dom.textArea)},s.prototype.hide=function(){this.dom.frame.parentNode&&this.dom.frame.parentNode.removeChild(this.dom.frame)},s.prototype.show=function(){this.dom.frame.parentNode||this.body.dom.center.appendChild(this.dom.frame)},s.prototype.setOptions=function(t){var e=["enabled","orientation","icons","left","right"];o.selectiveDeepExtend(e,this.options,t)},s.prototype.redraw=function(){var t=0;for(var e in this.groups)this.groups.hasOwnProperty(e)&&(1!=this.groups[e].visible||void 0!==this.linegraphOptions.visibility[e]&&1!=this.linegraphOptions.visibility[e]||t++);if(0==this.options[this.side].visible||0==this.amountOfGroups||0==this.options.enabled||0==t)this.hide();else{if(this.show(),"top-left"==this.options[this.side].position||"bottom-left"==this.options[this.side].position?(this.dom.frame.style.left="4px",this.dom.frame.style.textAlign="left",this.dom.textArea.style.textAlign="left",this.dom.textArea.style.left=this.options.iconSize+15+"px",this.dom.textArea.style.right="",this.svg.style.left="0px",this.svg.style.right=""):(this.dom.frame.style.right="4px",this.dom.frame.style.textAlign="right",this.dom.textArea.style.textAlign="right",this.dom.textArea.style.right=this.options.iconSize+15+"px",this.dom.textArea.style.left="",this.svg.style.right="0px",this.svg.style.left=""),"top-left"==this.options[this.side].position||"top-right"==this.options[this.side].position)this.dom.frame.style.top=4-Number(this.body.dom.center.style.top.replace("px",""))+"px",this.dom.frame.style.bottom="";else{var i=this.body.domProps.center.height-this.body.domProps.centerContainer.height;this.dom.frame.style.bottom=4+i+Number(this.body.dom.center.style.top.replace("px",""))+"px",this.dom.frame.style.top=""}0==this.options.icons?(this.dom.frame.style.width=this.dom.textArea.offsetWidth+10+"px",this.dom.textArea.style.right="",this.dom.textArea.style.left="",this.svg.style.width="0px"):(this.dom.frame.style.width=this.options.iconSize+15+this.dom.textArea.offsetWidth+10+"px",this.drawLegendIcons());var s="";for(var e in this.groups)this.groups.hasOwnProperty(e)&&(1!=this.groups[e].visible||void 0!==this.linegraphOptions.visibility[e]&&1!=this.linegraphOptions.visibility[e]||(s+=this.groups[e].content+"
"));this.dom.textArea.innerHTML=s,this.dom.textArea.style.lineHeight=.75*this.options.iconSize+this.options.iconSpacing+"px"}},s.prototype.drawLegendIcons=function(){if(this.dom.frame.parentNode){n.prepareElements(this.svgElements);var t=window.getComputedStyle(this.dom.frame).paddingTop,e=Number(t.replace("px","")),i=e,s=this.options.iconSize,o=.75*this.options.iconSize,r=e+.5*o+3;this.svg.style.width=s+5+e+"px";for(var a in this.groups)this.groups.hasOwnProperty(a)&&(1!=this.groups[a].visible||void 0!==this.linegraphOptions.visibility[a]&&1!=this.linegraphOptions.visibility[a]||(this.groups[a].drawIcon(i,r,this.svgElements,this.svg,s,o),r+=o+this.options.iconSpacing));n.cleanupElements(this.svgElements)}},t.exports=s},function(t,e,i){function s(t,e){this.id=o.randomUUID(),this.body=t,this.defaultOptions={yAxisOrientation:"left",defaultGroup:"default",sort:!0,sampling:!0,graphHeight:"400px",shaded:{enabled:!1,orientation:"bottom"},style:"line",barChart:{width:50,handleOverlap:"overlap",align:"center"},catmullRom:{enabled:!0,parametrization:"centripetal",alpha:.5},drawPoints:{enabled:!0,size:6,style:"square"},dataAxis:{showMinorLabels:!0,showMajorLabels:!0,icons:!1,width:"40px",visible:!0,alignZeros:!0,customRange:{left:{min:void 0,max:void 0},right:{min:void 0,max:void 0}}},legend:{enabled:!1,icons:!0,left:{visible:!0,position:"top-left"},right:{visible:!0,position:"top-right"}},groups:{visibility:{}}},this.options=o.extend({},this.defaultOptions),this.dom={},this.props={},this.hammer=null,this.groups={},this.abortedGraphUpdate=!1,this.updateSVGheight=!1,this.updateSVGheightOnResize=!1;var i=this;this.itemsData=null,this.groupsData=null,this.itemListeners={add:function(t,e){i._onAdd(e.items)},update:function(t,e){i._onUpdate(e.items)},remove:function(t,e){i._onRemove(e.items)}},this.groupListeners={add:function(t,e){i._onAddGroups(e.items)},update:function(t,e){i._onUpdateGroups(e.items)},remove:function(t,e){i._onRemoveGroups(e.items)}},this.items={},this.selection=[],this.lastStart=this.body.range.start,this.touchParams={},this.svgElements={},this.setOptions(e),this.groupsUsingDefaultStyles=[0],this.COUNTER=0,this.body.emitter.on("rangechanged",function(){i.lastStart=i.body.range.start,i.svg.style.left=o.option.asSize(-i.props.width),i.redraw.call(i,!0)}),this._create(),this.framework={svg:this.svg,svgElements:this.svgElements,options:this.options,groups:this.groups},this.body.emitter.emit("change")}var o=i(1),n=i(2),r=i(3),a=i(4),h=i(20),d=i(23),l=i(24),c=i(28),p=i(52),u="__ungrouped__";s.prototype=new h,s.prototype._create=function(){var t=document.createElement("div");t.className="LineGraph",this.dom.frame=t,this.svg=document.createElementNS("http://www.w3.org/2000/svg","svg"),this.svg.style.position="relative",this.svg.style.height=(""+this.options.graphHeight).replace("px","")+"px",this.svg.style.display="block",t.appendChild(this.svg),this.options.dataAxis.orientation="left",this.yAxisLeft=new d(this.body,this.options.dataAxis,this.svg,this.options.groups),this.options.dataAxis.orientation="right",this.yAxisRight=new d(this.body,this.options.dataAxis,this.svg,this.options.groups),delete this.options.dataAxis.orientation,this.legendLeft=new c(this.body,this.options.legend,"left",this.options.groups),this.legendRight=new c(this.body,this.options.legend,"right",this.options.groups),this.show()},s.prototype.setOptions=function(t){if(t){var e=["sampling","defaultGroup","height","graphHeight","yAxisOrientation","style","barChart","dataAxis","sort","groups"];void 0===t.graphHeight&&void 0!==t.height&&void 0!==this.body.domProps.centerContainer.height?(this.updateSVGheight=!0,this.updateSVGheightOnResize=!0):void 0!==this.body.domProps.centerContainer.height&&void 0!==t.graphHeight&&parseInt((t.graphHeight+"").replace("px",""))0){var d=this.body.util.toGlobalTime(-this.body.domProps.root.width),l=this.body.util.toGlobalTime(2*this.body.domProps.root.width),c={};for(this._getRelevantData(a,c,d,l),this._applySampling(a,c),e=0;eu&&console.log("WARNING: there may be an infinite loop in the _updateGraph emitter cycle."),this.COUNTER=0,this.abortedGraphUpdate=!1,e=0;e0)for(r=0;rs){d.push(h);break}d.push(h)}}else for(a=0;ai&&h.x0)for(var s=0;s0){var n=1,r=o.length,a=this.body.util.toGlobalScreen(o[o.length-1].x)-this.body.util.toGlobalScreen(o[0].x),h=r/a;n=Math.min(Math.ceil(.2*r),Math.max(1,Math.round(h)));for(var d=[],l=0;r>l;l+=n)d.push(o[l]);e[t[s]]=d}}},s.prototype._getYRanges=function(t,e,i){var s,o,n,r,a=[],h=[];if(t.length>0){for(n=0;n0&&(o=this.groups[t[n]],"stack"==r.barChart.handleOverlap&&"bar"==r.style?"left"==r.yAxisOrientation?a=a.concat(o.getYRange(s)):h=h.concat(o.getYRange(s)):i[t[n]]=o.getYRange(s,t[n]));p.getStackedBarYRange(a,i,t,"__barchartLeft","left"),p.getStackedBarYRange(h,i,t,"__barchartRight","right")}},s.prototype._updateYAxis=function(t,e){var i,s,o=!1,n=!1,r=!1,a=1e9,h=1e9,d=-1e9,l=-1e9;if(t.length>0){for(var c=0;ci?i:a,d=s>d?s:d):(r=!0,h=h>i?i:h,l=s>l?s:l));1==n&&this.yAxisLeft.setRange(a,d),1==r&&this.yAxisRight.setRange(h,l)}return o=this._toggleAxisVisiblity(n,this.yAxisLeft)||o,o=this._toggleAxisVisiblity(r,this.yAxisRight)||o,1==r&&1==n?(this.yAxisLeft.drawIcons=!0,this.yAxisRight.drawIcons=!0):(this.yAxisLeft.drawIcons=!1,this.yAxisRight.drawIcons=!1),this.yAxisRight.master=!n,0==this.yAxisRight.master?(this.yAxisLeft.lineOffset=1==r?this.yAxisRight.width:0,o=this.yAxisLeft.redraw()||o,this.yAxisRight.stepPixelsForced=this.yAxisLeft.stepPixels,this.yAxisRight.zeroCrossing=this.yAxisLeft.zeroCrossing,o=this.yAxisRight.redraw()||o):o=this.yAxisRight.redraw()||o,-1!=t.indexOf("__barchartLeft")&&t.splice(t.indexOf("__barchartLeft"),1),-1!=t.indexOf("__barchartRight")&&t.splice(t.indexOf("__barchartRight"),1),o},s.prototype._toggleAxisVisiblity=function(t,e){var i=!1;return 0==t?e.dom.frame.parentNode&&0==e.hidden&&(e.hide(),i=!0):e.dom.frame.parentNode||1!=e.hidden||(e.show(),i=!0),i},s.prototype._convertXcoordinates=function(t){for(var e,i,s=[],o=this.body.util.toScreen,n=0;ny;)y++,l=h.getCurrent(),c=h.isMajor(),u=h.getClassName(),f=m,m=this.body.util.toScreen(l),g=m-f,p&&(p.style.width=g+"px"),this.options.showMinorLabels&&this._repaintMinorText(m,h.getLabelMinor(),t,u),c&&this.options.showMajorLabels?(m>0&&(void 0==v&&(v=m),this._repaintMajorText(m,h.getLabelMajor(),t,u)),p=this._repaintMajorLine(m,t,u)):p=this._repaintMinorLine(m,t,u),h.next();if(this.options.showMajorLabels){var b=this.body.util.toTime(0),_=h.getLabelMajor(b),x=_.length*(this.props.majorCharWidth||10)+10;(void 0==v||v>x)&&this._repaintMajorText(0,_,t,u)}o.forEach(this.dom.redundant,function(t){for(;t.length;){var e=t.pop();e&&e.parentNode&&e.parentNode.removeChild(e)}})},s.prototype._repaintMinorText=function(t,e,i,s){var o=this.dom.redundant.minorTexts.shift();if(!o){var n=document.createTextNode("");o=document.createElement("div"),o.appendChild(n),this.dom.foreground.appendChild(o)}this.dom.minorTexts.push(o),o.childNodes[0].nodeValue=e,o.style.top="top"==i?this.props.majorLabelHeight+"px":"0",o.style.left=t+"px",o.className="text minor "+s},s.prototype._repaintMajorText=function(t,e,i,s){var o=this.dom.redundant.majorTexts.shift();if(!o){var n=document.createTextNode(e);o=document.createElement("div"),o.appendChild(n),this.dom.foreground.appendChild(o)}this.dom.majorTexts.push(o),o.childNodes[0].nodeValue=e,o.className="text major "+s,o.style.top="top"==i?"0":this.props.minorLabelHeight+"px",o.style.left=t+"px"},s.prototype._repaintMinorLine=function(t,e,i){var s=this.dom.redundant.lines.shift();s||(s=document.createElement("div"),this.dom.background.appendChild(s)),this.dom.lines.push(s);var o=this.props;return s.style.top="top"==e?o.majorLabelHeight+"px":this.body.domProps.top.height+"px",s.style.height=o.minorLineHeight+"px",s.style.left=t-o.minorLineWidth/2+"px",s.className="grid vertical minor "+i,s},s.prototype._repaintMajorLine=function(t,e,i){var s=this.dom.redundant.lines.shift();s||(s=document.createElement("div"),this.dom.background.appendChild(s)),this.dom.lines.push(s);var o=this.props;return s.style.top="top"==e?"0":this.body.domProps.top.height+"px",s.style.left=t-o.majorLineWidth/2+"px",s.style.height=o.majorLineHeight+"px",s.className="grid vertical major "+i,s},s.prototype._calculateCharSize=function(){this.dom.measureCharMinor||(this.dom.measureCharMinor=document.createElement("DIV"),this.dom.measureCharMinor.className="text minor measure",this.dom.measureCharMinor.style.position="absolute",this.dom.measureCharMinor.appendChild(document.createTextNode("0")),this.dom.foreground.appendChild(this.dom.measureCharMinor)),this.props.minorCharHeight=this.dom.measureCharMinor.clientHeight,this.props.minorCharWidth=this.dom.measureCharMinor.clientWidth,this.dom.measureCharMajor||(this.dom.measureCharMajor=document.createElement("DIV"),this.dom.measureCharMajor.className="text major measure",this.dom.measureCharMajor.style.position="absolute",this.dom.measureCharMajor.appendChild(document.createTextNode("0")),this.dom.foreground.appendChild(this.dom.measureCharMajor)),this.props.majorCharHeight=this.dom.measureCharMajor.clientHeight,this.props.majorCharWidth=this.dom.measureCharMajor.clientWidth},s.prototype.snap=function(t){return this.step.snap(t)},t.exports=s},function(t,e,i){function s(t,e,i){this.id=null,this.parent=null,this.data=t,this.dom=null,this.conversion=e||{},this.options=i||{},this.selected=!1,this.displayed=!1,this.dirty=!0,this.top=null,this.left=null,this.width=null,this.height=null}var o=i(45),n=i(1);s.prototype.stack=!0,s.prototype.select=function(){this.selected=!0,this.dirty=!0,this.displayed&&this.redraw()},s.prototype.unselect=function(){this.selected=!1,this.dirty=!0,this.displayed&&this.redraw()},s.prototype.setData=function(t){this.data=t,this.dirty=!0,this.displayed&&this.redraw()},s.prototype.setParent=function(t){this.displayed?(this.hide(),this.parent=t,this.parent&&this.show()):this.parent=t},s.prototype.isVisible=function(){return!1},s.prototype.show=function(){return!1},s.prototype.hide=function(){return!1},s.prototype.redraw=function(){},s.prototype.repositionX=function(){},s.prototype.repositionY=function(){},s.prototype._repaintDeleteButton=function(t){if(this.selected&&this.options.editable.remove&&!this.dom.deleteButton){var e=this,i=document.createElement("div");i.className="delete",i.title="Delete this item",o(i,{preventDefault:!0}).on("tap",function(t){e.parent.removeFromDataSet(e),t.stopPropagation()}),t.appendChild(i),this.dom.deleteButton=i}else!this.selected&&this.dom.deleteButton&&(this.dom.deleteButton.parentNode&&this.dom.deleteButton.parentNode.removeChild(this.dom.deleteButton),this.dom.deleteButton=null)},s.prototype._updateContents=function(t){var e;if(this.options.template){var i=this.parent.itemSet.itemsData.get(this.id);e=this.options.template(i)}else e=this.data.content;if(e!==this.content){if(e instanceof Element)t.innerHTML="",t.appendChild(e);else if(void 0!=e)t.innerHTML=e;else if("background"!=this.data.type||void 0!==this.data.content)throw new Error('Property "content" missing in item '+this.id);this.content=e}},s.prototype._updateTitle=function(t){null!=this.data.title?t.title=this.data.title||"":t.removeAttribute("title")},s.prototype._updateDataAttributes=function(t){if(this.options.dataAttributes&&this.options.dataAttributes.length>0){var e=[];if(Array.isArray(this.options.dataAttributes))e=this.options.dataAttributes;else{if("all"!=this.options.dataAttributes)return;e=Object.keys(this.data)}for(var i=0;it.start},s.prototype.redraw=function(){var t=this.dom;if(t||(this.dom={},t=this.dom,t.box=document.createElement("div"),t.content=document.createElement("div"),t.content.className="content",t.box.appendChild(t.content),this.dirty=!0),!this.parent)throw new Error("Cannot redraw item: no parent attached");if(!t.box.parentNode){var e=this.parent.dom.background;if(!e)throw new Error("Cannot redraw item: parent has no background container element");e.appendChild(t.box)}if(this.displayed=!0,this.dirty){this._updateContents(this.dom.content),this._updateTitle(this.dom.content),this._updateDataAttributes(this.dom.content),this._updateStyle(this.dom.box);var i=(this.data.className?" "+this.data.className:"")+(this.selected?" selected":"");t.box.className=this.baseClassName+i,this.overflow="hidden"!==window.getComputedStyle(t.content).overflow,this.props.content.width=this.dom.content.offsetWidth,this.height=0,this.dirty=!1}},s.prototype.show=r.prototype.show,s.prototype.hide=r.prototype.hide,s.prototype.repositionX=r.prototype.repositionX,s.prototype.repositionY=function(t){var e="top"===this.options.orientation;this.dom.content.style.top=e?"":"0",this.dom.content.style.bottom=e?"0":"";var i;if(void 0!==this.data.subgroup){var s=this.data.subgroup,o=this.parent.subgroups,r=o[s].index;if(1==e){i=this.parent.subgroups[s].height+t.item.vertical,i+=0==r?t.axis-.5*t.item.vertical:0;var a=this.parent.top;for(var h in o)o.hasOwnProperty(h)&&1==o[h].visible&&o[h].indexr&&(a+=o[h].height+t.item.vertical);i=this.parent.subgroups[s].height+t.item.vertical,this.dom.box.style.top=a+"px",this.dom.box.style.bottom=""}}else this.parent instanceof n?(i=Math.max(this.parent.height,this.parent.itemSet.body.domProps.center.height,this.parent.itemSet.body.domProps.centerContainer.height),this.dom.box.style.top=e?"0":"",this.dom.box.style.bottom=e?"":"0"):(i=this.parent.height,this.dom.box.style.top=this.parent.top+"px",this.dom.box.style.bottom="");this.dom.box.style.height=i+"px"},t.exports=s},function(t,e,i){function s(t,e,i){if(this.props={dot:{width:0,height:0},line:{width:0,height:0}},t&&void 0==t.start)throw new Error('Property "start" missing in item '+t);o.call(this,t,e,i)}{var o=i(31);i(1)}s.prototype=new o(null,null,null),s.prototype.isVisible=function(t){var e=(t.end-t.start)/4;return this.data.start>t.start-e&&this.data.startt.start-e&&this.data.startt.start},s.prototype.redraw=function(){var t=this.dom;if(t||(this.dom={},t=this.dom,t.box=document.createElement("div"),t.content=document.createElement("div"),t.content.className="content",t.box.appendChild(t.content),t.box["timeline-item"]=this,this.dirty=!0),!this.parent)throw new Error("Cannot redraw item: no parent attached");if(!t.box.parentNode){var e=this.parent.dom.foreground;if(!e)throw new Error("Cannot redraw item: parent has no foreground container element");e.appendChild(t.box)}if(this.displayed=!0,this.dirty){this._updateContents(this.dom.content),this._updateTitle(this.dom.box),this._updateDataAttributes(this.dom.box),this._updateStyle(this.dom.box);var i=(this.data.className?" "+this.data.className:"")+(this.selected?" selected":"");t.box.className=this.baseClassName+i,this.overflow="hidden"!==window.getComputedStyle(t.content).overflow,this.dom.content.style.maxWidth="none",this.props.content.width=this.dom.content.offsetWidth,this.height=this.dom.box.offsetHeight,this.dom.content.style.maxWidth="",this.dirty=!1}this._repaintDeleteButton(t.box),this._repaintDragLeft(),this._repaintDragRight()},s.prototype.show=function(){this.displayed||this.redraw()},s.prototype.hide=function(){if(this.displayed){var t=this.dom.box;t.parentNode&&t.parentNode.removeChild(t),this.top=null,this.left=null,this.displayed=!1}},s.prototype.repositionX=function(){var t,e,i=this.parent.width,s=this.conversion.toScreen(this.data.start),o=this.conversion.toScreen(this.data.end);-i>s&&(s=-i),o>2*i&&(o=2*i);var n=Math.max(o-s,1);switch(this.overflow?(this.left=s,this.width=n+this.props.content.width,e=this.props.content.width):(this.left=s,this.width=n,e=Math.min(o-s-2*this.options.padding,this.props.content.width)),this.dom.box.style.left=this.left+"px",this.dom.box.style.width=n+"px",this.options.align){case"left":this.dom.content.style.left="0";break;case"right":this.dom.content.style.left=Math.max(n-e-2*this.options.padding,0)+"px";break;case"center":this.dom.content.style.left=Math.max((n-e-2*this.options.padding)/2,0)+"px";break;default:t=this.overflow?o>0?Math.max(-s,0):-e:0>s?Math.min(-s,o-s-e-2*this.options.padding):0,this.dom.content.style.left=t+"px"}},s.prototype.repositionY=function(){var t=this.options.orientation,e=this.dom.box;e.style.top="top"==t?this.top+"px":this.parent.height-this.top-this.height+"px"},s.prototype._repaintDragLeft=function(){if(this.selected&&this.options.editable.updateTime&&!this.dom.dragLeft){var t=document.createElement("div");t.className="drag-left",t.dragLeftItem=this,o(t,{preventDefault:!0}).on("drag",function(){}),this.dom.box.appendChild(t),this.dom.dragLeft=t}else!this.selected&&this.dom.dragLeft&&(this.dom.dragLeft.parentNode&&this.dom.dragLeft.parentNode.removeChild(this.dom.dragLeft),this.dom.dragLeft=null)},s.prototype._repaintDragRight=function(){if(this.selected&&this.options.editable.updateTime&&!this.dom.dragRight){var t=document.createElement("div");t.className="drag-right",t.dragRightItem=this,o(t,{preventDefault:!0}).on("drag",function(){}),this.dom.box.appendChild(t),this.dom.dragRight=t}else!this.selected&&this.dom.dragRight&&(this.dom.dragRight.parentNode&&this.dom.dragRight.parentNode.removeChild(this.dom.dragRight),this.dom.dragRight=null)},t.exports=s},function(t,e,i){function s(t,e,i){if(!(this instanceof s))throw new SyntaxError("Constructor must be called with the new operator");this._determineBrowserMethod(),this._initializeMixinLoaders(),this.containerElement=t,this.renderRefreshRate=60,this.renderTimestep=1e3/this.renderRefreshRate,this.renderTime=0,this.physicsTime=0,this.runDoubleSpeed=!1,this.physicsDiscreteStepsize=.5,this.initializing=!0,this.triggerFunctions={add:null,edit:null,editEdge:null,connect:null,del:null},this.defaultOptions={nodes:{mass:1,radiusMin:10,radiusMax:30,radius:10,shape:"ellipse",image:void 0,widthMin:16,widthMax:64,fontColor:"black",fontSize:14,fontFace:"verdana",fontFill:void 0,fontStrokeWidth:0,fontStrokeColor:"white",level:-1,color:{border:"#2B7CE9",background:"#97C2FC",highlight:{border:"#2B7CE9",background:"#D2E5FF"},hover:{border:"#2B7CE9",background:"#D2E5FF"}},group:void 0,borderWidth:1,borderWidthSelected:void 0},edges:{widthMin:1,widthMax:15,width:1,widthSelectionMultiplier:2,hoverWidth:1.5,style:"line",color:{color:"#848484",highlight:"#848484",hover:"#848484"},fontColor:"#343434",fontSize:14,fontFace:"arial",fontFill:"white",fontStrokeWidth:0,fontStrokeColor:"white",labelAlignment:"horizontal",arrowScaleFactor:1,dash:{length:10,gap:5,altLength:void 0},inheritColor:"from"},configurePhysics:!1,physics:{barnesHut:{enabled:!0,thetaInverted:2,gravitationalConstant:-2e3,centralGravity:.3,springLength:95,springConstant:.04,damping:.09},repulsion:{centralGravity:0,springLength:200,springConstant:.05,nodeDistance:100,damping:.09},hierarchicalRepulsion:{enabled:!1,centralGravity:0,springLength:100,springConstant:.01,nodeDistance:150,damping:.09},damping:null,centralGravity:null,springLength:null,springConstant:null},clustering:{enabled:!1,initialMaxNodes:100,clusterThreshold:500,reduceToNodes:300,chainThreshold:.4,clusterEdgeThreshold:20,sectorThreshold:100,screenSizeThreshold:.2,fontSizeMultiplier:4,maxFontSize:1e3,forceAmplification:.1,distanceAmplification:.1,edgeGrowth:20,nodeScaling:{width:1,height:1,radius:1},maxNodeSizeIncrements:600,activeAreaBoxSize:80,clusterLevelDifference:2},navigation:{enabled:!1},keyboard:{enabled:!1,speed:{x:10,y:10,zoom:.02},bindToWindow:!0},dataManipulation:{enabled:!1,initiallyVisible:!1},hierarchicalLayout:{enabled:!1,levelSeparation:150,nodeSpacing:100,direction:"UD",layout:"hubsize"},freezeForStabilization:!1,smoothCurves:{enabled:!0,dynamic:!0,type:"continuous",roundness:.5},maxVelocity:30,minVelocity:.1,stabilize:!0,stabilizationIterations:1e3,zoomExtentOnStabilize:!0,locale:"en",locales:_,tooltip:{delay:300,fontColor:"black",fontSize:14,fontFace:"verdana",color:{border:"#666",background:"#FFFFC6"}},dragNetwork:!0,dragNodes:!0,zoomable:!0,hover:!1,hideEdgesOnDrag:!1,hideNodesOnDrag:!1,width:"100%",height:"100%",selectable:!0},this.constants=a.extend({},this.defaultOptions),this.pixelRatio=1,this.hoverObj={nodes:{},edges:{}},this.controlNodesActive=!1,this.navigationHammers={existing:[],_new:[]},this.animationSpeed=1/this.renderRefreshRate,this.animationEasingFunction="easeInOutQuint",this.animating=!1,this.easingTime=0,this.sourceScale=0,this.targetScale=0,this.sourceTranslation=0,this.targetTranslation=0,this.lockedOnNodeId=null,this.lockedOnNodeOffset=null,this.touchTime=0;var o=this;this.groups=new u,this.images=new m,this.images.setOnloadCallback(function(){o._redraw()}),this.xIncrement=0,this.yIncrement=0,this.zoomIncrement=0,this._loadPhysicsSystem(),this._create(),this._loadSectorSystem(),this._loadClusterSystem(),this._loadSelectionSystem(),this._loadHierarchySystem(),this._setTranslation(this.frame.clientWidth/2,this.frame.clientHeight/2),this._setScale(1),this.setOptions(i),this.freezeSimulation=!1,this.cachedFunctions={},this.startedStabilization=!1,this.stabilized=!1,this.stabilizationIterations=null,this.draggingNodes=!1,this.calculationNodes={},this.calculationNodeIndices=[],this.nodeIndices=[],this.nodes={},this.edges={},this.canvasTopLeft={x:0,y:0},this.canvasBottomRight={x:0,y:0},this.pointerPosition={x:0,y:0},this.areaCenter={},this.scale=1,this.previousScale=this.scale,this.nodesData=null,this.edgesData=null,this.nodesListeners={add:function(t,e){o._addNodes(e.items),o.start()},update:function(t,e){o._updateNodes(e.items,e.data),o.start()},remove:function(t,e){o._removeNodes(e.items),o.start()}},this.edgesListeners={add:function(t,e){o._addEdges(e.items),o.start()},update:function(t,e){o._updateEdges(e.items),o.start()},remove:function(t,e){o._removeEdges(e.items),o.start()}},this.moving=!0,this.timer=void 0,this.setData(e,this.constants.clustering.enabled||this.constants.hierarchicalLayout.enabled),this.initializing=!1,1==this.constants.hierarchicalLayout.enabled?this._setupHierarchicalLayout():0==this.constants.stabilize&&this.zoomExtent(void 0,!0,this.constants.clustering.enabled),this.constants.clustering.enabled&&this.startWithClustering()}var o=i(56),n=i(45),r=i(63),a=i(1),h=i(47),d=i(3),l=i(4),c=i(42),p=i(43),u=i(38),m=i(39),f=i(40),g=i(37),v=i(41),y=i(54),b=i(55),_=i(49);i(50),o(s.prototype),s.prototype._determineBrowserMethod=function(){var t=navigator.userAgent.toLowerCase();this.requiresTimeout=!1,-1!=t.indexOf("msie 9.0")?this.requiresTimeout=!0:-1!=t.indexOf("safari")&&t.indexOf("chrome")<=-1&&(this.requiresTimeout=!0)},s.prototype._getScriptPath=function(){for(var t=document.getElementsByTagName("script"),e=0;et.boundingBox.left&&(s=t.boundingBox.left),ot.boundingBox.bottom&&(e=t.boundingBox.top),i=this.constants.clustering.initialMaxNodes?49.07548/(n+142.05338)+91444e-8:12.662/(n+7.4147)+.0964822:1==this.constants.clustering.enabled&&n>=this.constants.clustering.initialMaxNodes?77.5271985/(n+187.266146)+476710517e-13:30.5062972/(n+19.93597763)+.08413486;var r=Math.min(this.frame.canvas.clientWidth/600,this.frame.canvas.clientHeight/600);s*=r}else{var a=1.1*Math.abs(o.maxX-o.minX),h=1.1*Math.abs(o.maxY-o.minY),d=this.frame.canvas.clientWidth/a,l=this.frame.canvas.clientHeight/h;s=l>=d?d:l}s>1&&(s=1);var c=this._findCenter(o);if(0==i){var p={position:c,scale:s,animation:t};this.moveTo(p),this.moving=!0,this.start()}else c.x*=s,c.y*=s,c.x-=.5*this.frame.canvas.clientWidth,c.y-=.5*this.frame.canvas.clientHeight,this._setScale(s),this._setTranslation(-c.x,-c.y)},s.prototype._updateNodeIndexList=function(){this._clearNodeIndexList();for(var t in this.nodes)this.nodes.hasOwnProperty(t)&&this.nodeIndices.push(t)},s.prototype.setData=function(t,e){if(void 0===e&&(e=!1),this.initializing=!0,t&&t.dot&&(t.nodes||t.edges))throw new SyntaxError('Data must contain either parameter "dot" or parameter pair "nodes" and "edges", but not both.');if(1==this.constants.dataManipulation.enabled&&this._createManipulatorBar(),this.setOptions(t&&t.options),t&&t.dot){if(t&&t.dot){var i=c.DOTToGraph(t.dot);return void this.setData(i)}}else if(t&&t.gephi){if(t&&t.gephi){var s=p.parseGephi(t.gephi);return void this.setData(s)}}else this._setNodes(t&&t.nodes),this._setEdges(t&&t.edges);this._putDataInSector(),0==e&&(1==this.constants.hierarchicalLayout.enabled?(this._resetLevels(),this._setupHierarchicalLayout()):this.constants.stabilize&&this._stabilize(),this.start()),this.initializing=!1},s.prototype.setOptions=function(t){if(t){var e,i=["nodes","edges","smoothCurves","hierarchicalLayout","clustering","navigation","keyboard","dataManipulation","onAdd","onEdit","onEditEdge","onConnect","onDelete","clickToUse"];if(a.selectiveNotDeepExtend(i,this.constants,t),a.selectiveNotDeepExtend(["color"],this.constants.nodes,t.nodes),a.selectiveNotDeepExtend(["color","length"],this.constants.edges,t.edges),t.physics&&(a.mergeOptions(this.constants.physics,t.physics,"barnesHut"),a.mergeOptions(this.constants.physics,t.physics,"repulsion"),t.physics.hierarchicalRepulsion)){this.constants.hierarchicalLayout.enabled=!0,this.constants.physics.hierarchicalRepulsion.enabled=!0,this.constants.physics.barnesHut.enabled=!1; -for(e in t.physics.hierarchicalRepulsion)t.physics.hierarchicalRepulsion.hasOwnProperty(e)&&(this.constants.physics.hierarchicalRepulsion[e]=t.physics.hierarchicalRepulsion[e])}if(t.onAdd&&(this.triggerFunctions.add=t.onAdd),t.onEdit&&(this.triggerFunctions.edit=t.onEdit),t.onEditEdge&&(this.triggerFunctions.editEdge=t.onEditEdge),t.onConnect&&(this.triggerFunctions.connect=t.onConnect),t.onDelete&&(this.triggerFunctions.del=t.onDelete),a.mergeOptions(this.constants,t,"smoothCurves"),a.mergeOptions(this.constants,t,"hierarchicalLayout"),a.mergeOptions(this.constants,t,"clustering"),a.mergeOptions(this.constants,t,"navigation"),a.mergeOptions(this.constants,t,"keyboard"),a.mergeOptions(this.constants,t,"dataManipulation"),t.dataManipulation&&(this.editMode=this.constants.dataManipulation.initiallyVisible),t.edges&&(void 0!==t.edges.color&&(a.isString(t.edges.color)?(this.constants.edges.color={},this.constants.edges.color.color=t.edges.color,this.constants.edges.color.highlight=t.edges.color,this.constants.edges.color.hover=t.edges.color):(void 0!==t.edges.color.color&&(this.constants.edges.color.color=t.edges.color.color),void 0!==t.edges.color.highlight&&(this.constants.edges.color.highlight=t.edges.color.highlight),void 0!==t.edges.color.hover&&(this.constants.edges.color.hover=t.edges.color.hover)),this.constants.edges.inheritColor=!1),t.edges.fontColor||void 0!==t.edges.color&&(a.isString(t.edges.color)?this.constants.edges.fontColor=t.edges.color:void 0!==t.edges.color.color&&(this.constants.edges.fontColor=t.edges.color.color))),t.nodes&&t.nodes.color){var s=a.parseColor(t.nodes.color);this.constants.nodes.color.background=s.background,this.constants.nodes.color.border=s.border,this.constants.nodes.color.highlight.background=s.highlight.background,this.constants.nodes.color.highlight.border=s.highlight.border,this.constants.nodes.color.hover.background=s.hover.background,this.constants.nodes.color.hover.border=s.hover.border}if(t.groups)for(var o in t.groups)if(t.groups.hasOwnProperty(o)){var n=t.groups[o];this.groups.add(o,n)}if(t.tooltip){for(e in t.tooltip)t.tooltip.hasOwnProperty(e)&&(this.constants.tooltip[e]=t.tooltip[e]);t.tooltip.color&&(this.constants.tooltip.color=a.parseColor(t.tooltip.color))}if("clickToUse"in t&&(t.clickToUse?this.activator||(this.activator=new b(this.frame),this.activator.on("change",this._createKeyBinds.bind(this))):this.activator&&(this.activator.destroy(),delete this.activator)),t.labels)throw new Error('Option "labels" is deprecated. Use options "locale" and "locales" instead.');this._loadPhysicsSystem(),this._loadNavigationControls(),this._loadManipulationSystem(),this._configureSmoothCurves(),this._bindHammer(),this._createKeyBinds(),this.setSize(this.constants.width,this.constants.height),this.moving=!0,this.start()}},s.prototype._create=function(){for(;this.containerElement.hasChildNodes();)this.containerElement.removeChild(this.containerElement.firstChild);if(this.frame=document.createElement("div"),this.frame.className="vis network-frame",this.frame.style.position="relative",this.frame.style.overflow="hidden",this.frame.tabIndex=900,this.frame.canvas=document.createElement("canvas"),this.frame.canvas.style.position="relative",this.frame.appendChild(this.frame.canvas),this.frame.canvas.getContext){var t=this.frame.canvas.getContext("2d");this.pixelRatio=(window.devicePixelRatio||1)/(t.webkitBackingStorePixelRatio||t.mozBackingStorePixelRatio||t.msBackingStorePixelRatio||t.oBackingStorePixelRatio||t.backingStorePixelRatio||1),this.frame.canvas.getContext("2d").setTransform(this.pixelRatio,0,0,this.pixelRatio,0,0)}else{var e=document.createElement("DIV");e.style.color="red",e.style.fontWeight="bold",e.style.padding="10px",e.innerHTML="Error: your browser does not support HTML canvas",this.frame.canvas.appendChild(e)}this._bindHammer()},s.prototype._bindHammer=function(){var t=this;void 0!==this.hammer&&this.hammer.dispose(),this.drag={},this.pinch={},this.hammer=n(this.frame.canvas,{prevent_default:!0}),this.hammer.on("tap",t._onTap.bind(t)),this.hammer.on("doubletap",t._onDoubleTap.bind(t)),this.hammer.on("hold",t._onHold.bind(t)),this.hammer.on("touch",t._onTouch.bind(t)),this.hammer.on("dragstart",t._onDragStart.bind(t)),this.hammer.on("drag",t._onDrag.bind(t)),this.hammer.on("dragend",t._onDragEnd.bind(t)),1==this.constants.zoomable&&(this.hammer.on("mousewheel",t._onMouseWheel.bind(t)),this.hammer.on("DOMMouseScroll",t._onMouseWheel.bind(t)),this.hammer.on("pinch",t._onPinch.bind(t))),this.hammer.on("mousemove",t._onMouseMoveTitle.bind(t)),this.hammerFrame=n(this.frame,{prevent_default:!0}),this.hammerFrame.on("release",t._onRelease.bind(t)),this.containerElement.appendChild(this.frame)},s.prototype._createKeyBinds=function(){var t=this;void 0!==this.keycharm&&this.keycharm.destroy(),this.keycharm=r(1==this.constants.keyboard.bindToWindow?{container:window,preventDefault:!1}:{container:this.frame,preventDefault:!1}),this.keycharm.reset(),this.constants.keyboard.enabled&&this.isActive()&&(this.keycharm.bind("up",this._moveUp.bind(t),"keydown"),this.keycharm.bind("up",this._yStopMoving.bind(t),"keyup"),this.keycharm.bind("down",this._moveDown.bind(t),"keydown"),this.keycharm.bind("down",this._yStopMoving.bind(t),"keyup"),this.keycharm.bind("left",this._moveLeft.bind(t),"keydown"),this.keycharm.bind("left",this._xStopMoving.bind(t),"keyup"),this.keycharm.bind("right",this._moveRight.bind(t),"keydown"),this.keycharm.bind("right",this._xStopMoving.bind(t),"keyup"),this.keycharm.bind("=",this._zoomIn.bind(t),"keydown"),this.keycharm.bind("=",this._stopZoom.bind(t),"keyup"),this.keycharm.bind("num+",this._zoomIn.bind(t),"keydown"),this.keycharm.bind("num+",this._stopZoom.bind(t),"keyup"),this.keycharm.bind("num-",this._zoomOut.bind(t),"keydown"),this.keycharm.bind("num-",this._stopZoom.bind(t),"keyup"),this.keycharm.bind("-",this._zoomOut.bind(t),"keydown"),this.keycharm.bind("-",this._stopZoom.bind(t),"keyup"),this.keycharm.bind("[",this._zoomIn.bind(t),"keydown"),this.keycharm.bind("[",this._stopZoom.bind(t),"keyup"),this.keycharm.bind("]",this._zoomOut.bind(t),"keydown"),this.keycharm.bind("]",this._stopZoom.bind(t),"keyup"),this.keycharm.bind("pageup",this._zoomIn.bind(t),"keydown"),this.keycharm.bind("pageup",this._stopZoom.bind(t),"keyup"),this.keycharm.bind("pagedown",this._zoomOut.bind(t),"keydown"),this.keycharm.bind("pagedown",this._stopZoom.bind(t),"keyup")),1==this.constants.dataManipulation.enabled&&(this.keycharm.bind("esc",this._createManipulatorBar.bind(t)),this.keycharm.bind("delete",this._deleteSelected.bind(t)))},s.prototype.destroy=function(){this.start=function(){},this.redraw=function(){},this.timer=!1,this._cleanupPhysicsConfiguration(),this.keycharm.reset(),this.hammer.dispose(),this.off(),this._recursiveDOMDelete(this.containerElement)},s.prototype._recursiveDOMDelete=function(t){for(;1==t.hasChildNodes();)this._recursiveDOMDelete(t.firstChild),t.removeChild(t.firstChild)},s.prototype._getPointer=function(t){return{x:t.pageX-a.getAbsoluteLeft(this.frame.canvas),y:t.pageY-a.getAbsoluteTop(this.frame.canvas)}},s.prototype._onTouch=function(t){(new Date).valueOf()-this.touchTime>100&&(this.drag.pointer=this._getPointer(t.gesture.center),this.drag.pinched=!1,this.pinch.scale=this._getScale(),this.touchTime=(new Date).valueOf(),this._handleTouch(this.drag.pointer))},s.prototype._onDragStart=function(t){this._handleDragStart(t)},s.prototype._handleDragStart=function(t){void 0===this.drag.pointer&&this._onTouch(t);var e=this._getNodeAt(this.drag.pointer);if(this.drag.dragging=!0,this.drag.selection=[],this.drag.translation=this._getTranslation(),this.drag.nodeId=null,this.draggingNodes=!1,null!=e&&1==this.constants.dragNodes){this.draggingNodes=!0,this.drag.nodeId=e.id,e.isSelected()||this._selectObject(e,!1),this.emit("dragStart",{nodeIds:this.getSelection().nodes});for(var i in this.selectionObj.nodes)if(this.selectionObj.nodes.hasOwnProperty(i)){var s=this.selectionObj.nodes[i],o={id:s.id,node:s,x:s.x,y:s.y,xFixed:s.xFixed,yFixed:s.yFixed};s.xFixed=!0,s.yFixed=!0,this.drag.selection.push(o)}}},s.prototype._onDrag=function(t){this._handleOnDrag(t)},s.prototype._handleOnDrag=function(t){if(!this.drag.pinched){this.releaseNode();var e=this._getPointer(t.gesture.center),i=this,s=this.drag,o=s.selection;if(o&&o.length&&1==this.constants.dragNodes){var n=e.x-s.pointer.x,r=e.y-s.pointer.y;o.forEach(function(t){var e=t.node;t.xFixed||(e.x=i._XconvertDOMtoCanvas(i._XconvertCanvasToDOM(t.x)+n)),t.yFixed||(e.y=i._YconvertDOMtoCanvas(i._YconvertCanvasToDOM(t.y)+r))}),this.moving||(this.moving=!0,this.start())}else if(1==this.constants.dragNetwork){if(void 0===this.drag.pointer)return void this._handleDragStart(t);var a=e.x-this.drag.pointer.x,h=e.y-this.drag.pointer.y;this._setTranslation(this.drag.translation.x+a,this.drag.translation.y+h),this._redraw()}}},s.prototype._onDragEnd=function(t){this._handleDragEnd(t)},s.prototype._handleDragEnd=function(){this.drag.dragging=!1;var t=this.drag.selection;t&&t.length?(t.forEach(function(t){t.node.xFixed=t.xFixed,t.node.yFixed=t.yFixed}),this.moving=!0,this.start()):this._redraw(),0==this.draggingNodes?this.emit("dragEnd",{nodeIds:[]}):this.emit("dragEnd",{nodeIds:this.getSelection().nodes})},s.prototype._onTap=function(t){var e=this._getPointer(t.gesture.center);this.pointerPosition=e,this._handleTap(e)},s.prototype._onDoubleTap=function(t){var e=this._getPointer(t.gesture.center);this._handleDoubleTap(e)},s.prototype._onHold=function(t){var e=this._getPointer(t.gesture.center);this.pointerPosition=e,this._handleOnHold(e)},s.prototype._onRelease=function(t){var e=this._getPointer(t.gesture.center);this._handleOnRelease(e)},s.prototype._onPinch=function(t){var e=this._getPointer(t.gesture.center);this.drag.pinched=!0,"scale"in this.pinch||(this.pinch.scale=1);var i=this.pinch.scale*t.gesture.scale;this._zoom(i,e)},s.prototype._zoom=function(t,e){if(1==this.constants.zoomable){var i=this._getScale();1e-5>t&&(t=1e-5),t>10&&(t=10);var s=null;void 0!==this.drag&&1==this.drag.dragging&&(s=this.DOMtoCanvas(this.drag.pointer));var o=this._getTranslation(),n=t/i,r=(1-n)*e.x+o.x*n,a=(1-n)*e.y+o.y*n;if(this.areaCenter={x:this._XconvertDOMtoCanvas(e.x),y:this._YconvertDOMtoCanvas(e.y)},this._setScale(t),this._setTranslation(r,a),this.updateClustersDefault(),null!=s){var h=this.canvasToDOM(s);this.drag.pointer.x=h.x,this.drag.pointer.y=h.y}return this._redraw(),t>i?this.emit("zoom",{direction:"+"}):this.emit("zoom",{direction:"-"}),t}},s.prototype._onMouseWheel=function(t){var e=0;if(t.wheelDelta?e=t.wheelDelta/120:t.detail&&(e=-t.detail/3),e){var i=this._getScale(),s=e/10;0>e&&(s/=1-s),i*=1+s;var o=h.fakeGesture(this,t),n=this._getPointer(o.center);this._zoom(i,n)}t.preventDefault()},s.prototype._onMouseMoveTitle=function(t){var e=h.fakeGesture(this,t),i=this._getPointer(e.center);this.popupObj&&this._checkHidePopup(i),0==this.constants.keyboard.bindToWindow&&1==this.constants.keyboard.enabled&&this.frame.focus();var s=this,o=function(){s._checkShowPopup(i)};if(this.popupTimer&&clearInterval(this.popupTimer),this.drag.dragging||(this.popupTimer=setTimeout(o,this.constants.tooltip.delay)),1==this.constants.hover){for(var n in this.hoverObj.edges)this.hoverObj.edges.hasOwnProperty(n)&&(this.hoverObj.edges[n].hover=!1,delete this.hoverObj.edges[n]);var r=this._getNodeAt(i);null==r&&(r=this._getEdgeAt(i)),null!=r&&this._hoverObject(r);for(var a in this.hoverObj.nodes)this.hoverObj.nodes.hasOwnProperty(a)&&(r instanceof f&&r.id!=a||r instanceof g||null==r)&&(this._blurObject(this.hoverObj.nodes[a]),delete this.hoverObj.nodes[a]);this.redraw()}},s.prototype._checkShowPopup=function(t){var e,i={left:this._XconvertDOMtoCanvas(t.x),top:this._YconvertDOMtoCanvas(t.y),right:this._XconvertDOMtoCanvas(t.x),bottom:this._YconvertDOMtoCanvas(t.y)},s=this.popupObj,o=!1;if(void 0==this.popupObj){var n=this.nodes,r=[];for(e in n)if(n.hasOwnProperty(e)){var a=n[e];a.isOverlappingWith(i)&&void 0!==a.getTitle()&&r.push(e)}r.length>0&&(this.popupObj=this.nodes[r[r.length-1]],o=!0)}if(void 0===this.popupObj&&0==o){var h=this.edges,d=[];for(e in h)if(h.hasOwnProperty(e)){var l=h[e];l.connected&&void 0!==l.getTitle()&&l.isOverlappingWith(i)&&d.push(e)}d.length>0&&(this.popupObj=this.edges[d[d.length-1]])}if(this.popupObj){if(this.popupObj!=s){var c=this;c.popup||(c.popup=new v(c.frame,c.constants.tooltip)),c.popup.setPosition(t.x-3,t.y-3),c.popup.setText(c.popupObj.getTitle()),c.popup.show()}}else this.popup&&this.popup.hide()},s.prototype._checkHidePopup=function(t){this.popupObj&&this._getNodeAt(t)||(this.popupObj=void 0,this.popup&&this.popup.hide())},s.prototype.setSize=function(t,e){var i=!1,s=this.frame.canvas.width,o=this.frame.canvas.height;t!=this.constants.width||e!=this.constants.height||this.frame.style.width!=t||this.frame.style.height!=e?(this.frame.style.width=t,this.frame.style.height=e,this.frame.canvas.style.width="100%",this.frame.canvas.style.height="100%",this.frame.canvas.width=this.frame.canvas.clientWidth*this.pixelRatio,this.frame.canvas.height=this.frame.canvas.clientHeight*this.pixelRatio,this.constants.width=t,this.constants.height=e,i=!0):(this.frame.canvas.width!=this.frame.canvas.clientWidth*this.pixelRatio&&(this.frame.canvas.width=this.frame.canvas.clientWidth*this.pixelRatio,i=!0),this.frame.canvas.height!=this.frame.canvas.clientHeight*this.pixelRatio&&(this.frame.canvas.height=this.frame.canvas.clientHeight*this.pixelRatio,i=!0)),1==i&&this.emit("resize",{width:this.frame.canvas.width*this.pixelRatio,height:this.frame.canvas.height*this.pixelRatio,oldWidth:s*this.pixelRatio,oldHeight:o*this.pixelRatio})},s.prototype._setNodes=function(t){var e=this.nodesData;if(t instanceof d||t instanceof l)this.nodesData=t;else if(Array.isArray(t))this.nodesData=new d,this.nodesData.add(t);else{if(t)throw new TypeError("Array or DataSet expected");this.nodesData=new d}if(e&&a.forEach(this.nodesListeners,function(t,i){e.off(i,t)}),this.nodes={},this.nodesData){var i=this;a.forEach(this.nodesListeners,function(t,e){i.nodesData.on(e,t)});var s=this.nodesData.getIds();this._addNodes(s)}this._updateSelection()},s.prototype._addNodes=function(t){for(var e,i=0,s=t.length;s>i;i++){e=t[i];var o=this.nodesData.get(e),n=new f(o,this.images,this.groups,this.constants);if(this.nodes[e]=n,!(0!=n.xFixed&&0!=n.yFixed||null!==n.x&&null!==n.y)){var r=1*t.length+10,a=2*Math.PI*Math.random();0==n.xFixed&&(n.x=r*Math.cos(a)),0==n.yFixed&&(n.y=r*Math.sin(a))}this.moving=!0}this._updateNodeIndexList(),1==this.constants.hierarchicalLayout.enabled&&0==this.initializing&&(this._resetLevels(),this._setupHierarchicalLayout()),this._updateCalculationNodes(),this._reconnectEdges(),this._updateValueRange(this.nodes),this.updateLabels()},s.prototype._updateNodes=function(t,e){for(var i=this.nodes,s=0,o=t.length;o>s;s++){var n=t[s],r=i[n],a=e[s];r?r.setProperties(a,this.constants):(r=new f(properties,this.images,this.groups,this.constants),i[n]=r)}this.moving=!0,1==this.constants.hierarchicalLayout.enabled&&0==this.initializing&&(this._resetLevels(),this._setupHierarchicalLayout()),this._updateNodeIndexList(),this._updateValueRange(i)},s.prototype._removeNodes=function(t){for(var e=this.nodes,i=0,s=t.length;s>i;i++){var o=t[i];delete e[o]}this._updateNodeIndexList(),1==this.constants.hierarchicalLayout.enabled&&0==this.initializing&&(this._resetLevels(),this._setupHierarchicalLayout()),this._updateCalculationNodes(),this._reconnectEdges(),this._updateSelection(),this._updateValueRange(e)},s.prototype._setEdges=function(t){var e=this.edgesData;if(t instanceof d||t instanceof l)this.edgesData=t;else if(Array.isArray(t))this.edgesData=new d,this.edgesData.add(t);else{if(t)throw new TypeError("Array or DataSet expected");this.edgesData=new d}if(e&&a.forEach(this.edgesListeners,function(t,i){e.off(i,t)}),this.edges={},this.edgesData){var i=this;a.forEach(this.edgesListeners,function(t,e){i.edgesData.on(e,t)});var s=this.edgesData.getIds();this._addEdges(s)}this._reconnectEdges()},s.prototype._addEdges=function(t){for(var e=this.edges,i=this.edgesData,s=0,o=t.length;o>s;s++){var n=t[s],r=e[n];r&&r.disconnect();var a=i.get(n,{showInternalIds:!0});e[n]=new g(a,this,this.constants)}this.moving=!0,this._updateValueRange(e),this._createBezierNodes(),this._updateCalculationNodes(),1==this.constants.hierarchicalLayout.enabled&&0==this.initializing&&(this._resetLevels(),this._setupHierarchicalLayout())},s.prototype._updateEdges=function(t){for(var e=this.edges,i=this.edgesData,s=0,o=t.length;o>s;s++){var n=t[s],r=i.get(n),a=e[n];a?(a.disconnect(),a.setProperties(r,this.constants),a.connect()):(a=new g(r,this,this.constants),this.edges[n]=a)}this._createBezierNodes(),1==this.constants.hierarchicalLayout.enabled&&0==this.initializing&&(this._resetLevels(),this._setupHierarchicalLayout()),this.moving=!0,this._updateValueRange(e)},s.prototype._removeEdges=function(t){for(var e=this.edges,i=0,s=t.length;s>i;i++){var o=t[i],n=e[o];n&&(null!=n.via&&delete this.sectors.support.nodes[n.via.id],n.disconnect(),delete e[o])}this.moving=!0,this._updateValueRange(e),1==this.constants.hierarchicalLayout.enabled&&0==this.initializing&&(this._resetLevels(),this._setupHierarchicalLayout()),this._updateCalculationNodes()},s.prototype._reconnectEdges=function(){var t,e=this.nodes,i=this.edges;for(t in e)e.hasOwnProperty(t)&&(e[t].edges=[],e[t].dynamicEdges=[]);for(t in i)if(i.hasOwnProperty(t)){var s=i[t];s.from=null,s.to=null,s.connect()}},s.prototype._updateValueRange=function(t){var e,i=void 0,s=void 0;for(e in t)if(t.hasOwnProperty(e)){var o=t[e].getValue();void 0!==o&&(i=void 0===i?o:Math.min(o,i),s=void 0===s?o:Math.max(o,s))}if(void 0!==i&&void 0!==s)for(e in t)t.hasOwnProperty(e)&&t[e].setValueRange(i,s)},s.prototype.redraw=function(){this.setSize(this.constants.width,this.constants.height),this._redraw()},s.prototype._redraw=function(t){var e=this.frame.canvas.getContext("2d");e.setTransform(this.pixelRatio,0,0,this.pixelRatio,0,0);var i=this.frame.canvas.width*this.pixelRatio,s=this.frame.canvas.height*this.pixelRatio;e.clearRect(0,0,i,s),e.save(),e.translate(this.translation.x,this.translation.y),e.scale(this.scale,this.scale),this.canvasTopLeft={x:this._XconvertDOMtoCanvas(0),y:this._YconvertDOMtoCanvas(0)},this.canvasBottomRight={x:this._XconvertDOMtoCanvas(this.frame.canvas.clientWidth*this.pixelRatio),y:this._YconvertDOMtoCanvas(this.frame.canvas.clientHeight*this.pixelRatio)},1!=t&&(this._doInAllSectors("_drawAllSectorNodes",e),(0==this.drag.dragging||void 0===this.drag.dragging||0==this.constants.hideEdgesOnDrag)&&this._doInAllSectors("_drawEdges",e)),(0==this.drag.dragging||void 0===this.drag.dragging||0==this.constants.hideNodesOnDrag)&&this._doInAllSectors("_drawNodes",e,!1),1!=t&&1==this.controlNodesActive&&this._doInAllSectors("_drawControlNodes",e),e.restore(),1==t&&e.clearRect(0,0,i,s)},s.prototype._setTranslation=function(t,e){void 0===this.translation&&(this.translation={x:0,y:0}),void 0!==t&&(this.translation.x=t),void 0!==e&&(this.translation.y=e),this.emit("viewChanged")},s.prototype._getTranslation=function(){return{x:this.translation.x,y:this.translation.y}},s.prototype._setScale=function(t){this.scale=t},s.prototype._getScale=function(){return this.scale},s.prototype._XconvertDOMtoCanvas=function(t){return(t-this.translation.x)/this.scale},s.prototype._XconvertCanvasToDOM=function(t){return t*this.scale+this.translation.x},s.prototype._YconvertDOMtoCanvas=function(t){return(t-this.translation.y)/this.scale},s.prototype._YconvertCanvasToDOM=function(t){return t*this.scale+this.translation.y},s.prototype.canvasToDOM=function(t){return{x:this._XconvertCanvasToDOM(t.x),y:this._YconvertCanvasToDOM(t.y)}},s.prototype.DOMtoCanvas=function(t){return{x:this._XconvertDOMtoCanvas(t.x),y:this._YconvertDOMtoCanvas(t.y)}},s.prototype._drawNodes=function(t,e){void 0===e&&(e=!1);var i=this.nodes,s=[];for(var o in i)i.hasOwnProperty(o)&&(i[o].setScaleAndPos(this.scale,this.canvasTopLeft,this.canvasBottomRight),i[o].isSelected()?s.push(o):(i[o].inArea()||e)&&i[o].draw(t));for(var n=0,r=s.length;r>n;n++)(i[s[n]].inArea()||e)&&i[s[n]].draw(t)},s.prototype._drawEdges=function(t){var e=this.edges;for(var i in e)if(e.hasOwnProperty(i)){var s=e[i];s.setScale(this.scale),s.connected&&e[i].draw(t)}},s.prototype._drawControlNodes=function(t){var e=this.edges;for(var i in e)e.hasOwnProperty(i)&&e[i]._drawControlNodes(t)},s.prototype._stabilize=function(){1==this.constants.freezeForStabilization&&this._freezeDefinedNodes();for(var t=0;this.moving&&t0)for(t in i)i.hasOwnProperty(t)&&(i[t].discreteStepLimited(e,this.constants.maxVelocity),s=!0);else for(t in i)i.hasOwnProperty(t)&&(i[t].discreteStep(e),s=!0);if(1==s){var o=this.constants.minVelocity/Math.max(this.scale,.05);return o>.5*this.constants.maxVelocity?!0:this._isMoving(o)}return!1},s.prototype._revertPhysicsState=function(){var t=this.nodes;for(var e in t)t.hasOwnProperty(e)&&t[e].revertPosition()},s.prototype._revertPhysicsTick=function(){this._doInAllActiveSectors("_revertPhysicsState"),1==this.constants.smoothCurves.enabled&&1==this.constants.smoothCurves.dynamic&&this._doInSupportSector("_revertPhysicsState")},s.prototype._physicsTick=function(){if(!this.freezeSimulation&&1==this.moving){var t=!1,e=!1;this._doInAllActiveSectors("_initializeForceCalculation");var i=this._doInAllActiveSectors("_discreteStepNodes");1==this.constants.smoothCurves.enabled&&1==this.constants.smoothCurves.dynamic&&(e=this._doInSupportSector("_discreteStepNodes"));for(var s=0;s2*e||1==this.runDoubleSpeed)&&1==this.moving&&(this._physicsTick(),0!=this.renderTime&&(this.runDoubleSpeed=!0))}var i=Date.now();this._redraw(),this.renderTime=Date.now()-i,this.start()},"undefined"!=typeof window&&(window.requestAnimationFrame=window.requestAnimationFrame||window.mozRequestAnimationFrame||window.webkitRequestAnimationFrame||window.msRequestAnimationFrame),s.prototype.start=function(){if(1==this.moving||0!=this.xIncrement||0!=this.yIncrement||0!=this.zoomIncrement||1==this.animating)this.timer||(this.timer=1==this.requiresTimeout?window.setTimeout(this._animationStep.bind(this),this.renderTimestep):window.requestAnimationFrame(this._animationStep.bind(this)));else if(this._redraw(),this.stabilizationIterations>1){var t=this,e={iterations:t.stabilizationIterations};this.stabilizationIterations=0,this.startedStabilization=!1,setTimeout(function(){t.emit("stabilized",e)},0)}else this.stabilizationIterations=0},s.prototype._handleNavigation=function(){if(0!=this.xIncrement||0!=this.yIncrement){var t=this._getTranslation();this._setTranslation(t.x+this.xIncrement,t.y+this.yIncrement)}if(0!=this.zoomIncrement){var e={x:this.frame.canvas.clientWidth/2,y:this.frame.canvas.clientHeight/2};this._zoom(this.scale*(1+this.zoomIncrement),e)}},s.prototype.toggleFreeze=function(){0==this.freezeSimulation?this.freezeSimulation=!0:(this.freezeSimulation=!1,this.start())},s.prototype._configureSmoothCurves=function(t){if(void 0===t&&(t=!0),1==this.constants.smoothCurves.enabled&&1==this.constants.smoothCurves.dynamic){this._createBezierNodes();for(var e in this.sectors.support.nodes)this.sectors.support.nodes.hasOwnProperty(e)&&void 0===this.edges[this.sectors.support.nodes[e].parentEdgeId]&&delete this.sectors.support.nodes[e]}else{this.sectors.support.nodes={};for(var i in this.edges)this.edges.hasOwnProperty(i)&&(this.edges[i].via=null)}this._updateCalculationNodes(),t||(this.moving=!0,this.start())},s.prototype._createBezierNodes=function(){if(1==this.constants.smoothCurves.enabled&&1==this.constants.smoothCurves.dynamic)for(var t in this.edges)if(this.edges.hasOwnProperty(t)){var e=this.edges[t];if(null==e.via){var i="edgeId:".concat(e.id);this.sectors.support.nodes[i]=new f({id:i,mass:1,shape:"circle",image:"",internalMultiplier:1},{},{},this.constants),e.via=this.sectors.support.nodes[i],e.via.parentEdgeId=e.id,e.positionBezierNode()}}},s.prototype._initializeMixinLoaders=function(){for(var t in y)y.hasOwnProperty(t)&&(s.prototype[t]=y[t])},s.prototype.storePosition=function(){console.log("storePosition is depricated: use .storePositions() from now on."),this.storePositions()},s.prototype.storePositions=function(){var t=[];for(var e in this.nodes)if(this.nodes.hasOwnProperty(e)){var i=this.nodes[e],s=!this.nodes.xFixed,o=!this.nodes.yFixed;(this.nodesData._data[e].x!=Math.round(i.x)||this.nodesData._data[e].y!=Math.round(i.y))&&t.push({id:e,x:Math.round(i.x),y:Math.round(i.y),allowedToMoveX:s,allowedToMoveY:o})}this.nodesData.update(t)},s.prototype.getPositions=function(t){var e={};if(void 0!==t){if(1==Array.isArray(t)){for(var i=0;i=1&&(this.animating=!1,this.easingTime=0,this._redraw=null!=this.lockedOnNodeId?this._lockedRedraw:this._classicRedraw,this.emit("animationFinished"))},s.prototype._classicRedraw=function(){},s.prototype.isActive=function(){return!this.activator||this.activator.active},s.prototype.setScale=function(){return this._setScale()},s.prototype.getScale=function(){return this._getScale()},s.prototype.getCenterCoordinates=function(){return this.DOMtoCanvas({x:.5*this.frame.canvas.clientWidth,y:.5*this.frame.canvas.clientHeight})},s.prototype.getBoundingBox=function(t){return void 0!==this.nodes[t]?this.nodes[t].boundingBox:void 0},t.exports=s},function(t,e,i){function s(t,e,i){if(!e)throw"No network provided";var s=["edges","physics"],n=o.selectiveBridgeObject(s,i);this.options=n.edges,this.physics=n.physics,this.options.smoothCurves=i.smoothCurves,this.network=e,this.id=void 0,this.fromId=void 0,this.toId=void 0,this.title=void 0,this.widthSelected=this.options.width*this.options.widthSelectionMultiplier,this.value=void 0,this.selected=!1,this.hover=!1,this.labelDimensions={top:0,left:0,width:0,height:0,yLine:0},this.dirtyLabel=!0,this.from=null,this.to=null,this.via=null,this.fromBackup=null,this.toBackup=null,this.originalFromId=[],this.originalToId=[],this.connected=!1,this.widthFixed=!1,this.lengthFixed=!1,this.setProperties(t),this.controlNodesEnabled=!1,this.controlNodes={from:null,to:null,positions:{}},this.connectedNode=null}var o=i(1),n=i(40);s.prototype.setProperties=function(t){if(t){var e=["style","fontSize","fontFace","fontColor","fontFill","fontStrokeWidth","fontStrokeColor","width","widthSelectionMultiplier","hoverWidth","arrowScaleFactor","dash","inheritColor","labelAlignment"];switch(o.selectiveDeepExtend(e,this.options,t),void 0!==t.from&&(this.fromId=t.from),void 0!==t.to&&(this.toId=t.to),void 0!==t.id&&(this.id=t.id),void 0!==t.label&&(this.label=t.label,this.dirtyLabel=!0),void 0!==t.title&&(this.title=t.title),void 0!==t.value&&(this.value=t.value),void 0!==t.length&&(this.physics.springLength=t.length),void 0!==t.color&&(this.options.inheritColor=!1,o.isString(t.color)?(this.options.color.color=t.color,this.options.color.highlight=t.color):(void 0!==t.color.color&&(this.options.color.color=t.color.color),void 0!==t.color.highlight&&(this.options.color.highlight=t.color.highlight),void 0!==t.color.hover&&(this.options.color.hover=t.color.hover))),this.connect(),this.widthFixed=this.widthFixed||void 0!==t.width,this.lengthFixed=this.lengthFixed||void 0!==t.length,this.widthSelected=this.options.width*this.options.widthSelectionMultiplier,this.options.style){case"line":this.draw=this._drawLine;break;case"arrow":this.draw=this._drawArrow;break;case"arrow-center":this.draw=this._drawArrowCenter; -break;case"dash-line":this.draw=this._drawDashLine;break;default:this.draw=this._drawLine}}},s.prototype.connect=function(){this.disconnect(),this.from=this.network.nodes[this.fromId]||null,this.to=this.network.nodes[this.toId]||null,this.connected=this.from&&this.to,this.connected?(this.from.attachEdge(this),this.to.attachEdge(this)):(this.from&&this.from.detachEdge(this),this.to&&this.to.detachEdge(this))},s.prototype.disconnect=function(){this.from&&(this.from.detachEdge(this),this.from=null),this.to&&(this.to.detachEdge(this),this.to=null),this.connected=!1},s.prototype.getTitle=function(){return"function"==typeof this.title?this.title():this.title},s.prototype.getValue=function(){return this.value},s.prototype.setValueRange=function(t,e){if(!this.widthFixed&&void 0!==this.value){var i=(this.options.widthMax-this.options.widthMin)/(e-t);this.options.width=(this.value-t)*i+this.options.widthMin,this.widthSelected=this.options.width*this.options.widthSelectionMultiplier}},s.prototype.draw=function(){throw"Method draw not initialized in edge"},s.prototype.isOverlappingWith=function(t){if(this.connected){var e=10,i=this.from.x,s=this.from.y,o=this.to.x,n=this.to.y,r=t.left,a=t.top,h=this._getDistanceToEdge(i,s,o,n,r,a);return e>h}return!1},s.prototype._getColor=function(){var t=this.options.color;return"to"==this.options.inheritColor?t={highlight:this.to.options.color.highlight.border,hover:this.to.options.color.hover.border,color:this.to.options.color.border}:("from"==this.options.inheritColor||1==this.options.inheritColor)&&(t={highlight:this.from.options.color.highlight.border,hover:this.from.options.color.hover.border,color:this.from.options.color.border}),1==this.selected?t.highlight:1==this.hover?t.hover:t.color},s.prototype._drawLine=function(t){if(t.strokeStyle=this._getColor(),t.lineWidth=this._getLineWidth(),this.from!=this.to){var e,i=this._line(t);if(this.label){if(1==this.options.smoothCurves.enabled&&null!=i){var s=.5*(.5*(this.from.x+i.x)+.5*(this.to.x+i.x)),o=.5*(.5*(this.from.y+i.y)+.5*(this.to.y+i.y));e={x:s,y:o}}else e=this._pointOnLine(.5);this._label(t,this.label,e.x,e.y)}}else{var n,r,a=this.physics.springLength/4,h=this.from;h.width||h.resize(t),h.width>h.height?(n=h.x+h.width/2,r=h.y-a):(n=h.x+a,r=h.y-h.height/2),this._circle(t,n,r,a),e=this._pointOnCircle(n,r,a,.5),this._label(t,this.label,e.x,e.y)}},s.prototype._getLineWidth=function(){return 1==this.selected?Math.max(Math.min(this.widthSelected,this.options.widthMax),.3*this.networkScaleInv):1==this.hover?Math.max(Math.min(this.options.hoverWidth,this.options.widthMax),.3*this.networkScaleInv):Math.max(this.options.width,.3*this.networkScaleInv)},s.prototype._getViaCoordinates=function(){if(1==this.options.smoothCurves.dynamic&&1==this.options.smoothCurves.enabled)return this.via;if(0==this.options.smoothCurves.enabled)return{x:0,y:0};var t=null,e=null,i=this.options.smoothCurves.roundness,s=this.options.smoothCurves.type,o=Math.abs(this.from.x-this.to.x),n=Math.abs(this.from.y-this.to.y);return"discrete"==s||"diagonalCross"==s?Math.abs(this.from.x-this.to.x)this.to.y?this.from.xthis.to.x&&(t=this.from.x-i*n,e=this.from.y-i*n):this.from.ythis.to.x&&(t=this.from.x-i*n,e=this.from.y+i*n)),"discrete"==s&&(t=i*n>o?this.from.x:t)):Math.abs(this.from.x-this.to.x)>Math.abs(this.from.y-this.to.y)&&(this.from.y>this.to.y?this.from.xthis.to.x&&(t=this.from.x-i*o,e=this.from.y-i*o):this.from.ythis.to.x&&(t=this.from.x-i*o,e=this.from.y+i*o)),"discrete"==s&&(e=i*o>n?this.from.y:e)):"straightCross"==s?Math.abs(this.from.x-this.to.x)Math.abs(this.from.y-this.to.y)&&(t=this.from.xthis.to.y?this.from.xthis.to.x&&(t=this.from.x-i*n,e=this.from.y-i*n,t=this.to.x>t?this.to.x:t):this.from.ythis.to.x&&(t=this.from.x-i*n,e=this.from.y+i*n,t=this.to.x>t?this.to.x:t)):Math.abs(this.from.x-this.to.x)>Math.abs(this.from.y-this.to.y)&&(this.from.y>this.to.y?this.from.xe?this.to.y:e):this.from.x>this.to.x&&(t=this.from.x-i*o,e=this.from.y-i*o,e=this.to.y>e?this.to.y:e):this.from.ythis.to.x&&(t=this.from.x-i*o,e=this.from.y+i*o,e=this.to.yd;d++){var l=t.measureText(n[d]).width;h=l>h?l:h}var c=this.options.fontSize*r,p=i-h/2,u=s-c/2;this.labelDimensions={top:u,left:p,width:h,height:c,yLine:o}}var o=this.labelDimensions.yLine;t.save(),"horizontal"!=this.options.labelAlignment&&(t.translate(i,o),this._rotateForLabelAlignment(t),i=0,o=0),this._drawLabelRect(t),this._drawLabelText(t,i,o,n,r,a),t.restore()}},s.prototype._rotateForLabelAlignment=function(t){var e=this.from.y-this.to.y,i=this.from.x-this.to.x,s=Math.atan2(e,i);(-1>s&&0>i||s>0&&0>i)&&(s+=Math.PI),t.rotate(s)},s.prototype._drawLabelRect=function(t){if(void 0!==this.options.fontFill&&null!==this.options.fontFill&&"none"!==this.options.fontFill){t.fillStyle=this.options.fontFill;var e=2;"line-center"==this.options.labelAlignment?t.fillRect(.5*-this.labelDimensions.width,.5*-this.labelDimensions.height,this.labelDimensions.width,this.labelDimensions.height):"line-above"==this.options.labelAlignment?t.fillRect(.5*-this.labelDimensions.width,-(this.labelDimensions.height+e),this.labelDimensions.width,this.labelDimensions.height):"line-below"==this.options.labelAlignment?t.fillRect(.5*-this.labelDimensions.width,e,this.labelDimensions.width,this.labelDimensions.height):t.fillRect(this.labelDimensions.left,this.labelDimensions.top,this.labelDimensions.width,this.labelDimensions.height)}},s.prototype._drawLabelText=function(t,e,i,s,o,n){if(t.fillStyle=this.options.fontColor||"black",t.textAlign="center","horizontal"!=this.options.labelAlignment){var r=2;"line-above"==this.options.labelAlignment?(t.textBaseline="alphabetic",i-=2*r):"line-below"==this.options.labelAlignment?(t.textBaseline="hanging",i+=2*r):t.textBaseline="middle"}else t.textBaseline="middle";this.options.fontStrokeWidth>0&&(t.lineWidth=this.options.fontStrokeWidth,t.strokeStyle=this.options.fontStrokeColor,t.lineJoin="round");for(var a=0;o>a;a++)this.options.fontStrokeWidth>0&&t.strokeText(s[a],e,i),t.fillText(s[a],e,i),i+=n},s.prototype._drawDashLine=function(t){t.strokeStyle=this._getColor(),t.lineWidth=this._getLineWidth();var e=null;if(void 0!==t.setLineDash){t.save();var i=[0];i=void 0!==this.options.dash.length&&void 0!==this.options.dash.gap?[this.options.dash.length,this.options.dash.gap]:[5,5],t.setLineDash(i),t.lineDashOffset=0,e=this._line(t),t.setLineDash([0]),t.lineDashOffset=0,t.restore()}else t.beginPath(),t.lineCap="round",void 0!==this.options.dash.altLength?t.dashedLine(this.from.x,this.from.y,this.to.x,this.to.y,[this.options.dash.length,this.options.dash.gap,this.options.dash.altLength,this.options.dash.gap]):void 0!==this.options.dash.length&&void 0!==this.options.dash.gap?t.dashedLine(this.from.x,this.from.y,this.to.x,this.to.y,[this.options.dash.length,this.options.dash.gap]):(t.moveTo(this.from.x,this.from.y),t.lineTo(this.to.x,this.to.y)),t.stroke();if(this.label){var s;if(1==this.options.smoothCurves.enabled&&null!=e){var o=.5*(.5*(this.from.x+e.x)+.5*(this.to.x+e.x)),n=.5*(.5*(this.from.y+e.y)+.5*(this.to.y+e.y));s={x:o,y:n}}else s=this._pointOnLine(.5);this._label(t,this.label,s.x,s.y)}},s.prototype._pointOnLine=function(t){return{x:(1-t)*this.from.x+t*this.to.x,y:(1-t)*this.from.y+t*this.to.y}},s.prototype._pointOnCircle=function(t,e,i,s){var o=2*(s-3/8)*Math.PI;return{x:t+i*Math.cos(o),y:e-i*Math.sin(o)}},s.prototype._drawArrowCenter=function(t){var e;if(t.strokeStyle=this._getColor(),t.fillStyle=t.strokeStyle,t.lineWidth=this._getLineWidth(),this.from!=this.to){var i=this._line(t),s=Math.atan2(this.to.y-this.from.y,this.to.x-this.from.x),o=(10+5*this.options.width)*this.options.arrowScaleFactor;if(1==this.options.smoothCurves.enabled&&null!=i){var n=.5*(.5*(this.from.x+i.x)+.5*(this.to.x+i.x)),r=.5*(.5*(this.from.y+i.y)+.5*(this.to.y+i.y));e={x:n,y:r}}else e=this._pointOnLine(.5);t.arrow(e.x,e.y,s,o),t.fill(),t.stroke(),this.label&&this._label(t,this.label,e.x,e.y)}else{var a,h,d=.25*Math.max(100,this.physics.springLength),l=this.from;l.width||l.resize(t),l.width>l.height?(a=l.x+.5*l.width,h=l.y-d):(a=l.x+d,h=l.y-.5*l.height),this._circle(t,a,h,d);var s=.2*Math.PI,o=(10+5*this.options.width)*this.options.arrowScaleFactor;e=this._pointOnCircle(a,h,d,.5),t.arrow(e.x,e.y,s,o),t.fill(),t.stroke(),this.label&&(e=this._pointOnCircle(a,h,d,.5),this._label(t,this.label,e.x,e.y))}},s.prototype._pointOnBezier=function(t){var e=this._getViaCoordinates(),i=Math.pow(1-t,2)*this.from.x+2*t*(1-t)*e.x+Math.pow(t,2)*this.to.x,s=Math.pow(1-t,2)*this.from.y+2*t*(1-t)*e.y+Math.pow(t,2)*this.to.y;return{x:i,y:s}},s.prototype._findBorderPosition=function(t,e){var i,s,o,n,r,a=10,h=0,d=0,l=1,c=.2,p=this.to;for(1==t&&(p=this.from);l>=d&&a>h;){var u=.5*(d+l);if(i=this._pointOnBezier(u),s=Math.atan2(p.y-i.y,p.x-i.x),o=p.distanceToBorder(e,s),n=Math.sqrt(Math.pow(i.x-p.x,2)+Math.pow(i.y-p.y,2)),r=o-n,Math.abs(r)r?0==t?d=u:l=u:0==t?l=u:d=u,h++}return i.t=u,i},s.prototype._drawArrow=function(t){t.strokeStyle=this._getColor(),t.fillStyle=t.strokeStyle,t.lineWidth=this._getLineWidth();var e,i,s;if(this.from!=this.to){if(this._line(t),1==this.options.smoothCurves.enabled){var o=this._getViaCoordinates();s=this._findBorderPosition(!1,t);var n=this._pointOnBezier(Math.max(0,s.t-.1));e=Math.atan2(s.y-n.y,s.x-n.x)}else{e=Math.atan2(this.to.y-this.from.y,this.to.x-this.from.x);var r=this.to.x-this.from.x,a=this.to.y-this.from.y,h=Math.sqrt(r*r+a*a),d=this.to.distanceToBorder(t,e),l=(h-d)/h;s={},s.x=(1-l)*this.from.x+l*this.to.x,s.y=(1-l)*this.from.y+l*this.to.y}if(i=(10+5*this.options.width)*this.options.arrowScaleFactor,t.arrow(s.x,s.y,e,i),t.fill(),t.stroke(),this.label){var c;c=1==this.options.smoothCurves.enabled&&null!=o?this._pointOnBezier(.5):this._pointOnLine(.5),this._label(t,this.label,c.x,c.y)}}else{var p,u,m,f=this.from,g=.25*Math.max(100,this.physics.springLength);f.width||f.resize(t),f.width>f.height?(p=f.x+.5*f.width,u=f.y-g,m={x:p,y:f.y,angle:.9*Math.PI}):(p=f.x+g,u=f.y-.5*f.height,m={x:f.x,y:u,angle:.6*Math.PI}),t.beginPath(),t.arc(p,u,g,0,2*Math.PI,!1),t.stroke();var i=(10+5*this.options.width)*this.options.arrowScaleFactor;t.arrow(m.x,m.y,m.angle,i),t.fill(),t.stroke(),this.label&&(c=this._pointOnCircle(p,u,g,.5),this._label(t,this.label,c.x,c.y))}},s.prototype._getDistanceToEdge=function(t,e,i,s,o,n){var r=0;if(this.from!=this.to)if(1==this.options.smoothCurves.enabled){var a,h;if(1==this.options.smoothCurves.enabled&&1==this.options.smoothCurves.dynamic)a=this.via.x,h=this.via.y;else{var d=this._getViaCoordinates();a=d.x,h=d.y}var l,c,p,u,m,f,g,v=1e9;for(c=0;10>c;c++)p=.1*c,u=Math.pow(1-p,2)*t+2*p*(1-p)*a+Math.pow(p,2)*i,m=Math.pow(1-p,2)*e+2*p*(1-p)*h+Math.pow(p,2)*s,c>0&&(l=this._getDistanceToLine(f,g,u,m,o,n),v=v>l?l:v),f=u,g=m;r=v}else r=this._getDistanceToLine(t,e,i,s,o,n);else{var u,m,y,b,_=.25*this.physics.springLength,x=this.from;x.width>x.height?(u=x.x+.5*x.width,m=x.y-_):(u=x.x+_,m=x.y-.5*x.height),y=u-o,b=m-n,r=Math.abs(Math.sqrt(y*y+b*b)-_)}return this.labelDimensions.lefto&&this.labelDimensions.topn?0:r},s.prototype._getDistanceToLine=function(t,e,i,s,o,n){var r=i-t,a=s-e,h=r*r+a*a,d=((o-t)*r+(n-e)*a)/h;d>1?d=1:0>d&&(d=0);var l=t+d*r,c=e+d*a,p=l-o,u=c-n;return Math.sqrt(p*p+u*u)},s.prototype.setScale=function(t){this.networkScaleInv=1/t},s.prototype.select=function(){this.selected=!0},s.prototype.unselect=function(){this.selected=!1},s.prototype.positionBezierNode=function(){null!==this.via&&null!==this.from&&null!==this.to?(this.via.x=.5*(this.from.x+this.to.x),this.via.y=.5*(this.from.y+this.to.y)):null!==this.via&&(this.via.x=0,this.via.y=0)},s.prototype._drawControlNodes=function(t){if(1==this.controlNodesEnabled){if(null===this.controlNodes.from&&null===this.controlNodes.to){var e="edgeIdFrom:".concat(this.id),i="edgeIdTo:".concat(this.id),s={nodes:{group:"",radius:7,borderWidth:2,borderWidthSelected:2},physics:{damping:0},clustering:{maxNodeSizeIncrements:0,nodeScaling:{width:0,height:0,radius:0}}};this.controlNodes.from=new n({id:e,shape:"dot",color:{background:"#ff0000",border:"#3c3c3c",highlight:{background:"#07f968"}}},{},{},s),this.controlNodes.to=new n({id:i,shape:"dot",color:{background:"#ff0000",border:"#3c3c3c",highlight:{background:"#07f968"}}},{},{},s)}this.controlNodes.positions={},0==this.controlNodes.from.selected&&(this.controlNodes.positions.from=this.getControlNodeFromPosition(t),this.controlNodes.from.x=this.controlNodes.positions.from.x,this.controlNodes.from.y=this.controlNodes.positions.from.y),0==this.controlNodes.to.selected&&(this.controlNodes.positions.to=this.getControlNodeToPosition(t),this.controlNodes.to.x=this.controlNodes.positions.to.x,this.controlNodes.to.y=this.controlNodes.positions.to.y),this.controlNodes.from.draw(t),this.controlNodes.to.draw(t)}else this.controlNodes={from:null,to:null,positions:{}}},s.prototype._enableControlNodes=function(){this.fromBackup=this.from,this.toBackup=this.to,this.controlNodesEnabled=!0},s.prototype._disableControlNodes=function(){this.fromId=this.from.id,this.toId=this.to.id,this.fromId!=this.fromBackup.id?this.fromBackup.detachEdge(this):this.toId!=this.toBackup.id&&this.toBackup.detachEdge(this),this.fromBackup=null,this.toBackup=null,this.controlNodesEnabled=!1},s.prototype._getSelectedControlNode=function(t,e){var i=this.controlNodes.positions,s=Math.sqrt(Math.pow(t-i.from.x,2)+Math.pow(e-i.from.y,2)),o=Math.sqrt(Math.pow(t-i.to.x,2)+Math.pow(e-i.to.y,2));return 15>s?(this.connectedNode=this.from,this.from=this.controlNodes.from,this.controlNodes.from):15>o?(this.connectedNode=this.to,this.to=this.controlNodes.to,this.controlNodes.to):null},s.prototype._restoreControlNodes=function(){1==this.controlNodes.from.selected?(this.from=this.connectedNode,this.connectedNode=null,this.controlNodes.from.unselect()):1==this.controlNodes.to.selected&&(this.to=this.connectedNode,this.connectedNode=null,this.controlNodes.to.unselect())},s.prototype.getControlNodeFromPosition=function(t){var e;if(1==this.options.smoothCurves.enabled)e=this._findBorderPosition(!0,t);else{var i=Math.atan2(this.to.y-this.from.y,this.to.x-this.from.x),s=this.to.x-this.from.x,o=this.to.y-this.from.y,n=Math.sqrt(s*s+o*o),r=this.from.distanceToBorder(t,i+Math.PI),a=(n-r)/n;e={},e.x=a*this.from.x+(1-a)*this.to.x,e.y=a*this.from.y+(1-a)*this.to.y}return e},s.prototype.getControlNodeToPosition=function(t){var e;if(1==this.options.smoothCurves.enabled)e=this._findBorderPosition(!1,t);else{var i=Math.atan2(this.to.y-this.from.y,this.to.x-this.from.x),s=this.to.x-this.from.x,o=this.to.y-this.from.y,n=Math.sqrt(s*s+o*o),r=this.to.distanceToBorder(t,i),a=(n-r)/n;e={},e.x=(1-a)*this.from.x+a*this.to.x,e.y=(1-a)*this.from.y+a*this.to.y}return e},t.exports=s},function(t,e,i){function s(){this.clear(),this.defaultIndex=0}i(1);s.DEFAULT=[{border:"#2B7CE9",background:"#97C2FC",highlight:{border:"#2B7CE9",background:"#D2E5FF"},hover:{border:"#2B7CE9",background:"#D2E5FF"}},{border:"#FFA500",background:"#FFFF00",highlight:{border:"#FFA500",background:"#FFFFA3"},hover:{border:"#FFA500",background:"#FFFFA3"}},{border:"#FA0A10",background:"#FB7E81",highlight:{border:"#FA0A10",background:"#FFAFB1"},hover:{border:"#FA0A10",background:"#FFAFB1"}},{border:"#41A906",background:"#7BE141",highlight:{border:"#41A906",background:"#A1EC76"},hover:{border:"#41A906",background:"#A1EC76"}},{border:"#E129F0",background:"#EB7DF4",highlight:{border:"#E129F0",background:"#F0B3F5"},hover:{border:"#E129F0",background:"#F0B3F5"}},{border:"#7C29F0",background:"#AD85E4",highlight:{border:"#7C29F0",background:"#D3BDF0"},hover:{border:"#7C29F0",background:"#D3BDF0"}},{border:"#C37F00",background:"#FFA807",highlight:{border:"#C37F00",background:"#FFCA66"},hover:{border:"#C37F00",background:"#FFCA66"}},{border:"#4220FB",background:"#6E6EFD",highlight:{border:"#4220FB",background:"#9B9BFD"},hover:{border:"#4220FB",background:"#9B9BFD"}},{border:"#FD5A77",background:"#FFC0CB",highlight:{border:"#FD5A77",background:"#FFD1D9"},hover:{border:"#FD5A77",background:"#FFD1D9"}},{border:"#4AD63A",background:"#C2FABC",highlight:{border:"#4AD63A",background:"#E6FFE3"},hover:{border:"#4AD63A",background:"#E6FFE3"}}],s.prototype.clear=function(){this.groups={},this.groups.length=function(){var t=0;for(var e in this)this.hasOwnProperty(e)&&t++;return t}},s.prototype.get=function(t){var e=this.groups[t];if(void 0==e){var i=this.defaultIndex%s.DEFAULT.length;this.defaultIndex++,e={},e.color=s.DEFAULT[i],this.groups[t]=e}return e},s.prototype.add=function(t,e){return this.groups[t]=e,e},t.exports=s},function(t){function e(){this.images={},this.imageBroken={},this.callback=void 0}e.prototype.setOnloadCallback=function(t){this.callback=t},e.prototype.load=function(t,e){var i=this.images[t];if(void 0===i){var s=this;i=new Image,i.onload=function(){0==this.width&&(document.body.appendChild(this),this.width=this.offsetWidth,this.height=this.offsetHeight,document.body.removeChild(this)),s.callback&&(s.images[t]=i,s.callback(this))},i.onerror=function(){void 0===e?(console.error("Could not load image:",t),delete this.src,s.callback&&s.callback(this)):s.imageBroken[t]===!0?this.src==e?(console.error("Could not load brokenImage:",e),delete this.src,s.callback&&s.callback(this)):(console.error("Could not load image:",t),this.src=e):(console.error("Could not load image:",t),this.src=e,s.imageBroken[t]=!0)},i.src=t}return i},t.exports=e},function(t,e,i){function s(t,e,i,s){var n=o.selectiveBridgeObject(["nodes"],s);this.options=n.nodes,this.selected=!1,this.hover=!1,this.edges=[],this.dynamicEdges=[],this.reroutedEdges={},this.fontDrawThreshold=3,this.id=void 0,this.allowedToMoveX=!1,this.allowedToMoveY=!1,this.xFixed=!1,this.yFixed=!1,this.horizontalAlignLeft=!0,this.verticalAlignTop=!0,this.baseRadiusValue=s.nodes.radius,this.radiusFixed=!1,this.level=-1,this.preassignedLevel=!1,this.hierarchyEnumerated=!1,this.labelDimensions={top:0,left:0,width:0,height:0,yLine:0},this.boundingBox={top:0,left:0,right:0,bottom:0},this.imagelist=e,this.grouplist=i,this.fx=0,this.fy=0,this.vx=0,this.vy=0,this.x=null,this.y=null,this.previousState={vx:0,vy:0,x:0,y:0},this.damping=s.physics.damping,this.fixedData={x:null,y:null},this.setProperties(t,n),this.resetCluster(),this.dynamicEdgesLength=0,this.clusterSession=0,this.clusterSizeWidthFactor=s.clustering.nodeScaling.width,this.clusterSizeHeightFactor=s.clustering.nodeScaling.height,this.clusterSizeRadiusFactor=s.clustering.nodeScaling.radius,this.maxNodeSizeIncrements=s.clustering.maxNodeSizeIncrements,this.growthIndicator=0,this.networkScaleInv=1,this.networkScale=1,this.canvasTopLeft={x:-300,y:-300},this.canvasBottomRight={x:300,y:300},this.parentEdgeId=null}var o=i(1);s.prototype.revertPosition=function(){this.x=this.previousState.x,this.y=this.previousState.y,this.vx=this.previousState.vx,this.vy=this.previousState.vy},s.prototype.resetCluster=function(){this.formationScale=void 0,this.clusterSize=1,this.containedNodes={},this.containedEdges={},this.clusterSessions=[]},s.prototype.attachEdge=function(t){-1==this.edges.indexOf(t)&&this.edges.push(t),-1==this.dynamicEdges.indexOf(t)&&this.dynamicEdges.push(t),this.dynamicEdgesLength=this.dynamicEdges.length},s.prototype.detachEdge=function(t){var e=this.edges.indexOf(t);-1!=e&&this.edges.splice(e,1),e=this.dynamicEdges.indexOf(t),-1!=e&&this.dynamicEdges.splice(e,1),this.dynamicEdgesLength=this.dynamicEdges.length},s.prototype.setProperties=function(t,e){if(t){var i=["borderWidth","borderWidthSelected","shape","image","brokenImage","radius","fontColor","fontSize","fontFace","fontFill","fontStrokeWidth","fontStrokeColor","group","mass"];if(o.selectiveDeepExtend(i,this.options,t),void 0!==t.id&&(this.id=t.id),void 0!==t.label&&(this.label=t.label,this.originalLabel=t.label),void 0!==t.title&&(this.title=t.title),void 0!==t.x&&(this.x=t.x),void 0!==t.y&&(this.y=t.y),void 0!==t.value&&(this.value=t.value),void 0!==t.level&&(this.level=t.level,this.preassignedLevel=!0),void 0!==t.horizontalAlignLeft&&(this.horizontalAlignLeft=t.horizontalAlignLeft),void 0!==t.verticalAlignTop&&(this.verticalAlignTop=t.verticalAlignTop),void 0!==t.triggerFunction&&(this.triggerFunction=t.triggerFunction),void 0===this.id)throw"Node must have an id";if("number"==typeof t.group||"string"==typeof t.group&&""!=t.group){var s=this.grouplist.get(t.group);o.deepExtend(this.options,s),this.options.color=o.parseColor(this.options.color)}if(void 0!==t.radius&&(this.baseRadiusValue=this.options.radius),void 0!==t.color&&(this.options.color=o.parseColor(t.color)),void 0!==this.options.image&&""!=this.options.image){if(!this.imagelist)throw"No imagelist provided";this.imageObj=this.imagelist.load(this.options.image,this.options.brokenImage)}switch(void 0!==t.allowedToMoveX?(this.xFixed=!t.allowedToMoveX,this.allowedToMoveX=t.allowedToMoveX):void 0!==t.x&&0==this.allowedToMoveX&&(this.xFixed=!0),void 0!==t.allowedToMoveY?(this.yFixed=!t.allowedToMoveY,this.allowedToMoveY=t.allowedToMoveY):void 0!==t.y&&0==this.allowedToMoveY&&(this.yFixed=!0),this.radiusFixed=this.radiusFixed||void 0!==t.radius,("image"===this.options.shape||"circularImage"===this.options.shape)&&(this.options.radiusMin=e.nodes.widthMin,this.options.radiusMax=e.nodes.widthMax),this.options.shape){case"database":this.draw=this._drawDatabase,this.resize=this._resizeDatabase;break;case"box":this.draw=this._drawBox,this.resize=this._resizeBox;break;case"circle":this.draw=this._drawCircle,this.resize=this._resizeCircle;break;case"ellipse":this.draw=this._drawEllipse,this.resize=this._resizeEllipse;break;case"image":this.draw=this._drawImage,this.resize=this._resizeImage;break;case"circularImage":this.draw=this._drawCircularImage,this.resize=this._resizeCircularImage;break;case"text":this.draw=this._drawText,this.resize=this._resizeText;break;case"dot":this.draw=this._drawDot,this.resize=this._resizeShape;break;case"square":this.draw=this._drawSquare,this.resize=this._resizeShape;break;case"triangle":this.draw=this._drawTriangle,this.resize=this._resizeShape;break;case"triangleDown":this.draw=this._drawTriangleDown,this.resize=this._resizeShape;break;case"star":this.draw=this._drawStar,this.resize=this._resizeShape;break;default:this.draw=this._drawEllipse,this.resize=this._resizeEllipse}this._reset()}},s.prototype.select=function(){this.selected=!0,this._reset()},s.prototype.unselect=function(){this.selected=!1,this._reset()},s.prototype.clearSizeCache=function(){this._reset()},s.prototype._reset=function(){this.width=void 0,this.height=void 0},s.prototype.getTitle=function(){return"function"==typeof this.title?this.title():this.title},s.prototype.distanceToBorder=function(t,e){var i=1;switch(this.width||this.resize(t),this.options.shape){case"circle":case"dot":return this.options.radius+i;case"ellipse":var s=this.width/2,o=this.height/2,n=Math.sin(e)*s,r=Math.cos(e)*o;return s*o/Math.sqrt(n*n+r*r);case"box":case"image":case"text":default:return this.width?Math.min(Math.abs(this.width/2/Math.cos(e)),Math.abs(this.height/2/Math.sin(e)))+i:0}},s.prototype._setForce=function(t,e){this.fx=t,this.fy=e},s.prototype._addForce=function(t,e){this.fx+=t,this.fy+=e},s.prototype.storeState=function(){this.previousState.x=this.x,this.previousState.y=this.y,this.previousState.vx=this.vx,this.previousState.vy=this.vy},s.prototype.discreteStep=function(t){if(this.storeState(),this.xFixed)this.fx=0,this.vx=0;else{var e=this.damping*this.vx,i=(this.fx-e)/this.options.mass;this.vx+=i*t,this.x+=this.vx*t}if(this.yFixed)this.fy=0,this.vy=0;else{var s=this.damping*this.vy,o=(this.fy-s)/this.options.mass;this.vy+=o*t,this.y+=this.vy*t}},s.prototype.discreteStepLimited=function(t,e){if(this.storeState(),this.xFixed)this.fx=0,this.vx=0;else{var i=this.damping*this.vx,s=(this.fx-i)/this.options.mass;this.vx+=s*t,this.vx=Math.abs(this.vx)>e?this.vx>0?e:-e:this.vx,this.x+=this.vx*t}if(this.yFixed)this.fy=0,this.vy=0;else{var o=this.damping*this.vy,n=(this.fy-o)/this.options.mass;this.vy+=n*t,this.vy=Math.abs(this.vy)>e?this.vy>0?e:-e:this.vy,this.y+=this.vy*t}},s.prototype.isFixed=function(){return this.xFixed&&this.yFixed},s.prototype.isMoving=function(t){var e=Math.sqrt(Math.pow(this.vx,2)+Math.pow(this.vy,2));return e>t},s.prototype.isSelected=function(){return this.selected},s.prototype.getValue=function(){return this.value},s.prototype.getDistance=function(t,e){var i=this.x-t,s=this.y-e;return Math.sqrt(i*i+s*s)},s.prototype.setValueRange=function(t,e){if(!this.radiusFixed&&void 0!==this.value)if(e==t)this.options.radius=(this.options.radiusMin+this.options.radiusMax)/2;else{var i=(this.options.radiusMax-this.options.radiusMin)/(e-t);this.options.radius=(this.value-t)*i+this.options.radiusMin}this.baseRadiusValue=this.options.radius},s.prototype.draw=function(){throw"Draw method not initialized for node"},s.prototype.resize=function(){throw"Resize method not initialized for node"},s.prototype.isOverlappingWith=function(t){return this.leftt.left&&this.topt.top},s.prototype._resizeImage=function(){if(!this.width||!this.height){var t,e;if(this.value){this.options.radius=this.baseRadiusValue;var i=this.imageObj.height/this.imageObj.width;void 0!==i?(t=this.options.radius||this.imageObj.width,e=this.options.radius*i||this.imageObj.height):(t=0,e=0)}else t=this.imageObj.width,e=this.imageObj.height;this.width=t,this.height=e,this.growthIndicator=0,this.width>0&&this.height>0&&(this.width+=Math.min(this.clusterSize-1,this.maxNodeSizeIncrements)*this.clusterSizeWidthFactor,this.height+=Math.min(this.clusterSize-1,this.maxNodeSizeIncrements)*this.clusterSizeHeightFactor,this.options.radius+=Math.min(this.clusterSize-1,this.maxNodeSizeIncrements)*this.clusterSizeRadiusFactor,this.growthIndicator=this.width-t)}},s.prototype._drawImageAtPosition=function(t){if(0!=this.imageObj.width){if(this.clusterSize>1){var e=this.clusterSize>1?10:0;e*=this.networkScaleInv,e=Math.min(.2*this.width,e),t.globalAlpha=.5,t.drawImage(this.imageObj,this.left-e,this.top-e,this.width+2*e,this.height+2*e)}t.globalAlpha=1,t.drawImage(this.imageObj,this.left,this.top,this.width,this.height)}},s.prototype._drawImageLabel=function(t){var e,i=0;if(this.height){i=this.height/2;var s=this.getTextSize(t);s.lineCount>=1&&(i+=s.height/2,i+=3)}e=this.y+i,this._label(t,this.label,this.x,e,void 0)},s.prototype._drawImage=function(t){this._resizeImage(t),this.left=this.x-this.width/2,this.top=this.y-this.height/2,this._drawImageAtPosition(t),this.boundingBox.top=this.top,this.boundingBox.left=this.left,this.boundingBox.right=this.left+this.width,this.boundingBox.bottom=this.top+this.height,this._drawImageLabel(t),this.boundingBox.left=Math.min(this.boundingBox.left,this.labelDimensions.left),this.boundingBox.right=Math.max(this.boundingBox.right,this.labelDimensions.left+this.labelDimensions.width),this.boundingBox.bottom=Math.max(this.boundingBox.bottom,this.boundingBox.bottom+this.labelDimensions.height)},s.prototype._resizeCircularImage=function(t){if(this.imageObj.src&&this.imageObj.width&&this.imageObj.height)this._swapToImageResizeWhenImageLoaded&&(this.width=0,this.height=0,delete this._swapToImageResizeWhenImageLoaded),this._resizeImage(t);else if(!this.width){var e=2*this.options.radius;this.width=e,this.height=e,this.options.radius+=.5*Math.min(this.clusterSize-1,this.maxNodeSizeIncrements)*this.clusterSizeRadiusFactor,this.growthIndicator=this.options.radius-.5*e,this._swapToImageResizeWhenImageLoaded=!0}},s.prototype._drawCircularImage=function(t){this._resizeCircularImage(t),this.left=this.x-this.width/2,this.top=this.y-this.height/2;var e=this.left+this.width/2,i=this.top+this.height/2,s=Math.abs(this.height/2);this._drawRawCircle(t,e,i,s),t.save(),t.circle(this.x,this.y,s),t.stroke(),t.clip(),this._drawImageAtPosition(t),t.restore(),this.boundingBox.top=this.y-this.options.radius,this.boundingBox.left=this.x-this.options.radius,this.boundingBox.right=this.x+this.options.radius,this.boundingBox.bottom=this.y+this.options.radius,this._drawImageLabel(t),this.boundingBox.left=Math.min(this.boundingBox.left,this.labelDimensions.left),this.boundingBox.right=Math.max(this.boundingBox.right,this.labelDimensions.left+this.labelDimensions.width),this.boundingBox.bottom=Math.max(this.boundingBox.bottom,this.boundingBox.bottom+this.labelDimensions.height)},s.prototype._resizeBox=function(t){if(!this.width){var e=5,i=this.getTextSize(t);this.width=i.width+2*e,this.height=i.height+2*e,this.width+=.5*Math.min(this.clusterSize-1,this.maxNodeSizeIncrements)*this.clusterSizeWidthFactor,this.height+=.5*Math.min(this.clusterSize-1,this.maxNodeSizeIncrements)*this.clusterSizeHeightFactor,this.growthIndicator=this.width-(i.width+2*e)}},s.prototype._drawBox=function(t){this._resizeBox(t),this.left=this.x-this.width/2,this.top=this.y-this.height/2;var e=2.5,i=this.options.borderWidth,s=this.options.borderWidthSelected||2*this.options.borderWidth;t.strokeStyle=this.selected?this.options.color.highlight.border:this.hover?this.options.color.hover.border:this.options.color.border,this.clusterSize>1&&(t.lineWidth=(this.selected?s:i)+(this.clusterSize>1?e:0),t.lineWidth*=this.networkScaleInv,t.lineWidth=Math.min(this.width,t.lineWidth),t.roundRect(this.left-2*t.lineWidth,this.top-2*t.lineWidth,this.width+4*t.lineWidth,this.height+4*t.lineWidth,this.options.radius),t.stroke()),t.lineWidth=(this.selected?s:i)+(this.clusterSize>1?e:0),t.lineWidth*=this.networkScaleInv,t.lineWidth=Math.min(this.width,t.lineWidth),t.fillStyle=this.selected?this.options.color.highlight.background:this.hover?this.options.color.hover.background:this.options.color.background,t.roundRect(this.left,this.top,this.width,this.height,this.options.radius),t.fill(),t.stroke(),this.boundingBox.top=this.top,this.boundingBox.left=this.left,this.boundingBox.right=this.left+this.width,this.boundingBox.bottom=this.top+this.height,this._label(t,this.label,this.x,this.y) -},s.prototype._resizeDatabase=function(t){if(!this.width){var e=5,i=this.getTextSize(t),s=i.width+2*e;this.width=s,this.height=s,this.width+=Math.min(this.clusterSize-1,this.maxNodeSizeIncrements)*this.clusterSizeWidthFactor,this.height+=Math.min(this.clusterSize-1,this.maxNodeSizeIncrements)*this.clusterSizeHeightFactor,this.options.radius+=Math.min(this.clusterSize-1,this.maxNodeSizeIncrements)*this.clusterSizeRadiusFactor,this.growthIndicator=this.width-s}},s.prototype._drawDatabase=function(t){this._resizeDatabase(t),this.left=this.x-this.width/2,this.top=this.y-this.height/2;var e=2.5,i=this.options.borderWidth,s=this.options.borderWidthSelected||2*this.options.borderWidth;t.strokeStyle=this.selected?this.options.color.highlight.border:this.hover?this.options.color.hover.border:this.options.color.border,this.clusterSize>1&&(t.lineWidth=(this.selected?s:i)+(this.clusterSize>1?e:0),t.lineWidth*=this.networkScaleInv,t.lineWidth=Math.min(this.width,t.lineWidth),t.database(this.x-this.width/2-2*t.lineWidth,this.y-.5*this.height-2*t.lineWidth,this.width+4*t.lineWidth,this.height+4*t.lineWidth),t.stroke()),t.lineWidth=(this.selected?s:i)+(this.clusterSize>1?e:0),t.lineWidth*=this.networkScaleInv,t.lineWidth=Math.min(this.width,t.lineWidth),t.fillStyle=this.selected?this.options.color.highlight.background:this.hover?this.options.color.hover.background:this.options.color.background,t.database(this.x-this.width/2,this.y-.5*this.height,this.width,this.height),t.fill(),t.stroke(),this.boundingBox.top=this.top,this.boundingBox.left=this.left,this.boundingBox.right=this.left+this.width,this.boundingBox.bottom=this.top+this.height,this._label(t,this.label,this.x,this.y)},s.prototype._resizeCircle=function(t){if(!this.width){var e=5,i=this.getTextSize(t),s=Math.max(i.width,i.height)+2*e;this.options.radius=s/2,this.width=s,this.height=s,this.options.radius+=.5*Math.min(this.clusterSize-1,this.maxNodeSizeIncrements)*this.clusterSizeRadiusFactor,this.growthIndicator=this.options.radius-.5*s}},s.prototype._drawRawCircle=function(t,e,i,s){var o=2.5,n=this.options.borderWidth,r=this.options.borderWidthSelected||2*this.options.borderWidth;t.strokeStyle=this.selected?this.options.color.highlight.border:this.hover?this.options.color.hover.border:this.options.color.border,this.clusterSize>1&&(t.lineWidth=(this.selected?r:n)+(this.clusterSize>1?o:0),t.lineWidth*=this.networkScaleInv,t.lineWidth=Math.min(this.width,t.lineWidth),t.circle(e,i,s+2*t.lineWidth),t.stroke()),t.lineWidth=(this.selected?r:n)+(this.clusterSize>1?o:0),t.lineWidth*=this.networkScaleInv,t.lineWidth=Math.min(this.width,t.lineWidth),t.fillStyle=this.selected?this.options.color.highlight.background:this.hover?this.options.color.hover.background:this.options.color.background,t.circle(this.x,this.y,s),t.fill(),t.stroke()},s.prototype._drawCircle=function(t){this._resizeCircle(t),this.left=this.x-this.width/2,this.top=this.y-this.height/2,this._drawRawCircle(t,this.x,this.y,this.options.radius),this.boundingBox.top=this.y-this.options.radius,this.boundingBox.left=this.x-this.options.radius,this.boundingBox.right=this.x+this.options.radius,this.boundingBox.bottom=this.y+this.options.radius,this._label(t,this.label,this.x,this.y)},s.prototype._resizeEllipse=function(t){if(!this.width){var e=this.getTextSize(t);this.width=1.5*e.width,this.height=2*e.height,this.width1&&(t.lineWidth=(this.selected?s:i)+(this.clusterSize>1?e:0),t.lineWidth*=this.networkScaleInv,t.lineWidth=Math.min(this.width,t.lineWidth),t.ellipse(this.left-2*t.lineWidth,this.top-2*t.lineWidth,this.width+4*t.lineWidth,this.height+4*t.lineWidth),t.stroke()),t.lineWidth=(this.selected?s:i)+(this.clusterSize>1?e:0),t.lineWidth*=this.networkScaleInv,t.lineWidth=Math.min(this.width,t.lineWidth),t.fillStyle=this.selected?this.options.color.highlight.background:this.hover?this.options.color.hover.background:this.options.color.background,t.ellipse(this.left,this.top,this.width,this.height),t.fill(),t.stroke(),this.boundingBox.top=this.top,this.boundingBox.left=this.left,this.boundingBox.right=this.left+this.width,this.boundingBox.bottom=this.top+this.height,this._label(t,this.label,this.x,this.y)},s.prototype._drawDot=function(t){this._drawShape(t,"circle")},s.prototype._drawTriangle=function(t){this._drawShape(t,"triangle")},s.prototype._drawTriangleDown=function(t){this._drawShape(t,"triangleDown")},s.prototype._drawSquare=function(t){this._drawShape(t,"square")},s.prototype._drawStar=function(t){this._drawShape(t,"star")},s.prototype._resizeShape=function(){if(!this.width){this.options.radius=this.baseRadiusValue;var t=2*this.options.radius;this.width=t,this.height=t,this.width+=Math.min(this.clusterSize-1,this.maxNodeSizeIncrements)*this.clusterSizeWidthFactor,this.height+=Math.min(this.clusterSize-1,this.maxNodeSizeIncrements)*this.clusterSizeHeightFactor,this.options.radius+=.5*Math.min(this.clusterSize-1,this.maxNodeSizeIncrements)*this.clusterSizeRadiusFactor,this.growthIndicator=this.width-t}},s.prototype._drawShape=function(t,e){this._resizeShape(t),this.left=this.x-this.width/2,this.top=this.y-this.height/2;var i=2.5,s=this.options.borderWidth,o=this.options.borderWidthSelected||2*this.options.borderWidth,n=2;switch(e){case"dot":n=2;break;case"square":n=2;break;case"triangle":n=3;break;case"triangleDown":n=3;break;case"star":n=4}t.strokeStyle=this.selected?this.options.color.highlight.border:this.hover?this.options.color.hover.border:this.options.color.border,this.clusterSize>1&&(t.lineWidth=(this.selected?o:s)+(this.clusterSize>1?i:0),t.lineWidth*=this.networkScaleInv,t.lineWidth=Math.min(this.width,t.lineWidth),t[e](this.x,this.y,this.options.radius+n*t.lineWidth),t.stroke()),t.lineWidth=(this.selected?o:s)+(this.clusterSize>1?i:0),t.lineWidth*=this.networkScaleInv,t.lineWidth=Math.min(this.width,t.lineWidth),t.fillStyle=this.selected?this.options.color.highlight.background:this.hover?this.options.color.hover.background:this.options.color.background,t[e](this.x,this.y,this.options.radius),t.fill(),t.stroke(),this.boundingBox.top=this.y-this.options.radius,this.boundingBox.left=this.x-this.options.radius,this.boundingBox.right=this.x+this.options.radius,this.boundingBox.bottom=this.y+this.options.radius,this.label&&(this._label(t,this.label,this.x,this.y+this.height/2,void 0,"hanging",!0),this.boundingBox.left=Math.min(this.boundingBox.left,this.labelDimensions.left),this.boundingBox.right=Math.max(this.boundingBox.right,this.labelDimensions.left+this.labelDimensions.width),this.boundingBox.bottom=Math.max(this.boundingBox.bottom,this.boundingBox.bottom+this.labelDimensions.height))},s.prototype._resizeText=function(t){if(!this.width){var e=5,i=this.getTextSize(t);this.width=i.width+2*e,this.height=i.height+2*e,this.width+=Math.min(this.clusterSize-1,this.maxNodeSizeIncrements)*this.clusterSizeWidthFactor,this.height+=Math.min(this.clusterSize-1,this.maxNodeSizeIncrements)*this.clusterSizeHeightFactor,this.options.radius+=Math.min(this.clusterSize-1,this.maxNodeSizeIncrements)*this.clusterSizeRadiusFactor,this.growthIndicator=this.width-(i.width+2*e)}},s.prototype._drawText=function(t){this._resizeText(t),this.left=this.x-this.width/2,this.top=this.y-this.height/2,this._label(t,this.label,this.x,this.y),this.boundingBox.top=this.top,this.boundingBox.left=this.left,this.boundingBox.right=this.left+this.width,this.boundingBox.bottom=this.top+this.height},s.prototype._label=function(t,e,i,s,o,n,r){if(e&&Number(this.options.fontSize)*this.networkScale>this.fontDrawThreshold){t.font=(this.selected?"bold ":"")+this.options.fontSize+"px "+this.options.fontFace;var a=e.split("\n"),h=a.length,d=Number(this.options.fontSize),l=s+(1-h)/2*d;1==r&&(l=s+(1-h)/(2*d));for(var c=t.measureText(a[0]).width,p=1;h>p;p++){var u=t.measureText(a[p]).width;c=u>c?u:c}var m=this.options.fontSize*h,f=i-c/2,g=s-m/2;"hanging"==n&&(g+=.5*d,g+=4,l+=4),this.labelDimensions={top:g,left:f,width:c,height:m,yLine:l},void 0!==this.options.fontFill&&null!==this.options.fontFill&&"none"!==this.options.fontFill&&(t.fillStyle=this.options.fontFill,t.fillRect(f,g,c,m)),t.fillStyle=this.options.fontColor||"black",t.textAlign=o||"center",t.textBaseline=n||"middle",this.options.fontStrokeWidth>0&&(t.lineWidth=this.options.fontStrokeWidth,t.strokeStyle=this.options.fontStrokeColor,t.lineJoin="round");for(var p=0;h>p;p++)this.options.fontStrokeWidth&&t.strokeText(a[p],i,l),t.fillText(a[p],i,l),l+=d}},s.prototype.getTextSize=function(t){if(void 0!==this.label){t.font=(this.selected?"bold ":"")+this.options.fontSize+"px "+this.options.fontFace;for(var e=this.label.split("\n"),i=(Number(this.options.fontSize)+4)*e.length,s=0,o=0,n=e.length;n>o;o++)s=Math.max(s,t.measureText(e[o]).width);return{width:s,height:i,lineCount:e.length}}return{width:0,height:0,lineCount:0}},s.prototype.inArea=function(){return void 0!==this.width?this.x+this.width*this.networkScaleInv>=this.canvasTopLeft.x&&this.x-this.width*this.networkScaleInv=this.canvasTopLeft.y&&this.y-this.height*this.networkScaleInv=this.canvasTopLeft.x&&this.x=this.canvasTopLeft.y&&this.ys&&(n=s-e-this.padding),no&&(r=o-i-this.padding),ri;i++)if(e.id===r.nodes[i].id){o=r.nodes[i];break}for(o||(o={id:e.id},t.node&&(o.attr=a(o.attr,t.node))),i=n.length-1;i>=0;i--){var h=n[i];h.nodes||(h.nodes=[]),-1==h.nodes.indexOf(o)&&h.nodes.push(o)}e.attr&&(o.attr=a(o.attr,e.attr))}function l(t,e){if(t.edges||(t.edges=[]),t.edges.push(e),t.edge){var i=a({},t.edge);e.attr=a(i,e.attr)}}function c(t,e,i,s,o){var n={from:e,to:i,type:s};return t.edge&&(n.attr=a({},t.edge)),n.attr=a(n.attr||{},o),n}function p(){for(N=D.NULL,k="";" "==E||" "==E||"\n"==E||"\r"==E;)o();do{var t=!1;if("#"==E){for(var e=O-1;" "==T.charAt(e)||" "==T.charAt(e);)e--;if("\n"==T.charAt(e)||""==T.charAt(e)){for(;""!=E&&"\n"!=E;)o();t=!0}}if("/"==E&&"/"==n()){for(;""!=E&&"\n"!=E;)o();t=!0}if("/"==E&&"*"==n()){for(;""!=E;){if("*"==E&&"/"==n()){o(),o();break}o()}t=!0}for(;" "==E||" "==E||"\n"==E||"\r"==E;)o()}while(t);if(""==E)return void(N=D.DELIMITER);var i=E+n();if(C[i])return N=D.DELIMITER,k=i,o(),void o();if(C[E])return N=D.DELIMITER,k=E,void o();if(r(E)||"-"==E){for(k+=E,o();r(E);)k+=E,o();return"false"==k?k=!1:"true"==k?k=!0:isNaN(Number(k))||(k=Number(k)),void(N=D.IDENTIFIER)}if('"'==E){for(o();""!=E&&('"'!=E||'"'==E&&'"'==n());)k+=E,'"'==E&&o(),o();if('"'!=E)throw x('End of string " expected');return o(),void(N=D.IDENTIFIER)}for(N=D.UNKNOWN;""!=E;)k+=E,o();throw new SyntaxError('Syntax error in part "'+w(k,30)+'"')}function u(){var t={};if(s(),p(),"strict"==k&&(t.strict=!0,p()),("graph"==k||"digraph"==k)&&(t.type=k,p()),N==D.IDENTIFIER&&(t.id=k,p()),"{"!=k)throw x("Angle bracket { expected");if(p(),m(t),"}"!=k)throw x("Angle bracket } expected");if(p(),""!==k)throw x("End of file expected");return p(),delete t.node,delete t.edge,delete t.graph,t}function m(t){for(;""!==k&&"}"!=k;)f(t),";"==k&&p()}function f(t){var e=g(t);if(e)return void b(t,e);var i=v(t);if(!i){if(N!=D.IDENTIFIER)throw x("Identifier expected");var s=k;if(p(),"="==k){if(p(),N!=D.IDENTIFIER)throw x("Identifier expected");t[s]=k,p()}else y(t,s)}}function g(t){var e=null;if("subgraph"==k&&(e={},e.type="subgraph",p(),N==D.IDENTIFIER&&(e.id=k,p())),"{"==k){if(p(),e||(e={}),e.parent=t,e.node=t.node,e.edge=t.edge,e.graph=t.graph,m(e),"}"!=k)throw x("Angle bracket } expected");p(),delete e.node,delete e.edge,delete e.graph,delete e.parent,t.subgraphs||(t.subgraphs=[]),t.subgraphs.push(e)}return e}function v(t){return"node"==k?(p(),t.node=_(),"node"):"edge"==k?(p(),t.edge=_(),"edge"):"graph"==k?(p(),t.graph=_(),"graph"):null}function y(t,e){var i={id:e},s=_();s&&(i.attr=s),d(t,i),b(t,e)}function b(t,e){for(;"->"==k||"--"==k;){var i,s=k;p();var o=g(t);if(o)i=o;else{if(N!=D.IDENTIFIER)throw x("Identifier or subgraph expected");i=k,d(t,{id:i}),p()}var n=_(),r=c(t,e,i,s,n);l(t,r),e=i}}function _(){for(var t=null;"["==k;){for(p(),t={};""!==k&&"]"!=k;){if(N!=D.IDENTIFIER)throw x("Attribute name expected");var e=k;if(p(),"="!=k)throw x("Equal sign = expected");if(p(),N!=D.IDENTIFIER)throw x("Attribute value expected");var i=k;h(t,e,i),p(),","==k&&p()}if("]"!=k)throw x("Bracket ] expected");p()}return t}function x(t){return new SyntaxError(t+', got "'+w(k,30)+'" (char '+O+")")}function w(t,e){return t.length<=e?t:t.substr(0,27)+"..."}function M(t,e,i){Array.isArray(t)?t.forEach(function(t){Array.isArray(e)?e.forEach(function(e){i(t,e)}):i(t,e)}):Array.isArray(e)?e.forEach(function(e){i(t,e)}):i(t,e)}function S(t){var e=i(t),s={nodes:[],edges:[],options:{}};if(e.nodes&&e.nodes.forEach(function(t){var e={id:t.id,label:String(t.label||t.id)};a(e,t.attr),e.image&&(e.shape="image"),s.nodes.push(e)}),e.edges){var o=function(t){var e={from:t.from,to:t.to};return a(e,t.attr),e.style="->"==t.type?"arrow":"line",e};e.edges.forEach(function(t){var e,i;e=t.from instanceof Object?t.from.nodes:{id:t.from},i=t.to instanceof Object?t.to.nodes:{id:t.to},t.from instanceof Object&&t.from.edges&&t.from.edges.forEach(function(t){var e=o(t);s.edges.push(e)}),M(e,i,function(e,i){var n=c(s,e.id,i.id,t.type,t.attr),r=o(n);s.edges.push(r)}),t.to instanceof Object&&t.to.edges&&t.to.edges.forEach(function(t){var e=o(t);s.edges.push(e)})})}return e.attr&&(s.options=e.attr),s}var D={NULL:0,DELIMITER:1,IDENTIFIER:2,UNKNOWN:3},C={"{":!0,"}":!0,"[":!0,"]":!0,";":!0,"=":!0,",":!0,"->":!0,"--":!0},T="",O=0,E="",k="",N=D.NULL,L=/[a-zA-Z_0-9.:#]/;e.parseDOT=i,e.DOTToGraph=S},function(t,e){function i(t,e){var i=[],s=[];this.options={edges:{inheritColor:!0},nodes:{allowedToMove:!1,parseColor:!1}},void 0!==e&&(this.options.nodes.allowedToMove=e.allowedToMove|!1,this.options.nodes.parseColor=e.parseColor|!1,this.options.edges.inheritColor=e.inheritColor|!0);for(var o=t.edges,n=t.nodes,r=0;r=s&&(s=864e5),e=new Date(e.valueOf()-.05*s),i=new Date(i.valueOf()+.05*s)}return{start:e,end:i}},s.prototype.setWindow=function(t,e,i){var s=i&&void 0!==i.animate?i.animate:!0;if(1==arguments.length){var o=arguments[0];this.range.setRange(o.start,o.end,s)}else this.range.setRange(t,e,s)},s.prototype.moveTo=function(t,e){var i=this.range.end-this.range.start,s=r.convert(t,"Date").valueOf(),o=s-i/2,n=s+i/2,a=e&&void 0!==e.animate?e.animate:!0;this.range.setRange(o,n,a)},s.prototype.getWindow=function(){var t=this.range.getRange();return{start:new Date(t.start),end:new Date(t.end)}},s.prototype.redraw=function(){var t=!1,e=this.options,i=this.props,s=this.dom;if(s){h.updateHiddenDates(this.body,this.options.hiddenDates),"top"==e.orientation?(r.addClassName(s.root,"top"),r.removeClassName(s.root,"bottom")):(r.removeClassName(s.root,"top"),r.addClassName(s.root,"bottom")),s.root.style.maxHeight=r.option.asSize(e.maxHeight,""),s.root.style.minHeight=r.option.asSize(e.minHeight,""),s.root.style.width=r.option.asSize(e.width,""),i.border.left=(s.centerContainer.offsetWidth-s.centerContainer.clientWidth)/2,i.border.right=i.border.left,i.border.top=(s.centerContainer.offsetHeight-s.centerContainer.clientHeight)/2,i.border.bottom=i.border.top;var o=s.root.offsetHeight-s.root.clientHeight,n=s.root.offsetWidth-s.root.clientWidth;0===s.centerContainer.clientHeight&&(i.border.left=i.border.top,i.border.right=i.border.left),0===s.root.clientHeight&&(n=o),i.center.height=s.center.offsetHeight,i.left.height=s.left.offsetHeight,i.right.height=s.right.offsetHeight,i.top.height=s.top.clientHeight||-i.border.top,i.bottom.height=s.bottom.clientHeight||-i.border.bottom;var a=Math.max(i.left.height,i.center.height,i.right.height),d=i.top.height+a+i.bottom.height+o+i.border.top+i.border.bottom;s.root.style.height=r.option.asSize(e.height,d+"px"),i.root.height=s.root.offsetHeight,i.background.height=i.root.height-o;var l=i.root.height-i.top.height-i.bottom.height-o;i.centerContainer.height=l,i.leftContainer.height=l,i.rightContainer.height=i.leftContainer.height,i.root.width=s.root.offsetWidth,i.background.width=i.root.width-n,i.left.width=s.leftContainer.clientWidth||-i.border.left,i.leftContainer.width=i.left.width,i.right.width=s.rightContainer.clientWidth||-i.border.right,i.rightContainer.width=i.right.width;var c=i.root.width-i.left.width-i.right.width-n;i.center.width=c,i.centerContainer.width=c,i.top.width=c,i.bottom.width=c,s.background.style.height=i.background.height+"px",s.backgroundVertical.style.height=i.background.height+"px",s.backgroundHorizontal.style.height=i.centerContainer.height+"px",s.centerContainer.style.height=i.centerContainer.height+"px",s.leftContainer.style.height=i.leftContainer.height+"px",s.rightContainer.style.height=i.rightContainer.height+"px",s.background.style.width=i.background.width+"px",s.backgroundVertical.style.width=i.centerContainer.width+"px",s.backgroundHorizontal.style.width=i.background.width+"px",s.centerContainer.style.width=i.center.width+"px",s.top.style.width=i.top.width+"px",s.bottom.style.width=i.bottom.width+"px",s.background.style.left="0",s.background.style.top="0",s.backgroundVertical.style.left=i.left.width+i.border.left+"px",s.backgroundVertical.style.top="0",s.backgroundHorizontal.style.left="0",s.backgroundHorizontal.style.top=i.top.height+"px",s.centerContainer.style.left=i.left.width+"px",s.centerContainer.style.top=i.top.height+"px",s.leftContainer.style.left="0",s.leftContainer.style.top=i.top.height+"px",s.rightContainer.style.left=i.left.width+i.center.width+"px",s.rightContainer.style.top=i.top.height+"px",s.top.style.left=i.left.width+"px",s.top.style.top="0",s.bottom.style.left=i.left.width+"px",s.bottom.style.top=i.top.height+i.centerContainer.height+"px",this._updateScrollTop();var p=this.props.scrollTop;"bottom"==e.orientation&&(p+=Math.max(this.props.centerContainer.height-this.props.center.height-this.props.border.top-this.props.border.bottom,0)),s.center.style.left="0",s.center.style.top=p+"px",s.left.style.left="0",s.left.style.top=p+"px",s.right.style.left="0",s.right.style.top=p+"px";var u=0==this.props.scrollTop?"hidden":"",m=this.props.scrollTop==this.props.scrollTopMin?"hidden":"";if(s.shadowTop.style.visibility=u,s.shadowBottom.style.visibility=m,s.shadowTopLeft.style.visibility=u,s.shadowBottomLeft.style.visibility=m,s.shadowTopRight.style.visibility=u,s.shadowBottomRight.style.visibility=m,this.components.forEach(function(e){t=e.redraw()||t}),t){var f=3;this.redrawCount0&&(this.props.scrollTop=0),this.props.scrollTops;s++){var o=s%2===0?1.3*i:.5*i;this.lineTo(t+o*Math.sin(2*s*Math.PI/10),e-o*Math.cos(2*s*Math.PI/10))}this.closePath()},CanvasRenderingContext2D.prototype.roundRect=function(t,e,i,s,o){var n=Math.PI/180;0>i-2*o&&(o=i/2),0>s-2*o&&(o=s/2),this.beginPath(),this.moveTo(t+o,e),this.lineTo(t+i-o,e),this.arc(t+i-o,e+o,o,270*n,360*n,!1),this.lineTo(t+i,e+s-o),this.arc(t+i-o,e+s-o,o,0,90*n,!1),this.lineTo(t+o,e+s),this.arc(t+o,e+s-o,o,90*n,180*n,!1),this.lineTo(t,e+o),this.arc(t+o,e+o,o,180*n,270*n,!1)},CanvasRenderingContext2D.prototype.ellipse=function(t,e,i,s){var o=.5522848,n=i/2*o,r=s/2*o,a=t+i,h=e+s,d=t+i/2,l=e+s/2;this.beginPath(),this.moveTo(t,l),this.bezierCurveTo(t,l-r,d-n,e,d,e),this.bezierCurveTo(d+n,e,a,l-r,a,l),this.bezierCurveTo(a,l+r,d+n,h,d,h),this.bezierCurveTo(d-n,h,t,l+r,t,l)},CanvasRenderingContext2D.prototype.database=function(t,e,i,s){var o=1/3,n=i,r=s*o,a=.5522848,h=n/2*a,d=r/2*a,l=t+n,c=e+r,p=t+n/2,u=e+r/2,m=e+(s-r/2),f=e+s;this.beginPath(),this.moveTo(l,u),this.bezierCurveTo(l,u+d,p+h,c,p,c),this.bezierCurveTo(p-h,c,t,u+d,t,u),this.bezierCurveTo(t,u-d,p-h,e,p,e),this.bezierCurveTo(p+h,e,l,u-d,l,u),this.lineTo(l,m),this.bezierCurveTo(l,m+d,p+h,f,p,f),this.bezierCurveTo(p-h,f,t,m+d,t,m),this.lineTo(t,u)},CanvasRenderingContext2D.prototype.arrow=function(t,e,i,s){var o=t-s*Math.cos(i),n=e-s*Math.sin(i),r=t-.9*s*Math.cos(i),a=e-.9*s*Math.sin(i),h=o+s/3*Math.cos(i+.5*Math.PI),d=n+s/3*Math.sin(i+.5*Math.PI),l=o+s/3*Math.cos(i-.5*Math.PI),c=n+s/3*Math.sin(i-.5*Math.PI);this.beginPath(),this.moveTo(t,e),this.lineTo(h,d),this.lineTo(r,a),this.lineTo(l,c),this.closePath()},CanvasRenderingContext2D.prototype.dashedLine=function(t,e,i,s,o){o||(o=[10,5]),0==p&&(p=.001);var n=o.length;this.moveTo(t,e);for(var r=i-t,a=s-e,h=a/r,d=Math.sqrt(r*r+a*a),l=0,c=!0;d>=.1;){var p=o[l++%n];p>d&&(p=d);var u=Math.sqrt(p*p/(1+h*h));0>r&&(u=-u),t+=u,e+=h*u,this[c?"lineTo":"moveTo"](t,e),d-=p,c=!c}})},function(t,e,i){function s(t,e){this.groupId=t,this.options=e}var o=i(2),n=i(53);s.prototype.getYRange=function(t){for(var e=t[0].y,i=t[0].y,s=0;st[s].y?t[s].y:e,i=i0){var r,a,h=Number(i.svg.style.height.replace("px",""));if(r=o.getSVGElement("path",i.svgElements,i.svg),r.setAttributeNS(null,"class",e.className),void 0!==e.style&&r.setAttributeNS(null,"style",e.style),a=1==e.options.catmullRom.enabled?s._catmullRom(t,e):s._linear(t),1==e.options.shaded.enabled){var d,l=o.getSVGElement("path",i.svgElements,i.svg);d="top"==e.options.shaded.orientation?"M"+t[0].x+",0 "+a+"L"+t[t.length-1].x+",0":"M"+t[0].x+","+h+" "+a+"L"+t[t.length-1].x+","+h,l.setAttributeNS(null,"class",e.className+" fill"),void 0!==e.options.shaded.style&&l.setAttributeNS(null,"style",e.options.shaded.style),l.setAttributeNS(null,"d",d)}r.setAttributeNS(null,"d","M"+a),1==e.options.drawPoints.enabled&&n.draw(t,e,i)}},s._catmullRomUniform=function(t){for(var e,i,s,o,n,r,a=Math.round(t[0].x)+","+Math.round(t[0].y)+" ",h=1/6,d=t.length,l=0;d-1>l;l++)e=0==l?t[0]:t[l-1],i=t[l],s=t[l+1],o=d>l+2?t[l+2]:s,n={x:(-e.x+6*i.x+s.x)*h,y:(-e.y+6*i.y+s.y)*h},r={x:(i.x+6*s.x-o.x)*h,y:(i.y+6*s.y-o.y)*h},a+="C"+n.x+","+n.y+" "+r.x+","+r.y+" "+s.x+","+s.y+" ";return a},s._catmullRom=function(t,e){var i=e.options.catmullRom.alpha;if(0==i||void 0===i)return this._catmullRomUniform(t);for(var s,o,n,r,a,h,d,l,c,p,u,m,f,g,v,y,b,_,x,w=Math.round(t[0].x)+","+Math.round(t[0].y)+" ",M=t.length,S=0;M-1>S;S++)s=0==S?t[0]:t[S-1],o=t[S],n=t[S+1],r=M>S+2?t[S+2]:n,d=Math.sqrt(Math.pow(s.x-o.x,2)+Math.pow(s.y-o.y,2)),l=Math.sqrt(Math.pow(o.x-n.x,2)+Math.pow(o.y-n.y,2)),c=Math.sqrt(Math.pow(n.x-r.x,2)+Math.pow(n.y-r.y,2)),g=Math.pow(c,i),y=Math.pow(c,2*i),v=Math.pow(l,i),b=Math.pow(l,2*i),x=Math.pow(d,i),_=Math.pow(d,2*i),p=2*_+3*x*v+b,u=2*y+3*g*v+b,m=3*x*(x+v),m>0&&(m=1/m),f=3*g*(g+v),f>0&&(f=1/f),a={x:(-b*s.x+p*o.x+_*n.x)*m,y:(-b*s.y+p*o.y+_*n.y)*m},h={x:(y*o.x+u*n.x-b*r.x)*f,y:(y*o.y+u*n.y-b*r.y)*f},0==a.x&&0==a.y&&(a=o),0==h.x&&0==h.y&&(h=n),w+="C"+a.x+","+a.y+" "+h.x+","+h.y+" "+n.x+","+n.y+" ";return w},s._linear=function(t){for(var e="",i=0;it[s].y?t[s].y:e,i=i0&&(n=Math.min(n,Math.abs(c[d-1].x-r))),a=s._getSafeDrawData(n,h,m);else{var g=d+(p[r].amount-p[r].resolved),v=d-(p[r].resolved+1);g0&&(n=Math.min(n,Math.abs(c[v].x-r))),a=s._getSafeDrawData(n,h,m),p[r].resolved+=1,"stack"==h.options.barChart.handleOverlap?(f=p[r].accumulated,p[r].accumulated+=h.zeroPosition-c[d].y):"sideBySide"==h.options.barChart.handleOverlap&&(a.width=a.width/p[r].amount,a.offset+=p[r].resolved*a.width-.5*a.width*(p[r].amount+1),"left"==h.options.barChart.align?a.offset-=.5*a.width:"right"==h.options.barChart.align&&(a.offset+=.5*a.width))}o.drawBar(c[d].x+a.offset,c[d].y-f,a.width,h.zeroPosition-c[d].y,h.className+" bar",i.svgElements,i.svg),1==h.options.drawPoints.enabled&&o.drawPoint(c[d].x+a.offset,c[d].y,h,i.svgElements,i.svg)}},s._getDataIntersections=function(t,e){for(var i,s=0;s0&&(i=Math.min(i,Math.abs(e[s-1].x-e[s].x))),0==i&&(void 0===t[e[s].x]&&(t[e[s].x]={amount:0,resolved:0,accumulated:0}),t[e[s].x].amount+=1)},s._getSafeDrawData=function(t,e,i){var s,o;return t0?(s=i>t?i:t,o=0,"left"==e.options.barChart.align?o-=.5*t:"right"==e.options.barChart.align&&(o+=.5*t)):(s=e.options.barChart.width,o=0,"left"==e.options.barChart.align?o-=.5*e.options.barChart.width:"right"==e.options.barChart.align&&(o+=.5*e.options.barChart.width)),{width:s,offset:o}},s.getStackedBarYRange=function(t,e,i,o,n){if(t.length>0){t.sort(function(t,e){return t.x==e.x?t.groupId-e.groupId:t.x-e.x});var r={};s._getDataIntersections(r,t),e[o]=s._getStackedBarYRange(r,t),e[o].yAxisOrientation=n,i.push(o)}},s._getStackedBarYRange=function(t,e){for(var i,s=e[0].y,o=e[0].y,n=0;ne[n].y?e[n].y:s,o=ot[r].accumulated?t[r].accumulated:s,o=ot[s].y?t[s].y:e,i=is;++s)i[s].apply(this,e)}return this},e.prototype.listeners=function(t){return this._callbacks=this._callbacks||{},this._callbacks[t]||[]},e.prototype.hasListeners=function(t){return!!this.listeners(t).length}},function(t,e){e.startWithClustering=function(){this.clusterToFit(this.constants.clustering.initialMaxNodes,!0),this.updateLabels(),this.stabilize&&this._stabilize(),this.start()},e.clusterToFit=function(t,e){for(var i=this.nodeIndices.length,s=50,o=0;i>t&&s>o;)o%3==0?(this.forceAggregateHubs(!0),this.normalizeClusterLevels()):this.increaseClusterLevel(),i=this.nodeIndices.length,o+=1;o>0&&1==e&&this.repositionNodes(),this._updateCalculationNodes()},e.openCluster=function(t){var e=this.moving;if(t.clusterSize>this.constants.clustering.sectorThreshold&&this._nodeInActiveArea(t)&&("default"!=this._sector()||1!=this.nodeIndices.length)){this._addSector(t);for(var i=0;this.nodeIndices.lengthi;)this.decreaseClusterLevel(),i+=1}else this._expandClusterNode(t,!1,!0),this._updateNodeIndexList(),this._updateDynamicEdges(),this._updateCalculationNodes(),this.updateLabels();this.moving!=e&&this.start()},e.updateClustersDefault=function(){1==this.constants.clustering.enabled&&this.updateClusters(0,!1,!1)},e.increaseClusterLevel=function(){this.updateClusters(-1,!1,!0)},e.decreaseClusterLevel=function(){this.updateClusters(1,!1,!0)},e.updateClusters=function(t,e,i,s){var o=this.moving,n=this.nodeIndices.length;this.previousScale>this.scale&&0==t&&this._collapseSector(),this.previousScale>this.scale||-1==t?this._formClusters(i):(this.previousScalethis.scale||-1==t)&&(this._aggregateHubs(i),this._updateNodeIndexList()),(this.previousScale>this.scale||-1==t)&&(this.handleChains(),this._updateNodeIndexList()),this.previousScale=this.scale,this._updateDynamicEdges(),this.updateLabels(),this.nodeIndices.lengththis.constants.clustering.chainThreshold&&this._reduceAmountOfChains(1-this.constants.clustering.chainThreshold/t)},e._aggregateHubs=function(t){this._getHubSize(),this._formClustersByHub(t,!1)},e.forceAggregateHubs=function(t){var e=this.moving,i=this.nodeIndices.length;this._aggregateHubs(!0),this._updateNodeIndexList(),this._updateDynamicEdges(),this.updateLabels(),this.nodeIndices.length!=i&&(this.clusterSession+=1),(0==t||void 0===t)&&this.moving!=e&&this.start()},e._openClustersBySize=function(){for(var t in this.nodes)if(this.nodes.hasOwnProperty(t)){var e=this.nodes[t];1==e.inView()&&(e.width*this.scale>this.constants.clustering.screenSizeThreshold*this.frame.canvas.clientWidth||e.height*this.scale>this.constants.clustering.screenSizeThreshold*this.frame.canvas.clientHeight)&&this.openCluster(e)}},e._openClusters=function(t,e){for(var i=0;i1&&(t.clusterSizei)){var r=n.from,a=n.to;n.to.options.mass>n.from.options.mass&&(r=n.to,a=n.from),1==a.dynamicEdgesLength?this._addToCluster(r,a,!1):1==r.dynamicEdgesLength&&this._addToCluster(a,r,!1)}}},e._forceClustersByZoom=function(){for(var t in this.nodes)if(this.nodes.hasOwnProperty(t)){var e=this.nodes[t];if(1==e.dynamicEdgesLength&&0!=e.dynamicEdges.length){var i=e.dynamicEdges[0],s=i.toId==e.id?this.nodes[i.fromId]:this.nodes[i.toId];e.id!=s.id&&(s.options.mass>e.options.mass?this._addToCluster(s,e,!0):this._addToCluster(e,s,!0))}}},e._clusterToSmallestNeighbour=function(t){for(var e=-1,i=null,s=0;so.clusterSessions.length&&(e=o.clusterSessions.length,i=o)}null!=o&&void 0!==this.nodes[o.id]&&this._addToCluster(o,t,!0)},e._formClustersByHub=function(t,e){for(var i in this.nodes)this.nodes.hasOwnProperty(i)&&this._formClusterFromHub(this.nodes[i],t,e)},e._formClusterFromHub=function(t,e,i,s){if(void 0===s&&(s=0),t.dynamicEdgesLength>=this.hubThreshold&&0==i||t.dynamicEdgesLength==this.hubThreshold&&1==i){for(var o,n,r,a=this.constants.clustering.clusterEdgeThreshold/this.scale,h=!1,d=[],l=t.dynamicEdges.length,c=0;l>c;c++)d.push(t.dynamicEdges[c].id);if(0==e)for(h=!1,c=0;l>c;c++){var p=this.edges[d[c]];if(void 0!==p&&p.connected&&p.toId!=p.fromId&&(o=p.to.x-p.from.x,n=p.to.y-p.from.y,r=Math.sqrt(o*o+n*n),a>r)){h=!0;break}}if(!e&&h||e)for(c=0;l>c;c++)if(p=this.edges[d[c]],void 0!==p){var u=this.nodes[p.fromId==t.id?p.toId:p.fromId];u.dynamicEdges.length<=this.hubThreshold+s&&u.id!=t.id&&this._addToCluster(t,u,e)}}},e._addToCluster=function(t,e,i){t.containedNodes[e.id]=e;for(var s=0;s1)for(var s=0;s1&&(e.label="[".concat(String(e.clusterSize),"]"))}for(t in this.nodes)this.nodes.hasOwnProperty(t)&&(e=this.nodes[t],1==e.clusterSize&&(e.label=void 0!==e.originalLabel?e.originalLabel:String(e.id)))},e.normalizeClusterLevels=function(){var t,e=0,i=1e9,s=0;for(t in this.nodes)this.nodes.hasOwnProperty(t)&&(s=this.nodes[t].clusterSessions.length,s>e&&(e=s),i>s&&(i=s));if(e-i>this.constants.clustering.clusterLevelDifference){var o=this.nodeIndices.length,n=e-this.constants.clustering.clusterLevelDifference;for(t in this.nodes)this.nodes.hasOwnProperty(t)&&this.nodes[t].clusterSessions.lengths&&(s=n.dynamicEdgesLength),t+=n.dynamicEdgesLength,e+=Math.pow(n.dynamicEdgesLength,2),i+=1}t/=i,e/=i;var r=e-Math.pow(t,2),a=Math.sqrt(r);this.hubThreshold=Math.floor(t+2*a),this.hubThreshold>s&&(this.hubThreshold=s)},e._reduceAmountOfChains=function(t){this.hubThreshold=2;var e=Math.floor(this.nodeIndices.length*t);for(var i in this.nodes)this.nodes.hasOwnProperty(i)&&2==this.nodes[i].dynamicEdgesLength&&this.nodes[i].dynamicEdges.length>=2&&e>0&&(this._formClusterFromHub(this.nodes[i],!0,!0,1),e-=1)},e._getChainFraction=function(){var t=0,e=0;for(var i in this.nodes)this.nodes.hasOwnProperty(i)&&(2==this.nodes[i].dynamicEdgesLength&&this.nodes[i].dynamicEdges.length>=2&&(t+=1),e+=1);return t/e}},function(t,e,i){var s=i(1),o=i(40);e._putDataInSector=function(){this.sectors.active[this._sector()].nodes=this.nodes,this.sectors.active[this._sector()].edges=this.edges,this.sectors.active[this._sector()].nodeIndices=this.nodeIndices},e._switchToSector=function(t,e){void 0===e||"active"==e?this._switchToActiveSector(t):this._switchToFrozenSector(t)},e._switchToActiveSector=function(t){this.nodeIndices=this.sectors.active[t].nodeIndices,this.nodes=this.sectors.active[t].nodes,this.edges=this.sectors.active[t].edges},e._switchToSupportSector=function(){this.nodeIndices=this.sectors.support.nodeIndices,this.nodes=this.sectors.support.nodes,this.edges=this.sectors.support.edges},e._switchToFrozenSector=function(t){this.nodeIndices=this.sectors.frozen[t].nodeIndices,this.nodes=this.sectors.frozen[t].nodes,this.edges=this.sectors.frozen[t].edges},e._loadLatestSector=function(){this._switchToSector(this._sector())},e._sector=function(){return this.activeSector[this.activeSector.length-1]},e._previousSector=function(){if(this.activeSector.length>1)return this.activeSector[this.activeSector.length-2];throw new TypeError("there are not enough sectors in the this.activeSector array.")},e._setActiveSector=function(t){this.activeSector.push(t)},e._forgetLastSector=function(){this.activeSector.pop()},e._createNewSector=function(t){this.sectors.active[t]={nodes:{},edges:{},nodeIndices:[],formationScale:this.scale,drawingNode:void 0},this.sectors.active[t].drawingNode=new o({id:t,color:{background:"#eaefef",border:"495c5e"}},{},{},this.constants),this.sectors.active[t].drawingNode.clusterSize=2},e._deleteActiveSector=function(t){delete this.sectors.active[t]},e._deleteFrozenSector=function(t){delete this.sectors.frozen[t]},e._freezeSector=function(t){this.sectors.frozen[t]=this.sectors.active[t],this._deleteActiveSector(t)},e._activateSector=function(t){this.sectors.active[t]=this.sectors.frozen[t],this._deleteFrozenSector(t)},e._mergeThisWithFrozen=function(t){for(var e in this.nodes)this.nodes.hasOwnProperty(e)&&(this.sectors.frozen[t].nodes[e]=this.nodes[e]);for(var i in this.edges)this.edges.hasOwnProperty(i)&&(this.sectors.frozen[t].edges[i]=this.edges[i]);for(var s=0;s1?this[t](o[0],o[1]):this[t](e))}return this._loadLatestSector(),i},e._doInSupportSector=function(t,e){var i=!1;if(void 0===e)this._switchToSupportSector(),i=this[t]();else{this._switchToSupportSector();var s=Array.prototype.splice.call(arguments,1);i=s.length>1?this[t](s[0],s[1]):this[t](e)}return this._loadLatestSector(),i},e._doInAllFrozenSectors=function(t,e){if(void 0===e)for(var i in this.sectors.frozen)this.sectors.frozen.hasOwnProperty(i)&&(this._switchToFrozenSector(i),this[t]());else for(var i in this.sectors.frozen)if(this.sectors.frozen.hasOwnProperty(i)){this._switchToFrozenSector(i);var s=Array.prototype.splice.call(arguments,1);s.length>1?this[t](s[0],s[1]):this[t](e)}this._loadLatestSector()},e._doInAllSectors=function(t,e){var i=Array.prototype.splice.call(arguments,1);void 0===e?(this._doInAllActiveSectors(t),this._doInAllFrozenSectors(t)):i.length>1?(this._doInAllActiveSectors(t,i[0],i[1]),this._doInAllFrozenSectors(t,i[0],i[1])):(this._doInAllActiveSectors(t,e),this._doInAllFrozenSectors(t,e))},e._clearNodeIndexList=function(){var t=this._sector();this.sectors.active[t].nodeIndices=[],this.nodeIndices=this.sectors.active[t].nodeIndices},e._drawSectorNodes=function(t,e){var i,s=1e9,o=-1e9,n=1e9,r=-1e9;for(var a in this.sectors[e])if(this.sectors[e].hasOwnProperty(a)&&void 0!==this.sectors[e][a].drawingNode){this._switchToSector(a,e),s=1e9,o=-1e9,n=1e9,r=-1e9;for(var h in this.nodes)this.nodes.hasOwnProperty(h)&&(i=this.nodes[h],i.resize(t),n>i.x-.5*i.width&&(n=i.x-.5*i.width),ri.y-.5*i.height&&(s=i.y-.5*i.height),o0?this.nodes[i[i.length-1]]:null},e._getEdgesOverlappingWith=function(t,e){var i=this.edges;for(var s in i)i.hasOwnProperty(s)&&i[s].isOverlappingWith(t)&&e.push(s)},e._getAllEdgesOverlappingWith=function(t){var e=[];return this._doInAllActiveSectors("_getEdgesOverlappingWith",t,e),e},e._getEdgeAt=function(t){var e=this._pointerToPositionObject(t),i=this._getAllEdgesOverlappingWith(e);return i.length>0?this.edges[i[i.length-1]]:null -},e._addToSelection=function(t){t instanceof s?this.selectionObj.nodes[t.id]=t:this.selectionObj.edges[t.id]=t},e._addToHover=function(t){t instanceof s?this.hoverObj.nodes[t.id]=t:this.hoverObj.edges[t.id]=t},e._removeFromSelection=function(t){t instanceof s?delete this.selectionObj.nodes[t.id]:delete this.selectionObj.edges[t.id]},e._unselectAll=function(t){void 0===t&&(t=!1);for(var e in this.selectionObj.nodes)this.selectionObj.nodes.hasOwnProperty(e)&&this.selectionObj.nodes[e].unselect();for(var i in this.selectionObj.edges)this.selectionObj.edges.hasOwnProperty(i)&&this.selectionObj.edges[i].unselect();this.selectionObj={nodes:{},edges:{}},0==t&&this.emit("select",this.getSelection())},e._unselectClusters=function(t){void 0===t&&(t=!1);for(var e in this.selectionObj.nodes)this.selectionObj.nodes.hasOwnProperty(e)&&this.selectionObj.nodes[e].clusterSize>1&&(this.selectionObj.nodes[e].unselect(),this._removeFromSelection(this.selectionObj.nodes[e]));0==t&&this.emit("select",this.getSelection())},e._getSelectedNodeCount=function(){var t=0;for(var e in this.selectionObj.nodes)this.selectionObj.nodes.hasOwnProperty(e)&&(t+=1);return t},e._getSelectedNode=function(){for(var t in this.selectionObj.nodes)if(this.selectionObj.nodes.hasOwnProperty(t))return this.selectionObj.nodes[t];return null},e._getSelectedEdge=function(){for(var t in this.selectionObj.edges)if(this.selectionObj.edges.hasOwnProperty(t))return this.selectionObj.edges[t];return null},e._getSelectedEdgeCount=function(){var t=0;for(var e in this.selectionObj.edges)this.selectionObj.edges.hasOwnProperty(e)&&(t+=1);return t},e._getSelectedObjectCount=function(){var t=0;for(var e in this.selectionObj.nodes)this.selectionObj.nodes.hasOwnProperty(e)&&(t+=1);for(var i in this.selectionObj.edges)this.selectionObj.edges.hasOwnProperty(i)&&(t+=1);return t},e._selectionIsEmpty=function(){for(var t in this.selectionObj.nodes)if(this.selectionObj.nodes.hasOwnProperty(t))return!1;for(var e in this.selectionObj.edges)if(this.selectionObj.edges.hasOwnProperty(e))return!1;return!0},e._clusterInSelection=function(){for(var t in this.selectionObj.nodes)if(this.selectionObj.nodes.hasOwnProperty(t)&&this.selectionObj.nodes[t].clusterSize>1)return!0;return!1},e._selectConnectedEdges=function(t){for(var e=0;ei;i++){o=t[i];var n=this.nodes[o];if(!n)throw new RangeError('Node with id "'+o+'" not found');this._selectObject(n,!0,!0,e,!0)}this.redraw()},e.selectEdges=function(t){var e,i,s;if(!t||void 0==t.length)throw"Selection must be an array with ids";for(this._unselectAll(!0),e=0,i=t.length;i>e;e++){s=t[e];var o=this.edges[s];if(!o)throw new RangeError('Edge with id "'+s+'" not found');this._selectObject(o,!0,!0,!1,!0)}this.redraw()},e._updateSelection=function(){for(var t in this.selectionObj.nodes)this.selectionObj.nodes.hasOwnProperty(t)&&(this.nodes.hasOwnProperty(t)||delete this.selectionObj.nodes[t]);for(var e in this.selectionObj.edges)this.selectionObj.edges.hasOwnProperty(e)&&(this.edges.hasOwnProperty(e)||delete this.selectionObj.edges[e])}},function(t,e,i){var s=i(1),o=i(40),n=i(37);e._clearManipulatorBar=function(){this._recursiveDOMDelete(this.manipulationDiv),this.manipulationDOM={},this._manipulationReleaseOverload=function(){},delete this.sectors.support.nodes.targetNode,delete this.sectors.support.nodes.targetViaNode,this.controlNodesActive=!1,this.freezeSimulation=!1},e._restoreOverloadedFunctions=function(){for(var t in this.cachedFunctions)this.cachedFunctions.hasOwnProperty(t)&&(this[t]=this.cachedFunctions[t],delete this.cachedFunctions[t])},e._toggleEditMode=function(){this.editMode=!this.editMode;var t=this.manipulationDiv,e=this.closeDiv,i=this.editModeDiv;1==this.editMode?(t.style.display="block",e.style.display="block",i.style.display="none",e.onclick=this._toggleEditMode.bind(this)):(t.style.display="none",e.style.display="none",i.style.display="block",e.onclick=null),this._createManipulatorBar()},e._createManipulatorBar=function(){this.boundFunction&&this.off("select",this.boundFunction);var t=this.constants.locales[this.constants.locale];if(void 0!==this.edgeBeingEdited&&(this.edgeBeingEdited._disableControlNodes(),this.edgeBeingEdited=void 0,this.selectedControlNode=null,this.controlNodesActive=!1,this._redraw()),this._restoreOverloadedFunctions(),this.freezeSimulation=!1,this.blockConnectingEdgeSelection=!1,this.forceAppendSelection=!1,this.manipulationDOM={},1==this.editMode){for(;this.manipulationDiv.hasChildNodes();)this.manipulationDiv.removeChild(this.manipulationDiv.firstChild);this.manipulationDOM.addNodeSpan=document.createElement("span"),this.manipulationDOM.addNodeSpan.className="network-manipulationUI add",this.manipulationDOM.addNodeLabelSpan=document.createElement("span"),this.manipulationDOM.addNodeLabelSpan.className="network-manipulationLabel",this.manipulationDOM.addNodeLabelSpan.innerHTML=t.addNode,this.manipulationDOM.addNodeSpan.appendChild(this.manipulationDOM.addNodeLabelSpan),this.manipulationDOM.seperatorLineDiv1=document.createElement("div"),this.manipulationDOM.seperatorLineDiv1.className="network-seperatorLine",this.manipulationDOM.addEdgeSpan=document.createElement("span"),this.manipulationDOM.addEdgeSpan.className="network-manipulationUI connect",this.manipulationDOM.addEdgeLabelSpan=document.createElement("span"),this.manipulationDOM.addEdgeLabelSpan.className="network-manipulationLabel",this.manipulationDOM.addEdgeLabelSpan.innerHTML=t.addEdge,this.manipulationDOM.addEdgeSpan.appendChild(this.manipulationDOM.addEdgeLabelSpan),this.manipulationDiv.appendChild(this.manipulationDOM.addNodeSpan),this.manipulationDiv.appendChild(this.manipulationDOM.seperatorLineDiv1),this.manipulationDiv.appendChild(this.manipulationDOM.addEdgeSpan),1==this._getSelectedNodeCount()&&this.triggerFunctions.edit?(this.manipulationDOM.seperatorLineDiv2=document.createElement("div"),this.manipulationDOM.seperatorLineDiv2.className="network-seperatorLine",this.manipulationDOM.editNodeSpan=document.createElement("span"),this.manipulationDOM.editNodeSpan.className="network-manipulationUI edit",this.manipulationDOM.editNodeLabelSpan=document.createElement("span"),this.manipulationDOM.editNodeLabelSpan.className="network-manipulationLabel",this.manipulationDOM.editNodeLabelSpan.innerHTML=t.editNode,this.manipulationDOM.editNodeSpan.appendChild(this.manipulationDOM.editNodeLabelSpan),this.manipulationDiv.appendChild(this.manipulationDOM.seperatorLineDiv2),this.manipulationDiv.appendChild(this.manipulationDOM.editNodeSpan)):1==this._getSelectedEdgeCount()&&0==this._getSelectedNodeCount()&&(this.manipulationDOM.seperatorLineDiv3=document.createElement("div"),this.manipulationDOM.seperatorLineDiv3.className="network-seperatorLine",this.manipulationDOM.editEdgeSpan=document.createElement("span"),this.manipulationDOM.editEdgeSpan.className="network-manipulationUI edit",this.manipulationDOM.editEdgeLabelSpan=document.createElement("span"),this.manipulationDOM.editEdgeLabelSpan.className="network-manipulationLabel",this.manipulationDOM.editEdgeLabelSpan.innerHTML=t.editEdge,this.manipulationDOM.editEdgeSpan.appendChild(this.manipulationDOM.editEdgeLabelSpan),this.manipulationDiv.appendChild(this.manipulationDOM.seperatorLineDiv3),this.manipulationDiv.appendChild(this.manipulationDOM.editEdgeSpan)),0==this._selectionIsEmpty()&&(this.manipulationDOM.seperatorLineDiv4=document.createElement("div"),this.manipulationDOM.seperatorLineDiv4.className="network-seperatorLine",this.manipulationDOM.deleteSpan=document.createElement("span"),this.manipulationDOM.deleteSpan.className="network-manipulationUI delete",this.manipulationDOM.deleteLabelSpan=document.createElement("span"),this.manipulationDOM.deleteLabelSpan.className="network-manipulationLabel",this.manipulationDOM.deleteLabelSpan.innerHTML=t.del,this.manipulationDOM.deleteSpan.appendChild(this.manipulationDOM.deleteLabelSpan),this.manipulationDiv.appendChild(this.manipulationDOM.seperatorLineDiv4),this.manipulationDiv.appendChild(this.manipulationDOM.deleteSpan)),this.manipulationDOM.addNodeSpan.onclick=this._createAddNodeToolbar.bind(this),this.manipulationDOM.addEdgeSpan.onclick=this._createAddEdgeToolbar.bind(this),1==this._getSelectedNodeCount()&&this.triggerFunctions.edit?this.manipulationDOM.editNodeSpan.onclick=this._editNode.bind(this):1==this._getSelectedEdgeCount()&&0==this._getSelectedNodeCount()&&(this.manipulationDOM.editEdgeSpan.onclick=this._createEditEdgeToolbar.bind(this)),0==this._selectionIsEmpty()&&(this.manipulationDOM.deleteSpan.onclick=this._deleteSelected.bind(this)),this.closeDiv.onclick=this._toggleEditMode.bind(this);var e=this;this.boundFunction=e._createManipulatorBar,this.on("select",this.boundFunction)}else{for(;this.editModeDiv.hasChildNodes();)this.editModeDiv.removeChild(this.editModeDiv.firstChild);this.manipulationDOM.editModeSpan=document.createElement("span"),this.manipulationDOM.editModeSpan.className="network-manipulationUI edit editmode",this.manipulationDOM.editModeLabelSpan=document.createElement("span"),this.manipulationDOM.editModeLabelSpan.className="network-manipulationLabel",this.manipulationDOM.editModeLabelSpan.innerHTML=t.edit,this.manipulationDOM.editModeSpan.appendChild(this.manipulationDOM.editModeLabelSpan),this.editModeDiv.appendChild(this.manipulationDOM.editModeSpan),this.manipulationDOM.editModeSpan.onclick=this._toggleEditMode.bind(this)}},e._createAddNodeToolbar=function(){this._clearManipulatorBar(),this.boundFunction&&this.off("select",this.boundFunction);var t=this.constants.locales[this.constants.locale];this.manipulationDOM={},this.manipulationDOM.backSpan=document.createElement("span"),this.manipulationDOM.backSpan.className="network-manipulationUI back",this.manipulationDOM.backLabelSpan=document.createElement("span"),this.manipulationDOM.backLabelSpan.className="network-manipulationLabel",this.manipulationDOM.backLabelSpan.innerHTML=t.back,this.manipulationDOM.backSpan.appendChild(this.manipulationDOM.backLabelSpan),this.manipulationDOM.seperatorLineDiv1=document.createElement("div"),this.manipulationDOM.seperatorLineDiv1.className="network-seperatorLine",this.manipulationDOM.descriptionSpan=document.createElement("span"),this.manipulationDOM.descriptionSpan.className="network-manipulationUI none",this.manipulationDOM.descriptionLabelSpan=document.createElement("span"),this.manipulationDOM.descriptionLabelSpan.className="network-manipulationLabel",this.manipulationDOM.descriptionLabelSpan.innerHTML=t.addDescription,this.manipulationDOM.descriptionSpan.appendChild(this.manipulationDOM.descriptionLabelSpan),this.manipulationDiv.appendChild(this.manipulationDOM.backSpan),this.manipulationDiv.appendChild(this.manipulationDOM.seperatorLineDiv1),this.manipulationDiv.appendChild(this.manipulationDOM.descriptionSpan),this.manipulationDOM.backSpan.onclick=this._createManipulatorBar.bind(this);var e=this;this.boundFunction=e._addNode,this.on("select",this.boundFunction)},e._createAddEdgeToolbar=function(){this._clearManipulatorBar(),this._unselectAll(!0),this.freezeSimulation=!0,this.boundFunction&&this.off("select",this.boundFunction);var t=this.constants.locales[this.constants.locale];this._unselectAll(),this.forceAppendSelection=!1,this.blockConnectingEdgeSelection=!0,this.manipulationDOM={},this.manipulationDOM.backSpan=document.createElement("span"),this.manipulationDOM.backSpan.className="network-manipulationUI back",this.manipulationDOM.backLabelSpan=document.createElement("span"),this.manipulationDOM.backLabelSpan.className="network-manipulationLabel",this.manipulationDOM.backLabelSpan.innerHTML=t.back,this.manipulationDOM.backSpan.appendChild(this.manipulationDOM.backLabelSpan),this.manipulationDOM.seperatorLineDiv1=document.createElement("div"),this.manipulationDOM.seperatorLineDiv1.className="network-seperatorLine",this.manipulationDOM.descriptionSpan=document.createElement("span"),this.manipulationDOM.descriptionSpan.className="network-manipulationUI none",this.manipulationDOM.descriptionLabelSpan=document.createElement("span"),this.manipulationDOM.descriptionLabelSpan.className="network-manipulationLabel",this.manipulationDOM.descriptionLabelSpan.innerHTML=t.edgeDescription,this.manipulationDOM.descriptionSpan.appendChild(this.manipulationDOM.descriptionLabelSpan),this.manipulationDiv.appendChild(this.manipulationDOM.backSpan),this.manipulationDiv.appendChild(this.manipulationDOM.seperatorLineDiv1),this.manipulationDiv.appendChild(this.manipulationDOM.descriptionSpan),this.manipulationDOM.backSpan.onclick=this._createManipulatorBar.bind(this);var e=this;this.boundFunction=e._handleConnect,this.on("select",this.boundFunction),this.cachedFunctions._handleTouch=this._handleTouch,this.cachedFunctions._manipulationReleaseOverload=this._manipulationReleaseOverload,this.cachedFunctions._handleDragStart=this._handleDragStart,this.cachedFunctions._handleDragEnd=this._handleDragEnd,this.cachedFunctions._handleOnHold=this._handleOnHold,this._handleTouch=this._handleConnect,this._manipulationReleaseOverload=function(){},this._handleOnHold=function(){},this._handleDragStart=function(){},this._handleDragEnd=this._finishConnect,this._redraw()},e._createEditEdgeToolbar=function(){this._clearManipulatorBar(),this.controlNodesActive=!0,this.boundFunction&&this.off("select",this.boundFunction),this.edgeBeingEdited=this._getSelectedEdge(),this.edgeBeingEdited._enableControlNodes();var t=this.constants.locales[this.constants.locale];this.manipulationDOM={},this.manipulationDOM.backSpan=document.createElement("span"),this.manipulationDOM.backSpan.className="network-manipulationUI back",this.manipulationDOM.backLabelSpan=document.createElement("span"),this.manipulationDOM.backLabelSpan.className="network-manipulationLabel",this.manipulationDOM.backLabelSpan.innerHTML=t.back,this.manipulationDOM.backSpan.appendChild(this.manipulationDOM.backLabelSpan),this.manipulationDOM.seperatorLineDiv1=document.createElement("div"),this.manipulationDOM.seperatorLineDiv1.className="network-seperatorLine",this.manipulationDOM.descriptionSpan=document.createElement("span"),this.manipulationDOM.descriptionSpan.className="network-manipulationUI none",this.manipulationDOM.descriptionLabelSpan=document.createElement("span"),this.manipulationDOM.descriptionLabelSpan.className="network-manipulationLabel",this.manipulationDOM.descriptionLabelSpan.innerHTML=t.editEdgeDescription,this.manipulationDOM.descriptionSpan.appendChild(this.manipulationDOM.descriptionLabelSpan),this.manipulationDiv.appendChild(this.manipulationDOM.backSpan),this.manipulationDiv.appendChild(this.manipulationDOM.seperatorLineDiv1),this.manipulationDiv.appendChild(this.manipulationDOM.descriptionSpan),this.manipulationDOM.backSpan.onclick=this._createManipulatorBar.bind(this),this.cachedFunctions._handleTouch=this._handleTouch,this.cachedFunctions._manipulationReleaseOverload=this._manipulationReleaseOverload,this.cachedFunctions._handleTap=this._handleTap,this.cachedFunctions._handleDragStart=this._handleDragStart,this.cachedFunctions._handleOnDrag=this._handleOnDrag,this._handleTouch=this._selectControlNode,this._handleTap=function(){},this._handleOnDrag=this._controlNodeDrag,this._handleDragStart=function(){},this._manipulationReleaseOverload=this._releaseControlNode,this._redraw()},e._selectControlNode=function(t){this.edgeBeingEdited.controlNodes.from.unselect(),this.edgeBeingEdited.controlNodes.to.unselect(),this.selectedControlNode=this.edgeBeingEdited._getSelectedControlNode(this._XconvertDOMtoCanvas(t.x),this._YconvertDOMtoCanvas(t.y)),null!==this.selectedControlNode&&(this.selectedControlNode.select(),this.freezeSimulation=!0),this._redraw()},e._controlNodeDrag=function(t){var e=this._getPointer(t.gesture.center);null!==this.selectedControlNode&&void 0!==this.selectedControlNode&&(this.selectedControlNode.x=this._XconvertDOMtoCanvas(e.x),this.selectedControlNode.y=this._YconvertDOMtoCanvas(e.y)),this._redraw()},e._releaseControlNode=function(t){var e=this._getNodeAt(t);null!==e?(1==this.edgeBeingEdited.controlNodes.from.selected&&(this.edgeBeingEdited._restoreControlNodes(),this._editEdge(e.id,this.edgeBeingEdited.to.id),this.edgeBeingEdited.controlNodes.from.unselect()),1==this.edgeBeingEdited.controlNodes.to.selected&&(this.edgeBeingEdited._restoreControlNodes(),this._editEdge(this.edgeBeingEdited.from.id,e.id),this.edgeBeingEdited.controlNodes.to.unselect())):this.edgeBeingEdited._restoreControlNodes(),this.freezeSimulation=!1,this._redraw()},e._handleConnect=function(t){if(0==this._getSelectedNodeCount()){var e=this._getNodeAt(t);if(null!=e)if(e.clusterSize>1)alert(this.constants.locales[this.constants.locale].createEdgeError);else{this._selectObject(e,!1);var i=this.sectors.support.nodes;i.targetNode=new o({id:"targetNode"},{},{},this.constants);var s=i.targetNode;s.x=e.x,s.y=e.y,this.edges.connectionEdge=new n({id:"connectionEdge",from:e.id,to:s.id},this,this.constants);var r=this.edges.connectionEdge;r.from=e,r.connected=!0,r.options.smoothCurves={enabled:!0,dynamic:!1,type:"continuous",roundness:.5},r.selected=!0,r.to=s,this.cachedFunctions._handleOnDrag=this._handleOnDrag,this._handleOnDrag=function(t){var e=this._getPointer(t.gesture.center),i=this.edges.connectionEdge;i.to.x=this._XconvertDOMtoCanvas(e.x),i.to.y=this._YconvertDOMtoCanvas(e.y)},this.moving=!0,this.start()}}},e._finishConnect=function(t){if(1==this._getSelectedNodeCount()){var e=this._getPointer(t.gesture.center);this._handleOnDrag=this.cachedFunctions._handleOnDrag,delete this.cachedFunctions._handleOnDrag;var i=this.edges.connectionEdge.fromId;delete this.edges.connectionEdge,delete this.sectors.support.nodes.targetNode,delete this.sectors.support.nodes.targetViaNode;var s=this._getNodeAt(e);null!=s&&(s.clusterSize>1?alert(this.constants.locales[this.constants.locale].createEdgeError):(this._createEdge(i,s.id),this._createManipulatorBar())),this._unselectAll()}},e._addNode=function(){if(this._selectionIsEmpty()&&1==this.editMode){var t=this._pointerToPositionObject(this.pointerPosition),e={id:s.randomUUID(),x:t.left,y:t.top,label:"new",allowedToMoveX:!0,allowedToMoveY:!0};if(this.triggerFunctions.add){if(2!=this.triggerFunctions.add.length)throw new Error("The function for add does not support two arguments (data,callback)");var i=this;this.triggerFunctions.add(e,function(t){i.nodesData.add(t),i._createManipulatorBar(),i.moving=!0,i.start()})}else this.nodesData.add(e),this._createManipulatorBar(),this.moving=!0,this.start()}},e._createEdge=function(t,e){if(1==this.editMode){var i={from:t,to:e};if(this.triggerFunctions.connect){if(2!=this.triggerFunctions.connect.length)throw new Error("The function for connect does not support two arguments (data,callback)");var s=this;this.triggerFunctions.connect(i,function(t){s.edgesData.add(t),s.moving=!0,s.start()})}else this.edgesData.add(i),this.moving=!0,this.start()}},e._editEdge=function(t,e){if(1==this.editMode){var i={id:this.edgeBeingEdited.id,from:t,to:e};if(this.triggerFunctions.editEdge){if(2!=this.triggerFunctions.editEdge.length)throw new Error("The function for edit does not support two arguments (data, callback)");var s=this;this.triggerFunctions.editEdge(i,function(t){s.edgesData.update(t),s.moving=!0,s.start()})}else this.edgesData.update(i),this.moving=!0,this.start()}},e._editNode=function(){if(!this.triggerFunctions.edit||1!=this.editMode)throw new Error("No edit function has been bound to this button");var t=this._getSelectedNode(),e={id:t.id,label:t.label,group:t.options.group,shape:t.options.shape,color:{background:t.options.color.background,border:t.options.color.border,highlight:{background:t.options.color.highlight.background,border:t.options.color.highlight.border}}};if(2!=this.triggerFunctions.edit.length)throw new Error("The function for edit does not support two arguments (data, callback)");var i=this;this.triggerFunctions.edit(e,function(t){i.nodesData.update(t),i._createManipulatorBar(),i.moving=!0,i.start()})},e._deleteSelected=function(){if(!this._selectionIsEmpty()&&1==this.editMode)if(this._clusterInSelection())alert(this.constants.locales[this.constants.locale].deleteClusterError);else{var t=this.getSelectedNodes(),e=this.getSelectedEdges();if(this.triggerFunctions.del){var i=this,s={nodes:t,edges:e};if(2!=this.triggerFunctions.del.length)throw new Error("The function for delete does not support two arguments (data, callback)");this.triggerFunctions.del(s,function(t){i.edgesData.remove(t.edges),i.nodesData.remove(t.nodes),i._unselectAll(),i.moving=!0,i.start()})}else this.edgesData.remove(e),this.nodesData.remove(t),this._unselectAll(),this.moving=!0,this.start()}}},function(t,e,i){var s=(i(1),i(45));e._cleanNavigation=function(){if(0!=this.navigationHammers.existing.length){for(var t=0;t0){var t,e,i=0,s=!1,o=!1;for(e in this.nodes)this.nodes.hasOwnProperty(e)&&(t=this.nodes[e],-1!=t.level?s=!0:o=!0,is&&(n.xFixed=!1,n.x=i[n.level].minPos,r=!0):n.yFixed&&n.level>s&&(n.yFixed=!1,n.y=i[n.level].minPos,r=!0),1==r&&(i[n.level].minPos+=i[n.level].nodeSpacing,n.edges.length>1&&this._placeBranchNodes(n.edges,n.id,i,n.level))}},e._setLevel=function(t,e,i){for(var s=0;st)&&(o.level=t,o.edges.length>1&&this._setLevel(t+1,o.edges,o.id))}},e._setLevelDirected=function(t,e,i){this.nodes[i].hierarchyEnumerated=!0;for(var s,o,n=0;n1&&s.hierarchyEnumerated===!1&&this._setLevelDirected(s.level,s.edges,s.id)},e._restoreNodes=function(){for(var t in this.nodes)this.nodes.hasOwnProperty(t)&&(this.nodes[t].xFixed=!1,this.nodes[t].yFixed=!1)}},function(t,e){var i,s,o;!function(n,r){s=[],i=r,o="function"==typeof i?i.apply(e,s):i,!(void 0!==o&&(t.exports=o))}(this,function(){function t(t){var e,i=t&&t.preventDefault||!1,s=t&&t.container||window,o={},n={keydown:{},keyup:{}},r={};for(e=97;122>=e;e++)r[String.fromCharCode(e)]={code:65+(e-97),shift:!1};for(e=65;90>=e;e++)r[String.fromCharCode(e)]={code:e,shift:!0};for(e=0;9>=e;e++)r[""+e]={code:48+e,shift:!1};for(e=1;12>=e;e++)r["F"+e]={code:111+e,shift:!1};for(e=0;9>=e;e++)r["num"+e]={code:96+e,shift:!1};r["num*"]={code:106,shift:!1},r["num+"]={code:107,shift:!1},r["num-"]={code:109,shift:!1},r["num/"]={code:111,shift:!1},r["num."]={code:110,shift:!1},r.left={code:37,shift:!1},r.up={code:38,shift:!1},r.right={code:39,shift:!1},r.down={code:40,shift:!1},r.space={code:32,shift:!1},r.enter={code:13,shift:!1},r.shift={code:16,shift:void 0},r.esc={code:27,shift:!1},r.backspace={code:8,shift:!1},r.tab={code:9,shift:!1},r.ctrl={code:17,shift:!1},r.alt={code:18,shift:!1},r["delete"]={code:46,shift:!1},r.pageup={code:33,shift:!1},r.pagedown={code:34,shift:!1},r["="]={code:187,shift:!1},r["-"]={code:189,shift:!1},r["]"]={code:221,shift:!1},r["["]={code:219,shift:!1};var a=function(t){d(t,"keydown")},h=function(t){d(t,"keyup")},d=function(t,e){if(void 0!==n[e][t.keyCode]){for(var s=n[e][t.keyCode],o=0;os;s++)if(e.call(i,t[s],s,t)===!1)return}else for(s in t)if(t.hasOwnProperty(s)&&e.call(i,t[s],s,t)===!1)return},inStr:function(t,e){return t.indexOf(e)>-1},inArray:function(t,e){if(t.indexOf){var i=t.indexOf(e);return-1===i?!1:i}for(var s=0,o=t.length;o>s;s++)if(t[s]===e)return s;return!1},toArray:function(t){return Array.prototype.slice.call(t,0)},hasParent:function(t,e){for(;t;){if(t==e)return!0;t=t.parentNode}return!1},getCenter:function(t){var e=[],i=[],s=[],o=[],n=Math.min,r=Math.max;return 1===t.length?{pageX:t[0].pageX,pageY:t[0].pageY,clientX:t[0].clientX,clientY:t[0].clientY}:(x.each(t,function(t){e.push(t.pageX),i.push(t.pageY),s.push(t.clientX),o.push(t.clientY)}),{pageX:(n.apply(Math,e)+r.apply(Math,e))/2,pageY:(n.apply(Math,i)+r.apply(Math,i))/2,clientX:(n.apply(Math,s)+r.apply(Math,s))/2,clientY:(n.apply(Math,o)+r.apply(Math,o))/2})},getVelocity:function(t,e,i){return{x:Math.abs(e/t)||0,y:Math.abs(i/t)||0}},getAngle:function(t,e){var i=e.clientX-t.clientX,s=e.clientY-t.clientY;return 180*Math.atan2(s,i)/Math.PI},getDirection:function(t,e){var i=Math.abs(t.clientX-e.clientX),s=Math.abs(t.clientY-e.clientY);return i>=s?t.clientX-e.clientX>0?l:p:t.clientY-e.clientY>0?c:d},getDistance:function(t,e){var i=e.clientX-t.clientX,s=e.clientY-t.clientY;return Math.sqrt(i*i+s*s)},getScale:function(t,e){return t.length>=2&&e.length>=2?this.getDistance(e[0],e[1])/this.getDistance(t[0],t[1]):1},getRotation:function(t,e){return t.length>=2&&e.length>=2?this.getAngle(e[1],e[0])-this.getAngle(t[1],t[0]):0},isVertical:function(t){return t==c||t==d},setPrefixedCss:function(t,e,i,s){var o=["","Webkit","Moz","O","ms"];e=x.toCamelCase(e);for(var n=0;n0&&this.started&&(r=v),this.started=!0;var d=this.collectEventData(i,r,o,t);return e!=y&&s.call(S,d),a&&(d.changedLength=h,d.eventType=a,s.call(S,d),d.eventType=r,delete d.changedLength),r==y&&(s.call(S,d),this.started=!1),r},determineEventTypes:function(){var t;return t=a.HAS_POINTEREVENTS?o.PointerEvent?["pointerdown","pointermove","pointerup pointercancel lostpointercapture"]:["MSPointerDown","MSPointerMove","MSPointerUp MSPointerCancel MSLostPointerCapture"]:a.NO_MOUSEEVENTS?["touchstart","touchmove","touchend touchcancel"]:["touchstart mousedown","touchmove mousemove","touchend touchcancel mouseup"],h[g]=t[0],h[v]=t[1],h[y]=t[2],h},getTouchList:function(t,e){if(a.HAS_POINTEREVENTS)return M.getTouchList();if(t.touches){if(e==v)return t.touches;var i=[],s=[].concat(x.toArray(t.touches),x.toArray(t.changedTouches)),o=[];return x.each(s,function(t){x.inArray(i,t.identifier)===!1&&o.push(t),i.push(t.identifier)}),o}return t.identifier=1,[t]},collectEventData:function(t,e,i,s){var o=m;return x.inStr(s.type,"mouse")||M.matchType(u,s)?o=u:M.matchType(f,s)&&(o=f),{center:x.getCenter(i),timeStamp:Date.now(),target:s.target,touches:i,eventType:e,pointerType:o,srcEvent:s,preventDefault:function(){var t=this.srcEvent;t.preventManipulation&&t.preventManipulation(),t.preventDefault&&t.preventDefault()},stopPropagation:function(){this.srcEvent.stopPropagation()},stopDetect:function(){return S.stopDetect()}}}},M=a.PointerEvent={pointers:{},getTouchList:function(){var t=[];return x.each(this.pointers,function(e){t.push(e)}),t},updatePointer:function(t,e){t==y||t!=y&&1!==e.buttons?delete this.pointers[e.pointerId]:(e.identifier=e.pointerId,this.pointers[e.pointerId]=e)},matchType:function(t,e){if(!e.pointerType)return!1;var i=e.pointerType,s={};return s[u]=i===(e.MSPOINTER_TYPE_MOUSE||u),s[m]=i===(e.MSPOINTER_TYPE_TOUCH||m),s[f]=i===(e.MSPOINTER_TYPE_PEN||f),s[t]},reset:function(){this.pointers={}}},S=a.detection={gestures:[],current:null,previous:null,stopped:!1,startDetect:function(t,e){this.current||(this.stopped=!1,this.current={inst:t,startEvent:x.extend({},e),lastEvent:!1,lastCalcEvent:!1,futureCalcEvent:!1,lastCalcData:{},name:""},this.detect(e))},detect:function(t){if(this.current&&!this.stopped){t=this.extendEventData(t);var e=this.current.inst,i=e.options;return x.each(this.gestures,function(s){!this.stopped&&e.enabled&&i[s.name]&&s.handler.call(s,t,e)},this),this.current&&(this.current.lastEvent=t),t.eventType==y&&this.stopDetect(),t}},stopDetect:function(){this.previous=x.extend({},this.current),this.current=null,this.stopped=!0},getCalculatedData:function(t,e,i,s,o){var n=this.current,r=!1,h=n.lastCalcEvent,d=n.lastCalcData;h&&t.timeStamp-h.timeStamp>a.CALCULATE_INTERVAL&&(e=h.center,i=t.timeStamp-h.timeStamp,s=t.center.clientX-h.center.clientX,o=t.center.clientY-h.center.clientY,r=!0),(t.eventType==_||t.eventType==b)&&(n.futureCalcEvent=t),(!n.lastCalcEvent||r)&&(d.velocity=x.getVelocity(i,s,o),d.angle=x.getAngle(e,t.center),d.direction=x.getDirection(e,t.center),n.lastCalcEvent=n.futureCalcEvent||t,n.futureCalcEvent=t),t.velocityX=d.velocity.x,t.velocityY=d.velocity.y,t.interimAngle=d.angle,t.interimDirection=d.direction},extendEventData:function(t){var e=this.current,i=e.startEvent,s=e.lastEvent||i;(t.eventType==_||t.eventType==b)&&(i.touches=[],x.each(t.touches,function(t){i.touches.push({clientX:t.clientX,clientY:t.clientY})}));var o=t.timeStamp-i.timeStamp,n=t.center.clientX-i.center.clientX,r=t.center.clientY-i.center.clientY;return this.getCalculatedData(t,s.center,o,n,r),x.extend(t,{startEvent:i,deltaTime:o,deltaX:n,deltaY:r,distance:x.getDistance(i.center,t.center),angle:x.getAngle(i.center,t.center),direction:x.getDirection(i.center,t.center),scale:x.getScale(i.touches,t.touches),rotation:x.getRotation(i.touches,t.touches)}),t},register:function(t){var e=t.defaults||{};return e[t.name]===n&&(e[t.name]=!0),x.extend(a.defaults,e,!0),t.index=t.index||1e3,this.gestures.push(t),this.gestures.sort(function(t,e){return t.indexe.index?1:0}),this.gestures}};a.Instance=function(t,e){var i=this;r(),this.element=t,this.enabled=!0,x.each(e,function(t,i){delete e[i],e[x.toCamelCase(i)]=t}),this.options=x.extend(x.extend({},a.defaults),e||{}),this.options.behavior&&x.toggleBehavior(this.element,this.options.behavior,!0),this.eventStartHandler=w.onTouch(t,g,function(t){i.enabled&&t.eventType==g?S.startDetect(i,t):t.eventType==_&&S.detect(t)}),this.eventHandlers=[]},a.Instance.prototype={on:function(t,e){var i=this;return w.on(i.element,t,e,function(t){i.eventHandlers.push({gesture:t,handler:e})}),i},off:function(t,e){var i=this;return w.off(i.element,t,e,function(t){var s=x.inArray({gesture:t,handler:e});s!==!1&&i.eventHandlers.splice(s,1)}),i},trigger:function(t,e){e||(e={});var i=a.DOCUMENT.createEvent("Event");i.initEvent(t,!0,!0),i.gesture=e;var s=this.element;return x.hasParent(e.target,s)&&(s=e.target),s.dispatchEvent(i),this},enable:function(t){return this.enabled=t,this},dispose:function(){var t,e;for(x.toggleBehavior(this.element,this.options.behavior,!1),t=-1;e=this.eventHandlers[++t];)x.off(this.element,e.gesture,e.handler);return this.eventHandlers=[],w.off(this.element,h[g],this.eventStartHandler),null}},function(t){function e(e,s){var o=S.current;if(!(s.options.dragMaxTouches>0&&e.touches.length>s.options.dragMaxTouches))switch(e.eventType){case g:i=!1;break;case v:if(e.distance0)){var r=Math.abs(s.options.dragMinDistance/e.distance);n.pageX+=e.deltaX*r,n.pageY+=e.deltaY*r,n.clientX+=e.deltaX*r,n.clientY+=e.deltaY*r,e=S.extendEventData(e)}(o.lastEvent.dragLockToAxis||s.options.dragLockToAxis&&s.options.dragLockMinDistance<=e.distance)&&(e.dragLockToAxis=!0);var a=o.lastEvent.direction;e.dragLockToAxis&&a!==e.direction&&(e.direction=x.isVertical(a)?e.deltaY<0?c:d:e.deltaX<0?l:p),i||(s.trigger(t+"start",e),i=!0),s.trigger(t,e),s.trigger(t+e.direction,e);var h=x.isVertical(e.direction);(s.options.dragBlockVertical&&h||s.options.dragBlockHorizontal&&!h)&&e.preventDefault();break;case b:i&&e.changedLength<=s.options.dragMaxTouches&&(s.trigger(t+"end",e),i=!1);break;case y:i=!1}}var i=!1;a.gestures.Drag={name:t,index:50,handler:e,defaults:{dragMinDistance:10,dragDistanceCorrection:!0,dragMaxTouches:1,dragBlockHorizontal:!1,dragBlockVertical:!1,dragLockToAxis:!1,dragLockMinDistance:25}}}("drag"),a.gestures.Gesture={name:"gesture",index:1337,handler:function(t,e){e.trigger(this.name,t)}},function(t){function e(e,s){var o=s.options,n=S.current;switch(e.eventType){case g:clearTimeout(i),n.name=t,i=setTimeout(function(){n&&n.name==t&&s.trigger(t,e)},o.holdTimeout);break;case v:e.distance>o.holdThreshold&&clearTimeout(i);break;case b:clearTimeout(i)}}var i;a.gestures.Hold={name:t,index:10,defaults:{holdTimeout:500,holdThreshold:2},handler:e}}("hold"),a.gestures.Release={name:"release",index:1/0,handler:function(t,e){t.eventType==b&&e.trigger(this.name,t)}},a.gestures.Swipe={name:"swipe",index:40,defaults:{swipeMinTouches:1,swipeMaxTouches:1,swipeVelocityX:.6,swipeVelocityY:.6},handler:function(t,e){if(t.eventType==b){var i=t.touches.length,s=e.options;if(is.swipeMaxTouches)return;(t.velocityX>s.swipeVelocityX||t.velocityY>s.swipeVelocityY)&&(e.trigger(this.name,t),e.trigger(this.name+t.direction,t))}}},function(t){function e(e,s){var o,n,r=s.options,a=S.current,h=S.previous;switch(e.eventType){case g:i=!1;break;case v:i=i||e.distance>r.tapMaxDistance;break;case y:!x.inStr(e.srcEvent.type,"cancel")&&e.deltaTimes.options.transformMinRotation&&s.trigger("rotate",e),o>s.options.transformMinScale&&(s.trigger("pinch",e),s.trigger("pinch"+(e.scale<1?"in":"out"),e));break;case b:i&&e.changedLength<2&&(s.trigger(t+"end",e),i=!1)}}var i=!1;a.gestures.Transform={name:t,index:45,defaults:{transformMinScale:.01,transformMinRotation:1},handler:e}}("transform"),s=function(){return a}.call(e,i,e,t),!(s!==n&&(t.exports=s))}(window)},function(t,e,i){var s;(function(t,o){(function(n){function r(t,e,i){switch(arguments.length){case 2:return null!=t?t:e;case 3:return null!=t?t:null!=e?e:i;default:throw new Error("Implement me")}}function a(t,e){return Le.call(t,e)}function h(){return{empty:!1,unusedTokens:[],unusedInput:[],overflow:-2,charsLeftOver:0,nullInput:!1,invalidMonth:null,invalidFormat:!1,userInvalidated:!1,iso:!1}}function d(t){Ce.suppressDeprecationWarnings===!1&&"undefined"!=typeof console&&console.warn&&console.warn("Deprecation warning: "+t)}function l(t,e){var i=!0;return b(function(){return i&&(d(t),i=!1),e.apply(this,arguments)},e)}function c(t,e){Mi[t]||(d(e),Mi[t]=!0)}function p(t,e){return function(i){return w(t.call(this,i),e)}}function u(t,e){return function(i){return this.localeData().ordinal(t.call(this,i),e)}}function m(t,e){var i,s,o=12*(e.year()-t.year())+(e.month()-t.month()),n=t.clone().add(o,"months");return 0>e-n?(i=t.clone().add(o-1,"months"),s=(e-n)/(n-i)):(i=t.clone().add(o+1,"months"),s=(e-n)/(i-n)),-(o+s)}function f(t,e,i){var s;return null==i?e:null!=t.meridiemHour?t.meridiemHour(e,i):null!=t.isPM?(s=t.isPM(i),s&&12>e&&(e+=12),s||12!==e||(e=0),e):e}function g(){}function v(t,e){e!==!1&&F(t),_(this,t),this._d=new Date(+t._d),Di===!1&&(Di=!0,Ce.updateOffset(this),Di=!1)}function y(t){var e=N(t),i=e.year||0,s=e.quarter||0,o=e.month||0,n=e.week||0,r=e.day||0,a=e.hour||0,h=e.minute||0,d=e.second||0,l=e.millisecond||0;this._milliseconds=+l+1e3*d+6e4*h+36e5*a,this._days=+r+7*n,this._months=+o+3*s+12*i,this._data={},this._locale=Ce.localeData(),this._bubble()}function b(t,e){for(var i in e)a(e,i)&&(t[i]=e[i]);return a(e,"toString")&&(t.toString=e.toString),a(e,"valueOf")&&(t.valueOf=e.valueOf),t}function _(t,e){var i,s,o;if("undefined"!=typeof e._isAMomentObject&&(t._isAMomentObject=e._isAMomentObject),"undefined"!=typeof e._i&&(t._i=e._i),"undefined"!=typeof e._f&&(t._f=e._f),"undefined"!=typeof e._l&&(t._l=e._l),"undefined"!=typeof e._strict&&(t._strict=e._strict),"undefined"!=typeof e._tzm&&(t._tzm=e._tzm),"undefined"!=typeof e._isUTC&&(t._isUTC=e._isUTC),"undefined"!=typeof e._offset&&(t._offset=e._offset),"undefined"!=typeof e._pf&&(t._pf=e._pf),"undefined"!=typeof e._locale&&(t._locale=e._locale),Ye.length>0)for(i in Ye)s=Ye[i],o=e[s],"undefined"!=typeof o&&(t[s]=o);return t}function x(t){return 0>t?Math.ceil(t):Math.floor(t)}function w(t,e,i){for(var s=""+Math.abs(t),o=t>=0;s.lengths;s++)(i&&t[s]!==e[s]||!i&&I(t[s])!==I(e[s]))&&r++;return r+n}function k(t){if(t){var e=t.toLowerCase().replace(/(.)s$/,"$1");t=gi[t]||vi[e]||e}return t}function N(t){var e,i,s={};for(i in t)a(t,i)&&(e=k(i),e&&(s[e]=t[i]));return s}function L(t){var e,i;if(0===t.indexOf("week"))e=7,i="day";else{if(0!==t.indexOf("month"))return;e=12,i="month"}Ce[t]=function(s,o){var r,a,h=Ce._locale[t],d=[];if("number"==typeof s&&(o=s,s=n),a=function(t){var e=Ce().utc().set(i,t);return h.call(Ce._locale,e,s||"")},null!=o)return a(o);for(r=0;e>r;r++)d.push(a(r));return d}}function I(t){var e=+t,i=0;return 0!==e&&isFinite(e)&&(i=e>=0?Math.floor(e):Math.ceil(e)),i}function z(t,e){return new Date(Date.UTC(t,e+1,0)).getUTCDate()}function P(t,e,i){return me(Ce([t,11,31+e-i]),e,i).week}function A(t){return R(t)?366:365}function R(t){return t%4===0&&t%100!==0||t%400===0}function F(t){var e;t._a&&-2===t._pf.overflow&&(e=t._a[ze]<0||t._a[ze]>11?ze:t._a[Pe]<1||t._a[Pe]>z(t._a[Ie],t._a[ze])?Pe:t._a[Ae]<0||t._a[Ae]>24||24===t._a[Ae]&&(0!==t._a[Re]||0!==t._a[Fe]||0!==t._a[He])?Ae:t._a[Re]<0||t._a[Re]>59?Re:t._a[Fe]<0||t._a[Fe]>59?Fe:t._a[He]<0||t._a[He]>999?He:-1,t._pf._overflowDayOfYear&&(Ie>e||e>Pe)&&(e=Pe),t._pf.overflow=e)}function H(t){return null==t._isValid&&(t._isValid=!isNaN(t._d.getTime())&&t._pf.overflow<0&&!t._pf.empty&&!t._pf.invalidMonth&&!t._pf.nullInput&&!t._pf.invalidFormat&&!t._pf.userInvalidated,t._strict&&(t._isValid=t._isValid&&0===t._pf.charsLeftOver&&0===t._pf.unusedTokens.length&&t._pf.bigHour===n)),t._isValid}function B(t){return t?t.toLowerCase().replace("_","-"):t}function Y(t){for(var e,i,s,o,n=0;n0;){if(s=W(o.slice(0,e).join("-")))return s;if(i&&i.length>=e&&E(o,i,!0)>=e-1)break;e--}n++}return null}function W(t){var e=null;if(!Be[t]&&We)try{e=Ce.locale(),!function(){var t=new Error('Cannot find module "./locale"');throw t.code="MODULE_NOT_FOUND",t}(),Ce.locale(e)}catch(i){}return Be[t]}function G(t,e){var i,s;return e._isUTC?(i=e.clone(),s=(Ce.isMoment(t)||O(t)?+t:+Ce(t))-+i,i._d.setTime(+i._d+s),Ce.updateOffset(i,!1),i):Ce(t).local()}function j(t){return t.match(/\[[\s\S]/)?t.replace(/^\[|\]$/g,""):t.replace(/\\/g,"")}function V(t){var e,i,s=t.match(Ue);for(e=0,i=s.length;i>e;e++)s[e]=wi[s[e]]?wi[s[e]]:j(s[e]);return function(o){var n="";for(e=0;i>e;e++)n+=s[e]instanceof Function?s[e].call(o,t):s[e];return n}}function U(t,e){return t.isValid()?(e=X(e,t.localeData()),yi[e]||(yi[e]=V(e)),yi[e](t)):t.localeData().invalidDate()}function X(t,e){function i(t){return e.longDateFormat(t)||t}var s=5;for(Xe.lastIndex=0;s>=0&&Xe.test(t);)t=t.replace(Xe,i),Xe.lastIndex=0,s-=1;return t}function q(t,e){var i,s=e._strict;switch(t){case"Q":return oi;case"DDDD":return ri;case"YYYY":case"GGGG":case"gggg":return s?ai:Qe;case"Y":case"G":case"g":return di;case"YYYYYY":case"YYYYY":case"GGGGG":case"ggggg":return s?hi:Ke;case"S":if(s)return oi;case"SS":if(s)return ni;case"SSS":if(s)return ri;case"DDD":return Ze;case"MMM":case"MMMM":case"dd":case"ddd":case"dddd":return Je;case"a":case"A":return e._locale._meridiemParse;case"x":return ii;case"X":return si;case"Z":case"ZZ":return ti;case"T":return ei;case"SSSS":return $e;case"MM":case"DD":case"YY":case"GG":case"gg":case"HH":case"hh":case"mm":case"ss":case"ww":case"WW":return s?ni:qe;case"M":case"D":case"d":case"H":case"h":case"m":case"s":case"w":case"W":case"e":case"E":return qe;case"Do":return s?e._locale._ordinalParse:e._locale._ordinalParseLenient;default:return i=new RegExp(se(ie(t.replace("\\","")),"i"))}}function Z(t){t=t||"";var e=t.match(ti)||[],i=e[e.length-1]||[],s=(i+"").match(mi)||["-",0,0],o=+(60*s[1])+I(s[2]);return"+"===s[0]?o:-o}function Q(t,e,i){var s,o=i._a;switch(t){case"Q":null!=e&&(o[ze]=3*(I(e)-1));break;case"M":case"MM":null!=e&&(o[ze]=I(e)-1);break;case"MMM":case"MMMM":s=i._locale.monthsParse(e,t,i._strict),null!=s?o[ze]=s:i._pf.invalidMonth=e;break;case"D":case"DD":null!=e&&(o[Pe]=I(e));break;case"Do":null!=e&&(o[Pe]=I(parseInt(e.match(/\d{1,2}/)[0],10)));break;case"DDD":case"DDDD":null!=e&&(i._dayOfYear=I(e));break;case"YY":o[Ie]=Ce.parseTwoDigitYear(e);break;case"YYYY":case"YYYYY":case"YYYYYY":o[Ie]=I(e);break;case"a":case"A":i._meridiem=e;break;case"h":case"hh":i._pf.bigHour=!0;case"H":case"HH":o[Ae]=I(e);break;case"m":case"mm":o[Re]=I(e);break;case"s":case"ss":o[Fe]=I(e);break;case"S":case"SS":case"SSS":case"SSSS":o[He]=I(1e3*("0."+e));break;case"x":i._d=new Date(I(e));break;case"X":i._d=new Date(1e3*parseFloat(e));break;case"Z":case"ZZ":i._useUTC=!0,i._tzm=Z(e);break;case"dd":case"ddd":case"dddd":s=i._locale.weekdaysParse(e),null!=s?(i._w=i._w||{},i._w.d=s):i._pf.invalidWeekday=e;break;case"w":case"ww":case"W":case"WW":case"d":case"e":case"E":t=t.substr(0,1);case"gggg":case"GGGG":case"GGGGG":t=t.substr(0,2),e&&(i._w=i._w||{},i._w[t]=I(e));break;case"gg":case"GG":i._w=i._w||{},i._w[t]=Ce.parseTwoDigitYear(e)}}function K(t){var e,i,s,o,n,a,h;e=t._w,null!=e.GG||null!=e.W||null!=e.E?(n=1,a=4,i=r(e.GG,t._a[Ie],me(Ce(),1,4).year),s=r(e.W,1),o=r(e.E,1)):(n=t._locale._week.dow,a=t._locale._week.doy,i=r(e.gg,t._a[Ie],me(Ce(),n,a).year),s=r(e.w,1),null!=e.d?(o=e.d,n>o&&++s):o=null!=e.e?e.e+n:n),h=fe(i,s,o,a,n),t._a[Ie]=h.year,t._dayOfYear=h.dayOfYear}function $(t){var e,i,s,o,n=[];if(!t._d){for(s=te(t),t._w&&null==t._a[Pe]&&null==t._a[ze]&&K(t),t._dayOfYear&&(o=r(t._a[Ie],s[Ie]),t._dayOfYear>A(o)&&(t._pf._overflowDayOfYear=!0),i=le(o,0,t._dayOfYear),t._a[ze]=i.getUTCMonth(),t._a[Pe]=i.getUTCDate()),e=0;3>e&&null==t._a[e];++e)t._a[e]=n[e]=s[e];for(;7>e;e++)t._a[e]=n[e]=null==t._a[e]?2===e?1:0:t._a[e];24===t._a[Ae]&&0===t._a[Re]&&0===t._a[Fe]&&0===t._a[He]&&(t._nextDay=!0,t._a[Ae]=0),t._d=(t._useUTC?le:de).apply(null,n),null!=t._tzm&&t._d.setUTCMinutes(t._d.getUTCMinutes()-t._tzm),t._nextDay&&(t._a[Ae]=24)}}function J(t){var e;t._d||(e=N(t._i),t._a=[e.year,e.month,e.day||e.date,e.hour,e.minute,e.second,e.millisecond],$(t))}function te(t){var e=new Date;return t._useUTC?[e.getUTCFullYear(),e.getUTCMonth(),e.getUTCDate()]:[e.getFullYear(),e.getMonth(),e.getDate()]}function ee(t){if(t._f===Ce.ISO_8601)return void ne(t);t._a=[],t._pf.empty=!0;var e,i,s,o,r,a=""+t._i,h=a.length,d=0;for(s=X(t._f,t._locale).match(Ue)||[],e=0;e0&&t._pf.unusedInput.push(r),a=a.slice(a.indexOf(i)+i.length),d+=i.length),wi[o]?(i?t._pf.empty=!1:t._pf.unusedTokens.push(o),Q(o,i,t)):t._strict&&!i&&t._pf.unusedTokens.push(o);t._pf.charsLeftOver=h-d,a.length>0&&t._pf.unusedInput.push(a),t._pf.bigHour===!0&&t._a[Ae]<=12&&(t._pf.bigHour=n),t._a[Ae]=f(t._locale,t._a[Ae],t._meridiem),$(t),F(t)}function ie(t){return t.replace(/\\(\[)|\\(\])|\[([^\]\[]*)\]|\\(.)/g,function(t,e,i,s,o){return e||i||s||o})}function se(t){return t.replace(/[-\/\\^$*+?.()|[\]{}]/g,"\\$&")}function oe(t){var e,i,s,o,n;if(0===t._f.length)return t._pf.invalidFormat=!0,void(t._d=new Date(0/0));for(o=0;on)&&(s=n,i=e));b(t,i||e)}function ne(t){var e,i,s=t._i,o=li.exec(s);if(o){for(t._pf.iso=!0,e=0,i=pi.length;i>e;e++)if(pi[e][1].exec(s)){t._f=pi[e][0]+(o[6]||" ");break}for(e=0,i=ui.length;i>e;e++)if(ui[e][1].exec(s)){t._f+=ui[e][0];break}s.match(ti)&&(t._f+="Z"),ee(t)}else t._isValid=!1}function re(t){ne(t),t._isValid===!1&&(delete t._isValid,Ce.createFromInputFallback(t))}function ae(t,e){var i,s=[];for(i=0;it&&a.setFullYear(t),a}function le(t){var e=new Date(Date.UTC.apply(null,arguments));return 1970>t&&e.setUTCFullYear(t),e}function ce(t,e){if("string"==typeof t)if(isNaN(t)){if(t=e.weekdaysParse(t),"number"!=typeof t)return null}else t=parseInt(t,10);return t}function pe(t,e,i,s,o){return o.relativeTime(e||1,!!i,t,s)}function ue(t,e,i){var s=Ce.duration(t).abs(),o=Ne(s.as("s")),n=Ne(s.as("m")),r=Ne(s.as("h")),a=Ne(s.as("d")),h=Ne(s.as("M")),d=Ne(s.as("y")),l=o0,l[4]=i,pe.apply({},l)}function me(t,e,i){var s,o=i-e,n=i-t.day();return n>o&&(n-=7),o-7>n&&(n+=7),s=Ce(t).add(n,"d"),{week:Math.ceil(s.dayOfYear()/7),year:s.year()}}function fe(t,e,i,s,o){var n,r,a=le(t,0,1).getUTCDay();return a=0===a?7:a,i=null!=i?i:o,n=o-a+(a>s?7:0)-(o>a?7:0),r=7*(e-1)+(i-o)+n+1,{year:r>0?t:t-1,dayOfYear:r>0?r:A(t-1)+r}}function ge(t){var e,i=t._i,s=t._f;return t._locale=t._locale||Ce.localeData(t._l),null===i||s===n&&""===i?Ce.invalid({nullInput:!0}):("string"==typeof i&&(t._i=i=t._locale.preparse(i)),Ce.isMoment(i)?new v(i,!0):(s?T(s)?oe(t):ee(t):he(t),e=new v(t),e._nextDay&&(e.add(1,"d"),e._nextDay=n),e))}function ve(t,e){var i,s;if(1===e.length&&T(e[0])&&(e=e[0]),!e.length)return Ce();for(i=e[0],s=1;s=0?"+":"-";return e+w(Math.abs(t),6)},gg:function(){return w(this.weekYear()%100,2)},gggg:function(){return w(this.weekYear(),4)},ggggg:function(){return w(this.weekYear(),5)},GG:function(){return w(this.isoWeekYear()%100,2)},GGGG:function(){return w(this.isoWeekYear(),4)},GGGGG:function(){return w(this.isoWeekYear(),5)},e:function(){return this.weekday()},E:function(){return this.isoWeekday()},a:function(){return this.localeData().meridiem(this.hours(),this.minutes(),!0)},A:function(){return this.localeData().meridiem(this.hours(),this.minutes(),!1)},H:function(){return this.hours()},h:function(){return this.hours()%12||12},m:function(){return this.minutes()},s:function(){return this.seconds()},S:function(){return I(this.milliseconds()/100)},SS:function(){return w(I(this.milliseconds()/10),2)},SSS:function(){return w(this.milliseconds(),3)},SSSS:function(){return w(this.milliseconds(),3)},Z:function(){var t=this.utcOffset(),e="+";return 0>t&&(t=-t,e="-"),e+w(I(t/60),2)+":"+w(I(t)%60,2)},ZZ:function(){var t=this.utcOffset(),e="+";return 0>t&&(t=-t,e="-"),e+w(I(t/60),2)+w(I(t)%60,2)},z:function(){return this.zoneAbbr()},zz:function(){return this.zoneName()},x:function(){return this.valueOf()},X:function(){return this.unix()},Q:function(){return this.quarter()}},Mi={},Si=["months","monthsShort","weekdays","weekdaysShort","weekdaysMin"],Di=!1;_i.length;)Oe=_i.pop(),wi[Oe+"o"]=u(wi[Oe],Oe);for(;xi.length;)Oe=xi.pop(),wi[Oe+Oe]=p(wi[Oe],2); -wi.DDDD=p(wi.DDD,3),b(g.prototype,{set:function(t){var e,i;for(i in t)e=t[i],"function"==typeof e?this[i]=e:this["_"+i]=e;this._ordinalParseLenient=new RegExp(this._ordinalParse.source+"|"+/\d{1,2}/.source)},_months:"January_February_March_April_May_June_July_August_September_October_November_December".split("_"),months:function(t){return this._months[t.month()]},_monthsShort:"Jan_Feb_Mar_Apr_May_Jun_Jul_Aug_Sep_Oct_Nov_Dec".split("_"),monthsShort:function(t){return this._monthsShort[t.month()]},monthsParse:function(t,e,i){var s,o,n;for(this._monthsParse||(this._monthsParse=[],this._longMonthsParse=[],this._shortMonthsParse=[]),s=0;12>s;s++){if(o=Ce.utc([2e3,s]),i&&!this._longMonthsParse[s]&&(this._longMonthsParse[s]=new RegExp("^"+this.months(o,"").replace(".","")+"$","i"),this._shortMonthsParse[s]=new RegExp("^"+this.monthsShort(o,"").replace(".","")+"$","i")),i||this._monthsParse[s]||(n="^"+this.months(o,"")+"|^"+this.monthsShort(o,""),this._monthsParse[s]=new RegExp(n.replace(".",""),"i")),i&&"MMMM"===e&&this._longMonthsParse[s].test(t))return s;if(i&&"MMM"===e&&this._shortMonthsParse[s].test(t))return s;if(!i&&this._monthsParse[s].test(t))return s}},_weekdays:"Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday".split("_"),weekdays:function(t){return this._weekdays[t.day()]},_weekdaysShort:"Sun_Mon_Tue_Wed_Thu_Fri_Sat".split("_"),weekdaysShort:function(t){return this._weekdaysShort[t.day()]},_weekdaysMin:"Su_Mo_Tu_We_Th_Fr_Sa".split("_"),weekdaysMin:function(t){return this._weekdaysMin[t.day()]},weekdaysParse:function(t){var e,i,s;for(this._weekdaysParse||(this._weekdaysParse=[]),e=0;7>e;e++)if(this._weekdaysParse[e]||(i=Ce([2e3,1]).day(e),s="^"+this.weekdays(i,"")+"|^"+this.weekdaysShort(i,"")+"|^"+this.weekdaysMin(i,""),this._weekdaysParse[e]=new RegExp(s.replace(".",""),"i")),this._weekdaysParse[e].test(t))return e},_longDateFormat:{LTS:"h:mm:ss A",LT:"h:mm A",L:"MM/DD/YYYY",LL:"MMMM D, YYYY",LLL:"MMMM D, YYYY LT",LLLL:"dddd, MMMM D, YYYY LT"},longDateFormat:function(t){var e=this._longDateFormat[t];return!e&&this._longDateFormat[t.toUpperCase()]&&(e=this._longDateFormat[t.toUpperCase()].replace(/MMMM|MM|DD|dddd/g,function(t){return t.slice(1)}),this._longDateFormat[t]=e),e},isPM:function(t){return"p"===(t+"").toLowerCase().charAt(0)},_meridiemParse:/[ap]\.?m?\.?/i,meridiem:function(t,e,i){return t>11?i?"pm":"PM":i?"am":"AM"},_calendar:{sameDay:"[Today at] LT",nextDay:"[Tomorrow at] LT",nextWeek:"dddd [at] LT",lastDay:"[Yesterday at] LT",lastWeek:"[Last] dddd [at] LT",sameElse:"L"},calendar:function(t,e,i){var s=this._calendar[t];return"function"==typeof s?s.apply(e,[i]):s},_relativeTime:{future:"in %s",past:"%s ago",s:"a few seconds",m:"a minute",mm:"%d minutes",h:"an hour",hh:"%d hours",d:"a day",dd:"%d days",M:"a month",MM:"%d months",y:"a year",yy:"%d years"},relativeTime:function(t,e,i,s){var o=this._relativeTime[i];return"function"==typeof o?o(t,e,i,s):o.replace(/%d/i,t)},pastFuture:function(t,e){var i=this._relativeTime[t>0?"future":"past"];return"function"==typeof i?i(e):i.replace(/%s/i,e)},ordinal:function(t){return this._ordinal.replace("%d",t)},_ordinal:"%d",_ordinalParse:/\d{1,2}/,preparse:function(t){return t},postformat:function(t){return t},week:function(t){return me(t,this._week.dow,this._week.doy).week},_week:{dow:0,doy:6},firstDayOfWeek:function(){return this._week.dow},firstDayOfYear:function(){return this._week.doy},_invalidDate:"Invalid date",invalidDate:function(){return this._invalidDate}}),Ce=function(t,e,i,s){var o;return"boolean"==typeof i&&(s=i,i=n),o={},o._isAMomentObject=!0,o._i=t,o._f=e,o._l=i,o._strict=s,o._isUTC=!1,o._pf=h(),ge(o)},Ce.suppressDeprecationWarnings=!1,Ce.createFromInputFallback=l("moment construction falls back to js Date. This is discouraged and will be removed in upcoming major release. Please refer to https://github.com/moment/moment/issues/1407 for more info.",function(t){t._d=new Date(t._i+(t._useUTC?" UTC":""))}),Ce.min=function(){var t=[].slice.call(arguments,0);return ve("isBefore",t)},Ce.max=function(){var t=[].slice.call(arguments,0);return ve("isAfter",t)},Ce.utc=function(t,e,i,s){var o;return"boolean"==typeof i&&(s=i,i=n),o={},o._isAMomentObject=!0,o._useUTC=!0,o._isUTC=!0,o._l=i,o._i=t,o._f=e,o._strict=s,o._pf=h(),ge(o).utc()},Ce.unix=function(t){return Ce(1e3*t)},Ce.duration=function(t,e){var i,s,o,n,r=t,h=null;return Ce.isDuration(t)?r={ms:t._milliseconds,d:t._days,M:t._months}:"number"==typeof t?(r={},e?r[e]=t:r.milliseconds=t):(h=je.exec(t))?(i="-"===h[1]?-1:1,r={y:0,d:I(h[Pe])*i,h:I(h[Ae])*i,m:I(h[Re])*i,s:I(h[Fe])*i,ms:I(h[He])*i}):(h=Ve.exec(t))?(i="-"===h[1]?-1:1,o=function(t){var e=t&&parseFloat(t.replace(",","."));return(isNaN(e)?0:e)*i},r={y:o(h[2]),M:o(h[3]),d:o(h[4]),h:o(h[5]),m:o(h[6]),s:o(h[7]),w:o(h[8])}):null==r?r={}:"object"==typeof r&&("from"in r||"to"in r)&&(n=S(Ce(r.from),Ce(r.to)),r={},r.ms=n.milliseconds,r.M=n.months),s=new y(r),Ce.isDuration(t)&&a(t,"_locale")&&(s._locale=t._locale),s},Ce.version=Ee,Ce.defaultFormat=ci,Ce.ISO_8601=function(){},Ce.momentProperties=Ye,Ce.updateOffset=function(){},Ce.relativeTimeThreshold=function(t,e){return bi[t]===n?!1:e===n?bi[t]:(bi[t]=e,!0)},Ce.lang=l("moment.lang is deprecated. Use moment.locale instead.",function(t,e){return Ce.locale(t,e)}),Ce.locale=function(t,e){var i;return t&&(i="undefined"!=typeof e?Ce.defineLocale(t,e):Ce.localeData(t),i&&(Ce.duration._locale=Ce._locale=i)),Ce._locale._abbr},Ce.defineLocale=function(t,e){return null!==e?(e.abbr=t,Be[t]||(Be[t]=new g),Be[t].set(e),Ce.locale(t),Be[t]):(delete Be[t],null)},Ce.langData=l("moment.langData is deprecated. Use moment.localeData instead.",function(t){return Ce.localeData(t)}),Ce.localeData=function(t){var e;if(t&&t._locale&&t._locale._abbr&&(t=t._locale._abbr),!t)return Ce._locale;if(!T(t)){if(e=W(t))return e;t=[t]}return Y(t)},Ce.isMoment=function(t){return t instanceof v||null!=t&&a(t,"_isAMomentObject")},Ce.isDuration=function(t){return t instanceof y};for(Oe=Si.length-1;Oe>=0;--Oe)L(Si[Oe]);Ce.normalizeUnits=function(t){return k(t)},Ce.invalid=function(t){var e=Ce.utc(0/0);return null!=t?b(e._pf,t):e._pf.userInvalidated=!0,e},Ce.parseZone=function(){return Ce.apply(null,arguments).parseZone()},Ce.parseTwoDigitYear=function(t){return I(t)+(I(t)>68?1900:2e3)},Ce.isDate=O,b(Ce.fn=v.prototype,{clone:function(){return Ce(this)},valueOf:function(){return+this._d-6e4*(this._offset||0)},unix:function(){return Math.floor(+this/1e3)},toString:function(){return this.clone().locale("en").format("ddd MMM DD YYYY HH:mm:ss [GMT]ZZ")},toDate:function(){return this._offset?new Date(+this):this._d},toISOString:function(){var t=Ce(this).utc();return 00:!1},parsingFlags:function(){return b({},this._pf)},invalidAt:function(){return this._pf.overflow},utc:function(t){return this.utcOffset(0,t)},local:function(t){return this._isUTC&&(this.utcOffset(0,t),this._isUTC=!1,t&&this.subtract(this._dateUtcOffset(),"m")),this},format:function(t){var e=U(this,t||Ce.defaultFormat);return this.localeData().postformat(e)},add:D(1,"add"),subtract:D(-1,"subtract"),diff:function(t,e,i){var s,o,n=G(t,this),r=6e4*(n.utcOffset()-this.utcOffset());return e=k(e),"year"===e||"month"===e||"quarter"===e?(o=m(this,n),"quarter"===e?o/=3:"year"===e&&(o/=12)):(s=this-n,o="second"===e?s/1e3:"minute"===e?s/6e4:"hour"===e?s/36e5:"day"===e?(s-r)/864e5:"week"===e?(s-r)/6048e5:s),i?o:x(o)},from:function(t,e){return Ce.duration({to:this,from:t}).locale(this.locale()).humanize(!e)},fromNow:function(t){return this.from(Ce(),t)},calendar:function(t){var e=t||Ce(),i=G(e,this).startOf("day"),s=this.diff(i,"days",!0),o=-6>s?"sameElse":-1>s?"lastWeek":0>s?"lastDay":1>s?"sameDay":2>s?"nextDay":7>s?"nextWeek":"sameElse";return this.format(this.localeData().calendar(o,this,Ce(e)))},isLeapYear:function(){return R(this.year())},isDST:function(){return this.utcOffset()>this.clone().month(0).utcOffset()||this.utcOffset()>this.clone().month(5).utcOffset()},day:function(t){var e=this._isUTC?this._d.getUTCDay():this._d.getDay();return null!=t?(t=ce(t,this.localeData()),this.add(t-e,"d")):e},month:xe("Month",!0),startOf:function(t){switch(t=k(t)){case"year":this.month(0);case"quarter":case"month":this.date(1);case"week":case"isoWeek":case"day":this.hours(0);case"hour":this.minutes(0);case"minute":this.seconds(0);case"second":this.milliseconds(0)}return"week"===t?this.weekday(0):"isoWeek"===t&&this.isoWeekday(1),"quarter"===t&&this.month(3*Math.floor(this.month()/3)),this},endOf:function(t){return t=k(t),t===n||"millisecond"===t?this:this.startOf(t).add(1,"isoWeek"===t?"week":t).subtract(1,"ms")},isAfter:function(t,e){var i;return e=k("undefined"!=typeof e?e:"millisecond"),"millisecond"===e?(t=Ce.isMoment(t)?t:Ce(t),+this>+t):(i=Ce.isMoment(t)?+t:+Ce(t),i<+this.clone().startOf(e))},isBefore:function(t,e){var i;return e=k("undefined"!=typeof e?e:"millisecond"),"millisecond"===e?(t=Ce.isMoment(t)?t:Ce(t),+t>+this):(i=Ce.isMoment(t)?+t:+Ce(t),+this.clone().endOf(e)t?this:t}),max:l("moment().max is deprecated, use moment.max instead. https://github.com/moment/moment/issues/1548",function(t){return t=Ce.apply(null,arguments),t>this?this:t}),zone:l("moment().zone is deprecated, use moment().utcOffset instead. https://github.com/moment/moment/issues/1779",function(t,e){return null!=t?("string"!=typeof t&&(t=-t),this.utcOffset(t,e),this):-this.utcOffset()}),utcOffset:function(t,e){var i,s=this._offset||0;return null!=t?("string"==typeof t&&(t=Z(t)),Math.abs(t)<16&&(t=60*t),!this._isUTC&&e&&(i=this._dateUtcOffset()),this._offset=t,this._isUTC=!0,null!=i&&this.add(i,"m"),s!==t&&(!e||this._changeInProgress?C(this,Ce.duration(t-s,"m"),1,!1):this._changeInProgress||(this._changeInProgress=!0,Ce.updateOffset(this,!0),this._changeInProgress=null)),this):this._isUTC?s:this._dateUtcOffset()},isLocal:function(){return!this._isUTC},isUtcOffset:function(){return this._isUTC},isUtc:function(){return this._isUTC&&0===this._offset},zoneAbbr:function(){return this._isUTC?"UTC":""},zoneName:function(){return this._isUTC?"Coordinated Universal Time":""},parseZone:function(){return this._tzm?this.utcOffset(this._tzm):"string"==typeof this._i&&this.utcOffset(Z(this._i)),this},hasAlignedHourOffset:function(t){return t=t?Ce(t).utcOffset():0,(this.utcOffset()-t)%60===0},daysInMonth:function(){return z(this.year(),this.month())},dayOfYear:function(t){var e=Ne((Ce(this).startOf("day")-Ce(this).startOf("year"))/864e5)+1;return null==t?e:this.add(t-e,"d")},quarter:function(t){return null==t?Math.ceil((this.month()+1)/3):this.month(3*(t-1)+this.month()%3)},weekYear:function(t){var e=me(this,this.localeData()._week.dow,this.localeData()._week.doy).year;return null==t?e:this.add(t-e,"y")},isoWeekYear:function(t){var e=me(this,1,4).year;return null==t?e:this.add(t-e,"y")},week:function(t){var e=this.localeData().week(this);return null==t?e:this.add(7*(t-e),"d")},isoWeek:function(t){var e=me(this,1,4).week;return null==t?e:this.add(7*(t-e),"d")},weekday:function(t){var e=(this.day()+7-this.localeData()._week.dow)%7;return null==t?e:this.add(t-e,"d")},isoWeekday:function(t){return null==t?this.day()||7:this.day(this.day()%7?t:t-7)},isoWeeksInYear:function(){return P(this.year(),1,4)},weeksInYear:function(){var t=this.localeData()._week;return P(this.year(),t.dow,t.doy)},get:function(t){return t=k(t),this[t]()},set:function(t,e){var i;if("object"==typeof t)for(i in t)this.set(i,t[i]);else t=k(t),"function"==typeof this[t]&&this[t](e);return this},locale:function(t){var e;return t===n?this._locale._abbr:(e=Ce.localeData(t),null!=e&&(this._locale=e),this)},lang:l("moment().lang() is deprecated. Instead, use moment().localeData() to get the language configuration. Use moment().locale() to change languages.",function(t){return t===n?this.localeData():this.locale(t)}),localeData:function(){return this._locale},_dateUtcOffset:function(){return 15*-Math.round(this._d.getTimezoneOffset()/15)}}),Ce.fn.millisecond=Ce.fn.milliseconds=xe("Milliseconds",!1),Ce.fn.second=Ce.fn.seconds=xe("Seconds",!1),Ce.fn.minute=Ce.fn.minutes=xe("Minutes",!1),Ce.fn.hour=Ce.fn.hours=xe("Hours",!0),Ce.fn.date=xe("Date",!0),Ce.fn.dates=l("dates accessor is deprecated. Use date instead.",xe("Date",!0)),Ce.fn.year=xe("FullYear",!0),Ce.fn.years=l("years accessor is deprecated. Use year instead.",xe("FullYear",!0)),Ce.fn.days=Ce.fn.day,Ce.fn.months=Ce.fn.month,Ce.fn.weeks=Ce.fn.week,Ce.fn.isoWeeks=Ce.fn.isoWeek,Ce.fn.quarters=Ce.fn.quarter,Ce.fn.toJSON=Ce.fn.toISOString,Ce.fn.isUTC=Ce.fn.isUtc,b(Ce.duration.fn=y.prototype,{_bubble:function(){var t,e,i,s=this._milliseconds,o=this._days,n=this._months,r=this._data,a=0;r.milliseconds=s%1e3,t=x(s/1e3),r.seconds=t%60,e=x(t/60),r.minutes=e%60,i=x(e/60),r.hours=i%24,o+=x(i/24),a=x(we(o)),o-=x(Me(a)),n+=x(o/30),o%=30,a+=x(n/12),n%=12,r.days=o,r.months=n,r.years=a},abs:function(){return this._milliseconds=Math.abs(this._milliseconds),this._days=Math.abs(this._days),this._months=Math.abs(this._months),this._data.milliseconds=Math.abs(this._data.milliseconds),this._data.seconds=Math.abs(this._data.seconds),this._data.minutes=Math.abs(this._data.minutes),this._data.hours=Math.abs(this._data.hours),this._data.months=Math.abs(this._data.months),this._data.years=Math.abs(this._data.years),this},weeks:function(){return x(this.days()/7)},valueOf:function(){return this._milliseconds+864e5*this._days+this._months%12*2592e6+31536e6*I(this._months/12)},humanize:function(t){var e=ue(this,!t,this.localeData());return t&&(e=this.localeData().pastFuture(+this,e)),this.localeData().postformat(e)},add:function(t,e){var i=Ce.duration(t,e);return this._milliseconds+=i._milliseconds,this._days+=i._days,this._months+=i._months,this._bubble(),this},subtract:function(t,e){var i=Ce.duration(t,e);return this._milliseconds-=i._milliseconds,this._days-=i._days,this._months-=i._months,this._bubble(),this},get:function(t){return t=k(t),this[t.toLowerCase()+"s"]()},as:function(t){var e,i;if(t=k(t),"month"===t||"year"===t)return e=this._days+this._milliseconds/864e5,i=this._months+12*we(e),"month"===t?i:i/12;switch(e=this._days+Math.round(Me(this._months/12)),t){case"week":return e/7+this._milliseconds/6048e5;case"day":return e+this._milliseconds/864e5;case"hour":return 24*e+this._milliseconds/36e5;case"minute":return 24*e*60+this._milliseconds/6e4;case"second":return 24*e*60*60+this._milliseconds/1e3;case"millisecond":return Math.floor(24*e*60*60*1e3)+this._milliseconds;default:throw new Error("Unknown unit "+t)}},lang:Ce.fn.lang,locale:Ce.fn.locale,toIsoString:l("toIsoString() is deprecated. Please use toISOString() instead (notice the capitals)",function(){return this.toISOString()}),toISOString:function(){var t=Math.abs(this.years()),e=Math.abs(this.months()),i=Math.abs(this.days()),s=Math.abs(this.hours()),o=Math.abs(this.minutes()),n=Math.abs(this.seconds()+this.milliseconds()/1e3);return this.asSeconds()?(this.asSeconds()<0?"-":"")+"P"+(t?t+"Y":"")+(e?e+"M":"")+(i?i+"D":"")+(s||o||n?"T":"")+(s?s+"H":"")+(o?o+"M":"")+(n?n+"S":""):"P0D"},localeData:function(){return this._locale},toJSON:function(){return this.toISOString()}}),Ce.duration.fn.toString=Ce.duration.fn.toISOString;for(Oe in fi)a(fi,Oe)&&Se(Oe.toLowerCase());Ce.duration.fn.asMilliseconds=function(){return this.as("ms")},Ce.duration.fn.asSeconds=function(){return this.as("s")},Ce.duration.fn.asMinutes=function(){return this.as("m")},Ce.duration.fn.asHours=function(){return this.as("h")},Ce.duration.fn.asDays=function(){return this.as("d")},Ce.duration.fn.asWeeks=function(){return this.as("weeks")},Ce.duration.fn.asMonths=function(){return this.as("M")},Ce.duration.fn.asYears=function(){return this.as("y")},Ce.locale("en",{ordinalParse:/\d{1,2}(th|st|nd|rd)/,ordinal:function(t){var e=t%10,i=1===I(t%100/10)?"th":1===e?"st":2===e?"nd":3===e?"rd":"th";return t+i}}),We?o.exports=Ce:(s=function(t,e,i){return i.config&&i.config()&&i.config().noGlobal===!0&&(ke.moment=Te),Ce}.call(e,i,e,o),!(s!==n&&(o.exports=s)),De(!0))}).call(this)}).call(e,function(){return this}(),i(71)(t))},function(t,e,i){function s(){this.constants.smoothCurves.enabled=!this.constants.smoothCurves.enabled;var t=document.getElementById("graph_toggleSmooth");t.style.background=1==this.constants.smoothCurves.enabled?"#A4FF56":"#FF8532",this._configureSmoothCurves(!1)}function o(){for(var t in this.calculationNodes)this.calculationNodes.hasOwnProperty(t)&&(this.calculationNodes[t].vx=0,this.calculationNodes[t].vy=0,this.calculationNodes[t].fx=0,this.calculationNodes[t].fy=0);1==this.constants.hierarchicalLayout.enabled?(this._setupHierarchicalLayout(),a.call(this,"graph_H_nd",1,"physics_hierarchicalRepulsion_nodeDistance"),a.call(this,"graph_H_cg",1,"physics_centralGravity"),a.call(this,"graph_H_sc",1,"physics_springConstant"),a.call(this,"graph_H_sl",1,"physics_springLength"),a.call(this,"graph_H_damp",1,"physics_damping")):this.repositionNodes(),this.moving=!0,this.start()}function n(){var t="No options are required, default values used.",e=[],i=document.getElementById("graph_physicsMethod1"),s=document.getElementById("graph_physicsMethod2");if(1==i.checked){if(this.constants.physics.barnesHut.gravitationalConstant!=this.backupConstants.physics.barnesHut.gravitationalConstant&&e.push("gravitationalConstant: "+this.constants.physics.barnesHut.gravitationalConstant),this.constants.physics.centralGravity!=this.backupConstants.physics.barnesHut.centralGravity&&e.push("centralGravity: "+this.constants.physics.centralGravity),this.constants.physics.springLength!=this.backupConstants.physics.barnesHut.springLength&&e.push("springLength: "+this.constants.physics.springLength),this.constants.physics.springConstant!=this.backupConstants.physics.barnesHut.springConstant&&e.push("springConstant: "+this.constants.physics.springConstant),this.constants.physics.damping!=this.backupConstants.physics.barnesHut.damping&&e.push("damping: "+this.constants.physics.damping),0!=e.length){t="var options = {",t+="physics: {barnesHut: {";for(var o=0;othis.constants.clustering.clusterThreshold&&1==this.constants.clustering.enabled&&this.clusterToFit(this.constants.clustering.reduceToNodes,!1),this._calculateForces())},e._calculateForces=function(){this._calculateGravitationalForces(),this._calculateNodeForces(),this.constants.physics.springConstant>0&&(1==this.constants.smoothCurves.enabled&&1==this.constants.smoothCurves.dynamic?this._calculateSpringForcesWithSupport():1==this.constants.physics.hierarchicalRepulsion.enabled?this._calculateHierarchicalSpringForces():this._calculateSpringForces())},e._updateCalculationNodes=function(){if(1==this.constants.smoothCurves.enabled&&1==this.constants.smoothCurves.dynamic){this.calculationNodes={},this.calculationNodeIndices=[];for(var t in this.nodes)this.nodes.hasOwnProperty(t)&&(this.calculationNodes[t]=this.nodes[t]);var e=this.sectors.support.nodes;for(var i in e)e.hasOwnProperty(i)&&(this.edges.hasOwnProperty(e[i].parentEdgeId)?this.calculationNodes[i]=e[i]:e[i]._setForce(0,0));for(var s in this.calculationNodes)this.calculationNodes.hasOwnProperty(s)&&this.calculationNodeIndices.push(s)}else this.calculationNodes=this.nodes,this.calculationNodeIndices=this.nodeIndices},e._calculateGravitationalForces=function(){var t,e,i,s,o,n=this.calculationNodes,r=this.constants.physics.centralGravity,a=0;for(o=0;oSimulation Mode:Barnes HutRepulsionHierarchical
Options:
',this.containerElement.parentElement.insertBefore(this.physicsConfiguration,this.containerElement),this.optionsDiv=document.createElement("div"),this.optionsDiv.style.fontSize="14px",this.optionsDiv.style.fontFamily="verdana",this.containerElement.parentElement.insertBefore(this.optionsDiv,this.containerElement); -var e;e=document.getElementById("graph_BH_gc"),e.onchange=a.bind(this,"graph_BH_gc",-1,"physics_barnesHut_gravitationalConstant"),e=document.getElementById("graph_BH_cg"),e.onchange=a.bind(this,"graph_BH_cg",1,"physics_centralGravity"),e=document.getElementById("graph_BH_sc"),e.onchange=a.bind(this,"graph_BH_sc",1,"physics_springConstant"),e=document.getElementById("graph_BH_sl"),e.onchange=a.bind(this,"graph_BH_sl",1,"physics_springLength"),e=document.getElementById("graph_BH_damp"),e.onchange=a.bind(this,"graph_BH_damp",1,"physics_damping"),e=document.getElementById("graph_R_nd"),e.onchange=a.bind(this,"graph_R_nd",1,"physics_repulsion_nodeDistance"),e=document.getElementById("graph_R_cg"),e.onchange=a.bind(this,"graph_R_cg",1,"physics_centralGravity"),e=document.getElementById("graph_R_sc"),e.onchange=a.bind(this,"graph_R_sc",1,"physics_springConstant"),e=document.getElementById("graph_R_sl"),e.onchange=a.bind(this,"graph_R_sl",1,"physics_springLength"),e=document.getElementById("graph_R_damp"),e.onchange=a.bind(this,"graph_R_damp",1,"physics_damping"),e=document.getElementById("graph_H_nd"),e.onchange=a.bind(this,"graph_H_nd",1,"physics_hierarchicalRepulsion_nodeDistance"),e=document.getElementById("graph_H_cg"),e.onchange=a.bind(this,"graph_H_cg",1,"physics_centralGravity"),e=document.getElementById("graph_H_sc"),e.onchange=a.bind(this,"graph_H_sc",1,"physics_springConstant"),e=document.getElementById("graph_H_sl"),e.onchange=a.bind(this,"graph_H_sl",1,"physics_springLength"),e=document.getElementById("graph_H_damp"),e.onchange=a.bind(this,"graph_H_damp",1,"physics_damping"),e=document.getElementById("graph_H_direction"),e.onchange=a.bind(this,"graph_H_direction",t,"hierarchicalLayout_direction"),e=document.getElementById("graph_H_levsep"),e.onchange=a.bind(this,"graph_H_levsep",1,"hierarchicalLayout_levelSeparation"),e=document.getElementById("graph_H_nspac"),e.onchange=a.bind(this,"graph_H_nspac",1,"hierarchicalLayout_nodeSpacing");var i=document.getElementById("graph_physicsMethod1"),d=document.getElementById("graph_physicsMethod2"),l=document.getElementById("graph_physicsMethod3");d.checked=!0,this.constants.physics.barnesHut.enabled&&(i.checked=!0),this.constants.hierarchicalLayout.enabled&&(l.checked=!0);var c=document.getElementById("graph_toggleSmooth"),p=document.getElementById("graph_repositionNodes"),u=document.getElementById("graph_generateOptions");c.onclick=s.bind(this),p.onclick=o.bind(this),u.onclick=n.bind(this),c.style.background=1==this.constants.smoothCurves&&0==this.constants.dynamicSmoothCurves?"#A4FF56":"#FF8532",r.apply(this),i.onchange=r.bind(this),d.onchange=r.bind(this),l.onchange=r.bind(this)}},e._overWriteGraphConstants=function(t,e){var i=t.split("_");1==i.length?this.constants[i[0]]=e:2==i.length?this.constants[i[0]][i[1]]=e:3==i.length&&(this.constants[i[0]][i[1]][i[2]]=e)}},function(t){function e(t){throw new Error("Cannot find module '"+t+"'.")}e.keys=function(){return[]},e.resolve=e,t.exports=e,e.id=67},function(t,e){e._calculateNodeForces=function(){var t,e,i,s,o,n,r,a,h,d,l,c=this.calculationNodes,p=this.calculationNodeIndices,u=-2/3,m=4/3,f=this.constants.physics.repulsion.nodeDistance,g=f;for(d=0;di&&(r=.5*g>i?1:v*i+m,r*=0==n?1:1+n*this.constants.clustering.forceAmplification,r/=Math.max(i,.01*g),s=t*r,o=e*r,a.fx-=s,a.fy-=o,h.fx+=s,h.fy+=o)}}},function(t,e){e._calculateNodeForces=function(){var t,e,i,s,o,n,r,a,h,d,l=this.calculationNodes,c=this.calculationNodeIndices,p=this.constants.physics.hierarchicalRepulsion.nodeDistance;for(h=0;hi?-Math.pow(u*i,2)+Math.pow(u*p,2):0,0==i?i=.01:n/=i,s=t*n,o=e*n,r.fx-=s,r.fy-=o,a.fx+=s,a.fy+=o}},e._calculateHierarchicalSpringForces=function(){for(var t,e,i,s,o,n,r,a,h,d=this.edges,l=this.calculationNodes,c=this.calculationNodeIndices,p=0;pn;n++)t=e[i[n]],t.options.mass>0&&(this._getForceContribution(o.root.children.NW,t),this._getForceContribution(o.root.children.NE,t),this._getForceContribution(o.root.children.SW,t),this._getForceContribution(o.root.children.SE,t))}},e._getForceContribution=function(t,e){if(t.childrenCount>0){var i,s,o;if(i=t.centerOfMass.x-e.x,s=t.centerOfMass.y-e.y,o=Math.sqrt(i*i+s*s),o*t.calcSize>this.constants.physics.barnesHut.thetaInverted){0==o&&(o=.1*Math.random(),i=o);var n=this.constants.physics.barnesHut.gravitationalConstant*t.mass*e.options.mass/(o*o*o),r=i*n,a=s*n;e.fx+=r,e.fy+=a}else if(4==t.childrenCount)this._getForceContribution(t.children.NW,e),this._getForceContribution(t.children.NE,e),this._getForceContribution(t.children.SW,e),this._getForceContribution(t.children.SE,e);else if(t.children.data.id!=e.id){0==o&&(o=.5*Math.random(),i=o);var n=this.constants.physics.barnesHut.gravitationalConstant*t.mass*e.options.mass/(o*o*o),r=i*n,a=s*n;e.fx+=r,e.fy+=a}}},e._formBarnesHutTree=function(t,e){for(var i,s=e.length,o=Number.MAX_VALUE,n=Number.MAX_VALUE,r=-Number.MAX_VALUE,a=-Number.MAX_VALUE,h=0;s>h;h++){var d=t[e[h]].x,l=t[e[h]].y;t[e[h]].options.mass>0&&(o>d&&(o=d),d>r&&(r=d),n>l&&(n=l),l>a&&(a=l))}var c=Math.abs(r-o)-Math.abs(a-n);c>0?(n-=.5*c,a+=.5*c):(o+=.5*c,r-=.5*c);var p=1e-5,u=Math.max(p,Math.abs(r-o)),m=.5*u,f=.5*(o+r),g=.5*(n+a),v={root:{centerOfMass:{x:0,y:0},mass:0,range:{minX:f-m,maxX:f+m,minY:g-m,maxY:g+m},size:u,calcSize:1/u,children:{data:null},maxWidth:0,level:0,childrenCount:4}};for(this._splitBranch(v.root),h=0;s>h;h++)i=t[e[h]],i.options.mass>0&&this._placeInTree(v.root,i);this.barnesHutTree=v},e._updateBranchMass=function(t,e){var i=t.mass+e.options.mass,s=1/i;t.centerOfMass.x=t.centerOfMass.x*t.mass+e.x*e.options.mass,t.centerOfMass.x*=s,t.centerOfMass.y=t.centerOfMass.y*t.mass+e.y*e.options.mass,t.centerOfMass.y*=s,t.mass=i;var o=Math.max(Math.max(e.height,e.radius),e.width);t.maxWidth=t.maxWidthe.x?t.children.NW.range.maxY>e.y?this._placeInRegion(t,e,"NW"):this._placeInRegion(t,e,"SW"):t.children.NW.range.maxY>e.y?this._placeInRegion(t,e,"NE"):this._placeInRegion(t,e,"SE")},e._placeInRegion=function(t,e,i){switch(t.children[i].childrenCount){case 0:t.children[i].children.data=e,t.children[i].childrenCount=1,this._updateBranchMass(t.children[i],e);break;case 1:t.children[i].children.data.x==e.x&&t.children[i].children.data.y==e.y?(e.x+=Math.random(),e.y+=Math.random()):(this._splitBranch(t.children[i]),this._placeInTree(t.children[i],e));break;case 4:this._placeInTree(t.children[i],e)}},e._splitBranch=function(t){var e=null;1==t.childrenCount&&(e=t.children.data,t.mass=0,t.centerOfMass.x=0,t.centerOfMass.y=0),t.childrenCount=4,t.children.data=null,this._insertRegion(t,"NW"),this._insertRegion(t,"NE"),this._insertRegion(t,"SW"),this._insertRegion(t,"SE"),null!=e&&this._placeInTree(t,e)},e._insertRegion=function(t,e){var i,s,o,n,r=.5*t.size;switch(e){case"NW":i=t.range.minX,s=t.range.minX+r,o=t.range.minY,n=t.range.minY+r;break;case"NE":i=t.range.minX+r,s=t.range.maxX,o=t.range.minY,n=t.range.minY+r;break;case"SW":i=t.range.minX,s=t.range.minX+r,o=t.range.minY+r,n=t.range.maxY;break;case"SE":i=t.range.minX+r,s=t.range.maxX,o=t.range.minY+r,n=t.range.maxY}t.children[e]={centerOfMass:{x:0,y:0},mass:0,range:{minX:i,maxX:s,minY:o,maxY:n},size:.5*t.size,calcSize:2*t.calcSize,children:{data:null},maxWidth:0,level:t.level+1,childrenCount:0}},e._drawTree=function(t,e){void 0!==this.barnesHutTree&&(t.lineWidth=1,this._drawBranch(this.barnesHutTree.root,t,e))},e._drawBranch=function(t,e,i){void 0===i&&(i="#FF0000"),4==t.childrenCount&&(this._drawBranch(t.children.NW,e),this._drawBranch(t.children.NE,e),this._drawBranch(t.children.SE,e),this._drawBranch(t.children.SW,e)),e.strokeStyle=i,e.beginPath(),e.moveTo(t.range.minX,t.range.minY),e.lineTo(t.range.maxX,t.range.minY),e.stroke(),e.beginPath(),e.moveTo(t.range.maxX,t.range.minY),e.lineTo(t.range.maxX,t.range.maxY),e.stroke(),e.beginPath(),e.moveTo(t.range.maxX,t.range.maxY),e.lineTo(t.range.minX,t.range.maxY),e.stroke(),e.beginPath(),e.moveTo(t.range.minX,t.range.maxY),e.lineTo(t.range.minX,t.range.minY),e.stroke()}},function(t){t.exports=function(t){return t.webpackPolyfill||(t.deprecate=function(){},t.paths=[],t.children=[],t.webpackPolyfill=1),t}}])}); +"use strict";!function(t,e){"object"==typeof exports&&"object"==typeof module?module.exports=e():"function"==typeof define&&define.amd?define(e):"object"==typeof exports?exports.vis=e():t.vis=e()}(this,function(){return function(t){function e(s){if(i[s])return i[s].exports;var o=i[s]={exports:{},id:s,loaded:!1};return t[s].call(o.exports,o,o.exports,e),o.loaded=!0,o.exports}var i={};return e.m=t,e.c=i,e.p="",e(0)}([function(t,e,i){e.util=i(1),e.DOMutil=i(2),e.DataSet=i(3),e.DataView=i(4),e.Queue=i(5),e.Graph3d=i(6),e.graph3d={Camera:i(7),Filter:i(8),Point2d:i(9),Point3d:i(10),Slider:i(11),StepNumber:i(12)},e.Timeline=i(13),e.Graph2d=i(14),e.timeline={DateUtil:i(15),DataStep:i(16),Range:i(17),stack:i(18),TimeStep:i(19),components:{items:{Item:i(20),BackgroundItem:i(21),BoxItem:i(22),PointItem:i(23),RangeItem:i(24)},Component:i(25),CurrentTime:i(26),CustomTime:i(27),DataAxis:i(28),GraphGroup:i(29),Group:i(30),BackgroundGroup:i(31),ItemSet:i(32),Legend:i(33),LineGraph:i(34),TimeAxis:i(35)}},e.Network=i(36),e.network={Edge:i(37),Groups:i(38),Images:i(39),Node:i(40),Popup:i(41),dotparser:i(42),gephiParser:i(43)},e.Graph=function(){throw new Error("Graph is renamed to Network. Please create a graph as new vis.Network(...)")},e.moment=i(44),e.hammer=i(45)},function(t,e,i){var s=i(44);e.isNumber=function(t){return t instanceof Number||"number"==typeof t},e.isString=function(t){return t instanceof String||"string"==typeof t},e.isDate=function(t){if(t instanceof Date)return!0;if(e.isString(t)){var i=o.exec(t);if(i)return!0;if(!isNaN(Date.parse(t)))return!0}return!1},e.isDataTable=function(t){return"undefined"!=typeof google&&google.visualization&&google.visualization.DataTable&&t instanceof google.visualization.DataTable},e.randomUUID=function(){var t=function(){return Math.floor(65536*Math.random()).toString(16)};return t()+t()+"-"+t()+"-"+t()+"-"+t()+"-"+t()+t()+t()},e.extend=function(t){for(var e=1,i=arguments.length;i>e;e++){var s=arguments[e];for(var o in s)s.hasOwnProperty(o)&&(t[o]=s[o])}return t},e.selectiveExtend=function(t,e){if(!Array.isArray(t))throw new Error("Array with property names expected as first argument");for(var i=2;ii;i++)if(t[i]!=e[i])return!1;return!0},e.convert=function(t,i){var n;if(void 0===t)return void 0;if(null===t)return null;if(!i)return t;if("string"!=typeof i&&!(i instanceof String))throw new Error("Type must be a string");switch(i){case"boolean":case"Boolean":return Boolean(t);case"number":case"Number":return Number(t.valueOf());case"string":case"String":return String(t);case"Date":if(e.isNumber(t))return new Date(t);if(t instanceof Date)return new Date(t.valueOf());if(s.isMoment(t))return new Date(t.valueOf());if(e.isString(t))return n=o.exec(t),n?new Date(Number(n[1])):s(t).toDate();throw new Error("Cannot convert object of type "+e.getType(t)+" to type Date");case"Moment":if(e.isNumber(t))return s(t);if(t instanceof Date)return s(t.valueOf());if(s.isMoment(t))return s(t);if(e.isString(t))return n=o.exec(t),s(n?Number(n[1]):t);throw new Error("Cannot convert object of type "+e.getType(t)+" to type Date");case"ISODate":if(e.isNumber(t))return new Date(t);if(t instanceof Date)return t.toISOString();if(s.isMoment(t))return t.toDate().toISOString();if(e.isString(t))return n=o.exec(t),n?new Date(Number(n[1])).toISOString():new Date(t).toISOString();throw new Error("Cannot convert object of type "+e.getType(t)+" to type ISODate");case"ASPDate":if(e.isNumber(t))return"/Date("+t+")/";if(t instanceof Date)return"/Date("+t.valueOf()+")/";if(e.isString(t)){n=o.exec(t);var r;return r=n?new Date(Number(n[1])).valueOf():new Date(t).valueOf(),"/Date("+r+")/"}throw new Error("Cannot convert object of type "+e.getType(t)+" to type ASPDate");default:throw new Error('Unknown type "'+i+'"')}};var o=/^\/?Date\((\-?\d+)/i;e.getType=function(t){var e=typeof t;return"object"==e?null==t?"null":t instanceof Boolean?"Boolean":t instanceof Number?"Number":t instanceof String?"String":Array.isArray(t)?"Array":t instanceof Date?"Date":"Object":"number"==e?"Number":"boolean"==e?"Boolean":"string"==e?"String":e},e.getAbsoluteLeft=function(t){return t.getBoundingClientRect().left+window.pageXOffset},e.getAbsoluteTop=function(t){return t.getBoundingClientRect().top+window.pageYOffset},e.addClassName=function(t,e){var i=t.className.split(" ");-1==i.indexOf(e)&&(i.push(e),t.className=i.join(" "))},e.removeClassName=function(t,e){var i=t.className.split(" "),s=i.indexOf(e);-1!=s&&(i.splice(s,1),t.className=i.join(" "))},e.forEach=function(t,e){var i,s;if(Array.isArray(t))for(i=0,s=t.length;s>i;i++)e(t[i],i,t);else for(i in t)t.hasOwnProperty(i)&&e(t[i],i,t)},e.toArray=function(t){var e=[];for(var i in t)t.hasOwnProperty(i)&&e.push(t[i]);return e},e.updateProperty=function(t,e,i){return t[e]!==i?(t[e]=i,!0):!1},e.addEventListener=function(t,e,i,s){t.addEventListener?(void 0===s&&(s=!1),"mousewheel"===e&&navigator.userAgent.indexOf("Firefox")>=0&&(e="DOMMouseScroll"),t.addEventListener(e,i,s)):t.attachEvent("on"+e,i)},e.removeEventListener=function(t,e,i,s){t.removeEventListener?(void 0===s&&(s=!1),"mousewheel"===e&&navigator.userAgent.indexOf("Firefox")>=0&&(e="DOMMouseScroll"),t.removeEventListener(e,i,s)):t.detachEvent("on"+e,i)},e.preventDefault=function(t){t||(t=window.event),t.preventDefault?t.preventDefault():t.returnValue=!1},e.getTarget=function(t){t||(t=window.event);var e;return t.target?e=t.target:t.srcElement&&(e=t.srcElement),void 0!=e.nodeType&&3==e.nodeType&&(e=e.parentNode),e},e.option={},e.option.asBoolean=function(t,e){return"function"==typeof t&&(t=t()),null!=t?0!=t:e||null},e.option.asNumber=function(t,e){return"function"==typeof t&&(t=t()),null!=t?Number(t)||e||null:e||null},e.option.asString=function(t,e){return"function"==typeof t&&(t=t()),null!=t?String(t):e||null},e.option.asSize=function(t,i){return"function"==typeof t&&(t=t()),e.isString(t)?t:e.isNumber(t)?t+"px":i||null},e.option.asElement=function(t,e){return"function"==typeof t&&(t=t()),t||e||null},e.hexToRGB=function(t){var e=/^#?([a-f\d])([a-f\d])([a-f\d])$/i;t=t.replace(e,function(t,e,i,s){return e+e+i+i+s+s});var i=/^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(t);return i?{r:parseInt(i[1],16),g:parseInt(i[2],16),b:parseInt(i[3],16)}:null},e.RGBToHex=function(t,e,i){return"#"+((1<<24)+(t<<16)+(e<<8)+i).toString(16).slice(1)},e.parseColor=function(t){var i;if(e.isString(t)){if(e.isValidRGB(t)){var s=t.substr(4).substr(0,t.length-5).split(",");t=e.RGBToHex(s[0],s[1],s[2])}if(e.isValidHex(t)){var o=e.hexToHSV(t),n={h:o.h,s:.45*o.s,v:Math.min(1,1.05*o.v)},r={h:o.h,s:Math.min(1,1.25*o.v),v:.6*o.v},a=e.HSVToHex(r.h,r.h,r.v),h=e.HSVToHex(n.h,n.s,n.v);i={background:t,border:a,highlight:{background:h,border:a},hover:{background:h,border:a}}}else i={background:t,border:t,highlight:{background:t,border:t},hover:{background:t,border:t}}}else i={},i.background=t.background||"white",i.border=t.border||i.background,e.isString(t.highlight)?i.highlight={border:t.highlight,background:t.highlight}:(i.highlight={},i.highlight.background=t.highlight&&t.highlight.background||i.background,i.highlight.border=t.highlight&&t.highlight.border||i.border),e.isString(t.hover)?i.hover={border:t.hover,background:t.hover}:(i.hover={},i.hover.background=t.hover&&t.hover.background||i.background,i.hover.border=t.hover&&t.hover.border||i.border);return i},e.RGBToHSV=function(t,e,i){t/=255,e/=255,i/=255;var s=Math.min(t,Math.min(e,i)),o=Math.max(t,Math.max(e,i));if(s==o)return{h:0,s:0,v:s};var n=t==s?e-i:i==s?t-e:i-t,r=t==s?3:i==s?1:5,a=60*(r-n/(o-s))/360,h=(o-s)/o,d=o;return{h:a,s:h,v:d}};var n={split:function(t){var e={};return t.split(";").forEach(function(t){if(""!=t.trim()){var i=t.split(":"),s=i[0].trim(),o=i[1].trim();e[s]=o}}),e},join:function(t){return Object.keys(t).map(function(e){return e+": "+t[e]}).join("; ")}};e.addCssText=function(t,i){var s=n.split(t.style.cssText),o=n.split(i),r=e.extend(s,o);t.style.cssText=n.join(r)},e.removeCssText=function(t,e){var i=n.split(t.style.cssText),s=n.split(e);for(var o in s)s.hasOwnProperty(o)&&delete i[o];t.style.cssText=n.join(i)},e.HSVToRGB=function(t,e,i){var s,o,n,r=Math.floor(6*t),a=6*t-r,h=i*(1-e),d=i*(1-a*e),l=i*(1-(1-a)*e);switch(r%6){case 0:s=i,o=l,n=h;break;case 1:s=d,o=i,n=h;break;case 2:s=h,o=i,n=l;break;case 3:s=h,o=d,n=i;break;case 4:s=l,o=h,n=i;break;case 5:s=i,o=h,n=d}return{r:Math.floor(255*s),g:Math.floor(255*o),b:Math.floor(255*n)}},e.HSVToHex=function(t,i,s){var o=e.HSVToRGB(t,i,s);return e.RGBToHex(o.r,o.g,o.b)},e.hexToHSV=function(t){var i=e.hexToRGB(t);return e.RGBToHSV(i.r,i.g,i.b)},e.isValidHex=function(t){var e=/(^#[0-9A-F]{6}$)|(^#[0-9A-F]{3}$)/i.test(t);return e},e.isValidRGB=function(t){t=t.replace(" ","");var e=/rgb\((\d{1,3}),(\d{1,3}),(\d{1,3})\)/i.test(t);return e},e.selectiveBridgeObject=function(t,i){if("object"==typeof i){for(var s=Object.create(i),o=0;o=r&&o>n;){var h=Math.floor((r+a)/2),d=t[h],l=void 0===s?d[i]:d[i][s],c=e(l);if(0==c)return h;-1==c?r=h+1:a=h-1,n++}return-1},e.binarySearchValue=function(t,e,i,s){for(var o,n,r,a,h=1e4,d=0,l=0,c=t.length-1;c>=l&&h>d;){if(a=Math.floor(.5*(c+l)),o=t[Math.max(0,a-1)][i],n=t[a][i],r=t[Math.min(t.length-1,a+1)][i],n==e)return a;if(e>o&&n>e)return"before"==s?Math.max(0,a-1):a;if(e>n&&r>e)return"before"==s?a:Math.min(t.length-1,a+1);e>n?l=a+1:c=a-1,d++}return-1},e.easeInOutQuad=function(t,e,i,s){var o=i-e;return t/=s/2,1>t?o/2*t*t+e:(t--,-o/2*(t*(t-2)-1)+e)},e.easingFunctions={linear:function(t){return t},easeInQuad:function(t){return t*t},easeOutQuad:function(t){return t*(2-t)},easeInOutQuad:function(t){return.5>t?2*t*t:-1+(4-2*t)*t},easeInCubic:function(t){return t*t*t},easeOutCubic:function(t){return--t*t*t+1},easeInOutCubic:function(t){return.5>t?4*t*t*t:(t-1)*(2*t-2)*(2*t-2)+1},easeInQuart:function(t){return t*t*t*t},easeOutQuart:function(t){return 1- --t*t*t*t},easeInOutQuart:function(t){return.5>t?8*t*t*t*t:1-8*--t*t*t*t},easeInQuint:function(t){return t*t*t*t*t},easeOutQuint:function(t){return 1+--t*t*t*t*t},easeInOutQuint:function(t){return.5>t?16*t*t*t*t*t:1+16*--t*t*t*t*t}}},function(t,e){e.prepareElements=function(t){for(var e in t)t.hasOwnProperty(e)&&(t[e].redundant=t[e].used,t[e].used=[])},e.cleanupElements=function(t){for(var e in t)if(t.hasOwnProperty(e)&&t[e].redundant){for(var i=0;i0?(s=e[t].redundant[0],e[t].redundant.shift()):(s=document.createElementNS("http://www.w3.org/2000/svg",t),i.appendChild(s)):(s=document.createElementNS("http://www.w3.org/2000/svg",t),e[t]={used:[],redundant:[]},i.appendChild(s)),e[t].used.push(s),s},e.getDOMElement=function(t,e,i,s){var o;return e.hasOwnProperty(t)?e[t].redundant.length>0?(o=e[t].redundant[0],e[t].redundant.shift()):(o=document.createElement(t),void 0!==s?i.insertBefore(o,s):i.appendChild(o)):(o=document.createElement(t),e[t]={used:[],redundant:[]},void 0!==s?i.insertBefore(o,s):i.appendChild(o)),e[t].used.push(o),o},e.drawPoint=function(t,i,s,o,n){var r;return"circle"==s.options.drawPoints.style?(r=e.getSVGElement("circle",o,n),r.setAttributeNS(null,"cx",t),r.setAttributeNS(null,"cy",i),r.setAttributeNS(null,"r",.5*s.options.drawPoints.size)):(r=e.getSVGElement("rect",o,n),r.setAttributeNS(null,"x",t-.5*s.options.drawPoints.size),r.setAttributeNS(null,"y",i-.5*s.options.drawPoints.size),r.setAttributeNS(null,"width",s.options.drawPoints.size),r.setAttributeNS(null,"height",s.options.drawPoints.size)),void 0!==s.options.drawPoints.styles&&r.setAttributeNS(null,"style",s.group.options.drawPoints.styles),r.setAttributeNS(null,"class",s.className+" point"),r},e.drawBar=function(t,i,s,o,n,r,a){if(0!=o){0>o&&(o*=-1,i-=o);var h=e.getSVGElement("rect",r,a);h.setAttributeNS(null,"x",t-.5*s),h.setAttributeNS(null,"y",i),h.setAttributeNS(null,"width",s),h.setAttributeNS(null,"height",o),h.setAttributeNS(null,"class",n)}}},function(t,e,i){function s(t,e){if(!t||Array.isArray(t)||o.isDataTable(t)||(e=t,t=null),this._options=e||{},this._data={},this.length=0,this._fieldId=this._options.fieldId||"id",this._type={},this._options.type)for(var i in this._options.type)if(this._options.type.hasOwnProperty(i)){var s=this._options.type[i];this._type[i]="Date"==s||"ISODate"==s||"ASPDate"==s?"Date":s}if(this._options.convert)throw new Error('Option "convert" is deprecated. Use "type" instead.');this._subscribers={},t&&this.add(t),this.setOptions(e)}var o=i(1),n=i(5);s.prototype.setOptions=function(t){t&&void 0!==t.queue&&(t.queue===!1?this._queue&&(this._queue.destroy(),delete this._queue):(this._queue||(this._queue=n.extend(this,{replace:["add","update","remove"]})),"object"==typeof t.queue&&this._queue.setOptions(t.queue)))},s.prototype.on=function(t,e){var i=this._subscribers[t];i||(i=[],this._subscribers[t]=i),i.push({callback:e})},s.prototype.subscribe=s.prototype.on,s.prototype.off=function(t,e){var i=this._subscribers[t];i&&(this._subscribers[t]=i.filter(function(t){return t.callback!=e}))},s.prototype.unsubscribe=s.prototype.off,s.prototype._trigger=function(t,e,i){if("*"==t)throw new Error("Cannot trigger event *");var s=[];t in this._subscribers&&(s=s.concat(this._subscribers[t])),"*"in this._subscribers&&(s=s.concat(this._subscribers["*"]));for(var o=0;or;r++)i=n._addItem(t[r]),s.push(i);else if(o.isDataTable(t))for(var h=this._getColumnNames(t),d=0,l=t.getNumberOfRows();l>d;d++){for(var c={},p=0,u=h.length;u>p;p++){var m=h[p];c[m]=t.getValue(d,p)}i=n._addItem(c),s.push(i)}else{if(!(t instanceof Object))throw new Error("Unknown dataType");i=n._addItem(t),s.push(i)}return s.length&&this._trigger("add",{items:s},e),s},s.prototype.update=function(t,e){var i=[],s=[],n=[],r=this,a=r._fieldId,h=function(t){var e=t[a];r._data[e]?(e=r._updateItem(t),s.push(e),n.push(t)):(e=r._addItem(t),i.push(e))};if(Array.isArray(t))for(var d=0,l=t.length;l>d;d++)h(t[d]);else if(o.isDataTable(t))for(var c=this._getColumnNames(t),p=0,u=t.getNumberOfRows();u>p;p++){for(var m={},f=0,g=c.length;g>f;f++){var v=c[f];m[v]=t.getValue(p,f)}h(m)}else{if(!(t instanceof Object))throw new Error("Unknown dataType");h(t)}return i.length&&this._trigger("add",{items:i},e),s.length&&this._trigger("update",{items:s,data:n},e),i.concat(s)},s.prototype.get=function(){var t,e,i,s,n=this,r=o.getType(arguments[0]);"String"==r||"Number"==r?(t=arguments[0],i=arguments[1],s=arguments[2]):"Array"==r?(e=arguments[0],i=arguments[1],s=arguments[2]):(i=arguments[0],s=arguments[1]);var a;if(i&&i.returnType){var h=["DataTable","Array","Object"];if(a=-1==h.indexOf(i.returnType)?"Array":i.returnType,s&&a!=o.getType(s))throw new Error('Type of parameter "data" ('+o.getType(s)+") does not correspond with specified options.type ("+i.type+")");if("DataTable"==a&&!o.isDataTable(s))throw new Error('Parameter "data" must be a DataTable when options.type is "DataTable"')}else a=s&&"DataTable"==o.getType(s)?"DataTable":"Array";var d,l,c,p,u=i&&i.type||this._options.type,m=i&&i.filter,f=[];if(void 0!=t)d=n._getItem(t,u),m&&!m(d)&&(d=null);else if(void 0!=e)for(c=0,p=e.length;p>c;c++)d=n._getItem(e[c],u),(!m||m(d))&&f.push(d);else for(l in this._data)this._data.hasOwnProperty(l)&&(d=n._getItem(l,u),(!m||m(d))&&f.push(d));if(i&&i.order&&void 0==t&&this._sort(f,i.order),i&&i.fields){var g=i.fields;if(void 0!=t)d=this._filterFields(d,g);else for(c=0,p=f.length;p>c;c++)f[c]=this._filterFields(f[c],g)}if("DataTable"==a){var v=this._getColumnNames(s);if(void 0!=t)n._appendRow(s,v,d);else for(c=0;cc;c++)s.push(f[c]);return s}return f},s.prototype.getIds=function(t){var e,i,s,o,n,r=this._data,a=t&&t.filter,h=t&&t.order,d=t&&t.type||this._options.type,l=[];if(a)if(h){n=[];for(s in r)r.hasOwnProperty(s)&&(o=this._getItem(s,d),a(o)&&n.push(o));for(this._sort(n,h),e=0,i=n.length;i>e;e++)l[e]=n[e][this._fieldId]}else for(s in r)r.hasOwnProperty(s)&&(o=this._getItem(s,d),a(o)&&l.push(o[this._fieldId]));else if(h){n=[];for(s in r)r.hasOwnProperty(s)&&n.push(r[s]);for(this._sort(n,h),e=0,i=n.length;i>e;e++)l[e]=n[e][this._fieldId]}else for(s in r)r.hasOwnProperty(s)&&(o=r[s],l.push(o[this._fieldId]));return l},s.prototype.getDataSet=function(){return this},s.prototype.forEach=function(t,e){var i,s,o=e&&e.filter,n=e&&e.type||this._options.type,r=this._data;if(e&&e.order)for(var a=this.get(e),h=0,d=a.length;d>h;h++)i=a[h],s=i[this._fieldId],t(i,s);else for(s in r)r.hasOwnProperty(s)&&(i=this._getItem(s,n),(!o||o(i))&&t(i,s))},s.prototype.map=function(t,e){var i,s=e&&e.filter,o=e&&e.type||this._options.type,n=[],r=this._data;for(var a in r)r.hasOwnProperty(a)&&(i=this._getItem(a,o),(!s||s(i))&&n.push(t(i,a)));return e&&e.order&&this._sort(n,e.order),n},s.prototype._filterFields=function(t,e){if(!t)return t;var i={};for(var s in t)t.hasOwnProperty(s)&&-1!=e.indexOf(s)&&(i[s]=t[s]);return i},s.prototype._sort=function(t,e){if(o.isString(e)){var i=e;t.sort(function(t,e){var s=t[i],o=e[i];return s>o?1:o>s?-1:0})}else{if("function"!=typeof e)throw new TypeError("Order must be a function or a string");t.sort(e)}},s.prototype.remove=function(t,e){var i,s,o,n=[];if(Array.isArray(t))for(i=0,s=t.length;s>i;i++)o=this._remove(t[i]),null!=o&&n.push(o);else o=this._remove(t),null!=o&&n.push(o);return n.length&&this._trigger("remove",{items:n},e),n},s.prototype._remove=function(t){if(o.isNumber(t)||o.isString(t)){if(this._data[t])return delete this._data[t],this.length--,t}else if(t instanceof Object){var e=t[this._fieldId];if(e&&this._data[e])return delete this._data[e],this.length--,e}return null},s.prototype.clear=function(t){var e=Object.keys(this._data);return this._data={},this.length=0,this._trigger("remove",{items:e},t),e},s.prototype.max=function(t){var e=this._data,i=null,s=null;for(var o in e)if(e.hasOwnProperty(o)){var n=e[o],r=n[t];null!=r&&(!i||r>s)&&(i=n,s=r)}return i},s.prototype.min=function(t){var e=this._data,i=null,s=null;for(var o in e)if(e.hasOwnProperty(o)){var n=e[o],r=n[t];null!=r&&(!i||s>r)&&(i=n,s=r)}return i},s.prototype.distinct=function(t){var e,i=this._data,s=[],n=this._options.type&&this._options.type[t]||null,r=0;for(var a in i)if(i.hasOwnProperty(a)){var h=i[a],d=h[t],l=!1;for(e=0;r>e;e++)if(s[e]==d){l=!0;break}l||void 0===d||(s[r]=d,r++)}if(n)for(e=0;ei;i++)e[i]=t.getColumnId(i)||t.getColumnLabel(i);return e},s.prototype._appendRow=function(t,e,i){for(var s=t.addRow(),o=0,n=e.length;n>o;o++){var r=e[o];t.setValue(s,o,i[r])}},t.exports=s},function(t,e,i){function s(t,e){this._data=null,this._ids={},this.length=0,this._options=e||{},this._fieldId="id",this._subscribers={};var i=this;this.listener=function(){i._onEvent.apply(i,arguments)},this.setData(t)}var o=i(1),n=i(3);s.prototype.setData=function(t){var e,i,s;if(this._data){this._data.unsubscribe&&this._data.unsubscribe("*",this.listener),e=[];for(var o in this._ids)this._ids.hasOwnProperty(o)&&e.push(o);this._ids={},this.length=0,this._trigger("remove",{items:e})}if(this._data=t,this._data){for(this._fieldId=this._options.fieldId||this._data&&this._data.options&&this._data.options.fieldId||"id",e=this._data.getIds({filter:this._options&&this._options.filter}),i=0,s=e.length;s>i;i++)o=e[i],this._ids[o]=!0;this.length=e.length,this._trigger("add",{items:e}),this._data.on&&this._data.on("*",this.listener)}},s.prototype.refresh=function(){for(var t,e=this._data.getIds({filter:this._options&&this._options.filter}),i={},s=[],o=[],n=0;ns;s++)n=a[s],r=this.get(n),r&&(this._ids[n]=!0,d.push(n));break;case"update":for(s=0,o=a.length;o>s;s++)n=a[s],r=this.get(n),r?this._ids[n]?l.push(n):(this._ids[n]=!0,d.push(n)):this._ids[n]&&(delete this._ids[n],c.push(n));break;case"remove":for(s=0,o=a.length;o>s;s++)n=a[s],this._ids[n]&&(delete this._ids[n],c.push(n))}this.length+=d.length-c.length,d.length&&this._trigger("add",{items:d},i),l.length&&this._trigger("update",{items:l},i),c.length&&this._trigger("remove",{items:c},i)}},s.prototype.on=n.prototype.on,s.prototype.off=n.prototype.off,s.prototype._trigger=n.prototype._trigger,s.prototype.subscribe=s.prototype.on,s.prototype.unsubscribe=s.prototype.off,t.exports=s},function(t){function e(t){this.delay=null,this.max=1/0,this._queue=[],this._timeout=null,this._extended=null,this.setOptions(t)}e.prototype.setOptions=function(t){t&&"undefined"!=typeof t.delay&&(this.delay=t.delay),t&&"undefined"!=typeof t.max&&(this.max=t.max),this._flushIfNeeded()},e.extend=function(t,i){var s=new e(i);if(void 0!==t.flush)throw new Error("Target object already has a property flush");t.flush=function(){s.flush()};var o=[{name:"flush",original:void 0}];if(i&&i.replace)for(var n=0;nthis.max&&this.flush(),clearTimeout(this._timeout),this.queue.length>0&&"number"==typeof this.delay){var t=this;this._timeout=setTimeout(function(){t.flush()},this.delay)}},e.prototype.flush=function(){for(;this._queue.length>0;){var t=this._queue.shift();t.fn.apply(t.context||t.fn,t.args||[])}},t.exports=e},function(t,e,i){function s(t,e,i){if(!(this instanceof s))throw new SyntaxError("Constructor must be called with the new operator");this.containerElement=t,this.width="400px",this.height="400px",this.margin=10,this.defaultXCenter="55%",this.defaultYCenter="50%",this.xLabel="x",this.yLabel="y",this.zLabel="z";var o=function(t){return t};this.xValueLabel=o,this.yValueLabel=o,this.zValueLabel=o,this.filterLabel="time",this.legendLabel="value",this.style=s.STYLE.DOT,this.showPerspective=!0,this.showGrid=!0,this.keepAspectRatio=!0,this.showShadow=!1,this.showGrayBottom=!1,this.showTooltip=!1,this.verticalRatio=.5,this.animationInterval=1e3,this.animationPreload=!1,this.camera=new p,this.eye=new l(0,0,-1),this.dataTable=null,this.dataPoints=null,this.colX=void 0,this.colY=void 0,this.colZ=void 0,this.colValue=void 0,this.colFilter=void 0,this.xMin=0,this.xStep=void 0,this.xMax=1,this.yMin=0,this.yStep=void 0,this.yMax=1,this.zMin=0,this.zStep=void 0,this.zMax=1,this.valueMin=0,this.valueMax=1,this.xBarWidth=1,this.yBarWidth=1,this.colorAxis="#4D4D4D",this.colorGrid="#D3D3D3",this.colorDot="#7DC1FF",this.colorDotBorder="#3267D2",this.create(),this.setOptions(i),e&&this.setData(e)}function o(t){return"clientX"in t?t.clientX:t.targetTouches[0]&&t.targetTouches[0].clientX||0}function n(t){return"clientY"in t?t.clientY:t.targetTouches[0]&&t.targetTouches[0].clientY||0}var r=i(56),a=i(3),h=i(4),d=i(1),l=i(10),c=i(9),p=i(7),u=i(8),m=i(11),f=i(12);r(s.prototype),s.prototype._setScale=function(){this.scale=new l(1/(this.xMax-this.xMin),1/(this.yMax-this.yMin),1/(this.zMax-this.zMin)),this.keepAspectRatio&&(this.scale.x3&&(this.colFilter=3);else{if(this.style!==s.STYLE.DOTCOLOR&&this.style!==s.STYLE.DOTSIZE&&this.style!==s.STYLE.BARCOLOR&&this.style!==s.STYLE.BARSIZE)throw'Unknown style "'+this.style+'"';this.colX=0,this.colY=1,this.colZ=2,this.colValue=3,t.getNumberOfColumns()>4&&(this.colFilter=4)}},s.prototype.getNumberOfRows=function(t){return t.length},s.prototype.getNumberOfColumns=function(t){var e=0;for(var i in t[0])t[0].hasOwnProperty(i)&&e++;return e},s.prototype.getDistinctValues=function(t,e){for(var i=[],s=0;st[s][e]&&(i.min=t[s][e]),i.maxt;t++){var m=(t-p)/(u-p),g=240*m,v=this._hsv2rgb(g,1,1);c.strokeStyle=v,c.beginPath(),c.moveTo(h,r+t),c.lineTo(a,r+t),c.stroke()}c.strokeStyle=this.colorAxis,c.strokeRect(h,r,i,n)}if(this.style===s.STYLE.DOTSIZE&&(c.strokeStyle=this.colorAxis,c.fillStyle=this.colorDot,c.beginPath(),c.moveTo(h,r),c.lineTo(a,r),c.lineTo(a-i+e,d),c.lineTo(h,d),c.closePath(),c.fill(),c.stroke()),this.style===s.STYLE.DOTCOLOR||this.style===s.STYLE.DOTSIZE){var y=5,b=new f(this.valueMin,this.valueMax,(this.valueMax-this.valueMin)/5,!0);for(b.start(),b.getCurrent()0?this.yMin:this.yMax,o=this._convert3Dto2D(new l(x,r,this.zMin)),Math.cos(2*_)>0?(g.textAlign="center",g.textBaseline="top",o.y+=b):Math.sin(2*_)<0?(g.textAlign="right",g.textBaseline="middle"):(g.textAlign="left",g.textBaseline="middle"),g.fillStyle=this.colorAxis,g.fillText(" "+this.xValueLabel(i.getCurrent())+" ",o.x,o.y),i.next()}for(g.lineWidth=1,s=void 0===this.defaultYStep,i=new f(this.yMin,this.yMax,this.yStep,s),i.start(),i.getCurrent()0?this.xMin:this.xMax,o=this._convert3Dto2D(new l(n,i.getCurrent(),this.zMin)),Math.cos(2*_)<0?(g.textAlign="center",g.textBaseline="top",o.y+=b):Math.sin(2*_)>0?(g.textAlign="right",g.textBaseline="middle"):(g.textAlign="left",g.textBaseline="middle"),g.fillStyle=this.colorAxis,g.fillText(" "+this.yValueLabel(i.getCurrent())+" ",o.x,o.y),i.next();for(g.lineWidth=1,s=void 0===this.defaultZStep,i=new f(this.zMin,this.zMax,this.zStep,s),i.start(),i.getCurrent()0?this.xMin:this.xMax,r=Math.sin(_)<0?this.yMin:this.yMax;!i.end();)t=this._convert3Dto2D(new l(n,r,i.getCurrent())),g.strokeStyle=this.colorAxis,g.beginPath(),g.moveTo(t.x,t.y),g.lineTo(t.x-b,t.y),g.stroke(),g.textAlign="right",g.textBaseline="middle",g.fillStyle=this.colorAxis,g.fillText(this.zValueLabel(i.getCurrent())+" ",t.x-5,t.y),i.next();g.lineWidth=1,t=this._convert3Dto2D(new l(n,r,this.zMin)),e=this._convert3Dto2D(new l(n,r,this.zMax)),g.strokeStyle=this.colorAxis,g.beginPath(),g.moveTo(t.x,t.y),g.lineTo(e.x,e.y),g.stroke(),g.lineWidth=1,p=this._convert3Dto2D(new l(this.xMin,this.yMin,this.zMin)),u=this._convert3Dto2D(new l(this.xMax,this.yMin,this.zMin)),g.strokeStyle=this.colorAxis,g.beginPath(),g.moveTo(p.x,p.y),g.lineTo(u.x,u.y),g.stroke(),p=this._convert3Dto2D(new l(this.xMin,this.yMax,this.zMin)),u=this._convert3Dto2D(new l(this.xMax,this.yMax,this.zMin)),g.strokeStyle=this.colorAxis,g.beginPath(),g.moveTo(p.x,p.y),g.lineTo(u.x,u.y),g.stroke(),g.lineWidth=1,t=this._convert3Dto2D(new l(this.xMin,this.yMin,this.zMin)),e=this._convert3Dto2D(new l(this.xMin,this.yMax,this.zMin)),g.strokeStyle=this.colorAxis,g.beginPath(),g.moveTo(t.x,t.y),g.lineTo(e.x,e.y),g.stroke(),t=this._convert3Dto2D(new l(this.xMax,this.yMin,this.zMin)),e=this._convert3Dto2D(new l(this.xMax,this.yMax,this.zMin)),g.strokeStyle=this.colorAxis,g.beginPath(),g.moveTo(t.x,t.y),g.lineTo(e.x,e.y),g.stroke();var w=this.xLabel;w.length>0&&(c=.1/this.scale.y,n=(this.xMin+this.xMax)/2,r=Math.cos(_)>0?this.yMin-c:this.yMax+c,o=this._convert3Dto2D(new l(n,r,this.zMin)),Math.cos(2*_)>0?(g.textAlign="center",g.textBaseline="top"):Math.sin(2*_)<0?(g.textAlign="right",g.textBaseline="middle"):(g.textAlign="left",g.textBaseline="middle"),g.fillStyle=this.colorAxis,g.fillText(w,o.x,o.y));var M=this.yLabel;M.length>0&&(d=.1/this.scale.x,n=Math.sin(_)>0?this.xMin-d:this.xMax+d,r=(this.yMin+this.yMax)/2,o=this._convert3Dto2D(new l(n,r,this.zMin)),Math.cos(2*_)<0?(g.textAlign="center",g.textBaseline="top"):Math.sin(2*_)>0?(g.textAlign="right",g.textBaseline="middle"):(g.textAlign="left",g.textBaseline="middle"),g.fillStyle=this.colorAxis,g.fillText(M,o.x,o.y));var S=this.zLabel;S.length>0&&(h=30,n=Math.cos(_)>0?this.xMin:this.xMax,r=Math.sin(_)<0?this.yMin:this.yMax,a=(this.zMin+this.zMax)/2,o=this._convert3Dto2D(new l(n,r,a)),g.textAlign="right",g.textBaseline="middle",g.fillStyle=this.colorAxis,g.fillText(S,o.x-h,o.y))},s.prototype._hsv2rgb=function(t,e,i){var s,o,n,r,a,h;switch(r=i*e,a=Math.floor(t/60),h=r*(1-Math.abs(t/60%2-1)),a){case 0:s=r,o=h,n=0;break;case 1:s=h,o=r,n=0;break;case 2:s=0,o=r,n=h;break;case 3:s=0,o=h,n=r;break;case 4:s=h,o=0,n=r;break;case 5:s=r,o=0,n=h;break;default:s=0,o=0,n=0}return"RGB("+parseInt(255*s)+","+parseInt(255*o)+","+parseInt(255*n)+")"},s.prototype._redrawDataGrid=function(){var t,e,i,o,n,r,a,h,d,c,p,u,m,f=this.frame.canvas,g=f.getContext("2d");if(!(void 0===this.dataPoints||this.dataPoints.length<=0)){for(n=0;n0}else r=!0;r?(m=(t.point.z+e.point.z+i.point.z+o.point.z)/4,c=240*(1-(m-this.zMin)*this.scale.z/this.verticalRatio),p=1,this.showShadow?(u=Math.min(1+M.x/S/2,1),a=this._hsv2rgb(c,p,u),h=a):(u=1,a=this._hsv2rgb(c,p,u),h=this.colorAxis)):(a="gray",h=this.colorAxis),d=.5,g.lineWidth=d,g.fillStyle=a,g.strokeStyle=h,g.beginPath(),g.moveTo(t.screen.x,t.screen.y),g.lineTo(e.screen.x,e.screen.y),g.lineTo(o.screen.x,o.screen.y),g.lineTo(i.screen.x,i.screen.y),g.closePath(),g.fill(),g.stroke()}}else for(n=0;np&&(p=0);var u,m,f;this.style===s.STYLE.DOTCOLOR?(u=240*(1-(d.point.value-this.valueMin)*this.scale.value),m=this._hsv2rgb(u,1,1),f=this._hsv2rgb(u,1,.8)):this.style===s.STYLE.DOTSIZE?(m=this.colorDot,f=this.colorDotBorder):(u=240*(1-(d.point.z-this.zMin)*this.scale.z/this.verticalRatio),m=this._hsv2rgb(u,1,1),f=this._hsv2rgb(u,1,.8)),i.lineWidth=1,i.strokeStyle=f,i.fillStyle=m,i.beginPath(),i.arc(d.screen.x,d.screen.y,p,0,2*Math.PI,!0),i.fill(),i.stroke()}}},s.prototype._redrawDataBar=function(){var t,e,i,o,n=this.frame.canvas,r=n.getContext("2d");if(!(void 0===this.dataPoints||this.dataPoints.length<=0)){for(t=0;t0&&(t=this.dataPoints[0],s.lineWidth=1,s.strokeStyle="blue",s.beginPath(),s.moveTo(t.screen.x,t.screen.y)),e=1;e0&&s.stroke()}},s.prototype._onMouseDown=function(t){if(t=t||window.event,this.leftButtonDown&&this._onMouseUp(t),this.leftButtonDown=t.which?1===t.which:1===t.button,this.leftButtonDown||this.touchDown){this.startMouseX=o(t),this.startMouseY=n(t),this.startStart=new Date(this.start),this.startEnd=new Date(this.end),this.startArmRotation=this.camera.getArmRotation(),this.frame.style.cursor="move";var e=this;this.onmousemove=function(t){e._onMouseMove(t)},this.onmouseup=function(t){e._onMouseUp(t)},d.addEventListener(document,"mousemove",e.onmousemove),d.addEventListener(document,"mouseup",e.onmouseup),d.preventDefault(t)}},s.prototype._onMouseMove=function(t){t=t||window.event;var e=parseFloat(o(t))-this.startMouseX,i=parseFloat(n(t))-this.startMouseY,s=this.startArmRotation.horizontal+e/200,r=this.startArmRotation.vertical+i/200,a=4,h=Math.sin(a/360*2*Math.PI);Math.abs(Math.sin(s))0?1:0>t?-1:0}var s=e[0],o=e[1],n=e[2],r=i((o.x-s.x)*(t.y-s.y)-(o.y-s.y)*(t.x-s.x)),a=i((n.x-o.x)*(t.y-o.y)-(n.y-o.y)*(t.x-o.x)),h=i((s.x-n.x)*(t.y-n.y)-(s.y-n.y)*(t.x-n.x));return!(0!=r&&0!=a&&r!=a||0!=a&&0!=h&&a!=h||0!=r&&0!=h&&r!=h)},s.prototype._dataPointFromXY=function(t,e){var i,o=100,n=null,r=null,a=null,h=new c(t,e);if(this.style===s.STYLE.BAR||this.style===s.STYLE.BARCOLOR||this.style===s.STYLE.BARSIZE)for(i=this.dataPoints.length-1;i>=0;i--){n=this.dataPoints[i];var d=n.surfaces;if(d)for(var l=d.length-1;l>=0;l--){var p=d[l],u=p.corners,m=[u[0].screen,u[1].screen,u[2].screen],f=[u[2].screen,u[3].screen,u[0].screen];if(this._insideTriangle(h,m)||this._insideTriangle(h,f))return n}}else for(i=0;ib)&&o>b&&(a=b,r=n)}}return r},s.prototype._showTooltip=function(t){var e,i,s;this.tooltip?(e=this.tooltip.dom.content,i=this.tooltip.dom.line,s=this.tooltip.dom.dot):(e=document.createElement("div"),e.style.position="absolute",e.style.padding="10px",e.style.border="1px solid #4d4d4d",e.style.color="#1a1a1a",e.style.background="rgba(255,255,255,0.7)",e.style.borderRadius="2px",e.style.boxShadow="5px 5px 10px rgba(128,128,128,0.5)",i=document.createElement("div"),i.style.position="absolute",i.style.height="40px",i.style.width="0",i.style.borderLeft="1px solid #4d4d4d",s=document.createElement("div"),s.style.position="absolute",s.style.height="0",s.style.width="0",s.style.border="5px solid #4d4d4d",s.style.borderRadius="5px",this.tooltip={dataPoint:null,dom:{content:e,line:i,dot:s}}),this._hideTooltip(),this.tooltip.dataPoint=t,e.innerHTML="function"==typeof this.showTooltip?this.showTooltip(t.point):"
x:"+t.point.x+"
y:"+t.point.y+"
z:"+t.point.z+"
",e.style.left="0",e.style.top="0",this.frame.appendChild(e),this.frame.appendChild(i),this.frame.appendChild(s);var o=e.offsetWidth,n=e.offsetHeight,r=i.offsetHeight,a=s.offsetWidth,h=s.offsetHeight,d=t.screen.x-o/2;d=Math.min(Math.max(d,10),this.frame.clientWidth-10-o),i.style.left=t.screen.x+"px",i.style.top=t.screen.y-r+"px",e.style.left=d+"px",e.style.top=t.screen.y-r-n+"px",s.style.left=t.screen.x-a/2+"px",s.style.top=t.screen.y-h/2+"px"},s.prototype._hideTooltip=function(){if(this.tooltip){this.tooltip.dataPoint=null;for(var t in this.tooltip.dom)if(this.tooltip.dom.hasOwnProperty(t)){var e=this.tooltip.dom[t];e&&e.parentNode&&e.parentNode.removeChild(e)}}},t.exports=s},function(t,e,i){function s(){this.armLocation=new o,this.armRotation={},this.armRotation.horizontal=0,this.armRotation.vertical=0,this.armLength=1.7,this.cameraLocation=new o,this.cameraRotation=new o(.5*Math.PI,0,0),this.calculateCameraOrientation()}var o=i(10);s.prototype.setArmLocation=function(t,e,i){this.armLocation.x=t,this.armLocation.y=e,this.armLocation.z=i,this.calculateCameraOrientation()},s.prototype.setArmRotation=function(t,e){void 0!==t&&(this.armRotation.horizontal=t),void 0!==e&&(this.armRotation.vertical=e,this.armRotation.vertical<0&&(this.armRotation.vertical=0),this.armRotation.vertical>.5*Math.PI&&(this.armRotation.vertical=.5*Math.PI)),(void 0!==t||void 0!==e)&&this.calculateCameraOrientation()},s.prototype.getArmRotation=function(){var t={};return t.horizontal=this.armRotation.horizontal,t.vertical=this.armRotation.vertical,t},s.prototype.setArmLength=function(t){void 0!==t&&(this.armLength=t,this.armLength<.71&&(this.armLength=.71),this.armLength>5&&(this.armLength=5),this.calculateCameraOrientation())},s.prototype.getArmLength=function(){return this.armLength},s.prototype.getCameraLocation=function(){return this.cameraLocation},s.prototype.getCameraRotation=function(){return this.cameraRotation},s.prototype.calculateCameraOrientation=function(){this.cameraLocation.x=this.armLocation.x-this.armLength*Math.sin(this.armRotation.horizontal)*Math.cos(this.armRotation.vertical),this.cameraLocation.y=this.armLocation.y-this.armLength*Math.cos(this.armRotation.horizontal)*Math.cos(this.armRotation.vertical),this.cameraLocation.z=this.armLocation.z+this.armLength*Math.sin(this.armRotation.vertical),this.cameraRotation.x=Math.PI/2-this.armRotation.vertical,this.cameraRotation.y=0,this.cameraRotation.z=-this.armRotation.horizontal},t.exports=s},function(t,e,i){function s(t,e,i){this.data=t,this.column=e,this.graph=i,this.index=void 0,this.value=void 0,this.values=i.getDistinctValues(t.get(),this.column),this.values.sort(function(t,e){return t>e?1:e>t?-1:0}),this.values.length>0&&this.selectValue(0),this.dataPoints=[],this.loaded=!1,this.onLoadCallback=void 0,i.animationPreload?(this.loaded=!1,this.loadInBackground()):this.loaded=!0}var o=i(4);s.prototype.isLoaded=function(){return this.loaded},s.prototype.getLoadedProgress=function(){for(var t=this.values.length,e=0;this.dataPoints[e];)e++;return Math.round(e/t*100)},s.prototype.getLabel=function(){return this.graph.filterLabel},s.prototype.getColumn=function(){return this.column},s.prototype.getSelectedValue=function(){return void 0===this.index?void 0:this.values[this.index]},s.prototype.getValues=function(){return this.values},s.prototype.getValue=function(t){if(t>=this.values.length)throw"Error: index out of range";return this.values[t]},s.prototype._getDataPoints=function(t){if(void 0===t&&(t=this.index),void 0===t)return[];var e;if(this.dataPoints[t])e=this.dataPoints[t];else{var i={};i.column=this.column,i.value=this.values[t];var s=new o(this.data,{filter:function(t){return t[i.column]==i.value}}).get();e=this.graph._getDataPoints(s),this.dataPoints[t]=e}return e},s.prototype.setOnLoadCallback=function(t){this.onLoadCallback=t},s.prototype.selectValue=function(t){if(t>=this.values.length)throw"Error: index out of range";this.index=t,this.value=this.values[t]},s.prototype.loadInBackground=function(t){void 0===t&&(t=0);var e=this.graph.frame; +if(t0&&(t--,this.setIndex(t))},s.prototype.next=function(){var t=this.getIndex();t0?this.setIndex(0):this.index=void 0},s.prototype.setIndex=function(t){if(!(ts&&(s=0),s>this.values.length-1&&(s=this.values.length-1),s},s.prototype.indexToLeft=function(t){var e=parseFloat(this.frame.bar.style.width)-this.frame.slide.clientWidth-10,i=t/(this.values.length-1)*e,s=i+3;return s},s.prototype._onMouseMove=function(t){var e=t.clientX-this.startClientX,i=this.startSlideX+e,s=this.leftToIndex(i);this.setIndex(s),o.preventDefault()},s.prototype._onMouseUp=function(){this.frame.style.cursor="auto",o.removeEventListener(document,"mousemove",this.onmousemove),o.removeEventListener(document,"mouseup",this.onmouseup),o.preventDefault()},t.exports=s},function(t){function e(t,e,i,s){this._start=0,this._end=0,this._step=1,this.prettyStep=!0,this.precision=5,this._current=0,this.setRange(t,e,i,s)}e.prototype.setRange=function(t,e,i,s){this._start=t?t:0,this._end=e?e:0,this.setStep(i,s)},e.prototype.setStep=function(t,i){void 0===t||0>=t||(void 0!==i&&(this.prettyStep=i),this._step=this.prettyStep===!0?e.calculatePrettyStep(t):t)},e.calculatePrettyStep=function(t){var e=function(t){return Math.log(t)/Math.LN10},i=Math.pow(10,Math.round(e(t))),s=2*Math.pow(10,Math.round(e(t/2))),o=5*Math.pow(10,Math.round(e(t/5))),n=i;return Math.abs(s-t)<=Math.abs(n-t)&&(n=s),Math.abs(o-t)<=Math.abs(n-t)&&(n=o),0>=n&&(n=1),n},e.prototype.getCurrent=function(){return parseFloat(this._current.toPrecision(this.precision))},e.prototype.getStep=function(){return this._step},e.prototype.start=function(){this._current=this._start-this._start%this._step},e.prototype.next=function(){this._current+=this._step},e.prototype.end=function(){return this._current>this._end},t.exports=e},function(t,e,i){function s(t,e,i,r){if(!(this instanceof s))throw new SyntaxError("Constructor must be called with the new operator");if(!(Array.isArray(i)||i instanceof n)&&i instanceof Object){var h=r;r=i,i=h}var u=this;this.defaultOptions={start:null,end:null,autoResize:!0,orientation:"bottom",width:null,height:null,maxHeight:null,minHeight:null},this.options=o.deepExtend({},this.defaultOptions),this._create(t),this.components=[],this.body={dom:this.dom,domProps:this.props,emitter:{on:this.on.bind(this),off:this.off.bind(this),emit:this.emit.bind(this)},hiddenDates:[],util:{snap:null,toScreen:u._toScreen.bind(u),toGlobalScreen:u._toGlobalScreen.bind(u),toTime:u._toTime.bind(u),toGlobalTime:u._toGlobalTime.bind(u)}},this.range=new a(this.body),this.components.push(this.range),this.body.range=this.range,this.timeAxis=new d(this.body),this.components.push(this.timeAxis),this.body.util.snap=this.timeAxis.snap.bind(this.timeAxis),this.currentTime=new l(this.body),this.components.push(this.currentTime),this.customTime=new c(this.body),this.components.push(this.customTime),this.itemSet=new p(this.body),this.components.push(this.itemSet),this.itemsData=null,this.groupsData=null,r&&this.setOptions(r),i&&this.setGroups(i),e?this.setItems(e):this.redraw()}var o=(i(56),i(45),i(1)),n=i(3),r=i(4),a=i(17),h=i(46),d=i(35),l=i(26),c=i(27),p=i(32);s.prototype=new h,s.prototype.setItems=function(t){var e,i=null==this.itemsData;if(e=t?t instanceof n||t instanceof r?t:new n(t,{type:{start:"Date",end:"Date"}}):null,this.itemsData=e,this.itemSet&&this.itemSet.setItems(e),i)if(void 0!=this.options.start||void 0!=this.options.end){if(void 0==this.options.start||void 0==this.options.end)var s=this._getDataRange();var o=void 0!=this.options.start?this.options.start:s.start,a=void 0!=this.options.end?this.options.end:s.end;this.setWindow(o,a,{animate:!1})}else this.fit({animate:!1})},s.prototype.setGroups=function(t){var e;e=t?t instanceof n||t instanceof r?t:new n(t):null,this.groupsData=e,this.itemSet.setGroups(e)},s.prototype.setSelection=function(t,e){this.itemSet&&this.itemSet.setSelection(t),e&&e.focus&&this.focus(t,e)},s.prototype.getSelection=function(){return this.itemSet&&this.itemSet.getSelection()||[]},s.prototype.focus=function(t,e){if(this.itemsData&&void 0!=t){var i=Array.isArray(t)?t:[t],s=this.itemsData.getDataSet().get(i,{type:{start:"Date",end:"Date"}}),o=null,n=null;if(s.forEach(function(t){var e=t.start.valueOf(),i="end"in t?t.end.valueOf():t.start.valueOf();(null===o||o>e)&&(o=e),(null===n||i>n)&&(n=i)}),null!==o&&null!==n){var r=(o+n)/2,a=Math.max(this.range.end-this.range.start,1.1*(n-o)),h=e&&void 0!==e.animate?e.animate:!0;this.range.setRange(r-a/2,r+a/2,h)}}},s.prototype.getItemRange=function(){var t=this.itemsData.getDataSet(),e=null,i=null;if(t){var s=t.min("start");e=s?o.convert(s.start,"Date").valueOf():null;var n=t.max("start");n&&(i=o.convert(n.start,"Date").valueOf());var r=t.max("end");r&&(i=null==i?o.convert(r.end,"Date").valueOf():Math.max(i,o.convert(r.end,"Date").valueOf()))}return{min:null!=e?new Date(e):null,max:null!=i?new Date(i):null}},t.exports=s},function(t,e,i){function s(t,e,i,s){if(!(Array.isArray(i)||i instanceof n)&&i instanceof Object){var r=s;s=i,i=r}var h=this;this.defaultOptions={start:null,end:null,autoResize:!0,orientation:"bottom",width:null,height:null,maxHeight:null,minHeight:null},this.options=o.deepExtend({},this.defaultOptions),this._create(t),this.components=[],this.body={dom:this.dom,domProps:this.props,emitter:{on:this.on.bind(this),off:this.off.bind(this),emit:this.emit.bind(this)},hiddenDates:[],util:{snap:null,toScreen:h._toScreen.bind(h),toGlobalScreen:h._toGlobalScreen.bind(h),toTime:h._toTime.bind(h),toGlobalTime:h._toGlobalTime.bind(h)}},this.range=new a(this.body),this.components.push(this.range),this.body.range=this.range,this.timeAxis=new d(this.body),this.components.push(this.timeAxis),this.body.util.snap=this.timeAxis.snap.bind(this.timeAxis),this.currentTime=new l(this.body),this.components.push(this.currentTime),this.customTime=new c(this.body),this.components.push(this.customTime),this.linegraph=new p(this.body),this.components.push(this.linegraph),this.itemsData=null,this.groupsData=null,s&&this.setOptions(s),i&&this.setGroups(i),e?this.setItems(e):this.redraw()}var o=(i(56),i(45),i(1)),n=i(3),r=i(4),a=i(17),h=i(46),d=i(35),l=i(26),c=i(27),p=i(34);s.prototype=new h,s.prototype.setItems=function(t){var e,i=null==this.itemsData;if(e=t?t instanceof n||t instanceof r?t:new n(t,{type:{start:"Date",end:"Date"}}):null,this.itemsData=e,this.linegraph&&this.linegraph.setItems(e),i)if(void 0!=this.options.start||void 0!=this.options.end){var s=void 0!=this.options.start?this.options.start:null,o=void 0!=this.options.end?this.options.end:null;this.setWindow(s,o,{animate:!1})}else this.fit({animate:!1})},s.prototype.setGroups=function(t){var e;e=t?t instanceof n||t instanceof r?t:new n(t):null,this.groupsData=e,this.linegraph.setGroups(e)},s.prototype.getLegend=function(t,e,i){return void 0===e&&(e=15),void 0===i&&(i=15),void 0!==this.linegraph.groups[t]?this.linegraph.groups[t].getLegend(e,i):"cannot find group:"+t},s.prototype.isGroupVisible=function(t){return void 0!==this.linegraph.groups[t]?this.linegraph.groups[t].visible&&(void 0===this.linegraph.options.groups.visibility[t]||1==this.linegraph.options.groups.visibility[t]):!1},s.prototype.getItemRange=function(){var t=null,e=null;for(var i in this.linegraph.groups)if(this.linegraph.groups.hasOwnProperty(i)&&1==this.linegraph.groups[i].visible)for(var s=0;sr?r:t,e=null==e?r:r>e?r:e}return{min:null!=t?new Date(t):null,max:null!=e?new Date(e):null}},t.exports=s},function(t,e,i){var s=i(44);e.convertHiddenOptions=function(t,e){if(t.hiddenDates=[],e&&1==Array.isArray(e)){for(var i=0;i=4*a){var p=0,u=n.clone();switch(i[h].repeat){case"daily":d.day()!=l.day()&&(p=1),d.dayOfYear(o.dayOfYear()),d.year(o.year()),d.subtract(7,"days"),l.dayOfYear(o.dayOfYear()),l.year(o.year()),l.subtract(7-p,"days"),u.add(1,"weeks");break;case"weekly":var m=l.diff(d,"days"),f=d.day();d.date(o.date()),d.month(o.month()),d.year(o.year()),l=d.clone(),d.day(f),l.day(f),l.add(m,"days"),d.subtract(1,"weeks"),l.subtract(1,"weeks"),u.add(1,"weeks");break;case"monthly":d.month()!=l.month()&&(p=1),d.month(o.month()),d.year(o.year()),d.subtract(1,"months"),l.month(o.month()),l.year(o.year()),l.subtract(1,"months"),l.add(p,"months"),u.add(1,"months");break;case"yearly":d.year()!=l.year()&&(p=1),d.year(o.year()),d.subtract(1,"years"),l.year(o.year()),l.subtract(1,"years"),l.add(p,"years"),u.add(1,"years");break;default:return void console.log("Wrong repeat format, allowed are: daily, weekly, monthly, yearly. Given:",i[h].repeat)}for(;u>d;)switch(t.hiddenDates.push({start:d.valueOf(),end:l.valueOf()}),i[h].repeat){case"daily":d.add(1,"days"),l.add(1,"days");break;case"weekly":d.add(1,"weeks"),l.add(1,"weeks");break;case"monthly":d.add(1,"months"),l.add(1,"months");break;case"yearly":d.add(1,"y"),l.add(1,"y");break;default:return void console.log("Wrong repeat format, allowed are: daily, weekly, monthly, yearly. Given:",i[h].repeat)}t.hiddenDates.push({start:d.valueOf(),end:l.valueOf()})}}e.removeDuplicates(t);var g=e.isHidden(t.range.start,t.hiddenDates),v=e.isHidden(t.range.end,t.hiddenDates),y=t.range.start,b=t.range.end;1==g.hidden&&(y=1==t.range.startToFront?g.startDate-1:g.endDate+1),1==v.hidden&&(b=1==t.range.endToFront?v.startDate-1:v.endDate+1),(1==g.hidden||1==v.hidden)&&t.range._applyRange(y,b)}},e.removeDuplicates=function(t){for(var e=t.hiddenDates,i=[],s=0;s=e[s].start&&e[o].end<=e[s].end?e[o].remove=!0:e[o].start>=e[s].start&&e[o].start<=e[s].end?(e[s].end=e[o].end,e[o].remove=!0):e[o].end>=e[s].start&&e[o].end<=e[s].end&&(e[s].start=e[o].start,e[o].remove=!0));for(var s=0;s=r&&a>o){i=!0;break}}if(1==i&&o=e&&i>r&&(s+=r-n)}return s},e.correctTimeForHidden=function(t,i,o){return o=s(o).toDate().valueOf(),o-=e.getHiddenDurationBefore(t,i,o)},e.getHiddenDurationBefore=function(t,e,i){var o=0;i=s(i).toDate().valueOf();for(var n=0;n=e.start&&a=a&&(o+=a-r)}return o},e.getAccumulatedHiddenDuration=function(t,e,i){for(var s=0,o=0,n=e.start,r=0;r=e.start&&h=i)break;s+=h-a}}return s},e.snapAwayFromHidden=function(t,i,s,o){var n=e.isHidden(i,t);return 1==n.hidden?0>s?1==o?n.startDate-(n.endDate-i)-1:n.startDate-1:1==o?n.endDate+(i-n.startDate)+1:n.endDate+1:i},e.isHidden=function(t,e){for(var i=0;i=s&&o>t)return{hidden:!0,startDate:s,endDate:o}}return{hidden:!1,startDate:s,endDate:o}}},function(t){function e(t,e,i,s,o,n){this.current=0,this.autoScale=!0,this.stepIndex=0,this.step=1,this.scale=1,this.marginStart,this.marginEnd,this.deadSpace=0,this.majorSteps=[1,2,5,10],this.minorSteps=[.25,.5,1,2],this.alignZeros=n,this.setRange(t,e,i,s,o)}e.prototype.setRange=function(t,e,i,s,o){this._start=void 0===o.min?t:o.min,this._end=void 0===o.max?e:o.max,this._start==this._end&&(this._start-=.75,this._end+=1),1==this.autoScale&&this.setMinimumStep(i,s),this.setFirst(o)},e.prototype.setMinimumStep=function(t,e){var i=this._end-this._start,s=1.2*i,o=t*(s/e),n=Math.round(Math.log(s)/Math.LN10),r=-1,a=Math.pow(10,n),h=0;0>n&&(h=n);for(var d=!1,l=h;Math.abs(l)<=Math.abs(n);l++){a=Math.pow(10,l);for(var c=0;c=o){d=!0,r=c;break}}if(1==d)break}this.stepIndex=r,this.scale=a,this.step=a*this.minorSteps[r]},e.prototype.setFirst=function(t){void 0===t&&(t={});var e=void 0===t.min?this._start-2*this.scale*this.minorSteps[this.stepIndex]:t.min,i=void 0===t.max?this._end+this.scale*this.minorSteps[this.stepIndex]:t.max;this.marginEnd=void 0===t.max?this.roundToMinor(i):t.max,this.marginStart=void 0===t.min?this.roundToMinor(e):t.min,1==this.alignZeros&&(this.marginEnd-this.marginStart)%this.step!=0&&(this.marginEnd+=this.marginEnd%this.step),this.deadSpace=this.roundToMinor(i)-i+this.roundToMinor(e)-e,this.marginRange=this.marginEnd-this.marginStart,this.current=this.marginEnd},e.prototype.roundToMinor=function(t){var e=t-t%(this.scale*this.minorSteps[this.stepIndex]);return t%(this.scale*this.minorSteps[this.stepIndex])>.5*this.scale*this.minorSteps[this.stepIndex]?e+this.scale*this.minorSteps[this.stepIndex]:e},e.prototype.hasNext=function(){return this.current>=this.marginStart},e.prototype.next=function(){var t=this.current;this.current-=this.step,this.current==t&&(this.current=this._end)},e.prototype.previous=function(){this.current+=this.step,this.marginEnd+=this.step,this.marginRange=this.marginEnd-this.marginStart},e.prototype.getCurrent=function(t){var e=Math.abs(this.current)0;s--){if("0"!=i[s]){if("."==i[s]||","==i[s]){i=i.slice(0,s);break}break}i=i.slice(0,s)}}else{var o="",n=i.indexOf("e");if(-1!=n&&(o=i.slice(n),i=i.slice(0,n)),n=Math.max(i.indexOf(","),i.indexOf(".")),-1===n?(0!==t&&(i+="."),n=i.length+t):0!==t&&(n+=t+1),n>i.length)for(var r=n-i.length;r>0;r--)i+="0";else i=i.slice(0,n);i+=o}return i},e.prototype.snap=function(){},e.prototype.isMajor=function(){return this.current%(this.scale*this.majorSteps[this.stepIndex])==0},t.exports=e},function(t,e,i){function s(t,e){var i=h().hours(0).minutes(0).seconds(0).milliseconds(0);this.start=i.clone().add(-3,"days").valueOf(),this.end=i.clone().add(4,"days").valueOf(),this.body=t,this.deltaDifference=0,this.scaleOffset=0,this.startToFront=!1,this.endToFront=!0,this.defaultOptions={start:null,end:null,direction:"horizontal",moveable:!0,zoomable:!0,min:null,max:null,zoomMin:10,zoomMax:31536e10},this.options=r.extend({},this.defaultOptions),this.props={touch:{}},this.animateTimer=null,this.body.emitter.on("dragstart",this._onDragStart.bind(this)),this.body.emitter.on("drag",this._onDrag.bind(this)),this.body.emitter.on("dragend",this._onDragEnd.bind(this)),this.body.emitter.on("hold",this._onHold.bind(this)),this.body.emitter.on("mousewheel",this._onMouseWheel.bind(this)),this.body.emitter.on("DOMMouseScroll",this._onMouseWheel.bind(this)),this.body.emitter.on("touch",this._onTouch.bind(this)),this.body.emitter.on("pinch",this._onPinch.bind(this)),this.setOptions(e)}function o(t){if("horizontal"!=t&&"vertical"!=t)throw new TypeError('Unknown direction "'+t+'". Choose "horizontal" or "vertical".')}function n(t,e){return{x:t.pageX-r.getAbsoluteLeft(e),y:t.pageY-r.getAbsoluteTop(e)}}var r=i(1),a=i(47),h=i(44),d=i(25),l=i(15);s.prototype=new d,s.prototype.setOptions=function(t){if(t){var e=["direction","min","max","zoomMin","zoomMax","moveable","zoomable","activate","hiddenDates"];r.selectiveExtend(e,this.options,t),("start"in t||"end"in t)&&this.setRange(t.start,t.end)}},s.prototype.setRange=function(t,e,i,s){s!==!0&&(s=!1);var o=void 0!=t?r.convert(t,"Date").valueOf():null,n=void 0!=e?r.convert(e,"Date").valueOf():null;if(this._cancelAnimation(),i){var a=this,h=this.start,d=this.end,c="number"==typeof i?i:500,p=(new Date).valueOf(),u=!1,m=function(){if(!a.props.touch.dragging){var t=(new Date).valueOf(),e=t-p,i=e>c,g=i||null===o?o:r.easeInOutQuad(e,h,o,c),v=i||null===n?n:r.easeInOutQuad(e,d,n,c);f=a._applyRange(g,v),l.updateHiddenDates(a.body,a.options.hiddenDates),u=u||f,f&&a.body.emitter.emit("rangechange",{start:new Date(a.start),end:new Date(a.end),byUser:s}),i?u&&a.body.emitter.emit("rangechanged",{start:new Date(a.start),end:new Date(a.end),byUser:s}):a.animateTimer=setTimeout(m,20)}};return m()}var f=this._applyRange(o,n);if(l.updateHiddenDates(this.body,this.options.hiddenDates),f){var g={start:new Date(this.start),end:new Date(this.end),byUser:s};this.body.emitter.emit("rangechange",g),this.body.emitter.emit("rangechanged",g)}},s.prototype._cancelAnimation=function(){this.animateTimer&&(clearTimeout(this.animateTimer),this.animateTimer=null)},s.prototype._applyRange=function(t,e){var i,s=null!=t?r.convert(t,"Date").valueOf():this.start,o=null!=e?r.convert(e,"Date").valueOf():this.end,n=null!=this.options.max?r.convert(this.options.max,"Date").valueOf():null,a=null!=this.options.min?r.convert(this.options.min,"Date").valueOf():null;if(isNaN(s)||null===s)throw new Error('Invalid start "'+t+'"');if(isNaN(o)||null===o)throw new Error('Invalid end "'+e+'"');if(s>o&&(o=s),null!==a&&a>s&&(i=a-s,s+=i,o+=i,null!=n&&o>n&&(o=n)),null!==n&&o>n&&(i=o-n,s-=i,o-=i,null!=a&&a>s&&(s=a)),null!==this.options.zoomMin){var h=parseFloat(this.options.zoomMin);0>h&&(h=0),h>o-s&&(this.end-this.start===h?(s=this.start,o=this.end):(i=h-(o-s),s-=i/2,o+=i/2))}if(null!==this.options.zoomMax){var d=parseFloat(this.options.zoomMax);0>d&&(d=0),o-s>d&&(this.end-this.start===d?(s=this.start,o=this.end):(i=o-s-d,s+=i/2,o-=i/2))}var l=this.start!=s||this.end!=o;return s>=this.start&&s<=this.end||o>=this.start&&o<=this.end||this.start>=s&&this.start<=o||this.end>=s&&this.end<=o||this.body.emitter.emit("checkRangedItems"),this.start=s,this.end=o,l},s.prototype.getRange=function(){return{start:this.start,end:this.end}},s.prototype.conversion=function(t,e){return s.conversion(this.start,this.end,t,e)},s.conversion=function(t,e,i,s){return void 0===s&&(s=0),0!=i&&e-t!=0?{offset:t,scale:i/(e-t-s)}:{offset:0,scale:1}},s.prototype._onDragStart=function(){this.deltaDifference=0,this.previousDelta=0,this.options.moveable&&this.props.touch.allowDragging&&(this.props.touch.start=this.start,this.props.touch.end=this.end,this.props.touch.dragging=!0,this.body.dom.root&&(this.body.dom.root.style.cursor="move"))},s.prototype._onDrag=function(t){if(this.options.moveable&&this.props.touch.allowDragging){var e=this.options.direction;o(e);var i="horizontal"==e?t.gesture.deltaX:t.gesture.deltaY;i-=this.deltaDifference;var s=this.props.touch.end-this.props.touch.start,n=l.getHiddenDurationBetween(this.body.hiddenDates,this.start,this.end);s-=n;var r="horizontal"==e?this.body.domProps.center.width:this.body.domProps.center.height,a=-i/r*s,h=this.props.touch.start+a,d=this.props.touch.end+a,c=l.snapAwayFromHidden(this.body.hiddenDates,h,this.previousDelta-i,!0),p=l.snapAwayFromHidden(this.body.hiddenDates,d,this.previousDelta-i,!0);if(c!=h||p!=d)return this.deltaDifference+=i,this.props.touch.start=c,this.props.touch.end=p,void this._onDrag(t);this.previousDelta=i,this._applyRange(h,d),this.body.emitter.emit("rangechange",{start:new Date(this.start),end:new Date(this.end),byUser:!0})}},s.prototype._onDragEnd=function(){this.options.moveable&&this.props.touch.allowDragging&&(this.props.touch.dragging=!1,this.body.dom.root&&(this.body.dom.root.style.cursor="auto"),this.body.emitter.emit("rangechanged",{start:new Date(this.start),end:new Date(this.end),byUser:!0}))},s.prototype._onMouseWheel=function(t){if(this.options.zoomable&&this.options.moveable){var e=0;if(t.wheelDelta?e=t.wheelDelta/120:t.detail&&(e=-t.detail/3),e){var i;i=0>e?1-e/5:1/(1+e/5);var s=a.fakeGesture(this,t),o=n(s.center,this.body.dom.center),r=this._pointerToDate(o);this.zoom(i,r,e)}t.preventDefault()}},s.prototype._onTouch=function(){this.props.touch.start=this.start,this.props.touch.end=this.end,this.props.touch.allowDragging=!0,this.props.touch.center=null,this.scaleOffset=0,this.deltaDifference=0},s.prototype._onHold=function(){this.props.touch.allowDragging=!1},s.prototype._onPinch=function(t){if(this.options.zoomable&&this.options.moveable&&(this.props.touch.allowDragging=!1,t.gesture.touches.length>1)){this.props.touch.center||(this.props.touch.center=n(t.gesture.center,this.body.dom.center));var e=1/(t.gesture.scale+this.scaleOffset),i=this._pointerToDate(this.props.touch.center),s=l.getHiddenDurationBetween(this.body.hiddenDates,this.start,this.end),o=l.getHiddenDurationBefore(this.body.hiddenDates,this,i),r=s-o,a=i-o+(this.props.touch.start-(i-o))*e,h=i+r+(this.props.touch.end-(i+r))*e;this.startToFront=1-e>0?!1:!0,this.endToFront=e-1>0?!1:!0;var d=l.snapAwayFromHidden(this.body.hiddenDates,a,1-e,!0),c=l.snapAwayFromHidden(this.body.hiddenDates,h,e-1,!0);(d!=a||c!=h)&&(this.props.touch.start=d,this.props.touch.end=c,this.scaleOffset=1-t.gesture.scale,a=d,h=c),this.setRange(a,h,!1,!0),this.startToFront=!1,this.endToFront=!0}},s.prototype._pointerToDate=function(t){var e,i=this.options.direction;if(o(i),"horizontal"==i)return this.body.util.toTime(t.x).valueOf();var s=this.body.domProps.center.height;return e=this.conversion(s),t.y/e.scale+e.offset},s.prototype.zoom=function(t,e,i){null==e&&(e=(this.start+this.end)/2);var s=l.getHiddenDurationBetween(this.body.hiddenDates,this.start,this.end),o=l.getHiddenDurationBefore(this.body.hiddenDates,this,e),n=s-o,r=e-o+(this.start-(e-o))*t,a=e+n+(this.end-(e+n))*t;this.startToFront=i>0?!1:!0,this.endToFront=-i>0?!1:!0;var h=l.snapAwayFromHidden(this.body.hiddenDates,r,i,!0),d=l.snapAwayFromHidden(this.body.hiddenDates,a,-i,!0);(h!=r||d!=a)&&(r=h,a=d),this.setRange(r,a,!1,!0),this.startToFront=!1,this.endToFront=!0},s.prototype.move=function(t){var e=this.end-this.start,i=this.start+e*t,s=this.end+e*t;this.start=i,this.end=s},s.prototype.moveTo=function(t){var e=(this.start+this.end)/2,i=e-t,s=this.start-i,o=this.end-i;this.setRange(s,o)},t.exports=s},function(t,e){var i=.001;e.orderByStart=function(t){t.sort(function(t,e){return t.data.start-e.data.start})},e.orderByEnd=function(t){t.sort(function(t,e){var i="end"in t.data?t.data.end:t.data.start,s="end"in e.data?e.data.end:e.data.start;return i-s})},e.stack=function(t,i,s){var o,n;if(s)for(o=0,n=t.length;n>o;o++)t[o].top=null;for(o=0,n=t.length;n>o;o++){var r=t[o];if(r.stack&&null===r.top){r.top=i.axis;do{for(var a=null,h=0,d=t.length;d>h;h++){var l=t[h];if(null!==l.top&&l!==r&&l.stack&&e.collision(r,l,i.item)){a=l;break}}null!=a&&(r.top=a.top+a.height+i.item.vertical)}while(a)}}},e.nostack=function(t,e,i){var s,o,n;for(s=0,o=t.length;o>s;s++)if(void 0!==t[s].data.subgroup){n=e.axis;for(var r in i)i.hasOwnProperty(r)&&1==i[r].visible&&i[r].indexe.left&&t.top-s.vertical+ie.top}},function(t,e,i){function s(t,e,i,o){this.current=new Date,this._start=new Date,this._end=new Date,this.autoScale=!0,this.scale="day",this.step=1,this.setRange(t,e,i),this.switchedDay=!1,this.switchedMonth=!1,this.switchedYear=!1,this.hiddenDates=o,void 0===o&&(this.hiddenDates=[]),this.format=s.FORMAT}var o=i(44),n=i(15),r=i(1);s.FORMAT={minorLabels:{millisecond:"SSS",second:"s",minute:"HH:mm",hour:"HH:mm",weekday:"ddd D",day:"D",month:"MMM",year:"YYYY"},majorLabels:{millisecond:"HH:mm:ss",second:"D MMMM HH:mm",minute:"ddd D MMMM",hour:"ddd D MMMM",weekday:"MMMM YYYY",day:"MMMM YYYY",month:"YYYY",year:""}},s.prototype.setFormat=function(t){var e=r.deepExtend({},s.FORMAT);this.format=r.deepExtend(e,t)},s.prototype.setRange=function(t,e,i){if(!(t instanceof Date&&e instanceof Date))throw"No legal start or end date in method setRange";this._start=void 0!=t?new Date(t.valueOf()):new Date,this._end=void 0!=e?new Date(e.valueOf()):new Date,this.autoScale&&this.setMinimumStep(i)},s.prototype.first=function(){this.current=new Date(this._start.valueOf()),this.roundToMinor()},s.prototype.roundToMinor=function(){switch(this.scale){case"year":this.current.setFullYear(this.step*Math.floor(this.current.getFullYear()/this.step)),this.current.setMonth(0);case"month":this.current.setDate(1);case"day":case"weekday":this.current.setHours(0);case"hour":this.current.setMinutes(0);case"minute":this.current.setSeconds(0);case"second":this.current.setMilliseconds(0)}if(1!=this.step)switch(this.scale){case"millisecond":this.current.setMilliseconds(this.current.getMilliseconds()-this.current.getMilliseconds()%this.step);break;case"second":this.current.setSeconds(this.current.getSeconds()-this.current.getSeconds()%this.step);break;case"minute":this.current.setMinutes(this.current.getMinutes()-this.current.getMinutes()%this.step);break;case"hour":this.current.setHours(this.current.getHours()-this.current.getHours()%this.step);break;case"weekday":case"day":this.current.setDate(this.current.getDate()-1-(this.current.getDate()-1)%this.step+1);break;case"month":this.current.setMonth(this.current.getMonth()-this.current.getMonth()%this.step);break;case"year":this.current.setFullYear(this.current.getFullYear()-this.current.getFullYear()%this.step)}},s.prototype.hasNext=function(){return this.current.valueOf()<=this._end.valueOf() +},s.prototype.next=function(){var t=this.current.valueOf();if(this.current.getMonth()<6)switch(this.scale){case"millisecond":this.current=new Date(this.current.valueOf()+this.step);break;case"second":this.current=new Date(this.current.valueOf()+1e3*this.step);break;case"minute":this.current=new Date(this.current.valueOf()+1e3*this.step*60);break;case"hour":this.current=new Date(this.current.valueOf()+1e3*this.step*60*60);var e=this.current.getHours();this.current.setHours(e-e%this.step);break;case"weekday":case"day":this.current.setDate(this.current.getDate()+this.step);break;case"month":this.current.setMonth(this.current.getMonth()+this.step);break;case"year":this.current.setFullYear(this.current.getFullYear()+this.step)}else switch(this.scale){case"millisecond":this.current=new Date(this.current.valueOf()+this.step);break;case"second":this.current.setSeconds(this.current.getSeconds()+this.step);break;case"minute":this.current.setMinutes(this.current.getMinutes()+this.step);break;case"hour":this.current.setHours(this.current.getHours()+this.step);break;case"weekday":case"day":this.current.setDate(this.current.getDate()+this.step);break;case"month":this.current.setMonth(this.current.getMonth()+this.step);break;case"year":this.current.setFullYear(this.current.getFullYear()+this.step)}if(1!=this.step)switch(this.scale){case"millisecond":this.current.getMilliseconds()0?t.step:1,this.autoScale=!1)},s.prototype.setAutoScale=function(t){this.autoScale=t},s.prototype.setMinimumStep=function(t){if(void 0!=t){var e=31104e6,i=2592e6,s=864e5,o=36e5,n=6e4,r=1e3,a=1;1e3*e>t&&(this.scale="year",this.step=1e3),500*e>t&&(this.scale="year",this.step=500),100*e>t&&(this.scale="year",this.step=100),50*e>t&&(this.scale="year",this.step=50),10*e>t&&(this.scale="year",this.step=10),5*e>t&&(this.scale="year",this.step=5),e>t&&(this.scale="year",this.step=1),3*i>t&&(this.scale="month",this.step=3),i>t&&(this.scale="month",this.step=1),5*s>t&&(this.scale="day",this.step=5),2*s>t&&(this.scale="day",this.step=2),s>t&&(this.scale="day",this.step=1),s/2>t&&(this.scale="weekday",this.step=1),4*o>t&&(this.scale="hour",this.step=4),o>t&&(this.scale="hour",this.step=1),15*n>t&&(this.scale="minute",this.step=15),10*n>t&&(this.scale="minute",this.step=10),5*n>t&&(this.scale="minute",this.step=5),n>t&&(this.scale="minute",this.step=1),15*r>t&&(this.scale="second",this.step=15),10*r>t&&(this.scale="second",this.step=10),5*r>t&&(this.scale="second",this.step=5),r>t&&(this.scale="second",this.step=1),200*a>t&&(this.scale="millisecond",this.step=200),100*a>t&&(this.scale="millisecond",this.step=100),50*a>t&&(this.scale="millisecond",this.step=50),10*a>t&&(this.scale="millisecond",this.step=10),5*a>t&&(this.scale="millisecond",this.step=5),a>t&&(this.scale="millisecond",this.step=1)}},s.prototype.snap=function(t){var e=new Date(t.valueOf());if("year"==this.scale){var i=e.getFullYear()+Math.round(e.getMonth()/12);e.setFullYear(Math.round(i/this.step)*this.step),e.setMonth(0),e.setDate(0),e.setHours(0),e.setMinutes(0),e.setSeconds(0),e.setMilliseconds(0)}else if("month"==this.scale)e.getDate()>15?(e.setDate(1),e.setMonth(e.getMonth()+1)):e.setDate(1),e.setHours(0),e.setMinutes(0),e.setSeconds(0),e.setMilliseconds(0);else if("day"==this.scale){switch(this.step){case 5:case 2:e.setHours(24*Math.round(e.getHours()/24));break;default:e.setHours(12*Math.round(e.getHours()/12))}e.setMinutes(0),e.setSeconds(0),e.setMilliseconds(0)}else if("weekday"==this.scale){switch(this.step){case 5:case 2:e.setHours(12*Math.round(e.getHours()/12));break;default:e.setHours(6*Math.round(e.getHours()/6))}e.setMinutes(0),e.setSeconds(0),e.setMilliseconds(0)}else if("hour"==this.scale){switch(this.step){case 4:e.setMinutes(60*Math.round(e.getMinutes()/60));break;default:e.setMinutes(30*Math.round(e.getMinutes()/30))}e.setSeconds(0),e.setMilliseconds(0)}else if("minute"==this.scale){switch(this.step){case 15:case 10:e.setMinutes(5*Math.round(e.getMinutes()/5)),e.setSeconds(0);break;case 5:e.setSeconds(60*Math.round(e.getSeconds()/60));break;default:e.setSeconds(30*Math.round(e.getSeconds()/30))}e.setMilliseconds(0)}else if("second"==this.scale)switch(this.step){case 15:case 10:e.setSeconds(5*Math.round(e.getSeconds()/5)),e.setMilliseconds(0);break;case 5:e.setMilliseconds(1e3*Math.round(e.getMilliseconds()/1e3));break;default:e.setMilliseconds(500*Math.round(e.getMilliseconds()/500))}else if("millisecond"==this.scale){var s=this.step>5?this.step/2:1;e.setMilliseconds(Math.round(e.getMilliseconds()/s)*s)}return e},s.prototype.isMajor=function(){if(1==this.switchedYear)switch(this.switchedYear=!1,this.scale){case"year":case"month":case"weekday":case"day":case"hour":case"minute":case"second":case"millisecond":return!0;default:return!1}else if(1==this.switchedMonth)switch(this.switchedMonth=!1,this.scale){case"weekday":case"day":case"hour":case"minute":case"second":case"millisecond":return!0;default:return!1}else if(1==this.switchedDay)switch(this.switchedDay=!1,this.scale){case"millisecond":case"second":case"minute":case"hour":return!0;default:return!1}switch(this.scale){case"millisecond":return 0==this.current.getMilliseconds();case"second":return 0==this.current.getSeconds();case"minute":return 0==this.current.getHours()&&0==this.current.getMinutes();case"hour":return 0==this.current.getHours();case"weekday":case"day":return 1==this.current.getDate();case"month":return 0==this.current.getMonth();case"year":return!1;default:return!1}},s.prototype.getLabelMinor=function(t){void 0==t&&(t=this.current);var e=this.format.minorLabels[this.scale];return e&&e.length>0?o(t).format(e):""},s.prototype.getLabelMajor=function(t){void 0==t&&(t=this.current);var e=this.format.majorLabels[this.scale];return e&&e.length>0?o(t).format(e):""},s.prototype.getClassName=function(){function t(t){return t/h%2==0?" even":" odd"}function e(t){return t.isSame(new Date,"day")?" today":t.isSame(o().add(1,"day"),"day")?" tomorrow":t.isSame(o().add(-1,"day"),"day")?" yesterday":""}function i(t){return t.isSame(new Date,"week")?" current-week":""}function s(t){return t.isSame(new Date,"month")?" current-month":""}function n(t){return t.isSame(new Date,"year")?" current-year":""}var r=o(this.current),a=r.locale?r.locale("en"):r.lang("en"),h=this.step;switch(this.scale){case"millisecond":return t(a.milliseconds()).trim();case"second":return t(a.seconds()).trim();case"minute":return t(a.minutes()).trim();case"hour":var d=a.hours();return 4==this.step&&(d=d+"-"+(d+4)),d+"h"+e(a)+t(a.hours());case"weekday":return a.format("dddd").toLowerCase()+e(a)+i(a)+t(a.date());case"day":var l=a.date(),c=a.format("MMMM").toLowerCase();return"day"+l+" "+c+s(a)+t(l-1);case"month":return a.format("MMMM").toLowerCase()+s(a)+t(a.month());case"year":var p=a.year();return"year"+p+n(a)+t(p);default:return""}},t.exports=s},function(t,e,i){function s(t,e,i){this.id=null,this.parent=null,this.data=t,this.dom=null,this.conversion=e||{},this.options=i||{},this.selected=!1,this.displayed=!1,this.dirty=!0,this.top=null,this.left=null,this.width=null,this.height=null}var o=i(45),n=i(1);s.prototype.stack=!0,s.prototype.select=function(){this.selected=!0,this.dirty=!0,this.displayed&&this.redraw()},s.prototype.unselect=function(){this.selected=!1,this.dirty=!0,this.displayed&&this.redraw()},s.prototype.setData=function(t){this.data=t,this.dirty=!0,this.displayed&&this.redraw()},s.prototype.setParent=function(t){this.displayed?(this.hide(),this.parent=t,this.parent&&this.show()):this.parent=t},s.prototype.isVisible=function(){return!1},s.prototype.show=function(){return!1},s.prototype.hide=function(){return!1},s.prototype.redraw=function(){},s.prototype.repositionX=function(){},s.prototype.repositionY=function(){},s.prototype._repaintDeleteButton=function(t){if(this.selected&&this.options.editable.remove&&!this.dom.deleteButton){var e=this,i=document.createElement("div");i.className="delete",i.title="Delete this item",o(i,{preventDefault:!0}).on("tap",function(t){e.parent.removeFromDataSet(e),t.stopPropagation()}),t.appendChild(i),this.dom.deleteButton=i}else!this.selected&&this.dom.deleteButton&&(this.dom.deleteButton.parentNode&&this.dom.deleteButton.parentNode.removeChild(this.dom.deleteButton),this.dom.deleteButton=null)},s.prototype._updateContents=function(t){var e;if(this.options.template){var i=this.parent.itemSet.itemsData.get(this.id);e=this.options.template(i)}else e=this.data.content;if(e!==this.content){if(e instanceof Element)t.innerHTML="",t.appendChild(e);else if(void 0!=e)t.innerHTML=e;else if("background"!=this.data.type||void 0!==this.data.content)throw new Error('Property "content" missing in item '+this.id);this.content=e}},s.prototype._updateTitle=function(t){null!=this.data.title?t.title=this.data.title||"":t.removeAttribute("title")},s.prototype._updateDataAttributes=function(t){if(this.options.dataAttributes&&this.options.dataAttributes.length>0){var e=[];if(Array.isArray(this.options.dataAttributes))e=this.options.dataAttributes;else{if("all"!=this.options.dataAttributes)return;e=Object.keys(this.data)}for(var i=0;it.start},s.prototype.redraw=function(){var t=this.dom;if(t||(this.dom={},t=this.dom,t.box=document.createElement("div"),t.content=document.createElement("div"),t.content.className="content",t.box.appendChild(t.content),this.dirty=!0),!this.parent)throw new Error("Cannot redraw item: no parent attached");if(!t.box.parentNode){var e=this.parent.dom.background;if(!e)throw new Error("Cannot redraw item: parent has no background container element");e.appendChild(t.box)}if(this.displayed=!0,this.dirty){this._updateContents(this.dom.content),this._updateTitle(this.dom.content),this._updateDataAttributes(this.dom.content),this._updateStyle(this.dom.box);var i=(this.data.className?" "+this.data.className:"")+(this.selected?" selected":"");t.box.className=this.baseClassName+i,this.overflow="hidden"!==window.getComputedStyle(t.content).overflow,this.props.content.width=this.dom.content.offsetWidth,this.height=0,this.dirty=!1}},s.prototype.show=r.prototype.show,s.prototype.hide=r.prototype.hide,s.prototype.repositionX=r.prototype.repositionX,s.prototype.repositionY=function(t){var e="top"===this.options.orientation;this.dom.content.style.top=e?"":"0",this.dom.content.style.bottom=e?"0":"";var i;if(void 0!==this.data.subgroup){var s=this.data.subgroup,o=this.parent.subgroups,r=o[s].index;if(1==e){i=this.parent.subgroups[s].height+t.item.vertical,i+=0==r?t.axis-.5*t.item.vertical:0;var a=this.parent.top;for(var h in o)o.hasOwnProperty(h)&&1==o[h].visible&&o[h].indexr&&(a+=o[h].height+t.item.vertical);i=this.parent.subgroups[s].height+t.item.vertical,this.dom.box.style.top=a+"px",this.dom.box.style.bottom=""}}else this.parent instanceof n?(i=Math.max(this.parent.height,this.parent.itemSet.body.domProps.center.height,this.parent.itemSet.body.domProps.centerContainer.height),this.dom.box.style.top=e?"0":"",this.dom.box.style.bottom=e?"":"0"):(i=this.parent.height,this.dom.box.style.top=this.parent.top+"px",this.dom.box.style.bottom="");this.dom.box.style.height=i+"px"},t.exports=s},function(t,e,i){function s(t,e,i){if(this.props={dot:{width:0,height:0},line:{width:0,height:0}},t&&void 0==t.start)throw new Error('Property "start" missing in item '+t);o.call(this,t,e,i)}{var o=i(20);i(1)}s.prototype=new o(null,null,null),s.prototype.isVisible=function(t){var e=(t.end-t.start)/4;return this.data.start>t.start-e&&this.data.startt.start-e&&this.data.startt.start},s.prototype.redraw=function(){var t=this.dom;if(t||(this.dom={},t=this.dom,t.box=document.createElement("div"),t.content=document.createElement("div"),t.content.className="content",t.box.appendChild(t.content),t.box["timeline-item"]=this,this.dirty=!0),!this.parent)throw new Error("Cannot redraw item: no parent attached");if(!t.box.parentNode){var e=this.parent.dom.foreground;if(!e)throw new Error("Cannot redraw item: parent has no foreground container element");e.appendChild(t.box)}if(this.displayed=!0,this.dirty){this._updateContents(this.dom.content),this._updateTitle(this.dom.box),this._updateDataAttributes(this.dom.box),this._updateStyle(this.dom.box);var i=(this.data.className?" "+this.data.className:"")+(this.selected?" selected":"");t.box.className=this.baseClassName+i,this.overflow="hidden"!==window.getComputedStyle(t.content).overflow,this.dom.content.style.maxWidth="none",this.props.content.width=this.dom.content.offsetWidth,this.height=this.dom.box.offsetHeight,this.dom.content.style.maxWidth="",this.dirty=!1}this._repaintDeleteButton(t.box),this._repaintDragLeft(),this._repaintDragRight()},s.prototype.show=function(){this.displayed||this.redraw()},s.prototype.hide=function(){if(this.displayed){var t=this.dom.box;t.parentNode&&t.parentNode.removeChild(t),this.top=null,this.left=null,this.displayed=!1}},s.prototype.repositionX=function(){var t,e,i=this.parent.width,s=this.conversion.toScreen(this.data.start),o=this.conversion.toScreen(this.data.end);-i>s&&(s=-i),o>2*i&&(o=2*i);var n=Math.max(o-s,1);switch(this.overflow?(this.left=s,this.width=n+this.props.content.width,e=this.props.content.width):(this.left=s,this.width=n,e=Math.min(o-s-2*this.options.padding,this.props.content.width)),this.dom.box.style.left=this.left+"px",this.dom.box.style.width=n+"px",this.options.align){case"left":this.dom.content.style.left="0";break;case"right":this.dom.content.style.left=Math.max(n-e-2*this.options.padding,0)+"px";break;case"center":this.dom.content.style.left=Math.max((n-e-2*this.options.padding)/2,0)+"px";break;default:t=this.overflow?o>0?Math.max(-s,0):-e:0>s?Math.min(-s,o-s-e-2*this.options.padding):0,this.dom.content.style.left=t+"px"}},s.prototype.repositionY=function(){var t=this.options.orientation,e=this.dom.box;e.style.top="top"==t?this.top+"px":this.parent.height-this.top-this.height+"px"},s.prototype._repaintDragLeft=function(){if(this.selected&&this.options.editable.updateTime&&!this.dom.dragLeft){var t=document.createElement("div");t.className="drag-left",t.dragLeftItem=this,o(t,{preventDefault:!0}).on("drag",function(){}),this.dom.box.appendChild(t),this.dom.dragLeft=t}else!this.selected&&this.dom.dragLeft&&(this.dom.dragLeft.parentNode&&this.dom.dragLeft.parentNode.removeChild(this.dom.dragLeft),this.dom.dragLeft=null)},s.prototype._repaintDragRight=function(){if(this.selected&&this.options.editable.updateTime&&!this.dom.dragRight){var t=document.createElement("div");t.className="drag-right",t.dragRightItem=this,o(t,{preventDefault:!0}).on("drag",function(){}),this.dom.box.appendChild(t),this.dom.dragRight=t}else!this.selected&&this.dom.dragRight&&(this.dom.dragRight.parentNode&&this.dom.dragRight.parentNode.removeChild(this.dom.dragRight),this.dom.dragRight=null)},t.exports=s},function(t){function e(){this.options=null,this.props=null}e.prototype.setOptions=function(t){t&&util.extend(this.options,t)},e.prototype.redraw=function(){return!1},e.prototype.destroy=function(){},e.prototype._isResized=function(){var t=this.props._previousWidth!==this.props.width||this.props._previousHeight!==this.props.height;return this.props._previousWidth=this.props.width,this.props._previousHeight=this.props.height,t},t.exports=e},function(t,e,i){function s(t,e){this.body=t,this.defaultOptions={showCurrentTime:!0,locales:a,locale:"en"},this.options=o.extend({},this.defaultOptions),this.offset=0,this._create(),this.setOptions(e)}var o=i(1),n=i(25),r=i(44),a=i(48);s.prototype=new n,s.prototype._create=function(){var t=document.createElement("div");t.className="currenttime",t.style.position="absolute",t.style.top="0px",t.style.height="100%",this.bar=t},s.prototype.destroy=function(){this.options.showCurrentTime=!1,this.redraw(),this.body=null},s.prototype.setOptions=function(t){t&&o.selectiveExtend(["showCurrentTime","locale","locales"],this.options,t)},s.prototype.redraw=function(){if(this.options.showCurrentTime){var t=this.body.dom.backgroundVertical;this.bar.parentNode!=t&&(this.bar.parentNode&&this.bar.parentNode.removeChild(this.bar),t.appendChild(this.bar),this.start());var e=new Date((new Date).valueOf()+this.offset),i=this.body.util.toScreen(e),s=this.options.locales[this.options.locale],o=s.current+" "+s.time+": "+r(e).format("dddd, MMMM Do YYYY, H:mm:ss");o=o.charAt(0).toUpperCase()+o.substring(1),this.bar.style.left=i+"px",this.bar.title=o}else this.bar.parentNode&&this.bar.parentNode.removeChild(this.bar),this.stop();return!1},s.prototype.start=function(){function t(){e.stop();var i=e.body.range.conversion(e.body.domProps.center.width).scale,s=1/i/10;30>s&&(s=30),s>1e3&&(s=1e3),e.redraw(),e.currentTimeTimer=setTimeout(t,s)}var e=this;t()},s.prototype.stop=function(){void 0!==this.currentTimeTimer&&(clearTimeout(this.currentTimeTimer),delete this.currentTimeTimer)},s.prototype.setCurrentTime=function(t){var e=o.convert(t,"Date").valueOf(),i=(new Date).valueOf();this.offset=e-i,this.redraw()},s.prototype.getCurrentTime=function(){return new Date((new Date).valueOf()+this.offset)},t.exports=s},function(t,e,i){function s(t,e){this.body=t,this.defaultOptions={showCustomTime:!1,locales:h,locale:"en"},this.options=n.extend({},this.defaultOptions),this.customTime=new Date,this.eventParams={},this._create(),this.setOptions(e)}var o=i(45),n=i(1),r=i(25),a=i(44),h=i(48);s.prototype=new r,s.prototype.setOptions=function(t){t&&n.selectiveExtend(["showCustomTime","locale","locales"],this.options,t)},s.prototype._create=function(){var t=document.createElement("div");t.className="customtime",t.style.position="absolute",t.style.top="0px",t.style.height="100%",this.bar=t;var e=document.createElement("div");e.style.position="relative",e.style.top="0px",e.style.left="-10px",e.style.height="100%",e.style.width="20px",t.appendChild(e),this.hammer=o(t,{prevent_default:!0}),this.hammer.on("dragstart",this._onDragStart.bind(this)),this.hammer.on("drag",this._onDrag.bind(this)),this.hammer.on("dragend",this._onDragEnd.bind(this))},s.prototype.destroy=function(){this.options.showCustomTime=!1,this.redraw(),this.hammer.enable(!1),this.hammer=null,this.body=null},s.prototype.redraw=function(){if(this.options.showCustomTime){var t=this.body.dom.backgroundVertical;this.bar.parentNode!=t&&(this.bar.parentNode&&this.bar.parentNode.removeChild(this.bar),t.appendChild(this.bar));var e=this.body.util.toScreen(this.customTime),i=this.options.locales[this.options.locale],s=i.time+": "+a(this.customTime).format("dddd, MMMM Do YYYY, H:mm:ss");s=s.charAt(0).toUpperCase()+s.substring(1),this.bar.style.left=e+"px",this.bar.title=s}else this.bar.parentNode&&this.bar.parentNode.removeChild(this.bar);return!1},s.prototype.setCustomTime=function(t){this.customTime=n.convert(t,"Date"),this.redraw()},s.prototype.getCustomTime=function(){return new Date(this.customTime.valueOf())},s.prototype._onDragStart=function(t){this.eventParams.dragging=!0,this.eventParams.customTime=this.customTime,t.stopPropagation(),t.preventDefault()},s.prototype._onDrag=function(t){if(this.eventParams.dragging){var e=t.gesture.deltaX,i=this.body.util.toScreen(this.eventParams.customTime)+e,s=this.body.util.toTime(i);this.setCustomTime(s),this.body.emitter.emit("timechange",{time:new Date(this.customTime.valueOf())}),t.stopPropagation(),t.preventDefault()}},s.prototype._onDragEnd=function(t){this.eventParams.dragging&&(this.body.emitter.emit("timechanged",{time:new Date(this.customTime.valueOf())}),t.stopPropagation(),t.preventDefault())},t.exports=s},function(t,e,i){function s(t,e,i,s){this.id=o.randomUUID(),this.body=t,this.defaultOptions={orientation:"left",showMinorLabels:!0,showMajorLabels:!0,icons:!0,majorLinesOffset:7,minorLinesOffset:4,labelOffsetX:10,labelOffsetY:2,iconWidth:20,width:"40px",visible:!0,alignZeros:!0,customRange:{left:{min:void 0,max:void 0},right:{min:void 0,max:void 0}},title:{left:{text:void 0},right:{text:void 0}},format:{left:{decimals:void 0},right:{decimals:void 0}}},this.linegraphOptions=s,this.linegraphSVG=i,this.props={},this.DOMelements={lines:{},labels:{},title:{}},this.dom={},this.range={start:0,end:0},this.options=o.extend({},this.defaultOptions),this.conversionFactor=1,this.setOptions(e),this.width=Number((""+this.options.width).replace("px","")),this.minWidth=this.width,this.height=this.linegraphSVG.offsetHeight,this.hidden=!1,this.stepPixels=25,this.stepPixelsForced=25,this.zeroCrossing=-1,this.lineOffset=0,this.master=!0,this.svgElements={},this.iconsRemoved=!1,this.groups={},this.amountOfGroups=0,this._create();var n=this;this.body.emitter.on("verticalDrag",function(){n.dom.lineContainer.style.top=n.body.domProps.scrollTop+"px"})}var o=i(1),n=i(2),r=i(25),a=i(16);s.prototype=new r,s.prototype.addGroup=function(t,e){this.groups.hasOwnProperty(t)||(this.groups[t]=e),this.amountOfGroups+=1},s.prototype.updateGroup=function(t,e){this.groups[t]=e},s.prototype.removeGroup=function(t){this.groups.hasOwnProperty(t)&&(delete this.groups[t],this.amountOfGroups-=1)},s.prototype.setOptions=function(t){if(t){var e=!1;this.options.orientation!=t.orientation&&void 0!==t.orientation&&(e=!0);var i=["orientation","showMinorLabels","showMajorLabels","icons","majorLinesOffset","minorLinesOffset","labelOffsetX","labelOffsetY","iconWidth","width","visible","customRange","title","format","alignZeros"];o.selectiveExtend(i,this.options,t),this.minWidth=Number((""+this.options.width).replace("px","")),1==e&&this.dom.frame&&(this.hide(),this.show())}},s.prototype._create=function(){this.dom.frame=document.createElement("div"),this.dom.frame.style.width=this.options.width,this.dom.frame.style.height=this.height,this.dom.lineContainer=document.createElement("div"),this.dom.lineContainer.style.width="100%",this.dom.lineContainer.style.height=this.height,this.dom.lineContainer.style.position="relative",this.svg=document.createElementNS("http://www.w3.org/2000/svg","svg"),this.svg.style.position="absolute",this.svg.style.top="0px",this.svg.style.height="100%",this.svg.style.width="100%",this.svg.style.display="block",this.dom.frame.appendChild(this.svg)},s.prototype._redrawGroupIcons=function(){n.prepareElements(this.svgElements);var t,e=this.options.iconWidth,i=15,s=4,o=s+.5*i;t="left"==this.options.orientation?s:this.width-e-s;for(var r in this.groups)this.groups.hasOwnProperty(r)&&(1!=this.groups[r].visible||void 0!==this.linegraphOptions.visibility[r]&&1!=this.linegraphOptions.visibility[r]||(this.groups[r].drawIcon(t,o,this.svgElements,this.svg,e,i),o+=i+s));n.cleanupElements(this.svgElements),this.iconsRemoved=!1},s.prototype._cleanupIcons=function(){0==this.iconsRemoved&&(n.prepareElements(this.svgElements),n.cleanupElements(this.svgElements),this.iconsRemoved=!0)},s.prototype.show=function(){this.hidden=!1,this.dom.frame.parentNode||("left"==this.options.orientation?this.body.dom.left.appendChild(this.dom.frame):this.body.dom.right.appendChild(this.dom.frame)),this.dom.lineContainer.parentNode||this.body.dom.backgroundHorizontal.appendChild(this.dom.lineContainer)},s.prototype.hide=function(){this.hidden=!0,this.dom.frame.parentNode&&this.dom.frame.parentNode.removeChild(this.dom.frame),this.dom.lineContainer.parentNode&&this.dom.lineContainer.parentNode.removeChild(this.dom.lineContainer)},s.prototype.setRange=function(t,e){0==this.master&&1==this.options.alignZeros&&-1!=this.zeroCrossing&&t>0&&(t=0),this.range.start=t,this.range.end=e},s.prototype.redraw=function(){var t=!1,e=0;this.dom.lineContainer.style.top=this.body.domProps.scrollTop+"px";for(var i in this.groups)this.groups.hasOwnProperty(i)&&(1!=this.groups[i].visible||void 0!==this.linegraphOptions.visibility[i]&&1!=this.linegraphOptions.visibility[i]||e++);if(0==this.amountOfGroups||0==e)this.hide();else{this.show(),this.height=Number(this.linegraphSVG.style.height.replace("px","")),this.dom.lineContainer.style.height=this.height+"px",this.width=1==this.options.visible?Number((""+this.options.width).replace("px","")):0;var s=this.props,o=this.dom.frame;o.className="dataaxis",this._calculateCharSize();var n=this.options.orientation,r=this.options.showMinorLabels,a=this.options.showMajorLabels;s.minorLabelHeight=r?s.minorCharHeight:0,s.majorLabelHeight=a?s.majorCharHeight:0,s.minorLineWidth=this.body.dom.backgroundHorizontal.offsetWidth-this.lineOffset-this.width+2*this.options.minorLinesOffset,s.minorLineHeight=1,s.majorLineWidth=this.body.dom.backgroundHorizontal.offsetWidth-this.lineOffset-this.width+2*this.options.majorLinesOffset,s.majorLineHeight=1,"left"==n?(o.style.top="0",o.style.left="0",o.style.bottom="",o.style.width=this.width+"px",o.style.height=this.height+"px",this.props.width=this.body.domProps.left.width,this.props.height=this.body.domProps.left.height):(o.style.top="",o.style.bottom="0",o.style.left="0",o.style.width=this.width+"px",o.style.height=this.height+"px",this.props.width=this.body.domProps.right.width,this.props.height=this.body.domProps.right.height),t=this._redrawLabels(),t=this._isResized()||t,1==this.options.icons?this._redrawGroupIcons():this._cleanupIcons(),this._redrawTitle(n)}return t},s.prototype._redrawLabels=function(){var t=!1; +n.prepareElements(this.DOMelements.lines),n.prepareElements(this.DOMelements.labels);var e=this.options.orientation,i=this.master?this.props.majorCharHeight||10:this.stepPixelsForced,s=new a(this.range.start,this.range.end,i,this.dom.frame.offsetHeight,this.options.customRange[this.options.orientation],0==this.master&&this.options.alignZeros);this.step=s;var o=(this.dom.frame.offsetHeight-s.deadSpace*(this.dom.frame.offsetHeight/s.marginRange))/((s.marginRange-s.deadSpace)/s.step);this.stepPixels=o;var r=this.height/o,h=0;if(0==this.master){o=this.stepPixelsForced,h=Math.round(this.dom.frame.offsetHeight/o-r);for(var d=0;.5*h>d;d++)s.previous();if(r=this.height/o,-1!=this.zeroCrossing&&1==this.options.alignZeros){var l=s.marginEnd/s.step-this.zeroCrossing;if(l>0)for(var d=0;l>d;d++)s.next();else if(0>l)for(var d=0;-l>d;d++)s.previous()}}else r+=.25;this.valueAtZero=s.marginEnd;var c,p=0,u=1;void 0!==this.options.format[e]&&(c=this.options.format[e].decimals),this.maxLabelSize=0;for(var m=0;u=0&&this._redrawLabel(m-2,s.getCurrent(c),e,"yAxis major",this.props.majorCharHeight),this._redrawLine(m,e,"grid horizontal major",this.options.majorLinesOffset,this.props.majorLineWidth)):this._redrawLine(m,e,"grid horizontal minor",this.options.minorLinesOffset,this.props.minorLineWidth),1==this.master&&0==s.current&&(this.zeroCrossing=u),u++}this.conversionFactor=0==this.master?m/(this.valueAtZero-s.current):this.dom.frame.offsetHeight/s.marginRange;var g=0;void 0!==this.options.title[e]&&void 0!==this.options.title[e].text&&(g=this.props.titleCharHeight);var v=1==this.options.icons?Math.max(this.options.iconWidth,g)+this.options.labelOffsetX+15:g+this.options.labelOffsetX+15;return this.maxLabelSize>this.width-v&&1==this.options.visible?(this.width=this.maxLabelSize+v,this.options.width=this.width+"px",n.cleanupElements(this.DOMelements.lines),n.cleanupElements(this.DOMelements.labels),this.redraw(),t=!0):this.maxLabelSizethis.minWidth?(this.width=Math.max(this.minWidth,this.maxLabelSize+v),this.options.width=this.width+"px",n.cleanupElements(this.DOMelements.lines),n.cleanupElements(this.DOMelements.labels),this.redraw(),t=!0):(n.cleanupElements(this.DOMelements.lines),n.cleanupElements(this.DOMelements.labels),t=!1),t},s.prototype.convertValue=function(t){var e=this.valueAtZero-t,i=e*this.conversionFactor;return i},s.prototype._redrawLabel=function(t,e,i,s,o){var r=n.getDOMElement("div",this.DOMelements.labels,this.dom.frame);r.className=s,r.innerHTML=e,"left"==i?(r.style.left="-"+this.options.labelOffsetX+"px",r.style.textAlign="right"):(r.style.right="-"+this.options.labelOffsetX+"px",r.style.textAlign="left"),r.style.top=t-.5*o+this.options.labelOffsetY+"px",e+="";var a=Math.max(this.props.majorCharWidth,this.props.minorCharWidth);this.maxLabelSized;d++){var c=this.visibleItems[d];c.repositionY(e)}return s},s.prototype._calculateHeight=function(t){var e,i=this.visibleItems;this.resetSubgroups();var s=this;if(i.length){var n=i[0].top,r=i[0].top+i[0].height;if(o.forEach(i,function(t){n=Math.min(n,t.top),r=Math.max(r,t.top+t.height),void 0!==t.data.subgroup&&(s.subgroups[t.data.subgroup].height=Math.max(s.subgroups[t.data.subgroup].height,t.height),s.subgroups[t.data.subgroup].visible=!0)}),n>t.axis){var a=n-t.axis;r-=a,o.forEach(i,function(t){t.top-=a})}e=r+t.item.vertical/2}else e=t.axis+t.item.vertical;return e=Math.max(e,this.props.label.height)},s.prototype.show=function(){this.dom.label.parentNode||this.itemSet.dom.labelSet.appendChild(this.dom.label),this.dom.foreground.parentNode||this.itemSet.dom.foreground.appendChild(this.dom.foreground),this.dom.background.parentNode||this.itemSet.dom.background.appendChild(this.dom.background),this.dom.axis.parentNode||this.itemSet.dom.axis.appendChild(this.dom.axis)},s.prototype.hide=function(){var t=this.dom.label;t.parentNode&&t.parentNode.removeChild(t);var e=this.dom.foreground;e.parentNode&&e.parentNode.removeChild(e);var i=this.dom.background;i.parentNode&&i.parentNode.removeChild(i);var s=this.dom.axis;s.parentNode&&s.parentNode.removeChild(s)},s.prototype.add=function(t){if(this.items[t.id]=t,t.setParent(this),void 0!==t.data.subgroup&&(void 0===this.subgroups[t.data.subgroup]&&(this.subgroups[t.data.subgroup]={height:0,visible:!1,index:this.subgroupIndex,items:[]},this.subgroupIndex++),this.subgroups[t.data.subgroup].items.push(t)),this.orderSubgroups(),-1==this.visibleItems.indexOf(t)){var e=this.itemSet.body.range;this._checkIfVisible(t,this.visibleItems,e)}},s.prototype.orderSubgroups=function(){if(void 0!==this.subgroupOrderer){var t=[];if("string"==typeof this.subgroupOrderer){for(var e in this.subgroups)t.push({subgroup:e,sortField:this.subgroups[e].items[0].data[this.subgroupOrderer]});t.sort(function(t,e){return t.sortField-e.sortField})}else if("function"==typeof this.subgroupOrderer){for(var e in this.subgroups)t.push(this.subgroups[e].items[0].data);t.sort(this.subgroupOrderer)}if(t.length>0)for(var i=0;it?-1:l>=t?0:1};if(e.length>0)for(n=0;nl}),1==this.checkRangedItems)for(this.checkRangedItems=!1,n=0;nl})}for(n=0;n=0&&(n=e[r],!o(n));r--)void 0===s[n.id]&&(s[n.id]=!0,i.push(n));for(r=t+1;rs;s++){var n=this.visibleItems[s];n.repositionY(e)}return i},s.prototype.show=function(){this.dom.background.parentNode||this.itemSet.dom.background.appendChild(this.dom.background)},t.exports=s},function(t,e,i){function s(t,e){this.body=t,this.defaultOptions={type:null,orientation:"bottom",align:"auto",stack:!0,groupOrder:null,selectable:!0,editable:{updateTime:!1,updateGroup:!1,add:!1,remove:!1},onAdd:function(t,e){e(t)},onUpdate:function(t,e){e(t)},onMove:function(t,e){e(t)},onRemove:function(t,e){e(t)},onMoving:function(t,e){e(t)},margin:{item:{horizontal:10,vertical:10},axis:20},padding:5},this.options=n.extend({},this.defaultOptions),this.itemOptions={type:{start:"Date",end:"Date"}},this.conversion={toScreen:t.util.toScreen,toTime:t.util.toTime},this.dom={},this.props={},this.hammer=null;var i=this;this.itemsData=null,this.groupsData=null,this.itemListeners={add:function(t,e){i._onAdd(e.items)},update:function(t,e){i._onUpdate(e.items)},remove:function(t,e){i._onRemove(e.items)}},this.groupListeners={add:function(t,e){i._onAddGroups(e.items)},update:function(t,e){i._onUpdateGroups(e.items)},remove:function(t,e){i._onRemoveGroups(e.items)}},this.items={},this.groups={},this.groupIds=[],this.selection=[],this.stackDirty=!0,this.touchParams={},this._create(),this.setOptions(e)}var o=i(45),n=i(1),r=i(3),a=i(4),h=i(25),d=i(30),l=i(31),c=i(22),p=i(23),u=i(24),m=i(21),f="__ungrouped__",g="__background__";s.prototype=new h,s.types={background:m,box:c,range:u,point:p},s.prototype._create=function(){var t=document.createElement("div");t.className="itemset",t["timeline-itemset"]=this,this.dom.frame=t;var e=document.createElement("div");e.className="background",t.appendChild(e),this.dom.background=e;var i=document.createElement("div");i.className="foreground",t.appendChild(i),this.dom.foreground=i;var s=document.createElement("div");s.className="axis",this.dom.axis=s;var n=document.createElement("div");n.className="labelset",this.dom.labelSet=n,this._updateUngrouped();var r=new l(g,null,this);r.show(),this.groups[g]=r,this.hammer=o(this.body.dom.centerContainer,{preventDefault:!0}),this.hammer.on("touch",this._onTouch.bind(this)),this.hammer.on("dragstart",this._onDragStart.bind(this)),this.hammer.on("drag",this._onDrag.bind(this)),this.hammer.on("dragend",this._onDragEnd.bind(this)),this.hammer.on("tap",this._onSelectItem.bind(this)),this.hammer.on("hold",this._onMultiSelectItem.bind(this)),this.hammer.on("doubletap",this._onAddItem.bind(this)),this.show()},s.prototype.setOptions=function(t){if(t){var e=["type","align","orientation","padding","stack","selectable","groupOrder","dataAttributes","template","hide"];n.selectiveExtend(e,this.options,t),"margin"in t&&("number"==typeof t.margin?(this.options.margin.axis=t.margin,this.options.margin.item.horizontal=t.margin,this.options.margin.item.vertical=t.margin):"object"==typeof t.margin&&(n.selectiveExtend(["axis"],this.options.margin,t.margin),"item"in t.margin&&("number"==typeof t.margin.item?(this.options.margin.item.horizontal=t.margin.item,this.options.margin.item.vertical=t.margin.item):"object"==typeof t.margin.item&&n.selectiveExtend(["horizontal","vertical"],this.options.margin.item,t.margin.item)))),"editable"in t&&("boolean"==typeof t.editable?(this.options.editable.updateTime=t.editable,this.options.editable.updateGroup=t.editable,this.options.editable.add=t.editable,this.options.editable.remove=t.editable):"object"==typeof t.editable&&n.selectiveExtend(["updateTime","updateGroup","add","remove"],this.options.editable,t.editable));var i=function(e){var i=t[e];if(i){if(!(i instanceof Function))throw new Error("option "+e+" must be a function "+e+"(item, callback)");this.options[e]=i}}.bind(this);["onAdd","onUpdate","onRemove","onMove","onMoving"].forEach(i),this.markDirty()}},s.prototype.markDirty=function(){this.groupIds=[],this.stackDirty=!0},s.prototype.destroy=function(){this.hide(),this.setItems(null),this.setGroups(null),this.hammer=null,this.body=null,this.conversion=null},s.prototype.hide=function(){this.dom.frame.parentNode&&this.dom.frame.parentNode.removeChild(this.dom.frame),this.dom.axis.parentNode&&this.dom.axis.parentNode.removeChild(this.dom.axis),this.dom.labelSet.parentNode&&this.dom.labelSet.parentNode.removeChild(this.dom.labelSet)},s.prototype.show=function(){this.dom.frame.parentNode||this.body.dom.center.appendChild(this.dom.frame),this.dom.axis.parentNode||this.body.dom.backgroundVertical.appendChild(this.dom.axis),this.dom.labelSet.parentNode||this.body.dom.left.appendChild(this.dom.labelSet)},s.prototype.setSelection=function(t){var e,i,s,o;for(void 0==t&&(t=[]),Array.isArray(t)||(t=[t]),e=0,i=this.selection.length;i>e;e++)s=this.selection[e],o=this.items[s],o&&o.unselect();for(this.selection=[],e=0,i=t.length;i>e;e++)s=t[e],o=this.items[s],o&&(this.selection.push(s),o.select())},s.prototype.getSelection=function(){return this.selection.concat([])},s.prototype.getVisibleItems=function(){var t=this.body.range.getRange(),e=this.body.util.toScreen(t.start),i=this.body.util.toScreen(t.end),s=[];for(var o in this.groups)if(this.groups.hasOwnProperty(o))for(var n=this.groups[o],r=n.visibleItems,a=0;ae&&s.push(h.id)}return s},s.prototype._deselect=function(t){for(var e=this.selection,i=0,s=e.length;s>i;i++)if(e[i]==t){e.splice(i,1);break}},s.prototype.redraw=function(){var t=this.options.margin,e=this.body.range,i=n.option.asSize,s=this.options,o=s.orientation,r=!1,a=this.dom.frame,h=s.editable.updateTime||s.editable.updateGroup;this.props.top=this.body.domProps.top.height+this.body.domProps.border.top,this.props.left=this.body.domProps.left.width+this.body.domProps.border.left,a.className="itemset"+(h?" editable":""),r=this._orderGroups()||r;var d=e.end-e.start,l=d!=this.lastVisibleInterval||this.props.width!=this.props.lastWidth;l&&(this.stackDirty=!0),this.lastVisibleInterval=d,this.props.lastWidth=this.props.width;var c=this.stackDirty,p=this._firstGroup(),u={item:t.item,axis:t.axis},m={item:t.item,axis:t.item.vertical/2},f=0,v=t.axis+t.item.vertical;return this.groups[g].redraw(e,m,c),n.forEach(this.groups,function(t){var i=t==p?u:m,s=t.redraw(e,i,c);r=s||r,f+=t.height}),f=Math.max(f,v),this.stackDirty=!1,a.style.height=i(f),this.props.width=a.offsetWidth,this.props.height=f,this.dom.axis.style.top=i("top"==o?this.body.domProps.top.height+this.body.domProps.border.top:this.body.domProps.top.height+this.body.domProps.centerContainer.height),this.dom.axis.style.left="0",r=this._isResized()||r},s.prototype._firstGroup=function(){var t="top"==this.options.orientation?0:this.groupIds.length-1,e=this.groupIds[t],i=this.groups[e]||this.groups[f];return i||null},s.prototype._updateUngrouped=function(){{var t,e,i=this.groups[f];this.groups[g]}if(this.groupsData){if(i){i.hide(),delete this.groups[f];for(e in this.items)if(this.items.hasOwnProperty(e)){t=this.items[e],t.parent&&t.parent.remove(t);var s=this._getGroupId(t.data),o=this.groups[s];o&&o.add(t)||t.hide()}}}else if(!i){var n=null,r=null;i=new d(n,r,this),this.groups[f]=i;for(e in this.items)this.items.hasOwnProperty(e)&&(t=this.items[e],i.add(t));i.show()}},s.prototype.getLabelSet=function(){return this.dom.labelSet},s.prototype.setItems=function(t){var e,i=this,s=this.itemsData;if(t){if(!(t instanceof r||t instanceof a))throw new TypeError("Data must be an instance of DataSet or DataView");this.itemsData=t}else this.itemsData=null;if(s&&(n.forEach(this.itemListeners,function(t,e){s.off(e,t)}),e=s.getIds(),this._onRemove(e)),this.itemsData){var o=this.id;n.forEach(this.itemListeners,function(t,e){i.itemsData.on(e,t,o)}),e=this.itemsData.getIds(),this._onAdd(e),this._updateUngrouped()}},s.prototype.getItems=function(){return this.itemsData},s.prototype.setGroups=function(t){var e,i=this;if(this.groupsData&&(n.forEach(this.groupListeners,function(t,e){i.groupsData.unsubscribe(e,t)}),e=this.groupsData.getIds(),this.groupsData=null,this._onRemoveGroups(e)),t){if(!(t instanceof r||t instanceof a))throw new TypeError("Data must be an instance of DataSet or DataView");this.groupsData=t}else this.groupsData=null;if(this.groupsData){var s=this.id;n.forEach(this.groupListeners,function(t,e){i.groupsData.on(e,t,s)}),e=this.groupsData.getIds(),this._onAddGroups(e)}this._updateUngrouped(),this._order(),this.body.emitter.emit("change",{queue:!0})},s.prototype.getGroups=function(){return this.groupsData},s.prototype.removeItem=function(t){var e=this.itemsData.get(t),i=this.itemsData.getDataSet();e&&this.options.onRemove(e,function(e){e&&i.remove(t)})},s.prototype._getType=function(t){return t.type||this.options.type||(t.end?"range":"box")},s.prototype._getGroupId=function(t){var e=this._getType(t);return"background"==e&&void 0==t.group?g:this.groupsData?t.group:f},s.prototype._onUpdate=function(t){var e=this;t.forEach(function(t){var i=e.itemsData.get(t,e.itemOptions),o=e.items[t],n=e._getType(i),r=s.types[n];if(o&&(r&&o instanceof r?e._updateItem(o,i):(e._removeItem(o),o=null)),!o){if(!r)throw new TypeError("rangeoverflow"==n?'Item type "rangeoverflow" is deprecated. Use css styling instead: .vis.timeline .item.range .content {overflow: visible;}':'Unknown item type "'+n+'"');o=new r(i,e.conversion,e.options),o.id=t,e._addItem(o)}}),this._order(),this.stackDirty=!0,this.body.emitter.emit("change",{queue:!0})},s.prototype._onAdd=s.prototype._onUpdate,s.prototype._onRemove=function(t){var e=0,i=this;t.forEach(function(t){var s=i.items[t];s&&(e++,i._removeItem(s))}),e&&(this._order(),this.stackDirty=!0,this.body.emitter.emit("change",{queue:!0}))},s.prototype._order=function(){n.forEach(this.groups,function(t){t.order()})},s.prototype._onUpdateGroups=function(t){this._onAddGroups(t)},s.prototype._onAddGroups=function(t){var e=this;t.forEach(function(t){var i=e.groupsData.get(t),s=e.groups[t];if(s)s.setData(i);else{if(t==f||t==g)throw new Error("Illegal group id. "+t+" is a reserved id.");var o=Object.create(e.options);n.extend(o,{height:null}),s=new d(t,i,e),e.groups[t]=s;for(var r in e.items)if(e.items.hasOwnProperty(r)){var a=e.items[r];a.data.group==t&&s.add(a)}s.order(),s.show()}}),this.body.emitter.emit("change",{queue:!0})},s.prototype._onRemoveGroups=function(t){var e=this.groups;t.forEach(function(t){var i=e[t];i&&(i.hide(),delete e[t])}),this.markDirty(),this.body.emitter.emit("change",{queue:!0})},s.prototype._orderGroups=function(){if(this.groupsData){var t=this.groupsData.getIds({order:this.options.groupOrder}),e=!n.equalArray(t,this.groupIds);if(e){var i=this.groups;t.forEach(function(t){i[t].hide()}),t.forEach(function(t){i[t].show()}),this.groupIds=t}return e}return!1},s.prototype._addItem=function(t){this.items[t.id]=t;var e=this._getGroupId(t.data),i=this.groups[e];i&&i.add(t)},s.prototype._updateItem=function(t,e){var i=t.data.group;if(t.setData(e),i!=t.data.group){var s=this.groups[i];s&&s.remove(t);var o=this._getGroupId(t.data),n=this.groups[o];n&&n.add(t)}},s.prototype._removeItem=function(t){t.hide(),delete this.items[t.id];var e=this.selection.indexOf(t.id);-1!=e&&this.selection.splice(e,1),t.parent&&t.parent.remove(t)},s.prototype._constructByEndArray=function(t){for(var e=[],i=0;i0||o.length>0)&&this.body.emitter.emit("select",{items:a})}},s.prototype._onAddItem=function(t){if(this.options.selectable&&this.options.editable.add){var e=this,i=this.body.util.snap||null,o=s.itemFromTarget(t);if(o){var r=e.itemsData.get(o.id);this.options.onUpdate(r,function(t){t&&e.itemsData.getDataSet().update(t)})}else{var a=n.getAbsoluteLeft(this.dom.frame),h=t.gesture.center.pageX-a,d=this.body.util.toTime(h),l={start:i?i(d):d,content:"new item"};if("range"===this.options.type){var c=this.body.util.toTime(h+this.props.width/5);l.end=i?i(c):c}l[this.itemsData._fieldId]=n.randomUUID();var p=s.groupFromTarget(t);p&&(l.group=p.groupId),this.options.onAdd(l,function(t){t&&e.itemsData.getDataSet().add(t)})}}},s.prototype._onMultiSelectItem=function(t){if(this.options.selectable){var e,i=s.itemFromTarget(t);if(i){e=this.getSelection();var o=t.gesture.touches[0]&&t.gesture.touches[0].shiftKey||!1;if(o){e.push(i.id);var n=s._getItemRange(this.itemsData.get(e,this.itemOptions));e=[];for(var r in this.items)if(this.items.hasOwnProperty(r)){var a=this.items[r],h=a.data.start,d=void 0!==a.data.end?a.data.end:h;h>=n.min&&d<=n.max&&e.push(a.id)}}else{var l=e.indexOf(i.id);-1==l?e.push(i.id):e.splice(l,1)}this.setSelection(e),this.body.emitter.emit("select",{items:this.getSelection()})}}},s._getItemRange=function(t){var e=null,i=null;return t.forEach(function(t){(null==i||t.starte)&&(e=t.end):(null==e||t.start>e)&&(e=t.start)}),{min:i,max:e}},s.itemFromTarget=function(t){for(var e=t.target;e;){if(e.hasOwnProperty("timeline-item"))return e["timeline-item"];e=e.parentNode}return null},s.groupFromTarget=function(t){for(var e=t.target;e;){if(e.hasOwnProperty("timeline-group"))return e["timeline-group"];e=e.parentNode}return null},s.itemSetFromTarget=function(t){for(var e=t.target;e;){if(e.hasOwnProperty("timeline-itemset"))return e["timeline-itemset"]; +e=e.parentNode}return null},t.exports=s},function(t,e,i){function s(t,e,i,s){this.body=t,this.defaultOptions={enabled:!0,icons:!0,iconSize:20,iconSpacing:6,left:{visible:!0,position:"top-left"},right:{visible:!0,position:"top-left"}},this.side=i,this.options=o.extend({},this.defaultOptions),this.linegraphOptions=s,this.svgElements={},this.dom={},this.groups={},this.amountOfGroups=0,this._create(),this.setOptions(e)}var o=i(1),n=i(2),r=i(25);s.prototype=new r,s.prototype.clear=function(){this.groups={},this.amountOfGroups=0},s.prototype.addGroup=function(t,e){this.groups.hasOwnProperty(t)||(this.groups[t]=e),this.amountOfGroups+=1},s.prototype.updateGroup=function(t,e){this.groups[t]=e},s.prototype.removeGroup=function(t){this.groups.hasOwnProperty(t)&&(delete this.groups[t],this.amountOfGroups-=1)},s.prototype._create=function(){this.dom.frame=document.createElement("div"),this.dom.frame.className="legend",this.dom.frame.style.position="absolute",this.dom.frame.style.top="10px",this.dom.frame.style.display="block",this.dom.textArea=document.createElement("div"),this.dom.textArea.className="legendText",this.dom.textArea.style.position="relative",this.dom.textArea.style.top="0px",this.svg=document.createElementNS("http://www.w3.org/2000/svg","svg"),this.svg.style.position="absolute",this.svg.style.top="0px",this.svg.style.width=this.options.iconSize+5+"px",this.svg.style.height="100%",this.dom.frame.appendChild(this.svg),this.dom.frame.appendChild(this.dom.textArea)},s.prototype.hide=function(){this.dom.frame.parentNode&&this.dom.frame.parentNode.removeChild(this.dom.frame)},s.prototype.show=function(){this.dom.frame.parentNode||this.body.dom.center.appendChild(this.dom.frame)},s.prototype.setOptions=function(t){var e=["enabled","orientation","icons","left","right"];o.selectiveDeepExtend(e,this.options,t)},s.prototype.redraw=function(){var t=0;for(var e in this.groups)this.groups.hasOwnProperty(e)&&(1!=this.groups[e].visible||void 0!==this.linegraphOptions.visibility[e]&&1!=this.linegraphOptions.visibility[e]||t++);if(0==this.options[this.side].visible||0==this.amountOfGroups||0==this.options.enabled||0==t)this.hide();else{if(this.show(),"top-left"==this.options[this.side].position||"bottom-left"==this.options[this.side].position?(this.dom.frame.style.left="4px",this.dom.frame.style.textAlign="left",this.dom.textArea.style.textAlign="left",this.dom.textArea.style.left=this.options.iconSize+15+"px",this.dom.textArea.style.right="",this.svg.style.left="0px",this.svg.style.right=""):(this.dom.frame.style.right="4px",this.dom.frame.style.textAlign="right",this.dom.textArea.style.textAlign="right",this.dom.textArea.style.right=this.options.iconSize+15+"px",this.dom.textArea.style.left="",this.svg.style.right="0px",this.svg.style.left=""),"top-left"==this.options[this.side].position||"top-right"==this.options[this.side].position)this.dom.frame.style.top=4-Number(this.body.dom.center.style.top.replace("px",""))+"px",this.dom.frame.style.bottom="";else{var i=this.body.domProps.center.height-this.body.domProps.centerContainer.height;this.dom.frame.style.bottom=4+i+Number(this.body.dom.center.style.top.replace("px",""))+"px",this.dom.frame.style.top=""}0==this.options.icons?(this.dom.frame.style.width=this.dom.textArea.offsetWidth+10+"px",this.dom.textArea.style.right="",this.dom.textArea.style.left="",this.svg.style.width="0px"):(this.dom.frame.style.width=this.options.iconSize+15+this.dom.textArea.offsetWidth+10+"px",this.drawLegendIcons());var s="";for(var e in this.groups)this.groups.hasOwnProperty(e)&&(1!=this.groups[e].visible||void 0!==this.linegraphOptions.visibility[e]&&1!=this.linegraphOptions.visibility[e]||(s+=this.groups[e].content+"
"));this.dom.textArea.innerHTML=s,this.dom.textArea.style.lineHeight=.75*this.options.iconSize+this.options.iconSpacing+"px"}},s.prototype.drawLegendIcons=function(){if(this.dom.frame.parentNode){n.prepareElements(this.svgElements);var t=window.getComputedStyle(this.dom.frame).paddingTop,e=Number(t.replace("px","")),i=e,s=this.options.iconSize,o=.75*this.options.iconSize,r=e+.5*o+3;this.svg.style.width=s+5+e+"px";for(var a in this.groups)this.groups.hasOwnProperty(a)&&(1!=this.groups[a].visible||void 0!==this.linegraphOptions.visibility[a]&&1!=this.linegraphOptions.visibility[a]||(this.groups[a].drawIcon(i,r,this.svgElements,this.svg,s,o),r+=o+this.options.iconSpacing));n.cleanupElements(this.svgElements)}},t.exports=s},function(t,e,i){function s(t,e){this.id=o.randomUUID(),this.body=t,this.defaultOptions={yAxisOrientation:"left",defaultGroup:"default",sort:!0,sampling:!0,graphHeight:"400px",shaded:{enabled:!1,orientation:"bottom"},style:"line",barChart:{width:50,handleOverlap:"overlap",align:"center"},catmullRom:{enabled:!0,parametrization:"centripetal",alpha:.5},drawPoints:{enabled:!0,size:6,style:"square"},dataAxis:{showMinorLabels:!0,showMajorLabels:!0,icons:!1,width:"40px",visible:!0,alignZeros:!0,customRange:{left:{min:void 0,max:void 0},right:{min:void 0,max:void 0}}},legend:{enabled:!1,icons:!0,left:{visible:!0,position:"top-left"},right:{visible:!0,position:"top-right"}},groups:{visibility:{}}},this.options=o.extend({},this.defaultOptions),this.dom={},this.props={},this.hammer=null,this.groups={},this.abortedGraphUpdate=!1,this.updateSVGheight=!1,this.updateSVGheightOnResize=!1;var i=this;this.itemsData=null,this.groupsData=null,this.itemListeners={add:function(t,e){i._onAdd(e.items)},update:function(t,e){i._onUpdate(e.items)},remove:function(t,e){i._onRemove(e.items)}},this.groupListeners={add:function(t,e){i._onAddGroups(e.items)},update:function(t,e){i._onUpdateGroups(e.items)},remove:function(t,e){i._onRemoveGroups(e.items)}},this.items={},this.selection=[],this.lastStart=this.body.range.start,this.touchParams={},this.svgElements={},this.setOptions(e),this.groupsUsingDefaultStyles=[0],this.COUNTER=0,this.body.emitter.on("rangechanged",function(){i.lastStart=i.body.range.start,i.svg.style.left=o.option.asSize(-i.props.width),i.redraw.call(i,!0)}),this._create(),this.framework={svg:this.svg,svgElements:this.svgElements,options:this.options,groups:this.groups},this.body.emitter.emit("change")}var o=i(1),n=i(2),r=i(3),a=i(4),h=i(25),d=i(28),l=i(29),c=i(33),p=i(50),u="__ungrouped__";s.prototype=new h,s.prototype._create=function(){var t=document.createElement("div");t.className="LineGraph",this.dom.frame=t,this.svg=document.createElementNS("http://www.w3.org/2000/svg","svg"),this.svg.style.position="relative",this.svg.style.height=(""+this.options.graphHeight).replace("px","")+"px",this.svg.style.display="block",t.appendChild(this.svg),this.options.dataAxis.orientation="left",this.yAxisLeft=new d(this.body,this.options.dataAxis,this.svg,this.options.groups),this.options.dataAxis.orientation="right",this.yAxisRight=new d(this.body,this.options.dataAxis,this.svg,this.options.groups),delete this.options.dataAxis.orientation,this.legendLeft=new c(this.body,this.options.legend,"left",this.options.groups),this.legendRight=new c(this.body,this.options.legend,"right",this.options.groups),this.show()},s.prototype.setOptions=function(t){if(t){var e=["sampling","defaultGroup","height","graphHeight","yAxisOrientation","style","barChart","dataAxis","sort","groups"];void 0===t.graphHeight&&void 0!==t.height&&void 0!==this.body.domProps.centerContainer.height?(this.updateSVGheight=!0,this.updateSVGheightOnResize=!0):void 0!==this.body.domProps.centerContainer.height&&void 0!==t.graphHeight&&parseInt((t.graphHeight+"").replace("px",""))0){var d=this.body.util.toGlobalTime(-this.body.domProps.root.width),l=this.body.util.toGlobalTime(2*this.body.domProps.root.width),c={};for(this._getRelevantData(a,c,d,l),this._applySampling(a,c),e=0;eu&&console.log("WARNING: there may be an infinite loop in the _updateGraph emitter cycle."),this.COUNTER=0,this.abortedGraphUpdate=!1,e=0;e0)for(r=0;rs){d.push(h);break}d.push(h)}}else for(a=0;ai&&h.x0)for(var s=0;s0){var n=1,r=o.length,a=this.body.util.toGlobalScreen(o[o.length-1].x)-this.body.util.toGlobalScreen(o[0].x),h=r/a;n=Math.min(Math.ceil(.2*r),Math.max(1,Math.round(h)));for(var d=[],l=0;r>l;l+=n)d.push(o[l]);e[t[s]]=d}}},s.prototype._getYRanges=function(t,e,i){var s,o,n,r,a=[],h=[];if(t.length>0){for(n=0;n0&&(o=this.groups[t[n]],"stack"==r.barChart.handleOverlap&&"bar"==r.style?"left"==r.yAxisOrientation?a=a.concat(o.getYRange(s)):h=h.concat(o.getYRange(s)):i[t[n]]=o.getYRange(s,t[n]));p.getStackedBarYRange(a,i,t,"__barchartLeft","left"),p.getStackedBarYRange(h,i,t,"__barchartRight","right")}},s.prototype._updateYAxis=function(t,e){var i,s,o=!1,n=!1,r=!1,a=1e9,h=1e9,d=-1e9,l=-1e9;if(t.length>0){for(var c=0;ci?i:a,d=s>d?s:d):(r=!0,h=h>i?i:h,l=s>l?s:l));1==n&&this.yAxisLeft.setRange(a,d),1==r&&this.yAxisRight.setRange(h,l)}return o=this._toggleAxisVisiblity(n,this.yAxisLeft)||o,o=this._toggleAxisVisiblity(r,this.yAxisRight)||o,1==r&&1==n?(this.yAxisLeft.drawIcons=!0,this.yAxisRight.drawIcons=!0):(this.yAxisLeft.drawIcons=!1,this.yAxisRight.drawIcons=!1),this.yAxisRight.master=!n,0==this.yAxisRight.master?(this.yAxisLeft.lineOffset=1==r?this.yAxisRight.width:0,o=this.yAxisLeft.redraw()||o,this.yAxisRight.stepPixelsForced=this.yAxisLeft.stepPixels,this.yAxisRight.zeroCrossing=this.yAxisLeft.zeroCrossing,o=this.yAxisRight.redraw()||o):o=this.yAxisRight.redraw()||o,-1!=t.indexOf("__barchartLeft")&&t.splice(t.indexOf("__barchartLeft"),1),-1!=t.indexOf("__barchartRight")&&t.splice(t.indexOf("__barchartRight"),1),o},s.prototype._toggleAxisVisiblity=function(t,e){var i=!1;return 0==t?e.dom.frame.parentNode&&0==e.hidden&&(e.hide(),i=!0):e.dom.frame.parentNode||1!=e.hidden||(e.show(),i=!0),i},s.prototype._convertXcoordinates=function(t){for(var e,i,s=[],o=this.body.util.toScreen,n=0;ny;)y++,l=h.getCurrent(),c=h.isMajor(),u=h.getClassName(),f=m,m=this.body.util.toScreen(l),g=m-f,p&&(p.style.width=g+"px"),this.options.showMinorLabels&&this._repaintMinorText(m,h.getLabelMinor(),t,u),c&&this.options.showMajorLabels?(m>0&&(void 0==v&&(v=m),this._repaintMajorText(m,h.getLabelMajor(),t,u)),p=this._repaintMajorLine(m,t,u)):p=this._repaintMinorLine(m,t,u),h.next();if(this.options.showMajorLabels){var b=this.body.util.toTime(0),_=h.getLabelMajor(b),x=_.length*(this.props.majorCharWidth||10)+10;(void 0==v||v>x)&&this._repaintMajorText(0,_,t,u)}o.forEach(this.dom.redundant,function(t){for(;t.length;){var e=t.pop();e&&e.parentNode&&e.parentNode.removeChild(e)}})},s.prototype._repaintMinorText=function(t,e,i,s){var o=this.dom.redundant.minorTexts.shift();if(!o){var n=document.createTextNode("");o=document.createElement("div"),o.appendChild(n),this.dom.foreground.appendChild(o)}this.dom.minorTexts.push(o),o.childNodes[0].nodeValue=e,o.style.top="top"==i?this.props.majorLabelHeight+"px":"0",o.style.left=t+"px",o.className="text minor "+s},s.prototype._repaintMajorText=function(t,e,i,s){var o=this.dom.redundant.majorTexts.shift();if(!o){var n=document.createTextNode(e);o=document.createElement("div"),o.appendChild(n),this.dom.foreground.appendChild(o)}this.dom.majorTexts.push(o),o.childNodes[0].nodeValue=e,o.className="text major "+s,o.style.top="top"==i?"0":this.props.minorLabelHeight+"px",o.style.left=t+"px"},s.prototype._repaintMinorLine=function(t,e,i){var s=this.dom.redundant.lines.shift();s||(s=document.createElement("div"),this.dom.background.appendChild(s)),this.dom.lines.push(s);var o=this.props;return s.style.top="top"==e?o.majorLabelHeight+"px":this.body.domProps.top.height+"px",s.style.height=o.minorLineHeight+"px",s.style.left=t-o.minorLineWidth/2+"px",s.className="grid vertical minor "+i,s},s.prototype._repaintMajorLine=function(t,e,i){var s=this.dom.redundant.lines.shift();s||(s=document.createElement("div"),this.dom.background.appendChild(s)),this.dom.lines.push(s);var o=this.props;return s.style.top="top"==e?"0":this.body.domProps.top.height+"px",s.style.left=t-o.majorLineWidth/2+"px",s.style.height=o.majorLineHeight+"px",s.className="grid vertical major "+i,s},s.prototype._calculateCharSize=function(){this.dom.measureCharMinor||(this.dom.measureCharMinor=document.createElement("DIV"),this.dom.measureCharMinor.className="text minor measure",this.dom.measureCharMinor.style.position="absolute",this.dom.measureCharMinor.appendChild(document.createTextNode("0")),this.dom.foreground.appendChild(this.dom.measureCharMinor)),this.props.minorCharHeight=this.dom.measureCharMinor.clientHeight,this.props.minorCharWidth=this.dom.measureCharMinor.clientWidth,this.dom.measureCharMajor||(this.dom.measureCharMajor=document.createElement("DIV"),this.dom.measureCharMajor.className="text major measure",this.dom.measureCharMajor.style.position="absolute",this.dom.measureCharMajor.appendChild(document.createTextNode("0")),this.dom.foreground.appendChild(this.dom.measureCharMajor)),this.props.majorCharHeight=this.dom.measureCharMajor.clientHeight,this.props.majorCharWidth=this.dom.measureCharMajor.clientWidth},s.prototype.snap=function(t){return this.step.snap(t)},t.exports=s},function(t,e,i){function s(t,e,i){if(!(this instanceof s))throw new SyntaxError("Constructor must be called with the new operator");this._determineBrowserMethod(),this._initializeMixinLoaders(),this.containerElement=t,this.renderRefreshRate=60,this.renderTimestep=1e3/this.renderRefreshRate,this.renderTime=0,this.physicsTime=0,this.runDoubleSpeed=!1,this.physicsDiscreteStepsize=.5,this.initializing=!0,this.triggerFunctions={add:null,edit:null,editEdge:null,connect:null,del:null},this.defaultOptions={nodes:{mass:1,radiusMin:10,radiusMax:30,radius:10,shape:"ellipse",image:void 0,widthMin:16,widthMax:64,fontColor:"black",fontSize:14,fontFace:"verdana",fontFill:void 0,fontStrokeWidth:0,fontStrokeColor:"white",level:-1,color:{border:"#2B7CE9",background:"#97C2FC",highlight:{border:"#2B7CE9",background:"#D2E5FF"},hover:{border:"#2B7CE9",background:"#D2E5FF"}},group:void 0,borderWidth:1,borderWidthSelected:void 0},edges:{widthMin:1,widthMax:15,width:1,widthSelectionMultiplier:2,hoverWidth:1.5,style:"line",color:{color:"#848484",highlight:"#848484",hover:"#848484"},fontColor:"#343434",fontSize:14,fontFace:"arial",fontFill:"white",fontStrokeWidth:0,fontStrokeColor:"white",labelAlignment:"horizontal",arrowScaleFactor:1,dash:{length:10,gap:5,altLength:void 0},inheritColor:"from"},configurePhysics:!1,physics:{barnesHut:{enabled:!0,thetaInverted:2,gravitationalConstant:-2e3,centralGravity:.3,springLength:95,springConstant:.04,damping:.09},repulsion:{centralGravity:0,springLength:200,springConstant:.05,nodeDistance:100,damping:.09},hierarchicalRepulsion:{enabled:!1,centralGravity:0,springLength:100,springConstant:.01,nodeDistance:150,damping:.09},damping:null,centralGravity:null,springLength:null,springConstant:null},clustering:{enabled:!1,initialMaxNodes:100,clusterThreshold:500,reduceToNodes:300,chainThreshold:.4,clusterEdgeThreshold:20,sectorThreshold:100,screenSizeThreshold:.2,fontSizeMultiplier:4,maxFontSize:1e3,forceAmplification:.1,distanceAmplification:.1,edgeGrowth:20,nodeScaling:{width:1,height:1,radius:1},maxNodeSizeIncrements:600,activeAreaBoxSize:80,clusterLevelDifference:2,clusterByZoom:!0},navigation:{enabled:!1},keyboard:{enabled:!1,speed:{x:10,y:10,zoom:.02},bindToWindow:!0},dataManipulation:{enabled:!1,initiallyVisible:!1},hierarchicalLayout:{enabled:!1,levelSeparation:150,nodeSpacing:100,direction:"UD",layout:"hubsize"},freezeForStabilization:!1,smoothCurves:{enabled:!0,dynamic:!0,type:"continuous",roundness:.5},maxVelocity:30,minVelocity:.1,stabilize:!0,stabilizationIterations:1e3,zoomExtentOnStabilize:!0,locale:"en",locales:_,tooltip:{delay:300,fontColor:"black",fontSize:14,fontFace:"verdana",color:{border:"#666",background:"#FFFFC6"}},dragNetwork:!0,dragNodes:!0,zoomable:!0,hover:!1,hideEdgesOnDrag:!1,hideNodesOnDrag:!1,width:"100%",height:"100%",selectable:!0},this.constants=a.extend({},this.defaultOptions),this.pixelRatio=1,this.hoverObj={nodes:{},edges:{}},this.controlNodesActive=!1,this.navigationHammers={existing:[],_new:[]},this.animationSpeed=1/this.renderRefreshRate,this.animationEasingFunction="easeInOutQuint",this.animating=!1,this.easingTime=0,this.sourceScale=0,this.targetScale=0,this.sourceTranslation=0,this.targetTranslation=0,this.lockedOnNodeId=null,this.lockedOnNodeOffset=null,this.touchTime=0;var o=this;this.groups=new u,this.images=new m,this.images.setOnloadCallback(function(){o._redraw()}),this.xIncrement=0,this.yIncrement=0,this.zoomIncrement=0,this._loadPhysicsSystem(),this._create(),this._loadSectorSystem(),this._loadClusterSystem(),this._loadSelectionSystem(),this._loadHierarchySystem(),this._setTranslation(this.frame.clientWidth/2,this.frame.clientHeight/2),this._setScale(1),this.setOptions(i),this.freezeSimulation=!1,this.cachedFunctions={},this.startedStabilization=!1,this.stabilized=!1,this.stabilizationIterations=null,this.draggingNodes=!1,this.calculationNodes={},this.calculationNodeIndices=[],this.nodeIndices=[],this.nodes={},this.edges={},this.canvasTopLeft={x:0,y:0},this.canvasBottomRight={x:0,y:0},this.pointerPosition={x:0,y:0},this.areaCenter={},this.scale=1,this.previousScale=this.scale,this.nodesData=null,this.edgesData=null,this.nodesListeners={add:function(t,e){o._addNodes(e.items),o.start()},update:function(t,e){o._updateNodes(e.items,e.data),o.start()},remove:function(t,e){o._removeNodes(e.items),o.start()}},this.edgesListeners={add:function(t,e){o._addEdges(e.items),o.start()},update:function(t,e){o._updateEdges(e.items),o.start()},remove:function(t,e){o._removeEdges(e.items),o.start()}},this.moving=!0,this.timer=void 0,this.setData(e,this.constants.clustering.enabled||this.constants.hierarchicalLayout.enabled),this.initializing=!1,1==this.constants.hierarchicalLayout.enabled?this._setupHierarchicalLayout():0==this.constants.stabilize&&this.zoomExtent(void 0,!0,this.constants.clustering.enabled),this.constants.clustering.enabled&&this.startWithClustering()}var o=i(56),n=i(45),r=i(57),a=i(1),h=i(47),d=i(3),l=i(4),c=i(42),p=i(43),u=i(38),m=i(39),f=i(40),g=i(37),v=i(41),y=i(52),b=i(53),_=i(54);i(55),o(s.prototype),s.prototype._determineBrowserMethod=function(){var t=navigator.userAgent.toLowerCase();this.requiresTimeout=!1,-1!=t.indexOf("msie 9.0")?this.requiresTimeout=!0:-1!=t.indexOf("safari")&&t.indexOf("chrome")<=-1&&(this.requiresTimeout=!0)},s.prototype._getScriptPath=function(){for(var t=document.getElementsByTagName("script"),e=0;et.boundingBox.left&&(s=t.boundingBox.left),ot.boundingBox.bottom&&(e=t.boundingBox.top),i=this.constants.clustering.initialMaxNodes?49.07548/(n+142.05338)+91444e-8:12.662/(n+7.4147)+.0964822:1==this.constants.clustering.enabled&&n>=this.constants.clustering.initialMaxNodes?77.5271985/(n+187.266146)+476710517e-13:30.5062972/(n+19.93597763)+.08413486;var r=Math.min(this.frame.canvas.clientWidth/600,this.frame.canvas.clientHeight/600);s*=r}else{var a=1.1*Math.abs(o.maxX-o.minX),h=1.1*Math.abs(o.maxY-o.minY),d=this.frame.canvas.clientWidth/a,l=this.frame.canvas.clientHeight/h;s=l>=d?d:l}s>1&&(s=1);var c=this._findCenter(o);if(0==i){var p={position:c,scale:s,animation:t};this.moveTo(p),this.moving=!0,this.start()}else c.x*=s,c.y*=s,c.x-=.5*this.frame.canvas.clientWidth,c.y-=.5*this.frame.canvas.clientHeight,this._setScale(s),this._setTranslation(-c.x,-c.y)},s.prototype._updateNodeIndexList=function(){this._clearNodeIndexList();for(var t in this.nodes)this.nodes.hasOwnProperty(t)&&this.nodeIndices.push(t)},s.prototype.setData=function(t,e){if(void 0===e&&(e=!1),this.initializing=!0,t&&t.dot&&(t.nodes||t.edges))throw new SyntaxError('Data must contain either parameter "dot" or parameter pair "nodes" and "edges", but not both.');if(1==this.constants.dataManipulation.enabled&&this._createManipulatorBar(),this.setOptions(t&&t.options),t&&t.dot){if(t&&t.dot){var i=c.DOTToGraph(t.dot);return void this.setData(i)}}else if(t&&t.gephi){if(t&&t.gephi){var s=p.parseGephi(t.gephi); +return void this.setData(s)}}else this._setNodes(t&&t.nodes),this._setEdges(t&&t.edges);this._putDataInSector(),0==e&&(1==this.constants.hierarchicalLayout.enabled?(this._resetLevels(),this._setupHierarchicalLayout()):this.constants.stabilize&&this._stabilize(),this.start()),this.initializing=!1},s.prototype.setOptions=function(t){if(t){var e,i=["nodes","edges","smoothCurves","hierarchicalLayout","clustering","navigation","keyboard","dataManipulation","onAdd","onEdit","onEditEdge","onConnect","onDelete","clickToUse"];if(a.selectiveNotDeepExtend(i,this.constants,t),a.selectiveNotDeepExtend(["color"],this.constants.nodes,t.nodes),a.selectiveNotDeepExtend(["color","length"],this.constants.edges,t.edges),t.physics&&(a.mergeOptions(this.constants.physics,t.physics,"barnesHut"),a.mergeOptions(this.constants.physics,t.physics,"repulsion"),t.physics.hierarchicalRepulsion)){this.constants.hierarchicalLayout.enabled=!0,this.constants.physics.hierarchicalRepulsion.enabled=!0,this.constants.physics.barnesHut.enabled=!1;for(e in t.physics.hierarchicalRepulsion)t.physics.hierarchicalRepulsion.hasOwnProperty(e)&&(this.constants.physics.hierarchicalRepulsion[e]=t.physics.hierarchicalRepulsion[e])}if(t.onAdd&&(this.triggerFunctions.add=t.onAdd),t.onEdit&&(this.triggerFunctions.edit=t.onEdit),t.onEditEdge&&(this.triggerFunctions.editEdge=t.onEditEdge),t.onConnect&&(this.triggerFunctions.connect=t.onConnect),t.onDelete&&(this.triggerFunctions.del=t.onDelete),a.mergeOptions(this.constants,t,"smoothCurves"),a.mergeOptions(this.constants,t,"hierarchicalLayout"),a.mergeOptions(this.constants,t,"clustering"),a.mergeOptions(this.constants,t,"navigation"),a.mergeOptions(this.constants,t,"keyboard"),a.mergeOptions(this.constants,t,"dataManipulation"),t.dataManipulation&&(this.editMode=this.constants.dataManipulation.initiallyVisible),t.edges&&(void 0!==t.edges.color&&(a.isString(t.edges.color)?(this.constants.edges.color={},this.constants.edges.color.color=t.edges.color,this.constants.edges.color.highlight=t.edges.color,this.constants.edges.color.hover=t.edges.color):(void 0!==t.edges.color.color&&(this.constants.edges.color.color=t.edges.color.color),void 0!==t.edges.color.highlight&&(this.constants.edges.color.highlight=t.edges.color.highlight),void 0!==t.edges.color.hover&&(this.constants.edges.color.hover=t.edges.color.hover)),this.constants.edges.inheritColor=!1),t.edges.fontColor||void 0!==t.edges.color&&(a.isString(t.edges.color)?this.constants.edges.fontColor=t.edges.color:void 0!==t.edges.color.color&&(this.constants.edges.fontColor=t.edges.color.color))),t.nodes&&t.nodes.color){var s=a.parseColor(t.nodes.color);this.constants.nodes.color.background=s.background,this.constants.nodes.color.border=s.border,this.constants.nodes.color.highlight.background=s.highlight.background,this.constants.nodes.color.highlight.border=s.highlight.border,this.constants.nodes.color.hover.background=s.hover.background,this.constants.nodes.color.hover.border=s.hover.border}if(t.groups)for(var o in t.groups)if(t.groups.hasOwnProperty(o)){var n=t.groups[o];this.groups.add(o,n)}if(t.tooltip){for(e in t.tooltip)t.tooltip.hasOwnProperty(e)&&(this.constants.tooltip[e]=t.tooltip[e]);t.tooltip.color&&(this.constants.tooltip.color=a.parseColor(t.tooltip.color))}if("clickToUse"in t&&(t.clickToUse?this.activator||(this.activator=new b(this.frame),this.activator.on("change",this._createKeyBinds.bind(this))):this.activator&&(this.activator.destroy(),delete this.activator)),t.labels)throw new Error('Option "labels" is deprecated. Use options "locale" and "locales" instead.');this._loadPhysicsSystem(),this._loadNavigationControls(),this._loadManipulationSystem(),this._configureSmoothCurves(),this._bindHammer(),this._createKeyBinds(),this.setSize(this.constants.width,this.constants.height),this.moving=!0,this.start()}},s.prototype._create=function(){for(;this.containerElement.hasChildNodes();)this.containerElement.removeChild(this.containerElement.firstChild);if(this.frame=document.createElement("div"),this.frame.className="vis network-frame",this.frame.style.position="relative",this.frame.style.overflow="hidden",this.frame.tabIndex=900,this.frame.canvas=document.createElement("canvas"),this.frame.canvas.style.position="relative",this.frame.appendChild(this.frame.canvas),this.frame.canvas.getContext){var t=this.frame.canvas.getContext("2d");this.pixelRatio=(window.devicePixelRatio||1)/(t.webkitBackingStorePixelRatio||t.mozBackingStorePixelRatio||t.msBackingStorePixelRatio||t.oBackingStorePixelRatio||t.backingStorePixelRatio||1),this.frame.canvas.getContext("2d").setTransform(this.pixelRatio,0,0,this.pixelRatio,0,0)}else{var e=document.createElement("DIV");e.style.color="red",e.style.fontWeight="bold",e.style.padding="10px",e.innerHTML="Error: your browser does not support HTML canvas",this.frame.canvas.appendChild(e)}this._bindHammer()},s.prototype._bindHammer=function(){var t=this;void 0!==this.hammer&&this.hammer.dispose(),this.drag={},this.pinch={},this.hammer=n(this.frame.canvas,{prevent_default:!0}),this.hammer.on("tap",t._onTap.bind(t)),this.hammer.on("doubletap",t._onDoubleTap.bind(t)),this.hammer.on("hold",t._onHold.bind(t)),this.hammer.on("touch",t._onTouch.bind(t)),this.hammer.on("dragstart",t._onDragStart.bind(t)),this.hammer.on("drag",t._onDrag.bind(t)),this.hammer.on("dragend",t._onDragEnd.bind(t)),1==this.constants.zoomable&&(this.hammer.on("mousewheel",t._onMouseWheel.bind(t)),this.hammer.on("DOMMouseScroll",t._onMouseWheel.bind(t)),this.hammer.on("pinch",t._onPinch.bind(t))),this.hammer.on("mousemove",t._onMouseMoveTitle.bind(t)),this.hammerFrame=n(this.frame,{prevent_default:!0}),this.hammerFrame.on("release",t._onRelease.bind(t)),this.containerElement.appendChild(this.frame)},s.prototype._createKeyBinds=function(){var t=this;void 0!==this.keycharm&&this.keycharm.destroy(),this.keycharm=r(1==this.constants.keyboard.bindToWindow?{container:window,preventDefault:!1}:{container:this.frame,preventDefault:!1}),this.keycharm.reset(),this.constants.keyboard.enabled&&this.isActive()&&(this.keycharm.bind("up",this._moveUp.bind(t),"keydown"),this.keycharm.bind("up",this._yStopMoving.bind(t),"keyup"),this.keycharm.bind("down",this._moveDown.bind(t),"keydown"),this.keycharm.bind("down",this._yStopMoving.bind(t),"keyup"),this.keycharm.bind("left",this._moveLeft.bind(t),"keydown"),this.keycharm.bind("left",this._xStopMoving.bind(t),"keyup"),this.keycharm.bind("right",this._moveRight.bind(t),"keydown"),this.keycharm.bind("right",this._xStopMoving.bind(t),"keyup"),this.keycharm.bind("=",this._zoomIn.bind(t),"keydown"),this.keycharm.bind("=",this._stopZoom.bind(t),"keyup"),this.keycharm.bind("num+",this._zoomIn.bind(t),"keydown"),this.keycharm.bind("num+",this._stopZoom.bind(t),"keyup"),this.keycharm.bind("num-",this._zoomOut.bind(t),"keydown"),this.keycharm.bind("num-",this._stopZoom.bind(t),"keyup"),this.keycharm.bind("-",this._zoomOut.bind(t),"keydown"),this.keycharm.bind("-",this._stopZoom.bind(t),"keyup"),this.keycharm.bind("[",this._zoomIn.bind(t),"keydown"),this.keycharm.bind("[",this._stopZoom.bind(t),"keyup"),this.keycharm.bind("]",this._zoomOut.bind(t),"keydown"),this.keycharm.bind("]",this._stopZoom.bind(t),"keyup"),this.keycharm.bind("pageup",this._zoomIn.bind(t),"keydown"),this.keycharm.bind("pageup",this._stopZoom.bind(t),"keyup"),this.keycharm.bind("pagedown",this._zoomOut.bind(t),"keydown"),this.keycharm.bind("pagedown",this._stopZoom.bind(t),"keyup")),this.keycharm.bind("1",this.increaseClusterLevel.bind(t),"keydown"),this.keycharm.bind("2",this.decreaseClusterLevel.bind(t),"keydown"),this.keycharm.bind("3",this.forceAggregateHubs.bind(t,!0),"keydown"),this.keycharm.bind("4",this.normalizeClusterLevels.bind(t),"keydown"),1==this.constants.dataManipulation.enabled&&(this.keycharm.bind("esc",this._createManipulatorBar.bind(t)),this.keycharm.bind("delete",this._deleteSelected.bind(t)))},s.prototype.destroy=function(){this.start=function(){},this.redraw=function(){},this.timer=!1,this._cleanupPhysicsConfiguration(),this.keycharm.reset(),this.hammer.dispose(),this.off(),this._recursiveDOMDelete(this.containerElement)},s.prototype._recursiveDOMDelete=function(t){for(;1==t.hasChildNodes();)this._recursiveDOMDelete(t.firstChild),t.removeChild(t.firstChild)},s.prototype._getPointer=function(t){return{x:t.pageX-a.getAbsoluteLeft(this.frame.canvas),y:t.pageY-a.getAbsoluteTop(this.frame.canvas)}},s.prototype._onTouch=function(t){(new Date).valueOf()-this.touchTime>100&&(this.drag.pointer=this._getPointer(t.gesture.center),this.drag.pinched=!1,this.pinch.scale=this._getScale(),this.touchTime=(new Date).valueOf(),this._handleTouch(this.drag.pointer))},s.prototype._onDragStart=function(t){this._handleDragStart(t)},s.prototype._handleDragStart=function(t){void 0===this.drag.pointer&&this._onTouch(t);var e=this._getNodeAt(this.drag.pointer);if(this.drag.dragging=!0,this.drag.selection=[],this.drag.translation=this._getTranslation(),this.drag.nodeId=null,this.draggingNodes=!1,null!=e&&1==this.constants.dragNodes){this.draggingNodes=!0,this.drag.nodeId=e.id,e.isSelected()||this._selectObject(e,!1),this.emit("dragStart",{nodeIds:this.getSelection().nodes});for(var i in this.selectionObj.nodes)if(this.selectionObj.nodes.hasOwnProperty(i)){var s=this.selectionObj.nodes[i],o={id:s.id,node:s,x:s.x,y:s.y,xFixed:s.xFixed,yFixed:s.yFixed};s.xFixed=!0,s.yFixed=!0,this.drag.selection.push(o)}}},s.prototype._onDrag=function(t){this._handleOnDrag(t)},s.prototype._handleOnDrag=function(t){if(!this.drag.pinched){this.releaseNode();var e=this._getPointer(t.gesture.center),i=this,s=this.drag,o=s.selection;if(o&&o.length&&1==this.constants.dragNodes){var n=e.x-s.pointer.x,r=e.y-s.pointer.y;o.forEach(function(t){var e=t.node;t.xFixed||(e.x=i._XconvertDOMtoCanvas(i._XconvertCanvasToDOM(t.x)+n)),t.yFixed||(e.y=i._YconvertDOMtoCanvas(i._YconvertCanvasToDOM(t.y)+r))}),this.moving||(this.moving=!0,this.start())}else if(1==this.constants.dragNetwork){if(void 0===this.drag.pointer)return void this._handleDragStart(t);var a=e.x-this.drag.pointer.x,h=e.y-this.drag.pointer.y;this._setTranslation(this.drag.translation.x+a,this.drag.translation.y+h),this._redraw()}}},s.prototype._onDragEnd=function(t){this._handleDragEnd(t)},s.prototype._handleDragEnd=function(){this.drag.dragging=!1;var t=this.drag.selection;t&&t.length?(t.forEach(function(t){t.node.xFixed=t.xFixed,t.node.yFixed=t.yFixed}),this.moving=!0,this.start()):this._redraw(),0==this.draggingNodes?this.emit("dragEnd",{nodeIds:[]}):this.emit("dragEnd",{nodeIds:this.getSelection().nodes})},s.prototype._onTap=function(t){var e=this._getPointer(t.gesture.center);this.pointerPosition=e,this._handleTap(e)},s.prototype._onDoubleTap=function(t){var e=this._getPointer(t.gesture.center);this._handleDoubleTap(e)},s.prototype._onHold=function(t){var e=this._getPointer(t.gesture.center);this.pointerPosition=e,this._handleOnHold(e)},s.prototype._onRelease=function(t){var e=this._getPointer(t.gesture.center);this._handleOnRelease(e)},s.prototype._onPinch=function(t){var e=this._getPointer(t.gesture.center);this.drag.pinched=!0,"scale"in this.pinch||(this.pinch.scale=1);var i=this.pinch.scale*t.gesture.scale;this._zoom(i,e)},s.prototype._zoom=function(t,e){if(1==this.constants.zoomable){var i=this._getScale();1e-5>t&&(t=1e-5),t>10&&(t=10);var s=null;void 0!==this.drag&&1==this.drag.dragging&&(s=this.DOMtoCanvas(this.drag.pointer));var o=this._getTranslation(),n=t/i,r=(1-n)*e.x+o.x*n,a=(1-n)*e.y+o.y*n;if(this.areaCenter={x:this._XconvertDOMtoCanvas(e.x),y:this._YconvertDOMtoCanvas(e.y)},this._setScale(t),this._setTranslation(r,a),this.updateClustersDefault(),null!=s){var h=this.canvasToDOM(s);this.drag.pointer.x=h.x,this.drag.pointer.y=h.y}return this._redraw(),t>i?this.emit("zoom",{direction:"+"}):this.emit("zoom",{direction:"-"}),t}},s.prototype._onMouseWheel=function(t){var e=0;if(t.wheelDelta?e=t.wheelDelta/120:t.detail&&(e=-t.detail/3),e){var i=this._getScale(),s=e/10;0>e&&(s/=1-s),i*=1+s;var o=h.fakeGesture(this,t),n=this._getPointer(o.center);this._zoom(i,n)}t.preventDefault()},s.prototype._onMouseMoveTitle=function(t){var e=h.fakeGesture(this,t),i=this._getPointer(e.center);this.popupObj&&this._checkHidePopup(i),0==this.constants.keyboard.bindToWindow&&1==this.constants.keyboard.enabled&&this.frame.focus();var s=this,o=function(){s._checkShowPopup(i)};if(this.popupTimer&&clearInterval(this.popupTimer),this.drag.dragging||(this.popupTimer=setTimeout(o,this.constants.tooltip.delay)),1==this.constants.hover){for(var n in this.hoverObj.edges)this.hoverObj.edges.hasOwnProperty(n)&&(this.hoverObj.edges[n].hover=!1,delete this.hoverObj.edges[n]);var r=this._getNodeAt(i);null==r&&(r=this._getEdgeAt(i)),null!=r&&this._hoverObject(r);for(var a in this.hoverObj.nodes)this.hoverObj.nodes.hasOwnProperty(a)&&(r instanceof f&&r.id!=a||r instanceof g||null==r)&&(this._blurObject(this.hoverObj.nodes[a]),delete this.hoverObj.nodes[a]);this.redraw()}},s.prototype._checkShowPopup=function(t){var e,i={left:this._XconvertDOMtoCanvas(t.x),top:this._YconvertDOMtoCanvas(t.y),right:this._XconvertDOMtoCanvas(t.x),bottom:this._YconvertDOMtoCanvas(t.y)},s=this.popupObj,o=!1;if(void 0==this.popupObj){var n=this.nodes,r=[];for(e in n)if(n.hasOwnProperty(e)){var a=n[e];a.isOverlappingWith(i)&&void 0!==a.getTitle()&&r.push(e)}r.length>0&&(this.popupObj=this.nodes[r[r.length-1]],o=!0)}if(void 0===this.popupObj&&0==o){var h=this.edges,d=[];for(e in h)if(h.hasOwnProperty(e)){var l=h[e];l.connected&&void 0!==l.getTitle()&&l.isOverlappingWith(i)&&d.push(e)}d.length>0&&(this.popupObj=this.edges[d[d.length-1]])}if(this.popupObj){if(this.popupObj!=s){var c=this;c.popup||(c.popup=new v(c.frame,c.constants.tooltip)),c.popup.setPosition(t.x-3,t.y-3),c.popup.setText(c.popupObj.getTitle()),c.popup.show()}}else this.popup&&this.popup.hide()},s.prototype._checkHidePopup=function(t){this.popupObj&&this._getNodeAt(t)||(this.popupObj=void 0,this.popup&&this.popup.hide())},s.prototype.setSize=function(t,e){var i=!1,s=this.frame.canvas.width,o=this.frame.canvas.height;t!=this.constants.width||e!=this.constants.height||this.frame.style.width!=t||this.frame.style.height!=e?(this.frame.style.width=t,this.frame.style.height=e,this.frame.canvas.style.width="100%",this.frame.canvas.style.height="100%",this.frame.canvas.width=this.frame.canvas.clientWidth*this.pixelRatio,this.frame.canvas.height=this.frame.canvas.clientHeight*this.pixelRatio,this.constants.width=t,this.constants.height=e,i=!0):(this.frame.canvas.width!=this.frame.canvas.clientWidth*this.pixelRatio&&(this.frame.canvas.width=this.frame.canvas.clientWidth*this.pixelRatio,i=!0),this.frame.canvas.height!=this.frame.canvas.clientHeight*this.pixelRatio&&(this.frame.canvas.height=this.frame.canvas.clientHeight*this.pixelRatio,i=!0)),1==i&&this.emit("resize",{width:this.frame.canvas.width*this.pixelRatio,height:this.frame.canvas.height*this.pixelRatio,oldWidth:s*this.pixelRatio,oldHeight:o*this.pixelRatio})},s.prototype._setNodes=function(t){var e=this.nodesData;if(t instanceof d||t instanceof l)this.nodesData=t;else if(Array.isArray(t))this.nodesData=new d,this.nodesData.add(t);else{if(t)throw new TypeError("Array or DataSet expected");this.nodesData=new d}if(e&&a.forEach(this.nodesListeners,function(t,i){e.off(i,t)}),this.nodes={},this.nodesData){var i=this;a.forEach(this.nodesListeners,function(t,e){i.nodesData.on(e,t)});var s=this.nodesData.getIds();this._addNodes(s)}this._updateSelection()},s.prototype._addNodes=function(t){for(var e,i=0,s=t.length;s>i;i++){e=t[i];var o=this.nodesData.get(e),n=new f(o,this.images,this.groups,this.constants);if(this.nodes[e]=n,!(0!=n.xFixed&&0!=n.yFixed||null!==n.x&&null!==n.y)){var r=1*t.length+10,a=2*Math.PI*Math.random();0==n.xFixed&&(n.x=r*Math.cos(a)),0==n.yFixed&&(n.y=r*Math.sin(a))}this.moving=!0}this._updateNodeIndexList(),1==this.constants.hierarchicalLayout.enabled&&0==this.initializing&&(this._resetLevels(),this._setupHierarchicalLayout()),this._updateCalculationNodes(),this._reconnectEdges(),this._updateValueRange(this.nodes),this.updateLabels()},s.prototype._updateNodes=function(t,e){for(var i=this.nodes,s=0,o=t.length;o>s;s++){var n=t[s],r=i[n],a=e[s];r?r.setProperties(a,this.constants):(r=new f(properties,this.images,this.groups,this.constants),i[n]=r)}this.moving=!0,1==this.constants.hierarchicalLayout.enabled&&0==this.initializing&&(this._resetLevels(),this._setupHierarchicalLayout()),this._updateNodeIndexList(),this._updateValueRange(i)},s.prototype._removeNodes=function(t){for(var e=this.nodes,i=0,s=t.length;s>i;i++){var o=t[i];delete e[o]}this._updateNodeIndexList(),1==this.constants.hierarchicalLayout.enabled&&0==this.initializing&&(this._resetLevels(),this._setupHierarchicalLayout()),this._updateCalculationNodes(),this._reconnectEdges(),this._updateSelection(),this._updateValueRange(e)},s.prototype._setEdges=function(t){var e=this.edgesData;if(t instanceof d||t instanceof l)this.edgesData=t;else if(Array.isArray(t))this.edgesData=new d,this.edgesData.add(t);else{if(t)throw new TypeError("Array or DataSet expected");this.edgesData=new d}if(e&&a.forEach(this.edgesListeners,function(t,i){e.off(i,t)}),this.edges={},this.edgesData){var i=this;a.forEach(this.edgesListeners,function(t,e){i.edgesData.on(e,t)});var s=this.edgesData.getIds();this._addEdges(s)}this._reconnectEdges()},s.prototype._addEdges=function(t){for(var e=this.edges,i=this.edgesData,s=0,o=t.length;o>s;s++){var n=t[s],r=e[n];r&&r.disconnect();var a=i.get(n,{showInternalIds:!0});e[n]=new g(a,this,this.constants)}this.moving=!0,this._updateValueRange(e),this._createBezierNodes(),this._updateCalculationNodes(),1==this.constants.hierarchicalLayout.enabled&&0==this.initializing&&(this._resetLevels(),this._setupHierarchicalLayout())},s.prototype._updateEdges=function(t){for(var e=this.edges,i=this.edgesData,s=0,o=t.length;o>s;s++){var n=t[s],r=i.get(n),a=e[n];a?(a.disconnect(),a.setProperties(r,this.constants),a.connect()):(a=new g(r,this,this.constants),this.edges[n]=a)}this._createBezierNodes(),1==this.constants.hierarchicalLayout.enabled&&0==this.initializing&&(this._resetLevels(),this._setupHierarchicalLayout()),this.moving=!0,this._updateValueRange(e)},s.prototype._removeEdges=function(t){for(var e=this.edges,i=0,s=t.length;s>i;i++){var o=t[i],n=e[o];n&&(null!=n.via&&delete this.sectors.support.nodes[n.via.id],n.disconnect(),delete e[o])}this.moving=!0,this._updateValueRange(e),1==this.constants.hierarchicalLayout.enabled&&0==this.initializing&&(this._resetLevels(),this._setupHierarchicalLayout()),this._updateCalculationNodes()},s.prototype._reconnectEdges=function(){var t,e=this.nodes,i=this.edges;for(t in e)e.hasOwnProperty(t)&&(e[t].edges=[],e[t].dynamicEdges=[]);for(t in i)if(i.hasOwnProperty(t)){var s=i[t];s.from=null,s.to=null,s.connect()}},s.prototype._updateValueRange=function(t){var e,i=void 0,s=void 0;for(e in t)if(t.hasOwnProperty(e)){var o=t[e].getValue();void 0!==o&&(i=void 0===i?o:Math.min(o,i),s=void 0===s?o:Math.max(o,s))}if(void 0!==i&&void 0!==s)for(e in t)t.hasOwnProperty(e)&&t[e].setValueRange(i,s)},s.prototype.redraw=function(){this.setSize(this.constants.width,this.constants.height),this._redraw()},s.prototype._redraw=function(t){var e=this.frame.canvas.getContext("2d");e.setTransform(this.pixelRatio,0,0,this.pixelRatio,0,0);var i=this.frame.canvas.width*this.pixelRatio,s=this.frame.canvas.height*this.pixelRatio;e.clearRect(0,0,i,s),e.save(),e.translate(this.translation.x,this.translation.y),e.scale(this.scale,this.scale),this.canvasTopLeft={x:this._XconvertDOMtoCanvas(0),y:this._YconvertDOMtoCanvas(0)},this.canvasBottomRight={x:this._XconvertDOMtoCanvas(this.frame.canvas.clientWidth*this.pixelRatio),y:this._YconvertDOMtoCanvas(this.frame.canvas.clientHeight*this.pixelRatio)},1!=t&&(this._doInAllSectors("_drawAllSectorNodes",e),(0==this.drag.dragging||void 0===this.drag.dragging||0==this.constants.hideEdgesOnDrag)&&this._doInAllSectors("_drawEdges",e)),(0==this.drag.dragging||void 0===this.drag.dragging||0==this.constants.hideNodesOnDrag)&&this._doInAllSectors("_drawNodes",e,!1),1!=t&&1==this.controlNodesActive&&this._doInAllSectors("_drawControlNodes",e),e.restore(),1==t&&e.clearRect(0,0,i,s)},s.prototype._setTranslation=function(t,e){void 0===this.translation&&(this.translation={x:0,y:0}),void 0!==t&&(this.translation.x=t),void 0!==e&&(this.translation.y=e),this.emit("viewChanged")},s.prototype._getTranslation=function(){return{x:this.translation.x,y:this.translation.y}},s.prototype._setScale=function(t){this.scale=t},s.prototype._getScale=function(){return this.scale},s.prototype._XconvertDOMtoCanvas=function(t){return(t-this.translation.x)/this.scale},s.prototype._XconvertCanvasToDOM=function(t){return t*this.scale+this.translation.x},s.prototype._YconvertDOMtoCanvas=function(t){return(t-this.translation.y)/this.scale},s.prototype._YconvertCanvasToDOM=function(t){return t*this.scale+this.translation.y},s.prototype.canvasToDOM=function(t){return{x:this._XconvertCanvasToDOM(t.x),y:this._YconvertCanvasToDOM(t.y)}},s.prototype.DOMtoCanvas=function(t){return{x:this._XconvertDOMtoCanvas(t.x),y:this._YconvertDOMtoCanvas(t.y)}},s.prototype._drawNodes=function(t,e){void 0===e&&(e=!1);var i=this.nodes,s=[];for(var o in i)i.hasOwnProperty(o)&&(i[o].setScaleAndPos(this.scale,this.canvasTopLeft,this.canvasBottomRight),i[o].isSelected()?s.push(o):(i[o].inArea()||e)&&i[o].draw(t));for(var n=0,r=s.length;r>n;n++)(i[s[n]].inArea()||e)&&i[s[n]].draw(t)},s.prototype._drawEdges=function(t){var e=this.edges;for(var i in e)if(e.hasOwnProperty(i)){var s=e[i];s.setScale(this.scale),s.connected&&e[i].draw(t)}},s.prototype._drawControlNodes=function(t){var e=this.edges;for(var i in e)e.hasOwnProperty(i)&&e[i]._drawControlNodes(t)},s.prototype._stabilize=function(){1==this.constants.freezeForStabilization&&this._freezeDefinedNodes();for(var t=0;this.moving&&t0)for(t in i)i.hasOwnProperty(t)&&(i[t].discreteStepLimited(e,this.constants.maxVelocity),s=!0);else for(t in i)i.hasOwnProperty(t)&&(i[t].discreteStep(e),s=!0);if(1==s){var o=this.constants.minVelocity/Math.max(this.scale,.05);return o>.5*this.constants.maxVelocity?!0:this._isMoving(o)}return!1},s.prototype._revertPhysicsState=function(){var t=this.nodes;for(var e in t)t.hasOwnProperty(e)&&t[e].revertPosition()},s.prototype._revertPhysicsTick=function(){this._doInAllActiveSectors("_revertPhysicsState"),1==this.constants.smoothCurves.enabled&&1==this.constants.smoothCurves.dynamic&&this._doInSupportSector("_revertPhysicsState")},s.prototype._physicsTick=function(){if(!this.freezeSimulation&&1==this.moving){var t=!1,e=!1;this._doInAllActiveSectors("_initializeForceCalculation");var i=this._doInAllActiveSectors("_discreteStepNodes");1==this.constants.smoothCurves.enabled&&1==this.constants.smoothCurves.dynamic&&(e=this._doInSupportSector("_discreteStepNodes"));for(var s=0;s2*e||1==this.runDoubleSpeed)&&1==this.moving&&(this._physicsTick(),0!=this.renderTime&&(this.runDoubleSpeed=!0))}var i=Date.now();this._redraw(),this.renderTime=Date.now()-i,this.start()},"undefined"!=typeof window&&(window.requestAnimationFrame=window.requestAnimationFrame||window.mozRequestAnimationFrame||window.webkitRequestAnimationFrame||window.msRequestAnimationFrame),s.prototype.start=function(){if(1==this.moving||0!=this.xIncrement||0!=this.yIncrement||0!=this.zoomIncrement||1==this.animating)this.timer||(this.timer=1==this.requiresTimeout?window.setTimeout(this._animationStep.bind(this),this.renderTimestep):window.requestAnimationFrame(this._animationStep.bind(this)));else if(this._redraw(),this.stabilizationIterations>1){var t=this,e={iterations:t.stabilizationIterations};this.stabilizationIterations=0,this.startedStabilization=!1,setTimeout(function(){t.emit("stabilized",e)},0)}else this.stabilizationIterations=0},s.prototype._handleNavigation=function(){if(0!=this.xIncrement||0!=this.yIncrement){var t=this._getTranslation();this._setTranslation(t.x+this.xIncrement,t.y+this.yIncrement)}if(0!=this.zoomIncrement){var e={x:this.frame.canvas.clientWidth/2,y:this.frame.canvas.clientHeight/2};this._zoom(this.scale*(1+this.zoomIncrement),e)}},s.prototype.toggleFreeze=function(){0==this.freezeSimulation?this.freezeSimulation=!0:(this.freezeSimulation=!1,this.start())},s.prototype._configureSmoothCurves=function(t){if(void 0===t&&(t=!0),1==this.constants.smoothCurves.enabled&&1==this.constants.smoothCurves.dynamic){this._createBezierNodes();for(var e in this.sectors.support.nodes)this.sectors.support.nodes.hasOwnProperty(e)&&void 0===this.edges[this.sectors.support.nodes[e].parentEdgeId]&&delete this.sectors.support.nodes[e]}else{this.sectors.support.nodes={};for(var i in this.edges)this.edges.hasOwnProperty(i)&&(this.edges[i].via=null)}this._updateCalculationNodes(),t||(this.moving=!0,this.start())},s.prototype._createBezierNodes=function(){if(1==this.constants.smoothCurves.enabled&&1==this.constants.smoothCurves.dynamic)for(var t in this.edges)if(this.edges.hasOwnProperty(t)){var e=this.edges[t];if(null==e.via){var i="edgeId:".concat(e.id);this.sectors.support.nodes[i]=new f({id:i,mass:1,shape:"circle",image:"",internalMultiplier:1},{},{},this.constants),e.via=this.sectors.support.nodes[i],e.via.parentEdgeId=e.id,e.positionBezierNode()}}},s.prototype._initializeMixinLoaders=function(){for(var t in y)y.hasOwnProperty(t)&&(s.prototype[t]=y[t])},s.prototype.storePosition=function(){console.log("storePosition is depricated: use .storePositions() from now on."),this.storePositions()},s.prototype.storePositions=function(){var t=[];for(var e in this.nodes)if(this.nodes.hasOwnProperty(e)){var i=this.nodes[e],s=!this.nodes.xFixed,o=!this.nodes.yFixed;(this.nodesData._data[e].x!=Math.round(i.x)||this.nodesData._data[e].y!=Math.round(i.y))&&t.push({id:e,x:Math.round(i.x),y:Math.round(i.y),allowedToMoveX:s,allowedToMoveY:o})}this.nodesData.update(t)},s.prototype.getPositions=function(t){var e={};if(void 0!==t){if(1==Array.isArray(t)){for(var i=0;i=1&&(this.animating=!1,this.easingTime=0,this._redraw=null!=this.lockedOnNodeId?this._lockedRedraw:this._classicRedraw,this.emit("animationFinished"))},s.prototype._classicRedraw=function(){},s.prototype.isActive=function(){return!this.activator||this.activator.active},s.prototype.setScale=function(){return this._setScale()},s.prototype.getScale=function(){return this._getScale()},s.prototype.getCenterCoordinates=function(){return this.DOMtoCanvas({x:.5*this.frame.canvas.clientWidth,y:.5*this.frame.canvas.clientHeight})},s.prototype.getBoundingBox=function(t){return void 0!==this.nodes[t]?this.nodes[t].boundingBox:void 0},t.exports=s},function(t,e,i){function s(t,e,i){if(!e)throw"No network provided";var s=["edges","physics"],n=o.selectiveBridgeObject(s,i);this.options=n.edges,this.physics=n.physics,this.options.smoothCurves=i.smoothCurves,this.network=e,this.id=void 0,this.fromId=void 0,this.toId=void 0,this.title=void 0,this.widthSelected=this.options.width*this.options.widthSelectionMultiplier,this.value=void 0,this.selected=!1,this.hover=!1,this.labelDimensions={top:0,left:0,width:0,height:0,yLine:0},this.dirtyLabel=!0,this.from=null,this.to=null,this.via=null,this.fromBackup=null,this.toBackup=null,this.originalFromId=[],this.originalToId=[],this.connected=!1,this.widthFixed=!1,this.lengthFixed=!1,this.setProperties(t),this.controlNodesEnabled=!1,this.controlNodes={from:null,to:null,positions:{}},this.connectedNode=null}var o=i(1),n=i(40); +s.prototype.setProperties=function(t){if(t){var e=["style","fontSize","fontFace","fontColor","fontFill","fontStrokeWidth","fontStrokeColor","width","widthSelectionMultiplier","hoverWidth","arrowScaleFactor","dash","inheritColor","labelAlignment"];switch(o.selectiveDeepExtend(e,this.options,t),void 0!==t.from&&(this.fromId=t.from),void 0!==t.to&&(this.toId=t.to),void 0!==t.id&&(this.id=t.id),void 0!==t.label&&(this.label=t.label,this.dirtyLabel=!0),void 0!==t.title&&(this.title=t.title),void 0!==t.value&&(this.value=t.value),void 0!==t.length&&(this.physics.springLength=t.length),void 0!==t.color&&(this.options.inheritColor=!1,o.isString(t.color)?(this.options.color.color=t.color,this.options.color.highlight=t.color):(void 0!==t.color.color&&(this.options.color.color=t.color.color),void 0!==t.color.highlight&&(this.options.color.highlight=t.color.highlight),void 0!==t.color.hover&&(this.options.color.hover=t.color.hover))),this.connect(),this.widthFixed=this.widthFixed||void 0!==t.width,this.lengthFixed=this.lengthFixed||void 0!==t.length,this.widthSelected=this.options.width*this.options.widthSelectionMultiplier,this.options.style){case"line":this.draw=this._drawLine;break;case"arrow":this.draw=this._drawArrow;break;case"arrow-center":this.draw=this._drawArrowCenter;break;case"dash-line":this.draw=this._drawDashLine;break;default:this.draw=this._drawLine}}},s.prototype.connect=function(){this.disconnect(),this.from=this.network.nodes[this.fromId]||null,this.to=this.network.nodes[this.toId]||null,this.connected=this.from&&this.to,this.connected?(this.from.attachEdge(this),this.to.attachEdge(this)):(this.from&&this.from.detachEdge(this),this.to&&this.to.detachEdge(this))},s.prototype.disconnect=function(){this.from&&(this.from.detachEdge(this),this.from=null),this.to&&(this.to.detachEdge(this),this.to=null),this.connected=!1},s.prototype.getTitle=function(){return"function"==typeof this.title?this.title():this.title},s.prototype.getValue=function(){return this.value},s.prototype.setValueRange=function(t,e){if(!this.widthFixed&&void 0!==this.value){var i=(this.options.widthMax-this.options.widthMin)/(e-t);this.options.width=(this.value-t)*i+this.options.widthMin,this.widthSelected=this.options.width*this.options.widthSelectionMultiplier}},s.prototype.draw=function(){throw"Method draw not initialized in edge"},s.prototype.isOverlappingWith=function(t){if(this.connected){var e=10,i=this.from.x,s=this.from.y,o=this.to.x,n=this.to.y,r=t.left,a=t.top,h=this._getDistanceToEdge(i,s,o,n,r,a);return e>h}return!1},s.prototype._getColor=function(){var t=this.options.color;return"to"==this.options.inheritColor?t={highlight:this.to.options.color.highlight.border,hover:this.to.options.color.hover.border,color:this.to.options.color.border}:("from"==this.options.inheritColor||1==this.options.inheritColor)&&(t={highlight:this.from.options.color.highlight.border,hover:this.from.options.color.hover.border,color:this.from.options.color.border}),1==this.selected?t.highlight:1==this.hover?t.hover:t.color},s.prototype._drawLine=function(t){if(t.strokeStyle=this._getColor(),t.lineWidth=this._getLineWidth(),this.from!=this.to){var e,i=this._line(t);if(this.label){if(1==this.options.smoothCurves.enabled&&null!=i){var s=.5*(.5*(this.from.x+i.x)+.5*(this.to.x+i.x)),o=.5*(.5*(this.from.y+i.y)+.5*(this.to.y+i.y));e={x:s,y:o}}else e=this._pointOnLine(.5);this._label(t,this.label,e.x,e.y)}}else{var n,r,a=this.physics.springLength/4,h=this.from;h.width||h.resize(t),h.width>h.height?(n=h.x+h.width/2,r=h.y-a):(n=h.x+a,r=h.y-h.height/2),this._circle(t,n,r,a),e=this._pointOnCircle(n,r,a,.5),this._label(t,this.label,e.x,e.y)}},s.prototype._getLineWidth=function(){return 1==this.selected?Math.max(Math.min(this.widthSelected,this.options.widthMax),.3*this.networkScaleInv):1==this.hover?Math.max(Math.min(this.options.hoverWidth,this.options.widthMax),.3*this.networkScaleInv):Math.max(this.options.width,.3*this.networkScaleInv)},s.prototype._getViaCoordinates=function(){if(1==this.options.smoothCurves.dynamic&&1==this.options.smoothCurves.enabled)return this.via;if(0==this.options.smoothCurves.enabled)return{x:0,y:0};var t=null,e=null,i=this.options.smoothCurves.roundness,s=this.options.smoothCurves.type,o=Math.abs(this.from.x-this.to.x),n=Math.abs(this.from.y-this.to.y);return"discrete"==s||"diagonalCross"==s?Math.abs(this.from.x-this.to.x)this.to.y?this.from.xthis.to.x&&(t=this.from.x-i*n,e=this.from.y-i*n):this.from.ythis.to.x&&(t=this.from.x-i*n,e=this.from.y+i*n)),"discrete"==s&&(t=i*n>o?this.from.x:t)):Math.abs(this.from.x-this.to.x)>Math.abs(this.from.y-this.to.y)&&(this.from.y>this.to.y?this.from.xthis.to.x&&(t=this.from.x-i*o,e=this.from.y-i*o):this.from.ythis.to.x&&(t=this.from.x-i*o,e=this.from.y+i*o)),"discrete"==s&&(e=i*o>n?this.from.y:e)):"straightCross"==s?Math.abs(this.from.x-this.to.x)Math.abs(this.from.y-this.to.y)&&(t=this.from.xthis.to.y?this.from.xthis.to.x&&(t=this.from.x-i*n,e=this.from.y-i*n,t=this.to.x>t?this.to.x:t):this.from.ythis.to.x&&(t=this.from.x-i*n,e=this.from.y+i*n,t=this.to.x>t?this.to.x:t)):Math.abs(this.from.x-this.to.x)>Math.abs(this.from.y-this.to.y)&&(this.from.y>this.to.y?this.from.xe?this.to.y:e):this.from.x>this.to.x&&(t=this.from.x-i*o,e=this.from.y-i*o,e=this.to.y>e?this.to.y:e):this.from.ythis.to.x&&(t=this.from.x-i*o,e=this.from.y+i*o,e=this.to.yd;d++){var l=t.measureText(n[d]).width;h=l>h?l:h}var c=this.options.fontSize*r,p=i-h/2,u=s-c/2;this.labelDimensions={top:u,left:p,width:h,height:c,yLine:o}}var o=this.labelDimensions.yLine;t.save(),"horizontal"!=this.options.labelAlignment&&(t.translate(i,o),this._rotateForLabelAlignment(t),i=0,o=0),this._drawLabelRect(t),this._drawLabelText(t,i,o,n,r,a),t.restore()}},s.prototype._rotateForLabelAlignment=function(t){var e=this.from.y-this.to.y,i=this.from.x-this.to.x,s=Math.atan2(e,i);(-1>s&&0>i||s>0&&0>i)&&(s+=Math.PI),t.rotate(s)},s.prototype._drawLabelRect=function(t){if(void 0!==this.options.fontFill&&null!==this.options.fontFill&&"none"!==this.options.fontFill){t.fillStyle=this.options.fontFill;var e=2;"line-center"==this.options.labelAlignment?t.fillRect(.5*-this.labelDimensions.width,.5*-this.labelDimensions.height,this.labelDimensions.width,this.labelDimensions.height):"line-above"==this.options.labelAlignment?t.fillRect(.5*-this.labelDimensions.width,-(this.labelDimensions.height+e),this.labelDimensions.width,this.labelDimensions.height):"line-below"==this.options.labelAlignment?t.fillRect(.5*-this.labelDimensions.width,e,this.labelDimensions.width,this.labelDimensions.height):t.fillRect(this.labelDimensions.left,this.labelDimensions.top,this.labelDimensions.width,this.labelDimensions.height)}},s.prototype._drawLabelText=function(t,e,i,s,o,n){if(t.fillStyle=this.options.fontColor||"black",t.textAlign="center","horizontal"!=this.options.labelAlignment){var r=2;"line-above"==this.options.labelAlignment?(t.textBaseline="alphabetic",i-=2*r):"line-below"==this.options.labelAlignment?(t.textBaseline="hanging",i+=2*r):t.textBaseline="middle"}else t.textBaseline="middle";this.options.fontStrokeWidth>0&&(t.lineWidth=this.options.fontStrokeWidth,t.strokeStyle=this.options.fontStrokeColor,t.lineJoin="round");for(var a=0;o>a;a++)this.options.fontStrokeWidth>0&&t.strokeText(s[a],e,i),t.fillText(s[a],e,i),i+=n},s.prototype._drawDashLine=function(t){t.strokeStyle=this._getColor(),t.lineWidth=this._getLineWidth();var e=null;if(void 0!==t.setLineDash){t.save();var i=[0];i=void 0!==this.options.dash.length&&void 0!==this.options.dash.gap?[this.options.dash.length,this.options.dash.gap]:[5,5],t.setLineDash(i),t.lineDashOffset=0,e=this._line(t),t.setLineDash([0]),t.lineDashOffset=0,t.restore()}else t.beginPath(),t.lineCap="round",void 0!==this.options.dash.altLength?t.dashedLine(this.from.x,this.from.y,this.to.x,this.to.y,[this.options.dash.length,this.options.dash.gap,this.options.dash.altLength,this.options.dash.gap]):void 0!==this.options.dash.length&&void 0!==this.options.dash.gap?t.dashedLine(this.from.x,this.from.y,this.to.x,this.to.y,[this.options.dash.length,this.options.dash.gap]):(t.moveTo(this.from.x,this.from.y),t.lineTo(this.to.x,this.to.y)),t.stroke();if(this.label){var s;if(1==this.options.smoothCurves.enabled&&null!=e){var o=.5*(.5*(this.from.x+e.x)+.5*(this.to.x+e.x)),n=.5*(.5*(this.from.y+e.y)+.5*(this.to.y+e.y));s={x:o,y:n}}else s=this._pointOnLine(.5);this._label(t,this.label,s.x,s.y)}},s.prototype._pointOnLine=function(t){return{x:(1-t)*this.from.x+t*this.to.x,y:(1-t)*this.from.y+t*this.to.y}},s.prototype._pointOnCircle=function(t,e,i,s){var o=2*(s-3/8)*Math.PI;return{x:t+i*Math.cos(o),y:e-i*Math.sin(o)}},s.prototype._drawArrowCenter=function(t){var e;if(t.strokeStyle=this._getColor(),t.fillStyle=t.strokeStyle,t.lineWidth=this._getLineWidth(),this.from!=this.to){var i=this._line(t),s=Math.atan2(this.to.y-this.from.y,this.to.x-this.from.x),o=(10+5*this.options.width)*this.options.arrowScaleFactor;if(1==this.options.smoothCurves.enabled&&null!=i){var n=.5*(.5*(this.from.x+i.x)+.5*(this.to.x+i.x)),r=.5*(.5*(this.from.y+i.y)+.5*(this.to.y+i.y));e={x:n,y:r}}else e=this._pointOnLine(.5);t.arrow(e.x,e.y,s,o),t.fill(),t.stroke(),this.label&&this._label(t,this.label,e.x,e.y)}else{var a,h,d=.25*Math.max(100,this.physics.springLength),l=this.from;l.width||l.resize(t),l.width>l.height?(a=l.x+.5*l.width,h=l.y-d):(a=l.x+d,h=l.y-.5*l.height),this._circle(t,a,h,d);var s=.2*Math.PI,o=(10+5*this.options.width)*this.options.arrowScaleFactor;e=this._pointOnCircle(a,h,d,.5),t.arrow(e.x,e.y,s,o),t.fill(),t.stroke(),this.label&&(e=this._pointOnCircle(a,h,d,.5),this._label(t,this.label,e.x,e.y))}},s.prototype._pointOnBezier=function(t){var e=this._getViaCoordinates(),i=Math.pow(1-t,2)*this.from.x+2*t*(1-t)*e.x+Math.pow(t,2)*this.to.x,s=Math.pow(1-t,2)*this.from.y+2*t*(1-t)*e.y+Math.pow(t,2)*this.to.y;return{x:i,y:s}},s.prototype._findBorderPosition=function(t,e){var i,s,o,n,r,a=10,h=0,d=0,l=1,c=.2,p=this.to;for(1==t&&(p=this.from);l>=d&&a>h;){var u=.5*(d+l);if(i=this._pointOnBezier(u),s=Math.atan2(p.y-i.y,p.x-i.x),o=p.distanceToBorder(e,s),n=Math.sqrt(Math.pow(i.x-p.x,2)+Math.pow(i.y-p.y,2)),r=o-n,Math.abs(r)r?0==t?d=u:l=u:0==t?l=u:d=u,h++}return i.t=u,i},s.prototype._drawArrow=function(t){t.strokeStyle=this._getColor(),t.fillStyle=t.strokeStyle,t.lineWidth=this._getLineWidth();var e,i,s;if(this.from!=this.to){if(this._line(t),1==this.options.smoothCurves.enabled){var o=this._getViaCoordinates();s=this._findBorderPosition(!1,t);var n=this._pointOnBezier(Math.max(0,s.t-.1));e=Math.atan2(s.y-n.y,s.x-n.x)}else{e=Math.atan2(this.to.y-this.from.y,this.to.x-this.from.x);var r=this.to.x-this.from.x,a=this.to.y-this.from.y,h=Math.sqrt(r*r+a*a),d=this.to.distanceToBorder(t,e),l=(h-d)/h;s={},s.x=(1-l)*this.from.x+l*this.to.x,s.y=(1-l)*this.from.y+l*this.to.y}if(i=(10+5*this.options.width)*this.options.arrowScaleFactor,t.arrow(s.x,s.y,e,i),t.fill(),t.stroke(),this.label){var c;c=1==this.options.smoothCurves.enabled&&null!=o?this._pointOnBezier(.5):this._pointOnLine(.5),this._label(t,this.label,c.x,c.y)}}else{var p,u,m,f=this.from,g=.25*Math.max(100,this.physics.springLength);f.width||f.resize(t),f.width>f.height?(p=f.x+.5*f.width,u=f.y-g,m={x:p,y:f.y,angle:.9*Math.PI}):(p=f.x+g,u=f.y-.5*f.height,m={x:f.x,y:u,angle:.6*Math.PI}),t.beginPath(),t.arc(p,u,g,0,2*Math.PI,!1),t.stroke();var i=(10+5*this.options.width)*this.options.arrowScaleFactor;t.arrow(m.x,m.y,m.angle,i),t.fill(),t.stroke(),this.label&&(c=this._pointOnCircle(p,u,g,.5),this._label(t,this.label,c.x,c.y))}},s.prototype._getDistanceToEdge=function(t,e,i,s,o,n){var r=0;if(this.from!=this.to)if(1==this.options.smoothCurves.enabled){var a,h;if(1==this.options.smoothCurves.enabled&&1==this.options.smoothCurves.dynamic)a=this.via.x,h=this.via.y;else{var d=this._getViaCoordinates();a=d.x,h=d.y}var l,c,p,u,m,f,g,v=1e9;for(c=0;10>c;c++)p=.1*c,u=Math.pow(1-p,2)*t+2*p*(1-p)*a+Math.pow(p,2)*i,m=Math.pow(1-p,2)*e+2*p*(1-p)*h+Math.pow(p,2)*s,c>0&&(l=this._getDistanceToLine(f,g,u,m,o,n),v=v>l?l:v),f=u,g=m;r=v}else r=this._getDistanceToLine(t,e,i,s,o,n);else{var u,m,y,b,_=.25*this.physics.springLength,x=this.from;x.width>x.height?(u=x.x+.5*x.width,m=x.y-_):(u=x.x+_,m=x.y-.5*x.height),y=u-o,b=m-n,r=Math.abs(Math.sqrt(y*y+b*b)-_)}return this.labelDimensions.lefto&&this.labelDimensions.topn?0:r},s.prototype._getDistanceToLine=function(t,e,i,s,o,n){var r=i-t,a=s-e,h=r*r+a*a,d=((o-t)*r+(n-e)*a)/h;d>1?d=1:0>d&&(d=0);var l=t+d*r,c=e+d*a,p=l-o,u=c-n;return Math.sqrt(p*p+u*u)},s.prototype.setScale=function(t){this.networkScaleInv=1/t},s.prototype.select=function(){this.selected=!0},s.prototype.unselect=function(){this.selected=!1},s.prototype.positionBezierNode=function(){null!==this.via&&null!==this.from&&null!==this.to?(this.via.x=.5*(this.from.x+this.to.x),this.via.y=.5*(this.from.y+this.to.y)):null!==this.via&&(this.via.x=0,this.via.y=0)},s.prototype._drawControlNodes=function(t){if(1==this.controlNodesEnabled){if(null===this.controlNodes.from&&null===this.controlNodes.to){var e="edgeIdFrom:".concat(this.id),i="edgeIdTo:".concat(this.id),s={nodes:{group:"",radius:7,borderWidth:2,borderWidthSelected:2},physics:{damping:0},clustering:{maxNodeSizeIncrements:0,nodeScaling:{width:0,height:0,radius:0}}};this.controlNodes.from=new n({id:e,shape:"dot",color:{background:"#ff0000",border:"#3c3c3c",highlight:{background:"#07f968"}}},{},{},s),this.controlNodes.to=new n({id:i,shape:"dot",color:{background:"#ff0000",border:"#3c3c3c",highlight:{background:"#07f968"}}},{},{},s)}this.controlNodes.positions={},0==this.controlNodes.from.selected&&(this.controlNodes.positions.from=this.getControlNodeFromPosition(t),this.controlNodes.from.x=this.controlNodes.positions.from.x,this.controlNodes.from.y=this.controlNodes.positions.from.y),0==this.controlNodes.to.selected&&(this.controlNodes.positions.to=this.getControlNodeToPosition(t),this.controlNodes.to.x=this.controlNodes.positions.to.x,this.controlNodes.to.y=this.controlNodes.positions.to.y),this.controlNodes.from.draw(t),this.controlNodes.to.draw(t)}else this.controlNodes={from:null,to:null,positions:{}}},s.prototype._enableControlNodes=function(){this.fromBackup=this.from,this.toBackup=this.to,this.controlNodesEnabled=!0},s.prototype._disableControlNodes=function(){this.fromId=this.from.id,this.toId=this.to.id,this.fromId!=this.fromBackup.id?this.fromBackup.detachEdge(this):this.toId!=this.toBackup.id&&this.toBackup.detachEdge(this),this.fromBackup=null,this.toBackup=null,this.controlNodesEnabled=!1},s.prototype._getSelectedControlNode=function(t,e){var i=this.controlNodes.positions,s=Math.sqrt(Math.pow(t-i.from.x,2)+Math.pow(e-i.from.y,2)),o=Math.sqrt(Math.pow(t-i.to.x,2)+Math.pow(e-i.to.y,2));return 15>s?(this.connectedNode=this.from,this.from=this.controlNodes.from,this.controlNodes.from):15>o?(this.connectedNode=this.to,this.to=this.controlNodes.to,this.controlNodes.to):null},s.prototype._restoreControlNodes=function(){1==this.controlNodes.from.selected?(this.from=this.connectedNode,this.connectedNode=null,this.controlNodes.from.unselect()):1==this.controlNodes.to.selected&&(this.to=this.connectedNode,this.connectedNode=null,this.controlNodes.to.unselect())},s.prototype.getControlNodeFromPosition=function(t){var e;if(1==this.options.smoothCurves.enabled)e=this._findBorderPosition(!0,t);else{var i=Math.atan2(this.to.y-this.from.y,this.to.x-this.from.x),s=this.to.x-this.from.x,o=this.to.y-this.from.y,n=Math.sqrt(s*s+o*o),r=this.from.distanceToBorder(t,i+Math.PI),a=(n-r)/n;e={},e.x=a*this.from.x+(1-a)*this.to.x,e.y=a*this.from.y+(1-a)*this.to.y}return e},s.prototype.getControlNodeToPosition=function(t){var e;if(1==this.options.smoothCurves.enabled)e=this._findBorderPosition(!1,t);else{var i=Math.atan2(this.to.y-this.from.y,this.to.x-this.from.x),s=this.to.x-this.from.x,o=this.to.y-this.from.y,n=Math.sqrt(s*s+o*o),r=this.to.distanceToBorder(t,i),a=(n-r)/n;e={},e.x=(1-a)*this.from.x+a*this.to.x,e.y=(1-a)*this.from.y+a*this.to.y}return e},t.exports=s},function(t,e,i){function s(){this.clear(),this.defaultIndex=0}i(1);s.DEFAULT=[{border:"#2B7CE9",background:"#97C2FC",highlight:{border:"#2B7CE9",background:"#D2E5FF"},hover:{border:"#2B7CE9",background:"#D2E5FF"}},{border:"#FFA500",background:"#FFFF00",highlight:{border:"#FFA500",background:"#FFFFA3"},hover:{border:"#FFA500",background:"#FFFFA3"}},{border:"#FA0A10",background:"#FB7E81",highlight:{border:"#FA0A10",background:"#FFAFB1"},hover:{border:"#FA0A10",background:"#FFAFB1"}},{border:"#41A906",background:"#7BE141",highlight:{border:"#41A906",background:"#A1EC76"},hover:{border:"#41A906",background:"#A1EC76"}},{border:"#E129F0",background:"#EB7DF4",highlight:{border:"#E129F0",background:"#F0B3F5"},hover:{border:"#E129F0",background:"#F0B3F5"}},{border:"#7C29F0",background:"#AD85E4",highlight:{border:"#7C29F0",background:"#D3BDF0"},hover:{border:"#7C29F0",background:"#D3BDF0"}},{border:"#C37F00",background:"#FFA807",highlight:{border:"#C37F00",background:"#FFCA66"},hover:{border:"#C37F00",background:"#FFCA66"}},{border:"#4220FB",background:"#6E6EFD",highlight:{border:"#4220FB",background:"#9B9BFD"},hover:{border:"#4220FB",background:"#9B9BFD"}},{border:"#FD5A77",background:"#FFC0CB",highlight:{border:"#FD5A77",background:"#FFD1D9"},hover:{border:"#FD5A77",background:"#FFD1D9"}},{border:"#4AD63A",background:"#C2FABC",highlight:{border:"#4AD63A",background:"#E6FFE3"},hover:{border:"#4AD63A",background:"#E6FFE3"}}],s.prototype.clear=function(){this.groups={},this.groups.length=function(){var t=0;for(var e in this)this.hasOwnProperty(e)&&t++;return t}},s.prototype.get=function(t){var e=this.groups[t];if(void 0==e){var i=this.defaultIndex%s.DEFAULT.length;this.defaultIndex++,e={},e.color=s.DEFAULT[i],this.groups[t]=e}return e},s.prototype.add=function(t,e){return this.groups[t]=e,e},t.exports=s},function(t){function e(){this.images={},this.imageBroken={},this.callback=void 0}e.prototype.setOnloadCallback=function(t){this.callback=t},e.prototype.load=function(t,e){var i=this.images[t];if(void 0===i){var s=this;i=new Image,i.onload=function(){0==this.width&&(document.body.appendChild(this),this.width=this.offsetWidth,this.height=this.offsetHeight,document.body.removeChild(this)),s.callback&&(s.images[t]=i,s.callback(this))},i.onerror=function(){void 0===e?(console.error("Could not load image:",t),delete this.src,s.callback&&s.callback(this)):s.imageBroken[t]===!0?this.src==e?(console.error("Could not load brokenImage:",e),delete this.src,s.callback&&s.callback(this)):(console.error("Could not load image:",t),this.src=e):(console.error("Could not load image:",t),this.src=e,s.imageBroken[t]=!0)},i.src=t}return i},t.exports=e},function(t,e,i){function s(t,e,i,s){var n=o.selectiveBridgeObject(["nodes"],s);this.options=n.nodes,this.selected=!1,this.hover=!1,this.edges=[],this.dynamicEdges=[],this.reroutedEdges={},this.fontDrawThreshold=3,this.id=void 0,this.allowedToMoveX=!1,this.allowedToMoveY=!1,this.xFixed=!1,this.yFixed=!1,this.horizontalAlignLeft=!0,this.verticalAlignTop=!0,this.baseRadiusValue=s.nodes.radius,this.radiusFixed=!1,this.level=-1,this.preassignedLevel=!1,this.hierarchyEnumerated=!1,this.labelDimensions={top:0,left:0,width:0,height:0,yLine:0},this.boundingBox={top:0,left:0,right:0,bottom:0},this.imagelist=e,this.grouplist=i,this.fx=0,this.fy=0,this.vx=0,this.vy=0,this.x=null,this.y=null,this.previousState={vx:0,vy:0,x:0,y:0},this.damping=s.physics.damping,this.fixedData={x:null,y:null},this.setProperties(t,n),this.resetCluster(),this.dynamicEdgesLength=0,this.clusterSession=0,this.clusterSizeWidthFactor=s.clustering.nodeScaling.width,this.clusterSizeHeightFactor=s.clustering.nodeScaling.height,this.clusterSizeRadiusFactor=s.clustering.nodeScaling.radius,this.maxNodeSizeIncrements=s.clustering.maxNodeSizeIncrements,this.growthIndicator=0,this.networkScaleInv=1,this.networkScale=1,this.canvasTopLeft={x:-300,y:-300},this.canvasBottomRight={x:300,y:300},this.parentEdgeId=null}var o=i(1);s.prototype.revertPosition=function(){this.x=this.previousState.x,this.y=this.previousState.y,this.vx=this.previousState.vx,this.vy=this.previousState.vy},s.prototype.resetCluster=function(){this.formationScale=void 0,this.clusterSize=1,this.containedNodes={},this.containedEdges={},this.clusterSessions=[]},s.prototype.attachEdge=function(t){-1==this.edges.indexOf(t)&&this.edges.push(t),-1==this.dynamicEdges.indexOf(t)&&this.dynamicEdges.push(t),this.dynamicEdgesLength=this.dynamicEdges.length},s.prototype.detachEdge=function(t){var e=this.edges.indexOf(t);-1!=e&&this.edges.splice(e,1),e=this.dynamicEdges.indexOf(t),-1!=e&&this.dynamicEdges.splice(e,1),this.dynamicEdgesLength=this.dynamicEdges.length},s.prototype.setProperties=function(t,e){if(t){var i=["borderWidth","borderWidthSelected","shape","image","brokenImage","radius","fontColor","fontSize","fontFace","fontFill","fontStrokeWidth","fontStrokeColor","group","mass"];if(o.selectiveDeepExtend(i,this.options,t),void 0!==t.id&&(this.id=t.id),void 0!==t.label&&(this.label=t.label,this.originalLabel=t.label),void 0!==t.title&&(this.title=t.title),void 0!==t.x&&(this.x=t.x),void 0!==t.y&&(this.y=t.y),void 0!==t.value&&(this.value=t.value),void 0!==t.level&&(this.level=t.level,this.preassignedLevel=!0),void 0!==t.horizontalAlignLeft&&(this.horizontalAlignLeft=t.horizontalAlignLeft),void 0!==t.verticalAlignTop&&(this.verticalAlignTop=t.verticalAlignTop),void 0!==t.triggerFunction&&(this.triggerFunction=t.triggerFunction),void 0===this.id)throw"Node must have an id";if("number"==typeof t.group||"string"==typeof t.group&&""!=t.group){var s=this.grouplist.get(t.group);o.deepExtend(this.options,s),this.options.color=o.parseColor(this.options.color)}if(void 0!==t.radius&&(this.baseRadiusValue=this.options.radius),void 0!==t.color&&(this.options.color=o.parseColor(t.color)),void 0!==this.options.image&&""!=this.options.image){if(!this.imagelist)throw"No imagelist provided";this.imageObj=this.imagelist.load(this.options.image,this.options.brokenImage)}switch(void 0!==t.allowedToMoveX?(this.xFixed=!t.allowedToMoveX,this.allowedToMoveX=t.allowedToMoveX):void 0!==t.x&&0==this.allowedToMoveX&&(this.xFixed=!0),void 0!==t.allowedToMoveY?(this.yFixed=!t.allowedToMoveY,this.allowedToMoveY=t.allowedToMoveY):void 0!==t.y&&0==this.allowedToMoveY&&(this.yFixed=!0),this.radiusFixed=this.radiusFixed||void 0!==t.radius,("image"===this.options.shape||"circularImage"===this.options.shape)&&(this.options.radiusMin=e.nodes.widthMin,this.options.radiusMax=e.nodes.widthMax),this.options.shape){case"database":this.draw=this._drawDatabase,this.resize=this._resizeDatabase;break;case"box":this.draw=this._drawBox,this.resize=this._resizeBox;break;case"circle":this.draw=this._drawCircle,this.resize=this._resizeCircle;break;case"ellipse":this.draw=this._drawEllipse,this.resize=this._resizeEllipse;break;case"image":this.draw=this._drawImage,this.resize=this._resizeImage;break;case"circularImage":this.draw=this._drawCircularImage,this.resize=this._resizeCircularImage;break;case"text":this.draw=this._drawText,this.resize=this._resizeText;break;case"dot":this.draw=this._drawDot,this.resize=this._resizeShape;break;case"square":this.draw=this._drawSquare,this.resize=this._resizeShape;break;case"triangle":this.draw=this._drawTriangle,this.resize=this._resizeShape;break;case"triangleDown":this.draw=this._drawTriangleDown,this.resize=this._resizeShape;break;case"star":this.draw=this._drawStar,this.resize=this._resizeShape;break;default:this.draw=this._drawEllipse,this.resize=this._resizeEllipse}this._reset()}},s.prototype.select=function(){this.selected=!0,this._reset()},s.prototype.unselect=function(){this.selected=!1,this._reset()},s.prototype.clearSizeCache=function(){this._reset()},s.prototype._reset=function(){this.width=void 0,this.height=void 0},s.prototype.getTitle=function(){return"function"==typeof this.title?this.title():this.title},s.prototype.distanceToBorder=function(t,e){var i=1;switch(this.width||this.resize(t),this.options.shape){case"circle":case"dot":return this.options.radius+i;case"ellipse":var s=this.width/2,o=this.height/2,n=Math.sin(e)*s,r=Math.cos(e)*o;return s*o/Math.sqrt(n*n+r*r);case"box":case"image":case"text":default:return this.width?Math.min(Math.abs(this.width/2/Math.cos(e)),Math.abs(this.height/2/Math.sin(e)))+i:0}},s.prototype._setForce=function(t,e){this.fx=t,this.fy=e},s.prototype._addForce=function(t,e){this.fx+=t,this.fy+=e},s.prototype.storeState=function(){this.previousState.x=this.x,this.previousState.y=this.y,this.previousState.vx=this.vx,this.previousState.vy=this.vy},s.prototype.discreteStep=function(t){if(this.storeState(),this.xFixed)this.fx=0,this.vx=0;else{var e=this.damping*this.vx,i=(this.fx-e)/this.options.mass;this.vx+=i*t,this.x+=this.vx*t}if(this.yFixed)this.fy=0,this.vy=0;else{var s=this.damping*this.vy,o=(this.fy-s)/this.options.mass;this.vy+=o*t,this.y+=this.vy*t}},s.prototype.discreteStepLimited=function(t,e){if(this.storeState(),this.xFixed)this.fx=0,this.vx=0;else{var i=this.damping*this.vx,s=(this.fx-i)/this.options.mass;this.vx+=s*t,this.vx=Math.abs(this.vx)>e?this.vx>0?e:-e:this.vx,this.x+=this.vx*t}if(this.yFixed)this.fy=0,this.vy=0;else{var o=this.damping*this.vy,n=(this.fy-o)/this.options.mass;this.vy+=n*t,this.vy=Math.abs(this.vy)>e?this.vy>0?e:-e:this.vy,this.y+=this.vy*t}},s.prototype.isFixed=function(){return this.xFixed&&this.yFixed},s.prototype.isMoving=function(t){var e=Math.sqrt(Math.pow(this.vx,2)+Math.pow(this.vy,2));return e>t},s.prototype.isSelected=function(){return this.selected},s.prototype.getValue=function(){return this.value},s.prototype.getDistance=function(t,e){var i=this.x-t,s=this.y-e;return Math.sqrt(i*i+s*s)},s.prototype.setValueRange=function(t,e){if(!this.radiusFixed&&void 0!==this.value)if(e==t)this.options.radius=(this.options.radiusMin+this.options.radiusMax)/2;else{var i=(this.options.radiusMax-this.options.radiusMin)/(e-t);this.options.radius=(this.value-t)*i+this.options.radiusMin}this.baseRadiusValue=this.options.radius},s.prototype.draw=function(){throw"Draw method not initialized for node"},s.prototype.resize=function(){throw"Resize method not initialized for node"},s.prototype.isOverlappingWith=function(t){return this.leftt.left&&this.topt.top},s.prototype._resizeImage=function(){if(!this.width||!this.height){var t,e;if(this.value){this.options.radius=this.baseRadiusValue;var i=this.imageObj.height/this.imageObj.width;void 0!==i?(t=this.options.radius||this.imageObj.width,e=this.options.radius*i||this.imageObj.height):(t=0,e=0)}else t=this.imageObj.width,e=this.imageObj.height;this.width=t,this.height=e,this.growthIndicator=0,this.width>0&&this.height>0&&(this.width+=Math.min(this.clusterSize-1,this.maxNodeSizeIncrements)*this.clusterSizeWidthFactor,this.height+=Math.min(this.clusterSize-1,this.maxNodeSizeIncrements)*this.clusterSizeHeightFactor,this.options.radius+=Math.min(this.clusterSize-1,this.maxNodeSizeIncrements)*this.clusterSizeRadiusFactor,this.growthIndicator=this.width-t)}},s.prototype._drawImageAtPosition=function(t){if(0!=this.imageObj.width){if(this.clusterSize>1){var e=this.clusterSize>1?10:0;e*=this.networkScaleInv,e=Math.min(.2*this.width,e),t.globalAlpha=.5,t.drawImage(this.imageObj,this.left-e,this.top-e,this.width+2*e,this.height+2*e)}t.globalAlpha=1,t.drawImage(this.imageObj,this.left,this.top,this.width,this.height)}},s.prototype._drawImageLabel=function(t){var e,i=0;if(this.height){i=this.height/2;var s=this.getTextSize(t);s.lineCount>=1&&(i+=s.height/2,i+=3)}e=this.y+i,this._label(t,this.label,this.x,e,void 0)},s.prototype._drawImage=function(t){this._resizeImage(t),this.left=this.x-this.width/2,this.top=this.y-this.height/2,this._drawImageAtPosition(t),this.boundingBox.top=this.top,this.boundingBox.left=this.left,this.boundingBox.right=this.left+this.width,this.boundingBox.bottom=this.top+this.height,this._drawImageLabel(t),this.boundingBox.left=Math.min(this.boundingBox.left,this.labelDimensions.left),this.boundingBox.right=Math.max(this.boundingBox.right,this.labelDimensions.left+this.labelDimensions.width),this.boundingBox.bottom=Math.max(this.boundingBox.bottom,this.boundingBox.bottom+this.labelDimensions.height)},s.prototype._resizeCircularImage=function(t){if(this.imageObj.src&&this.imageObj.width&&this.imageObj.height)this._swapToImageResizeWhenImageLoaded&&(this.width=0,this.height=0,delete this._swapToImageResizeWhenImageLoaded),this._resizeImage(t);else if(!this.width){var e=2*this.options.radius;this.width=e,this.height=e,this.options.radius+=.5*Math.min(this.clusterSize-1,this.maxNodeSizeIncrements)*this.clusterSizeRadiusFactor,this.growthIndicator=this.options.radius-.5*e,this._swapToImageResizeWhenImageLoaded=!0}},s.prototype._drawCircularImage=function(t){this._resizeCircularImage(t),this.left=this.x-this.width/2,this.top=this.y-this.height/2;var e=this.left+this.width/2,i=this.top+this.height/2,s=Math.abs(this.height/2);this._drawRawCircle(t,e,i,s),t.save(),t.circle(this.x,this.y,s),t.stroke(),t.clip(),this._drawImageAtPosition(t),t.restore(),this.boundingBox.top=this.y-this.options.radius,this.boundingBox.left=this.x-this.options.radius,this.boundingBox.right=this.x+this.options.radius,this.boundingBox.bottom=this.y+this.options.radius,this._drawImageLabel(t),this.boundingBox.left=Math.min(this.boundingBox.left,this.labelDimensions.left),this.boundingBox.right=Math.max(this.boundingBox.right,this.labelDimensions.left+this.labelDimensions.width),this.boundingBox.bottom=Math.max(this.boundingBox.bottom,this.boundingBox.bottom+this.labelDimensions.height)},s.prototype._resizeBox=function(t){if(!this.width){var e=5,i=this.getTextSize(t);this.width=i.width+2*e,this.height=i.height+2*e,this.width+=.5*Math.min(this.clusterSize-1,this.maxNodeSizeIncrements)*this.clusterSizeWidthFactor,this.height+=.5*Math.min(this.clusterSize-1,this.maxNodeSizeIncrements)*this.clusterSizeHeightFactor,this.growthIndicator=this.width-(i.width+2*e) +}},s.prototype._drawBox=function(t){this._resizeBox(t),this.left=this.x-this.width/2,this.top=this.y-this.height/2;var e=2.5,i=this.options.borderWidth,s=this.options.borderWidthSelected||2*this.options.borderWidth;t.strokeStyle=this.selected?this.options.color.highlight.border:this.hover?this.options.color.hover.border:this.options.color.border,this.clusterSize>1&&(t.lineWidth=(this.selected?s:i)+(this.clusterSize>1?e:0),t.lineWidth*=this.networkScaleInv,t.lineWidth=Math.min(this.width,t.lineWidth),t.roundRect(this.left-2*t.lineWidth,this.top-2*t.lineWidth,this.width+4*t.lineWidth,this.height+4*t.lineWidth,this.options.radius),t.stroke()),t.lineWidth=(this.selected?s:i)+(this.clusterSize>1?e:0),t.lineWidth*=this.networkScaleInv,t.lineWidth=Math.min(this.width,t.lineWidth),t.fillStyle=this.selected?this.options.color.highlight.background:this.hover?this.options.color.hover.background:this.options.color.background,t.roundRect(this.left,this.top,this.width,this.height,this.options.radius),t.fill(),t.stroke(),this.boundingBox.top=this.top,this.boundingBox.left=this.left,this.boundingBox.right=this.left+this.width,this.boundingBox.bottom=this.top+this.height,this._label(t,this.label,this.x,this.y)},s.prototype._resizeDatabase=function(t){if(!this.width){var e=5,i=this.getTextSize(t),s=i.width+2*e;this.width=s,this.height=s,this.width+=Math.min(this.clusterSize-1,this.maxNodeSizeIncrements)*this.clusterSizeWidthFactor,this.height+=Math.min(this.clusterSize-1,this.maxNodeSizeIncrements)*this.clusterSizeHeightFactor,this.options.radius+=Math.min(this.clusterSize-1,this.maxNodeSizeIncrements)*this.clusterSizeRadiusFactor,this.growthIndicator=this.width-s}},s.prototype._drawDatabase=function(t){this._resizeDatabase(t),this.left=this.x-this.width/2,this.top=this.y-this.height/2;var e=2.5,i=this.options.borderWidth,s=this.options.borderWidthSelected||2*this.options.borderWidth;t.strokeStyle=this.selected?this.options.color.highlight.border:this.hover?this.options.color.hover.border:this.options.color.border,this.clusterSize>1&&(t.lineWidth=(this.selected?s:i)+(this.clusterSize>1?e:0),t.lineWidth*=this.networkScaleInv,t.lineWidth=Math.min(this.width,t.lineWidth),t.database(this.x-this.width/2-2*t.lineWidth,this.y-.5*this.height-2*t.lineWidth,this.width+4*t.lineWidth,this.height+4*t.lineWidth),t.stroke()),t.lineWidth=(this.selected?s:i)+(this.clusterSize>1?e:0),t.lineWidth*=this.networkScaleInv,t.lineWidth=Math.min(this.width,t.lineWidth),t.fillStyle=this.selected?this.options.color.highlight.background:this.hover?this.options.color.hover.background:this.options.color.background,t.database(this.x-this.width/2,this.y-.5*this.height,this.width,this.height),t.fill(),t.stroke(),this.boundingBox.top=this.top,this.boundingBox.left=this.left,this.boundingBox.right=this.left+this.width,this.boundingBox.bottom=this.top+this.height,this._label(t,this.label,this.x,this.y)},s.prototype._resizeCircle=function(t){if(!this.width){var e=5,i=this.getTextSize(t),s=Math.max(i.width,i.height)+2*e;this.options.radius=s/2,this.width=s,this.height=s,this.options.radius+=.5*Math.min(this.clusterSize-1,this.maxNodeSizeIncrements)*this.clusterSizeRadiusFactor,this.growthIndicator=this.options.radius-.5*s}},s.prototype._drawRawCircle=function(t,e,i,s){var o=2.5,n=this.options.borderWidth,r=this.options.borderWidthSelected||2*this.options.borderWidth;t.strokeStyle=this.selected?this.options.color.highlight.border:this.hover?this.options.color.hover.border:this.options.color.border,this.clusterSize>1&&(t.lineWidth=(this.selected?r:n)+(this.clusterSize>1?o:0),t.lineWidth*=this.networkScaleInv,t.lineWidth=Math.min(this.width,t.lineWidth),t.circle(e,i,s+2*t.lineWidth),t.stroke()),t.lineWidth=(this.selected?r:n)+(this.clusterSize>1?o:0),t.lineWidth*=this.networkScaleInv,t.lineWidth=Math.min(this.width,t.lineWidth),t.fillStyle=this.selected?this.options.color.highlight.background:this.hover?this.options.color.hover.background:this.options.color.background,t.circle(this.x,this.y,s),t.fill(),t.stroke()},s.prototype._drawCircle=function(t){this._resizeCircle(t),this.left=this.x-this.width/2,this.top=this.y-this.height/2,this._drawRawCircle(t,this.x,this.y,this.options.radius),this.boundingBox.top=this.y-this.options.radius,this.boundingBox.left=this.x-this.options.radius,this.boundingBox.right=this.x+this.options.radius,this.boundingBox.bottom=this.y+this.options.radius,this._label(t,this.label,this.x,this.y)},s.prototype._resizeEllipse=function(t){if(!this.width){var e=this.getTextSize(t);this.width=1.5*e.width,this.height=2*e.height,this.width1&&(t.lineWidth=(this.selected?s:i)+(this.clusterSize>1?e:0),t.lineWidth*=this.networkScaleInv,t.lineWidth=Math.min(this.width,t.lineWidth),t.ellipse(this.left-2*t.lineWidth,this.top-2*t.lineWidth,this.width+4*t.lineWidth,this.height+4*t.lineWidth),t.stroke()),t.lineWidth=(this.selected?s:i)+(this.clusterSize>1?e:0),t.lineWidth*=this.networkScaleInv,t.lineWidth=Math.min(this.width,t.lineWidth),t.fillStyle=this.selected?this.options.color.highlight.background:this.hover?this.options.color.hover.background:this.options.color.background,t.ellipse(this.left,this.top,this.width,this.height),t.fill(),t.stroke(),this.boundingBox.top=this.top,this.boundingBox.left=this.left,this.boundingBox.right=this.left+this.width,this.boundingBox.bottom=this.top+this.height,this._label(t,this.label,this.x,this.y)},s.prototype._drawDot=function(t){this._drawShape(t,"circle")},s.prototype._drawTriangle=function(t){this._drawShape(t,"triangle")},s.prototype._drawTriangleDown=function(t){this._drawShape(t,"triangleDown")},s.prototype._drawSquare=function(t){this._drawShape(t,"square")},s.prototype._drawStar=function(t){this._drawShape(t,"star")},s.prototype._resizeShape=function(){if(!this.width){this.options.radius=this.baseRadiusValue;var t=2*this.options.radius;this.width=t,this.height=t,this.width+=Math.min(this.clusterSize-1,this.maxNodeSizeIncrements)*this.clusterSizeWidthFactor,this.height+=Math.min(this.clusterSize-1,this.maxNodeSizeIncrements)*this.clusterSizeHeightFactor,this.options.radius+=.5*Math.min(this.clusterSize-1,this.maxNodeSizeIncrements)*this.clusterSizeRadiusFactor,this.growthIndicator=this.width-t}},s.prototype._drawShape=function(t,e){this._resizeShape(t),this.left=this.x-this.width/2,this.top=this.y-this.height/2;var i=2.5,s=this.options.borderWidth,o=this.options.borderWidthSelected||2*this.options.borderWidth,n=2;switch(e){case"dot":n=2;break;case"square":n=2;break;case"triangle":n=3;break;case"triangleDown":n=3;break;case"star":n=4}t.strokeStyle=this.selected?this.options.color.highlight.border:this.hover?this.options.color.hover.border:this.options.color.border,this.clusterSize>1&&(t.lineWidth=(this.selected?o:s)+(this.clusterSize>1?i:0),t.lineWidth*=this.networkScaleInv,t.lineWidth=Math.min(this.width,t.lineWidth),t[e](this.x,this.y,this.options.radius+n*t.lineWidth),t.stroke()),t.lineWidth=(this.selected?o:s)+(this.clusterSize>1?i:0),t.lineWidth*=this.networkScaleInv,t.lineWidth=Math.min(this.width,t.lineWidth),t.fillStyle=this.selected?this.options.color.highlight.background:this.hover?this.options.color.hover.background:this.options.color.background,t[e](this.x,this.y,this.options.radius),t.fill(),t.stroke(),this.boundingBox.top=this.y-this.options.radius,this.boundingBox.left=this.x-this.options.radius,this.boundingBox.right=this.x+this.options.radius,this.boundingBox.bottom=this.y+this.options.radius,this.label&&(this._label(t,this.label,this.x,this.y+this.height/2,void 0,"hanging",!0),this.boundingBox.left=Math.min(this.boundingBox.left,this.labelDimensions.left),this.boundingBox.right=Math.max(this.boundingBox.right,this.labelDimensions.left+this.labelDimensions.width),this.boundingBox.bottom=Math.max(this.boundingBox.bottom,this.boundingBox.bottom+this.labelDimensions.height))},s.prototype._resizeText=function(t){if(!this.width){var e=5,i=this.getTextSize(t);this.width=i.width+2*e,this.height=i.height+2*e,this.width+=Math.min(this.clusterSize-1,this.maxNodeSizeIncrements)*this.clusterSizeWidthFactor,this.height+=Math.min(this.clusterSize-1,this.maxNodeSizeIncrements)*this.clusterSizeHeightFactor,this.options.radius+=Math.min(this.clusterSize-1,this.maxNodeSizeIncrements)*this.clusterSizeRadiusFactor,this.growthIndicator=this.width-(i.width+2*e)}},s.prototype._drawText=function(t){this._resizeText(t),this.left=this.x-this.width/2,this.top=this.y-this.height/2,this._label(t,this.label,this.x,this.y),this.boundingBox.top=this.top,this.boundingBox.left=this.left,this.boundingBox.right=this.left+this.width,this.boundingBox.bottom=this.top+this.height},s.prototype._label=function(t,e,i,s,o,n,r){if(e&&Number(this.options.fontSize)*this.networkScale>this.fontDrawThreshold){t.font=(this.selected?"bold ":"")+this.options.fontSize+"px "+this.options.fontFace;var a=e.split("\n"),h=a.length,d=Number(this.options.fontSize),l=s+(1-h)/2*d;1==r&&(l=s+(1-h)/(2*d));for(var c=t.measureText(a[0]).width,p=1;h>p;p++){var u=t.measureText(a[p]).width;c=u>c?u:c}var m=this.options.fontSize*h,f=i-c/2,g=s-m/2;"hanging"==n&&(g+=.5*d,g+=4,l+=4),this.labelDimensions={top:g,left:f,width:c,height:m,yLine:l},void 0!==this.options.fontFill&&null!==this.options.fontFill&&"none"!==this.options.fontFill&&(t.fillStyle=this.options.fontFill,t.fillRect(f,g,c,m)),t.fillStyle=this.options.fontColor||"black",t.textAlign=o||"center",t.textBaseline=n||"middle",this.options.fontStrokeWidth>0&&(t.lineWidth=this.options.fontStrokeWidth,t.strokeStyle=this.options.fontStrokeColor,t.lineJoin="round");for(var p=0;h>p;p++)this.options.fontStrokeWidth&&t.strokeText(a[p],i,l),t.fillText(a[p],i,l),l+=d}},s.prototype.getTextSize=function(t){if(void 0!==this.label){t.font=(this.selected?"bold ":"")+this.options.fontSize+"px "+this.options.fontFace;for(var e=this.label.split("\n"),i=(Number(this.options.fontSize)+4)*e.length,s=0,o=0,n=e.length;n>o;o++)s=Math.max(s,t.measureText(e[o]).width);return{width:s,height:i,lineCount:e.length}}return{width:0,height:0,lineCount:0}},s.prototype.inArea=function(){return void 0!==this.width?this.x+this.width*this.networkScaleInv>=this.canvasTopLeft.x&&this.x-this.width*this.networkScaleInv=this.canvasTopLeft.y&&this.y-this.height*this.networkScaleInv=this.canvasTopLeft.x&&this.x=this.canvasTopLeft.y&&this.ys&&(n=s-e-this.padding),no&&(r=o-i-this.padding),ri;i++)if(e.id===r.nodes[i].id){o=r.nodes[i];break}for(o||(o={id:e.id},t.node&&(o.attr=a(o.attr,t.node))),i=n.length-1;i>=0;i--){var h=n[i];h.nodes||(h.nodes=[]),-1==h.nodes.indexOf(o)&&h.nodes.push(o)}e.attr&&(o.attr=a(o.attr,e.attr))}function l(t,e){if(t.edges||(t.edges=[]),t.edges.push(e),t.edge){var i=a({},t.edge);e.attr=a(i,e.attr)}}function c(t,e,i,s,o){var n={from:e,to:i,type:s};return t.edge&&(n.attr=a({},t.edge)),n.attr=a(n.attr||{},o),n}function p(){for(N=D.NULL,k="";" "==E||" "==E||"\n"==E||"\r"==E;)o();do{var t=!1;if("#"==E){for(var e=O-1;" "==T.charAt(e)||" "==T.charAt(e);)e--;if("\n"==T.charAt(e)||""==T.charAt(e)){for(;""!=E&&"\n"!=E;)o();t=!0}}if("/"==E&&"/"==n()){for(;""!=E&&"\n"!=E;)o();t=!0}if("/"==E&&"*"==n()){for(;""!=E;){if("*"==E&&"/"==n()){o(),o();break}o()}t=!0}for(;" "==E||" "==E||"\n"==E||"\r"==E;)o()}while(t);if(""==E)return void(N=D.DELIMITER);var i=E+n();if(C[i])return N=D.DELIMITER,k=i,o(),void o();if(C[E])return N=D.DELIMITER,k=E,void o();if(r(E)||"-"==E){for(k+=E,o();r(E);)k+=E,o();return"false"==k?k=!1:"true"==k?k=!0:isNaN(Number(k))||(k=Number(k)),void(N=D.IDENTIFIER)}if('"'==E){for(o();""!=E&&('"'!=E||'"'==E&&'"'==n());)k+=E,'"'==E&&o(),o();if('"'!=E)throw x('End of string " expected');return o(),void(N=D.IDENTIFIER)}for(N=D.UNKNOWN;""!=E;)k+=E,o();throw new SyntaxError('Syntax error in part "'+w(k,30)+'"')}function u(){var t={};if(s(),p(),"strict"==k&&(t.strict=!0,p()),("graph"==k||"digraph"==k)&&(t.type=k,p()),N==D.IDENTIFIER&&(t.id=k,p()),"{"!=k)throw x("Angle bracket { expected");if(p(),m(t),"}"!=k)throw x("Angle bracket } expected");if(p(),""!==k)throw x("End of file expected");return p(),delete t.node,delete t.edge,delete t.graph,t}function m(t){for(;""!==k&&"}"!=k;)f(t),";"==k&&p()}function f(t){var e=g(t);if(e)return void b(t,e);var i=v(t);if(!i){if(N!=D.IDENTIFIER)throw x("Identifier expected");var s=k;if(p(),"="==k){if(p(),N!=D.IDENTIFIER)throw x("Identifier expected");t[s]=k,p()}else y(t,s)}}function g(t){var e=null;if("subgraph"==k&&(e={},e.type="subgraph",p(),N==D.IDENTIFIER&&(e.id=k,p())),"{"==k){if(p(),e||(e={}),e.parent=t,e.node=t.node,e.edge=t.edge,e.graph=t.graph,m(e),"}"!=k)throw x("Angle bracket } expected");p(),delete e.node,delete e.edge,delete e.graph,delete e.parent,t.subgraphs||(t.subgraphs=[]),t.subgraphs.push(e)}return e}function v(t){return"node"==k?(p(),t.node=_(),"node"):"edge"==k?(p(),t.edge=_(),"edge"):"graph"==k?(p(),t.graph=_(),"graph"):null}function y(t,e){var i={id:e},s=_();s&&(i.attr=s),d(t,i),b(t,e)}function b(t,e){for(;"->"==k||"--"==k;){var i,s=k;p();var o=g(t);if(o)i=o;else{if(N!=D.IDENTIFIER)throw x("Identifier or subgraph expected");i=k,d(t,{id:i}),p()}var n=_(),r=c(t,e,i,s,n);l(t,r),e=i}}function _(){for(var t=null;"["==k;){for(p(),t={};""!==k&&"]"!=k;){if(N!=D.IDENTIFIER)throw x("Attribute name expected");var e=k;if(p(),"="!=k)throw x("Equal sign = expected");if(p(),N!=D.IDENTIFIER)throw x("Attribute value expected");var i=k;h(t,e,i),p(),","==k&&p()}if("]"!=k)throw x("Bracket ] expected");p()}return t}function x(t){return new SyntaxError(t+', got "'+w(k,30)+'" (char '+O+")")}function w(t,e){return t.length<=e?t:t.substr(0,27)+"..."}function M(t,e,i){Array.isArray(t)?t.forEach(function(t){Array.isArray(e)?e.forEach(function(e){i(t,e)}):i(t,e)}):Array.isArray(e)?e.forEach(function(e){i(t,e)}):i(t,e)}function S(t){var e=i(t),s={nodes:[],edges:[],options:{}};if(e.nodes&&e.nodes.forEach(function(t){var e={id:t.id,label:String(t.label||t.id)};a(e,t.attr),e.image&&(e.shape="image"),s.nodes.push(e)}),e.edges){var o=function(t){var e={from:t.from,to:t.to};return a(e,t.attr),e.style="->"==t.type?"arrow":"line",e};e.edges.forEach(function(t){var e,i;e=t.from instanceof Object?t.from.nodes:{id:t.from},i=t.to instanceof Object?t.to.nodes:{id:t.to},t.from instanceof Object&&t.from.edges&&t.from.edges.forEach(function(t){var e=o(t);s.edges.push(e)}),M(e,i,function(e,i){var n=c(s,e.id,i.id,t.type,t.attr),r=o(n);s.edges.push(r)}),t.to instanceof Object&&t.to.edges&&t.to.edges.forEach(function(t){var e=o(t);s.edges.push(e)})})}return e.attr&&(s.options=e.attr),s}var D={NULL:0,DELIMITER:1,IDENTIFIER:2,UNKNOWN:3},C={"{":!0,"}":!0,"[":!0,"]":!0,";":!0,"=":!0,",":!0,"->":!0,"--":!0},T="",O=0,E="",k="",N=D.NULL,L=/[a-zA-Z_0-9.:#]/;e.parseDOT=i,e.DOTToGraph=S},function(t,e){function i(t,e){var i=[],s=[];this.options={edges:{inheritColor:!0},nodes:{allowedToMove:!1,parseColor:!1}},void 0!==e&&(this.options.nodes.allowedToMove=e.allowedToMove|!1,this.options.nodes.parseColor=e.parseColor|!1,this.options.edges.inheritColor=e.inheritColor|!0);for(var o=t.edges,n=t.nodes,r=0;r=s&&(s=864e5),e=new Date(e.valueOf()-.05*s),i=new Date(i.valueOf()+.05*s)}return{start:e,end:i}},s.prototype.setWindow=function(t,e,i){var s=i&&void 0!==i.animate?i.animate:!0;if(1==arguments.length){var o=arguments[0];this.range.setRange(o.start,o.end,s)}else this.range.setRange(t,e,s)},s.prototype.moveTo=function(t,e){var i=this.range.end-this.range.start,s=r.convert(t,"Date").valueOf(),o=s-i/2,n=s+i/2,a=e&&void 0!==e.animate?e.animate:!0;this.range.setRange(o,n,a)},s.prototype.getWindow=function(){var t=this.range.getRange();return{start:new Date(t.start),end:new Date(t.end)}},s.prototype.redraw=function(){var t=!1,e=this.options,i=this.props,s=this.dom;if(s){h.updateHiddenDates(this.body,this.options.hiddenDates),"top"==e.orientation?(r.addClassName(s.root,"top"),r.removeClassName(s.root,"bottom")):(r.removeClassName(s.root,"top"),r.addClassName(s.root,"bottom")),s.root.style.maxHeight=r.option.asSize(e.maxHeight,""),s.root.style.minHeight=r.option.asSize(e.minHeight,""),s.root.style.width=r.option.asSize(e.width,""),i.border.left=(s.centerContainer.offsetWidth-s.centerContainer.clientWidth)/2,i.border.right=i.border.left,i.border.top=(s.centerContainer.offsetHeight-s.centerContainer.clientHeight)/2,i.border.bottom=i.border.top;var o=s.root.offsetHeight-s.root.clientHeight,n=s.root.offsetWidth-s.root.clientWidth;0===s.centerContainer.clientHeight&&(i.border.left=i.border.top,i.border.right=i.border.left),0===s.root.clientHeight&&(n=o),i.center.height=s.center.offsetHeight,i.left.height=s.left.offsetHeight,i.right.height=s.right.offsetHeight,i.top.height=s.top.clientHeight||-i.border.top,i.bottom.height=s.bottom.clientHeight||-i.border.bottom;var a=Math.max(i.left.height,i.center.height,i.right.height),d=i.top.height+a+i.bottom.height+o+i.border.top+i.border.bottom;s.root.style.height=r.option.asSize(e.height,d+"px"),i.root.height=s.root.offsetHeight,i.background.height=i.root.height-o;var l=i.root.height-i.top.height-i.bottom.height-o;i.centerContainer.height=l,i.leftContainer.height=l,i.rightContainer.height=i.leftContainer.height,i.root.width=s.root.offsetWidth,i.background.width=i.root.width-n,i.left.width=s.leftContainer.clientWidth||-i.border.left,i.leftContainer.width=i.left.width,i.right.width=s.rightContainer.clientWidth||-i.border.right,i.rightContainer.width=i.right.width;var c=i.root.width-i.left.width-i.right.width-n;i.center.width=c,i.centerContainer.width=c,i.top.width=c,i.bottom.width=c,s.background.style.height=i.background.height+"px",s.backgroundVertical.style.height=i.background.height+"px",s.backgroundHorizontal.style.height=i.centerContainer.height+"px",s.centerContainer.style.height=i.centerContainer.height+"px",s.leftContainer.style.height=i.leftContainer.height+"px",s.rightContainer.style.height=i.rightContainer.height+"px",s.background.style.width=i.background.width+"px",s.backgroundVertical.style.width=i.centerContainer.width+"px",s.backgroundHorizontal.style.width=i.background.width+"px",s.centerContainer.style.width=i.center.width+"px",s.top.style.width=i.top.width+"px",s.bottom.style.width=i.bottom.width+"px",s.background.style.left="0",s.background.style.top="0",s.backgroundVertical.style.left=i.left.width+i.border.left+"px",s.backgroundVertical.style.top="0",s.backgroundHorizontal.style.left="0",s.backgroundHorizontal.style.top=i.top.height+"px",s.centerContainer.style.left=i.left.width+"px",s.centerContainer.style.top=i.top.height+"px",s.leftContainer.style.left="0",s.leftContainer.style.top=i.top.height+"px",s.rightContainer.style.left=i.left.width+i.center.width+"px",s.rightContainer.style.top=i.top.height+"px",s.top.style.left=i.left.width+"px",s.top.style.top="0",s.bottom.style.left=i.left.width+"px",s.bottom.style.top=i.top.height+i.centerContainer.height+"px",this._updateScrollTop();var p=this.props.scrollTop;"bottom"==e.orientation&&(p+=Math.max(this.props.centerContainer.height-this.props.center.height-this.props.border.top-this.props.border.bottom,0)),s.center.style.left="0",s.center.style.top=p+"px",s.left.style.left="0",s.left.style.top=p+"px",s.right.style.left="0",s.right.style.top=p+"px";var u=0==this.props.scrollTop?"hidden":"",m=this.props.scrollTop==this.props.scrollTopMin?"hidden":"";if(s.shadowTop.style.visibility=u,s.shadowBottom.style.visibility=m,s.shadowTopLeft.style.visibility=u,s.shadowBottomLeft.style.visibility=m,s.shadowTopRight.style.visibility=u,s.shadowBottomRight.style.visibility=m,this.components.forEach(function(e){t=e.redraw()||t}),t){var f=3;this.redrawCount0&&(this.props.scrollTop=0),this.props.scrollTopt[s].y?t[s].y:e,i=i0){var r,a,h=Number(i.svg.style.height.replace("px",""));if(r=o.getSVGElement("path",i.svgElements,i.svg),r.setAttributeNS(null,"class",e.className),void 0!==e.style&&r.setAttributeNS(null,"style",e.style),a=1==e.options.catmullRom.enabled?s._catmullRom(t,e):s._linear(t),1==e.options.shaded.enabled){var d,l=o.getSVGElement("path",i.svgElements,i.svg);d="top"==e.options.shaded.orientation?"M"+t[0].x+",0 "+a+"L"+t[t.length-1].x+",0":"M"+t[0].x+","+h+" "+a+"L"+t[t.length-1].x+","+h,l.setAttributeNS(null,"class",e.className+" fill"),void 0!==e.options.shaded.style&&l.setAttributeNS(null,"style",e.options.shaded.style),l.setAttributeNS(null,"d",d)}r.setAttributeNS(null,"d","M"+a),1==e.options.drawPoints.enabled&&n.draw(t,e,i)}},s._catmullRomUniform=function(t){for(var e,i,s,o,n,r,a=Math.round(t[0].x)+","+Math.round(t[0].y)+" ",h=1/6,d=t.length,l=0;d-1>l;l++)e=0==l?t[0]:t[l-1],i=t[l],s=t[l+1],o=d>l+2?t[l+2]:s,n={x:(-e.x+6*i.x+s.x)*h,y:(-e.y+6*i.y+s.y)*h},r={x:(i.x+6*s.x-o.x)*h,y:(i.y+6*s.y-o.y)*h},a+="C"+n.x+","+n.y+" "+r.x+","+r.y+" "+s.x+","+s.y+" ";return a},s._catmullRom=function(t,e){var i=e.options.catmullRom.alpha;if(0==i||void 0===i)return this._catmullRomUniform(t);for(var s,o,n,r,a,h,d,l,c,p,u,m,f,g,v,y,b,_,x,w=Math.round(t[0].x)+","+Math.round(t[0].y)+" ",M=t.length,S=0;M-1>S;S++)s=0==S?t[0]:t[S-1],o=t[S],n=t[S+1],r=M>S+2?t[S+2]:n,d=Math.sqrt(Math.pow(s.x-o.x,2)+Math.pow(s.y-o.y,2)),l=Math.sqrt(Math.pow(o.x-n.x,2)+Math.pow(o.y-n.y,2)),c=Math.sqrt(Math.pow(n.x-r.x,2)+Math.pow(n.y-r.y,2)),g=Math.pow(c,i),y=Math.pow(c,2*i),v=Math.pow(l,i),b=Math.pow(l,2*i),x=Math.pow(d,i),_=Math.pow(d,2*i),p=2*_+3*x*v+b,u=2*y+3*g*v+b,m=3*x*(x+v),m>0&&(m=1/m),f=3*g*(g+v),f>0&&(f=1/f),a={x:(-b*s.x+p*o.x+_*n.x)*m,y:(-b*s.y+p*o.y+_*n.y)*m},h={x:(y*o.x+u*n.x-b*r.x)*f,y:(y*o.y+u*n.y-b*r.y)*f},0==a.x&&0==a.y&&(a=o),0==h.x&&0==h.y&&(h=n),w+="C"+a.x+","+a.y+" "+h.x+","+h.y+" "+n.x+","+n.y+" ";return w},s._linear=function(t){for(var e="",i=0;it[s].y?t[s].y:e,i=i0&&(n=Math.min(n,Math.abs(c[d-1].x-r))),a=s._getSafeDrawData(n,h,m);else{var g=d+(p[r].amount-p[r].resolved),v=d-(p[r].resolved+1);g0&&(n=Math.min(n,Math.abs(c[v].x-r))),a=s._getSafeDrawData(n,h,m),p[r].resolved+=1,"stack"==h.options.barChart.handleOverlap?(f=p[r].accumulated,p[r].accumulated+=h.zeroPosition-c[d].y):"sideBySide"==h.options.barChart.handleOverlap&&(a.width=a.width/p[r].amount,a.offset+=p[r].resolved*a.width-.5*a.width*(p[r].amount+1),"left"==h.options.barChart.align?a.offset-=.5*a.width:"right"==h.options.barChart.align&&(a.offset+=.5*a.width))}o.drawBar(c[d].x+a.offset,c[d].y-f,a.width,h.zeroPosition-c[d].y,h.className+" bar",i.svgElements,i.svg),1==h.options.drawPoints.enabled&&o.drawPoint(c[d].x+a.offset,c[d].y,h,i.svgElements,i.svg)}},s._getDataIntersections=function(t,e){for(var i,s=0;s0&&(i=Math.min(i,Math.abs(e[s-1].x-e[s].x))),0==i&&(void 0===t[e[s].x]&&(t[e[s].x]={amount:0,resolved:0,accumulated:0}),t[e[s].x].amount+=1)},s._getSafeDrawData=function(t,e,i){var s,o;return t0?(s=i>t?i:t,o=0,"left"==e.options.barChart.align?o-=.5*t:"right"==e.options.barChart.align&&(o+=.5*t)):(s=e.options.barChart.width,o=0,"left"==e.options.barChart.align?o-=.5*e.options.barChart.width:"right"==e.options.barChart.align&&(o+=.5*e.options.barChart.width)),{width:s,offset:o}},s.getStackedBarYRange=function(t,e,i,o,n){if(t.length>0){t.sort(function(t,e){return t.x==e.x?t.groupId-e.groupId:t.x-e.x});var r={};s._getDataIntersections(r,t),e[o]=s._getStackedBarYRange(r,t),e[o].yAxisOrientation=n,i.push(o)}},s._getStackedBarYRange=function(t,e){for(var i,s=e[0].y,o=e[0].y,n=0;ne[n].y?e[n].y:s,o=ot[r].accumulated?t[r].accumulated:s,o=ot[s].y?t[s].y:e,i=is;s++){var o=s%2===0?1.3*i:.5*i;this.lineTo(t+o*Math.sin(2*s*Math.PI/10),e-o*Math.cos(2*s*Math.PI/10))}this.closePath()},CanvasRenderingContext2D.prototype.roundRect=function(t,e,i,s,o){var n=Math.PI/180;0>i-2*o&&(o=i/2),0>s-2*o&&(o=s/2),this.beginPath(),this.moveTo(t+o,e),this.lineTo(t+i-o,e),this.arc(t+i-o,e+o,o,270*n,360*n,!1),this.lineTo(t+i,e+s-o),this.arc(t+i-o,e+s-o,o,0,90*n,!1),this.lineTo(t+o,e+s),this.arc(t+o,e+s-o,o,90*n,180*n,!1),this.lineTo(t,e+o),this.arc(t+o,e+o,o,180*n,270*n,!1)},CanvasRenderingContext2D.prototype.ellipse=function(t,e,i,s){var o=.5522848,n=i/2*o,r=s/2*o,a=t+i,h=e+s,d=t+i/2,l=e+s/2;this.beginPath(),this.moveTo(t,l),this.bezierCurveTo(t,l-r,d-n,e,d,e),this.bezierCurveTo(d+n,e,a,l-r,a,l),this.bezierCurveTo(a,l+r,d+n,h,d,h),this.bezierCurveTo(d-n,h,t,l+r,t,l)},CanvasRenderingContext2D.prototype.database=function(t,e,i,s){var o=1/3,n=i,r=s*o,a=.5522848,h=n/2*a,d=r/2*a,l=t+n,c=e+r,p=t+n/2,u=e+r/2,m=e+(s-r/2),f=e+s;this.beginPath(),this.moveTo(l,u),this.bezierCurveTo(l,u+d,p+h,c,p,c),this.bezierCurveTo(p-h,c,t,u+d,t,u),this.bezierCurveTo(t,u-d,p-h,e,p,e),this.bezierCurveTo(p+h,e,l,u-d,l,u),this.lineTo(l,m),this.bezierCurveTo(l,m+d,p+h,f,p,f),this.bezierCurveTo(p-h,f,t,m+d,t,m),this.lineTo(t,u)},CanvasRenderingContext2D.prototype.arrow=function(t,e,i,s){var o=t-s*Math.cos(i),n=e-s*Math.sin(i),r=t-.9*s*Math.cos(i),a=e-.9*s*Math.sin(i),h=o+s/3*Math.cos(i+.5*Math.PI),d=n+s/3*Math.sin(i+.5*Math.PI),l=o+s/3*Math.cos(i-.5*Math.PI),c=n+s/3*Math.sin(i-.5*Math.PI);this.beginPath(),this.moveTo(t,e),this.lineTo(h,d),this.lineTo(r,a),this.lineTo(l,c),this.closePath()},CanvasRenderingContext2D.prototype.dashedLine=function(t,e,i,s,o){o||(o=[10,5]),0==p&&(p=.001);var n=o.length;this.moveTo(t,e);for(var r=i-t,a=s-e,h=a/r,d=Math.sqrt(r*r+a*a),l=0,c=!0;d>=.1;){var p=o[l++%n];p>d&&(p=d);var u=Math.sqrt(p*p/(1+h*h));0>r&&(u=-u),t+=u,e+=h*u,this[c?"lineTo":"moveTo"](t,e),d-=p,c=!c}})},function(t){function e(t){return t?i(t):void 0}function i(t){for(var i in e.prototype)t[i]=e.prototype[i];return t}t.exports=e,e.prototype.on=e.prototype.addEventListener=function(t,e){return this._callbacks=this._callbacks||{},(this._callbacks[t]=this._callbacks[t]||[]).push(e),this},e.prototype.once=function(t,e){function i(){s.off(t,i),e.apply(this,arguments)}var s=this;return this._callbacks=this._callbacks||{},i.fn=e,this.on(t,i),this},e.prototype.off=e.prototype.removeListener=e.prototype.removeAllListeners=e.prototype.removeEventListener=function(t,e){if(this._callbacks=this._callbacks||{},0==arguments.length)return this._callbacks={},this;var i=this._callbacks[t];if(!i)return this;if(1==arguments.length)return delete this._callbacks[t],this;for(var s,o=0;os;++s)i[s].apply(this,e)}return this},e.prototype.listeners=function(t){return this._callbacks=this._callbacks||{},this._callbacks[t]||[]},e.prototype.hasListeners=function(t){return!!this.listeners(t).length}},function(t,e){var i,s,o;!function(n,r){s=[],i=r,o="function"==typeof i?i.apply(e,s):i,!(void 0!==o&&(t.exports=o))}(this,function(){function t(t){var e,i=t&&t.preventDefault||!1,s=t&&t.container||window,o={},n={keydown:{},keyup:{}},r={};for(e=97;122>=e;e++)r[String.fromCharCode(e)]={code:65+(e-97),shift:!1};for(e=65;90>=e;e++)r[String.fromCharCode(e)]={code:e,shift:!0};for(e=0;9>=e;e++)r[""+e]={code:48+e,shift:!1};for(e=1;12>=e;e++)r["F"+e]={code:111+e,shift:!1};for(e=0;9>=e;e++)r["num"+e]={code:96+e,shift:!1};r["num*"]={code:106,shift:!1},r["num+"]={code:107,shift:!1},r["num-"]={code:109,shift:!1},r["num/"]={code:111,shift:!1},r["num."]={code:110,shift:!1},r.left={code:37,shift:!1},r.up={code:38,shift:!1},r.right={code:39,shift:!1},r.down={code:40,shift:!1},r.space={code:32,shift:!1},r.enter={code:13,shift:!1},r.shift={code:16,shift:void 0},r.esc={code:27,shift:!1},r.backspace={code:8,shift:!1},r.tab={code:9,shift:!1},r.ctrl={code:17,shift:!1},r.alt={code:18,shift:!1},r["delete"]={code:46,shift:!1},r.pageup={code:33,shift:!1},r.pagedown={code:34,shift:!1},r["="]={code:187,shift:!1},r["-"]={code:189,shift:!1},r["]"]={code:221,shift:!1},r["["]={code:219,shift:!1};var a=function(t){d(t,"keydown")},h=function(t){d(t,"keyup")},d=function(t,e){if(void 0!==n[e][t.keyCode]){for(var s=n[e][t.keyCode],o=0;oe-n?(i=t.clone().add(o-1,"months"),s=(e-n)/(n-i)):(i=t.clone().add(o+1,"months"),s=(e-n)/(i-n)),-(o+s)}function f(t,e,i){var s;return null==i?e:null!=t.meridiemHour?t.meridiemHour(e,i):null!=t.isPM?(s=t.isPM(i),s&&12>e&&(e+=12),s||12!==e||(e=0),e):e}function g(){}function v(t,e){e!==!1&&F(t),_(this,t),this._d=new Date(+t._d),Di===!1&&(Di=!0,Ce.updateOffset(this),Di=!1)}function y(t){var e=N(t),i=e.year||0,s=e.quarter||0,o=e.month||0,n=e.week||0,r=e.day||0,a=e.hour||0,h=e.minute||0,d=e.second||0,l=e.millisecond||0;this._milliseconds=+l+1e3*d+6e4*h+36e5*a,this._days=+r+7*n,this._months=+o+3*s+12*i,this._data={},this._locale=Ce.localeData(),this._bubble()}function b(t,e){for(var i in e)a(e,i)&&(t[i]=e[i]);return a(e,"toString")&&(t.toString=e.toString),a(e,"valueOf")&&(t.valueOf=e.valueOf),t}function _(t,e){var i,s,o;if("undefined"!=typeof e._isAMomentObject&&(t._isAMomentObject=e._isAMomentObject),"undefined"!=typeof e._i&&(t._i=e._i),"undefined"!=typeof e._f&&(t._f=e._f),"undefined"!=typeof e._l&&(t._l=e._l),"undefined"!=typeof e._strict&&(t._strict=e._strict),"undefined"!=typeof e._tzm&&(t._tzm=e._tzm),"undefined"!=typeof e._isUTC&&(t._isUTC=e._isUTC),"undefined"!=typeof e._offset&&(t._offset=e._offset),"undefined"!=typeof e._pf&&(t._pf=e._pf),"undefined"!=typeof e._locale&&(t._locale=e._locale),Ye.length>0)for(i in Ye)s=Ye[i],o=e[s],"undefined"!=typeof o&&(t[s]=o);return t}function x(t){return 0>t?Math.ceil(t):Math.floor(t)}function w(t,e,i){for(var s=""+Math.abs(t),o=t>=0;s.lengths;s++)(i&&t[s]!==e[s]||!i&&I(t[s])!==I(e[s]))&&r++;return r+n}function k(t){if(t){var e=t.toLowerCase().replace(/(.)s$/,"$1");t=gi[t]||vi[e]||e}return t}function N(t){var e,i,s={};for(i in t)a(t,i)&&(e=k(i),e&&(s[e]=t[i]));return s}function L(t){var e,i;if(0===t.indexOf("week"))e=7,i="day";else{if(0!==t.indexOf("month"))return;e=12,i="month"}Ce[t]=function(s,o){var r,a,h=Ce._locale[t],d=[];if("number"==typeof s&&(o=s,s=n),a=function(t){var e=Ce().utc().set(i,t);return h.call(Ce._locale,e,s||"")},null!=o)return a(o);for(r=0;e>r;r++)d.push(a(r));return d}}function I(t){var e=+t,i=0;return 0!==e&&isFinite(e)&&(i=e>=0?Math.floor(e):Math.ceil(e)),i}function z(t,e){return new Date(Date.UTC(t,e+1,0)).getUTCDate()}function P(t,e,i){return me(Ce([t,11,31+e-i]),e,i).week}function A(t){return R(t)?366:365}function R(t){return t%4===0&&t%100!==0||t%400===0}function F(t){var e;t._a&&-2===t._pf.overflow&&(e=t._a[ze]<0||t._a[ze]>11?ze:t._a[Pe]<1||t._a[Pe]>z(t._a[Ie],t._a[ze])?Pe:t._a[Ae]<0||t._a[Ae]>24||24===t._a[Ae]&&(0!==t._a[Re]||0!==t._a[Fe]||0!==t._a[He])?Ae:t._a[Re]<0||t._a[Re]>59?Re:t._a[Fe]<0||t._a[Fe]>59?Fe:t._a[He]<0||t._a[He]>999?He:-1,t._pf._overflowDayOfYear&&(Ie>e||e>Pe)&&(e=Pe),t._pf.overflow=e)}function H(t){return null==t._isValid&&(t._isValid=!isNaN(t._d.getTime())&&t._pf.overflow<0&&!t._pf.empty&&!t._pf.invalidMonth&&!t._pf.nullInput&&!t._pf.invalidFormat&&!t._pf.userInvalidated,t._strict&&(t._isValid=t._isValid&&0===t._pf.charsLeftOver&&0===t._pf.unusedTokens.length&&t._pf.bigHour===n)),t._isValid}function B(t){return t?t.toLowerCase().replace("_","-"):t}function Y(t){for(var e,i,s,o,n=0;n0;){if(s=W(o.slice(0,e).join("-")))return s;if(i&&i.length>=e&&E(o,i,!0)>=e-1)break;e--}n++}return null}function W(t){var e=null;if(!Be[t]&&We)try{e=Ce.locale(),!function(){var t=new Error('Cannot find module "./locale"');throw t.code="MODULE_NOT_FOUND",t}(),Ce.locale(e)}catch(i){}return Be[t]}function G(t,e){var i,s;return e._isUTC?(i=e.clone(),s=(Ce.isMoment(t)||O(t)?+t:+Ce(t))-+i,i._d.setTime(+i._d+s),Ce.updateOffset(i,!1),i):Ce(t).local()}function j(t){return t.match(/\[[\s\S]/)?t.replace(/^\[|\]$/g,""):t.replace(/\\/g,"")}function V(t){var e,i,s=t.match(Ue);for(e=0,i=s.length;i>e;e++)s[e]=wi[s[e]]?wi[s[e]]:j(s[e]);return function(o){var n="";for(e=0;i>e;e++)n+=s[e]instanceof Function?s[e].call(o,t):s[e];return n}}function U(t,e){return t.isValid()?(e=X(e,t.localeData()),yi[e]||(yi[e]=V(e)),yi[e](t)):t.localeData().invalidDate()}function X(t,e){function i(t){return e.longDateFormat(t)||t}var s=5;for(Xe.lastIndex=0;s>=0&&Xe.test(t);)t=t.replace(Xe,i),Xe.lastIndex=0,s-=1;return t}function q(t,e){var i,s=e._strict;switch(t){case"Q":return oi;case"DDDD":return ri;case"YYYY":case"GGGG":case"gggg":return s?ai:Qe;case"Y":case"G":case"g":return di;case"YYYYYY":case"YYYYY":case"GGGGG":case"ggggg":return s?hi:Ke;case"S":if(s)return oi;case"SS":if(s)return ni;case"SSS":if(s)return ri;case"DDD":return Ze;case"MMM":case"MMMM":case"dd":case"ddd":case"dddd":return Je;case"a":case"A":return e._locale._meridiemParse;case"x":return ii;case"X":return si;case"Z":case"ZZ":return ti;case"T":return ei;case"SSSS":return $e;case"MM":case"DD":case"YY":case"GG":case"gg":case"HH":case"hh":case"mm":case"ss":case"ww":case"WW":return s?ni:qe;case"M":case"D":case"d":case"H":case"h":case"m":case"s":case"w":case"W":case"e":case"E":return qe;case"Do":return s?e._locale._ordinalParse:e._locale._ordinalParseLenient;default:return i=new RegExp(se(ie(t.replace("\\","")),"i"))}}function Z(t){t=t||"";var e=t.match(ti)||[],i=e[e.length-1]||[],s=(i+"").match(mi)||["-",0,0],o=+(60*s[1])+I(s[2]);return"+"===s[0]?o:-o}function Q(t,e,i){var s,o=i._a;switch(t){case"Q":null!=e&&(o[ze]=3*(I(e)-1));break;case"M":case"MM":null!=e&&(o[ze]=I(e)-1);break;case"MMM":case"MMMM":s=i._locale.monthsParse(e,t,i._strict),null!=s?o[ze]=s:i._pf.invalidMonth=e;break;case"D":case"DD":null!=e&&(o[Pe]=I(e));break;case"Do":null!=e&&(o[Pe]=I(parseInt(e.match(/\d{1,2}/)[0],10)));break;case"DDD":case"DDDD":null!=e&&(i._dayOfYear=I(e));break;case"YY":o[Ie]=Ce.parseTwoDigitYear(e);break;case"YYYY":case"YYYYY":case"YYYYYY":o[Ie]=I(e);break;case"a":case"A":i._meridiem=e;break;case"h":case"hh":i._pf.bigHour=!0;case"H":case"HH":o[Ae]=I(e);break;case"m":case"mm":o[Re]=I(e);break;case"s":case"ss":o[Fe]=I(e);break;case"S":case"SS":case"SSS":case"SSSS":o[He]=I(1e3*("0."+e));break;case"x":i._d=new Date(I(e));break;case"X":i._d=new Date(1e3*parseFloat(e));break;case"Z":case"ZZ":i._useUTC=!0,i._tzm=Z(e);break;case"dd":case"ddd":case"dddd":s=i._locale.weekdaysParse(e),null!=s?(i._w=i._w||{},i._w.d=s):i._pf.invalidWeekday=e;break;case"w":case"ww":case"W":case"WW":case"d":case"e":case"E":t=t.substr(0,1);case"gggg":case"GGGG":case"GGGGG":t=t.substr(0,2),e&&(i._w=i._w||{},i._w[t]=I(e));break;case"gg":case"GG":i._w=i._w||{},i._w[t]=Ce.parseTwoDigitYear(e)}}function K(t){var e,i,s,o,n,a,h;e=t._w,null!=e.GG||null!=e.W||null!=e.E?(n=1,a=4,i=r(e.GG,t._a[Ie],me(Ce(),1,4).year),s=r(e.W,1),o=r(e.E,1)):(n=t._locale._week.dow,a=t._locale._week.doy,i=r(e.gg,t._a[Ie],me(Ce(),n,a).year),s=r(e.w,1),null!=e.d?(o=e.d,n>o&&++s):o=null!=e.e?e.e+n:n),h=fe(i,s,o,a,n),t._a[Ie]=h.year,t._dayOfYear=h.dayOfYear}function $(t){var e,i,s,o,n=[];if(!t._d){for(s=te(t),t._w&&null==t._a[Pe]&&null==t._a[ze]&&K(t),t._dayOfYear&&(o=r(t._a[Ie],s[Ie]),t._dayOfYear>A(o)&&(t._pf._overflowDayOfYear=!0),i=le(o,0,t._dayOfYear),t._a[ze]=i.getUTCMonth(),t._a[Pe]=i.getUTCDate()),e=0;3>e&&null==t._a[e];++e)t._a[e]=n[e]=s[e];for(;7>e;e++)t._a[e]=n[e]=null==t._a[e]?2===e?1:0:t._a[e];24===t._a[Ae]&&0===t._a[Re]&&0===t._a[Fe]&&0===t._a[He]&&(t._nextDay=!0,t._a[Ae]=0),t._d=(t._useUTC?le:de).apply(null,n),null!=t._tzm&&t._d.setUTCMinutes(t._d.getUTCMinutes()-t._tzm),t._nextDay&&(t._a[Ae]=24)}}function J(t){var e;t._d||(e=N(t._i),t._a=[e.year,e.month,e.day||e.date,e.hour,e.minute,e.second,e.millisecond],$(t))}function te(t){var e=new Date;return t._useUTC?[e.getUTCFullYear(),e.getUTCMonth(),e.getUTCDate()]:[e.getFullYear(),e.getMonth(),e.getDate()]}function ee(t){if(t._f===Ce.ISO_8601)return void ne(t);t._a=[],t._pf.empty=!0;var e,i,s,o,r,a=""+t._i,h=a.length,d=0;for(s=X(t._f,t._locale).match(Ue)||[],e=0;e0&&t._pf.unusedInput.push(r),a=a.slice(a.indexOf(i)+i.length),d+=i.length),wi[o]?(i?t._pf.empty=!1:t._pf.unusedTokens.push(o),Q(o,i,t)):t._strict&&!i&&t._pf.unusedTokens.push(o);t._pf.charsLeftOver=h-d,a.length>0&&t._pf.unusedInput.push(a),t._pf.bigHour===!0&&t._a[Ae]<=12&&(t._pf.bigHour=n),t._a[Ae]=f(t._locale,t._a[Ae],t._meridiem),$(t),F(t)}function ie(t){return t.replace(/\\(\[)|\\(\])|\[([^\]\[]*)\]|\\(.)/g,function(t,e,i,s,o){return e||i||s||o})}function se(t){return t.replace(/[-\/\\^$*+?.()|[\]{}]/g,"\\$&")}function oe(t){var e,i,s,o,n;if(0===t._f.length)return t._pf.invalidFormat=!0,void(t._d=new Date(0/0));for(o=0;on)&&(s=n,i=e));b(t,i||e)}function ne(t){var e,i,s=t._i,o=li.exec(s);if(o){for(t._pf.iso=!0,e=0,i=pi.length;i>e;e++)if(pi[e][1].exec(s)){t._f=pi[e][0]+(o[6]||" ");break}for(e=0,i=ui.length;i>e;e++)if(ui[e][1].exec(s)){t._f+=ui[e][0];break}s.match(ti)&&(t._f+="Z"),ee(t)}else t._isValid=!1}function re(t){ne(t),t._isValid===!1&&(delete t._isValid,Ce.createFromInputFallback(t))}function ae(t,e){var i,s=[];for(i=0;it&&a.setFullYear(t),a}function le(t){var e=new Date(Date.UTC.apply(null,arguments));return 1970>t&&e.setUTCFullYear(t),e}function ce(t,e){if("string"==typeof t)if(isNaN(t)){if(t=e.weekdaysParse(t),"number"!=typeof t)return null}else t=parseInt(t,10);return t}function pe(t,e,i,s,o){return o.relativeTime(e||1,!!i,t,s)}function ue(t,e,i){var s=Ce.duration(t).abs(),o=Ne(s.as("s")),n=Ne(s.as("m")),r=Ne(s.as("h")),a=Ne(s.as("d")),h=Ne(s.as("M")),d=Ne(s.as("y")),l=o0,l[4]=i,pe.apply({},l)}function me(t,e,i){var s,o=i-e,n=i-t.day();return n>o&&(n-=7),o-7>n&&(n+=7),s=Ce(t).add(n,"d"),{week:Math.ceil(s.dayOfYear()/7),year:s.year()}}function fe(t,e,i,s,o){var n,r,a=le(t,0,1).getUTCDay();return a=0===a?7:a,i=null!=i?i:o,n=o-a+(a>s?7:0)-(o>a?7:0),r=7*(e-1)+(i-o)+n+1,{year:r>0?t:t-1,dayOfYear:r>0?r:A(t-1)+r}}function ge(t){var e,i=t._i,s=t._f;return t._locale=t._locale||Ce.localeData(t._l),null===i||s===n&&""===i?Ce.invalid({nullInput:!0}):("string"==typeof i&&(t._i=i=t._locale.preparse(i)),Ce.isMoment(i)?new v(i,!0):(s?T(s)?oe(t):ee(t):he(t),e=new v(t),e._nextDay&&(e.add(1,"d"),e._nextDay=n),e))}function ve(t,e){var i,s;if(1===e.length&&T(e[0])&&(e=e[0]),!e.length)return Ce();for(i=e[0],s=1;s=0?"+":"-";return e+w(Math.abs(t),6)},gg:function(){return w(this.weekYear()%100,2)},gggg:function(){return w(this.weekYear(),4)},ggggg:function(){return w(this.weekYear(),5)},GG:function(){return w(this.isoWeekYear()%100,2)},GGGG:function(){return w(this.isoWeekYear(),4)},GGGGG:function(){return w(this.isoWeekYear(),5)},e:function(){return this.weekday()},E:function(){return this.isoWeekday()},a:function(){return this.localeData().meridiem(this.hours(),this.minutes(),!0)},A:function(){return this.localeData().meridiem(this.hours(),this.minutes(),!1)},H:function(){return this.hours()},h:function(){return this.hours()%12||12},m:function(){return this.minutes()},s:function(){return this.seconds()},S:function(){return I(this.milliseconds()/100)},SS:function(){return w(I(this.milliseconds()/10),2)},SSS:function(){return w(this.milliseconds(),3)},SSSS:function(){return w(this.milliseconds(),3)},Z:function(){var t=this.utcOffset(),e="+";return 0>t&&(t=-t,e="-"),e+w(I(t/60),2)+":"+w(I(t)%60,2)},ZZ:function(){var t=this.utcOffset(),e="+";return 0>t&&(t=-t,e="-"),e+w(I(t/60),2)+w(I(t)%60,2)},z:function(){return this.zoneAbbr()},zz:function(){return this.zoneName()},x:function(){return this.valueOf()},X:function(){return this.unix()},Q:function(){return this.quarter()}},Mi={},Si=["months","monthsShort","weekdays","weekdaysShort","weekdaysMin"],Di=!1;_i.length;)Oe=_i.pop(),wi[Oe+"o"]=u(wi[Oe],Oe);for(;xi.length;)Oe=xi.pop(),wi[Oe+Oe]=p(wi[Oe],2);wi.DDDD=p(wi.DDD,3),b(g.prototype,{set:function(t){var e,i;for(i in t)e=t[i],"function"==typeof e?this[i]=e:this["_"+i]=e;this._ordinalParseLenient=new RegExp(this._ordinalParse.source+"|"+/\d{1,2}/.source)},_months:"January_February_March_April_May_June_July_August_September_October_November_December".split("_"),months:function(t){return this._months[t.month()]},_monthsShort:"Jan_Feb_Mar_Apr_May_Jun_Jul_Aug_Sep_Oct_Nov_Dec".split("_"),monthsShort:function(t){return this._monthsShort[t.month()]},monthsParse:function(t,e,i){var s,o,n;for(this._monthsParse||(this._monthsParse=[],this._longMonthsParse=[],this._shortMonthsParse=[]),s=0;12>s;s++){if(o=Ce.utc([2e3,s]),i&&!this._longMonthsParse[s]&&(this._longMonthsParse[s]=new RegExp("^"+this.months(o,"").replace(".","")+"$","i"),this._shortMonthsParse[s]=new RegExp("^"+this.monthsShort(o,"").replace(".","")+"$","i")),i||this._monthsParse[s]||(n="^"+this.months(o,"")+"|^"+this.monthsShort(o,""),this._monthsParse[s]=new RegExp(n.replace(".",""),"i")),i&&"MMMM"===e&&this._longMonthsParse[s].test(t))return s;if(i&&"MMM"===e&&this._shortMonthsParse[s].test(t))return s;if(!i&&this._monthsParse[s].test(t))return s}},_weekdays:"Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday".split("_"),weekdays:function(t){return this._weekdays[t.day()]},_weekdaysShort:"Sun_Mon_Tue_Wed_Thu_Fri_Sat".split("_"),weekdaysShort:function(t){return this._weekdaysShort[t.day()]},_weekdaysMin:"Su_Mo_Tu_We_Th_Fr_Sa".split("_"),weekdaysMin:function(t){return this._weekdaysMin[t.day()]},weekdaysParse:function(t){var e,i,s;for(this._weekdaysParse||(this._weekdaysParse=[]),e=0;7>e;e++)if(this._weekdaysParse[e]||(i=Ce([2e3,1]).day(e),s="^"+this.weekdays(i,"")+"|^"+this.weekdaysShort(i,"")+"|^"+this.weekdaysMin(i,""),this._weekdaysParse[e]=new RegExp(s.replace(".",""),"i")),this._weekdaysParse[e].test(t))return e},_longDateFormat:{LTS:"h:mm:ss A",LT:"h:mm A",L:"MM/DD/YYYY",LL:"MMMM D, YYYY",LLL:"MMMM D, YYYY LT",LLLL:"dddd, MMMM D, YYYY LT"},longDateFormat:function(t){var e=this._longDateFormat[t];return!e&&this._longDateFormat[t.toUpperCase()]&&(e=this._longDateFormat[t.toUpperCase()].replace(/MMMM|MM|DD|dddd/g,function(t){return t.slice(1)}),this._longDateFormat[t]=e),e},isPM:function(t){return"p"===(t+"").toLowerCase().charAt(0)},_meridiemParse:/[ap]\.?m?\.?/i,meridiem:function(t,e,i){return t>11?i?"pm":"PM":i?"am":"AM"},_calendar:{sameDay:"[Today at] LT",nextDay:"[Tomorrow at] LT",nextWeek:"dddd [at] LT",lastDay:"[Yesterday at] LT",lastWeek:"[Last] dddd [at] LT",sameElse:"L"},calendar:function(t,e,i){var s=this._calendar[t];return"function"==typeof s?s.apply(e,[i]):s},_relativeTime:{future:"in %s",past:"%s ago",s:"a few seconds",m:"a minute",mm:"%d minutes",h:"an hour",hh:"%d hours",d:"a day",dd:"%d days",M:"a month",MM:"%d months",y:"a year",yy:"%d years"},relativeTime:function(t,e,i,s){var o=this._relativeTime[i];return"function"==typeof o?o(t,e,i,s):o.replace(/%d/i,t)},pastFuture:function(t,e){var i=this._relativeTime[t>0?"future":"past"];return"function"==typeof i?i(e):i.replace(/%s/i,e)},ordinal:function(t){return this._ordinal.replace("%d",t)},_ordinal:"%d",_ordinalParse:/\d{1,2}/,preparse:function(t){return t},postformat:function(t){return t},week:function(t){return me(t,this._week.dow,this._week.doy).week},_week:{dow:0,doy:6},firstDayOfWeek:function(){return this._week.dow},firstDayOfYear:function(){return this._week.doy},_invalidDate:"Invalid date",invalidDate:function(){return this._invalidDate}}),Ce=function(t,e,i,s){var o;return"boolean"==typeof i&&(s=i,i=n),o={},o._isAMomentObject=!0,o._i=t,o._f=e,o._l=i,o._strict=s,o._isUTC=!1,o._pf=h(),ge(o)},Ce.suppressDeprecationWarnings=!1,Ce.createFromInputFallback=l("moment construction falls back to js Date. This is discouraged and will be removed in upcoming major release. Please refer to https://github.com/moment/moment/issues/1407 for more info.",function(t){t._d=new Date(t._i+(t._useUTC?" UTC":""))}),Ce.min=function(){var t=[].slice.call(arguments,0);return ve("isBefore",t)},Ce.max=function(){var t=[].slice.call(arguments,0);return ve("isAfter",t)},Ce.utc=function(t,e,i,s){var o;return"boolean"==typeof i&&(s=i,i=n),o={},o._isAMomentObject=!0,o._useUTC=!0,o._isUTC=!0,o._l=i,o._i=t,o._f=e,o._strict=s,o._pf=h(),ge(o).utc()},Ce.unix=function(t){return Ce(1e3*t)},Ce.duration=function(t,e){var i,s,o,n,r=t,h=null;return Ce.isDuration(t)?r={ms:t._milliseconds,d:t._days,M:t._months}:"number"==typeof t?(r={},e?r[e]=t:r.milliseconds=t):(h=je.exec(t))?(i="-"===h[1]?-1:1,r={y:0,d:I(h[Pe])*i,h:I(h[Ae])*i,m:I(h[Re])*i,s:I(h[Fe])*i,ms:I(h[He])*i}):(h=Ve.exec(t))?(i="-"===h[1]?-1:1,o=function(t){var e=t&&parseFloat(t.replace(",","."));return(isNaN(e)?0:e)*i},r={y:o(h[2]),M:o(h[3]),d:o(h[4]),h:o(h[5]),m:o(h[6]),s:o(h[7]),w:o(h[8])}):null==r?r={}:"object"==typeof r&&("from"in r||"to"in r)&&(n=S(Ce(r.from),Ce(r.to)),r={},r.ms=n.milliseconds,r.M=n.months),s=new y(r),Ce.isDuration(t)&&a(t,"_locale")&&(s._locale=t._locale),s},Ce.version=Ee,Ce.defaultFormat=ci,Ce.ISO_8601=function(){},Ce.momentProperties=Ye,Ce.updateOffset=function(){},Ce.relativeTimeThreshold=function(t,e){return bi[t]===n?!1:e===n?bi[t]:(bi[t]=e,!0)},Ce.lang=l("moment.lang is deprecated. Use moment.locale instead.",function(t,e){return Ce.locale(t,e)}),Ce.locale=function(t,e){var i;return t&&(i="undefined"!=typeof e?Ce.defineLocale(t,e):Ce.localeData(t),i&&(Ce.duration._locale=Ce._locale=i)),Ce._locale._abbr},Ce.defineLocale=function(t,e){return null!==e?(e.abbr=t,Be[t]||(Be[t]=new g),Be[t].set(e),Ce.locale(t),Be[t]):(delete Be[t],null)},Ce.langData=l("moment.langData is deprecated. Use moment.localeData instead.",function(t){return Ce.localeData(t)}),Ce.localeData=function(t){var e;if(t&&t._locale&&t._locale._abbr&&(t=t._locale._abbr),!t)return Ce._locale;if(!T(t)){if(e=W(t))return e;t=[t]}return Y(t)},Ce.isMoment=function(t){return t instanceof v||null!=t&&a(t,"_isAMomentObject")},Ce.isDuration=function(t){return t instanceof y};for(Oe=Si.length-1;Oe>=0;--Oe)L(Si[Oe]);Ce.normalizeUnits=function(t){return k(t)},Ce.invalid=function(t){var e=Ce.utc(0/0);return null!=t?b(e._pf,t):e._pf.userInvalidated=!0,e},Ce.parseZone=function(){return Ce.apply(null,arguments).parseZone()},Ce.parseTwoDigitYear=function(t){return I(t)+(I(t)>68?1900:2e3)},Ce.isDate=O,b(Ce.fn=v.prototype,{clone:function(){return Ce(this)},valueOf:function(){return+this._d-6e4*(this._offset||0)},unix:function(){return Math.floor(+this/1e3)},toString:function(){return this.clone().locale("en").format("ddd MMM DD YYYY HH:mm:ss [GMT]ZZ")},toDate:function(){return this._offset?new Date(+this):this._d},toISOString:function(){var t=Ce(this).utc();return 00:!1},parsingFlags:function(){return b({},this._pf)},invalidAt:function(){return this._pf.overflow},utc:function(t){return this.utcOffset(0,t)},local:function(t){return this._isUTC&&(this.utcOffset(0,t),this._isUTC=!1,t&&this.subtract(this._dateUtcOffset(),"m")),this},format:function(t){var e=U(this,t||Ce.defaultFormat);return this.localeData().postformat(e)},add:D(1,"add"),subtract:D(-1,"subtract"),diff:function(t,e,i){var s,o,n=G(t,this),r=6e4*(n.utcOffset()-this.utcOffset());return e=k(e),"year"===e||"month"===e||"quarter"===e?(o=m(this,n),"quarter"===e?o/=3:"year"===e&&(o/=12)):(s=this-n,o="second"===e?s/1e3:"minute"===e?s/6e4:"hour"===e?s/36e5:"day"===e?(s-r)/864e5:"week"===e?(s-r)/6048e5:s),i?o:x(o)},from:function(t,e){return Ce.duration({to:this,from:t}).locale(this.locale()).humanize(!e)},fromNow:function(t){return this.from(Ce(),t)},calendar:function(t){var e=t||Ce(),i=G(e,this).startOf("day"),s=this.diff(i,"days",!0),o=-6>s?"sameElse":-1>s?"lastWeek":0>s?"lastDay":1>s?"sameDay":2>s?"nextDay":7>s?"nextWeek":"sameElse";return this.format(this.localeData().calendar(o,this,Ce(e)))},isLeapYear:function(){return R(this.year())},isDST:function(){return this.utcOffset()>this.clone().month(0).utcOffset()||this.utcOffset()>this.clone().month(5).utcOffset()},day:function(t){var e=this._isUTC?this._d.getUTCDay():this._d.getDay();return null!=t?(t=ce(t,this.localeData()),this.add(t-e,"d")):e},month:xe("Month",!0),startOf:function(t){switch(t=k(t)){case"year":this.month(0);case"quarter":case"month":this.date(1);case"week":case"isoWeek":case"day":this.hours(0);case"hour":this.minutes(0);case"minute":this.seconds(0);case"second":this.milliseconds(0)}return"week"===t?this.weekday(0):"isoWeek"===t&&this.isoWeekday(1),"quarter"===t&&this.month(3*Math.floor(this.month()/3)),this},endOf:function(t){return t=k(t),t===n||"millisecond"===t?this:this.startOf(t).add(1,"isoWeek"===t?"week":t).subtract(1,"ms")},isAfter:function(t,e){var i;return e=k("undefined"!=typeof e?e:"millisecond"),"millisecond"===e?(t=Ce.isMoment(t)?t:Ce(t),+this>+t):(i=Ce.isMoment(t)?+t:+Ce(t),i<+this.clone().startOf(e))},isBefore:function(t,e){var i;return e=k("undefined"!=typeof e?e:"millisecond"),"millisecond"===e?(t=Ce.isMoment(t)?t:Ce(t),+t>+this):(i=Ce.isMoment(t)?+t:+Ce(t),+this.clone().endOf(e)t?this:t}),max:l("moment().max is deprecated, use moment.max instead. https://github.com/moment/moment/issues/1548",function(t){return t=Ce.apply(null,arguments),t>this?this:t}),zone:l("moment().zone is deprecated, use moment().utcOffset instead. https://github.com/moment/moment/issues/1779",function(t,e){return null!=t?("string"!=typeof t&&(t=-t),this.utcOffset(t,e),this):-this.utcOffset()}),utcOffset:function(t,e){var i,s=this._offset||0;return null!=t?("string"==typeof t&&(t=Z(t)),Math.abs(t)<16&&(t=60*t),!this._isUTC&&e&&(i=this._dateUtcOffset()),this._offset=t,this._isUTC=!0,null!=i&&this.add(i,"m"),s!==t&&(!e||this._changeInProgress?C(this,Ce.duration(t-s,"m"),1,!1):this._changeInProgress||(this._changeInProgress=!0,Ce.updateOffset(this,!0),this._changeInProgress=null)),this):this._isUTC?s:this._dateUtcOffset()},isLocal:function(){return!this._isUTC},isUtcOffset:function(){return this._isUTC},isUtc:function(){return this._isUTC&&0===this._offset},zoneAbbr:function(){return this._isUTC?"UTC":""},zoneName:function(){return this._isUTC?"Coordinated Universal Time":""},parseZone:function(){return this._tzm?this.utcOffset(this._tzm):"string"==typeof this._i&&this.utcOffset(Z(this._i)),this},hasAlignedHourOffset:function(t){return t=t?Ce(t).utcOffset():0,(this.utcOffset()-t)%60===0},daysInMonth:function(){return z(this.year(),this.month())},dayOfYear:function(t){var e=Ne((Ce(this).startOf("day")-Ce(this).startOf("year"))/864e5)+1;return null==t?e:this.add(t-e,"d")},quarter:function(t){return null==t?Math.ceil((this.month()+1)/3):this.month(3*(t-1)+this.month()%3)},weekYear:function(t){var e=me(this,this.localeData()._week.dow,this.localeData()._week.doy).year;return null==t?e:this.add(t-e,"y")},isoWeekYear:function(t){var e=me(this,1,4).year;return null==t?e:this.add(t-e,"y")},week:function(t){var e=this.localeData().week(this);return null==t?e:this.add(7*(t-e),"d")},isoWeek:function(t){var e=me(this,1,4).week;return null==t?e:this.add(7*(t-e),"d")},weekday:function(t){var e=(this.day()+7-this.localeData()._week.dow)%7;return null==t?e:this.add(t-e,"d")},isoWeekday:function(t){return null==t?this.day()||7:this.day(this.day()%7?t:t-7)},isoWeeksInYear:function(){return P(this.year(),1,4)},weeksInYear:function(){var t=this.localeData()._week;return P(this.year(),t.dow,t.doy)},get:function(t){return t=k(t),this[t]()},set:function(t,e){var i;if("object"==typeof t)for(i in t)this.set(i,t[i]);else t=k(t),"function"==typeof this[t]&&this[t](e);return this},locale:function(t){var e;return t===n?this._locale._abbr:(e=Ce.localeData(t),null!=e&&(this._locale=e),this)},lang:l("moment().lang() is deprecated. Instead, use moment().localeData() to get the language configuration. Use moment().locale() to change languages.",function(t){return t===n?this.localeData():this.locale(t)}),localeData:function(){return this._locale},_dateUtcOffset:function(){return 15*-Math.round(this._d.getTimezoneOffset()/15)}}),Ce.fn.millisecond=Ce.fn.milliseconds=xe("Milliseconds",!1),Ce.fn.second=Ce.fn.seconds=xe("Seconds",!1),Ce.fn.minute=Ce.fn.minutes=xe("Minutes",!1),Ce.fn.hour=Ce.fn.hours=xe("Hours",!0),Ce.fn.date=xe("Date",!0),Ce.fn.dates=l("dates accessor is deprecated. Use date instead.",xe("Date",!0)),Ce.fn.year=xe("FullYear",!0),Ce.fn.years=l("years accessor is deprecated. Use year instead.",xe("FullYear",!0)),Ce.fn.days=Ce.fn.day,Ce.fn.months=Ce.fn.month,Ce.fn.weeks=Ce.fn.week,Ce.fn.isoWeeks=Ce.fn.isoWeek,Ce.fn.quarters=Ce.fn.quarter,Ce.fn.toJSON=Ce.fn.toISOString,Ce.fn.isUTC=Ce.fn.isUtc,b(Ce.duration.fn=y.prototype,{_bubble:function(){var t,e,i,s=this._milliseconds,o=this._days,n=this._months,r=this._data,a=0;r.milliseconds=s%1e3,t=x(s/1e3),r.seconds=t%60,e=x(t/60),r.minutes=e%60,i=x(e/60),r.hours=i%24,o+=x(i/24),a=x(we(o)),o-=x(Me(a)),n+=x(o/30),o%=30,a+=x(n/12),n%=12,r.days=o,r.months=n,r.years=a},abs:function(){return this._milliseconds=Math.abs(this._milliseconds),this._days=Math.abs(this._days),this._months=Math.abs(this._months),this._data.milliseconds=Math.abs(this._data.milliseconds),this._data.seconds=Math.abs(this._data.seconds),this._data.minutes=Math.abs(this._data.minutes),this._data.hours=Math.abs(this._data.hours),this._data.months=Math.abs(this._data.months),this._data.years=Math.abs(this._data.years),this},weeks:function(){return x(this.days()/7)},valueOf:function(){return this._milliseconds+864e5*this._days+this._months%12*2592e6+31536e6*I(this._months/12)},humanize:function(t){var e=ue(this,!t,this.localeData());return t&&(e=this.localeData().pastFuture(+this,e)),this.localeData().postformat(e)},add:function(t,e){var i=Ce.duration(t,e);return this._milliseconds+=i._milliseconds,this._days+=i._days,this._months+=i._months,this._bubble(),this},subtract:function(t,e){var i=Ce.duration(t,e);return this._milliseconds-=i._milliseconds,this._days-=i._days,this._months-=i._months,this._bubble(),this},get:function(t){return t=k(t),this[t.toLowerCase()+"s"]()},as:function(t){var e,i;if(t=k(t),"month"===t||"year"===t)return e=this._days+this._milliseconds/864e5,i=this._months+12*we(e),"month"===t?i:i/12;switch(e=this._days+Math.round(Me(this._months/12)),t){case"week":return e/7+this._milliseconds/6048e5;case"day":return e+this._milliseconds/864e5;case"hour":return 24*e+this._milliseconds/36e5;case"minute":return 24*e*60+this._milliseconds/6e4;case"second":return 24*e*60*60+this._milliseconds/1e3;case"millisecond":return Math.floor(24*e*60*60*1e3)+this._milliseconds;default:throw new Error("Unknown unit "+t)}},lang:Ce.fn.lang,locale:Ce.fn.locale,toIsoString:l("toIsoString() is deprecated. Please use toISOString() instead (notice the capitals)",function(){return this.toISOString()}),toISOString:function(){var t=Math.abs(this.years()),e=Math.abs(this.months()),i=Math.abs(this.days()),s=Math.abs(this.hours()),o=Math.abs(this.minutes()),n=Math.abs(this.seconds()+this.milliseconds()/1e3);return this.asSeconds()?(this.asSeconds()<0?"-":"")+"P"+(t?t+"Y":"")+(e?e+"M":"")+(i?i+"D":"")+(s||o||n?"T":"")+(s?s+"H":"")+(o?o+"M":"")+(n?n+"S":""):"P0D"},localeData:function(){return this._locale},toJSON:function(){return this.toISOString()}}),Ce.duration.fn.toString=Ce.duration.fn.toISOString;for(Oe in fi)a(fi,Oe)&&Se(Oe.toLowerCase());Ce.duration.fn.asMilliseconds=function(){return this.as("ms")},Ce.duration.fn.asSeconds=function(){return this.as("s")},Ce.duration.fn.asMinutes=function(){return this.as("m")},Ce.duration.fn.asHours=function(){return this.as("h")},Ce.duration.fn.asDays=function(){return this.as("d")},Ce.duration.fn.asWeeks=function(){return this.as("weeks")},Ce.duration.fn.asMonths=function(){return this.as("M")},Ce.duration.fn.asYears=function(){return this.as("y")},Ce.locale("en",{ordinalParse:/\d{1,2}(th|st|nd|rd)/,ordinal:function(t){var e=t%10,i=1===I(t%100/10)?"th":1===e?"st":2===e?"nd":3===e?"rd":"th";return t+i}}),We?o.exports=Ce:(s=function(t,e,i){return i.config&&i.config()&&i.config().noGlobal===!0&&(ke.moment=Te),Ce}.call(e,i,e,o),!(s!==n&&(o.exports=s)),De(!0))}).call(this)}).call(e,function(){return this}(),i(71)(t))},function(t,e,i){var s;!function(o,n){function r(){a.READY||(w.determineEventTypes(),x.each(a.gestures,function(t){S.register(t)}),w.onTouch(a.DOCUMENT,v,S.detect),w.onTouch(a.DOCUMENT,y,S.detect),a.READY=!0)}var a=function D(t,e){return new D.Instance(t,e||{})};a.VERSION="1.1.3",a.defaults={behavior:{userSelect:"none",touchAction:"pan-y",touchCallout:"none",contentZooming:"none",userDrag:"none",tapHighlightColor:"rgba(0,0,0,0)"}},a.DOCUMENT=document,a.HAS_POINTEREVENTS=navigator.pointerEnabled||navigator.msPointerEnabled,a.HAS_TOUCHEVENTS="ontouchstart"in o,a.IS_MOBILE=/mobile|tablet|ip(ad|hone|od)|android|silk/i.test(navigator.userAgent),a.NO_MOUSEEVENTS=a.HAS_TOUCHEVENTS&&a.IS_MOBILE||a.HAS_POINTEREVENTS,a.CALCULATE_INTERVAL=25;var h={},d=a.DIRECTION_DOWN="down",l=a.DIRECTION_LEFT="left",c=a.DIRECTION_UP="up",p=a.DIRECTION_RIGHT="right",u=a.POINTER_MOUSE="mouse",m=a.POINTER_TOUCH="touch",f=a.POINTER_PEN="pen",g=a.EVENT_START="start",v=a.EVENT_MOVE="move",y=a.EVENT_END="end",b=a.EVENT_RELEASE="release",_=a.EVENT_TOUCH="touch";a.READY=!1,a.plugins=a.plugins||{},a.gestures=a.gestures||{};var x=a.utils={extend:function(t,e,i){for(var s in e)!e.hasOwnProperty(s)||t[s]!==n&&i||(t[s]=e[s]);return t},on:function(t,e,i){t.addEventListener(e,i,!1)},off:function(t,e,i){t.removeEventListener(e,i,!1)},each:function(t,e,i){var s,o;if("forEach"in t)t.forEach(e,i);else if(t.length!==n){for(s=0,o=t.length;o>s;s++)if(e.call(i,t[s],s,t)===!1)return}else for(s in t)if(t.hasOwnProperty(s)&&e.call(i,t[s],s,t)===!1)return},inStr:function(t,e){return t.indexOf(e)>-1},inArray:function(t,e){if(t.indexOf){var i=t.indexOf(e);return-1===i?!1:i}for(var s=0,o=t.length;o>s;s++)if(t[s]===e)return s;return!1},toArray:function(t){return Array.prototype.slice.call(t,0)},hasParent:function(t,e){for(;t;){if(t==e)return!0;t=t.parentNode}return!1},getCenter:function(t){var e=[],i=[],s=[],o=[],n=Math.min,r=Math.max;return 1===t.length?{pageX:t[0].pageX,pageY:t[0].pageY,clientX:t[0].clientX,clientY:t[0].clientY}:(x.each(t,function(t){e.push(t.pageX),i.push(t.pageY),s.push(t.clientX),o.push(t.clientY)}),{pageX:(n.apply(Math,e)+r.apply(Math,e))/2,pageY:(n.apply(Math,i)+r.apply(Math,i))/2,clientX:(n.apply(Math,s)+r.apply(Math,s))/2,clientY:(n.apply(Math,o)+r.apply(Math,o))/2})},getVelocity:function(t,e,i){return{x:Math.abs(e/t)||0,y:Math.abs(i/t)||0}},getAngle:function(t,e){var i=e.clientX-t.clientX,s=e.clientY-t.clientY;return 180*Math.atan2(s,i)/Math.PI},getDirection:function(t,e){var i=Math.abs(t.clientX-e.clientX),s=Math.abs(t.clientY-e.clientY);return i>=s?t.clientX-e.clientX>0?l:p:t.clientY-e.clientY>0?c:d},getDistance:function(t,e){var i=e.clientX-t.clientX,s=e.clientY-t.clientY;return Math.sqrt(i*i+s*s)},getScale:function(t,e){return t.length>=2&&e.length>=2?this.getDistance(e[0],e[1])/this.getDistance(t[0],t[1]):1},getRotation:function(t,e){return t.length>=2&&e.length>=2?this.getAngle(e[1],e[0])-this.getAngle(t[1],t[0]):0},isVertical:function(t){return t==c||t==d},setPrefixedCss:function(t,e,i,s){var o=["","Webkit","Moz","O","ms"];e=x.toCamelCase(e);for(var n=0;n0&&this.started&&(r=v),this.started=!0;var d=this.collectEventData(i,r,o,t);return e!=y&&s.call(S,d),a&&(d.changedLength=h,d.eventType=a,s.call(S,d),d.eventType=r,delete d.changedLength),r==y&&(s.call(S,d),this.started=!1),r},determineEventTypes:function(){var t;return t=a.HAS_POINTEREVENTS?o.PointerEvent?["pointerdown","pointermove","pointerup pointercancel lostpointercapture"]:["MSPointerDown","MSPointerMove","MSPointerUp MSPointerCancel MSLostPointerCapture"]:a.NO_MOUSEEVENTS?["touchstart","touchmove","touchend touchcancel"]:["touchstart mousedown","touchmove mousemove","touchend touchcancel mouseup"],h[g]=t[0],h[v]=t[1],h[y]=t[2],h},getTouchList:function(t,e){if(a.HAS_POINTEREVENTS)return M.getTouchList();if(t.touches){if(e==v)return t.touches;var i=[],s=[].concat(x.toArray(t.touches),x.toArray(t.changedTouches)),o=[];return x.each(s,function(t){x.inArray(i,t.identifier)===!1&&o.push(t),i.push(t.identifier)}),o}return t.identifier=1,[t]},collectEventData:function(t,e,i,s){var o=m;return x.inStr(s.type,"mouse")||M.matchType(u,s)?o=u:M.matchType(f,s)&&(o=f),{center:x.getCenter(i),timeStamp:Date.now(),target:s.target,touches:i,eventType:e,pointerType:o,srcEvent:s,preventDefault:function(){var t=this.srcEvent;t.preventManipulation&&t.preventManipulation(),t.preventDefault&&t.preventDefault()},stopPropagation:function(){this.srcEvent.stopPropagation()},stopDetect:function(){return S.stopDetect()}}}},M=a.PointerEvent={pointers:{},getTouchList:function(){var t=[];return x.each(this.pointers,function(e){t.push(e)}),t},updatePointer:function(t,e){t==y||t!=y&&1!==e.buttons?delete this.pointers[e.pointerId]:(e.identifier=e.pointerId,this.pointers[e.pointerId]=e)},matchType:function(t,e){if(!e.pointerType)return!1;var i=e.pointerType,s={};return s[u]=i===(e.MSPOINTER_TYPE_MOUSE||u),s[m]=i===(e.MSPOINTER_TYPE_TOUCH||m),s[f]=i===(e.MSPOINTER_TYPE_PEN||f),s[t]},reset:function(){this.pointers={}}},S=a.detection={gestures:[],current:null,previous:null,stopped:!1,startDetect:function(t,e){this.current||(this.stopped=!1,this.current={inst:t,startEvent:x.extend({},e),lastEvent:!1,lastCalcEvent:!1,futureCalcEvent:!1,lastCalcData:{},name:""},this.detect(e))},detect:function(t){if(this.current&&!this.stopped){t=this.extendEventData(t);var e=this.current.inst,i=e.options;return x.each(this.gestures,function(s){!this.stopped&&e.enabled&&i[s.name]&&s.handler.call(s,t,e)},this),this.current&&(this.current.lastEvent=t),t.eventType==y&&this.stopDetect(),t}},stopDetect:function(){this.previous=x.extend({},this.current),this.current=null,this.stopped=!0},getCalculatedData:function(t,e,i,s,o){var n=this.current,r=!1,h=n.lastCalcEvent,d=n.lastCalcData;h&&t.timeStamp-h.timeStamp>a.CALCULATE_INTERVAL&&(e=h.center,i=t.timeStamp-h.timeStamp,s=t.center.clientX-h.center.clientX,o=t.center.clientY-h.center.clientY,r=!0),(t.eventType==_||t.eventType==b)&&(n.futureCalcEvent=t),(!n.lastCalcEvent||r)&&(d.velocity=x.getVelocity(i,s,o),d.angle=x.getAngle(e,t.center),d.direction=x.getDirection(e,t.center),n.lastCalcEvent=n.futureCalcEvent||t,n.futureCalcEvent=t),t.velocityX=d.velocity.x,t.velocityY=d.velocity.y,t.interimAngle=d.angle,t.interimDirection=d.direction},extendEventData:function(t){var e=this.current,i=e.startEvent,s=e.lastEvent||i;(t.eventType==_||t.eventType==b)&&(i.touches=[],x.each(t.touches,function(t){i.touches.push({clientX:t.clientX,clientY:t.clientY})}));var o=t.timeStamp-i.timeStamp,n=t.center.clientX-i.center.clientX,r=t.center.clientY-i.center.clientY;return this.getCalculatedData(t,s.center,o,n,r),x.extend(t,{startEvent:i,deltaTime:o,deltaX:n,deltaY:r,distance:x.getDistance(i.center,t.center),angle:x.getAngle(i.center,t.center),direction:x.getDirection(i.center,t.center),scale:x.getScale(i.touches,t.touches),rotation:x.getRotation(i.touches,t.touches)}),t},register:function(t){var e=t.defaults||{};return e[t.name]===n&&(e[t.name]=!0),x.extend(a.defaults,e,!0),t.index=t.index||1e3,this.gestures.push(t),this.gestures.sort(function(t,e){return t.indexe.index?1:0}),this.gestures}};a.Instance=function(t,e){var i=this;r(),this.element=t,this.enabled=!0,x.each(e,function(t,i){delete e[i],e[x.toCamelCase(i)]=t}),this.options=x.extend(x.extend({},a.defaults),e||{}),this.options.behavior&&x.toggleBehavior(this.element,this.options.behavior,!0),this.eventStartHandler=w.onTouch(t,g,function(t){i.enabled&&t.eventType==g?S.startDetect(i,t):t.eventType==_&&S.detect(t)}),this.eventHandlers=[]},a.Instance.prototype={on:function(t,e){var i=this;return w.on(i.element,t,e,function(t){i.eventHandlers.push({gesture:t,handler:e})}),i},off:function(t,e){var i=this;return w.off(i.element,t,e,function(t){var s=x.inArray({gesture:t,handler:e});s!==!1&&i.eventHandlers.splice(s,1)}),i},trigger:function(t,e){e||(e={});var i=a.DOCUMENT.createEvent("Event");i.initEvent(t,!0,!0),i.gesture=e;var s=this.element;return x.hasParent(e.target,s)&&(s=e.target),s.dispatchEvent(i),this},enable:function(t){return this.enabled=t,this},dispose:function(){var t,e;for(x.toggleBehavior(this.element,this.options.behavior,!1),t=-1;e=this.eventHandlers[++t];)x.off(this.element,e.gesture,e.handler);return this.eventHandlers=[],w.off(this.element,h[g],this.eventStartHandler),null}},function(t){function e(e,s){var o=S.current;if(!(s.options.dragMaxTouches>0&&e.touches.length>s.options.dragMaxTouches))switch(e.eventType){case g:i=!1;break;case v:if(e.distance0)){var r=Math.abs(s.options.dragMinDistance/e.distance);n.pageX+=e.deltaX*r,n.pageY+=e.deltaY*r,n.clientX+=e.deltaX*r,n.clientY+=e.deltaY*r,e=S.extendEventData(e)}(o.lastEvent.dragLockToAxis||s.options.dragLockToAxis&&s.options.dragLockMinDistance<=e.distance)&&(e.dragLockToAxis=!0);var a=o.lastEvent.direction;e.dragLockToAxis&&a!==e.direction&&(e.direction=x.isVertical(a)?e.deltaY<0?c:d:e.deltaX<0?l:p),i||(s.trigger(t+"start",e),i=!0),s.trigger(t,e),s.trigger(t+e.direction,e);var h=x.isVertical(e.direction);(s.options.dragBlockVertical&&h||s.options.dragBlockHorizontal&&!h)&&e.preventDefault();break;case b:i&&e.changedLength<=s.options.dragMaxTouches&&(s.trigger(t+"end",e),i=!1);break;case y:i=!1}}var i=!1;a.gestures.Drag={name:t,index:50,handler:e,defaults:{dragMinDistance:10,dragDistanceCorrection:!0,dragMaxTouches:1,dragBlockHorizontal:!1,dragBlockVertical:!1,dragLockToAxis:!1,dragLockMinDistance:25}}}("drag"),a.gestures.Gesture={name:"gesture",index:1337,handler:function(t,e){e.trigger(this.name,t)}},function(t){function e(e,s){var o=s.options,n=S.current;switch(e.eventType){case g:clearTimeout(i),n.name=t,i=setTimeout(function(){n&&n.name==t&&s.trigger(t,e)},o.holdTimeout);break;case v:e.distance>o.holdThreshold&&clearTimeout(i);break;case b:clearTimeout(i)}}var i;a.gestures.Hold={name:t,index:10,defaults:{holdTimeout:500,holdThreshold:2},handler:e}}("hold"),a.gestures.Release={name:"release",index:1/0,handler:function(t,e){t.eventType==b&&e.trigger(this.name,t)}},a.gestures.Swipe={name:"swipe",index:40,defaults:{swipeMinTouches:1,swipeMaxTouches:1,swipeVelocityX:.6,swipeVelocityY:.6},handler:function(t,e){if(t.eventType==b){var i=t.touches.length,s=e.options;if(is.swipeMaxTouches)return;(t.velocityX>s.swipeVelocityX||t.velocityY>s.swipeVelocityY)&&(e.trigger(this.name,t),e.trigger(this.name+t.direction,t))}}},function(t){function e(e,s){var o,n,r=s.options,a=S.current,h=S.previous;switch(e.eventType){case g:i=!1;break;case v:i=i||e.distance>r.tapMaxDistance;break;case y:!x.inStr(e.srcEvent.type,"cancel")&&e.deltaTimes.options.transformMinRotation&&s.trigger("rotate",e),o>s.options.transformMinScale&&(s.trigger("pinch",e),s.trigger("pinch"+(e.scale<1?"in":"out"),e)); +break;case b:i&&e.changedLength<2&&(s.trigger(t+"end",e),i=!1)}}var i=!1;a.gestures.Transform={name:t,index:45,defaults:{transformMinScale:.01,transformMinRotation:1},handler:e}}("transform"),s=function(){return a}.call(e,i,e,t),!(s!==n&&(t.exports=s))}(window)},function(t,e,i){function s(){this.constants.smoothCurves.enabled=!this.constants.smoothCurves.enabled;var t=document.getElementById("graph_toggleSmooth");t.style.background=1==this.constants.smoothCurves.enabled?"#A4FF56":"#FF8532",this._configureSmoothCurves(!1)}function o(){for(var t in this.calculationNodes)this.calculationNodes.hasOwnProperty(t)&&(this.calculationNodes[t].vx=0,this.calculationNodes[t].vy=0,this.calculationNodes[t].fx=0,this.calculationNodes[t].fy=0);1==this.constants.hierarchicalLayout.enabled?(this._setupHierarchicalLayout(),a.call(this,"graph_H_nd",1,"physics_hierarchicalRepulsion_nodeDistance"),a.call(this,"graph_H_cg",1,"physics_centralGravity"),a.call(this,"graph_H_sc",1,"physics_springConstant"),a.call(this,"graph_H_sl",1,"physics_springLength"),a.call(this,"graph_H_damp",1,"physics_damping")):this.repositionNodes(),this.moving=!0,this.start()}function n(){var t="No options are required, default values used.",e=[],i=document.getElementById("graph_physicsMethod1"),s=document.getElementById("graph_physicsMethod2");if(1==i.checked){if(this.constants.physics.barnesHut.gravitationalConstant!=this.backupConstants.physics.barnesHut.gravitationalConstant&&e.push("gravitationalConstant: "+this.constants.physics.barnesHut.gravitationalConstant),this.constants.physics.centralGravity!=this.backupConstants.physics.barnesHut.centralGravity&&e.push("centralGravity: "+this.constants.physics.centralGravity),this.constants.physics.springLength!=this.backupConstants.physics.barnesHut.springLength&&e.push("springLength: "+this.constants.physics.springLength),this.constants.physics.springConstant!=this.backupConstants.physics.barnesHut.springConstant&&e.push("springConstant: "+this.constants.physics.springConstant),this.constants.physics.damping!=this.backupConstants.physics.barnesHut.damping&&e.push("damping: "+this.constants.physics.damping),0!=e.length){t="var options = {",t+="physics: {barnesHut: {";for(var o=0;othis.constants.clustering.clusterThreshold&&1==this.constants.clustering.enabled&&this.clusterToFit(this.constants.clustering.reduceToNodes,!1),this._calculateForces())},e._calculateForces=function(){this._calculateGravitationalForces(),this._calculateNodeForces(),this.constants.physics.springConstant>0&&(1==this.constants.smoothCurves.enabled&&1==this.constants.smoothCurves.dynamic?this._calculateSpringForcesWithSupport():1==this.constants.physics.hierarchicalRepulsion.enabled?this._calculateHierarchicalSpringForces():this._calculateSpringForces())},e._updateCalculationNodes=function(){if(1==this.constants.smoothCurves.enabled&&1==this.constants.smoothCurves.dynamic){this.calculationNodes={},this.calculationNodeIndices=[];for(var t in this.nodes)this.nodes.hasOwnProperty(t)&&(this.calculationNodes[t]=this.nodes[t]);var e=this.sectors.support.nodes;for(var i in e)e.hasOwnProperty(i)&&(this.edges.hasOwnProperty(e[i].parentEdgeId)?this.calculationNodes[i]=e[i]:e[i]._setForce(0,0));for(var s in this.calculationNodes)this.calculationNodes.hasOwnProperty(s)&&this.calculationNodeIndices.push(s)}else this.calculationNodes=this.nodes,this.calculationNodeIndices=this.nodeIndices},e._calculateGravitationalForces=function(){var t,e,i,s,o,n=this.calculationNodes,r=this.constants.physics.centralGravity,a=0;for(o=0;oSimulation Mode:Barnes HutRepulsionHierarchical
Options:
',this.containerElement.parentElement.insertBefore(this.physicsConfiguration,this.containerElement),this.optionsDiv=document.createElement("div"),this.optionsDiv.style.fontSize="14px",this.optionsDiv.style.fontFamily="verdana",this.containerElement.parentElement.insertBefore(this.optionsDiv,this.containerElement);var e;e=document.getElementById("graph_BH_gc"),e.onchange=a.bind(this,"graph_BH_gc",-1,"physics_barnesHut_gravitationalConstant"),e=document.getElementById("graph_BH_cg"),e.onchange=a.bind(this,"graph_BH_cg",1,"physics_centralGravity"),e=document.getElementById("graph_BH_sc"),e.onchange=a.bind(this,"graph_BH_sc",1,"physics_springConstant"),e=document.getElementById("graph_BH_sl"),e.onchange=a.bind(this,"graph_BH_sl",1,"physics_springLength"),e=document.getElementById("graph_BH_damp"),e.onchange=a.bind(this,"graph_BH_damp",1,"physics_damping"),e=document.getElementById("graph_R_nd"),e.onchange=a.bind(this,"graph_R_nd",1,"physics_repulsion_nodeDistance"),e=document.getElementById("graph_R_cg"),e.onchange=a.bind(this,"graph_R_cg",1,"physics_centralGravity"),e=document.getElementById("graph_R_sc"),e.onchange=a.bind(this,"graph_R_sc",1,"physics_springConstant"),e=document.getElementById("graph_R_sl"),e.onchange=a.bind(this,"graph_R_sl",1,"physics_springLength"),e=document.getElementById("graph_R_damp"),e.onchange=a.bind(this,"graph_R_damp",1,"physics_damping"),e=document.getElementById("graph_H_nd"),e.onchange=a.bind(this,"graph_H_nd",1,"physics_hierarchicalRepulsion_nodeDistance"),e=document.getElementById("graph_H_cg"),e.onchange=a.bind(this,"graph_H_cg",1,"physics_centralGravity"),e=document.getElementById("graph_H_sc"),e.onchange=a.bind(this,"graph_H_sc",1,"physics_springConstant"),e=document.getElementById("graph_H_sl"),e.onchange=a.bind(this,"graph_H_sl",1,"physics_springLength"),e=document.getElementById("graph_H_damp"),e.onchange=a.bind(this,"graph_H_damp",1,"physics_damping"),e=document.getElementById("graph_H_direction"),e.onchange=a.bind(this,"graph_H_direction",t,"hierarchicalLayout_direction"),e=document.getElementById("graph_H_levsep"),e.onchange=a.bind(this,"graph_H_levsep",1,"hierarchicalLayout_levelSeparation"),e=document.getElementById("graph_H_nspac"),e.onchange=a.bind(this,"graph_H_nspac",1,"hierarchicalLayout_nodeSpacing");var i=document.getElementById("graph_physicsMethod1"),d=document.getElementById("graph_physicsMethod2"),l=document.getElementById("graph_physicsMethod3");d.checked=!0,this.constants.physics.barnesHut.enabled&&(i.checked=!0),this.constants.hierarchicalLayout.enabled&&(l.checked=!0);var c=document.getElementById("graph_toggleSmooth"),p=document.getElementById("graph_repositionNodes"),u=document.getElementById("graph_generateOptions");c.onclick=s.bind(this),p.onclick=o.bind(this),u.onclick=n.bind(this),c.style.background=1==this.constants.smoothCurves&&0==this.constants.dynamicSmoothCurves?"#A4FF56":"#FF8532",r.apply(this),i.onchange=r.bind(this),d.onchange=r.bind(this),l.onchange=r.bind(this)}},e._overWriteGraphConstants=function(t,e){var i=t.split("_");1==i.length?this.constants[i[0]]=e:2==i.length?this.constants[i[0]][i[1]]=e:3==i.length&&(this.constants[i[0]][i[1]][i[2]]=e)}},function(t,e){e.startWithClustering=function(){this.clusterToFit(this.constants.clustering.initialMaxNodes,!0),this.updateLabels(),this.stabilize&&this._stabilize(),this.start()},e.clusterToFit=function(t,e){for(var i=this.nodeIndices.length,s=2,o=0;i>t&&s>o;)console.log("Performing clustering:",o,i,this.clusterSession),i=this.nodeIndices.length,o+=1;console.log("finished"),o>0&&1==e&&this.repositionNodes(),this._updateCalculationNodes()},e.openCluster=function(t){var e=this.moving;if(t.clusterSize>this.constants.clustering.sectorThreshold&&this._nodeInActiveArea(t)&&("default"!=this._sector()||1!=this.nodeIndices.length)){this._addSector(t);for(var i=0;this.nodeIndices.lengthi;)this.decreaseClusterLevel(),i+=1}else this._expandClusterNode(t,!1,!0),this._updateNodeIndexList(),this._updateDynamicEdges(),this._updateCalculationNodes(),this.updateLabels();this.moving!=e&&this.start()},e.updateClustersDefault=function(){1==this.constants.clustering.enabled&&1==this.constants.clustering.clusterByZoom&&this.updateClusters(0,!1,!1)},e.increaseClusterLevel=function(){this.updateClusters(-1,!1,!0)},e.decreaseClusterLevel=function(){this.updateClusters(1,!1,!0)},e.updateClusters=function(t,e,i,s){var o=this.moving,n=this.nodeIndices.length;this.previousScale>this.scale&&0==t&&this._collapseSector(),this.previousScale>this.scale||-1==t?this._formClusters(i):(this.previousScalethis.scale||-1==t)&&(this._aggregateHubs(i),this._updateNodeIndexList()),(this.previousScale>this.scale||-1==t)&&(this.handleChains(),this._updateNodeIndexList()),this.previousScale=this.scale,this._updateDynamicEdges(),this.updateLabels(),this.nodeIndices.lengththis.constants.clustering.chainThreshold&&this._reduceAmountOfChains(1-this.constants.clustering.chainThreshold/t)},e._aggregateHubs=function(t){this._getHubSize(),this._formClustersByHub(t,!1)},e.forceAggregateHubs=function(t){var e=this.moving,i=this.nodeIndices.length;this._aggregateHubs(!0),this._updateNodeIndexList(),this._updateCalculationNodes(),this._updateDynamicEdges(),this.updateLabels(),this.nodeIndices.length!=i&&(this.clusterSession+=1),(0==t||void 0===t)&&this.moving!=e&&this.start()},e._openClustersBySize=function(){for(var t in this.nodes)if(this.nodes.hasOwnProperty(t)){var e=this.nodes[t];1==e.inView()&&(e.width*this.scale>this.constants.clustering.screenSizeThreshold*this.frame.canvas.clientWidth||e.height*this.scale>this.constants.clustering.screenSizeThreshold*this.frame.canvas.clientHeight)&&this.openCluster(e)}},e._openClusters=function(t,e){for(var i=0;i1&&(t.clusterSizei)){var r=n.from,a=n.to;n.to.options.mass>n.from.options.mass&&(r=n.to,a=n.from),1==a.dynamicEdgesLength?this._addToCluster(r,a,!1):1==r.dynamicEdgesLength&&this._addToCluster(a,r,!1)}}},e._forceClustersByZoom=function(){for(var t in this.nodes)if(this.nodes.hasOwnProperty(t)){var e=this.nodes[t];if(1==e.dynamicEdgesLength&&0!=e.dynamicEdges.length){var i=e.dynamicEdges[0],s=i.toId==e.id?this.nodes[i.fromId]:this.nodes[i.toId];e.id!=s.id&&(s.options.mass>e.options.mass?this._addToCluster(s,e,!0):this._addToCluster(e,s,!0))}}},e._clusterToSmallestNeighbour=function(t){for(var e=-1,i=null,s=0;so.clusterSessions.length&&(e=o.clusterSessions.length,i=o)}null!=o&&void 0!==this.nodes[o.id]&&this._addToCluster(o,t,!0)},e._formClustersByHub=function(t,e){for(var i in this.nodes)this.nodes.hasOwnProperty(i)&&this._formClusterFromHub(this.nodes[i],t,e)},e._formClusterFromHub=function(t,e,i,s){if(void 0===s&&(s=0),t.dynamicEdgesLength<0&&console.error(t.dynamicEdgesLength,this.hubThreshold,i),t.dynamicEdgesLength>=this.hubThreshold&&0==i||t.dynamicEdgesLength==this.hubThreshold&&1==i){for(var o,n,r,a=this.constants.clustering.clusterEdgeThreshold/this.scale,h=!1,d=[],l=t.dynamicEdges.length,c=0;l>c;c++)d.push(t.dynamicEdges[c].id);if(0==e)for(h=!1,c=0;l>c;c++){var p=this.edges[d[c]];if(void 0!==p&&p.connected&&p.toId!=p.fromId&&(o=p.to.x-p.from.x,n=p.to.y-p.from.y,r=Math.sqrt(o*o+n*n),a>r)){h=!0;break}}if(!e&&h||e){var u=[],m={};for(c=0;l>c;c++){p=this.edges[d[c]];var f=this.nodes[p.fromId==t.id?p.toId:p.fromId];void 0===m[f.id]&&(m[f.id]=!0,u.push(f))}for(c=0;c1)for(var s=0;s1&&(e.label="[".concat(String(e.clusterSize),"]"))}for(t in this.nodes)this.nodes.hasOwnProperty(t)&&(e=this.nodes[t],1==e.clusterSize&&(e.label=void 0!==e.originalLabel?e.originalLabel:String(e.id)))},e.normalizeClusterLevels=function(){var t,e=0,i=1e9,s=0;for(t in this.nodes)this.nodes.hasOwnProperty(t)&&(s=this.nodes[t].clusterSessions.length,s>e&&(e=s),i>s&&(i=s));if(e-i>this.constants.clustering.clusterLevelDifference){var o=this.nodeIndices.length,n=e-this.constants.clustering.clusterLevelDifference;for(t in this.nodes)this.nodes.hasOwnProperty(t)&&this.nodes[t].clusterSessions.lengths&&(s=n.dynamicEdgesLength),t+=n.dynamicEdgesLength,e+=Math.pow(n.dynamicEdgesLength,2),i+=1}t/=i,e/=i;var r=e-Math.pow(t,2),a=Math.sqrt(r);this.hubThreshold=Math.floor(t+2*a),this.hubThreshold>s&&(this.hubThreshold=s)},e._reduceAmountOfChains=function(t){this.hubThreshold=2;var e=Math.floor(this.nodeIndices.length*t);for(var i in this.nodes)this.nodes.hasOwnProperty(i)&&2==this.nodes[i].dynamicEdgesLength&&this.nodes[i].dynamicEdges.length>=2&&e>0&&(this._formClusterFromHub(this.nodes[i],!0,!0,1),e-=1)},e._getChainFraction=function(){var t=0,e=0;for(var i in this.nodes)this.nodes.hasOwnProperty(i)&&(2==this.nodes[i].dynamicEdgesLength&&this.nodes[i].dynamicEdges.length>=2&&(t+=1),e+=1);return t/e}},function(t,e,i){var s=i(1),o=i(40);e._putDataInSector=function(){this.sectors.active[this._sector()].nodes=this.nodes,this.sectors.active[this._sector()].edges=this.edges,this.sectors.active[this._sector()].nodeIndices=this.nodeIndices},e._switchToSector=function(t,e){void 0===e||"active"==e?this._switchToActiveSector(t):this._switchToFrozenSector(t)},e._switchToActiveSector=function(t){this.nodeIndices=this.sectors.active[t].nodeIndices,this.nodes=this.sectors.active[t].nodes,this.edges=this.sectors.active[t].edges},e._switchToSupportSector=function(){this.nodeIndices=this.sectors.support.nodeIndices,this.nodes=this.sectors.support.nodes,this.edges=this.sectors.support.edges},e._switchToFrozenSector=function(t){this.nodeIndices=this.sectors.frozen[t].nodeIndices,this.nodes=this.sectors.frozen[t].nodes,this.edges=this.sectors.frozen[t].edges},e._loadLatestSector=function(){this._switchToSector(this._sector())},e._sector=function(){return this.activeSector[this.activeSector.length-1]},e._previousSector=function(){if(this.activeSector.length>1)return this.activeSector[this.activeSector.length-2];throw new TypeError("there are not enough sectors in the this.activeSector array.")},e._setActiveSector=function(t){this.activeSector.push(t)},e._forgetLastSector=function(){this.activeSector.pop()},e._createNewSector=function(t){this.sectors.active[t]={nodes:{},edges:{},nodeIndices:[],formationScale:this.scale,drawingNode:void 0},this.sectors.active[t].drawingNode=new o({id:t,color:{background:"#eaefef",border:"495c5e"}},{},{},this.constants),this.sectors.active[t].drawingNode.clusterSize=2},e._deleteActiveSector=function(t){delete this.sectors.active[t]},e._deleteFrozenSector=function(t){delete this.sectors.frozen[t]},e._freezeSector=function(t){this.sectors.frozen[t]=this.sectors.active[t],this._deleteActiveSector(t)},e._activateSector=function(t){this.sectors.active[t]=this.sectors.frozen[t],this._deleteFrozenSector(t)},e._mergeThisWithFrozen=function(t){for(var e in this.nodes)this.nodes.hasOwnProperty(e)&&(this.sectors.frozen[t].nodes[e]=this.nodes[e]);for(var i in this.edges)this.edges.hasOwnProperty(i)&&(this.sectors.frozen[t].edges[i]=this.edges[i]);for(var s=0;s1?this[t](o[0],o[1]):this[t](e))}return this._loadLatestSector(),i},e._doInSupportSector=function(t,e){var i=!1;if(void 0===e)this._switchToSupportSector(),i=this[t]();else{this._switchToSupportSector();var s=Array.prototype.splice.call(arguments,1);i=s.length>1?this[t](s[0],s[1]):this[t](e)}return this._loadLatestSector(),i},e._doInAllFrozenSectors=function(t,e){if(void 0===e)for(var i in this.sectors.frozen)this.sectors.frozen.hasOwnProperty(i)&&(this._switchToFrozenSector(i),this[t]());else for(var i in this.sectors.frozen)if(this.sectors.frozen.hasOwnProperty(i)){this._switchToFrozenSector(i);var s=Array.prototype.splice.call(arguments,1);s.length>1?this[t](s[0],s[1]):this[t](e)}this._loadLatestSector()},e._doInAllSectors=function(t,e){var i=Array.prototype.splice.call(arguments,1);void 0===e?(this._doInAllActiveSectors(t),this._doInAllFrozenSectors(t)):i.length>1?(this._doInAllActiveSectors(t,i[0],i[1]),this._doInAllFrozenSectors(t,i[0],i[1])):(this._doInAllActiveSectors(t,e),this._doInAllFrozenSectors(t,e))},e._clearNodeIndexList=function(){var t=this._sector();this.sectors.active[t].nodeIndices=[],this.nodeIndices=this.sectors.active[t].nodeIndices},e._drawSectorNodes=function(t,e){var i,s=1e9,o=-1e9,n=1e9,r=-1e9;for(var a in this.sectors[e])if(this.sectors[e].hasOwnProperty(a)&&void 0!==this.sectors[e][a].drawingNode){this._switchToSector(a,e),s=1e9,o=-1e9,n=1e9,r=-1e9;for(var h in this.nodes)this.nodes.hasOwnProperty(h)&&(i=this.nodes[h],i.resize(t),n>i.x-.5*i.width&&(n=i.x-.5*i.width),ri.y-.5*i.height&&(s=i.y-.5*i.height),o0?this.nodes[i[i.length-1]]:null},e._getEdgesOverlappingWith=function(t,e){var i=this.edges;for(var s in i)i.hasOwnProperty(s)&&i[s].isOverlappingWith(t)&&e.push(s)},e._getAllEdgesOverlappingWith=function(t){var e=[];return this._doInAllActiveSectors("_getEdgesOverlappingWith",t,e),e},e._getEdgeAt=function(t){var e=this._pointerToPositionObject(t),i=this._getAllEdgesOverlappingWith(e);return i.length>0?this.edges[i[i.length-1]]:null},e._addToSelection=function(t){t instanceof s?this.selectionObj.nodes[t.id]=t:this.selectionObj.edges[t.id]=t},e._addToHover=function(t){t instanceof s?this.hoverObj.nodes[t.id]=t:this.hoverObj.edges[t.id]=t},e._removeFromSelection=function(t){t instanceof s?delete this.selectionObj.nodes[t.id]:delete this.selectionObj.edges[t.id]},e._unselectAll=function(t){void 0===t&&(t=!1);for(var e in this.selectionObj.nodes)this.selectionObj.nodes.hasOwnProperty(e)&&this.selectionObj.nodes[e].unselect();for(var i in this.selectionObj.edges)this.selectionObj.edges.hasOwnProperty(i)&&this.selectionObj.edges[i].unselect();this.selectionObj={nodes:{},edges:{}},0==t&&this.emit("select",this.getSelection())},e._unselectClusters=function(t){void 0===t&&(t=!1);for(var e in this.selectionObj.nodes)this.selectionObj.nodes.hasOwnProperty(e)&&this.selectionObj.nodes[e].clusterSize>1&&(this.selectionObj.nodes[e].unselect(),this._removeFromSelection(this.selectionObj.nodes[e]));0==t&&this.emit("select",this.getSelection())},e._getSelectedNodeCount=function(){var t=0;for(var e in this.selectionObj.nodes)this.selectionObj.nodes.hasOwnProperty(e)&&(t+=1);return t},e._getSelectedNode=function(){for(var t in this.selectionObj.nodes)if(this.selectionObj.nodes.hasOwnProperty(t))return this.selectionObj.nodes[t];return null},e._getSelectedEdge=function(){for(var t in this.selectionObj.edges)if(this.selectionObj.edges.hasOwnProperty(t))return this.selectionObj.edges[t];return null},e._getSelectedEdgeCount=function(){var t=0;for(var e in this.selectionObj.edges)this.selectionObj.edges.hasOwnProperty(e)&&(t+=1);return t},e._getSelectedObjectCount=function(){var t=0;for(var e in this.selectionObj.nodes)this.selectionObj.nodes.hasOwnProperty(e)&&(t+=1);for(var i in this.selectionObj.edges)this.selectionObj.edges.hasOwnProperty(i)&&(t+=1);return t},e._selectionIsEmpty=function(){for(var t in this.selectionObj.nodes)if(this.selectionObj.nodes.hasOwnProperty(t))return!1;for(var e in this.selectionObj.edges)if(this.selectionObj.edges.hasOwnProperty(e))return!1;return!0},e._clusterInSelection=function(){for(var t in this.selectionObj.nodes)if(this.selectionObj.nodes.hasOwnProperty(t)&&this.selectionObj.nodes[t].clusterSize>1)return!0;return!1},e._selectConnectedEdges=function(t){for(var e=0;ei;i++){o=t[i];var n=this.nodes[o];if(!n)throw new RangeError('Node with id "'+o+'" not found');this._selectObject(n,!0,!0,e,!0)}this.redraw()},e.selectEdges=function(t){var e,i,s;if(!t||void 0==t.length)throw"Selection must be an array with ids";for(this._unselectAll(!0),e=0,i=t.length;i>e;e++){s=t[e];var o=this.edges[s];if(!o)throw new RangeError('Edge with id "'+s+'" not found');this._selectObject(o,!0,!0,!1,!0)}this.redraw()},e._updateSelection=function(){for(var t in this.selectionObj.nodes)this.selectionObj.nodes.hasOwnProperty(t)&&(this.nodes.hasOwnProperty(t)||delete this.selectionObj.nodes[t]);for(var e in this.selectionObj.edges)this.selectionObj.edges.hasOwnProperty(e)&&(this.edges.hasOwnProperty(e)||delete this.selectionObj.edges[e])}},function(t,e,i){var s=i(1),o=i(40),n=i(37);e._clearManipulatorBar=function(){this._recursiveDOMDelete(this.manipulationDiv),this.manipulationDOM={},this._manipulationReleaseOverload=function(){},delete this.sectors.support.nodes.targetNode,delete this.sectors.support.nodes.targetViaNode,this.controlNodesActive=!1,this.freezeSimulation=!1},e._restoreOverloadedFunctions=function(){for(var t in this.cachedFunctions)this.cachedFunctions.hasOwnProperty(t)&&(this[t]=this.cachedFunctions[t],delete this.cachedFunctions[t])},e._toggleEditMode=function(){this.editMode=!this.editMode;var t=this.manipulationDiv,e=this.closeDiv,i=this.editModeDiv;1==this.editMode?(t.style.display="block",e.style.display="block",i.style.display="none",e.onclick=this._toggleEditMode.bind(this)):(t.style.display="none",e.style.display="none",i.style.display="block",e.onclick=null),this._createManipulatorBar()},e._createManipulatorBar=function(){this.boundFunction&&this.off("select",this.boundFunction);var t=this.constants.locales[this.constants.locale];if(void 0!==this.edgeBeingEdited&&(this.edgeBeingEdited._disableControlNodes(),this.edgeBeingEdited=void 0,this.selectedControlNode=null,this.controlNodesActive=!1,this._redraw()),this._restoreOverloadedFunctions(),this.freezeSimulation=!1,this.blockConnectingEdgeSelection=!1,this.forceAppendSelection=!1,this.manipulationDOM={},1==this.editMode){for(;this.manipulationDiv.hasChildNodes();)this.manipulationDiv.removeChild(this.manipulationDiv.firstChild);this.manipulationDOM.addNodeSpan=document.createElement("span"),this.manipulationDOM.addNodeSpan.className="network-manipulationUI add",this.manipulationDOM.addNodeLabelSpan=document.createElement("span"),this.manipulationDOM.addNodeLabelSpan.className="network-manipulationLabel",this.manipulationDOM.addNodeLabelSpan.innerHTML=t.addNode,this.manipulationDOM.addNodeSpan.appendChild(this.manipulationDOM.addNodeLabelSpan),this.manipulationDOM.seperatorLineDiv1=document.createElement("div"),this.manipulationDOM.seperatorLineDiv1.className="network-seperatorLine",this.manipulationDOM.addEdgeSpan=document.createElement("span"),this.manipulationDOM.addEdgeSpan.className="network-manipulationUI connect",this.manipulationDOM.addEdgeLabelSpan=document.createElement("span"),this.manipulationDOM.addEdgeLabelSpan.className="network-manipulationLabel",this.manipulationDOM.addEdgeLabelSpan.innerHTML=t.addEdge,this.manipulationDOM.addEdgeSpan.appendChild(this.manipulationDOM.addEdgeLabelSpan),this.manipulationDiv.appendChild(this.manipulationDOM.addNodeSpan),this.manipulationDiv.appendChild(this.manipulationDOM.seperatorLineDiv1),this.manipulationDiv.appendChild(this.manipulationDOM.addEdgeSpan),1==this._getSelectedNodeCount()&&this.triggerFunctions.edit?(this.manipulationDOM.seperatorLineDiv2=document.createElement("div"),this.manipulationDOM.seperatorLineDiv2.className="network-seperatorLine",this.manipulationDOM.editNodeSpan=document.createElement("span"),this.manipulationDOM.editNodeSpan.className="network-manipulationUI edit",this.manipulationDOM.editNodeLabelSpan=document.createElement("span"),this.manipulationDOM.editNodeLabelSpan.className="network-manipulationLabel",this.manipulationDOM.editNodeLabelSpan.innerHTML=t.editNode,this.manipulationDOM.editNodeSpan.appendChild(this.manipulationDOM.editNodeLabelSpan),this.manipulationDiv.appendChild(this.manipulationDOM.seperatorLineDiv2),this.manipulationDiv.appendChild(this.manipulationDOM.editNodeSpan)):1==this._getSelectedEdgeCount()&&0==this._getSelectedNodeCount()&&(this.manipulationDOM.seperatorLineDiv3=document.createElement("div"),this.manipulationDOM.seperatorLineDiv3.className="network-seperatorLine",this.manipulationDOM.editEdgeSpan=document.createElement("span"),this.manipulationDOM.editEdgeSpan.className="network-manipulationUI edit",this.manipulationDOM.editEdgeLabelSpan=document.createElement("span"),this.manipulationDOM.editEdgeLabelSpan.className="network-manipulationLabel",this.manipulationDOM.editEdgeLabelSpan.innerHTML=t.editEdge,this.manipulationDOM.editEdgeSpan.appendChild(this.manipulationDOM.editEdgeLabelSpan),this.manipulationDiv.appendChild(this.manipulationDOM.seperatorLineDiv3),this.manipulationDiv.appendChild(this.manipulationDOM.editEdgeSpan)),0==this._selectionIsEmpty()&&(this.manipulationDOM.seperatorLineDiv4=document.createElement("div"),this.manipulationDOM.seperatorLineDiv4.className="network-seperatorLine",this.manipulationDOM.deleteSpan=document.createElement("span"),this.manipulationDOM.deleteSpan.className="network-manipulationUI delete",this.manipulationDOM.deleteLabelSpan=document.createElement("span"),this.manipulationDOM.deleteLabelSpan.className="network-manipulationLabel",this.manipulationDOM.deleteLabelSpan.innerHTML=t.del,this.manipulationDOM.deleteSpan.appendChild(this.manipulationDOM.deleteLabelSpan),this.manipulationDiv.appendChild(this.manipulationDOM.seperatorLineDiv4),this.manipulationDiv.appendChild(this.manipulationDOM.deleteSpan)),this.manipulationDOM.addNodeSpan.onclick=this._createAddNodeToolbar.bind(this),this.manipulationDOM.addEdgeSpan.onclick=this._createAddEdgeToolbar.bind(this),1==this._getSelectedNodeCount()&&this.triggerFunctions.edit?this.manipulationDOM.editNodeSpan.onclick=this._editNode.bind(this):1==this._getSelectedEdgeCount()&&0==this._getSelectedNodeCount()&&(this.manipulationDOM.editEdgeSpan.onclick=this._createEditEdgeToolbar.bind(this)),0==this._selectionIsEmpty()&&(this.manipulationDOM.deleteSpan.onclick=this._deleteSelected.bind(this)),this.closeDiv.onclick=this._toggleEditMode.bind(this);var e=this;this.boundFunction=e._createManipulatorBar,this.on("select",this.boundFunction)}else{for(;this.editModeDiv.hasChildNodes();)this.editModeDiv.removeChild(this.editModeDiv.firstChild);this.manipulationDOM.editModeSpan=document.createElement("span"),this.manipulationDOM.editModeSpan.className="network-manipulationUI edit editmode",this.manipulationDOM.editModeLabelSpan=document.createElement("span"),this.manipulationDOM.editModeLabelSpan.className="network-manipulationLabel",this.manipulationDOM.editModeLabelSpan.innerHTML=t.edit,this.manipulationDOM.editModeSpan.appendChild(this.manipulationDOM.editModeLabelSpan),this.editModeDiv.appendChild(this.manipulationDOM.editModeSpan),this.manipulationDOM.editModeSpan.onclick=this._toggleEditMode.bind(this)}},e._createAddNodeToolbar=function(){this._clearManipulatorBar(),this.boundFunction&&this.off("select",this.boundFunction);var t=this.constants.locales[this.constants.locale];this.manipulationDOM={},this.manipulationDOM.backSpan=document.createElement("span"),this.manipulationDOM.backSpan.className="network-manipulationUI back",this.manipulationDOM.backLabelSpan=document.createElement("span"),this.manipulationDOM.backLabelSpan.className="network-manipulationLabel",this.manipulationDOM.backLabelSpan.innerHTML=t.back,this.manipulationDOM.backSpan.appendChild(this.manipulationDOM.backLabelSpan),this.manipulationDOM.seperatorLineDiv1=document.createElement("div"),this.manipulationDOM.seperatorLineDiv1.className="network-seperatorLine",this.manipulationDOM.descriptionSpan=document.createElement("span"),this.manipulationDOM.descriptionSpan.className="network-manipulationUI none",this.manipulationDOM.descriptionLabelSpan=document.createElement("span"),this.manipulationDOM.descriptionLabelSpan.className="network-manipulationLabel",this.manipulationDOM.descriptionLabelSpan.innerHTML=t.addDescription,this.manipulationDOM.descriptionSpan.appendChild(this.manipulationDOM.descriptionLabelSpan),this.manipulationDiv.appendChild(this.manipulationDOM.backSpan),this.manipulationDiv.appendChild(this.manipulationDOM.seperatorLineDiv1),this.manipulationDiv.appendChild(this.manipulationDOM.descriptionSpan),this.manipulationDOM.backSpan.onclick=this._createManipulatorBar.bind(this);var e=this;this.boundFunction=e._addNode,this.on("select",this.boundFunction)},e._createAddEdgeToolbar=function(){this._clearManipulatorBar(),this._unselectAll(!0),this.freezeSimulation=!0,this.boundFunction&&this.off("select",this.boundFunction);var t=this.constants.locales[this.constants.locale];this._unselectAll(),this.forceAppendSelection=!1,this.blockConnectingEdgeSelection=!0,this.manipulationDOM={},this.manipulationDOM.backSpan=document.createElement("span"),this.manipulationDOM.backSpan.className="network-manipulationUI back",this.manipulationDOM.backLabelSpan=document.createElement("span"),this.manipulationDOM.backLabelSpan.className="network-manipulationLabel",this.manipulationDOM.backLabelSpan.innerHTML=t.back,this.manipulationDOM.backSpan.appendChild(this.manipulationDOM.backLabelSpan),this.manipulationDOM.seperatorLineDiv1=document.createElement("div"),this.manipulationDOM.seperatorLineDiv1.className="network-seperatorLine",this.manipulationDOM.descriptionSpan=document.createElement("span"),this.manipulationDOM.descriptionSpan.className="network-manipulationUI none",this.manipulationDOM.descriptionLabelSpan=document.createElement("span"),this.manipulationDOM.descriptionLabelSpan.className="network-manipulationLabel",this.manipulationDOM.descriptionLabelSpan.innerHTML=t.edgeDescription,this.manipulationDOM.descriptionSpan.appendChild(this.manipulationDOM.descriptionLabelSpan),this.manipulationDiv.appendChild(this.manipulationDOM.backSpan),this.manipulationDiv.appendChild(this.manipulationDOM.seperatorLineDiv1),this.manipulationDiv.appendChild(this.manipulationDOM.descriptionSpan),this.manipulationDOM.backSpan.onclick=this._createManipulatorBar.bind(this);var e=this;this.boundFunction=e._handleConnect,this.on("select",this.boundFunction),this.cachedFunctions._handleTouch=this._handleTouch,this.cachedFunctions._manipulationReleaseOverload=this._manipulationReleaseOverload,this.cachedFunctions._handleDragStart=this._handleDragStart,this.cachedFunctions._handleDragEnd=this._handleDragEnd,this.cachedFunctions._handleOnHold=this._handleOnHold,this._handleTouch=this._handleConnect,this._manipulationReleaseOverload=function(){},this._handleOnHold=function(){},this._handleDragStart=function(){},this._handleDragEnd=this._finishConnect,this._redraw()},e._createEditEdgeToolbar=function(){this._clearManipulatorBar(),this.controlNodesActive=!0,this.boundFunction&&this.off("select",this.boundFunction),this.edgeBeingEdited=this._getSelectedEdge(),this.edgeBeingEdited._enableControlNodes();var t=this.constants.locales[this.constants.locale];this.manipulationDOM={},this.manipulationDOM.backSpan=document.createElement("span"),this.manipulationDOM.backSpan.className="network-manipulationUI back",this.manipulationDOM.backLabelSpan=document.createElement("span"),this.manipulationDOM.backLabelSpan.className="network-manipulationLabel",this.manipulationDOM.backLabelSpan.innerHTML=t.back,this.manipulationDOM.backSpan.appendChild(this.manipulationDOM.backLabelSpan),this.manipulationDOM.seperatorLineDiv1=document.createElement("div"),this.manipulationDOM.seperatorLineDiv1.className="network-seperatorLine",this.manipulationDOM.descriptionSpan=document.createElement("span"),this.manipulationDOM.descriptionSpan.className="network-manipulationUI none",this.manipulationDOM.descriptionLabelSpan=document.createElement("span"),this.manipulationDOM.descriptionLabelSpan.className="network-manipulationLabel",this.manipulationDOM.descriptionLabelSpan.innerHTML=t.editEdgeDescription,this.manipulationDOM.descriptionSpan.appendChild(this.manipulationDOM.descriptionLabelSpan),this.manipulationDiv.appendChild(this.manipulationDOM.backSpan),this.manipulationDiv.appendChild(this.manipulationDOM.seperatorLineDiv1),this.manipulationDiv.appendChild(this.manipulationDOM.descriptionSpan),this.manipulationDOM.backSpan.onclick=this._createManipulatorBar.bind(this),this.cachedFunctions._handleTouch=this._handleTouch,this.cachedFunctions._manipulationReleaseOverload=this._manipulationReleaseOverload,this.cachedFunctions._handleTap=this._handleTap,this.cachedFunctions._handleDragStart=this._handleDragStart,this.cachedFunctions._handleOnDrag=this._handleOnDrag,this._handleTouch=this._selectControlNode,this._handleTap=function(){},this._handleOnDrag=this._controlNodeDrag,this._handleDragStart=function(){},this._manipulationReleaseOverload=this._releaseControlNode,this._redraw()},e._selectControlNode=function(t){this.edgeBeingEdited.controlNodes.from.unselect(),this.edgeBeingEdited.controlNodes.to.unselect(),this.selectedControlNode=this.edgeBeingEdited._getSelectedControlNode(this._XconvertDOMtoCanvas(t.x),this._YconvertDOMtoCanvas(t.y)),null!==this.selectedControlNode&&(this.selectedControlNode.select(),this.freezeSimulation=!0),this._redraw()},e._controlNodeDrag=function(t){var e=this._getPointer(t.gesture.center);null!==this.selectedControlNode&&void 0!==this.selectedControlNode&&(this.selectedControlNode.x=this._XconvertDOMtoCanvas(e.x),this.selectedControlNode.y=this._YconvertDOMtoCanvas(e.y)),this._redraw()},e._releaseControlNode=function(t){var e=this._getNodeAt(t);null!==e?(1==this.edgeBeingEdited.controlNodes.from.selected&&(this.edgeBeingEdited._restoreControlNodes(),this._editEdge(e.id,this.edgeBeingEdited.to.id),this.edgeBeingEdited.controlNodes.from.unselect()),1==this.edgeBeingEdited.controlNodes.to.selected&&(this.edgeBeingEdited._restoreControlNodes(),this._editEdge(this.edgeBeingEdited.from.id,e.id),this.edgeBeingEdited.controlNodes.to.unselect())):this.edgeBeingEdited._restoreControlNodes(),this.freezeSimulation=!1,this._redraw()},e._handleConnect=function(t){if(0==this._getSelectedNodeCount()){var e=this._getNodeAt(t);if(null!=e)if(e.clusterSize>1)alert(this.constants.locales[this.constants.locale].createEdgeError);else{this._selectObject(e,!1);var i=this.sectors.support.nodes;i.targetNode=new o({id:"targetNode"},{},{},this.constants);var s=i.targetNode;s.x=e.x,s.y=e.y,this.edges.connectionEdge=new n({id:"connectionEdge",from:e.id,to:s.id},this,this.constants);var r=this.edges.connectionEdge;r.from=e,r.connected=!0,r.options.smoothCurves={enabled:!0,dynamic:!1,type:"continuous",roundness:.5},r.selected=!0,r.to=s,this.cachedFunctions._handleOnDrag=this._handleOnDrag,this._handleOnDrag=function(t){var e=this._getPointer(t.gesture.center),i=this.edges.connectionEdge;i.to.x=this._XconvertDOMtoCanvas(e.x),i.to.y=this._YconvertDOMtoCanvas(e.y)},this.moving=!0,this.start()}}},e._finishConnect=function(t){if(1==this._getSelectedNodeCount()){var e=this._getPointer(t.gesture.center);this._handleOnDrag=this.cachedFunctions._handleOnDrag,delete this.cachedFunctions._handleOnDrag;var i=this.edges.connectionEdge.fromId;delete this.edges.connectionEdge,delete this.sectors.support.nodes.targetNode,delete this.sectors.support.nodes.targetViaNode;var s=this._getNodeAt(e);null!=s&&(s.clusterSize>1?alert(this.constants.locales[this.constants.locale].createEdgeError):(this._createEdge(i,s.id),this._createManipulatorBar())),this._unselectAll()}},e._addNode=function(){if(this._selectionIsEmpty()&&1==this.editMode){var t=this._pointerToPositionObject(this.pointerPosition),e={id:s.randomUUID(),x:t.left,y:t.top,label:"new",allowedToMoveX:!0,allowedToMoveY:!0};if(this.triggerFunctions.add){if(2!=this.triggerFunctions.add.length)throw new Error("The function for add does not support two arguments (data,callback)");var i=this;this.triggerFunctions.add(e,function(t){i.nodesData.add(t),i._createManipulatorBar(),i.moving=!0,i.start()})}else this.nodesData.add(e),this._createManipulatorBar(),this.moving=!0,this.start()}},e._createEdge=function(t,e){if(1==this.editMode){var i={from:t,to:e};if(this.triggerFunctions.connect){if(2!=this.triggerFunctions.connect.length)throw new Error("The function for connect does not support two arguments (data,callback)");var s=this;this.triggerFunctions.connect(i,function(t){s.edgesData.add(t),s.moving=!0,s.start()})}else this.edgesData.add(i),this.moving=!0,this.start()}},e._editEdge=function(t,e){if(1==this.editMode){var i={id:this.edgeBeingEdited.id,from:t,to:e};if(this.triggerFunctions.editEdge){if(2!=this.triggerFunctions.editEdge.length)throw new Error("The function for edit does not support two arguments (data, callback)");var s=this;this.triggerFunctions.editEdge(i,function(t){s.edgesData.update(t),s.moving=!0,s.start()})}else this.edgesData.update(i),this.moving=!0,this.start()}},e._editNode=function(){if(!this.triggerFunctions.edit||1!=this.editMode)throw new Error("No edit function has been bound to this button");var t=this._getSelectedNode(),e={id:t.id,label:t.label,group:t.options.group,shape:t.options.shape,color:{background:t.options.color.background,border:t.options.color.border,highlight:{background:t.options.color.highlight.background,border:t.options.color.highlight.border}}};if(2!=this.triggerFunctions.edit.length)throw new Error("The function for edit does not support two arguments (data, callback)");var i=this;this.triggerFunctions.edit(e,function(t){i.nodesData.update(t),i._createManipulatorBar(),i.moving=!0,i.start()})},e._deleteSelected=function(){if(!this._selectionIsEmpty()&&1==this.editMode)if(this._clusterInSelection())alert(this.constants.locales[this.constants.locale].deleteClusterError); +else{var t=this.getSelectedNodes(),e=this.getSelectedEdges();if(this.triggerFunctions.del){var i=this,s={nodes:t,edges:e};if(2!=this.triggerFunctions.del.length)throw new Error("The function for delete does not support two arguments (data, callback)");this.triggerFunctions.del(s,function(t){i.edgesData.remove(t.edges),i.nodesData.remove(t.nodes),i._unselectAll(),i.moving=!0,i.start()})}else this.edgesData.remove(e),this.nodesData.remove(t),this._unselectAll(),this.moving=!0,this.start()}}},function(t,e,i){var s=(i(1),i(45));e._cleanNavigation=function(){if(0!=this.navigationHammers.existing.length){for(var t=0;t0){var t,e,i=0,s=!1,o=!1;for(e in this.nodes)this.nodes.hasOwnProperty(e)&&(t=this.nodes[e],-1!=t.level?s=!0:o=!0,is&&(n.xFixed=!1,n.x=i[n.level].minPos,r=!0):n.yFixed&&n.level>s&&(n.yFixed=!1,n.y=i[n.level].minPos,r=!0),1==r&&(i[n.level].minPos+=i[n.level].nodeSpacing,n.edges.length>1&&this._placeBranchNodes(n.edges,n.id,i,n.level))}},e._setLevel=function(t,e,i){for(var s=0;st)&&(o.level=t,o.edges.length>1&&this._setLevel(t+1,o.edges,o.id))}},e._setLevelDirected=function(t,e,i){this.nodes[i].hierarchyEnumerated=!0;for(var s,o,n=0;n1&&s.hierarchyEnumerated===!1&&this._setLevelDirected(s.level,s.edges,s.id)},e._restoreNodes=function(){for(var t in this.nodes)this.nodes.hasOwnProperty(t)&&(this.nodes[t].xFixed=!1,this.nodes[t].yFixed=!1)}},function(t){function e(t){throw new Error("Cannot find module '"+t+"'.")}e.keys=function(){return[]},e.resolve=e,t.exports=e,e.id=67},function(t,e){e._calculateNodeForces=function(){var t,e,i,s,o,n,r,a,h,d,l,c=this.calculationNodes,p=this.calculationNodeIndices,u=-2/3,m=4/3,f=this.constants.physics.repulsion.nodeDistance,g=f;for(d=0;di&&(r=.5*g>i?1:v*i+m,r*=0==n?1:1+n*this.constants.clustering.forceAmplification,r/=Math.max(i,.01*g),s=t*r,o=e*r,a.fx-=s,a.fy-=o,h.fx+=s,h.fy+=o)}}},function(t,e){e._calculateNodeForces=function(){var t,e,i,s,o,n,r,a,h,d,l=this.calculationNodes,c=this.calculationNodeIndices,p=this.constants.physics.hierarchicalRepulsion.nodeDistance;for(h=0;hi?-Math.pow(u*i,2)+Math.pow(u*p,2):0,0==i?i=.01:n/=i,s=t*n,o=e*n,r.fx-=s,r.fy-=o,a.fx+=s,a.fy+=o}},e._calculateHierarchicalSpringForces=function(){for(var t,e,i,s,o,n,r,a,h,d=this.edges,l=this.calculationNodes,c=this.calculationNodeIndices,p=0;pn;n++)t=e[i[n]],t.options.mass>0&&(this._getForceContribution(o.root.children.NW,t),this._getForceContribution(o.root.children.NE,t),this._getForceContribution(o.root.children.SW,t),this._getForceContribution(o.root.children.SE,t))}},e._getForceContribution=function(t,e){if(t.childrenCount>0){var i,s,o;if(i=t.centerOfMass.x-e.x,s=t.centerOfMass.y-e.y,o=Math.sqrt(i*i+s*s),o*t.calcSize>this.constants.physics.barnesHut.thetaInverted){0==o&&(o=.1*Math.random(),i=o);var n=this.constants.physics.barnesHut.gravitationalConstant*t.mass*e.options.mass/(o*o*o),r=i*n,a=s*n;e.fx+=r,e.fy+=a}else if(4==t.childrenCount)this._getForceContribution(t.children.NW,e),this._getForceContribution(t.children.NE,e),this._getForceContribution(t.children.SW,e),this._getForceContribution(t.children.SE,e);else if(t.children.data.id!=e.id){0==o&&(o=.5*Math.random(),i=o);var n=this.constants.physics.barnesHut.gravitationalConstant*t.mass*e.options.mass/(o*o*o),r=i*n,a=s*n;e.fx+=r,e.fy+=a}}},e._formBarnesHutTree=function(t,e){for(var i,s=e.length,o=Number.MAX_VALUE,n=Number.MAX_VALUE,r=-Number.MAX_VALUE,a=-Number.MAX_VALUE,h=0;s>h;h++){var d=t[e[h]].x,l=t[e[h]].y;t[e[h]].options.mass>0&&(o>d&&(o=d),d>r&&(r=d),n>l&&(n=l),l>a&&(a=l))}var c=Math.abs(r-o)-Math.abs(a-n);c>0?(n-=.5*c,a+=.5*c):(o+=.5*c,r-=.5*c);var p=1e-5,u=Math.max(p,Math.abs(r-o)),m=.5*u,f=.5*(o+r),g=.5*(n+a),v={root:{centerOfMass:{x:0,y:0},mass:0,range:{minX:f-m,maxX:f+m,minY:g-m,maxY:g+m},size:u,calcSize:1/u,children:{data:null},maxWidth:0,level:0,childrenCount:4}};for(this._splitBranch(v.root),h=0;s>h;h++)i=t[e[h]],i.options.mass>0&&this._placeInTree(v.root,i);this.barnesHutTree=v},e._updateBranchMass=function(t,e){var i=t.mass+e.options.mass,s=1/i;t.centerOfMass.x=t.centerOfMass.x*t.mass+e.x*e.options.mass,t.centerOfMass.x*=s,t.centerOfMass.y=t.centerOfMass.y*t.mass+e.y*e.options.mass,t.centerOfMass.y*=s,t.mass=i;var o=Math.max(Math.max(e.height,e.radius),e.width);t.maxWidth=t.maxWidthe.x?t.children.NW.range.maxY>e.y?this._placeInRegion(t,e,"NW"):this._placeInRegion(t,e,"SW"):t.children.NW.range.maxY>e.y?this._placeInRegion(t,e,"NE"):this._placeInRegion(t,e,"SE")},e._placeInRegion=function(t,e,i){switch(t.children[i].childrenCount){case 0:t.children[i].children.data=e,t.children[i].childrenCount=1,this._updateBranchMass(t.children[i],e);break;case 1:t.children[i].children.data.x==e.x&&t.children[i].children.data.y==e.y?(e.x+=Math.random(),e.y+=Math.random()):(this._splitBranch(t.children[i]),this._placeInTree(t.children[i],e));break;case 4:this._placeInTree(t.children[i],e)}},e._splitBranch=function(t){var e=null;1==t.childrenCount&&(e=t.children.data,t.mass=0,t.centerOfMass.x=0,t.centerOfMass.y=0),t.childrenCount=4,t.children.data=null,this._insertRegion(t,"NW"),this._insertRegion(t,"NE"),this._insertRegion(t,"SW"),this._insertRegion(t,"SE"),null!=e&&this._placeInTree(t,e)},e._insertRegion=function(t,e){var i,s,o,n,r=.5*t.size;switch(e){case"NW":i=t.range.minX,s=t.range.minX+r,o=t.range.minY,n=t.range.minY+r;break;case"NE":i=t.range.minX+r,s=t.range.maxX,o=t.range.minY,n=t.range.minY+r;break;case"SW":i=t.range.minX,s=t.range.minX+r,o=t.range.minY+r,n=t.range.maxY;break;case"SE":i=t.range.minX+r,s=t.range.maxX,o=t.range.minY+r,n=t.range.maxY}t.children[e]={centerOfMass:{x:0,y:0},mass:0,range:{minX:i,maxX:s,minY:o,maxY:n},size:.5*t.size,calcSize:2*t.calcSize,children:{data:null},maxWidth:0,level:t.level+1,childrenCount:0}},e._drawTree=function(t,e){void 0!==this.barnesHutTree&&(t.lineWidth=1,this._drawBranch(this.barnesHutTree.root,t,e))},e._drawBranch=function(t,e,i){void 0===i&&(i="#FF0000"),4==t.childrenCount&&(this._drawBranch(t.children.NW,e),this._drawBranch(t.children.NE,e),this._drawBranch(t.children.SE,e),this._drawBranch(t.children.SW,e)),e.strokeStyle=i,e.beginPath(),e.moveTo(t.range.minX,t.range.minY),e.lineTo(t.range.maxX,t.range.minY),e.stroke(),e.beginPath(),e.moveTo(t.range.maxX,t.range.minY),e.lineTo(t.range.maxX,t.range.maxY),e.stroke(),e.beginPath(),e.moveTo(t.range.maxX,t.range.maxY),e.lineTo(t.range.minX,t.range.maxY),e.stroke(),e.beginPath(),e.moveTo(t.range.minX,t.range.maxY),e.lineTo(t.range.minX,t.range.minY),e.stroke()}},function(t){t.exports=function(t){return t.webpackPolyfill||(t.deprecate=function(){},t.paths=[],t.children=[],t.webpackPolyfill=1),t}}])}); //# sourceMappingURL=vis.map diff --git a/lib/DataSet.js b/lib/DataSet.js index 975f40de..ccb2c2d6 100644 --- a/lib/DataSet.js +++ b/lib/DataSet.js @@ -651,12 +651,16 @@ DataSet.prototype.map = function (callback, options) { /** * Filter the fields of an item - * @param {Object} item + * @param {Object | null} item * @param {String[]} fields Field names - * @return {Object} filteredItem + * @return {Object | null} filteredItem or null if no item is provided * @private */ DataSet.prototype._filterFields = function (item, fields) { + if (!item) { // item is null + return item; + } + var filteredItem = {}; for (var field in item) { From 81dc7642fef9e9870bd5ac2ab35d6b7c20e94741 Mon Sep 17 00:00:00 2001 From: jos Date: Mon, 9 Feb 2015 15:05:52 +0100 Subject: [PATCH 7/7] Fixed #621: item ranges being displayed with a minimum of 10px (twice the padding). --- HISTORY.md | 11 +++++++++++ bower.json | 2 +- dist/vis.css | 8 +++----- dist/vis.js | 4 ++-- dist/vis.min.css | 2 +- dist/vis.min.js | 4 ++-- examples/timeline/18_range_overflow.html | 4 ++-- lib/timeline/component/css/item.css | 8 +++----- package.json | 2 +- 9 files changed, 26 insertions(+), 19 deletions(-) diff --git a/HISTORY.md b/HISTORY.md index 2bee89bc..9a3f7457 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -2,6 +2,17 @@ http://visjs.org +## not yet released, version 4.0.0-SNAPSHOT + +### Timeline + +- Fixed range items not being displayed smaller than 10 pixels (twice the + padding). In order to have overflowing text, one should now apply css style + `.vis.timeline .item.range { overflow: visible; }` instead of + `.vis.timeline .item.range .content { overflow: visible; }`. + See example 18_range_overflow.html. + + ## not yet released, version 3.9.2-SNAPSHOT ### Network diff --git a/bower.json b/bower.json index 1c1fd918..7d53e2ad 100644 --- a/bower.json +++ b/bower.json @@ -1,6 +1,6 @@ { "name": "vis", - "version": "3.9.2-SNAPSHOT", + "version": "4.0.0-SNAPSHOT", "main": ["dist/vis.min.js", "dist/vis.min.css"], "description": "A dynamic, browser-based visualization library.", "homepage": "http://visjs.org/", diff --git a/dist/vis.css b/dist/vis.css index 81c94194..a8d8510f 100644 --- a/dist/vis.css +++ b/dist/vis.css @@ -173,7 +173,7 @@ border-width: 1px; background-color: #D5DDF6; display: inline-block; - padding: 5px; + overflow: hidden; } .vis.timeline .item.selected { @@ -217,7 +217,6 @@ } .vis.timeline .item.background { - overflow: hidden; border: none; background-color: rgba(213, 221, 246, 0.4); box-sizing: border-box; @@ -229,13 +228,11 @@ position: relative; display: inline-block; max-width: 100%; - overflow: hidden; } .vis.timeline .item.background .content { position: absolute; display: inline-block; - overflow: hidden; max-width: 100%; margin: 5px; } @@ -250,7 +247,8 @@ .vis.timeline .item .content { white-space: nowrap; - overflow: hidden; + box-sizing: border-box; + padding: 5px; } .vis.timeline .item .delete { diff --git a/dist/vis.js b/dist/vis.js index 59f20156..9cba7e82 100644 --- a/dist/vis.js +++ b/dist/vis.js @@ -4,8 +4,8 @@ * * A dynamic, browser-based visualization library. * - * @version 3.9.2-SNAPSHOT - * @date 2015-02-05 + * @version 4.0.0-SNAPSHOT + * @date 2015-02-09 * * @license * Copyright (C) 2011-2014 Almende B.V, http://almende.com diff --git a/dist/vis.min.css b/dist/vis.min.css index a390c40d..2c5af6eb 100644 --- a/dist/vis.min.css +++ b/dist/vis.min.css @@ -1 +1 @@ -.vis .overlay{position:absolute;top:0;left:0;width:100%;height:100%;z-index:10}.vis-active{box-shadow:0 0 10px #86d5f8}.vis [class*=span]{min-height:0;width:auto}.vis.timeline.root{position:relative;border:1px solid #bfbfbf;overflow:hidden;padding:0;margin:0;box-sizing:border-box}.vis.timeline .vispanel{position:absolute;padding:0;margin:0;box-sizing:border-box}.vis.timeline .vispanel.bottom,.vis.timeline .vispanel.center,.vis.timeline .vispanel.left,.vis.timeline .vispanel.right,.vis.timeline .vispanel.top{border:1px #bfbfbf}.vis.timeline .vispanel.center,.vis.timeline .vispanel.left,.vis.timeline .vispanel.right{border-top-style:solid;border-bottom-style:solid;overflow:hidden}.vis.timeline .vispanel.bottom,.vis.timeline .vispanel.center,.vis.timeline .vispanel.top{border-left-style:solid;border-right-style:solid}.vis.timeline .background{overflow:hidden}.vis.timeline .vispanel>.content{position:relative}.vis.timeline .vispanel .shadow{position:absolute;width:100%;height:1px;box-shadow:0 0 10px rgba(0,0,0,.8)}.vis.timeline .vispanel .shadow.top{top:-1px;left:0}.vis.timeline .vispanel .shadow.bottom{bottom:-1px;left:0}.vis.timeline .labelset{position:relative;overflow:hidden;box-sizing:border-box}.vis.timeline .labelset .vlabel{position:relative;left:0;top:0;width:100%;color:#4d4d4d;box-sizing:border-box;border-bottom:1px solid #bfbfbf}.vis.timeline .labelset .vlabel:last-child{border-bottom:none}.vis.timeline .labelset .vlabel .inner{display:inline-block;padding:5px}.vis.timeline .labelset .vlabel .inner.hidden{padding:0}.vis.timeline .itemset{position:relative;padding:0;margin:0;box-sizing:border-box}.vis.timeline .itemset .background,.vis.timeline .itemset .foreground{position:absolute;width:100%;height:100%;overflow:visible}.vis.timeline .axis{position:absolute;width:100%;height:0;left:0;z-index:1}.vis.timeline .foreground .group{position:relative;box-sizing:border-box;border-bottom:1px solid #bfbfbf}.vis.timeline .foreground .group:last-child{border-bottom:none}.vis.timeline .item{position:absolute;color:#1A1A1A;border-color:#97B0F8;border-width:1px;background-color:#D5DDF6;display:inline-block;padding:5px}.vis.timeline .item.selected{border-color:#FFC200;background-color:#FFF785;z-index:2}.vis.timeline .editable .item.selected{cursor:move}.vis.timeline .item.point.selected{background-color:#FFF785}.vis.timeline .item.box{text-align:center;border-style:solid;border-radius:2px}.vis.timeline .item.point{background:0 0}.vis.timeline .item.dot{position:absolute;padding:0;border-width:4px;border-style:solid;border-radius:4px}.vis.timeline .item.range{border-style:solid;border-radius:2px;box-sizing:border-box}.vis.timeline .item.background{overflow:hidden;border:none;background-color:rgba(213,221,246,.4);box-sizing:border-box;padding:0;margin:0}.vis.timeline .item.range .content{position:relative;display:inline-block;max-width:100%;overflow:hidden}.vis.timeline .item.background .content{position:absolute;display:inline-block;overflow:hidden;max-width:100%;margin:5px}.vis.timeline .item.line{padding:0;position:absolute;width:0;border-left-width:1px;border-left-style:solid}.vis.timeline .item .content{white-space:nowrap;overflow:hidden}.vis.timeline .item .delete{background:url(img/timeline/delete.png) top center no-repeat;position:absolute;width:24px;height:24px;top:0;right:-24px;cursor:pointer}.vis.timeline .item.range .drag-left{position:absolute;width:24px;height:100%;top:0;left:-4px;cursor:w-resize}.vis.timeline .item.range .drag-right{position:absolute;width:24px;height:100%;top:0;right:-4px;cursor:e-resize}.vis.timeline .timeaxis{position:relative;overflow:hidden}.vis.timeline .timeaxis.foreground{top:0;left:0;width:100%}.vis.timeline .timeaxis.background{position:absolute;top:0;left:0;width:100%;height:100%}.vis.timeline .timeaxis .text{position:absolute;color:#4d4d4d;padding:3px;white-space:nowrap}.vis.timeline .timeaxis .text.measure{position:absolute;padding-left:0;padding-right:0;margin-left:0;margin-right:0;visibility:hidden}.vis.timeline .timeaxis .grid.vertical{position:absolute;border-left:1px solid}.vis.timeline .timeaxis .grid.minor{border-color:#e5e5e5}.vis.timeline .timeaxis .grid.major{border-color:#bfbfbf}.vis.timeline .currenttime{background-color:#FF7F6E;width:2px;z-index:1}.vis.timeline .customtime{background-color:#6E94FF;width:2px;cursor:move;z-index:1}.vis.timeline .vispanel.background.horizontal .grid.horizontal{position:absolute;width:100%;height:0;border-bottom:1px solid}.vis.timeline .vispanel.background.horizontal .grid.minor{border-color:#e5e5e5}.vis.timeline .vispanel.background.horizontal .grid.major{border-color:#bfbfbf}.vis.timeline .dataaxis .yAxis.major{width:100%;position:absolute;color:#4d4d4d;white-space:nowrap}.vis.timeline .dataaxis .yAxis.major.measure{padding:0;margin:0;border:0;visibility:hidden;width:auto}.vis.timeline .dataaxis .yAxis.minor{position:absolute;width:100%;color:#bebebe;white-space:nowrap}.vis.timeline .dataaxis .yAxis.minor.measure{padding:0;margin:0;border:0;visibility:hidden;width:auto}.vis.timeline .dataaxis .yAxis.title{position:absolute;color:#4d4d4d;white-space:nowrap;bottom:20px;text-align:center}.vis.timeline .dataaxis .yAxis.title.measure{padding:0;margin:0;visibility:hidden;width:auto}.vis.timeline .dataaxis .yAxis.title.left{bottom:0;-webkit-transform-origin:left top;-moz-transform-origin:left top;-ms-transform-origin:left top;-o-transform-origin:left top;transform-origin:left bottom;-webkit-transform:rotate(-90deg);-moz-transform:rotate(-90deg);-ms-transform:rotate(-90deg);-o-transform:rotate(-90deg);transform:rotate(-90deg)}.vis.timeline .dataaxis .yAxis.title.right{bottom:0;-webkit-transform-origin:right bottom;-moz-transform-origin:right bottom;-ms-transform-origin:right bottom;-o-transform-origin:right bottom;transform-origin:right bottom;-webkit-transform:rotate(90deg);-moz-transform:rotate(90deg);-ms-transform:rotate(90deg);-o-transform:rotate(90deg);transform:rotate(90deg)}.vis.timeline .legend{background-color:rgba(247,252,255,.65);padding:5px;border-color:#b3b3b3;border-style:solid;border-width:1px;box-shadow:2px 2px 10px rgba(154,154,154,.55)}.vis.timeline .legendText{white-space:nowrap;display:inline-block}.vis.timeline .graphGroup0{fill:#4f81bd;fill-opacity:0;stroke-width:2px;stroke:#4f81bd}.vis.timeline .graphGroup1{fill:#f79646;fill-opacity:0;stroke-width:2px;stroke:#f79646}.vis.timeline .graphGroup2{fill:#8c51cf;fill-opacity:0;stroke-width:2px;stroke:#8c51cf}.vis.timeline .graphGroup3{fill:#75c841;fill-opacity:0;stroke-width:2px;stroke:#75c841}.vis.timeline .graphGroup4{fill:#ff0100;fill-opacity:0;stroke-width:2px;stroke:#ff0100}.vis.timeline .graphGroup5{fill:#37d8e6;fill-opacity:0;stroke-width:2px;stroke:#37d8e6}.vis.timeline .graphGroup6{fill:#042662;fill-opacity:0;stroke-width:2px;stroke:#042662}.vis.timeline .graphGroup7{fill:#00ff26;fill-opacity:0;stroke-width:2px;stroke:#00ff26}.vis.timeline .graphGroup8{fill:#f0f;fill-opacity:0;stroke-width:2px;stroke:#f0f}.vis.timeline .graphGroup9{fill:#8f3938;fill-opacity:0;stroke-width:2px;stroke:#8f3938}.vis.timeline .fill{fill-opacity:.1;stroke:none}.vis.timeline .bar{fill-opacity:.5;stroke-width:1px}.vis.timeline .point{stroke-width:2px;fill-opacity:1}.vis.timeline .legendBackground{stroke-width:1px;fill-opacity:.9;fill:#fff;stroke:#c2c2c2}.vis.timeline .outline{stroke-width:1px;fill-opacity:1;fill:#fff;stroke:#e5e5e5}.vis.timeline .iconFill{fill-opacity:.3;stroke:none}div.network-manipulationDiv{border-width:0;border-bottom:1px;border-style:solid;border-color:#d6d9d8;background:#fff;background:-moz-linear-gradient(top,#fff 0,#fcfcfc 48%,#fafafa 50%,#fcfcfc 100%);background:-webkit-gradient(linear,left top,left bottom,color-stop(0,#fff),color-stop(48%,#fcfcfc),color-stop(50%,#fafafa),color-stop(100%,#fcfcfc));background:-webkit-linear-gradient(top,#fff 0,#fcfcfc 48%,#fafafa 50%,#fcfcfc 100%);background:-o-linear-gradient(top,#fff 0,#fcfcfc 48%,#fafafa 50%,#fcfcfc 100%);background:-ms-linear-gradient(top,#fff 0,#fcfcfc 48%,#fafafa 50%,#fcfcfc 100%);background:linear-gradient(to bottom,#fff 0,#fcfcfc 48%,#fafafa 50%,#fcfcfc 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffff', endColorstr='#fcfcfc', GradientType=0);position:absolute;left:0;top:0;width:100%;height:30px}div.network-manipulation-editMode{position:absolute;left:0;top:0;height:30px;margin-top:20px}div.network-manipulation-closeDiv{position:absolute;right:0;top:0;width:30px;height:30px;background-position:20px 3px;background-repeat:no-repeat;background-image:url(img/network/cross.png);cursor:pointer;-webkit-touch-callout:none;-webkit-user-select:none;-khtml-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}div.network-manipulation-closeDiv:hover{opacity:.6}span.network-manipulationUI{font-family:verdana;font-size:12px;-moz-border-radius:15px;border-radius:15px;display:inline-block;background-position:0 0;background-repeat:no-repeat;height:24px;margin:-14px 0 0 10px;vertical-align:middle;cursor:pointer;padding:0 8px;-webkit-touch-callout:none;-webkit-user-select:none;-khtml-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}span.network-manipulationUI:hover{box-shadow:1px 1px 8px rgba(0,0,0,.2)}span.network-manipulationUI:active{box-shadow:1px 1px 8px rgba(0,0,0,.5)}span.network-manipulationUI.back{background-image:url(img/network/backIcon.png)}span.network-manipulationUI.none:hover{box-shadow:1px 1px 8px transparent;cursor:default}span.network-manipulationUI.none:active{box-shadow:1px 1px 8px transparent}span.network-manipulationUI.none{padding:0}span.network-manipulationUI.notification{margin:2px;font-weight:700}span.network-manipulationUI.add{background-image:url(img/network/addNodeIcon.png)}span.network-manipulationUI.edit{background-image:url(img/network/editIcon.png)}span.network-manipulationUI.edit.editmode{background-color:#fcfcfc;border-style:solid;border-width:1px;border-color:#ccc}span.network-manipulationUI.connect{background-image:url(img/network/connectIcon.png)}span.network-manipulationUI.delete{background-image:url(img/network/deleteIcon.png)}span.network-manipulationLabel{margin:0 0 0 23px;line-height:25px}div.network-seperatorLine{display:inline-block;width:1px;height:20px;background-color:#bdbdbd;margin:5px 7px 0 15px}div.network-navigation_wrapper{position:absolute;left:0;top:0;width:100%;height:100%}div.network-navigation{width:34px;height:34px;-moz-border-radius:17px;border-radius:17px;position:absolute;display:inline-block;background-position:2px 2px;background-repeat:no-repeat;cursor:pointer;-webkit-touch-callout:none;-webkit-user-select:none;-khtml-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}div.network-navigation:hover{box-shadow:0 0 3px 3px rgba(56,207,21,.3)}div.network-navigation:active{box-shadow:0 0 1px 3px rgba(56,207,21,.95)}div.network-navigation.up{background-image:url(img/network/upArrow.png);bottom:50px;left:55px}div.network-navigation.down{background-image:url(img/network/downArrow.png);bottom:10px;left:55px}div.network-navigation.left{background-image:url(img/network/leftArrow.png);bottom:10px;left:15px}div.network-navigation.right{background-image:url(img/network/rightArrow.png);bottom:10px;left:95px}div.network-navigation.zoomIn{background-image:url(img/network/plus.png);bottom:10px;right:15px}div.network-navigation.zoomOut{background-image:url(img/network/minus.png);bottom:10px;right:55px}div.network-navigation.zoomExtends{background-image:url(img/network/zoomExtends.png);bottom:50px;right:15px} \ No newline at end of file +.vis .overlay{position:absolute;top:0;left:0;width:100%;height:100%;z-index:10}.vis-active{box-shadow:0 0 10px #86d5f8}.vis [class*=span]{min-height:0;width:auto}.vis.timeline.root{position:relative;border:1px solid #bfbfbf;overflow:hidden;padding:0;margin:0;box-sizing:border-box}.vis.timeline .vispanel{position:absolute;padding:0;margin:0;box-sizing:border-box}.vis.timeline .vispanel.bottom,.vis.timeline .vispanel.center,.vis.timeline .vispanel.left,.vis.timeline .vispanel.right,.vis.timeline .vispanel.top{border:1px #bfbfbf}.vis.timeline .vispanel.center,.vis.timeline .vispanel.left,.vis.timeline .vispanel.right{border-top-style:solid;border-bottom-style:solid;overflow:hidden}.vis.timeline .vispanel.bottom,.vis.timeline .vispanel.center,.vis.timeline .vispanel.top{border-left-style:solid;border-right-style:solid}.vis.timeline .background{overflow:hidden}.vis.timeline .vispanel>.content{position:relative}.vis.timeline .vispanel .shadow{position:absolute;width:100%;height:1px;box-shadow:0 0 10px rgba(0,0,0,.8)}.vis.timeline .vispanel .shadow.top{top:-1px;left:0}.vis.timeline .vispanel .shadow.bottom{bottom:-1px;left:0}.vis.timeline .labelset{position:relative;overflow:hidden;box-sizing:border-box}.vis.timeline .labelset .vlabel{position:relative;left:0;top:0;width:100%;color:#4d4d4d;box-sizing:border-box;border-bottom:1px solid #bfbfbf}.vis.timeline .labelset .vlabel:last-child{border-bottom:none}.vis.timeline .labelset .vlabel .inner{display:inline-block;padding:5px}.vis.timeline .labelset .vlabel .inner.hidden{padding:0}.vis.timeline .itemset{position:relative;padding:0;margin:0;box-sizing:border-box}.vis.timeline .itemset .background,.vis.timeline .itemset .foreground{position:absolute;width:100%;height:100%;overflow:visible}.vis.timeline .axis{position:absolute;width:100%;height:0;left:0;z-index:1}.vis.timeline .foreground .group{position:relative;box-sizing:border-box;border-bottom:1px solid #bfbfbf}.vis.timeline .foreground .group:last-child{border-bottom:none}.vis.timeline .item{position:absolute;color:#1A1A1A;border-color:#97B0F8;border-width:1px;background-color:#D5DDF6;display:inline-block;overflow:hidden}.vis.timeline .item.selected{border-color:#FFC200;background-color:#FFF785;z-index:2}.vis.timeline .editable .item.selected{cursor:move}.vis.timeline .item.point.selected{background-color:#FFF785}.vis.timeline .item.box{text-align:center;border-style:solid;border-radius:2px}.vis.timeline .item.point{background:0 0}.vis.timeline .item.dot{position:absolute;padding:0;border-width:4px;border-style:solid;border-radius:4px}.vis.timeline .item.range{border-style:solid;border-radius:2px;box-sizing:border-box}.vis.timeline .item.background{border:none;background-color:rgba(213,221,246,.4);box-sizing:border-box;padding:0;margin:0}.vis.timeline .item.range .content{position:relative;display:inline-block;max-width:100%}.vis.timeline .item.background .content{position:absolute;display:inline-block;max-width:100%;margin:5px}.vis.timeline .item.line{padding:0;position:absolute;width:0;border-left-width:1px;border-left-style:solid}.vis.timeline .item .content{white-space:nowrap;box-sizing:border-box;padding:5px}.vis.timeline .item .delete{background:url(img/timeline/delete.png) top center no-repeat;position:absolute;width:24px;height:24px;top:0;right:-24px;cursor:pointer}.vis.timeline .item.range .drag-left{position:absolute;width:24px;height:100%;top:0;left:-4px;cursor:w-resize}.vis.timeline .item.range .drag-right{position:absolute;width:24px;height:100%;top:0;right:-4px;cursor:e-resize}.vis.timeline .timeaxis{position:relative;overflow:hidden}.vis.timeline .timeaxis.foreground{top:0;left:0;width:100%}.vis.timeline .timeaxis.background{position:absolute;top:0;left:0;width:100%;height:100%}.vis.timeline .timeaxis .text{position:absolute;color:#4d4d4d;padding:3px;white-space:nowrap}.vis.timeline .timeaxis .text.measure{position:absolute;padding-left:0;padding-right:0;margin-left:0;margin-right:0;visibility:hidden}.vis.timeline .timeaxis .grid.vertical{position:absolute;border-left:1px solid}.vis.timeline .timeaxis .grid.minor{border-color:#e5e5e5}.vis.timeline .timeaxis .grid.major{border-color:#bfbfbf}.vis.timeline .currenttime{background-color:#FF7F6E;width:2px;z-index:1}.vis.timeline .customtime{background-color:#6E94FF;width:2px;cursor:move;z-index:1}.vis.timeline .vispanel.background.horizontal .grid.horizontal{position:absolute;width:100%;height:0;border-bottom:1px solid}.vis.timeline .vispanel.background.horizontal .grid.minor{border-color:#e5e5e5}.vis.timeline .vispanel.background.horizontal .grid.major{border-color:#bfbfbf}.vis.timeline .dataaxis .yAxis.major{width:100%;position:absolute;color:#4d4d4d;white-space:nowrap}.vis.timeline .dataaxis .yAxis.major.measure{padding:0;margin:0;border:0;visibility:hidden;width:auto}.vis.timeline .dataaxis .yAxis.minor{position:absolute;width:100%;color:#bebebe;white-space:nowrap}.vis.timeline .dataaxis .yAxis.minor.measure{padding:0;margin:0;border:0;visibility:hidden;width:auto}.vis.timeline .dataaxis .yAxis.title{position:absolute;color:#4d4d4d;white-space:nowrap;bottom:20px;text-align:center}.vis.timeline .dataaxis .yAxis.title.measure{padding:0;margin:0;visibility:hidden;width:auto}.vis.timeline .dataaxis .yAxis.title.left{bottom:0;-webkit-transform-origin:left top;-moz-transform-origin:left top;-ms-transform-origin:left top;-o-transform-origin:left top;transform-origin:left bottom;-webkit-transform:rotate(-90deg);-moz-transform:rotate(-90deg);-ms-transform:rotate(-90deg);-o-transform:rotate(-90deg);transform:rotate(-90deg)}.vis.timeline .dataaxis .yAxis.title.right{bottom:0;-webkit-transform-origin:right bottom;-moz-transform-origin:right bottom;-ms-transform-origin:right bottom;-o-transform-origin:right bottom;transform-origin:right bottom;-webkit-transform:rotate(90deg);-moz-transform:rotate(90deg);-ms-transform:rotate(90deg);-o-transform:rotate(90deg);transform:rotate(90deg)}.vis.timeline .legend{background-color:rgba(247,252,255,.65);padding:5px;border-color:#b3b3b3;border-style:solid;border-width:1px;box-shadow:2px 2px 10px rgba(154,154,154,.55)}.vis.timeline .legendText{white-space:nowrap;display:inline-block}.vis.timeline .graphGroup0{fill:#4f81bd;fill-opacity:0;stroke-width:2px;stroke:#4f81bd}.vis.timeline .graphGroup1{fill:#f79646;fill-opacity:0;stroke-width:2px;stroke:#f79646}.vis.timeline .graphGroup2{fill:#8c51cf;fill-opacity:0;stroke-width:2px;stroke:#8c51cf}.vis.timeline .graphGroup3{fill:#75c841;fill-opacity:0;stroke-width:2px;stroke:#75c841}.vis.timeline .graphGroup4{fill:#ff0100;fill-opacity:0;stroke-width:2px;stroke:#ff0100}.vis.timeline .graphGroup5{fill:#37d8e6;fill-opacity:0;stroke-width:2px;stroke:#37d8e6}.vis.timeline .graphGroup6{fill:#042662;fill-opacity:0;stroke-width:2px;stroke:#042662}.vis.timeline .graphGroup7{fill:#00ff26;fill-opacity:0;stroke-width:2px;stroke:#00ff26}.vis.timeline .graphGroup8{fill:#f0f;fill-opacity:0;stroke-width:2px;stroke:#f0f}.vis.timeline .graphGroup9{fill:#8f3938;fill-opacity:0;stroke-width:2px;stroke:#8f3938}.vis.timeline .fill{fill-opacity:.1;stroke:none}.vis.timeline .bar{fill-opacity:.5;stroke-width:1px}.vis.timeline .point{stroke-width:2px;fill-opacity:1}.vis.timeline .legendBackground{stroke-width:1px;fill-opacity:.9;fill:#fff;stroke:#c2c2c2}.vis.timeline .outline{stroke-width:1px;fill-opacity:1;fill:#fff;stroke:#e5e5e5}.vis.timeline .iconFill{fill-opacity:.3;stroke:none}div.network-manipulationDiv{border-width:0;border-bottom:1px;border-style:solid;border-color:#d6d9d8;background:#fff;background:-moz-linear-gradient(top,#fff 0,#fcfcfc 48%,#fafafa 50%,#fcfcfc 100%);background:-webkit-gradient(linear,left top,left bottom,color-stop(0,#fff),color-stop(48%,#fcfcfc),color-stop(50%,#fafafa),color-stop(100%,#fcfcfc));background:-webkit-linear-gradient(top,#fff 0,#fcfcfc 48%,#fafafa 50%,#fcfcfc 100%);background:-o-linear-gradient(top,#fff 0,#fcfcfc 48%,#fafafa 50%,#fcfcfc 100%);background:-ms-linear-gradient(top,#fff 0,#fcfcfc 48%,#fafafa 50%,#fcfcfc 100%);background:linear-gradient(to bottom,#fff 0,#fcfcfc 48%,#fafafa 50%,#fcfcfc 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffff', endColorstr='#fcfcfc', GradientType=0);position:absolute;left:0;top:0;width:100%;height:30px}div.network-manipulation-editMode{position:absolute;left:0;top:0;height:30px;margin-top:20px}div.network-manipulation-closeDiv{position:absolute;right:0;top:0;width:30px;height:30px;background-position:20px 3px;background-repeat:no-repeat;background-image:url(img/network/cross.png);cursor:pointer;-webkit-touch-callout:none;-webkit-user-select:none;-khtml-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}div.network-manipulation-closeDiv:hover{opacity:.6}span.network-manipulationUI{font-family:verdana;font-size:12px;-moz-border-radius:15px;border-radius:15px;display:inline-block;background-position:0 0;background-repeat:no-repeat;height:24px;margin:-14px 0 0 10px;vertical-align:middle;cursor:pointer;padding:0 8px;-webkit-touch-callout:none;-webkit-user-select:none;-khtml-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}span.network-manipulationUI:hover{box-shadow:1px 1px 8px rgba(0,0,0,.2)}span.network-manipulationUI:active{box-shadow:1px 1px 8px rgba(0,0,0,.5)}span.network-manipulationUI.back{background-image:url(img/network/backIcon.png)}span.network-manipulationUI.none:hover{box-shadow:1px 1px 8px transparent;cursor:default}span.network-manipulationUI.none:active{box-shadow:1px 1px 8px transparent}span.network-manipulationUI.none{padding:0}span.network-manipulationUI.notification{margin:2px;font-weight:700}span.network-manipulationUI.add{background-image:url(img/network/addNodeIcon.png)}span.network-manipulationUI.edit{background-image:url(img/network/editIcon.png)}span.network-manipulationUI.edit.editmode{background-color:#fcfcfc;border-style:solid;border-width:1px;border-color:#ccc}span.network-manipulationUI.connect{background-image:url(img/network/connectIcon.png)}span.network-manipulationUI.delete{background-image:url(img/network/deleteIcon.png)}span.network-manipulationLabel{margin:0 0 0 23px;line-height:25px}div.network-seperatorLine{display:inline-block;width:1px;height:20px;background-color:#bdbdbd;margin:5px 7px 0 15px}div.network-navigation_wrapper{position:absolute;left:0;top:0;width:100%;height:100%}div.network-navigation{width:34px;height:34px;-moz-border-radius:17px;border-radius:17px;position:absolute;display:inline-block;background-position:2px 2px;background-repeat:no-repeat;cursor:pointer;-webkit-touch-callout:none;-webkit-user-select:none;-khtml-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}div.network-navigation:hover{box-shadow:0 0 3px 3px rgba(56,207,21,.3)}div.network-navigation:active{box-shadow:0 0 1px 3px rgba(56,207,21,.95)}div.network-navigation.up{background-image:url(img/network/upArrow.png);bottom:50px;left:55px}div.network-navigation.down{background-image:url(img/network/downArrow.png);bottom:10px;left:55px}div.network-navigation.left{background-image:url(img/network/leftArrow.png);bottom:10px;left:15px}div.network-navigation.right{background-image:url(img/network/rightArrow.png);bottom:10px;left:95px}div.network-navigation.zoomIn{background-image:url(img/network/plus.png);bottom:10px;right:15px}div.network-navigation.zoomOut{background-image:url(img/network/minus.png);bottom:10px;right:55px}div.network-navigation.zoomExtends{background-image:url(img/network/zoomExtends.png);bottom:50px;right:15px} \ No newline at end of file diff --git a/dist/vis.min.js b/dist/vis.min.js index 9f87aad1..641fe98b 100644 --- a/dist/vis.min.js +++ b/dist/vis.min.js @@ -4,8 +4,8 @@ * * A dynamic, browser-based visualization library. * - * @version 3.9.2-SNAPSHOT - * @date 2015-02-05 + * @version 4.0.0-SNAPSHOT + * @date 2015-02-09 * * @license * Copyright (C) 2011-2014 Almende B.V, http://almende.com diff --git a/examples/timeline/18_range_overflow.html b/examples/timeline/18_range_overflow.html index 8f9f7506..d222fa75 100644 --- a/examples/timeline/18_range_overflow.html +++ b/examples/timeline/18_range_overflow.html @@ -11,7 +11,7 @@ font-family: sans-serif; } - .vis.timeline .item.range .content { + .vis.timeline .item.range { overflow: visible; } @@ -22,7 +22,7 @@ In case of ranges being spread over a wide range of time, it can be interesting to have the text contents of the ranges overflow the box. This can be achieved by changing the overflow property of the contents to visible with css:

-.vis.timeline .item.range .content {
+.vis.timeline .item.range {
   overflow: visible;
 }
 
diff --git a/lib/timeline/component/css/item.css b/lib/timeline/component/css/item.css index bdc67340..8b766ab5 100644 --- a/lib/timeline/component/css/item.css +++ b/lib/timeline/component/css/item.css @@ -6,7 +6,7 @@ border-width: 1px; background-color: #D5DDF6; display: inline-block; - padding: 5px; + overflow: hidden; } .vis.timeline .item.selected { @@ -50,7 +50,6 @@ } .vis.timeline .item.background { - overflow: hidden; border: none; background-color: rgba(213, 221, 246, 0.4); box-sizing: border-box; @@ -62,13 +61,11 @@ position: relative; display: inline-block; max-width: 100%; - overflow: hidden; } .vis.timeline .item.background .content { position: absolute; display: inline-block; - overflow: hidden; max-width: 100%; margin: 5px; } @@ -83,7 +80,8 @@ .vis.timeline .item .content { white-space: nowrap; - overflow: hidden; + box-sizing: border-box; + padding: 5px; } .vis.timeline .item .delete { diff --git a/package.json b/package.json index f8743277..3edd4105 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "vis", - "version": "3.9.2-SNAPSHOT", + "version": "4.0.0-SNAPSHOT", "description": "A dynamic, browser-based visualization library.", "homepage": "http://visjs.org/", "repository": {