diff --git a/.gitignore b/.gitignore
index 485dee64..ce1e679d 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1 +1,3 @@
.idea
+node_modules
+.settings
\ No newline at end of file
diff --git a/dist/vis.css b/dist/vis.css
index 1f2c2de7..ff815c72 100644
--- a/dist/vis.css
+++ b/dist/vis.css
@@ -2,34 +2,76 @@
}
-.vis.timeline.rootpanel {
+.vis.timeline.root {
position: relative;
+ border: 1px solid #bfbfbf;
+
overflow: hidden;
+ padding: 0;
+ margin: 0;
- border: 1px solid #bfbfbf;
box-sizing: border-box;
-
- /* FIXME: there is an issue with the height of the items when panel height is animated
- -webkit-transition: height 4s ease-in-out;
- transition: height 4s ease-in-out;
- /**/
}
-.vis.timeline .vpanel {
+.vis.timeline .vispanel {
position: absolute;
- overflow: hidden;
+
+ padding: 0;
+ margin: 0;
box-sizing: border-box;
}
-.vis.timeline .vpanel.side {
- border-right: 1px solid #bfbfbf;
+.vis.timeline .vispanel.center,
+.vis.timeline .vispanel.left,
+.vis.timeline .vispanel.right,
+.vis.timeline .vispanel.top,
+.vis.timeline .vispanel.bottom {
+ 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.center,
+.vis.timeline .vispanel.top,
+.vis.timeline .vispanel.bottom {
+ 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,0.8);
+ /* TODO: find a nice way to ensure shadows are drawn on top of items
+ z-index: 1;
+ */
}
-.vis.timeline .vpanel.side.hidden {
- display: none;
+.vis.timeline .vispanel .shadow.top {
+ top: -1px;
+ left: 0;
}
+.vis.timeline .vispanel .shadow.bottom {
+ bottom: -1px;
+ left: 0;
+}
.vis.timeline .labelset {
position: relative;
@@ -50,14 +92,12 @@
box-sizing: border-box;
}
-.vis.timeline.top .labelset .vlabel {
- border-top: 1px solid #bfbfbf;
- border-bottom: none;
+.vis.timeline .labelset .vlabel {
+ border-bottom: 1px solid #bfbfbf;
}
-.vis.timeline.bottom .labelset .vlabel {
- border-top: none;
- border-bottom: 1px solid #bfbfbf;
+.vis.timeline .labelset .vlabel:last-child {
+ border-bottom: none;
}
.vis.timeline .labelset .vlabel .inner {
@@ -65,6 +105,10 @@
padding: 5px;
}
+.vis.timeline .labelset .vlabel .inner.hidden {
+ padding: 0;
+}
+
.vis.timeline .itemset {
position: relative;
@@ -72,38 +116,33 @@
margin: 0;
box-sizing: border-box;
-
- /* FIXME: get transition working for rootpanel and itemset
- -webkit-transition: height 4s ease-in-out;
- transition: height 4s ease-in-out;
- /**/
}
-.vis.timeline .background {
-}
-
-.vis.timeline .foreground {
+.vis.timeline .itemset .background,
+.vis.timeline .itemset .foreground {
+ position: absolute;
+ width: 100%;
+ height: 100%;
}
.vis.timeline .axis {
- overflow: visible;
+ position: absolute;
+ width: 100%;
+ height: 0;
+ left: 1px;
+ z-index: 1;
}
-.vis.timeline .group {
+.vis.timeline .foreground .group {
position: relative;
box-sizing: border-box;
+ border-bottom: 1px solid #bfbfbf;
}
-.vis.timeline.top .group {
- border-top: 1px solid #bfbfbf;
+.vis.timeline .foreground .group:last-child {
border-bottom: none;
}
-.vis.timeline.bottom .group {
- border-top: none;
- border-bottom: 1px solid #bfbfbf;
-}
-
.vis.timeline .item {
position: absolute;
@@ -113,11 +152,6 @@
background-color: #D5DDF6;
display: inline-block;
padding: 5px;
-
- /* TODO: enable css transitions
- -webkit-transition: top .4s ease-in-out, bottom .4s ease-in-out;
- transition: top .4s ease-in-out, bottom .4s ease-in-out;
- /**/
}
.vis.timeline .item.selected {
@@ -126,7 +160,7 @@
z-index: 999;
}
-.vis.timeline.editable .item.selected {
+.vis.timeline .editable .item.selected {
cursor: move;
}
@@ -176,11 +210,6 @@
width: 0;
border-left-width: 1px;
border-left-style: solid;
-
- /* TODO: enable css transitions
- -webkit-transition: height .4s ease-in-out, top .4s ease-in-out;
- transition: height .4s ease-in-out, top .4s ease-in-out;
- /**/
}
.vis.timeline .item .content {
@@ -223,7 +252,22 @@
}
.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 {
@@ -248,14 +292,6 @@
border-right: 1px solid;
}
-.vis.timeline .timeaxis .grid.horizontal {
- position: absolute;
- left: 0;
- width: 100%;
- height: 0;
- border-bottom: 1px solid;
-}
-
.vis.timeline .timeaxis .grid.minor {
border-color: #e5e5e5;
}
@@ -267,14 +303,47 @@
.vis.timeline .currenttime {
background-color: #FF7F6E;
width: 2px;
- z-index: 9;
+ z-index: 1;
}
.vis.timeline .customtime {
background-color: #6E94FF;
width: 2px;
cursor: move;
- z-index: 9;
+ z-index: 1;
+}
+.vis.timeline.root {
+ /*
+ -webkit-transition: height .4s ease-in-out;
+ transition: height .4s ease-in-out;
+ */
+}
+
+.vis.timeline .vispanel {
+ /*
+ -webkit-transition: height .4s ease-in-out, top .4s ease-in-out;
+ transition: height .4s ease-in-out, top .4s ease-in-out;
+ */
+}
+
+.vis.timeline .axis {
+ /*
+ -webkit-transition: top .4s ease-in-out;
+ transition: top .4s ease-in-out;
+ */
+}
+
+/* TODO: get animation working nicely
+
+.vis.timeline .item {
+ -webkit-transition: top .4s ease-in-out;
+ transition: top .4s ease-in-out;
+}
+
+.vis.timeline .item.line {
+ -webkit-transition: height .4s ease-in-out, top .4s ease-in-out;
+ transition: height .4s ease-in-out, top .4s ease-in-out;
}
+/**/
div.graph-manipulationDiv {
border-width:0px;
border-bottom: 1px;
diff --git a/dist/vis.js b/dist/vis.js
index 0ac9259e..71d48208 100644
--- a/dist/vis.js
+++ b/dist/vis.js
@@ -4,8 +4,8 @@
*
* A dynamic, browser-based visualization library.
*
- * @version 1.1.0
- * @date 2014-06-06
+ * @version 2.0.0
+ * @date 2014-06-19
*
* @license
* Copyright (C) 2011-2014 Almende B.V, http://almende.com
@@ -318,7 +318,7 @@ var util = {};
* @param {*} object
* @return {Boolean} isNumber
*/
-util.isNumber = function isNumber(object) {
+util.isNumber = function(object) {
return (object instanceof Number || typeof object == 'number');
};
@@ -327,7 +327,7 @@ util.isNumber = function isNumber(object) {
* @param {*} object
* @return {Boolean} isString
*/
-util.isString = function isString(object) {
+util.isString = function(object) {
return (object instanceof String || typeof object == 'string');
};
@@ -336,7 +336,7 @@ util.isString = function isString(object) {
* @param {Date | String} object
* @return {Boolean} isDate
*/
-util.isDate = function isDate(object) {
+util.isDate = function(object) {
if (object instanceof Date) {
return true;
}
@@ -359,7 +359,7 @@ util.isDate = function isDate(object) {
* @param {*} object
* @return {Boolean} isDataTable
*/
-util.isDataTable = function isDataTable(object) {
+util.isDataTable = function(object) {
return (typeof (google) !== 'undefined') &&
(google.visualization) &&
(google.visualization.DataTable) &&
@@ -371,7 +371,7 @@ util.isDataTable = function isDataTable(object) {
* source: http://stackoverflow.com/a/105074/1262753
* @return {String} uuid
*/
-util.randomUUID = function randomUUID () {
+util.randomUUID = function() {
var S4 = function () {
return Math.floor(
Math.random() * 0x10000 /* 65536 */
@@ -398,7 +398,34 @@ util.extend = function (a, b) {
for (var i = 1, len = arguments.length; i < len; i++) {
var other = arguments[i];
for (var prop in other) {
- if (other.hasOwnProperty(prop) && other[prop] !== undefined) {
+ if (other.hasOwnProperty(prop)) {
+ a[prop] = other[prop];
+ }
+ }
+ }
+
+ return a;
+};
+
+/**
+ * Extend object a with selected properties of object b or a series of objects
+ * Only properties with defined values are copied
+ * @param {Array.} props
+ * @param {Object} a
+ * @param {... Object} b
+ * @return {Object} a
+ */
+util.selectiveExtend = function (props, a, b) {
+ if (!Array.isArray(props)) {
+ throw new Error('Array with property names expected as first argument');
+ }
+
+ for (var i = 1, len = arguments.length; i < len; i++) {
+ var other = arguments[i];
+
+ for (var p = 0, pp = props.length; p < pp; p++) {
+ var prop = props[p];
+ if (other.hasOwnProperty(prop)) {
a[prop] = other[prop];
}
}
@@ -413,7 +440,7 @@ util.extend = function (a, b) {
* @param {Object} b
* @returns {Object}
*/
-util.deepExtend = function deepExtend (a, b) {
+util.deepExtend = function(a, b) {
// TODO: add support for Arrays to deepExtend
if (Array.isArray(b)) {
throw new TypeError('Arrays are not supported by deepExtend');
@@ -426,7 +453,7 @@ util.deepExtend = function deepExtend (a, b) {
a[prop] = {};
}
if (a[prop].constructor === Object) {
- deepExtend(a[prop], b[prop]);
+ util.deepExtend(a[prop], b[prop]);
}
else {
a[prop] = b[prop];
@@ -467,7 +494,7 @@ util.equalArray = function (a, b) {
* @return {*} object
* @throws Error
*/
-util.convert = function convert(object, type) {
+util.convert = function(object, type) {
var match;
if (object === undefined) {
@@ -602,8 +629,7 @@ util.convert = function convert(object, type) {
}
default:
- throw new Error('Cannot convert object of type ' + util.getType(object) +
- ' to type "' + type + '"');
+ throw new Error('Unknown type "' + type + '"');
}
};
@@ -617,7 +643,7 @@ var ASPDateRegex = /^\/?Date\((\-?\d+)/i;
* @param {*} object
* @return {String} type
*/
-util.getType = function getType(object) {
+util.getType = function(object) {
var type = typeof object;
if (type == 'object') {
@@ -660,7 +686,7 @@ util.getType = function getType(object) {
* @return {number} left The absolute left position of this element
* in the browser page.
*/
-util.getAbsoluteLeft = function getAbsoluteLeft (elem) {
+util.getAbsoluteLeft = function(elem) {
var doc = document.documentElement;
var body = document.body;
@@ -680,7 +706,7 @@ util.getAbsoluteLeft = function getAbsoluteLeft (elem) {
* @return {number} top The absolute top position of this element
* in the browser page.
*/
-util.getAbsoluteTop = function getAbsoluteTop (elem) {
+util.getAbsoluteTop = function(elem) {
var doc = document.documentElement;
var body = document.body;
@@ -699,7 +725,7 @@ util.getAbsoluteTop = function getAbsoluteTop (elem) {
* @param {Event} event
* @return {Number} pageY
*/
-util.getPageY = function getPageY (event) {
+util.getPageY = function(event) {
if ('pageY' in event) {
return event.pageY;
}
@@ -725,7 +751,7 @@ util.getPageY = function getPageY (event) {
* @param {Event} event
* @return {Number} pageX
*/
-util.getPageX = function getPageX (event) {
+util.getPageX = function(event) {
if ('pageY' in event) {
return event.pageX;
}
@@ -751,7 +777,7 @@ util.getPageX = function getPageX (event) {
* @param {Element} elem
* @param {String} className
*/
-util.addClassName = function addClassName(elem, className) {
+util.addClassName = function(elem, className) {
var classes = elem.className.split(' ');
if (classes.indexOf(className) == -1) {
classes.push(className); // add the class to the array
@@ -764,7 +790,7 @@ util.addClassName = function addClassName(elem, className) {
* @param {Element} elem
* @param {String} className
*/
-util.removeClassName = function removeClassname(elem, className) {
+util.removeClassName = function(elem, className) {
var classes = elem.className.split(' ');
var index = classes.indexOf(className);
if (index != -1) {
@@ -782,7 +808,7 @@ util.removeClassName = function removeClassname(elem, className) {
* the object or array with three parameters:
* callback(value, index, object)
*/
-util.forEach = function forEach (object, callback) {
+util.forEach = function(object, callback) {
var i,
len;
if (object instanceof Array) {
@@ -807,7 +833,7 @@ util.forEach = function forEach (object, callback) {
* @param {Object} object
* @param {Array} array
*/
-util.toArray = function toArray(object) {
+util.toArray = function(object) {
var array = [];
for (var prop in object) {
@@ -824,7 +850,7 @@ util.toArray = function toArray(object) {
* @param {*} value
* @return {Boolean} changed
*/
-util.updateProperty = function updateProperty (object, key, value) {
+util.updateProperty = function(object, key, value) {
if (object[key] !== value) {
object[key] = value;
return true;
@@ -842,7 +868,7 @@ util.updateProperty = function updateProperty (object, key, value) {
* @param {function} listener The callback function to be executed
* @param {boolean} [useCapture]
*/
-util.addEventListener = function addEventListener(element, action, listener, useCapture) {
+util.addEventListener = function(element, action, listener, useCapture) {
if (element.addEventListener) {
if (useCapture === undefined)
useCapture = false;
@@ -864,7 +890,7 @@ util.addEventListener = function addEventListener(element, action, listener, use
* @param {function} listener The listener function
* @param {boolean} [useCapture]
*/
-util.removeEventListener = function removeEventListener(element, action, listener, useCapture) {
+util.removeEventListener = function(element, action, listener, useCapture) {
if (element.removeEventListener) {
// non-IE browsers
if (useCapture === undefined)
@@ -887,7 +913,7 @@ util.removeEventListener = function removeEventListener(element, action, listene
* @param {Event} event
* @return {Element} target element
*/
-util.getTarget = function getTarget(event) {
+util.getTarget = function(event) {
// code from http://www.quirksmode.org/js/events_properties.html
if (!event) {
event = window.event;
@@ -915,7 +941,7 @@ util.getTarget = function getTarget(event) {
* @param {Element} element
* @param {Event} event
*/
-util.fakeGesture = function fakeGesture (element, event) {
+util.fakeGesture = function(element, event) {
var eventType = null;
// for hammer.js 1.0.5
@@ -1031,7 +1057,7 @@ util.option.asElement = function (value, defaultValue) {
-util.GiveDec = function GiveDec(Hex) {
+util.GiveDec = function(Hex) {
var Value;
if (Hex == "A")
@@ -1052,7 +1078,7 @@ util.GiveDec = function GiveDec(Hex) {
return Value;
};
-util.GiveHex = function GiveHex(Dec) {
+util.GiveHex = function(Dec) {
var Value;
if(Dec == 10)
@@ -1156,7 +1182,7 @@ util.parseColor = function(color) {
* @param {String} hex
* @returns {{r: *, g: *, b: *}}
*/
-util.hexToRGB = function hexToRGB(hex) {
+util.hexToRGB = function(hex) {
hex = hex.replace("#","").toUpperCase();
var a = util.GiveDec(hex.substring(0, 1));
@@ -1173,7 +1199,7 @@ util.hexToRGB = function hexToRGB(hex) {
return {r:r,g:g,b:b};
};
-util.RGBToHex = function RGBToHex(red,green,blue) {
+util.RGBToHex = function(red,green,blue) {
var a = util.GiveHex(Math.floor(red / 16));
var b = util.GiveHex(red % 16);
var c = util.GiveHex(Math.floor(green / 16));
@@ -1195,7 +1221,7 @@ util.RGBToHex = function RGBToHex(red,green,blue) {
* @returns {*}
* @constructor
*/
-util.RGBToHSV = function RGBToHSV (red,green,blue) {
+util.RGBToHSV = function(red,green,blue) {
red=red/255; green=green/255; blue=blue/255;
var minRGB = Math.min(red,Math.min(green,blue));
var maxRGB = Math.max(red,Math.max(green,blue));
@@ -1223,7 +1249,7 @@ util.RGBToHSV = function RGBToHSV (red,green,blue) {
* @returns {{r: number, g: number, b: number}}
* @constructor
*/
-util.HSVToRGB = function HSVToRGB(h, s, v) {
+util.HSVToRGB = function(h, s, v) {
var r, g, b;
var i = Math.floor(h * 6);
@@ -1244,22 +1270,22 @@ util.HSVToRGB = function HSVToRGB(h, s, v) {
return {r:Math.floor(r * 255), g:Math.floor(g * 255), b:Math.floor(b * 255) };
};
-util.HSVToHex = function HSVToHex(h, s, v) {
+util.HSVToHex = function(h, s, v) {
var rgb = util.HSVToRGB(h, s, v);
return util.RGBToHex(rgb.r, rgb.g, rgb.b);
};
-util.hexToHSV = function hexToHSV(hex) {
+util.hexToHSV = function(hex) {
var rgb = util.hexToRGB(hex);
return util.RGBToHSV(rgb.r, rgb.g, rgb.b);
};
-util.isValidHex = function isValidHex(hex) {
+util.isValidHex = function(hex) {
var isOk = /(^#[0-9A-F]{6}$)|(^#[0-9A-F]{3}$)/i.test(hex);
return isOk;
};
-util.copyObject = function copyObject(objectFrom, objectTo) {
+util.copyObject = function(objectFrom, objectTo) {
for (var i in objectFrom) {
if (objectFrom.hasOwnProperty(i)) {
if (typeof objectFrom[i] == "object") {
@@ -1279,7 +1305,7 @@ util.copyObject = function copyObject(objectFrom, objectTo) {
* Usage:
* var dataSet = new DataSet({
* fieldId: '_id',
- * convert: {
+ * type: {
* // ...
* }
* });
@@ -1305,43 +1331,46 @@ util.copyObject = function copyObject(objectFrom, objectTo) {
* @param {Object} [options] Available options:
* {String} fieldId Field name of the id in the
* items, 'id' by default.
- * {Object.} [convert]
+ * {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
@@ -1567,7 +1596,6 @@ DataSet.prototype.update = function (data, senderId) {
*/
DataSet.prototype.get = function (args) {
var me = this;
- var globalShowInternalIds = this.showInternalIds;
// parse the arguments
var id, ids, options, data;
@@ -1591,42 +1619,35 @@ DataSet.prototype.get = function (args) {
}
// determine the return type
- var type;
- if (options && options.type) {
- type = (options.type == 'DataTable') ? 'DataTable' : 'Array';
+ var returnType;
+ if (options && options.returnType) {
+ returnType = (options.returnType == 'DataTable') ? 'DataTable' : 'Array';
- if (data && (type != util.getType(data))) {
+ 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 (type == 'DataTable' && !util.isDataTable(data)) {
+ if (returnType == 'DataTable' && !util.isDataTable(data)) {
throw new Error('Parameter "data" must be a DataTable ' +
'when options.type is "DataTable"');
}
}
else if (data) {
- type = (util.getType(data) == 'DataTable') ? 'DataTable' : 'Array';
+ returnType = (util.getType(data) == 'DataTable') ? 'DataTable' : 'Array';
}
else {
- type = 'Array';
- }
-
- // we allow the setting of this value for a single get request.
- if (options != undefined) {
- if (options.showInternalIds != undefined) {
- this.showInternalIds = options.showInternalIds;
- }
+ returnType = 'Array';
}
// build options
- var convert = options && options.convert || this.options.convert;
+ 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, convert);
+ item = me._getItem(id, type);
if (filter && !filter(item)) {
item = null;
}
@@ -1634,7 +1655,7 @@ DataSet.prototype.get = function (args) {
else if (ids != undefined) {
// return a subset of items
for (i = 0, len = ids.length; i < len; i++) {
- item = me._getItem(ids[i], convert);
+ item = me._getItem(ids[i], type);
if (!filter || filter(item)) {
items.push(item);
}
@@ -1642,9 +1663,9 @@ DataSet.prototype.get = function (args) {
}
else {
// return all items
- for (itemId in this.data) {
- if (this.data.hasOwnProperty(itemId)) {
- item = me._getItem(itemId, convert);
+ for (itemId in this._data) {
+ if (this._data.hasOwnProperty(itemId)) {
+ item = me._getItem(itemId, type);
if (!filter || filter(item)) {
items.push(item);
}
@@ -1652,9 +1673,6 @@ DataSet.prototype.get = function (args) {
}
}
- // restore the global value of showInternalIds
- this.showInternalIds = globalShowInternalIds;
-
// order the results
if (options && options.order && id == undefined) {
this._sort(items, options.order);
@@ -1674,7 +1692,7 @@ DataSet.prototype.get = function (args) {
}
// return the results
- if (type == 'DataTable') {
+ if (returnType == 'DataTable') {
var columns = this._getColumnNames(data);
if (id != undefined) {
// append a single item to the data table
@@ -1720,10 +1738,10 @@ DataSet.prototype.get = function (args) {
* @return {Array} ids
*/
DataSet.prototype.getIds = function (options) {
- var data = this.data,
+ var data = this._data,
filter = options && options.filter,
order = options && options.order,
- convert = options && options.convert || this.options.convert,
+ type = options && options.type || this._options.type,
i,
len,
id,
@@ -1738,7 +1756,7 @@ DataSet.prototype.getIds = function (options) {
items = [];
for (id in data) {
if (data.hasOwnProperty(id)) {
- item = this._getItem(id, convert);
+ item = this._getItem(id, type);
if (filter(item)) {
items.push(item);
}
@@ -1748,16 +1766,16 @@ DataSet.prototype.getIds = function (options) {
this._sort(items, order);
for (i = 0, len = items.length; i < len; i++) {
- ids[i] = items[i][this.fieldId];
+ ids[i] = items[i][this._fieldId];
}
}
else {
// create unordered list
for (id in data) {
if (data.hasOwnProperty(id)) {
- item = this._getItem(id, convert);
+ item = this._getItem(id, type);
if (filter(item)) {
- ids.push(item[this.fieldId]);
+ ids.push(item[this._fieldId]);
}
}
}
@@ -1777,7 +1795,7 @@ DataSet.prototype.getIds = function (options) {
this._sort(items, order);
for (i = 0, len = items.length; i < len; i++) {
- ids[i] = items[i][this.fieldId];
+ ids[i] = items[i][this._fieldId];
}
}
else {
@@ -1785,7 +1803,7 @@ DataSet.prototype.getIds = function (options) {
for (id in data) {
if (data.hasOwnProperty(id)) {
item = data[id];
- ids.push(item[this.fieldId]);
+ ids.push(item[this._fieldId]);
}
}
}
@@ -1798,7 +1816,7 @@ DataSet.prototype.getIds = function (options) {
* Execute a callback function for every item in the dataset.
* @param {function} callback
* @param {Object} [options] Available options:
- * {Object.} [convert]
+ * {Object.} [type]
* {String[]} [fields] filter fields
* {function} [filter] filter items
* {String | function} [order] Order the items by
@@ -1806,8 +1824,8 @@ DataSet.prototype.getIds = function (options) {
*/
DataSet.prototype.forEach = function (callback, options) {
var filter = options && options.filter,
- convert = options && options.convert || this.options.convert,
- data = this.data,
+ type = options && options.type || this._options.type,
+ data = this._data,
item,
id;
@@ -1817,7 +1835,7 @@ DataSet.prototype.forEach = function (callback, options) {
for (var i = 0, len = items.length; i < len; i++) {
item = items[i];
- id = item[this.fieldId];
+ id = item[this._fieldId];
callback(item, id);
}
}
@@ -1825,7 +1843,7 @@ DataSet.prototype.forEach = function (callback, options) {
// unordered
for (id in data) {
if (data.hasOwnProperty(id)) {
- item = this._getItem(id, convert);
+ item = this._getItem(id, type);
if (!filter || filter(item)) {
callback(item, id);
}
@@ -1838,7 +1856,7 @@ DataSet.prototype.forEach = function (callback, options) {
* Map every item in the dataset.
* @param {function} callback
* @param {Object} [options] Available options:
- * {Object.} [convert]
+ * {Object.} [type]
* {String[]} [fields] filter fields
* {function} [filter] filter items
* {String | function} [order] Order the items by
@@ -1847,15 +1865,15 @@ DataSet.prototype.forEach = function (callback, options) {
*/
DataSet.prototype.map = function (callback, options) {
var filter = options && options.filter,
- convert = options && options.convert || this.options.convert,
+ type = options && options.type || this._options.type,
mappedItems = [],
- data = this.data,
+ data = this._data,
item;
// convert and filter items
for (var id in data) {
if (data.hasOwnProperty(id)) {
- item = this._getItem(id, convert);
+ item = this._getItem(id, type);
if (!filter || filter(item)) {
mappedItems.push(callback(item, id));
}
@@ -1927,7 +1945,7 @@ DataSet.prototype.remove = function (id, senderId) {
var removedIds = [],
i, len, removedId;
- if (id instanceof Array) {
+ if (Array.isArray(id)) {
for (i = 0, len = id.length; i < len; i++) {
removedId = this._remove(id[i]);
if (removedId != null) {
@@ -1957,17 +1975,15 @@ DataSet.prototype.remove = function (id, senderId) {
*/
DataSet.prototype._remove = function (id) {
if (util.isNumber(id) || util.isString(id)) {
- if (this.data[id]) {
- delete this.data[id];
- delete this.internalIds[id];
+ if (this._data[id]) {
+ delete this._data[id];
return id;
}
}
else if (id instanceof Object) {
- var itemId = id[this.fieldId];
- if (itemId && this.data[itemId]) {
- delete this.data[itemId];
- delete this.internalIds[itemId];
+ var itemId = id[this._fieldId];
+ if (itemId && this._data[itemId]) {
+ delete this._data[itemId];
return itemId;
}
}
@@ -1980,10 +1996,9 @@ DataSet.prototype._remove = function (id) {
* @return {Array} removedIds The ids of all removed items
*/
DataSet.prototype.clear = function (senderId) {
- var ids = Object.keys(this.data);
+ var ids = Object.keys(this._data);
- this.data = {};
- this.internalIds = {};
+ this._data = {};
this._trigger('remove', {items: ids}, senderId);
@@ -1996,7 +2011,7 @@ DataSet.prototype.clear = function (senderId) {
* @return {Object | null} item Item containing max value, or null if no items
*/
DataSet.prototype.max = function (field) {
- var data = this.data,
+ var data = this._data,
max = null,
maxField = null;
@@ -2020,7 +2035,7 @@ DataSet.prototype.max = function (field) {
* @return {Object | null} item Item containing max value, or null if no items
*/
DataSet.prototype.min = function (field) {
- var data = this.data,
+ var data = this._data,
min = null,
minField = null;
@@ -2046,17 +2061,18 @@ DataSet.prototype.min = function (field) {
* The returned array is unordered.
*/
DataSet.prototype.distinct = function (field) {
- var data = this.data,
- values = [],
- fieldType = this.options.convert[field],
- count = 0;
+ 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 = util.convert(item[field], fieldType);
+ var value = item[field];
var exists = false;
- for (var i = 0; i < count; i++) {
+ for (i = 0; i < count; i++) {
if (values[i] == value) {
exists = true;
break;
@@ -2069,6 +2085,12 @@ DataSet.prototype.distinct = function (field) {
}
}
+ if (fieldType) {
+ for (i = 0; i < values.length; i++) {
+ values[i] = util.convert(values[i], fieldType);
+ }
+ }
+
return values;
};
@@ -2079,11 +2101,11 @@ DataSet.prototype.distinct = function (field) {
* @private
*/
DataSet.prototype._addItem = function (item) {
- var id = item[this.fieldId];
+ var id = item[this._fieldId];
if (id != undefined) {
// check whether this id is already taken
- if (this.data[id]) {
+ if (this._data[id]) {
// item already exists
throw new Error('Cannot add item: item with id ' + id + ' already exists');
}
@@ -2091,18 +2113,17 @@ DataSet.prototype._addItem = function (item) {
else {
// generate an id
id = util.randomUUID();
- item[this.fieldId] = id;
- this.internalIds[id] = item;
+ item[this._fieldId] = id;
}
var d = {};
for (var field in item) {
if (item.hasOwnProperty(field)) {
- var fieldType = this.convert[field]; // type may be undefined
+ var fieldType = this._type[field]; // type may be undefined
d[field] = util.convert(item[field], fieldType);
}
}
- this.data[id] = d;
+ this._data[id] = d;
return id;
};
@@ -2110,31 +2131,26 @@ DataSet.prototype._addItem = function (item) {
/**
* Get an item. Fields can be converted to a specific type
* @param {String} id
- * @param {Object.} [convert] field types to convert
+ * @param {Object.} [types] field types to convert
* @return {Object | null} item
* @private
*/
-DataSet.prototype._getItem = function (id, convert) {
+DataSet.prototype._getItem = function (id, types) {
var field, value;
// get the item from the dataset
- var raw = this.data[id];
+ var raw = this._data[id];
if (!raw) {
return null;
}
// convert the items field types
- var converted = {},
- fieldId = this.fieldId,
- internalIds = this.internalIds;
- if (convert) {
+ var converted = {};
+ if (types) {
for (field in raw) {
if (raw.hasOwnProperty(field)) {
value = raw[field];
- // output all fields, except internal ids
- if ((field != fieldId) || (!(value in internalIds) || this.showInternalIds)) {
- converted[field] = util.convert(value, convert[field]);
- }
+ converted[field] = util.convert(value, types[field]);
}
}
}
@@ -2143,10 +2159,7 @@ DataSet.prototype._getItem = function (id, convert) {
for (field in raw) {
if (raw.hasOwnProperty(field)) {
value = raw[field];
- // output all fields, except internal ids
- if ((field != fieldId) || (!(value in internalIds) || this.showInternalIds)) {
- converted[field] = value;
- }
+ converted[field] = value;
}
}
}
@@ -2162,11 +2175,11 @@ DataSet.prototype._getItem = function (id, convert) {
* @private
*/
DataSet.prototype._updateItem = function (item) {
- var id = item[this.fieldId];
+ 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];
+ var d = this._data[id];
if (!d) {
// item doesn't exist
throw new Error('Cannot update item: no item with id ' + id + ' found');
@@ -2175,7 +2188,7 @@ DataSet.prototype._updateItem = function (item) {
// merge with current item
for (var field in item) {
if (item.hasOwnProperty(field)) {
- var fieldType = this.convert[field]; // type may be undefined
+ var fieldType = this._type[field]; // type may be undefined
d[field] = util.convert(item[field], fieldType);
}
}
@@ -2183,17 +2196,6 @@ DataSet.prototype._updateItem = function (item) {
return id;
};
-/**
- * check if an id is an internal or external id
- * @param id
- * @returns {boolean}
- * @private
- */
-DataSet.prototype.isInternalId = function(id) {
- return (id in this.internalIds);
-};
-
-
/**
* Get an array with the column names of a Google DataTable
* @param {DataTable} dataTable
@@ -2235,13 +2237,11 @@ DataSet.prototype._appendRow = function (dataTable, columns, item) {
* @constructor DataView
*/
function DataView (data, options) {
- this.id = util.randomUUID();
-
- this.data = null;
- this.ids = {}; // ids of the items currently in memory (just contains a boolean true)
- this.options = options || {};
- this.fieldId = 'id'; // name of the field containing id
- this.subscribers = {}; // event subscribers
+ this._data = null;
+ this._ids = {}; // ids of the items currently in memory (just contains a boolean true)
+ this._options = options || {};
+ this._fieldId = 'id'; // name of the field containing id
+ this._subscribers = {}; // event subscribers
var me = this;
this.listener = function () {
@@ -2259,44 +2259,44 @@ function DataView (data, options) {
* @param {DataSet | DataView} data
*/
DataView.prototype.setData = function (data) {
- var ids, dataItems, i, len;
+ var ids, i, len;
- if (this.data) {
+ if (this._data) {
// unsubscribe from current dataset
- if (this.data.unsubscribe) {
- this.data.unsubscribe('*', this.listener);
+ if (this._data.unsubscribe) {
+ this._data.unsubscribe('*', this.listener);
}
// trigger a remove of all items in memory
ids = [];
- for (var id in this.ids) {
- if (this.ids.hasOwnProperty(id)) {
+ for (var id in this._ids) {
+ if (this._ids.hasOwnProperty(id)) {
ids.push(id);
}
}
- this.ids = {};
+ this._ids = {};
this._trigger('remove', {items: ids});
}
- this.data = data;
+ this._data = data;
- if (this.data) {
+ if (this._data) {
// update fieldId
- this.fieldId = this.options.fieldId ||
- (this.data && this.data.options && this.data.options.fieldId) ||
+ this._fieldId = this._options.fieldId ||
+ (this._data && this._data.options && this._data.options.fieldId) ||
'id';
// trigger an add of all added items
- ids = this.data.getIds({filter: this.options && this.options.filter});
+ 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._ids[id] = true;
}
this._trigger('add', {items: ids});
// subscribe to new dataset
- if (this.data.on) {
- this.data.on('*', this.listener);
+ if (this._data.on) {
+ this._data.on('*', this.listener);
}
}
};
@@ -2354,12 +2354,12 @@ DataView.prototype.get = function (args) {
}
// extend the options with the default options and provided options
- var viewOptions = util.extend({}, this.options, options);
+ var viewOptions = util.extend({}, this._options, options);
// create a combined filter method when needed
- if (this.options.filter && options && options.filter) {
+ if (this._options.filter && options && options.filter) {
viewOptions.filter = function (item) {
- return me.options.filter(item) && options.filter(item);
+ return me._options.filter(item) && options.filter(item);
}
}
@@ -2371,7 +2371,7 @@ DataView.prototype.get = function (args) {
getArguments.push(viewOptions);
getArguments.push(data);
- return this.data && this.data.get.apply(this.data, getArguments);
+ return this._data && this._data.get.apply(this._data, getArguments);
};
/**
@@ -2385,8 +2385,8 @@ DataView.prototype.get = function (args) {
DataView.prototype.getIds = function (options) {
var ids;
- if (this.data) {
- var defaultFilter = this.options.filter;
+ if (this._data) {
+ var defaultFilter = this._options.filter;
var filter;
if (options && options.filter) {
@@ -2403,7 +2403,7 @@ DataView.prototype.getIds = function (options) {
filter = defaultFilter;
}
- ids = this.data.getIds({
+ ids = this._data.getIds({
filter: filter,
order: options && options.order
});
@@ -2427,7 +2427,7 @@ DataView.prototype.getIds = function (options) {
DataView.prototype._onEvent = function (event, params, senderId) {
var i, len, id, item,
ids = params && params.items,
- data = this.data,
+ data = this._data,
added = [],
updated = [],
removed = [];
@@ -2440,7 +2440,7 @@ DataView.prototype._onEvent = function (event, params, senderId) {
id = ids[i];
item = this.get(id);
if (item) {
- this.ids[id] = true;
+ this._ids[id] = true;
added.push(id);
}
}
@@ -2455,17 +2455,17 @@ DataView.prototype._onEvent = function (event, params, senderId) {
item = this.get(id);
if (item) {
- if (this.ids[id]) {
+ if (this._ids[id]) {
updated.push(id);
}
else {
- this.ids[id] = true;
+ this._ids[id] = true;
added.push(id);
}
}
else {
- if (this.ids[id]) {
- delete this.ids[id];
+ if (this._ids[id]) {
+ delete this._ids[id];
removed.push(id);
}
else {
@@ -2480,8 +2480,8 @@ DataView.prototype._onEvent = function (event, params, senderId) {
// 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];
+ if (this._ids[id]) {
+ delete this._ids[id];
removed.push(id);
}
}
@@ -2519,7 +2519,7 @@ var stack = {};
* Order items by their start data
* @param {Item[]} items
*/
-stack.orderByStart = function orderByStart(items) {
+stack.orderByStart = function(items) {
items.sort(function (a, b) {
return a.data.start - b.data.start;
});
@@ -2530,7 +2530,7 @@ stack.orderByStart = function orderByStart(items) {
* is used.
* @param {Item[]} items
*/
-stack.orderByEnd = function orderByEnd(items) {
+stack.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;
@@ -2550,7 +2550,7 @@ stack.orderByEnd = function orderByEnd(items) {
* If true, all items will be repositioned. If false (default), only
* items having a top===null will be re-stacked
*/
-stack.stack = function _stack (items, margin, force) {
+stack.stack = function(items, margin, force) {
var i, iMax;
if (force) {
@@ -2595,7 +2595,7 @@ stack.stack = function _stack (items, margin, force) {
* @param {{item: number, axis: number}} margin
* Margins between items and between items and the axis.
*/
-stack.nostack = function nostack (items, margin) {
+stack.nostack = function(items, margin) {
var i, iMax;
// reset top position of all items
@@ -2616,7 +2616,7 @@ stack.nostack = function nostack (items, margin) {
* the requested margin.
* @return {boolean} true if a and b collide, else false
*/
-stack.collision = function collision (a, b, margin) {
+stack.collision = function(a, b, margin) {
return ((a.left - margin) < (b.left + b.width) &&
(a.left + a.width + margin) > b.left &&
(a.top - margin) < (b.top + b.height) &&
@@ -3095,57 +3095,81 @@ TimeStep.prototype.getLabelMajor = function(date) {
* 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 {RootPanel} root Root panel, used to subscribe to events
- * @param {Panel} parent Parent panel, used to attach to the DOM
+ * @param {{dom: Object, domProps: Object, emitter: Emitter}} body
* @param {Object} [options] See description at Range.setOptions
*/
-function Range(root, parent, options) {
- this.id = util.randomUUID();
- this.start = null; // Number
- this.end = null; // Number
+function Range(body, options) {
+ var now = moment().hours(0).minutes(0).seconds(0).milliseconds(0);
+ this.start = now.clone().add('days', -3).valueOf(); // Number
+ this.end = now.clone().add('days', 4).valueOf(); // Number
- this.root = root;
- this.parent = parent;
- this.options = options || {};
+ this.body = body;
+
+ // 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.props = {
+ touch: {}
+ };
// drag listeners for dragging
- this.root.on('dragstart', this._onDragStart.bind(this));
- this.root.on('drag', this._onDrag.bind(this));
- this.root.on('dragend', this._onDragEnd.bind(this));
+ 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.root.on('hold', this._onHold.bind(this));
+ this.body.emitter.on('hold', this._onHold.bind(this));
// mouse wheel for zooming
- this.root.on('mousewheel', this._onMouseWheel.bind(this));
- this.root.on('DOMMouseScroll', this._onMouseWheel.bind(this)); // For FF
+ this.body.emitter.on('mousewheel', this._onMouseWheel.bind(this));
+ this.body.emitter.on('DOMMouseScroll', this._onMouseWheel.bind(this)); // For FF
// pinch to zoom
- this.root.on('touch', this._onTouch.bind(this));
- this.root.on('pinch', this._onPinch.bind(this));
+ this.body.emitter.on('touch', this._onTouch.bind(this));
+ this.body.emitter.on('pinch', this._onPinch.bind(this));
this.setOptions(options);
}
-// turn Range into an event emitter
-Emitter(Range.prototype);
+Range.prototype = new Component();
/**
* Set options for the range controller
* @param {Object} options Available options:
+ * {Number | Date | String} start Start date for the range
+ * {Number | Date | String} end End date for the range
* {Number} min Minimum value for start
* {Number} max Maximum value for end
* {Number} zoomMin Set a minimum value for
* (end - start).
* {Number} zoomMax Set a maximum value for
* (end - start).
+ * {Boolean} moveable Enable moving of the range
+ * by dragging. True by default
+ * {Boolean} zoomable Enable zooming of the range
+ * by pinching/scrolling. True by default
*/
Range.prototype.setOptions = function (options) {
- util.extend(this.options, options);
+ if (options) {
+ // copy the options that we know
+ var fields = ['direction', 'min', 'max', 'zoomMin', 'zoomMax', 'moveable', 'zoomable'];
+ util.selectiveExtend(fields, this.options, options);
- // re-apply range with new limitations
- if (this.start !== null && this.end !== null) {
- this.setRange(this.start, this.end);
+ if ('start' in options || 'end' in options) {
+ // apply a new range. both start and end are optional
+ this.setRange(options.start, options.end);
+ }
}
};
@@ -3172,8 +3196,8 @@ Range.prototype.setRange = function(start, end) {
start: new Date(this.start),
end: new Date(this.end)
};
- this.emit('rangechange', params);
- this.emit('rangechanged', params);
+ this.body.emitter.emit('rangechange', params);
+ this.body.emitter.emit('rangechanged', params);
}
};
@@ -3332,77 +3356,75 @@ Range.conversion = function (start, end, width) {
}
};
-// global (private) object to store drag params
-var touchParams = {};
-
/**
* Start dragging horizontally or vertically
* @param {Event} event
* @private
*/
Range.prototype._onDragStart = 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 (touchParams.ignore) return;
-
- // TODO: reckon with option movable
+ if (!this.props.touch.allowDragging) return;
- touchParams.start = this.start;
- touchParams.end = this.end;
+ this.props.touch.start = this.start;
+ this.props.touch.end = this.end;
- var frame = this.parent.frame;
- if (frame) {
- frame.style.cursor = 'move';
+ if (this.body.dom.root) {
+ this.body.dom.root.style.cursor = 'move';
}
};
/**
- * Perform dragging operating.
+ * Perform dragging operation
* @param {Event} event
* @private
*/
Range.prototype._onDrag = function (event) {
+ // only allow dragging when configured as movable
+ if (!this.options.moveable) return;
+
var direction = this.options.direction;
validateDirection(direction);
- // TODO: reckon with option movable
-
-
// 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 (touchParams.ignore) return;
+ if (!this.props.touch.allowDragging) return;
var delta = (direction == 'horizontal') ? event.gesture.deltaX : event.gesture.deltaY,
- interval = (touchParams.end - touchParams.start),
- width = (direction == 'horizontal') ? this.parent.width : this.parent.height,
+ interval = (this.props.touch.end - this.props.touch.start),
+ width = (direction == 'horizontal') ? this.body.domProps.center.width : this.body.domProps.center.height,
diffRange = -delta / width * interval;
- this._applyRange(touchParams.start + diffRange, touchParams.end + diffRange);
+ this._applyRange(this.props.touch.start + diffRange, this.props.touch.end + diffRange);
- this.emit('rangechange', {
+ this.body.emitter.emit('rangechange', {
start: new Date(this.start),
end: new Date(this.end)
});
};
/**
- * Stop dragging operating.
+ * Stop dragging operation
* @param {event} event
* @private
*/
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 (touchParams.ignore) return;
+ if (!this.props.touch.allowDragging) return;
- // TODO: reckon with option movable
-
- if (this.parent.frame) {
- this.parent.frame.style.cursor = 'auto';
+ if (this.body.dom.root) {
+ this.body.dom.root.style.cursor = 'auto';
}
// fire a rangechanged event
- this.emit('rangechanged', {
+ this.body.emitter.emit('rangechanged', {
start: new Date(this.start),
end: new Date(this.end)
});
@@ -3415,7 +3437,8 @@ Range.prototype._onDragEnd = function (event) {
* @private
*/
Range.prototype._onMouseWheel = function(event) {
- // TODO: reckon with option zoomable
+ // only allow zooming when configured as zoomable and moveable
+ if (!(this.options.zoomable && this.options.moveable)) return;
// retrieve delta
var delta = 0;
@@ -3445,7 +3468,7 @@ Range.prototype._onMouseWheel = function(event) {
// calculate center, the date to zoom around
var gesture = util.fakeGesture(this, event),
- pointer = getPointer(gesture.center, this.parent.frame),
+ pointer = getPointer(gesture.center, this.body.dom.center),
pointerDate = this._pointerToDate(pointer);
this.zoom(scale, pointerDate);
@@ -3461,17 +3484,10 @@ Range.prototype._onMouseWheel = function(event) {
* @private
*/
Range.prototype._onTouch = function (event) {
- touchParams.start = this.start;
- touchParams.end = this.end;
- touchParams.ignore = false;
- touchParams.center = null;
-
- // don't move the range when dragging a selected event
- // TODO: it's not so neat to have to know about the state of the ItemSet
- var item = ItemSet.itemFromTarget(event);
- if (item && item.selected && this.options.editable) {
- touchParams.ignore = true;
- }
+ this.props.touch.start = this.start;
+ this.props.touch.end = this.end;
+ this.props.touch.allowDragging = true;
+ this.props.touch.center = null;
};
/**
@@ -3479,7 +3495,7 @@ Range.prototype._onTouch = function (event) {
* @private
*/
Range.prototype._onHold = function () {
- touchParams.ignore = true;
+ this.props.touch.allowDragging = false;
};
/**
@@ -3488,25 +3504,22 @@ Range.prototype._onHold = function () {
* @private
*/
Range.prototype._onPinch = function (event) {
- var direction = this.options.direction;
- touchParams.ignore = true;
+ // only allow zooming when configured as zoomable and moveable
+ if (!(this.options.zoomable && this.options.moveable)) return;
- // TODO: reckon with option zoomable
+ this.props.touch.allowDragging = false;
if (event.gesture.touches.length > 1) {
- if (!touchParams.center) {
- touchParams.center = getPointer(event.gesture.center, this.parent.frame);
+ if (!this.props.touch.center) {
+ this.props.touch.center = getPointer(event.gesture.center, this.body.dom.center);
}
var scale = 1 / event.gesture.scale,
- initDate = this._pointerToDate(touchParams.center),
- center = getPointer(event.gesture.center, this.parent.frame),
- date = this._pointerToDate(this.parent, center),
- delta = date - initDate; // TODO: utilize delta
+ initDate = this._pointerToDate(this.props.touch.center);
// calculate new start and end
- var newStart = parseInt(initDate + (touchParams.start - initDate) * scale);
- var newEnd = parseInt(initDate + (touchParams.end - initDate) * scale);
+ var newStart = parseInt(initDate + (this.props.touch.start - initDate) * scale);
+ var newEnd = parseInt(initDate + (this.props.touch.end - initDate) * scale);
// apply new range
this.setRange(newStart, newEnd);
@@ -3526,12 +3539,12 @@ Range.prototype._pointerToDate = function (pointer) {
validateDirection(direction);
if (direction == 'horizontal') {
- var width = this.parent.width;
+ var width = this.body.domProps.center.width;
conversion = this.conversion(width);
return pointer.x / conversion.scale + conversion.offset;
}
else {
- var height = this.parent.height;
+ var height = this.body.domProps.center.height;
conversion = this.conversion(height);
return pointer.y / conversion.scale + conversion.offset;
}
@@ -3612,74 +3625,39 @@ Range.prototype.moveTo = function(moveTo) {
/**
* Prototype for visual components
+ * @param {{dom: Object, domProps: Object, emitter: Emitter, range: Range}} [body]
+ * @param {Object} [options]
*/
-function Component () {
- this.id = null;
- this.parent = null;
- this.childs = null;
+function Component (body, options) {
this.options = null;
-
- this.top = 0;
- this.left = 0;
- this.width = 0;
- this.height = 0;
+ this.props = null;
}
-// Turn the Component into an event emitter
-Emitter(Component.prototype);
-
/**
- * Set parameters for the frame. Parameters will be merged in current parameter
- * set.
- * @param {Object} options Available parameters:
- * {String | function} [className]
- * {String | Number | function} [left]
- * {String | Number | function} [top]
- * {String | Number | function} [width]
- * {String | Number | function} [height]
+ * Set options for the component. The new options will be merged into the
+ * current options.
+ * @param {Object} options
*/
-Component.prototype.setOptions = function setOptions(options) {
+Component.prototype.setOptions = function(options) {
if (options) {
util.extend(this.options, options);
-
- this.repaint();
- }
-};
-
-/**
- * Get an option value by name
- * The function will first check this.options object, and else will check
- * this.defaultOptions.
- * @param {String} name
- * @return {*} value
- */
-Component.prototype.getOption = function getOption(name) {
- var value;
- if (this.options) {
- value = this.options[name];
- }
- if (value === undefined && this.defaultOptions) {
- value = this.defaultOptions[name];
}
- return value;
};
/**
- * Get the frame element of the component, the outer HTML DOM element.
- * @returns {HTMLElement | null} frame
+ * Repaint the component
+ * @return {boolean} Returns true if the component is resized
*/
-Component.prototype.getFrame = function getFrame() {
+Component.prototype.redraw = function() {
// should be implemented by the component
- return null;
+ return false;
};
/**
- * Repaint the component
- * @return {boolean} Returns true if the component is resized
+ * Destroy the component. Cleanup DOM and event listeners
*/
-Component.prototype.repaint = function repaint() {
+Component.prototype.destroy = function() {
// should be implemented by the component
- return false;
};
/**
@@ -3688,523 +3666,179 @@ Component.prototype.repaint = function repaint() {
* @return {Boolean} Returns true if the component is resized
* @protected
*/
-Component.prototype._isResized = function _isResized() {
- var resized = (this._previousWidth !== this.width || this._previousHeight !== this.height);
+Component.prototype._isResized = function() {
+ var resized = (this.props._previousWidth !== this.props.width ||
+ this.props._previousHeight !== this.props.height);
- this._previousWidth = this.width;
- this._previousHeight = this.height;
+ this.props._previousWidth = this.props.width;
+ this.props._previousHeight = this.props.height;
return resized;
};
/**
- * A panel can contain components
- * @param {Object} [options] Available parameters:
- * {String | Number | function} [left]
- * {String | Number | function} [top]
- * {String | Number | function} [width]
- * {String | Number | function} [height]
- * {String | function} [className]
- * @constructor Panel
+ * 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 Panel(options) {
- this.id = util.randomUUID();
- this.parent = null;
- this.childs = [];
+function TimeAxis (body, options) {
+ this.dom = {
+ foreground: null,
+ majorLines: [],
+ majorTexts: [],
+ minorLines: [],
+ minorTexts: [],
+ redundant: {
+ majorLines: [],
+ majorTexts: [],
+ minorLines: [],
+ minorTexts: []
+ }
+ };
+ this.props = {
+ range: {
+ start: 0,
+ end: 0,
+ minimumStep: 0
+ },
+ lineTop: 0
+ };
- this.options = options || {};
+ this.defaultOptions = {
+ orientation: 'bottom', // supported: 'top', 'bottom'
+ // TODO: implement timeaxis orientations 'left' and 'right'
+ showMinorLabels: true,
+ showMajorLabels: true
+ };
+ this.options = util.extend({}, this.defaultOptions);
- // create frame
- this.frame = (typeof document !== 'undefined') ? document.createElement('div') : null;
-}
+ this.body = body;
-Panel.prototype = new Component();
+ // create the HTML DOM
+ this._create();
-/**
- * Set options. Will extend the current options.
- * @param {Object} [options] Available parameters:
- * {String | function} [className]
- * {String | Number | function} [left]
- * {String | Number | function} [top]
- * {String | Number | function} [width]
- * {String | Number | function} [height]
- */
-Panel.prototype.setOptions = Component.prototype.setOptions;
+ this.setOptions(options);
+}
-/**
- * Get the outer frame of the panel
- * @returns {HTMLElement} frame
- */
-Panel.prototype.getFrame = function () {
- return this.frame;
-};
+TimeAxis.prototype = new Component();
/**
- * Append a child to the panel
- * @param {Component} child
+ * Set options for the TimeAxis.
+ * Parameters will be merged in current options.
+ * @param {Object} options Available options:
+ * {string} [orientation]
+ * {boolean} [showMinorLabels]
+ * {boolean} [showMajorLabels]
*/
-Panel.prototype.appendChild = function (child) {
- this.childs.push(child);
- child.parent = this;
-
- // attach to the DOM
- var frame = child.getFrame();
- if (frame) {
- if (frame.parentNode) {
- frame.parentNode.removeChild(frame);
- }
- this.frame.appendChild(frame);
+TimeAxis.prototype.setOptions = function(options) {
+ if (options) {
+ // copy all options that we know
+ util.selectiveExtend(['orientation', 'showMinorLabels', 'showMajorLabels'], this.options, options);
}
};
/**
- * Insert a child to the panel
- * @param {Component} child
- * @param {Component} beforeChild
+ * Create the HTML DOM for the TimeAxis
*/
-Panel.prototype.insertBefore = function (child, beforeChild) {
- var index = this.childs.indexOf(beforeChild);
- if (index != -1) {
- this.childs.splice(index, 0, child);
- child.parent = this;
-
- // attach to the DOM
- var frame = child.getFrame();
- if (frame) {
- if (frame.parentNode) {
- frame.parentNode.removeChild(frame);
- }
+TimeAxis.prototype._create = function() {
+ this.dom.foreground = document.createElement('div');
+ this.dom.background = document.createElement('div');
- var beforeFrame = beforeChild.getFrame();
- if (beforeFrame) {
- this.frame.insertBefore(frame, beforeFrame);
- }
- else {
- this.frame.appendChild(frame);
- }
- }
- }
+ this.dom.foreground.className = 'timeaxis foreground';
+ this.dom.background.className = 'timeaxis background';
};
/**
- * Remove a child from the panel
- * @param {Component} child
+ * Destroy the TimeAxis
*/
-Panel.prototype.removeChild = function (child) {
- var index = this.childs.indexOf(child);
- if (index != -1) {
- this.childs.splice(index, 1);
- child.parent = null;
-
- // remove from the DOM
- var frame = child.getFrame();
- if (frame && frame.parentNode) {
- this.frame.removeChild(frame);
- }
+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);
}
-};
-/**
- * Test whether the panel contains given child
- * @param {Component} child
- */
-Panel.prototype.hasChild = function (child) {
- var index = this.childs.indexOf(child);
- return (index != -1);
+ this.body = null;
};
/**
* Repaint the component
- * @return {boolean} Returns true if the component was resized since previous repaint
- */
-Panel.prototype.repaint = function () {
- var asString = util.option.asString,
- options = this.options,
- frame = this.getFrame();
-
- // update className
- frame.className = 'vpanel' + (options.className ? (' ' + asString(options.className)) : '');
-
- // repaint the child components
- var childsResized = this._repaintChilds();
-
- // update frame size
- this._updateSize();
-
- return this._isResized() || childsResized;
-};
-
-/**
- * Repaint all childs of the panel
* @return {boolean} Returns true if the component is resized
- * @private
- */
-Panel.prototype._repaintChilds = function () {
- var resized = false;
- for (var i = 0, ii = this.childs.length; i < ii; i++) {
- resized = this.childs[i].repaint() || resized;
- }
- return resized;
-};
-
-/**
- * Apply the size from options to the panel, and recalculate it's actual size.
- * @private
- */
-Panel.prototype._updateSize = function () {
- // apply size
- this.frame.style.top = util.option.asSize(this.options.top);
- this.frame.style.bottom = util.option.asSize(this.options.bottom);
- this.frame.style.left = util.option.asSize(this.options.left);
- this.frame.style.right = util.option.asSize(this.options.right);
- this.frame.style.width = util.option.asSize(this.options.width, '100%');
- this.frame.style.height = util.option.asSize(this.options.height, '');
-
- // get actual size
- this.top = this.frame.offsetTop;
- this.left = this.frame.offsetLeft;
- this.width = this.frame.offsetWidth;
- this.height = this.frame.offsetHeight;
-};
-
-/**
- * A root panel can hold components. The root panel must be initialized with
- * a DOM element as container.
- * @param {HTMLElement} container
- * @param {Object} [options] Available parameters: see RootPanel.setOptions.
- * @constructor RootPanel
- * @extends Panel
*/
-function RootPanel(container, options) {
- this.id = util.randomUUID();
- this.container = container;
-
- this.options = options || {};
- this.defaultOptions = {
- autoResize: true
- };
-
- // create the HTML DOM
- this._create();
-
- // attach the root panel to the provided container
- if (!this.container) throw new Error('Cannot repaint root panel: no container attached');
- this.container.appendChild(this.getFrame());
+TimeAxis.prototype.redraw = function () {
+ var options = this.options,
+ props = this.props,
+ foreground = this.dom.foreground,
+ 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);
- this._initWatch();
-}
+ // calculate character width and height
+ this._calculateCharSize();
-RootPanel.prototype = new Panel();
+ // 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;
-/**
- * Create the HTML DOM for the root panel
- */
-RootPanel.prototype._create = function _create() {
- // create frame
- this.frame = document.createElement('div');
+ // 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;
- // create event listeners for all interesting events, these events will be
- // emitted via emitter
- this.hammer = Hammer(this.frame, {
- prevent_default: true
- });
- this.listeners = {};
+ 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
- var me = this;
- var events = [
- 'touch', 'pinch', 'tap', 'doubletap', 'hold',
- 'dragstart', 'drag', 'dragend',
- 'mousewheel', 'DOMMouseScroll' // DOMMouseScroll is for Firefox
- ];
- events.forEach(function (event) {
- var listener = function () {
- var args = [event].concat(Array.prototype.slice.call(arguments, 0));
- me.emit.apply(me, args);
- };
- me.hammer.on(event, listener);
- me.listeners[event] = listener;
- });
-};
+ // 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);
-/**
- * Set options. Will extend the current options.
- * @param {Object} [options] Available parameters:
- * {String | function} [className]
- * {String | Number | function} [left]
- * {String | Number | function} [top]
- * {String | Number | function} [width]
- * {String | Number | function} [height]
- * {Boolean | function} [autoResize]
- */
-RootPanel.prototype.setOptions = function setOptions(options) {
- if (options) {
- util.extend(this.options, options);
+ foreground.style.height = this.props.height + 'px';
- this.repaint();
+ this._repaintLabels();
- this._initWatch();
+ // put DOM online again (at the same place)
+ if (foregroundNextSibling) {
+ parent.insertBefore(foreground, foregroundNextSibling);
}
-};
-
-/**
- * Get the frame of the root panel
- */
-RootPanel.prototype.getFrame = function getFrame() {
- return this.frame;
-};
-
-/**
- * Repaint the root panel
- */
-RootPanel.prototype.repaint = function repaint() {
- // update class name
- var options = this.options;
- var editable = options.editable.updateTime || options.editable.updateGroup;
- var className = 'vis timeline rootpanel ' + options.orientation + (editable ? ' editable' : '');
- if (options.className) className += ' ' + util.option.asString(className);
- this.frame.className = className;
-
- // repaint the child components
- var childsResized = this._repaintChilds();
-
- // update frame size
- this.frame.style.maxHeight = util.option.asSize(this.options.maxHeight, '');
- this.frame.style.minHeight = util.option.asSize(this.options.minHeight, '');
- this._updateSize();
-
- // if the root panel or any of its childs is resized, repaint again,
- // as other components may need to be resized accordingly
- var resized = this._isResized() || childsResized;
- if (resized) {
- setTimeout(this.repaint.bind(this), 0);
+ else {
+ parent.appendChild(foreground)
}
-};
-
-/**
- * Initialize watching when option autoResize is true
- * @private
- */
-RootPanel.prototype._initWatch = function _initWatch() {
- var autoResize = this.getOption('autoResize');
- if (autoResize) {
- this._watch();
+ if (backgroundNextSibling) {
+ this.body.dom.backgroundVertical.insertBefore(background, backgroundNextSibling);
}
else {
- this._unwatch();
+ this.body.dom.backgroundVertical.appendChild(background)
}
+
+ return this._isResized() || parentChanged;
};
/**
- * Watch for changes in the size of the frame. On resize, the Panel will
- * automatically redraw itself.
+ * Repaint major and minor text labels and vertical grid lines
* @private
*/
-RootPanel.prototype._watch = function _watch() {
- var me = this;
-
- this._unwatch();
-
- var checkSize = function checkSize() {
- var autoResize = me.getOption('autoResize');
- if (!autoResize) {
- // stop watching when the option autoResize is changed to false
- me._unwatch();
- return;
- }
-
- if (me.frame) {
- // check whether the frame is resized
- if ((me.frame.clientWidth != me.lastWidth) ||
- (me.frame.clientHeight != me.lastHeight)) {
- me.lastWidth = me.frame.clientWidth;
- me.lastHeight = me.frame.clientHeight;
- me.repaint();
- // TODO: emit a resize event instead?
- }
- }
- };
-
- // TODO: automatically cleanup the event listener when the frame is deleted
- util.addEventListener(window, 'resize', checkSize);
-
- this.watchTimer = setInterval(checkSize, 1000);
-};
-
-/**
- * Stop watching for a resize of the frame.
- * @private
- */
-RootPanel.prototype._unwatch = function _unwatch() {
- if (this.watchTimer) {
- clearInterval(this.watchTimer);
- this.watchTimer = undefined;
- }
-
- // TODO: remove event listener on window.resize
-};
-
-/**
- * A horizontal time axis
- * @param {Object} [options] See TimeAxis.setOptions for the available
- * options.
- * @constructor TimeAxis
- * @extends Component
- */
-function TimeAxis (options) {
- this.id = util.randomUUID();
-
- this.dom = {
- majorLines: [],
- majorTexts: [],
- minorLines: [],
- minorTexts: [],
- redundant: {
- majorLines: [],
- majorTexts: [],
- minorLines: [],
- minorTexts: []
- }
- };
- this.props = {
- range: {
- start: 0,
- end: 0,
- minimumStep: 0
- },
- lineTop: 0
- };
-
- this.options = options || {};
- this.defaultOptions = {
- orientation: 'bottom', // supported: 'top', 'bottom'
- // TODO: implement timeaxis orientations 'left' and 'right'
- showMinorLabels: true,
- showMajorLabels: true
- };
-
- this.range = null;
-
- // create the HTML DOM
- this._create();
-}
-
-TimeAxis.prototype = new Component();
-
-// TODO: comment options
-TimeAxis.prototype.setOptions = Component.prototype.setOptions;
-
-/**
- * Create the HTML DOM for the TimeAxis
- */
-TimeAxis.prototype._create = function _create() {
- this.frame = document.createElement('div');
-};
-
-/**
- * Set a range (start and end)
- * @param {Range | Object} range A Range or an object containing start and end.
- */
-TimeAxis.prototype.setRange = function (range) {
- if (!(range instanceof Range) && (!range || !range.start || !range.end)) {
- throw new TypeError('Range must be an instance of Range, ' +
- 'or an object containing start and end.');
- }
- this.range = range;
-};
-
-/**
- * Get the outer frame of the time axis
- * @return {HTMLElement} frame
- */
-TimeAxis.prototype.getFrame = function getFrame() {
- return this.frame;
-};
-
-/**
- * Repaint the component
- * @return {boolean} Returns true if the component is resized
- */
-TimeAxis.prototype.repaint = function () {
- var asSize = util.option.asSize,
- options = this.options,
- props = this.props,
- frame = this.frame;
-
- // update classname
- frame.className = 'timeaxis'; // TODO: add className from options if defined
-
- var parent = frame.parentNode;
- if (parent) {
- // calculate character width and height
- this._calculateCharSize();
-
- // TODO: recalculate sizes only needed when parent is resized or options is changed
- var orientation = this.getOption('orientation'),
- showMinorLabels = this.getOption('showMinorLabels'),
- showMajorLabels = this.getOption('showMajorLabels');
-
- // determine the width and height of the elemens for the axis
- var parentHeight = this.parent.height;
- props.minorLabelHeight = showMinorLabels ? props.minorCharHeight : 0;
- props.majorLabelHeight = showMajorLabels ? props.majorCharHeight : 0;
- this.height = props.minorLabelHeight + props.majorLabelHeight;
- this.width = frame.offsetWidth; // TODO: only update the width when the frame is resized?
-
- props.minorLineHeight = parentHeight + props.minorLabelHeight;
- props.minorLineWidth = 1; // TODO: really calculate width
- props.majorLineHeight = parentHeight + this.height;
- props.majorLineWidth = 1; // TODO: really calculate width
-
- // take frame offline while updating (is almost twice as fast)
- var beforeChild = frame.nextSibling;
- parent.removeChild(frame);
-
- // TODO: top/bottom positioning should be determined by options set in the Timeline, not here
- if (orientation == 'top') {
- frame.style.top = '0';
- frame.style.left = '0';
- frame.style.bottom = '';
- frame.style.width = asSize(options.width, '100%');
- frame.style.height = this.height + 'px';
- }
- else { // bottom
- frame.style.top = '';
- frame.style.bottom = '0';
- frame.style.left = '0';
- frame.style.width = asSize(options.width, '100%');
- frame.style.height = this.height + 'px';
- }
-
- this._repaintLabels();
-
- this._repaintLine();
-
- // put frame online again
- if (beforeChild) {
- parent.insertBefore(frame, beforeChild);
- }
- else {
- parent.appendChild(frame)
- }
- }
-
- return this._isResized();
-};
-
-/**
- * Repaint major and minor text labels and vertical grid lines
- * @private
- */
-TimeAxis.prototype._repaintLabels = function () {
- var orientation = this.getOption('orientation');
+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.range.start, 'Number'),
- end = util.convert(this.range.end, 'Number'),
- minimumStep = this.options.toTime((this.props.minorCharWidth || 10) * 7).valueOf()
- -this.options.toTime(0).valueOf();
+ var start = util.convert(this.body.range.start, 'Number'),
+ end = util.convert(this.body.range.end, 'Number'),
+ minimumStep = this.body.util.toTime((this.props.minorCharWidth || 10) * 7).valueOf()
+ -this.body.util.toTime(0).valueOf();
var step = new TimeStep(new Date(start), new Date(end), minimumStep);
this.step = step;
@@ -4227,16 +3861,16 @@ TimeAxis.prototype._repaintLabels = function () {
while (step.hasNext() && max < 1000) {
max++;
var cur = step.getCurrent(),
- x = this.options.toScreen(cur),
+ x = this.body.util.toScreen(cur),
isMajor = step.isMajor();
// TODO: lines must have a width, such that we can create css backgrounds
- if (this.getOption('showMinorLabels')) {
+ if (this.options.showMinorLabels) {
this._repaintMinorText(x, step.getLabelMinor(), orientation);
}
- if (isMajor && this.getOption('showMajorLabels')) {
+ if (isMajor && this.options.showMajorLabels) {
if (x > 0) {
if (xFirstMajorLabel == undefined) {
xFirstMajorLabel = x;
@@ -4253,8 +3887,8 @@ TimeAxis.prototype._repaintLabels = function () {
}
// create a major label on the left when needed
- if (this.getOption('showMajorLabels')) {
- var leftTime = this.options.toTime(0),
+ 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
@@ -4291,20 +3925,13 @@ TimeAxis.prototype._repaintMinorText = function (x, text, orientation) {
label = document.createElement('div');
label.appendChild(content);
label.className = 'text minor';
- this.frame.appendChild(label);
+ this.dom.foreground.appendChild(label);
}
this.dom.minorTexts.push(label);
label.childNodes[0].nodeValue = text;
- if (orientation == 'top') {
- label.style.top = this.props.majorLabelHeight + 'px';
- label.style.bottom = '';
- }
- else {
- label.style.top = '';
- label.style.bottom = this.props.majorLabelHeight + 'px';
- }
+ label.style.top = (orientation == 'top') ? (this.props.majorLabelHeight + 'px') : '0';
label.style.left = x + 'px';
//label.title = title; // TODO: this is a heavy operation
};
@@ -4326,21 +3953,14 @@ TimeAxis.prototype._repaintMajorText = function (x, text, orientation) {
label = document.createElement('div');
label.className = 'text major';
label.appendChild(content);
- this.frame.appendChild(label);
+ this.dom.foreground.appendChild(label);
}
this.dom.majorTexts.push(label);
label.childNodes[0].nodeValue = text;
//label.title = title; // TODO: this is a heavy operation
- if (orientation == 'top') {
- label.style.top = '0px';
- label.style.bottom = '';
- }
- else {
- label.style.top = '';
- label.style.bottom = '0px';
- }
+ label.style.top = (orientation == 'top') ? '0' : (this.props.minorLabelHeight + 'px');
label.style.left = x + 'px';
};
@@ -4358,18 +3978,16 @@ TimeAxis.prototype._repaintMinorLine = function (x, orientation) {
// create vertical line
line = document.createElement('div');
line.className = 'grid vertical minor';
- this.frame.appendChild(line);
+ this.dom.background.appendChild(line);
}
this.dom.minorLines.push(line);
var props = this.props;
if (orientation == 'top') {
- line.style.top = this.props.majorLabelHeight + 'px';
- line.style.bottom = '';
+ line.style.top = props.majorLabelHeight + 'px';
}
else {
- line.style.top = '';
- line.style.bottom = this.props.majorLabelHeight + 'px';
+ line.style.top = this.body.domProps.top.height + 'px';
}
line.style.height = props.minorLineHeight + 'px';
line.style.left = (x - props.minorLineWidth / 2) + 'px';
@@ -4389,72 +4007,28 @@ TimeAxis.prototype._repaintMajorLine = function (x, orientation) {
// create vertical line
line = document.createElement('DIV');
line.className = 'grid vertical major';
- this.frame.appendChild(line);
+ this.dom.background.appendChild(line);
}
this.dom.majorLines.push(line);
var props = this.props;
if (orientation == 'top') {
- line.style.top = '0px';
- line.style.bottom = '';
+ line.style.top = '0';
}
else {
- line.style.top = '';
- line.style.bottom = '0px';
+ line.style.top = this.body.domProps.top.height + 'px';
}
line.style.left = (x - props.majorLineWidth / 2) + 'px';
line.style.height = props.majorLineHeight + 'px';
};
-
-/**
- * Repaint the horizontal line for the axis
- * @private
- */
-TimeAxis.prototype._repaintLine = function() {
- var line = this.dom.line,
- frame = this.frame,
- orientation = this.getOption('orientation');
-
- // line before all axis elements
- if (this.getOption('showMinorLabels') || this.getOption('showMajorLabels')) {
- if (line) {
- // put this line at the end of all childs
- frame.removeChild(line);
- frame.appendChild(line);
- }
- else {
- // create the axis line
- line = document.createElement('div');
- line.className = 'grid horizontal major';
- frame.appendChild(line);
- this.dom.line = line;
- }
-
- if (orientation == 'top') {
- line.style.top = this.height + 'px';
- line.style.bottom = '';
- }
- else {
- line.style.top = '';
- line.style.bottom = this.height + 'px';
- }
- }
- else {
- if (line && line.parentNode) {
- line.parentNode.removeChild(line);
- delete this.dom.line;
- }
- }
-};
-
/**
* 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 repaint. Size may change, for
+ // 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
@@ -4464,7 +4038,7 @@ TimeAxis.prototype._calculateCharSize = function () {
this.dom.measureCharMinor.style.position = 'absolute';
this.dom.measureCharMinor.appendChild(document.createTextNode('0'));
- this.frame.appendChild(this.dom.measureCharMinor);
+ this.dom.foreground.appendChild(this.dom.measureCharMinor);
}
this.props.minorCharHeight = this.dom.measureCharMinor.clientHeight;
this.props.minorCharWidth = this.dom.measureCharMinor.clientWidth;
@@ -4476,7 +4050,7 @@ TimeAxis.prototype._calculateCharSize = function () {
this.dom.measureCharMajor.style.position = 'absolute';
this.dom.measureCharMajor.appendChild(document.createTextNode('0'));
- this.frame.appendChild(this.dom.measureCharMajor);
+ this.dom.foreground.appendChild(this.dom.measureCharMajor);
}
this.props.majorCharHeight = this.dom.measureCharMajor.clientHeight;
this.props.majorCharWidth = this.dom.measureCharMajor.clientWidth;
@@ -4488,40 +4062,40 @@ TimeAxis.prototype._calculateCharSize = function () {
* @param {Date} date the date to be snapped.
* @return {Date} snappedDate
*/
-TimeAxis.prototype.snap = function snap (date) {
+TimeAxis.prototype.snap = function(date) {
return this.step.snap(date);
};
/**
* A current time bar
- * @param {Range} range
+ * @param {{range: Range, dom: Object, domProps: Object}} body
* @param {Object} [options] Available parameters:
* {Boolean} [showCurrentTime]
* @constructor CurrentTime
* @extends Component
*/
-function CurrentTime (range, options) {
- this.id = util.randomUUID();
+function CurrentTime (body, options) {
+ this.body = body;
- this.range = range;
- this.options = options || {};
+ // default options
this.defaultOptions = {
- showCurrentTime: false
+ showCurrentTime: true
};
+ this.options = util.extend({}, this.defaultOptions);
this._create();
+
+ this.setOptions(options);
}
CurrentTime.prototype = new Component();
-CurrentTime.prototype.setOptions = Component.prototype.setOptions;
-
/**
* Create the HTML DOM for the current time bar
* @private
*/
-CurrentTime.prototype._create = function _create () {
+CurrentTime.prototype._create = function() {
var bar = document.createElement('div');
bar.className = 'currenttime';
bar.style.position = 'absolute';
@@ -4532,25 +4106,57 @@ CurrentTime.prototype._create = function _create () {
};
/**
- * Get the frame element of the current time bar
- * @returns {HTMLElement} frame
+ * 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 options for the component. Options will be merged in current options.
+ * @param {Object} options Available parameters:
+ * {boolean} [showCurrentTime]
*/
-CurrentTime.prototype.getFrame = function getFrame() {
- return this.bar;
+CurrentTime.prototype.setOptions = function(options) {
+ if (options) {
+ // copy all options that we know
+ util.selectiveExtend(['showCurrentTime'], this.options, options);
+ }
};
/**
* Repaint the component
* @return {boolean} Returns true if the component is resized
*/
-CurrentTime.prototype.repaint = function repaint() {
- var parent = this.parent;
+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);
- var now = new Date();
- var x = this.options.toScreen(now);
+ this.start();
+ }
- this.bar.style.left = x + 'px';
- this.bar.title = 'Current time: ' + now;
+ var now = new Date();
+ var x = this.body.util.toScreen(now);
+
+ this.bar.style.left = x + 'px';
+ this.bar.title = 'Current time: ' + now;
+ }
+ else {
+ // remove the line from the DOM
+ if (this.bar.parentNode) {
+ this.bar.parentNode.removeChild(this.bar);
+ }
+ this.stop();
+ }
return false;
};
@@ -4558,19 +4164,19 @@ CurrentTime.prototype.repaint = function repaint() {
/**
* Start auto refreshing the current time bar
*/
-CurrentTime.prototype.start = function start() {
+CurrentTime.prototype.start = function() {
var me = this;
function update () {
me.stop();
// determine interval to refresh
- var scale = me.range.conversion(me.parent.width).scale;
+ 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.repaint();
+ me.redraw();
// start a timer to adjust for the new time
me.currentTimeTimer = setTimeout(update, interval);
@@ -4582,7 +4188,7 @@ CurrentTime.prototype.start = function start() {
/**
* Stop auto refreshing the current time bar
*/
-CurrentTime.prototype.stop = function stop() {
+CurrentTime.prototype.stop = function() {
if (this.currentTimeTimer !== undefined) {
clearTimeout(this.currentTimeTimer);
delete this.currentTimeTimer;
@@ -4591,36 +4197,50 @@ CurrentTime.prototype.stop = function stop() {
/**
* A custom time bar
+ * @param {{range: Range, dom: Object}} body
* @param {Object} [options] Available parameters:
* {Boolean} [showCustomTime]
* @constructor CustomTime
* @extends Component
*/
-function CustomTime (options) {
- this.id = util.randomUUID();
+function CustomTime (body, options) {
+ this.body = body;
- this.options = options || {};
+ // default options
this.defaultOptions = {
showCustomTime: false
};
+ 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();
-CustomTime.prototype.setOptions = Component.prototype.setOptions;
+/**
+ * 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'], this.options, options);
+ }
+};
/**
* Create the DOM for the custom time
* @private
*/
-CustomTime.prototype._create = function _create () {
+CustomTime.prototype._create = function() {
var bar = document.createElement('div');
bar.className = 'customtime';
bar.style.position = 'absolute';
@@ -4646,22 +4266,44 @@ CustomTime.prototype._create = function _create () {
};
/**
- * Get the frame element of the custom time bar
- * @returns {HTMLElement} frame
+ * Destroy the CustomTime bar
*/
-CustomTime.prototype.getFrame = function getFrame() {
- return this.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;
};
/**
* Repaint the component
* @return {boolean} Returns true if the component is resized
*/
-CustomTime.prototype.repaint = function () {
- var x = this.options.toScreen(this.customTime);
+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);
- this.bar.style.left = x + 'px';
- this.bar.title = 'Time: ' + this.customTime;
+ this.bar.style.left = x + 'px';
+ this.bar.title = 'Time: ' + this.customTime;
+ }
+ else {
+ // remove the line from the DOM
+ if (this.bar.parentNode) {
+ this.bar.parentNode.removeChild(this.bar);
+ }
+ }
return false;
};
@@ -4672,7 +4314,7 @@ CustomTime.prototype.repaint = function () {
*/
CustomTime.prototype.setCustomTime = function(time) {
this.customTime = new Date(time.valueOf());
- this.repaint();
+ this.redraw();
};
/**
@@ -4705,13 +4347,13 @@ CustomTime.prototype._onDrag = function (event) {
if (!this.eventParams.dragging) return;
var deltaX = event.gesture.deltaX,
- x = this.options.toScreen(this.eventParams.customTime) + deltaX,
- time = this.options.toTime(x);
+ x = this.body.util.toScreen(this.eventParams.customTime) + deltaX,
+ time = this.body.util.toTime(x);
this.setCustomTime(time);
// fire a timechange event
- this.emit('timechange', {
+ this.body.emitter.emit('timechange', {
time: new Date(this.customTime.valueOf())
});
@@ -4728,7 +4370,7 @@ CustomTime.prototype._onDragEnd = function (event) {
if (!this.eventParams.dragging) return;
// fire a timechanged event
- this.emit('timechanged', {
+ this.body.emitter.emit('timechanged', {
time: new Date(this.customTime.valueOf())
});
@@ -4742,55 +4384,92 @@ var UNGROUPED = '__ungrouped__'; // reserved group id for ungrouped items
* 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 {Panel} backgroundPanel Panel which can be used to display the
- * vertical lines of box items.
- * @param {Panel} axisPanel Panel on the axis where the dots of box-items
- * can be displayed.
- * @param {Panel} sidePanel Left side panel holding labels
+ * @param {{dom: Object, domProps: Object, emitter: Emitter, range: Range}} body
* @param {Object} [options] See ItemSet.setOptions for the available options.
* @constructor ItemSet
- * @extends Panel
+ * @extends Component
*/
-function ItemSet(backgroundPanel, axisPanel, sidePanel, options) {
- this.id = util.randomUUID();
+function ItemSet(body, options) {
+ this.body = body;
- // one options object is shared by this itemset and all its items
- this.options = options || {};
- this.backgroundPanel = backgroundPanel;
- this.axisPanel = axisPanel;
- this.sidePanel = sidePanel;
- this.itemOptions = Object.create(this.options);
+ this.defaultOptions = {
+ type: 'box',
+ orientation: 'bottom', // 'top' or 'bottom'
+ align: 'center', // 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);
+ },
+
+ margin: {
+ item: 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
- this.range = null; // Range or Object {start: number, end: number}
// listeners for the DataSet of the items
this.itemListeners = {
'add': function (event, params, senderId) {
- if (senderId != me.id) me._onAdd(params.items);
+ me._onAdd(params.items);
},
'update': function (event, params, senderId) {
- if (senderId != me.id) me._onUpdate(params.items);
+ me._onUpdate(params.items);
},
'remove': function (event, params, senderId) {
- if (senderId != me.id) me._onRemove(params.items);
+ me._onRemove(params.items);
}
};
// listeners for the DataSet of the groups
this.groupListeners = {
'add': function (event, params, senderId) {
- if (senderId != me.id) me._onAddGroups(params.items);
+ me._onAddGroups(params.items);
},
'update': function (event, params, senderId) {
- if (senderId != me.id) me._onUpdateGroups(params.items);
+ me._onUpdateGroups(params.items);
},
'remove': function (event, params, senderId) {
- if (senderId != me.id) me._onRemoveGroups(params.items);
+ me._onRemoveGroups(params.items);
}
};
@@ -4799,15 +4478,17 @@ function ItemSet(backgroundPanel, axisPanel, sidePanel, options) {
this.groupIds = [];
this.selection = []; // list with the ids of all selected nodes
- this.stackDirty = true; // if true, all items will be restacked on next repaint
+ 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);
}
-ItemSet.prototype = new Panel();
+ItemSet.prototype = new Component();
// available item types will be registered here
ItemSet.types = {
@@ -4820,15 +4501,16 @@ ItemSet.types = {
/**
* Create the HTML DOM for the ItemSet
*/
-ItemSet.prototype._create = function _create(){
+ItemSet.prototype._create = function(){
var frame = document.createElement('div');
+ frame.className = 'itemset';
frame['timeline-itemset'] = this;
- this.frame = frame;
+ this.dom.frame = frame;
// create background panel
var background = document.createElement('div');
background.className = 'background';
- this.backgroundPanel.frame.appendChild(background);
+ frame.appendChild(background);
this.dom.background = background;
// create foreground panel
@@ -4841,33 +4523,46 @@ ItemSet.prototype._create = function _create(){
var axis = document.createElement('div');
axis.className = 'axis';
this.dom.axis = axis;
- this.axisPanel.frame.appendChild(axis);
// create labelset
var labelSet = document.createElement('div');
labelSet.className = 'labelset';
this.dom.labelSet = labelSet;
- this.sidePanel.frame.appendChild(labelSet);
// create ungrouped Group
this._updateUngrouped();
// attach event listeners
- // TODO: use event listeners from the rootpanel to improve performance?
- this.hammer = Hammer(frame, {
+ // 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, {
prevent_default: 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();
};
/**
* Set options for the ItemSet. Existing options will be extended/overwritten.
* @param {Object} [options] The following options are available:
- * {String | function} [className]
- * class name for the itemset
- * {String} [type]
+ * {String} type
* Default type for the items. Choose from 'box'
* (default), 'point', or 'range'. The default
* Style can be overwritten by individual items.
@@ -4878,44 +4573,129 @@ ItemSet.prototype._create = function _create(){
* {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
* Margin between items in pixels. 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.
- * {Function} snap
- * Function to let items snap to nice dates when
- * dragging items.
- */
-ItemSet.prototype.setOptions = function setOptions(options) {
- Component.prototype.setOptions.call(this, options);
+ * {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'];
+ 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 = options.margin;
+ }
+ else if (typeof options.margin === 'object'){
+ util.selectiveExtend(['axis', 'item'], this.options.margin, options.margin);
+ }
+ }
+
+ 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) {
+ if (name in options) {
+ var fn = options[name];
+ if (!(fn instanceof Function) || fn.length != 2) {
+ throw new Error('option ' + name + ' must be a function ' + name + '(item, callback)');
+ }
+ this.options[name] = fn;
+ }
+ }).bind(this);
+ ['onAdd', 'onUpdate', 'onRemove', 'onMove'].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 repaint
+ * Mark the ItemSet dirty so it will refresh everything with next redraw
*/
-ItemSet.prototype.markDirty = function markDirty() {
+ItemSet.prototype.markDirty = function() {
this.groupIds = [];
this.stackDirty = true;
};
+/**
+ * Destroy the ItemSet
+ */
+ItemSet.prototype.destroy = function() {
+ this.hide();
+ this.setItems(null);
+ this.setGroups(null);
+
+ this.hammer = null;
+
+ this.body = null;
+ this.conversion = null;
+};
+
/**
* Hide the component from the DOM
*/
-ItemSet.prototype.hide = function hide() {
+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);
}
- // remove the background with vertical lines
- 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);
@@ -4926,33 +4706,21 @@ ItemSet.prototype.hide = function hide() {
* Show the component in the DOM (when not already visible).
* @return {Boolean} changed
*/
-ItemSet.prototype.show = function show() {
- // show axis with dots
- if (!this.dom.axis.parentNode) {
- this.axisPanel.frame.appendChild(this.dom.axis);
+ItemSet.prototype.show = function() {
+ // show frame containing the items
+ if (!this.dom.frame.parentNode) {
+ this.body.dom.center.appendChild(this.dom.frame);
}
- // show background with vertical lines
- if (!this.dom.background.parentNode) {
- this.backgroundPanel.frame.appendChild(this.dom.background);
+ // show axis with dots
+ if (!this.dom.axis.parentNode) {
+ this.body.dom.backgroundVertical.appendChild(this.dom.axis);
}
// show labelset containing labels
if (!this.dom.labelSet.parentNode) {
- this.sidePanel.frame.appendChild(this.dom.labelSet);
- }
-};
-
-/**
- * Set range (start and end).
- * @param {Range | Object} range A Range or an object containing start and end.
- */
-ItemSet.prototype.setRange = function setRange(range) {
- if (!(range instanceof Range) && (!range || !range.start || !range.end)) {
- throw new TypeError('Range must be an instance of Range, ' +
- 'or an object containing start and end.');
+ this.body.dom.left.appendChild(this.dom.labelSet);
}
- this.range = range;
};
/**
@@ -4962,7 +4730,7 @@ ItemSet.prototype.setRange = function setRange(range) {
* selected. If ids is an empty array, all items will be
* unselected.
*/
-ItemSet.prototype.setSelection = function setSelection(ids) {
+ItemSet.prototype.setSelection = function(ids) {
var i, ii, id, item;
if (ids) {
@@ -4994,7 +4762,7 @@ ItemSet.prototype.setSelection = function setSelection(ids) {
* Get the selected items by their id
* @return {Array} ids The ids of the selected items
*/
-ItemSet.prototype.getSelection = function getSelection() {
+ItemSet.prototype.getSelection = function() {
return this.selection.concat([]);
};
@@ -5003,7 +4771,7 @@ ItemSet.prototype.getSelection = function getSelection() {
* @param {String | Number} id
* @private
*/
-ItemSet.prototype._deselect = function _deselect(id) {
+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!
@@ -5013,51 +4781,35 @@ ItemSet.prototype._deselect = function _deselect(id) {
}
};
-/**
- * Return the item sets frame
- * @returns {HTMLElement} frame
- */
-ItemSet.prototype.getFrame = function getFrame() {
- return this.frame;
-};
-
/**
* Repaint the component
* @return {boolean} Returns true if the component is resized
*/
-ItemSet.prototype.repaint = function repaint() {
+ItemSet.prototype.redraw = function() {
var margin = this.options.margin,
- range = this.range,
+ range = this.body.range,
asSize = util.option.asSize,
- asString = util.option.asString,
options = this.options,
- orientation = this.getOption('orientation'),
+ orientation = options.orientation,
resized = false,
- frame = this.frame;
-
- // TODO: document this feature to specify one margin for both item and axis distance
- if (typeof margin === 'number') {
- margin = {
- item: margin,
- axis: margin
- };
- }
+ frame = this.dom.frame,
+ editable = options.editable.updateTime || options.editable.updateGroup;
- // update className
- frame.className = 'itemset' + (options.className ? (' ' + asString(options.className)) : '');
+ // update class name
+ frame.className = 'itemset' + (editable ? ' editable' : '');
// reorder the groups (if needed)
resized = this._orderGroups() || resized;
// 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 = this.range.end - this.range.start;
- var zoomed = (visibleInterval != this.lastVisibleInterval) || (this.width != this.lastWidth);
+ 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.lastWidth = this.width;
+ this.props.lastWidth = this.props.width;
- // repaint all groups
+ // redraw all groups
var restack = this.stackDirty,
firstGroup = this._firstGroup(),
firstMargin = {
@@ -5072,34 +4824,27 @@ ItemSet.prototype.repaint = function repaint() {
minHeight = margin.axis + margin.item;
util.forEach(this.groups, function (group) {
var groupMargin = (group == firstGroup) ? firstMargin : nonFirstMargin;
- resized = group.repaint(range, groupMargin, restack) || resized;
+ var groupResized = group.redraw(range, groupMargin, restack);
+ resized = groupResized || resized;
height += group.height;
});
height = Math.max(height, minHeight);
this.stackDirty = false;
- // reposition frame
- frame.style.left = asSize(options.left, '');
- frame.style.right = asSize(options.right, '');
- frame.style.top = asSize((orientation == 'top') ? '0' : '');
- frame.style.bottom = asSize((orientation == 'top') ? '' : '0');
- frame.style.width = asSize(options.width, '100%');
+ // update frame height
frame.style.height = asSize(height);
- //frame.style.height = asSize('height' in options ? options.height : height); // TODO: reckon with height
// calculate actual size and position
- this.top = frame.offsetTop;
- this.left = frame.offsetLeft;
- this.width = frame.offsetWidth;
- this.height = height;
+ this.props.top = frame.offsetTop;
+ this.props.left = frame.offsetLeft;
+ this.props.width = frame.offsetWidth;
+ this.props.height = height;
// reposition axis
- this.dom.axis.style.left = asSize(options.left, '0');
- this.dom.axis.style.right = asSize(options.right, '');
- this.dom.axis.style.width = asSize(options.width, '100%');
- this.dom.axis.style.height = asSize(0);
- this.dom.axis.style.top = asSize((orientation == 'top') ? '0' : '');
- this.dom.axis.style.bottom = asSize((orientation == 'top') ? '' : '0');
+ 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 = this.body.domProps.border.left + 'px';
// check if this component is resized
resized = this._isResized() || resized;
@@ -5112,7 +4857,7 @@ ItemSet.prototype.repaint = function repaint() {
* @return {Group | null} firstGroup
* @private
*/
-ItemSet.prototype._firstGroup = function _firstGroup() {
+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];
@@ -5125,7 +4870,7 @@ ItemSet.prototype._firstGroup = function _firstGroup() {
* there are no groups specified.
* @protected
*/
-ItemSet.prototype._updateUngrouped = function _updateUngrouped() {
+ItemSet.prototype._updateUngrouped = function() {
var ungrouped = this.groups[UNGROUPED];
if (this.groupsData) {
@@ -5149,40 +4894,16 @@ ItemSet.prototype._updateUngrouped = function _updateUngrouped() {
}
}
- ungrouped.show();
- }
- }
-};
-
-/**
- * Get the foreground container element
- * @return {HTMLElement} foreground
- */
-ItemSet.prototype.getForeground = function getForeground() {
- return this.dom.foreground;
-};
-
-/**
- * Get the background container element
- * @return {HTMLElement} background
- */
-ItemSet.prototype.getBackground = function getBackground() {
- return this.dom.background;
-};
-
-/**
- * Get the axis container element
- * @return {HTMLElement} axis
- */
-ItemSet.prototype.getAxis = function getAxis() {
- return this.dom.axis;
+ ungrouped.show();
+ }
+ }
};
/**
* Get the element for the labelset
* @return {HTMLElement} labelSet
*/
-ItemSet.prototype.getLabelSet = function getLabelSet() {
+ItemSet.prototype.getLabelSet = function() {
return this.dom.labelSet;
};
@@ -5190,7 +4911,7 @@ ItemSet.prototype.getLabelSet = function getLabelSet() {
* Set items
* @param {vis.DataSet | null} items
*/
-ItemSet.prototype.setItems = function setItems(items) {
+ItemSet.prototype.setItems = function(items) {
var me = this,
ids,
oldItemsData = this.itemsData;
@@ -5209,7 +4930,7 @@ ItemSet.prototype.setItems = function setItems(items) {
if (oldItemsData) {
// unsubscribe from old dataset
util.forEach(this.itemListeners, function (callback, event) {
- oldItemsData.unsubscribe(event, callback);
+ oldItemsData.off(event, callback);
});
// remove all drawn items
@@ -5237,7 +4958,7 @@ ItemSet.prototype.setItems = function setItems(items) {
* Get the current items
* @returns {vis.DataSet | null}
*/
-ItemSet.prototype.getItems = function getItems() {
+ItemSet.prototype.getItems = function() {
return this.itemsData;
};
@@ -5245,7 +4966,7 @@ ItemSet.prototype.getItems = function getItems() {
* Set groups
* @param {vis.DataSet} groups
*/
-ItemSet.prototype.setGroups = function setGroups(groups) {
+ItemSet.prototype.setGroups = function(groups) {
var me = this,
ids;
@@ -5258,7 +4979,7 @@ ItemSet.prototype.setGroups = function setGroups(groups) {
// remove all drawn groups
ids = this.groupsData.getIds();
this.groupsData = null;
- this._onRemoveGroups(ids); // note: this will cause a repaint
+ this._onRemoveGroups(ids); // note: this will cause a redraw
}
// replace the dataset
@@ -5290,14 +5011,14 @@ ItemSet.prototype.setGroups = function setGroups(groups) {
// update the order of all items in each group
this._order();
- this.emit('change');
+ this.body.emitter.emit('change');
};
/**
* Get the current groups
* @returns {vis.DataSet | null} groups
*/
-ItemSet.prototype.getGroups = function getGroups() {
+ItemSet.prototype.getGroups = function() {
return this.groupsData;
};
@@ -5305,7 +5026,7 @@ ItemSet.prototype.getGroups = function getGroups() {
* Remove an item by its id
* @param {String | Number} id
*/
-ItemSet.prototype.removeItem = function removeItem (id) {
+ItemSet.prototype.removeItem = function(id) {
var item = this.itemsData.get(id),
dataset = this._myDataSet();
@@ -5326,14 +5047,12 @@ ItemSet.prototype.removeItem = function removeItem (id) {
* @param {Number[]} ids
* @protected
*/
-ItemSet.prototype._onUpdate = function _onUpdate(ids) {
- var me = this,
- items = this.items,
- itemOptions = this.itemOptions;
+ItemSet.prototype._onUpdate = function(ids) {
+ var me = this;
ids.forEach(function (id) {
- var itemData = me.itemsData.get(id),
- item = items[id],
+ var itemData = me.itemsData.get(id, me.itemOptions),
+ item = me.items[id],
type = itemData.type ||
(itemData.start && itemData.end && 'range') ||
me.options.type ||
@@ -5356,7 +5075,7 @@ ItemSet.prototype._onUpdate = function _onUpdate(ids) {
if (!item) {
// create item
if (constructor) {
- item = new constructor(itemData, me.options, itemOptions);
+ item = new constructor(itemData, me.conversion, me.options);
item.id = id; // TODO: not so nice setting id afterwards
me._addItem(item);
}
@@ -5367,8 +5086,8 @@ ItemSet.prototype._onUpdate = function _onUpdate(ids) {
});
this._order();
- this.stackDirty = true; // force re-stacking of all items next repaint
- this.emit('change');
+ this.stackDirty = true; // force re-stacking of all items next redraw
+ this.body.emitter.emit('change');
};
/**
@@ -5383,7 +5102,7 @@ ItemSet.prototype._onAdd = ItemSet.prototype._onUpdate;
* @param {Number[]} ids
* @protected
*/
-ItemSet.prototype._onRemove = function _onRemove(ids) {
+ItemSet.prototype._onRemove = function(ids) {
var count = 0;
var me = this;
ids.forEach(function (id) {
@@ -5397,8 +5116,8 @@ ItemSet.prototype._onRemove = function _onRemove(ids) {
if (count) {
// update order
this._order();
- this.stackDirty = true; // force re-stacking of all items next repaint
- this.emit('change');
+ this.stackDirty = true; // force re-stacking of all items next redraw
+ this.body.emitter.emit('change');
}
};
@@ -5406,7 +5125,7 @@ ItemSet.prototype._onRemove = function _onRemove(ids) {
* Update the order of item in all groups
* @private
*/
-ItemSet.prototype._order = function _order() {
+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) {
@@ -5419,7 +5138,7 @@ ItemSet.prototype._order = function _order() {
* @param {Number[]} ids
* @private
*/
-ItemSet.prototype._onUpdateGroups = function _onUpdateGroups(ids) {
+ItemSet.prototype._onUpdateGroups = function(ids) {
this._onAddGroups(ids);
};
@@ -5428,7 +5147,7 @@ ItemSet.prototype._onUpdateGroups = function _onUpdateGroups(ids) {
* @param {Number[]} ids
* @private
*/
-ItemSet.prototype._onAddGroups = function _onAddGroups(ids) {
+ItemSet.prototype._onAddGroups = function(ids) {
var me = this;
ids.forEach(function (id) {
@@ -5468,7 +5187,7 @@ ItemSet.prototype._onAddGroups = function _onAddGroups(ids) {
}
});
- this.emit('change');
+ this.body.emitter.emit('change');
};
/**
@@ -5476,7 +5195,7 @@ ItemSet.prototype._onAddGroups = function _onAddGroups(ids) {
* @param {Number[]} ids
* @private
*/
-ItemSet.prototype._onRemoveGroups = function _onRemoveGroups(ids) {
+ItemSet.prototype._onRemoveGroups = function(ids) {
var groups = this.groups;
ids.forEach(function (id) {
var group = groups[id];
@@ -5489,7 +5208,7 @@ ItemSet.prototype._onRemoveGroups = function _onRemoveGroups(ids) {
this.markDirty();
- this.emit('change');
+ this.body.emitter.emit('change');
};
/**
@@ -5532,7 +5251,7 @@ ItemSet.prototype._orderGroups = function () {
* @param {Item} item
* @private
*/
-ItemSet.prototype._addItem = function _addItem(item) {
+ItemSet.prototype._addItem = function(item) {
this.items[item.id] = item;
// add to group
@@ -5547,12 +5266,12 @@ ItemSet.prototype._addItem = function _addItem(item) {
* @param {Object} itemData
* @private
*/
-ItemSet.prototype._updateItem = function _updateItem(item, itemData) {
+ItemSet.prototype._updateItem = function(item, itemData) {
var oldGroupId = item.data.group;
item.data = itemData;
if (item.displayed) {
- item.repaint();
+ item.redraw();
}
// update group
@@ -5572,7 +5291,7 @@ ItemSet.prototype._updateItem = function _updateItem(item, itemData) {
* @param {Item} item
* @private
*/
-ItemSet.prototype._removeItem = function _removeItem(item) {
+ItemSet.prototype._removeItem = function(item) {
// remove from DOM
item.hide();
@@ -5595,7 +5314,7 @@ ItemSet.prototype._removeItem = function _removeItem(item) {
* @returns {Array}
* @private
*/
-ItemSet.prototype._constructByEndArray = function _constructByEndArray(array) {
+ItemSet.prototype._constructByEndArray = function(array) {
var endArray = [];
for (var i = 0; i < array.length; i++) {
@@ -5607,25 +5326,17 @@ ItemSet.prototype._constructByEndArray = function _constructByEndArray(array) {
};
/**
- * Get the width of the group labels
- * @return {Number} width
- */
-ItemSet.prototype.getLabelsWidth = function getLabelsWidth() {
- var width = 0;
-
- util.forEach(this.groups, function (group) {
- width = Math.max(width, group.getLabelWidth());
- });
-
- return width;
-};
-
-/**
- * Get the height of the itemsets background
- * @return {Number} height
+ * 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.getBackgroundHeight = function getBackgroundHeight() {
- return this.height;
+ItemSet.prototype._onTouch = function (event) {
+ // store the touched item, used in _onDragStart
+ this.touchParams.item = ItemSet.itemFromTarget(event);
};
/**
@@ -5638,7 +5349,7 @@ ItemSet.prototype._onDragStart = function (event) {
return;
}
- var item = ItemSet.itemFromTarget(event),
+ var item = this.touchParams.item || null,
me = this,
props;
@@ -5704,9 +5415,10 @@ ItemSet.prototype._onDragStart = function (event) {
*/
ItemSet.prototype._onDrag = function (event) {
if (this.touchParams.itemProps) {
- var snap = this.options.snap || null,
+ var range = this.body.range,
+ snap = this.body.util.snap || null,
deltaX = event.gesture.deltaX,
- scale = (this.width / (this.range.end - this.range.start)),
+ scale = (this.props.width / (range.end - range.start)),
offset = deltaX / scale;
// move
@@ -5738,8 +5450,8 @@ ItemSet.prototype._onDrag = function (event) {
// TODO: implement onMoving handler
- this.stackDirty = true; // force re-stacking of all items next repaint
- this.emit('change');
+ this.stackDirty = true; // force re-stacking of all items next redraw
+ this.body.emitter.emit('change');
event.stopPropagation();
}
@@ -5759,16 +5471,18 @@ ItemSet.prototype._onDragEnd = function (event) {
this.touchParams.itemProps.forEach(function (props) {
var id = props.item.id,
- itemData = me.itemsData.get(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.convert['start']);
+ 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.convert['end']);
+ 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);
@@ -5780,7 +5494,7 @@ ItemSet.prototype._onDragEnd = function (event) {
me.options.onMove(itemData, function (itemData) {
if (itemData) {
// apply changes
- itemData[dataset.fieldId] = id; // ensure the item contains its id (can be undefined)
+ itemData[dataset._fieldId] = id; // ensure the item contains its id (can be undefined)
changes.push(itemData);
}
else {
@@ -5788,8 +5502,8 @@ ItemSet.prototype._onDragEnd = function (event) {
if ('start' in props) props.item.data.start = props.start;
if ('end' in props) props.item.data.end = props.end;
- me.stackDirty = true; // force re-stacking of all items next repaint
- me.emit('change');
+ me.stackDirty = true; // force re-stacking of all items next redraw
+ me.body.emitter.emit('change');
}
});
}
@@ -5805,13 +5519,137 @@ ItemSet.prototype._onDragEnd = function (event) {
}
};
+/**
+ * Handle selecting/deselecting an item when tapping it
+ * @param {Event} event
+ * @private
+ */
+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: this.getSelection()
+ });
+ }
+
+ event.stopPropagation();
+};
+
+/**
+ * Handle creation and updates of an item on double tap
+ * @param event
+ * @private
+ */
+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
+
+ // 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.update(itemData);
+ }
+ });
+ }
+ else {
+ // add item
+ var xAbs = vis.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' || this.options.type == 'rangeoverflow') {
+ var end = this.body.util.toTime(x + this.props.width / 5);
+ newItem.end = snap ? snap(end) : end;
+ }
+
+ newItem[this.itemsData.fieldId] = util.randomUUID();
+
+ var group = ItemSet.groupFromTarget(event);
+ if (group) {
+ newItem.group = group.groupId;
+ }
+
+ // execute async handler to customize (or cancel) adding an item
+ this.options.onAdd(newItem, function (item) {
+ if (item) {
+ me.itemsData.add(newItem);
+ // TODO: need to trigger a redraw?
+ }
+ });
+ }
+};
+
+/**
+ * Handle selecting/deselecting multiple items when holding an item
+ * @param {Event} event
+ * @private
+ */
+ItemSet.prototype._onMultiSelectItem = function (event) {
+ if (!this.options.selectable) return;
+
+ var selection,
+ item = ItemSet.itemFromTarget(event);
+
+ if (item) {
+ // multi select items
+ selection = this.getSelection(); // 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()
+ });
+
+ event.stopPropagation();
+ }
+};
+
/**
* 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 itemFromTarget (event) {
+ItemSet.itemFromTarget = function(event) {
var target = event.target;
while (target) {
if (target.hasOwnProperty('timeline-item')) {
@@ -5829,7 +5667,7 @@ ItemSet.itemFromTarget = function itemFromTarget (event) {
* @param {Event} event
* @return {Group | null} group
*/
-ItemSet.groupFromTarget = function groupFromTarget (event) {
+ItemSet.groupFromTarget = function(event) {
var target = event.target;
while (target) {
if (target.hasOwnProperty('timeline-group')) {
@@ -5847,7 +5685,7 @@ ItemSet.groupFromTarget = function groupFromTarget (event) {
* @param {Event} event
* @return {ItemSet | null} item
*/
-ItemSet.itemSetFromTarget = function itemSetFromTarget (event) {
+ItemSet.itemSetFromTarget = function(event) {
var target = event.target;
while (target) {
if (target.hasOwnProperty('timeline-itemset')) {
@@ -5864,7 +5702,7 @@ ItemSet.itemSetFromTarget = function itemSetFromTarget (event) {
* @returns {null | DataSet} dataset
* @private
*/
-ItemSet.prototype._myDataSet = function _myDataSet() {
+ItemSet.prototype._myDataSet = function() {
// find the root DataSet
var dataset = this.itemsData;
while (dataset instanceof DataView) {
@@ -5876,17 +5714,18 @@ ItemSet.prototype._myDataSet = function _myDataSet() {
* @constructor Item
* @param {Object} data Object containing (optional) parameters type,
* start, end, content, group, className.
- * @param {Object} [options] Options to set initial property values
- * @param {Object} [defaultOptions] default options
+ * @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, options, defaultOptions) {
+function Item (data, conversion, options) {
this.id = null;
this.parent = null;
this.data = data;
this.dom = null;
+ this.conversion = conversion || {};
this.options = options || {};
- this.defaultOptions = defaultOptions || {};
this.selected = false;
this.displayed = false;
@@ -5901,24 +5740,24 @@ function Item (data, options, defaultOptions) {
/**
* Select current item
*/
-Item.prototype.select = function select() {
+Item.prototype.select = function() {
this.selected = true;
- if (this.displayed) this.repaint();
+ if (this.displayed) this.redraw();
};
/**
* Unselect current item
*/
-Item.prototype.unselect = function unselect() {
+Item.prototype.unselect = function() {
this.selected = false;
- if (this.displayed) this.repaint();
+ if (this.displayed) this.redraw();
};
/**
* Set a parent for the item
* @param {ItemSet | Group} parent
*/
-Item.prototype.setParent = function setParent(parent) {
+Item.prototype.setParent = function(parent) {
if (this.displayed) {
this.hide();
this.parent = parent;
@@ -5936,7 +5775,7 @@ Item.prototype.setParent = function setParent(parent) {
* @returns {{start: Number, end: Number}} range with a timestamp for start and end
* @returns {boolean} True if visible
*/
-Item.prototype.isVisible = function isVisible (range) {
+Item.prototype.isVisible = function(range) {
// Should be implemented by Item implementations
return false;
};
@@ -5945,7 +5784,7 @@ Item.prototype.isVisible = function isVisible (range) {
* Show the Item in the DOM (when not already visible)
* @return {Boolean} changed
*/
-Item.prototype.show = function show() {
+Item.prototype.show = function() {
return false;
};
@@ -5953,28 +5792,28 @@ Item.prototype.show = function show() {
* Hide the Item from the DOM (when visible)
* @return {Boolean} changed
*/
-Item.prototype.hide = function hide() {
+Item.prototype.hide = function() {
return false;
};
/**
* Repaint the item
*/
-Item.prototype.repaint = function repaint() {
+Item.prototype.redraw = function() {
// should be implemented by the item
};
/**
* Reposition the Item horizontally
*/
-Item.prototype.repositionX = function repositionX() {
+Item.prototype.repositionX = function() {
// should be implemented by the item
};
/**
* Reposition the Item vertically
*/
-Item.prototype.repositionY = function repositionY() {
+Item.prototype.repositionY = function() {
// should be implemented by the item
};
@@ -6016,11 +5855,12 @@ Item.prototype._repaintDeleteButton = function (anchor) {
* @extends Item
* @param {Object} data Object containing parameters start
* content, className.
- * @param {Object} [options] Options to set initial property values
- * @param {Object} [defaultOptions] default options
+ * @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 ItemBox (data, options, defaultOptions) {
+function ItemBox (data, conversion, options) {
this.props = {
dot: {
width: 0,
@@ -6039,17 +5879,17 @@ function ItemBox (data, options, defaultOptions) {
}
}
- Item.call(this, data, options, defaultOptions);
+ Item.call(this, data, conversion, options);
}
-ItemBox.prototype = new Item (null);
+ItemBox.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
*/
-ItemBox.prototype.isVisible = function isVisible (range) {
+ItemBox.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;
@@ -6059,7 +5899,7 @@ ItemBox.prototype.isVisible = function isVisible (range) {
/**
* Repaint the item
*/
-ItemBox.prototype.repaint = function repaint() {
+ItemBox.prototype.redraw = function() {
var dom = this.dom;
if (!dom) {
// create DOM
@@ -6088,21 +5928,21 @@ ItemBox.prototype.repaint = function repaint() {
// append DOM to parent DOM
if (!this.parent) {
- throw new Error('Cannot repaint item: no parent attached');
+ throw new Error('Cannot redraw item: no parent attached');
}
if (!dom.box.parentNode) {
- var foreground = this.parent.getForeground();
- if (!foreground) throw new Error('Cannot repaint time axis: parent has no foreground container element');
+ var foreground = this.parent.dom.foreground;
+ if (!foreground) throw new Error('Cannot redraw time axis: parent has no foreground container element');
foreground.appendChild(dom.box);
}
if (!dom.line.parentNode) {
- var background = this.parent.getBackground();
- if (!background) throw new Error('Cannot repaint time axis: parent has no background container element');
+ var background = this.parent.dom.background;
+ if (!background) throw new Error('Cannot redraw time axis: parent has no background container element');
background.appendChild(dom.line);
}
if (!dom.dot.parentNode) {
- var axis = this.parent.getAxis();
- if (!background) throw new Error('Cannot repaint time axis: parent has no axis container element');
+ var axis = this.parent.dom.axis;
+ if (!background) throw new Error('Cannot redraw time axis: parent has no axis container element');
axis.appendChild(dom.dot);
}
this.displayed = true;
@@ -6154,16 +5994,16 @@ ItemBox.prototype.repaint = function repaint() {
* Show the item in the DOM (when not already displayed). The items DOM will
* be created when needed.
*/
-ItemBox.prototype.show = function show() {
+ItemBox.prototype.show = function() {
if (!this.displayed) {
- this.repaint();
+ this.redraw();
}
};
/**
* Hide the item from the DOM (when visible)
*/
-ItemBox.prototype.hide = function hide() {
+ItemBox.prototype.hide = function() {
if (this.displayed) {
var dom = this.dom;
@@ -6182,9 +6022,9 @@ ItemBox.prototype.hide = function hide() {
* Reposition the item horizontally
* @Override
*/
-ItemBox.prototype.repositionX = function repositionX() {
- var start = this.defaultOptions.toScreen(this.data.start),
- align = this.options.align || this.defaultOptions.align,
+ItemBox.prototype.repositionX = function() {
+ var start = this.conversion.toScreen(this.data.start),
+ align = this.options.align,
left,
box = this.dom.box,
line = this.dom.line,
@@ -6216,27 +6056,26 @@ ItemBox.prototype.repositionX = function repositionX() {
* Reposition the item vertically
* @Override
*/
-ItemBox.prototype.repositionY = function repositionY () {
- var orientation = this.options.orientation || this.defaultOptions.orientation,
+ItemBox.prototype.repositionY = function() {
+ var orientation = this.options.orientation,
box = this.dom.box,
line = this.dom.line,
dot = this.dom.dot;
if (orientation == 'top') {
- box.style.top = (this.top || 0) + 'px';
- box.style.bottom = '';
+ box.style.top = (this.top || 0) + 'px';
- line.style.top = '0';
- line.style.bottom = '';
+ line.style.top = '0';
line.style.height = (this.parent.top + this.top + 1) + 'px';
+ line.style.bottom = '';
}
else { // orientation 'bottom'
- box.style.top = '';
- box.style.bottom = (this.top || 0) + 'px';
+ var itemSetHeight = this.parent.itemSet.props.height; // TODO: this is nasty
+ var lineHeight = itemSetHeight - this.parent.top - this.parent.height + this.top;
- line.style.top = (this.parent.top + this.parent.height - this.top - 1) + 'px';
+ box.style.top = (this.parent.height - this.top - this.height || 0) + 'px';
+ line.style.top = (itemSetHeight - lineHeight) + 'px';
line.style.bottom = '0';
- line.style.height = '';
}
dot.style.top = (-this.props.dot.height / 2) + 'px';
@@ -6247,11 +6086,12 @@ ItemBox.prototype.repositionY = function repositionY () {
* @extends Item
* @param {Object} data Object containing parameters start
* content, className.
- * @param {Object} [options] Options to set initial property values
- * @param {Object} [defaultOptions] default options
+ * @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 ItemPoint (data, options, defaultOptions) {
+function ItemPoint (data, conversion, options) {
this.props = {
dot: {
top: 0,
@@ -6271,17 +6111,17 @@ function ItemPoint (data, options, defaultOptions) {
}
}
- Item.call(this, data, options, defaultOptions);
+ Item.call(this, data, conversion, options);
}
-ItemPoint.prototype = new Item (null);
+ItemPoint.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
*/
-ItemPoint.prototype.isVisible = function isVisible (range) {
+ItemPoint.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;
@@ -6291,7 +6131,7 @@ ItemPoint.prototype.isVisible = function isVisible (range) {
/**
* Repaint the item
*/
-ItemPoint.prototype.repaint = function repaint() {
+ItemPoint.prototype.redraw = function() {
var dom = this.dom;
if (!dom) {
// create DOM
@@ -6300,7 +6140,7 @@ ItemPoint.prototype.repaint = function repaint() {
// background box
dom.point = document.createElement('div');
- // className is updated in repaint()
+ // className is updated in redraw()
// contents box, right from the dot
dom.content = document.createElement('div');
@@ -6317,12 +6157,12 @@ ItemPoint.prototype.repaint = function repaint() {
// append DOM to parent DOM
if (!this.parent) {
- throw new Error('Cannot repaint item: no parent attached');
+ throw new Error('Cannot redraw item: no parent attached');
}
if (!dom.point.parentNode) {
- var foreground = this.parent.getForeground();
+ var foreground = this.parent.dom.foreground;
if (!foreground) {
- throw new Error('Cannot repaint time axis: parent has no foreground container element');
+ throw new Error('Cannot redraw time axis: parent has no foreground container element');
}
foreground.appendChild(dom.point);
}
@@ -6381,16 +6221,16 @@ ItemPoint.prototype.repaint = function repaint() {
* Show the item in the DOM (when not already visible). The items DOM will
* be created when needed.
*/
-ItemPoint.prototype.show = function show() {
+ItemPoint.prototype.show = function() {
if (!this.displayed) {
- this.repaint();
+ this.redraw();
}
};
/**
* Hide the item from the DOM (when visible)
*/
-ItemPoint.prototype.hide = function hide() {
+ItemPoint.prototype.hide = function() {
if (this.displayed) {
if (this.dom.point.parentNode) {
this.dom.point.parentNode.removeChild(this.dom.point);
@@ -6407,8 +6247,8 @@ ItemPoint.prototype.hide = function hide() {
* Reposition the item horizontally
* @Override
*/
-ItemPoint.prototype.repositionX = function repositionX() {
- var start = this.defaultOptions.toScreen(this.data.start);
+ItemPoint.prototype.repositionX = function() {
+ var start = this.conversion.toScreen(this.data.start);
this.left = start - this.props.dot.width;
@@ -6420,17 +6260,15 @@ ItemPoint.prototype.repositionX = function repositionX() {
* Reposition the item vertically
* @Override
*/
-ItemPoint.prototype.repositionY = function repositionY () {
- var orientation = this.options.orientation || this.defaultOptions.orientation,
+ItemPoint.prototype.repositionY = function() {
+ var orientation = this.options.orientation,
point = this.dom.point;
if (orientation == 'top') {
point.style.top = this.top + 'px';
- point.style.bottom = '';
}
else {
- point.style.top = '';
- point.style.bottom = this.top + 'px';
+ point.style.top = (this.parent.height - this.top - this.height) + 'px';
}
};
@@ -6439,11 +6277,12 @@ ItemPoint.prototype.repositionY = function repositionY () {
* @extends Item
* @param {Object} data Object containing parameters start, end
* content, className.
- * @param {Object} [options] Options to set initial property values
- * @param {Object} [defaultOptions] default options
- * // TODO: describe available options
+ * @param {{toScreen: function, toTime: function}} conversion
+ * Conversion functions from time to screen and vice versa
+ * @param {Object} [options] Configuration options
+ * // TODO: describe options
*/
-function ItemRange (data, options, defaultOptions) {
+function ItemRange (data, conversion, options) {
this.props = {
content: {
width: 0
@@ -6460,10 +6299,10 @@ function ItemRange (data, options, defaultOptions) {
}
}
- Item.call(this, data, options, defaultOptions);
+ Item.call(this, data, conversion, options);
}
-ItemRange.prototype = new Item (null);
+ItemRange.prototype = new Item (null, null, null);
ItemRange.prototype.baseClassName = 'item range';
@@ -6472,7 +6311,7 @@ ItemRange.prototype.baseClassName = 'item range';
* @returns {{start: Number, end: Number}} range with a timestamp for start and end
* @returns {boolean} True if visible
*/
-ItemRange.prototype.isVisible = function isVisible (range) {
+ItemRange.prototype.isVisible = function(range) {
// determine visibility
return (this.data.start < range.end) && (this.data.end > range.start);
};
@@ -6480,7 +6319,7 @@ ItemRange.prototype.isVisible = function isVisible (range) {
/**
* Repaint the item
*/
-ItemRange.prototype.repaint = function repaint() {
+ItemRange.prototype.redraw = function() {
var dom = this.dom;
if (!dom) {
// create DOM
@@ -6489,7 +6328,7 @@ ItemRange.prototype.repaint = function repaint() {
// background box
dom.box = document.createElement('div');
- // className is updated in repaint()
+ // className is updated in redraw()
// contents box
dom.content = document.createElement('div');
@@ -6502,12 +6341,12 @@ ItemRange.prototype.repaint = function repaint() {
// append DOM to parent DOM
if (!this.parent) {
- throw new Error('Cannot repaint item: no parent attached');
+ throw new Error('Cannot redraw item: no parent attached');
}
if (!dom.box.parentNode) {
- var foreground = this.parent.getForeground();
+ var foreground = this.parent.dom.foreground;
if (!foreground) {
- throw new Error('Cannot repaint time axis: parent has no foreground container element');
+ throw new Error('Cannot redraw time axis: parent has no foreground container element');
}
foreground.appendChild(dom.box);
}
@@ -6557,9 +6396,9 @@ ItemRange.prototype.repaint = function repaint() {
* Show the item in the DOM (when not already visible). The items DOM will
* be created when needed.
*/
-ItemRange.prototype.show = function show() {
+ItemRange.prototype.show = function() {
if (!this.displayed) {
- this.repaint();
+ this.redraw();
}
};
@@ -6567,7 +6406,7 @@ ItemRange.prototype.show = function show() {
* Hide the item from the DOM (when visible)
* @return {Boolean} changed
*/
-ItemRange.prototype.hide = function hide() {
+ItemRange.prototype.hide = function() {
if (this.displayed) {
var box = this.dom.box;
@@ -6586,12 +6425,12 @@ ItemRange.prototype.hide = function hide() {
* Reposition the item horizontally
* @Override
*/
-ItemRange.prototype.repositionX = function repositionX() {
+ItemRange.prototype.repositionX = function() {
var props = this.props,
parentWidth = this.parent.width,
- start = this.defaultOptions.toScreen(this.data.start),
- end = this.defaultOptions.toScreen(this.data.end),
- padding = 'padding' in this.options ? this.options.padding : this.defaultOptions.padding,
+ start = this.conversion.toScreen(this.data.start),
+ end = this.conversion.toScreen(this.data.end),
+ padding = this.options.padding,
contentLeft;
// limit the width of the this, as browsers cannot draw very wide divs
@@ -6624,17 +6463,15 @@ ItemRange.prototype.repositionX = function repositionX() {
* Reposition the item vertically
* @Override
*/
-ItemRange.prototype.repositionY = function repositionY() {
- var orientation = this.options.orientation || this.defaultOptions.orientation,
+ItemRange.prototype.repositionY = function() {
+ var orientation = this.options.orientation,
box = this.dom.box;
if (orientation == 'top') {
box.style.top = this.top + 'px';
- box.style.bottom = '';
}
else {
- box.style.top = '';
- box.style.bottom = this.top + 'px';
+ box.style.top = (this.parent.height - this.top - this.height) + 'px';
}
};
@@ -6703,11 +6540,12 @@ ItemRange.prototype._repaintDragRight = function () {
* @extends ItemRange
* @param {Object} data Object containing parameters start, end
* content, className.
- * @param {Object} [options] Options to set initial property values
- * @param {Object} [defaultOptions] default options
- * // TODO: describe available options
+ * @param {{toScreen: function, toTime: function}} conversion
+ * Conversion functions from time to screen and vice versa
+ * @param {Object} [options] Configuration options
+ * // TODO: describe options
*/
-function ItemRangeOverflow (data, options, defaultOptions) {
+function ItemRangeOverflow (data, conversion, options) {
this.props = {
content: {
left: 0,
@@ -6715,10 +6553,10 @@ function ItemRangeOverflow (data, options, defaultOptions) {
}
};
- ItemRange.call(this, data, options, defaultOptions);
+ ItemRange.call(this, data, conversion, options);
}
-ItemRangeOverflow.prototype = new ItemRange (null);
+ItemRangeOverflow.prototype = new ItemRange (null, null, null);
ItemRangeOverflow.prototype.baseClassName = 'item rangeoverflow';
@@ -6726,11 +6564,10 @@ ItemRangeOverflow.prototype.baseClassName = 'item rangeoverflow';
* Reposition the item horizontally
* @Override
*/
-ItemRangeOverflow.prototype.repositionX = function repositionX() {
+ItemRangeOverflow.prototype.repositionX = function() {
var parentWidth = this.parent.width,
- start = this.defaultOptions.toScreen(this.data.start),
- end = this.defaultOptions.toScreen(this.data.end),
- padding = 'padding' in this.options ? this.options.padding : this.defaultOptions.padding,
+ start = this.conversion.toScreen(this.data.start),
+ end = this.conversion.toScreen(this.data.end),
contentLeft;
// limit the width of the this, as browsers cannot draw very wide divs
@@ -6774,6 +6611,7 @@ function Group (groupId, data, itemSet) {
height: 0
}
};
+ this.className = null;
this.items = {}; // items filtered by groupId of this group
this.visibleItems = []; // items currently visible in window
@@ -6807,8 +6645,10 @@ Group.prototype._create = function() {
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
@@ -6823,7 +6663,7 @@ Group.prototype._create = function() {
* Set the group data for this group
* @param {Object} data Group data, can contain properties content and className
*/
-Group.prototype.setData = function setData(data) {
+Group.prototype.setData = function(data) {
// update contents
var content = data && data.content;
if (content instanceof Element) {
@@ -6836,42 +6676,34 @@ Group.prototype.setData = function setData(data) {
this.dom.inner.innerHTML = this.groupId;
}
+ 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;
- if (className) {
+ var className = data && data.className || null;
+ if (className != this.className) {
+ if (this.className) {
+ util.removeClassName(this.dom.label, className);
+ util.removeClassName(this.dom.foreground, className);
+ util.removeClassName(this.dom.background, className);
+ util.removeClassName(this.dom.axis, 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);
}
};
-/**
- * Get the foreground container element
- * @return {HTMLElement} foreground
- */
-Group.prototype.getForeground = function getForeground() {
- return this.dom.foreground;
-};
-
-/**
- * Get the background container element
- * @return {HTMLElement} background
- */
-Group.prototype.getBackground = function getBackground() {
- return this.dom.background;
-};
-
-/**
- * Get the axis container element
- * @return {HTMLElement} axis
- */
-Group.prototype.getAxis = function getAxis() {
- return this.dom.axis;
-};
-
/**
* Get the width of the group label
* @return {number} width
*/
-Group.prototype.getLabelWidth = function getLabelWidth() {
+Group.prototype.getLabelWidth = function() {
return this.props.label.width;
};
@@ -6883,7 +6715,7 @@ Group.prototype.getLabelWidth = function getLabelWidth() {
* @param {boolean} [restack=false] Force restacking of all items
* @return {boolean} Returns true if the group is resized
*/
-Group.prototype.repaint = function repaint(range, margin, restack) {
+Group.prototype.redraw = function(range, margin, restack) {
var resized = false;
this.visibleItems = this._updateVisibleItems(this.orderedItems, this.visibleItems, range);
@@ -6896,7 +6728,7 @@ Group.prototype.repaint = function repaint(range, margin, restack) {
util.forEach(this.items, function (item) {
item.dirty = true;
- if (item.displayed) item.repaint();
+ if (item.displayed) item.redraw();
});
restack = true;
@@ -6909,10 +6741,6 @@ Group.prototype.repaint = function repaint(range, margin, restack) {
else { // no stacking
stack.nostack(this.visibleItems, margin);
}
- for (var i = 0, ii = this.visibleItems.length; i < ii; i++) {
- var item = this.visibleItems[i];
- item.repositionY();
- }
// recalculate the height of the group
var height;
@@ -6946,34 +6774,40 @@ Group.prototype.repaint = function repaint(range, margin, restack) {
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();
+ }
+
return resized;
};
/**
* Show this group: attach to the DOM
*/
-Group.prototype.show = function show() {
+Group.prototype.show = function() {
if (!this.dom.label.parentNode) {
- this.itemSet.getLabelSet().appendChild(this.dom.label);
+ this.itemSet.dom.labelSet.appendChild(this.dom.label);
}
if (!this.dom.foreground.parentNode) {
- this.itemSet.getForeground().appendChild(this.dom.foreground);
+ this.itemSet.dom.foreground.appendChild(this.dom.foreground);
}
if (!this.dom.background.parentNode) {
- this.itemSet.getBackground().appendChild(this.dom.background);
+ this.itemSet.dom.background.appendChild(this.dom.background);
}
if (!this.dom.axis.parentNode) {
- this.itemSet.getAxis().appendChild(this.dom.axis);
+ this.itemSet.dom.axis.appendChild(this.dom.axis);
}
};
/**
* Hide this group: remove from the DOM
*/
-Group.prototype.hide = function hide() {
+Group.prototype.hide = function() {
var label = this.dom.label;
if (label.parentNode) {
label.parentNode.removeChild(label);
@@ -6999,12 +6833,12 @@ Group.prototype.hide = function hide() {
* Add an item to the group
* @param {Item} item
*/
-Group.prototype.add = function add(item) {
+Group.prototype.add = function(item) {
this.items[item.id] = item;
item.setParent(this);
if (item instanceof ItemRange && this.visibleItems.indexOf(item) == -1) {
- var range = this.itemSet.range; // TODO: not nice accessing the range like this
+ var range = this.itemSet.body.range; // TODO: not nice accessing the range like this
this._checkIfVisible(item, this.visibleItems, range);
}
};
@@ -7013,7 +6847,7 @@ Group.prototype.add = function add(item) {
* Remove an item from the group
* @param {Item} item
*/
-Group.prototype.remove = function remove(item) {
+Group.prototype.remove = function(item) {
delete this.items[item.id];
item.setParent(this.itemSet);
@@ -7028,14 +6862,14 @@ Group.prototype.remove = function remove(item) {
* Remove an item from the corresponding DataSet
* @param {Item} item
*/
-Group.prototype.removeFromDataSet = function removeFromDataSet(item) {
+Group.prototype.removeFromDataSet = function(item) {
this.itemSet.removeItem(item.id);
};
/**
* Reorder the items
*/
-Group.prototype.order = function order() {
+Group.prototype.order = function() {
var array = util.toArray(this.items);
this.orderedItems.byStart = array;
this.orderedItems.byEnd = this._constructByEndArray(array);
@@ -7050,7 +6884,7 @@ Group.prototype.order = function order() {
* @returns {ItemRange[]}
* @private
*/
-Group.prototype._constructByEndArray = function _constructByEndArray(array) {
+Group.prototype._constructByEndArray = function(array) {
var endArray = [];
for (var i = 0; i < array.length; i++) {
@@ -7069,7 +6903,7 @@ Group.prototype._constructByEndArray = function _constructByEndArray(array) {
* @return {Item[]} visibleItems The new visible items.
* @private
*/
-Group.prototype._updateVisibleItems = function _updateVisibleItems(orderedItems, visibleItems, range) {
+Group.prototype._updateVisibleItems = function(orderedItems, visibleItems, range) {
var initialPosByStart,
newVisibleItems = [],
i;
@@ -7132,7 +6966,7 @@ Group.prototype._updateVisibleItems = function _updateVisibleItems(orderedItems,
* @returns {number}
* @private
*/
-Group.prototype._binarySearch = function _binarySearch(orderedItems, range, byEnd) {
+Group.prototype._binarySearch = function(orderedItems, range, byEnd) {
var array = [];
var byTime = byEnd ? 'end' : 'start';
if (byEnd == true) {array = orderedItems.byEnd; }
@@ -7193,7 +7027,7 @@ Group.prototype._binarySearch = function _binarySearch(orderedItems, range, byEn
* @returns {boolean}
* @private
*/
-Group.prototype._checkIfInvisible = function _checkIfInvisible(item, visibleItems, range) {
+Group.prototype._checkIfInvisible = function(item, visibleItems, range) {
if (item.isVisible(range)) {
if (!item.displayed) item.show();
item.repositionX();
@@ -7218,7 +7052,7 @@ Group.prototype._checkIfInvisible = function _checkIfInvisible(item, visibleItem
* @param {{start:number, end:number}} range
* @private
*/
-Group.prototype._checkIfVisible = function _checkIfVisible(item, visibleItems, range) {
+Group.prototype._checkIfVisible = function(item, visibleItems, range) {
if (item.isVisible(range)) {
if (!item.displayed) item.show();
// reposition item horizontally
@@ -7238,271 +7072,64 @@ Group.prototype._checkIfVisible = function _checkIfVisible(item, visibleItems, r
* @constructor
*/
function Timeline (container, items, options) {
- // validate arguments
- if (!container) throw new Error('No container element provided');
-
var me = this;
- var now = moment().hours(0).minutes(0).seconds(0).milliseconds(0);
this.defaultOptions = {
- orientation: 'bottom',
- direction: 'horizontal', // 'horizontal' or 'vertical'
- autoResize: true,
- stack: true,
-
- editable: {
- updateTime: false,
- updateGroup: false,
- add: false,
- remove: false
- },
-
- selectable: true,
-
start: null,
- end: null,
- min: null,
- max: null,
- zoomMin: 10, // milliseconds
- zoomMax: 1000 * 60 * 60 * 24 * 365 * 10000, // milliseconds
- // moveable: true, // TODO: option moveable
- // zoomable: true, // TODO: option zoomable
+ end: null,
- showMinorLabels: true,
- showMajorLabels: true,
- showCurrentTime: false,
- showCustomTime: false,
-
- groupOrder: null,
+ autoResize: true,
+ orientation: 'bottom',
width: null,
height: null,
maxHeight: null,
- minHeight: null,
-
- type: 'box',
- align: 'center',
- margin: {
- axis: 20,
- item: 10
- },
- padding: 5,
-
- onAdd: function (item, callback) {
- callback(item);
- },
- onUpdate: function (item, callback) {
- callback(item);
- },
- onMove: function (item, callback) {
- callback(item);
- },
- onRemove: function (item, callback) {
- callback(item);
- }
+ minHeight: null
};
+ this.options = util.deepExtend({}, this.defaultOptions);
- this.options = {};
- util.deepExtend(this.options, this.defaultOptions);
- util.deepExtend(this.options, {
- snap: null, // will be specified after timeaxis is created
-
- toScreen: me._toScreen.bind(me),
- toTime: me._toTime.bind(me)
- });
-
- // root panel
- var rootOptions = util.extend(Object.create(this.options), {
- height: function () {
- if (me.options.height) {
- // fixed height
- return me.options.height;
- }
- else {
- // auto height
- // TODO: implement a css based solution to automatically have the right hight
- return (me.timeAxis.height + me.contentPanel.height) + 'px';
- }
- }
- });
- this.rootPanel = new RootPanel(container, rootOptions);
-
- // single select (or unselect) when tapping an item
- this.rootPanel.on('tap', this._onSelectItem.bind(this));
-
- // multi select when holding mouse/touch, or on ctrl+click
- this.rootPanel.on('hold', this._onMultiSelectItem.bind(this));
+ // Create the DOM, props, and emitter
+ this._create(container);
- // add item on doubletap
- this.rootPanel.on('doubletap', this._onAddItem.bind(this));
+ // all components listed here will be repainted automatically
+ this.components = [];
- // side panel
- var sideOptions = util.extend(Object.create(this.options), {
- top: function () {
- return (sideOptions.orientation == 'top') ? '0' : '';
- },
- bottom: function () {
- return (sideOptions.orientation == 'top') ? '' : '0';
- },
- left: '0',
- right: null,
- height: '100%',
- width: function () {
- if (me.itemSet) {
- return me.itemSet.getLabelsWidth();
- }
- else {
- return 0;
- }
+ this.body = {
+ dom: this.dom,
+ domProps: this.props,
+ emitter: {
+ on: this.on.bind(this),
+ off: this.off.bind(this),
+ emit: this.emit.bind(this)
},
- className: function () {
- return 'side' + (me.groupsData ? '' : ' hidden');
+ util: {
+ snap: null, // will be specified after TimeAxis is created
+ toScreen: me._toScreen.bind(me),
+ toTime: me._toTime.bind(me)
}
- });
- this.sidePanel = new Panel(sideOptions);
- this.rootPanel.appendChild(this.sidePanel);
-
- // main panel (contains time axis and itemsets)
- var mainOptions = util.extend(Object.create(this.options), {
- left: function () {
- // we align left to enable a smooth resizing of the window
- return me.sidePanel.width;
- },
- right: null,
- height: '100%',
- width: function () {
- return me.rootPanel.width - me.sidePanel.width;
- },
- className: 'main'
- });
- this.mainPanel = new Panel(mainOptions);
- this.rootPanel.appendChild(this.mainPanel);
+ };
// range
- // TODO: move range inside rootPanel?
- var rangeOptions = Object.create(this.options);
- this.range = new Range(this.rootPanel, this.mainPanel, rangeOptions);
- this.range.setRange(
- now.clone().add('days', -3).valueOf(),
- now.clone().add('days', 4).valueOf()
- );
- this.range.on('rangechange', function (properties) {
- me.rootPanel.repaint();
- me.emit('rangechange', properties);
- });
- this.range.on('rangechanged', function (properties) {
- me.rootPanel.repaint();
- me.emit('rangechanged', properties);
- });
-
- // panel with time axis
- var timeAxisOptions = util.extend(Object.create(rootOptions), {
- range: this.range,
- left: null,
- top: null,
- width: null,
- height: null
- });
- this.timeAxis = new TimeAxis(timeAxisOptions);
- this.timeAxis.setRange(this.range);
- this.options.snap = this.timeAxis.snap.bind(this.timeAxis);
- this.mainPanel.appendChild(this.timeAxis);
-
- // content panel (contains itemset(s))
- var contentOptions = util.extend(Object.create(this.options), {
- top: function () {
- return (me.options.orientation == 'top') ? (me.timeAxis.height + 'px') : '';
- },
- bottom: function () {
- return (me.options.orientation == 'top') ? '' : (me.timeAxis.height + 'px');
- },
- left: null,
- right: null,
- height: null,
- width: null,
- className: 'content'
- });
- this.contentPanel = new Panel(contentOptions);
- this.mainPanel.appendChild(this.contentPanel);
-
- // content panel (contains the vertical lines of box items)
- var backgroundOptions = util.extend(Object.create(this.options), {
- top: function () {
- return (me.options.orientation == 'top') ? (me.timeAxis.height + 'px') : '';
- },
- bottom: function () {
- return (me.options.orientation == 'top') ? '' : (me.timeAxis.height + 'px');
- },
- left: null,
- right: null,
- height: function () {
- return me.contentPanel.height;
- },
- width: null,
- className: 'background'
- });
- this.backgroundPanel = new Panel(backgroundOptions);
- this.mainPanel.insertBefore(this.backgroundPanel, this.contentPanel);
-
- // panel with axis holding the dots of item boxes
- var axisPanelOptions = util.extend(Object.create(rootOptions), {
- left: 0,
- top: function () {
- return (me.options.orientation == 'top') ? (me.timeAxis.height + 'px') : '';
- },
- bottom: function () {
- return (me.options.orientation == 'top') ? '' : (me.timeAxis.height + 'px');
- },
- width: '100%',
- height: 0,
- className: 'axis'
- });
- this.axisPanel = new Panel(axisPanelOptions);
- this.mainPanel.appendChild(this.axisPanel);
-
- // content panel (contains itemset(s))
- var sideContentOptions = util.extend(Object.create(this.options), {
- top: function () {
- return (me.options.orientation == 'top') ? (me.timeAxis.height + 'px') : '';
- },
- bottom: function () {
- return (me.options.orientation == 'top') ? '' : (me.timeAxis.height + 'px');
- },
- left: null,
- right: null,
- height: null,
- width: null,
- className: 'side-content'
- });
- this.sideContentPanel = new Panel(sideContentOptions);
- this.sidePanel.appendChild(this.sideContentPanel);
+ 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
- // Note: time bar will be attached in this.setOptions when selected
- this.currentTime = new CurrentTime(this.range, rootOptions);
+ 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(rootOptions);
- this.customTime.on('timechange', function (time) {
- me.emit('timechange', time);
- });
- this.customTime.on('timechanged', function (time) {
- me.emit('timechanged', time);
- });
+ this.customTime = new CustomTime(this.body);
+ this.components.push(this.customTime);
- // itemset containing items and groups
- var itemOptions = util.extend(Object.create(this.options), {
- left: null,
- right: null,
- top: null,
- bottom: null,
- width: null,
- height: null
- });
- this.itemSet = new ItemSet(this.backgroundPanel, this.axisPanel, this.sideContentPanel, itemOptions);
- this.itemSet.setRange(this.range);
- this.itemSet.on('change', me.rootPanel.repaint.bind(me.rootPanel));
- this.contentPanel.appendChild(this.itemSet);
+ // item set
+ this.itemSet = new ItemSet(this.body);
+ this.components.push(this.itemSet);
this.itemsData = null; // DataSet
this.groupsData = null; // DataSet
@@ -7516,88 +7143,217 @@ function Timeline (container, items, options) {
if (items) {
this.setItems(items);
}
+ else {
+ this.redraw();
+ }
}
// turn Timeline into an event emitter
Emitter(Timeline.prototype);
/**
- * Set options
- * @param {Object} options TODO: describe the available options
+ * Create the main DOM for the Timeline: a root panel containing left, right,
+ * top, bottom, content, and background panel.
+ * @param {Element} container The container element where the Timeline will
+ * be attached.
+ * @private
*/
-Timeline.prototype.setOptions = function (options) {
- util.deepExtend(this.options, options);
+Timeline.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.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('change', 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));
- if ('editable' in options) {
- var isBoolean = typeof options.editable === 'boolean';
+ // create event listeners for all interesting events, these events will be
+ // emitted via emitter
+ this.hammer = Hammer(this.dom.root, {
+ prevent_default: true
+ });
+ this.listeners = {};
- this.options.editable = {
- updateTime: isBoolean ? options.editable : (options.editable.updateTime || false),
- updateGroup: isBoolean ? options.editable : (options.editable.updateGroup || false),
- add: isBoolean ? options.editable : (options.editable.add || false),
- remove: isBoolean ? options.editable : (options.editable.remove || false)
+ var me = this;
+ 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));
+ 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
- // force update of range (apply new min/max etc.)
- // both start and end are optional
- this.range.setRange(options.start, options.end);
+ // attach the root panel to the provided container
+ if (!container) throw new Error('No container provided');
+ container.appendChild(this.dom.root);
+};
- if ('editable' in options || 'selectable' in options) {
- if (this.options.selectable) {
- // force update of selection
- this.setSelection(this.getSelection());
- }
- else {
- // remove selection
- this.setSelection([]);
- }
- }
+/**
+ * Destroy the Timeline, clean up all DOM elements and event listeners.
+ */
+Timeline.prototype.destroy = function () {
+ // unbind datasets
+ this.clear();
- // force the itemSet to refresh: options like orientation and margins may be changed
- this.itemSet.markDirty();
+ // remove all event listeners
+ this.off();
- // validate the callback functions
- var validateCallback = (function (fn) {
- if (!(this.options[fn] instanceof Function) || this.options[fn].length != 2) {
- throw new Error('option ' + fn + ' must be a function ' + fn + '(item, callback)');
- }
- }).bind(this);
- ['onAdd', 'onUpdate', 'onRemove', 'onMove'].forEach(validateCallback);
+ // stop checking for changed size
+ this._stopAutoResize();
- // add/remove the current time bar
- if (this.options.showCurrentTime) {
- if (!this.mainPanel.hasChild(this.currentTime)) {
- this.mainPanel.appendChild(this.currentTime);
- this.currentTime.start();
- }
- }
- else {
- if (this.mainPanel.hasChild(this.currentTime)) {
- this.currentTime.stop();
- this.mainPanel.removeChild(this.currentTime);
- }
+ // remove from DOM
+ if (this.dom.root.parentNode) {
+ this.dom.root.parentNode.removeChild(this.dom.root);
}
+ this.dom = null;
- // add/remove the custom time bar
- if (this.options.showCustomTime) {
- if (!this.mainPanel.hasChild(this.customTime)) {
- this.mainPanel.appendChild(this.customTime);
+ // cleanup hammer touch events
+ for (var event in this.listeners) {
+ if (this.listeners.hasOwnProperty(event)) {
+ delete this.listeners[event];
}
}
- else {
- if (this.mainPanel.hasChild(this.customTime)) {
- this.mainPanel.removeChild(this.customTime);
- }
+ this.listeners = null;
+ this.hammer = null;
+
+ // give all components the opportunity to cleanup
+ this.components.forEach(function (component) {
+ component.destroy();
+ });
+
+ this.body = null;
+};
+
+/**
+ * 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
+ */
+Timeline.prototype.setOptions = function (options) {
+ if (options) {
+ // copy the known options
+ var fields = ['width', 'height', 'minHeight', 'maxHeight', 'autoResize', 'start', 'end', 'orientation'];
+ util.selectiveExtend(fields, this.options, options);
+
+ // 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.');
}
- // repaint everything
- this.rootPanel.repaint();
+ // redraw everything
+ this.redraw();
};
/**
@@ -7642,7 +7398,7 @@ Timeline.prototype.setItems = function(items) {
else {
// turn an array into a dataset
newDataSet = new DataSet(items, {
- convert: {
+ type: {
start: 'Date',
end: 'Date'
}
@@ -7651,13 +7407,13 @@ Timeline.prototype.setItems = function(items) {
// set items
this.itemsData = newDataSet;
- this.itemSet.setItems(newDataSet);
+ this.itemSet && this.itemSet.setItems(newDataSet);
- if (initialLoad && (this.options.start == undefined || this.options.end == undefined)) {
+ if (initialLoad && ('start' in this.options || 'end' in this.options)) {
this.fit();
- var start = (this.options.start != undefined) ? util.convert(this.options.start, 'Date') : null;
- var end = (this.options.end != undefined) ? util.convert(this.options.end, 'Date') : null;
+ var start = ('start' in this.options) ? util.convert(this.options.start, 'Date') : null;
+ var end = ('end' in this.options) ? util.convert(this.options.end, 'Date') : null;
this.setWindow(start, end);
}
@@ -7667,7 +7423,7 @@ Timeline.prototype.setItems = function(items) {
* Set groups
* @param {vis.DataSet | Array | google.visualization.DataTable} groups
*/
-Timeline.prototype.setGroups = function setGroups(groups) {
+Timeline.prototype.setGroups = function(groups) {
// convert to type DataSet when needed
var newDataSet;
if (!groups) {
@@ -7695,7 +7451,7 @@ Timeline.prototype.setGroups = function setGroups(groups) {
* @param {Object} [what] Optionally specify what to clear. By default:
* {items: true, groups: true, options: true}
*/
-Timeline.prototype.clear = function clear(what) {
+Timeline.prototype.clear = function(what) {
// clear items
if (!what || what.items) {
this.setItems(null);
@@ -7706,16 +7462,20 @@ Timeline.prototype.clear = function clear(what) {
this.setGroups(null);
}
- // clear options
+ // clear options of timeline and of each of the components
if (!what || what.options) {
- this.setOptions(this.defaultOptions);
+ this.components.forEach(function (component) {
+ component.setOptions(component.defaultOptions);
+ });
+
+ this.setOptions(this.defaultOptions); // this will also do a redraw
}
};
/**
* Set Timeline window such that it fits all items
*/
-Timeline.prototype.fit = function fit() {
+Timeline.prototype.fit = function() {
// apply the data range as range
var dataRange = this.getItemRange();
@@ -7746,7 +7506,7 @@ Timeline.prototype.fit = function fit() {
* When no minimum is found, min==null
* When no maximum is found, max==null
*/
-Timeline.prototype.getItemRange = function getItemRange() {
+Timeline.prototype.getItemRange = function() {
// calculate min from start filed
var itemsData = this.itemsData,
min = null,
@@ -7755,20 +7515,22 @@ Timeline.prototype.getItemRange = function getItemRange() {
if (itemsData) {
// calculate the minimum value of the field 'start'
var minItem = itemsData.min('start');
- min = minItem ? minItem.start.valueOf() : null;
+ 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 = itemsData.max('start');
if (maxStartItem) {
- max = maxStartItem.start.valueOf();
+ max = util.convert(maxStartItem.start, 'Date').valueOf();
}
var maxEndItem = itemsData.max('end');
if (maxEndItem) {
if (max == null) {
- max = maxEndItem.end.valueOf();
+ max = util.convert(maxEndItem.end, 'Date').valueOf();
}
else {
- max = Math.max(max, maxEndItem.end.valueOf());
+ max = Math.max(max, util.convert(maxEndItem.end, 'Date').valueOf());
}
}
}
@@ -7786,16 +7548,16 @@ Timeline.prototype.getItemRange = function getItemRange() {
* selected. If ids is an empty array, all items will be
* unselected.
*/
-Timeline.prototype.setSelection = function setSelection (ids) {
- this.itemSet.setSelection(ids);
+Timeline.prototype.setSelection = function(ids) {
+ this.itemSet && this.itemSet.setSelection(ids);
};
/**
* Get the selected items by their id
* @return {Array} ids The ids of the selected items
*/
-Timeline.prototype.getSelection = function getSelection() {
- return this.itemSet.getSelection();
+Timeline.prototype.getSelection = function() {
+ return this.itemSet && this.itemSet.getSelection() || [];
};
/**
@@ -7811,7 +7573,7 @@ Timeline.prototype.getSelection = function getSelection() {
* @param {Date | Number | String | Object} [start] Start date of visible window
* @param {Date | Number | String} [end] End date of visible window
*/
-Timeline.prototype.setWindow = function setWindow(start, end) {
+Timeline.prototype.setWindow = function(start, end) {
if (arguments.length == 1) {
var range = arguments[0];
this.range.setRange(range.start, range.end);
@@ -7825,7 +7587,7 @@ Timeline.prototype.setWindow = function setWindow(start, end) {
* Get the visible window
* @return {{start: Date, end: Date}} Visible range
*/
-Timeline.prototype.getWindow = function setWindow() {
+Timeline.prototype.getWindow = function() {
var range = this.range.getRange();
return {
start: new Date(range.start),
@@ -7837,160 +7599,321 @@ Timeline.prototype.getWindow = function setWindow() {
* Force a redraw of the Timeline. Can be useful to manually redraw when
* option autoResize=false
*/
-Timeline.prototype.redraw = function redraw() {
- this.rootPanel.repaint();
+Timeline.prototype.redraw = function() {
+ var resized = false,
+ options = this.options,
+ props = this.props,
+ dom = this.dom;
+
+ if (!dom) return; // when destroyed
+
+ // update class names
+ dom.root.className = 'vis timeline root ' + options.orientation;
+
+ // 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;
+
+ // 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 + '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 Timeline 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, 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
+ this.redraw();
+ }
};
// TODO: deprecated since version 1.1.0, remove some day
-Timeline.prototype.repaint = function repaint() {
+Timeline.prototype.repaint = function () {
throw new Error('Function repaint is deprecated. Use redraw instead.');
};
/**
- * Handle selecting/deselecting an item when tapping it
- * @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
*/
-// TODO: move this function to ItemSet
-Timeline.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);
+// TODO: move this function to Range
+Timeline.prototype._toTime = function(x) {
+ var conversion = this.range.conversion(this.props.center.width);
+ return new Date(x / conversion.scale + conversion.offset);
+};
- var newSelection = this.getSelection();
+/**
+ * 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
+Timeline.prototype._toScreen = function(time) {
+ var conversion = this.range.conversion(this.props.center.width);
+ return (time.valueOf() - conversion.offset) * conversion.scale;
+};
- // emit a select event,
- // except when old selection is empty and new selection is still empty
- if (newSelection.length > 0 || oldSelection.length > 0) {
- this.emit('select', {
- items: this.getSelection()
- });
+/**
+ * Initialize watching when option autoResize is true
+ * @private
+ */
+Timeline.prototype._initAutoResize = function () {
+ if (this.options.autoResize == true) {
+ this._startAutoResize();
+ }
+ else {
+ this._stopAutoResize();
}
-
- event.stopPropagation();
};
/**
- * Handle creation and updates of an item on double tap
- * @param event
+ * Watch for changes in the size of the container. On resize, the Panel will
+ * automatically redraw itself.
* @private
*/
-Timeline.prototype._onAddItem = function (event) {
- if (!this.options.selectable) return;
- if (!this.options.editable.add) return;
+Timeline.prototype._startAutoResize = function () {
+ var me = this;
- var me = this,
- item = ItemSet.itemFromTarget(event);
+ this._stopAutoResize();
- if (item) {
- // update item
+ this._onResize = function() {
+ if (me.options.autoResize != true) {
+ // stop watching when the option autoResize is changed to false
+ me._stopAutoResize();
+ return;
+ }
- // 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.update(itemData);
- }
- });
- }
- else {
- // add item
- var xAbs = vis.util.getAbsoluteLeft(this.contentPanel.frame);
- var x = event.gesture.center.pageX - xAbs;
- var newItem = {
- start: this.timeAxis.snap(this._toTime(x)),
- content: 'new item'
- };
+ if (me.dom.root) {
+ // check whether the frame is resized
+ if ((me.dom.root.clientWidth != me.props.lastWidth) ||
+ (me.dom.root.clientHeight != me.props.lastHeight)) {
+ me.props.lastWidth = me.dom.root.clientWidth;
+ me.props.lastHeight = me.dom.root.clientHeight;
- // when default type is a range, add a default end date to the new item
- if (this.options.type === 'range' || this.options.type == 'rangeoverflow') {
- newItem.end = this.timeAxis.snap(this._toTime(x + this.rootPanel.width / 5));
+ me.emit('change');
+ }
}
+ };
- var id = util.randomUUID();
- newItem[this.itemsData.fieldId] = id;
+ // add event listener to window resize
+ util.addEventListener(window, 'resize', this._onResize);
- var group = ItemSet.groupFromTarget(event);
- if (group) {
- newItem.group = group.groupId;
- }
+ this.watchTimer = setInterval(this._onResize, 1000);
+};
- // execute async handler to customize (or cancel) adding an item
- this.options.onAdd(newItem, function (item) {
- if (item) {
- me.itemsData.add(newItem);
- // TODO: need to trigger a redraw?
- }
- });
+/**
+ * Stop watching for a resize of the frame.
+ * @private
+ */
+Timeline.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;
};
/**
- * Handle selecting/deselecting multiple items when holding an item
+ * Start moving the timeline vertically
* @param {Event} event
* @private
*/
-// TODO: move this function to ItemSet
-Timeline.prototype._onMultiSelectItem = function (event) {
- if (!this.options.selectable) return;
+Timeline.prototype._onTouch = function (event) {
+ this.touch.allowDragging = true;
+};
- var selection,
- item = ItemSet.itemFromTarget(event);
+/**
+ * Start moving the timeline vertically
+ * @param {Event} event
+ * @private
+ */
+Timeline.prototype._onPinch = function (event) {
+ this.touch.allowDragging = false;
+};
- if (item) {
- // multi select items
- selection = this.getSelection(); // 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);
+/**
+ * Start moving the timeline vertically
+ * @param {Event} event
+ * @private
+ */
+Timeline.prototype._onDragStart = function (event) {
+ this.touch.initialScrollTop = this.props.scrollTop;
+};
- this.emit('select', {
- items: this.getSelection()
- });
+/**
+ * Move the timeline vertically
+ * @param {Event} event
+ * @private
+ */
+Timeline.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;
- event.stopPropagation();
+ 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
}
};
/**
- * 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
+ * Apply a scrollTop
+ * @param {Number} scrollTop
+ * @returns {Number} scrollTop Returns the applied scrollTop
* @private
*/
-Timeline.prototype._toTime = function _toTime(x) {
- var conversion = this.range.conversion(this.mainPanel.width);
- return new Date(x / conversion.scale + conversion.offset);
+Timeline.prototype._setScrollTop = function (scrollTop) {
+ this.props.scrollTop = scrollTop;
+ this._updateScrollTop();
+ return this.props.scrollTop;
};
/**
- * 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.
+ * Update the current scrollTop when the height of the containers has been changed
+ * @returns {Number} scrollTop Returns the applied scrollTop
* @private
*/
-Timeline.prototype._toScreen = function _toScreen(time) {
- var conversion = this.range.conversion(this.mainPanel.width);
- return (time.valueOf() - conversion.offset) * conversion.scale;
+Timeline.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
+ */
+Timeline.prototype._getScrollTop = function () {
+ return this.props.scrollTop;
};
(function(exports) {
@@ -9081,9 +9004,9 @@ function Node(properties, imagelist, grouplist, constants) {
this.edges = []; // all edges connected to this node
this.dynamicEdges = [];
this.reroutedEdges = {};
- this.group = constants.nodes.group;
- this.fontSize = constants.nodes.fontSize;
+ this.group = constants.nodes.group;
+ this.fontSize = Number(constants.nodes.fontSize);
this.fontFace = constants.nodes.fontFace;
this.fontColor = constants.nodes.fontColor;
this.fontDrawThreshold = 3;
@@ -9863,7 +9786,6 @@ Node.prototype._drawShape = function (ctx, shape) {
ctx.lineWidth = Math.min(0.1 * this.width,ctx.lineWidth);
ctx.fillStyle = this.selected ? this.color.highlight.background : this.hover ? this.color.hover.background : this.color.background;
-
ctx[shape](this.x, this.y, this.radius);
ctx.fill();
ctx.stroke();
@@ -10081,6 +10003,10 @@ function Edge (properties, graph, constants) {
this.lengthFixed = false;
this.setProperties(properties, constants);
+
+ this.controlNodesEnabled = false;
+ this.controlNodes = {from:null, to:null, positions:{}};
+ this.connectedNode = null;
}
/**
@@ -10740,44 +10666,65 @@ Edge.prototype._drawArrow = function(ctx) {
* @private
*/
Edge.prototype._getDistanceToEdge = function (x1,y1, x2,y2, x3,y3) { // x3,y3 is the point
- if (this.smooth == true) {
- var minDistance = 1e9;
- var i,t,x,y,dx,dy;
- for (i = 0; i < 10; i++) {
- t = 0.1*i;
- x = Math.pow(1-t,2)*x1 + (2*t*(1 - t))*this.via.x + Math.pow(t,2)*x2;
- y = Math.pow(1-t,2)*y1 + (2*t*(1 - t))*this.via.y + Math.pow(t,2)*y2;
- dx = Math.abs(x3-x);
- dy = Math.abs(y3-y);
- minDistance = Math.min(minDistance,Math.sqrt(dx*dx + dy*dy));
- }
- return minDistance
+ if (this.from != this.to) {
+ if (this.smooth == true) {
+ var minDistance = 1e9;
+ var i,t,x,y,dx,dy;
+ for (i = 0; i < 10; i++) {
+ t = 0.1*i;
+ x = Math.pow(1-t,2)*x1 + (2*t*(1 - t))*this.via.x + Math.pow(t,2)*x2;
+ y = Math.pow(1-t,2)*y1 + (2*t*(1 - t))*this.via.y + Math.pow(t,2)*y2;
+ dx = Math.abs(x3-x);
+ dy = Math.abs(y3-y);
+ minDistance = Math.min(minDistance,Math.sqrt(dx*dx + dy*dy));
+ }
+ return minDistance
+ }
+ else {
+ 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);
+ }
}
else {
- 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;
+ var x, y, dx, dy;
+ var radius = this.length / 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 if (u < 0) {
- u = 0;
+ else {
+ x = node.x + radius;
+ y = node.y - node.height / 2;
}
-
- 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);
+ dx = x - x3;
+ dy = y - y3;
+ return Math.abs(Math.sqrt(dx*dx + dy*dy) - radius);
}
};
@@ -10807,6 +10754,148 @@ Edge.prototype.positionBezierNode = function() {
this.via.y = 0.5 * (this.from.y + this.to.y);
}
};
+
+/**
+ * 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:8},
+ physics:{damping:0},
+ clustering: {maxNodeSizeIncrements: 0 ,nodeScaling: {width:0, height: 0, radius:0}}
+ };
+ this.controlNodes.from = new Node(
+ {id:nodeIdFrom,
+ shape:'dot',
+ color:{background:'#ff4e00', border:'#3c3c3c', highlight: {background:'#07f968'}}
+ },{},{},constants);
+ this.controlNodes.to = new Node(
+ {id:nodeIdTo,
+ shape:'dot',
+ color:{background:'#ff4e00', border:'#3c3c3c', highlight: {background:'#07f968'}}
+ },{},{},constants);
+ }
+
+ if (this.controlNodes.from.selected == false && this.controlNodes.to.selected == false) {
+ this.controlNodes.positions = this.getControlNodePositions(ctx);
+ this.controlNodes.from.x = this.controlNodes.positions.from.x;
+ this.controlNodes.from.y = this.controlNodes.positions.from.y;
+ 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.controlNodesEnabled = true;
+}
+
+/**
+ * disable control nodes
+ * @private
+ */
+Edge.prototype._disableControlNodes = function() {
+ 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();
+ }
+ 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 {{from: {x: number, y: number}, to: {x: *, y: *}}}
+ */
+Edge.prototype.getControlNodePositions = function(ctx) {
+ 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;
+ var xFrom = (fromBorderPoint) * this.from.x + (1 - fromBorderPoint) * this.to.x;
+ var yFrom = (fromBorderPoint) * this.from.y + (1 - fromBorderPoint) * this.to.y;
+
+
+ if (this.smooth == true) {
+ angle = Math.atan2((this.to.y - this.via.y), (this.to.x - this.via.x));
+ dx = (this.to.x - this.via.x);
+ dy = (this.to.y - this.via.y);
+ edgeSegmentLength = Math.sqrt(dx * dx + dy * dy);
+ }
+ var toBorderDist = this.to.distanceToBorder(ctx, angle);
+ var toBorderPoint = (edgeSegmentLength - toBorderDist) / edgeSegmentLength;
+
+ var xTo,yTo;
+ if (this.smooth == true) {
+ xTo = (1 - toBorderPoint) * this.via.x + toBorderPoint * this.to.x;
+ yTo = (1 - toBorderPoint) * this.via.y + toBorderPoint * this.to.y;
+ }
+ else {
+ xTo = (1 - toBorderPoint) * this.from.x + toBorderPoint * this.to.x;
+ yTo = (1 - toBorderPoint) * this.from.y + toBorderPoint * this.to.y;
+ }
+
+ return {from:{x:xFrom,y:yFrom},to:{x:xTo,y:yTo}};
+}
/**
* Popup is a class to create a popup window with some text
* @param {Element} container The container object.
@@ -12667,6 +12756,11 @@ var manipulationMixin = {
if (this.boundFunction) {
this.off('select', this.boundFunction);
}
+ if (this.edgeBeingEdited !== undefined) {
+ this.edgeBeingEdited._disableControlNodes();
+ this.edgeBeingEdited = undefined;
+ this.selectedControlNode = null;
+ }
// restore overloaded functions
this._restoreOverloadedFunctions();
@@ -12695,6 +12789,12 @@ var manipulationMixin = {
"" +
""+this.constants.labels['editNode'] +"";
}
+ else if (this._getSelectedEdgeCount() == 1 && this._getSelectedNodeCount() == 0) {
+ this.manipulationDiv.innerHTML += "" +
+ "" +
+ "" +
+ ""+this.constants.labels['editEdge'] +"";
+ }
if (this._selectionIsEmpty() == false) {
this.manipulationDiv.innerHTML += "" +
"" +
@@ -12712,6 +12812,10 @@ var manipulationMixin = {
var editButton = document.getElementById("graph-manipulate-editNode");
editButton.onclick = this._editNode.bind(this);
}
+ else if (this._getSelectedEdgeCount() == 1 && this._getSelectedNodeCount() == 0) {
+ var editButton = document.getElementById("graph-manipulate-editEdge");
+ editButton.onclick = this._createEditEdgeToolbar.bind(this);
+ }
if (this._selectionIsEmpty() == false) {
var deleteButton = document.getElementById("graph-manipulate-delete");
deleteButton.onclick = this._deleteSelected.bind(this);
@@ -12805,9 +12909,105 @@ var manipulationMixin = {
// redraw to show the unselect
this._redraw();
+ },
+
+ /**
+ * create the toolbar to edit edges
+ *
+ * @private
+ */
+ _createEditEdgeToolbar : function() {
+ // clear the toolbar
+ this._clearManipulatorBar();
+
+ if (this.boundFunction) {
+ this.off('select', this.boundFunction);
+ }
+
+ this.edgeBeingEdited = this._getSelectedEdge();
+ this.edgeBeingEdited._enableControlNodes();
+
+ this.manipulationDiv.innerHTML = "" +
+ "" +
+ "" + this.constants.labels['back'] + " " +
+ "" +
+ "" +
+ "" + this.constants.labels['editEdgeDescription'] + "";
+
+ // bind the icon
+ var backButton = document.getElementById("graph-manipulate-back");
+ backButton.onclick = this._createManipulatorBar.bind(this);
+
+ // temporarily overload functions
+ this.cachedFunctions["_handleTouch"] = this._handleTouch;
+ this.cachedFunctions["_handleOnRelease"] = this._handleOnRelease;
+ 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._handleOnRelease = this._releaseControlNode;
+
+ // redraw to show the unselect
+ 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
+ */
+ _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 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
+ */
+ _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();
},
+ _releaseControlNode : function(pointer) {
+ var newNode = this._getNodeAt(pointer);
+ if (newNode != null) {
+ if (this.edgeBeingEdited.controlNodes.from.selected == true) {
+ this._editEdge(newNode.id, this.edgeBeingEdited.to.id);
+ this.edgeBeingEdited.controlNodes.from.unselect();
+ }
+ if (this.edgeBeingEdited.controlNodes.to.selected == true) {
+ this._editEdge(this.edgeBeingEdited.from.id, newNode.id);
+ this.edgeBeingEdited.controlNodes.to.unselect();
+ }
+ }
+ 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
@@ -12953,6 +13153,36 @@ var manipulationMixin = {
}
},
+ /**
+ * connect two nodes with a new edge.
+ *
+ * @private
+ */
+ _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 {
+ alert(this.constants.labels["linkError"]);
+ this.moving = true;
+ this.start();
+ }
+ }
+ 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.
@@ -12993,6 +13223,8 @@ var manipulationMixin = {
},
+
+
/**
* delete everything in the selection
*
@@ -13321,7 +13553,7 @@ var SectorMixin = {
// console.log("the node is part of the active sector");
// }
// else {
-// console.log("I dont know what the fuck happened!!");
+// console.log("I dont know what happened!!");
// }
// when we switch to a new sector, we remove the node that will be expanded from the current nodes list.
@@ -13585,6 +13817,7 @@ var SectorMixin = {
this._loadLatestSector();
}
};
+
/**
* Creation of the ClusterMixin var.
*
@@ -14972,7 +15205,7 @@ var SelectionMixin = {
},
/**
- * return the number of selected nodes
+ * return the selected node
*
* @returns {number}
* @private
@@ -14986,6 +15219,21 @@ var SelectionMixin = {
return null;
},
+ /**
+ * return the selected edge
+ *
+ * @returns {number}
+ * @private
+ */
+ _getSelectedEdge : function() {
+ for (var edgeId in this.selectionObj.edges) {
+ if (this.selectionObj.edges.hasOwnProperty(edgeId)) {
+ return this.selectionObj.edges[edgeId];
+ }
+ }
+ return null;
+ },
+
/**
* return the number of selected edges
@@ -15189,7 +15437,6 @@ var SelectionMixin = {
* @private
*/
_handleTouch : function(pointer) {
-
},
@@ -15795,14 +16042,14 @@ function Graph (container, data, options) {
this.renderTimestep = 1000 / this.renderRefreshRate; // ms -- saves calculation later on
this.renderTime = 0.5 * this.renderTimestep; // measured time it takes to render a frame
this.maxPhysicsTicksPerRender = 3; // max amount of physics ticks per render step.
- this.physicsDiscreteStepsize = 0.65; // discrete stepsize of the simulation
+ this.physicsDiscreteStepsize = 0.50; // discrete stepsize of the simulation
this.stabilize = true; // stabilize before displaying the graph
this.selectable = true;
this.initializing = true;
// these functions are triggered when the dataset is edited
- this.triggerFunctions = {add:null,edit:null,connect:null,del:null};
+ this.triggerFunctions = {add:null,edit:null,editEdge:null,connect:null,del:null};
// set constant values
this.constants = {
@@ -15938,9 +16185,11 @@ function Graph (container, data, options) {
link:"Add Link",
del:"Delete selected",
editNode:"Edit Node",
+ editEdge:"Edit Edge",
back:"Back",
addDescription:"Click in an empty space to place a new node.",
linkDescription:"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.",
addError:"The function for add does not support two arguments (data,callback).",
linkError:"The function for connect does not support two arguments (data,callback).",
editError:"The function for edit does not support two arguments (data, callback).",
@@ -15958,12 +16207,13 @@ function Graph (container, data, options) {
background: '#FFFFC6'
}
},
- moveable: true,
+ dragGraph: true,
+ dragNodes: true,
zoomable: true,
hover: false
};
this.hoverObj = {nodes:{},edges:{}};
- this.editMode = this.constants.dataManipulation.initiallyVisible;
+
// Node variables
var graph = this;
@@ -15997,7 +16247,6 @@ function Graph (container, data, options) {
this._setScale(1);
this.setOptions(options);
-
// other vars
this.freezeSimulation = false;// freeze the simulation
this.cachedFunctions = {};
@@ -16301,7 +16550,8 @@ Graph.prototype.setOptions = function (options) {
if (options.freezeForStabilization !== undefined) {this.constants.freezeForStabilization = options.freezeForStabilization;}
if (options.configurePhysics !== undefined){this.constants.configurePhysics = options.configurePhysics;}
if (options.stabilizationIterations !== undefined) {this.constants.stabilizationIterations = options.stabilizationIterations;}
- if (options.moveable !== undefined) {this.constants.moveable = options.moveable;}
+ if (options.dragGraph !== undefined) {this.constants.dragGraph = options.dragGraph;}
+ if (options.dragNodes !== undefined) {this.constants.dragNodes = options.dragNodes;}
if (options.zoomable !== undefined) {this.constants.zoomable = options.zoomable;}
if (options.hover !== undefined) {this.constants.hover = options.hover;}
@@ -16321,6 +16571,10 @@ Graph.prototype.setOptions = function (options) {
this.triggerFunctions.edit = options.onEdit;
}
+ if (options.onEditEdge) {
+ this.triggerFunctions.editEdge = options.onEditEdge;
+ }
+
if (options.onConnect) {
this.triggerFunctions.connect = options.onConnect;
}
@@ -16415,6 +16669,7 @@ Graph.prototype.setOptions = function (options) {
this.constants.dataManipulation[prop] = options.dataManipulation[prop];
}
}
+ this.editMode = this.constants.dataManipulation.initiallyVisible;
}
else if (options.dataManipulation !== undefined) {
this.constants.dataManipulation.enabled = false;
@@ -16728,7 +16983,7 @@ Graph.prototype._handleOnDrag = function(event) {
var me = this,
drag = this.drag,
selection = drag.selection;
- if (selection && selection.length) {
+ if (selection && selection.length && this.constants.dragNodes == true) {
// calculate delta's and new location
var deltaX = pointer.x - drag.pointer.x,
deltaY = pointer.y - drag.pointer.y;
@@ -16753,7 +17008,7 @@ Graph.prototype._handleOnDrag = function(event) {
}
}
else {
- if (this.constants.moveable == true) {
+ if (this.constants.dragGraph == true) {
// move the graph
var diffX = pointer.x - this.drag.pointer.x;
var diffY = pointer.y - this.drag.pointer.y;
@@ -17449,7 +17704,6 @@ Graph.prototype._updateValueRange = function(obj) {
*/
Graph.prototype.redraw = function() {
this.setSize(this.width, this.height);
-
this._redraw();
};
@@ -17481,6 +17735,7 @@ Graph.prototype._redraw = function() {
this._doInAllSectors("_drawAllSectorNodes",ctx);
this._doInAllSectors("_drawEdges",ctx);
this._doInAllSectors("_drawNodes",ctx,false);
+ this._doInAllSectors("_drawControlNodes",ctx);
// this._doInSupportSector("_drawNodes",ctx,true);
// this._drawTree(ctx,"#F00F0F");
@@ -17665,6 +17920,21 @@ Graph.prototype._drawEdges = function(ctx) {
}
};
+/**
+ * Redraw all edges
+ * The 2d context of a HTML canvas can be retrieved by canvas.getContext('2d');
+ * @param {CanvasRenderingContext2D} ctx
+ * @private
+ */
+Graph.prototype._drawControlNodes = function(ctx) {
+ var edges = this.edges;
+ for (var id in edges) {
+ if (edges.hasOwnProperty(id)) {
+ edges[id]._drawControlNodes(ctx);
+ }
+ }
+};
+
/**
* Find a stable position for all nodes
* @private
@@ -21368,8 +21638,6 @@ var vis = {
},
Component: Component,
- Panel: Panel,
- RootPanel: RootPanel,
ItemSet: ItemSet,
TimeAxis: TimeAxis
},
@@ -23005,7 +23273,7 @@ else {
})(this);
},{}],4:[function(require,module,exports){
var global=typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {};//! moment.js
-//! version : 2.6.0
+//! version : 2.7.0
//! authors : Tim Wood, Iskren Chernev, Moment.js contributors
//! license : MIT
//! momentjs.com
@@ -23017,7 +23285,7 @@ var global=typeof self !== "undefined" ? self : typeof window !== "undefined" ?
************************************/
var moment,
- VERSION = "2.6.0",
+ VERSION = "2.7.0",
// the global-scope this is NOT the global object in Node.js
globalScope = typeof global !== 'undefined' ? global : this,
oldGlobalMoment,
@@ -23042,6 +23310,7 @@ var global=typeof self !== "undefined" ? self : typeof window !== "undefined" ?
_f : null,
_l : null,
_strict : null,
+ _tzm : null,
_isUTC : null,
_offset : null, // optional. Combine with _isUTC
_pf : null,
@@ -23150,6 +23419,16 @@ var global=typeof self !== "undefined" ? self : typeof window !== "undefined" ?
// format function strings
formatFunctions = {},
+ // default relative time thresholds
+ relativeTimeThresholds = {
+ s: 45, //seconds to minutes
+ m: 45, //minutes to hours
+ h: 22, //hours to days
+ dd: 25, //days to month (month == 1)
+ dm: 45, //days to months (months > 1)
+ dy: 345 //days 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(' '),
@@ -23289,6 +23568,16 @@ var global=typeof self !== "undefined" ? self : typeof window !== "undefined" ?
lists = ['months', 'monthsShort', 'weekdays', 'weekdaysShort', 'weekdaysMin'];
+ // 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");
+ }
+ }
+
function defaultParsingFlags() {
// We need to deep clone this object, and es5 standard is not very
// helpful.
@@ -24157,30 +24446,86 @@ var global=typeof self !== "undefined" ? self : typeof window !== "undefined" ?
config._useUTC = true;
config._tzm = timezoneMinutesFromString(input);
break;
+ // WEEKDAY - human
+ case 'dd':
+ case 'ddd':
+ case 'dddd':
+ a = getLangDefinition(config._l).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 'dd':
- case 'ddd':
- case 'dddd':
case 'e':
case 'E':
token = token.substr(0, 1);
/* falls through */
- case 'gg':
case 'gggg':
- case 'GG':
case 'GGGG':
case 'GGGGG':
token = token.substr(0, 2);
if (input) {
config._w = config._w || {};
- config._w[token] = input;
+ 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, lang;
+
+ 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 {
+ lang = getLangDefinition(config._l);
+ dow = lang._week.dow;
+ doy = lang._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.
@@ -24188,8 +24533,7 @@ var global=typeof self !== "undefined" ? self : typeof window !== "undefined" ?
// 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, fixYear, w, temp, lang, weekday, week;
+ var i, date, input = [], currentDate, yearToUse;
if (config._d) {
return;
@@ -24199,39 +24543,12 @@ var global=typeof self !== "undefined" ? self : typeof window !== "undefined" ?
//compute day of the year from weeks and weekdays
if (config._w && config._a[DATE] == null && config._a[MONTH] == null) {
- fixYear = function (val) {
- var intVal = parseInt(val, 10);
- return val ?
- (val.length < 3 ? (intVal > 68 ? 1900 + intVal : 2000 + intVal) : intVal) :
- (config._a[YEAR] == null ? moment().weekYear() : config._a[YEAR]);
- };
-
- w = config._w;
- if (w.GG != null || w.W != null || w.E != null) {
- temp = dayOfYearFromWeeks(fixYear(w.GG), w.W || 1, w.E, 4, 1);
- }
- else {
- lang = getLangDefinition(config._l);
- weekday = w.d != null ? parseWeekday(w.d, lang) :
- (w.e != null ? parseInt(w.e, 10) + lang._week.dow : 0);
-
- week = parseInt(w.w, 10) || 1;
-
- //if we're parsing 'd', then the low day numbers may be next week
- if (w.d != null && weekday < lang._week.dow) {
- week++;
- }
-
- temp = dayOfYearFromWeeks(fixYear(w.gg), week, weekday, lang._week.doy, lang._week.dow);
- }
-
- config._a[YEAR] = temp.year;
- config._dayOfYear = temp.dayOfYear;
+ dayOfYearFromWeekInfo(config);
}
//if the day of the year is set, figure out what it is
if (config._dayOfYear) {
- yearToUse = config._a[YEAR] == null ? currentDate[YEAR] : config._a[YEAR];
+ yearToUse = dfl(config._a[YEAR], currentDate[YEAR]);
if (config._dayOfYear > daysInYear(yearToUse)) {
config._pf._overflowDayOfYear = true;
@@ -24256,11 +24573,12 @@ var global=typeof self !== "undefined" ? self : typeof window !== "undefined" ?
config._a[i] = input[i] = (config._a[i] == null) ? (i === 2 ? 1 : 0) : config._a[i];
}
- // add the offsets to the time to be parsed so that we can have a clean array for checking isValid
- input[HOUR] += toInt((config._tzm || 0) / 60);
- input[MINUTE] += toInt((config._tzm || 0) % 60);
-
config._d = (config._useUTC ? makeUTCDate : makeDate).apply(null, input);
+ // Apply timezone offset from input. The actual zone can be changed
+ // with parseZone.
+ if (config._tzm != null) {
+ config._d.setUTCMinutes(config._d.getUTCMinutes() + config._tzm);
+ }
}
function dateFromObject(config) {
@@ -24300,6 +24618,11 @@ var global=typeof self !== "undefined" ? self : typeof window !== "undefined" ?
// date from string and format string
function makeDateFromStringAndFormat(config) {
+ if (config._f === moment.ISO_8601) {
+ parseISO(config);
+ return;
+ }
+
config._a = [];
config._pf.empty = true;
@@ -24412,7 +24735,7 @@ var global=typeof self !== "undefined" ? self : typeof window !== "undefined" ?
}
// date from iso format
- function makeDateFromString(config) {
+ function parseISO(config) {
var i, l,
string = config._i,
match = isoRegex.exec(string);
@@ -24436,8 +24759,16 @@ var global=typeof self !== "undefined" ? self : typeof window !== "undefined" ?
config._f += "Z";
}
makeDateFromStringAndFormat(config);
+ } else {
+ config._isValid = false;
}
- else {
+ }
+
+ // date from iso format or fallback
+ function makeDateFromString(config) {
+ parseISO(config);
+ if (config._isValid === false) {
+ delete config._isValid;
moment.createFromInputFallback(config);
}
}
@@ -24518,15 +24849,15 @@ var global=typeof self !== "undefined" ? self : typeof window !== "undefined" ?
hours = round(minutes / 60),
days = round(hours / 24),
years = round(days / 365),
- args = seconds < 45 && ['s', seconds] ||
+ args = seconds < relativeTimeThresholds.s && ['s', seconds] ||
minutes === 1 && ['m'] ||
- minutes < 45 && ['mm', minutes] ||
+ minutes < relativeTimeThresholds.m && ['mm', minutes] ||
hours === 1 && ['h'] ||
- hours < 22 && ['hh', hours] ||
+ hours < relativeTimeThresholds.h && ['hh', hours] ||
days === 1 && ['d'] ||
- days <= 25 && ['dd', days] ||
- days <= 45 && ['M'] ||
- days < 345 && ['MM', round(days / 30)] ||
+ days <= relativeTimeThresholds.dd && ['dd', days] ||
+ days <= relativeTimeThresholds.dm && ['M'] ||
+ days < relativeTimeThresholds.dy && ['MM', round(days / 30)] ||
years === 1 && ['y'] || ['yy', years];
args[2] = withoutSuffix;
args[3] = milliseconds > 0;
@@ -24572,6 +24903,7 @@ var global=typeof self !== "undefined" ? self : typeof window !== "undefined" ?
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;
@@ -24647,6 +24979,40 @@ var global=typeof self !== "undefined" ? self : typeof window !== "undefined" ?
config._d = new Date(config._i);
});
+ // 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, lang, strict) {
var c;
@@ -24743,6 +25109,9 @@ var global=typeof self !== "undefined" ? self : typeof window !== "undefined" ?
// 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;
@@ -24751,6 +25120,15 @@ var global=typeof self !== "undefined" ? self : typeof window !== "undefined" ?
// 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;
+ }
+ relativeTimeThresholds[threshold] = limit;
+ return true;
+ };
+
// This function will load languages and then set the global language. If
// no arguments are passed in, it will simply return the current global
// language key.
@@ -24906,7 +25284,9 @@ var global=typeof self !== "undefined" ? self : typeof window !== "undefined" ?
add : function (input, val) {
var dur;
// switch args to support add('s', 1) and add(1, 's')
- if (typeof input === 'string') {
+ if (typeof input === 'string' && typeof val === 'string') {
+ dur = moment.duration(isNaN(+val) ? +input : +val, isNaN(+val) ? val : input);
+ } else if (typeof input === 'string') {
dur = moment.duration(+val, input);
} else {
dur = moment.duration(input, val);
@@ -24918,7 +25298,9 @@ var global=typeof self !== "undefined" ? self : typeof window !== "undefined" ?
subtract : function (input, val) {
var dur;
// switch args to support subtract('s', 1) and subtract(1, 's')
- if (typeof input === 'string') {
+ if (typeof input === 'string' && typeof val === 'string') {
+ dur = moment.duration(isNaN(+val) ? +input : +val, isNaN(+val) ? val : input);
+ } else if (typeof input === 'string') {
dur = moment.duration(+val, input);
} else {
dur = moment.duration(input, val);
@@ -24969,10 +25351,11 @@ var global=typeof self !== "undefined" ? self : typeof window !== "undefined" ?
return this.from(moment(), withoutSuffix);
},
- calendar : function () {
+ calendar : function (time) {
// We want to compare the start of today, vs this.
// Getting start-of-today depends on whether we're zone'd or not.
- var sod = makeAs(moment(), this).startOf('day'),
+ var now = time || moment(),
+ sod = makeAs(now, this).startOf('day'),
diff = this.diff(sod, 'days', true),
format = diff < -6 ? 'sameElse' :
diff < -1 ? 'lastWeek' :
@@ -25067,15 +25450,21 @@ var global=typeof self !== "undefined" ? self : typeof window !== "undefined" ?
return +this.clone().startOf(units) === +makeAs(input, this).startOf(units);
},
- min: function (other) {
- other = moment.apply(null, arguments);
- return other < this ? this : other;
- },
-
- max: function (other) {
- other = moment.apply(null, arguments);
- return other > this ? this : other;
- },
+ 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;
+ }
+ ),
// keepTime = true means only change the timezone, without affecting
// the local hour. So 5:31:26 +0300 --[zone(2, true)]--> 5:31:26 +0200
diff --git a/dist/vis.min.css b/dist/vis.min.css
index 6f9c3d0e..e08fd306 100644
--- a/dist/vis.min.css
+++ b/dist/vis.min.css
@@ -1 +1 @@
-.vis.timeline.rootpanel{position:relative;overflow:hidden;border:1px solid #bfbfbf;box-sizing:border-box}.vis.timeline .vpanel{position:absolute;overflow:hidden;box-sizing:border-box}.vis.timeline .vpanel.side{border-right:1px solid #bfbfbf}.vis.timeline .vpanel.side.hidden{display:none}.vis.timeline .labelset{position:relative;width:100%;overflow:hidden;box-sizing:border-box}.vis.timeline .labelset .vlabel{position:relative;left:0;top:0;width:100%;color:#4d4d4d;box-sizing:border-box}.vis.timeline.top .labelset .vlabel{border-top:1px solid #bfbfbf;border-bottom:none}.vis.timeline.bottom .labelset .vlabel{border-top:none;border-bottom:1px solid #bfbfbf}.vis.timeline .labelset .vlabel .inner{display:inline-block;padding:5px}.vis.timeline .itemset{position:relative;padding:0;margin:0;box-sizing:border-box}.vis.timeline .axis{overflow:visible}.vis.timeline .group{position:relative;box-sizing:border-box}.vis.timeline.top .group{border-top:1px solid #bfbfbf;border-bottom:none}.vis.timeline.bottom .group{border-top:none;border-bottom:1px solid #bfbfbf}.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:999}.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,.vis.timeline .item.rangeoverflow{border-style:solid;border-radius:2px;box-sizing:border-box}.vis.timeline .item.range .content,.vis.timeline .item.rangeoverflow .content{position:relative;display:inline-block}.vis.timeline .item.range .content{overflow:hidden;max-width:100%}.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) no-repeat top center;position:absolute;width:24px;height:24px;top:0;right:-24px;cursor:pointer}.vis.timeline .item.range .drag-left,.vis.timeline .item.rangeoverflow .drag-left{position:absolute;width:24px;height:100%;top:0;left:-4px;cursor:w-resize;z-index:10000}.vis.timeline .item.range .drag-right,.vis.timeline .item.rangeoverflow .drag-right{position:absolute;width:24px;height:100%;top:0;right:-4px;cursor:e-resize;z-index:10001}.vis.timeline .timeaxis{position:absolute}.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;width:0;border-right:1px solid}.vis.timeline .timeaxis .grid.horizontal{position:absolute;left:0;width:100%;height:0;border-bottom: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:9}.vis.timeline .customtime{background-color:#6E94FF;width:2px;cursor:move;z-index:9}div.graph-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);width:600px;height:30px;z-index:10;position:absolute}div.graph-manipulation-editMode{height:30px;z-index:10;position:absolute;margin-top:20px}div.graph-manipulation-closeDiv{height:30px;width:30px;z-index:11;position:absolute;margin-top:3px;margin-left:590px;background-position:0 0;background-repeat:no-repeat;background-image:url(img/graph/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}span.graph-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.graph-manipulationUI:hover{box-shadow:1px 1px 8px rgba(0,0,0,.2)}span.graph-manipulationUI:active{box-shadow:1px 1px 8px rgba(0,0,0,.5)}span.graph-manipulationUI.back{background-image:url(img/graph/backIcon.png)}span.graph-manipulationUI.none:hover{box-shadow:1px 1px 8px rgba(0,0,0,0);cursor:default}span.graph-manipulationUI.none:active{box-shadow:1px 1px 8px rgba(0,0,0,0)}span.graph-manipulationUI.none{padding:0}span.graph-manipulationUI.notification{margin:2px;font-weight:700}span.graph-manipulationUI.add{background-image:url(img/graph/addNodeIcon.png)}span.graph-manipulationUI.edit{background-image:url(img/graph/editIcon.png)}span.graph-manipulationUI.edit.editmode{background-color:#fcfcfc;border-style:solid;border-width:1px;border-color:#ccc}span.graph-manipulationUI.connect{background-image:url(img/graph/connectIcon.png)}span.graph-manipulationUI.delete{background-image:url(img/graph/deleteIcon.png)}span.graph-manipulationLabel{margin:0 0 0 23px;line-height:25px}div.graph-seperatorLine{display:inline-block;width:1px;height:20px;background-color:#bdbdbd;margin:5px 7px 0 15px}div.graph-navigation{width:34px;height:34px;z-index:10;-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.graph-navigation:hover{box-shadow:0 0 3px 3px rgba(56,207,21,.3)}div.graph-navigation.active,div.graph-navigation:active{box-shadow:0 0 1px 3px rgba(56,207,21,.95)}div.graph-navigation.up{background-image:url(img/graph/upArrow.png);bottom:50px;left:55px}div.graph-navigation.down{background-image:url(img/graph/downArrow.png);bottom:10px;left:55px}div.graph-navigation.left{background-image:url(img/graph/leftArrow.png);bottom:10px;left:15px}div.graph-navigation.right{background-image:url(img/graph/rightArrow.png);bottom:10px;left:95px}div.graph-navigation.zoomIn{background-image:url(img/graph/plus.png);bottom:10px;right:15px}div.graph-navigation.zoomOut{background-image:url(img/graph/minus.png);bottom:10px;right:55px}div.graph-navigation.zoomExtends{background-image:url(img/graph/zoomExtends.png);bottom:50px;right:15px}
\ No newline at end of file
+.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;width:100%;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%}.vis.timeline .axis{position:absolute;width:100%;height:0;left:1px;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:999}.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,.vis.timeline .item.rangeoverflow{border-style:solid;border-radius:2px;box-sizing:border-box}.vis.timeline .item.range .content,.vis.timeline .item.rangeoverflow .content{position:relative;display:inline-block}.vis.timeline .item.range .content{overflow:hidden;max-width:100%}.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) no-repeat top center;position:absolute;width:24px;height:24px;top:0;right:-24px;cursor:pointer}.vis.timeline .item.range .drag-left,.vis.timeline .item.rangeoverflow .drag-left{position:absolute;width:24px;height:100%;top:0;left:-4px;cursor:w-resize;z-index:10000}.vis.timeline .item.range .drag-right,.vis.timeline .item.rangeoverflow .drag-right{position:absolute;width:24px;height:100%;top:0;right:-4px;cursor:e-resize;z-index:10001}.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;width:0;border-right: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}div.graph-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);width:600px;height:30px;z-index:10;position:absolute}div.graph-manipulation-editMode{height:30px;z-index:10;position:absolute;margin-top:20px}div.graph-manipulation-closeDiv{height:30px;width:30px;z-index:11;position:absolute;margin-top:3px;margin-left:590px;background-position:0 0;background-repeat:no-repeat;background-image:url(img/graph/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}span.graph-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.graph-manipulationUI:hover{box-shadow:1px 1px 8px rgba(0,0,0,.2)}span.graph-manipulationUI:active{box-shadow:1px 1px 8px rgba(0,0,0,.5)}span.graph-manipulationUI.back{background-image:url(img/graph/backIcon.png)}span.graph-manipulationUI.none:hover{box-shadow:1px 1px 8px transparent;cursor:default}span.graph-manipulationUI.none:active{box-shadow:1px 1px 8px transparent}span.graph-manipulationUI.none{padding:0}span.graph-manipulationUI.notification{margin:2px;font-weight:700}span.graph-manipulationUI.add{background-image:url(img/graph/addNodeIcon.png)}span.graph-manipulationUI.edit{background-image:url(img/graph/editIcon.png)}span.graph-manipulationUI.edit.editmode{background-color:#fcfcfc;border-style:solid;border-width:1px;border-color:#ccc}span.graph-manipulationUI.connect{background-image:url(img/graph/connectIcon.png)}span.graph-manipulationUI.delete{background-image:url(img/graph/deleteIcon.png)}span.graph-manipulationLabel{margin:0 0 0 23px;line-height:25px}div.graph-seperatorLine{display:inline-block;width:1px;height:20px;background-color:#bdbdbd;margin:5px 7px 0 15px}div.graph-navigation{width:34px;height:34px;z-index:10;-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.graph-navigation:hover{box-shadow:0 0 3px 3px rgba(56,207,21,.3)}div.graph-navigation.active,div.graph-navigation:active{box-shadow:0 0 1px 3px rgba(56,207,21,.95)}div.graph-navigation.up{background-image:url(img/graph/upArrow.png);bottom:50px;left:55px}div.graph-navigation.down{background-image:url(img/graph/downArrow.png);bottom:10px;left:55px}div.graph-navigation.left{background-image:url(img/graph/leftArrow.png);bottom:10px;left:15px}div.graph-navigation.right{background-image:url(img/graph/rightArrow.png);bottom:10px;left:95px}div.graph-navigation.zoomIn{background-image:url(img/graph/plus.png);bottom:10px;right:15px}div.graph-navigation.zoomOut{background-image:url(img/graph/minus.png);bottom:10px;right:55px}div.graph-navigation.zoomExtends{background-image:url(img/graph/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 5adfc5c1..7ade499b 100644
--- a/dist/vis.min.js
+++ b/dist/vis.min.js
@@ -4,8 +4,8 @@
*
* A dynamic, browser-based visualization library.
*
- * @version 1.1.0
- * @date 2014-06-06
+ * @version 2.0.0
+ * @date 2014-06-19
*
* @license
* Copyright (C) 2011-2014 Almende B.V, http://almende.com
@@ -22,13 +22,14 @@
* License for the specific language governing permissions and limitations under
* the License.
*/
-!function(t){if("object"==typeof exports)module.exports=t();else if("function"==typeof define&&define.amd)define(t);else{var e;"undefined"!=typeof window?e=window:"undefined"!=typeof global?e=global:"undefined"!=typeof self&&(e=self),e.vis=t()}}(function(){var define,module,exports;return function t(e,i,s){function n(a,r){if(!i[a]){if(!e[a]){var h="function"==typeof require&&require;if(!r&&h)return h(a,!0);if(o)return o(a,!0);throw new Error("Cannot find module '"+a+"'")}var d=i[a]={exports:{}};e[a][0].call(d.exports,function(t){var i=e[a][1][t];return n(i?i:t)},d,d.exports,t,e,i,s)}return i[a].exports}for(var o="function"==typeof require&&require,a=0;ae?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}function Slider(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 moment="undefined"!=typeof window&&window.moment||require("moment"),Emitter=require("emitter-component"),Hammer;Hammer="undefined"!=typeof window?window.Hammer||require("hammerjs"):function(){throw Error("hammer.js is only available in a browser, not in node.js.")};var mousetrap;if(mousetrap="undefined"!=typeof window?window.mousetrap||require("mousetrap"):function(){throw Error("mouseTrap is only available in a browser, not in node.js.")},!Array.prototype.indexOf){Array.prototype.indexOf=function(t){for(var e=0;ei;++i)t.call(e||this,this[i],i,this)}),Array.prototype.map||(Array.prototype.map=function(t,e){var i,s,n;if(null==this)throw new TypeError(" this is null or not defined");var o=Object(this),a=o.length>>>0;if("function"!=typeof t)throw new TypeError(t+" is not a function");for(e&&(i=e),s=new Array(a),n=0;a>n;){var r,h;n in o&&(r=o[n],h=t.call(i,r,n,o),s[n]=h),n++}return s}),Array.prototype.filter||(Array.prototype.filter=function(t){"use strict";if(null==this)throw new TypeError;var e=Object(this),i=e.length>>>0;if("function"!=typeof t)throw new TypeError;for(var s=[],n=arguments[1],o=0;i>o;o++)if(o in e){var a=e[o];t.call(n,a,o,e)&&s.push(a)}return s}),Object.keys||(Object.keys=function(){var t=Object.prototype.hasOwnProperty,e=!{toString:null}.propertyIsEnumerable("toString"),i=["toString","toLocaleString","valueOf","hasOwnProperty","isPrototypeOf","propertyIsEnumerable","constructor"],s=i.length;return function(n){if("object"!=typeof n&&"function"!=typeof n||null===n)throw new TypeError("Object.keys called on non-object");var o=[];for(var a in n)t.call(n,a)&&o.push(a);if(e)for(var r=0;s>r;r++)t.call(n,i[r])&&o.push(i[r]);return o}}()),Array.isArray||(Array.isArray=function(t){return"[object Array]"===Object.prototype.toString.call(t)}),Function.prototype.bind||(Function.prototype.bind=function(t){if("function"!=typeof this)throw new TypeError("Function.prototype.bind - what is trying to be bound is not callable");var e=Array.prototype.slice.call(arguments,1),i=this,s=function(){},n=function(){return i.apply(this instanceof s&&t?this:t,e.concat(Array.prototype.slice.call(arguments)))};return s.prototype=this.prototype,n.prototype=new s,n}),Object.create||(Object.create=function(t){function e(){}if(arguments.length>1)throw new Error("Object.create implementation only accepts the first parameter.");return e.prototype=t,new e}),Function.prototype.bind||(Function.prototype.bind=function(t){if("function"!=typeof this)throw new TypeError("Function.prototype.bind - what is trying to be bound is not callable");var e=Array.prototype.slice.call(arguments,1),i=this,s=function(){},n=function(){return i.apply(this instanceof s&&t?this:t,e.concat(Array.prototype.slice.call(arguments)))};return s.prototype=this.prototype,n.prototype=new s,n});var util={};util.isNumber=function(t){return t instanceof Number||"number"==typeof t},util.isString=function(t){return t instanceof String||"string"==typeof t},util.isDate=function(t){if(t instanceof Date)return!0;if(util.isString(t)){var e=ASPDateRegex.exec(t);if(e)return!0;if(!isNaN(Date.parse(t)))return!0}return!1},util.isDataTable=function(t){return"undefined"!=typeof google&&google.visualization&&google.visualization.DataTable&&t instanceof google.visualization.DataTable},util.randomUUID=function(){var t=function(){return Math.floor(65536*Math.random()).toString(16)};return t()+t()+"-"+t()+"-"+t()+"-"+t()+"-"+t()+t()+t()},util.extend=function(t){for(var e=1,i=arguments.length;i>e;e++){var s=arguments[e];for(var n in s)s.hasOwnProperty(n)&&void 0!==s[n]&&(t[n]=s[n])}return t},util.deepExtend=function t(e,i){if(Array.isArray(i))throw new TypeError("Arrays are not supported by deepExtend");for(var s in i)if(i.hasOwnProperty(s))if(i[s]&&i[s].constructor===Object)void 0===e[s]&&(e[s]={}),e[s].constructor===Object?t(e[s],i[s]):e[s]=i[s];else{if(Array.isArray(i[s]))throw new TypeError("Arrays are not supported by deepExtend");e[s]=i[s]}return e},util.equalArray=function(t,e){if(t.length!=e.length)return!1;for(var i=0,s=t.length;s>i;i++)if(t[i]!=e[i])return!1;return!0},util.convert=function(t,e){var i;if(void 0===t)return void 0;if(null===t)return null;if(!e)return t;if("string"!=typeof e&&!(e instanceof String))throw new Error("Type must be a string");switch(e){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(util.isNumber(t))return new Date(t);if(t instanceof Date)return new Date(t.valueOf());if(moment.isMoment(t))return new Date(t.valueOf());if(util.isString(t))return i=ASPDateRegex.exec(t),i?new Date(Number(i[1])):moment(t).toDate();throw new Error("Cannot convert object of type "+util.getType(t)+" to type Date");case"Moment":if(util.isNumber(t))return moment(t);if(t instanceof Date)return moment(t.valueOf());if(moment.isMoment(t))return moment(t);if(util.isString(t))return i=ASPDateRegex.exec(t),moment(i?Number(i[1]):t);throw new Error("Cannot convert object of type "+util.getType(t)+" to type Date");case"ISODate":if(util.isNumber(t))return new Date(t);if(t instanceof Date)return t.toISOString();if(moment.isMoment(t))return t.toDate().toISOString();if(util.isString(t))return i=ASPDateRegex.exec(t),i?new Date(Number(i[1])).toISOString():new Date(t).toISOString();throw new Error("Cannot convert object of type "+util.getType(t)+" to type ISODate");case"ASPDate":if(util.isNumber(t))return"/Date("+t+")/";if(t instanceof Date)return"/Date("+t.valueOf()+")/";if(util.isString(t)){i=ASPDateRegex.exec(t);var s;return s=i?new Date(Number(i[1])).valueOf():new Date(t).valueOf(),"/Date("+s+")/"}throw new Error("Cannot convert object of type "+util.getType(t)+" to type ASPDate");default:throw new Error("Cannot convert object of type "+util.getType(t)+' to type "'+e+'"')}};var ASPDateRegex=/^\/?Date\((\-?\d+)/i;util.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":t instanceof Array?"Array":t instanceof Date?"Date":"Object":"number"==e?"Number":"boolean"==e?"Boolean":"string"==e?"String":e},util.getAbsoluteLeft=function(t){for(var e=document.documentElement,i=document.body,s=t.offsetLeft,n=t.offsetParent;null!=n&&n!=i&&n!=e;)s+=n.offsetLeft,s-=n.scrollLeft,n=n.offsetParent;return s},util.getAbsoluteTop=function(t){for(var e=document.documentElement,i=document.body,s=t.offsetTop,n=t.offsetParent;null!=n&&n!=i&&n!=e;)s+=n.offsetTop,s-=n.scrollTop,n=n.offsetParent;return s},util.getPageY=function(t){if("pageY"in t)return t.pageY;var e;e="targetTouches"in t&&t.targetTouches.length?t.targetTouches[0].clientY:t.clientY;var i=document.documentElement,s=document.body;return e+(i&&i.scrollTop||s&&s.scrollTop||0)-(i&&i.clientTop||s&&s.clientTop||0)},util.getPageX=function(t){if("pageY"in t)return t.pageX;var e;e="targetTouches"in t&&t.targetTouches.length?t.targetTouches[0].clientX:t.clientX;var i=document.documentElement,s=document.body;return e+(i&&i.scrollLeft||s&&s.scrollLeft||0)-(i&&i.clientLeft||s&&s.clientLeft||0)},util.addClassName=function(t,e){var i=t.className.split(" ");-1==i.indexOf(e)&&(i.push(e),t.className=i.join(" "))},util.removeClassName=function(t,e){var i=t.className.split(" "),s=i.indexOf(e);-1!=s&&(i.splice(s,1),t.className=i.join(" "))},util.forEach=function(t,e){var i,s;if(t instanceof Array)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)},util.toArray=function(t){var e=[];for(var i in t)t.hasOwnProperty(i)&&e.push(t[i]);return e},util.updateProperty=function(t,e,i){return t[e]!==i?(t[e]=i,!0):!1},util.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)},util.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)},util.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},util.fakeGesture=function(t,e){var i=null,s=Hammer.event.collectEventData(this,i,e);return isNaN(s.center.pageX)&&(s.center.pageX=e.pageX),isNaN(s.center.pageY)&&(s.center.pageY=e.pageY),s},util.option={},util.option.asBoolean=function(t,e){return"function"==typeof t&&(t=t()),null!=t?0!=t:e||null},util.option.asNumber=function(t,e){return"function"==typeof t&&(t=t()),null!=t?Number(t)||e||null:e||null},util.option.asString=function(t,e){return"function"==typeof t&&(t=t()),null!=t?String(t):e||null},util.option.asSize=function(t,e){return"function"==typeof t&&(t=t()),util.isString(t)?t:util.isNumber(t)?t+"px":e||null},util.option.asElement=function(t,e){return"function"==typeof t&&(t=t()),t||e||null},util.GiveDec=function GiveDec(Hex){var Value;return Value="A"==Hex?10:"B"==Hex?11:"C"==Hex?12:"D"==Hex?13:"E"==Hex?14:"F"==Hex?15:eval(Hex)},util.GiveHex=function(t){var e;return e=10==t?"A":11==t?"B":12==t?"C":13==t?"D":14==t?"E":15==t?"F":""+t},util.parseColor=function(t){var e;if(util.isString(t))if(util.isValidHex(t)){var i=util.hexToHSV(t),s={h:i.h,s:.45*i.s,v:Math.min(1,1.05*i.v)},n={h:i.h,s:Math.min(1,1.25*i.v),v:.6*i.v},o=util.HSVToHex(n.h,n.h,n.v),a=util.HSVToHex(s.h,s.s,s.v);e={background:t,border:o,highlight:{background:a,border:o},hover:{background:a,border:o}}}else e={background:t,border:t,highlight:{background:t,border:t},hover:{background:t,border:t}};else e={},e.background=t.background||"white",e.border=t.border||e.background,util.isString(t.highlight)?e.highlight={border:t.highlight,background:t.highlight}:(e.highlight={},e.highlight.background=t.highlight&&t.highlight.background||e.background,e.highlight.border=t.highlight&&t.highlight.border||e.border),util.isString(t.hover)?e.hover={border:t.hover,background:t.hover}:(e.hover={},e.hover.background=t.hover&&t.hover.background||e.background,e.hover.border=t.hover&&t.hover.border||e.border);return e},util.hexToRGB=function(t){t=t.replace("#","").toUpperCase();var e=util.GiveDec(t.substring(0,1)),i=util.GiveDec(t.substring(1,2)),s=util.GiveDec(t.substring(2,3)),n=util.GiveDec(t.substring(3,4)),o=util.GiveDec(t.substring(4,5)),a=util.GiveDec(t.substring(5,6)),r=16*e+i,h=16*s+n,i=16*o+a;return{r:r,g:h,b:i}},util.RGBToHex=function(t,e,i){var s=util.GiveHex(Math.floor(t/16)),n=util.GiveHex(t%16),o=util.GiveHex(Math.floor(e/16)),a=util.GiveHex(e%16),r=util.GiveHex(Math.floor(i/16)),h=util.GiveHex(i%16),d=s+n+o+a+r+h;return"#"+d},util.RGBToHSV=function(t,e,i){t/=255,e/=255,i/=255;var s=Math.min(t,Math.min(e,i)),n=Math.max(t,Math.max(e,i));if(s==n)return{h:0,s:0,v:s};var o=t==s?e-i:i==s?t-e:i-t,a=t==s?3:i==s?1:5,r=60*(a-o/(n-s))/360,h=(n-s)/n,d=n;return{h:r,s:h,v:d}},util.HSVToRGB=function(t,e,i){var s,n,o,a=Math.floor(6*t),r=6*t-a,h=i*(1-e),d=i*(1-r*e),l=i*(1-(1-r)*e);switch(a%6){case 0:s=i,n=l,o=h;break;case 1:s=d,n=i,o=h;break;case 2:s=h,n=i,o=l;break;case 3:s=h,n=d,o=i;break;case 4:s=l,n=h,o=i;break;case 5:s=i,n=h,o=d}return{r:Math.floor(255*s),g:Math.floor(255*n),b:Math.floor(255*o)}},util.HSVToHex=function(t,e,i){var s=util.HSVToRGB(t,e,i);return util.RGBToHex(s.r,s.g,s.b)},util.hexToHSV=function(t){var e=util.hexToRGB(t);return util.RGBToHSV(e.r,e.g,e.b)},util.isValidHex=function(t){var e=/(^#[0-9A-F]{6}$)|(^#[0-9A-F]{3}$)/i.test(t);return e},util.copyObject=function(t,e){for(var i in t)t.hasOwnProperty(i)&&("object"==typeof t[i]?(e[i]={},util.copyObject(t[i],e[i])):e[i]=t[i])},DataSet.prototype.on=function(t,e){var i=this.subscribers[t];i||(i=[],this.subscribers[t]=i),i.push({callback:e})},DataSet.prototype.subscribe=DataSet.prototype.on,DataSet.prototype.off=function(t,e){var i=this.subscribers[t];i&&(this.subscribers[t]=i.filter(function(t){return t.callback!=e}))},DataSet.prototype.unsubscribe=DataSet.prototype.off,DataSet.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 n=0;no;o++)i=n._addItem(t[o]),s.push(i);else if(util.isDataTable(t))for(var r=this._getColumnNames(t),h=0,d=t.getNumberOfRows();d>h;h++){for(var l={},c=0,u=r.length;u>c;c++){var p=r[c];l[p]=t.getValue(h,c)}i=n._addItem(l),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},DataSet.prototype.update=function(t,e){var i=[],s=[],n=this,o=n.fieldId,a=function(t){var e=t[o];n.data[e]?(e=n._updateItem(t),s.push(e)):(e=n._addItem(t),i.push(e))};if(t instanceof Array)for(var r=0,h=t.length;h>r;r++)a(t[r]);else if(util.isDataTable(t))for(var d=this._getColumnNames(t),l=0,c=t.getNumberOfRows();c>l;l++){for(var u={},p=0,m=d.length;m>p;p++){var g=d[p];u[g]=t.getValue(l,p)}a(u)}else{if(!(t instanceof Object))throw new Error("Unknown dataType");a(t)}return i.length&&this._trigger("add",{items:i},e),s.length&&this._trigger("update",{items:s},e),i.concat(s)},DataSet.prototype.get=function(){var t,e,i,s,n=this,o=this.showInternalIds,a=util.getType(arguments[0]);"String"==a||"Number"==a?(t=arguments[0],i=arguments[1],s=arguments[2]):"Array"==a?(e=arguments[0],i=arguments[1],s=arguments[2]):(i=arguments[0],s=arguments[1]);var r;if(i&&i.type){if(r="DataTable"==i.type?"DataTable":"Array",s&&r!=util.getType(s))throw new Error('Type of parameter "data" ('+util.getType(s)+") does not correspond with specified options.type ("+i.type+")");if("DataTable"==r&&!util.isDataTable(s))throw new Error('Parameter "data" must be a DataTable when options.type is "DataTable"')}else r=s&&"DataTable"==util.getType(s)?"DataTable":"Array";void 0!=i&&void 0!=i.showInternalIds&&(this.showInternalIds=i.showInternalIds);var h,d,l,c,u=i&&i.convert||this.options.convert,p=i&&i.filter,m=[];if(void 0!=t)h=n._getItem(t,u),p&&!p(h)&&(h=null);else if(void 0!=e)for(l=0,c=e.length;c>l;l++)h=n._getItem(e[l],u),(!p||p(h))&&m.push(h);else for(d in this.data)this.data.hasOwnProperty(d)&&(h=n._getItem(d,u),(!p||p(h))&&m.push(h));if(this.showInternalIds=o,i&&i.order&&void 0==t&&this._sort(m,i.order),i&&i.fields){var g=i.fields;if(void 0!=t)h=this._filterFields(h,g);else for(l=0,c=m.length;c>l;l++)m[l]=this._filterFields(m[l],g)}if("DataTable"==r){var f=this._getColumnNames(s);if(void 0!=t)n._appendRow(s,f,h);else for(l=0,c=m.length;c>l;l++)n._appendRow(s,f,m[l]);return s}if(void 0!=t)return h;if(s){for(l=0,c=m.length;c>l;l++)s.push(m[l]);return s}return m},DataSet.prototype.getIds=function(t){var e,i,s,n,o,a=this.data,r=t&&t.filter,h=t&&t.order,d=t&&t.convert||this.options.convert,l=[];if(r)if(h){o=[];for(s in a)a.hasOwnProperty(s)&&(n=this._getItem(s,d),r(n)&&o.push(n));for(this._sort(o,h),e=0,i=o.length;i>e;e++)l[e]=o[e][this.fieldId]}else for(s in a)a.hasOwnProperty(s)&&(n=this._getItem(s,d),r(n)&&l.push(n[this.fieldId]));else if(h){o=[];for(s in a)a.hasOwnProperty(s)&&o.push(a[s]);for(this._sort(o,h),e=0,i=o.length;i>e;e++)l[e]=o[e][this.fieldId]}else for(s in a)a.hasOwnProperty(s)&&(n=a[s],l.push(n[this.fieldId]));return l},DataSet.prototype.forEach=function(t,e){var i,s,n=e&&e.filter,o=e&&e.convert||this.options.convert,a=this.data;if(e&&e.order)for(var r=this.get(e),h=0,d=r.length;d>h;h++)i=r[h],s=i[this.fieldId],t(i,s);else for(s in a)a.hasOwnProperty(s)&&(i=this._getItem(s,o),(!n||n(i))&&t(i,s))},DataSet.prototype.map=function(t,e){var i,s=e&&e.filter,n=e&&e.convert||this.options.convert,o=[],a=this.data;for(var r in a)a.hasOwnProperty(r)&&(i=this._getItem(r,n),(!s||s(i))&&o.push(t(i,r)));return e&&e.order&&this._sort(o,e.order),o},DataSet.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},DataSet.prototype._sort=function(t,e){if(util.isString(e)){var i=e;t.sort(function(t,e){var s=t[i],n=e[i];return s>n?1:n>s?-1:0})}else{if("function"!=typeof e)throw new TypeError("Order must be a function or a string");t.sort(e)}},DataSet.prototype.remove=function(t,e){var i,s,n,o=[];if(t instanceof Array)for(i=0,s=t.length;s>i;i++)n=this._remove(t[i]),null!=n&&o.push(n);else n=this._remove(t),null!=n&&o.push(n);return o.length&&this._trigger("remove",{items:o},e),o},DataSet.prototype._remove=function(t){if(util.isNumber(t)||util.isString(t)){if(this.data[t])return delete this.data[t],delete this.internalIds[t],t}else if(t instanceof Object){var e=t[this.fieldId];if(e&&this.data[e])return delete this.data[e],delete this.internalIds[e],e}return null},DataSet.prototype.clear=function(t){var e=Object.keys(this.data);return this.data={},this.internalIds={},this._trigger("remove",{items:e},t),e},DataSet.prototype.max=function(t){var e=this.data,i=null,s=null;for(var n in e)if(e.hasOwnProperty(n)){var o=e[n],a=o[t];null!=a&&(!i||a>s)&&(i=o,s=a)}return i},DataSet.prototype.min=function(t){var e=this.data,i=null,s=null;for(var n in e)if(e.hasOwnProperty(n)){var o=e[n],a=o[t];null!=a&&(!i||s>a)&&(i=o,s=a)}return i},DataSet.prototype.distinct=function(t){var e=this.data,i=[],s=this.options.convert[t],n=0;for(var o in e)if(e.hasOwnProperty(o)){for(var a=e[o],r=util.convert(a[t],s),h=!1,d=0;n>d;d++)if(i[d]==r){h=!0;break}h||void 0===r||(i[n]=r,n++)}return i},DataSet.prototype._addItem=function(t){var e=t[this.fieldId];if(void 0!=e){if(this.data[e])throw new Error("Cannot add item: item with id "+e+" already exists")}else e=util.randomUUID(),t[this.fieldId]=e,this.internalIds[e]=t;var i={};for(var s in t)if(t.hasOwnProperty(s)){var n=this.convert[s];i[s]=util.convert(t[s],n)}return this.data[e]=i,e},DataSet.prototype._getItem=function(t,e){var i,s,n=this.data[t];if(!n)return null;var o={},a=this.fieldId,r=this.internalIds;if(e)for(i in n)n.hasOwnProperty(i)&&(s=n[i],i==a&&s in r&&!this.showInternalIds||(o[i]=util.convert(s,e[i])));else for(i in n)n.hasOwnProperty(i)&&(s=n[i],i==a&&s in r&&!this.showInternalIds||(o[i]=s));return o},DataSet.prototype._updateItem=function(t){var e=t[this.fieldId];if(void 0==e)throw new Error("Cannot update item: item has no id (item: "+JSON.stringify(t)+")");var i=this.data[e];if(!i)throw new Error("Cannot update item: no item with id "+e+" found");for(var s in t)if(t.hasOwnProperty(s)){var n=this.convert[s];i[s]=util.convert(t[s],n)}return e},DataSet.prototype.isInternalId=function(t){return t in this.internalIds},DataSet.prototype._getColumnNames=function(t){for(var e=[],i=0,s=t.getNumberOfColumns();s>i;i++)e[i]=t.getColumnId(i)||t.getColumnLabel(i);return e},DataSet.prototype._appendRow=function(t,e,i){for(var s=t.addRow(),n=0,o=e.length;o>n;n++){var a=e[n];t.setValue(s,n,i[a])}},DataView.prototype.setData=function(t){var e,i,s;if(this.data){this.data.unsubscribe&&this.data.unsubscribe("*",this.listener),e=[];for(var n in this.ids)this.ids.hasOwnProperty(n)&&e.push(n);this.ids={},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++)n=e[i],this.ids[n]=!0;this._trigger("add",{items:e}),this.data.on&&this.data.on("*",this.listener)}},DataView.prototype.get=function(){var t,e,i,s=this,n=util.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 o=util.extend({},this.options,e);this.options.filter&&e&&e.filter&&(o.filter=function(t){return s.options.filter(t)&&e.filter(t)});var a=[];return void 0!=t&&a.push(t),a.push(o),a.push(i),this.data&&this.data.get.apply(this.data,a)},DataView.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},DataView.prototype._onEvent=function(t,e,i){var s,n,o,a,r=e&&e.items,h=this.data,d=[],l=[],c=[];if(r&&h){switch(t){case"add":for(s=0,n=r.length;n>s;s++)o=r[s],a=this.get(o),a&&(this.ids[o]=!0,d.push(o));break;case"update":for(s=0,n=r.length;n>s;s++)o=r[s],a=this.get(o),a?this.ids[o]?l.push(o):(this.ids[o]=!0,d.push(o)):this.ids[o]&&(delete this.ids[o],c.push(o));break;case"remove":for(s=0,n=r.length;n>s;s++)o=r[s],this.ids[o]&&(delete this.ids[o],c.push(o))}d.length&&this._trigger("add",{items:d},i),l.length&&this._trigger("update",{items:l},i),c.length&&this._trigger("remove",{items:c},i)}},DataView.prototype.on=DataSet.prototype.on,DataView.prototype.off=DataSet.prototype.off,DataView.prototype._trigger=DataSet.prototype._trigger,DataView.prototype.subscribe=DataView.prototype.on,DataView.prototype.unsubscribe=DataView.prototype.off;var stack={};stack.orderByStart=function(t){t.sort(function(t,e){return t.data.start-e.data.start})},stack.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})},stack.stack=function(t,e,i){var s,n;if(i)for(s=0,n=t.length;n>s;s++)t[s].top=null;for(s=0,n=t.length;n>s;s++){var o=t[s];if(null===o.top){o.top=e.axis;do{for(var a=null,r=0,h=t.length;h>r;r++){var d=t[r];if(null!==d.top&&d!==o&&stack.collision(o,d,e.item)){a=d;break}}null!=a&&(o.top=a.top+a.height+e.item)}while(a)}}},stack.nostack=function(t,e){var i,s;for(i=0,s=t.length;s>i;i++)t[i].top=e.axis},stack.collision=function(t,e,i){return t.left-ie.left&&t.top-ie.top},TimeStep.SCALE={MILLISECOND:1,SECOND:2,MINUTE:3,HOUR:4,DAY:5,WEEKDAY:6,MONTH:7,YEAR:8},TimeStep.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)},TimeStep.prototype.first=function(){this.current=new Date(this._start.valueOf()),this.roundToMinor()},TimeStep.prototype.roundToMinor=function(){switch(this.scale){case TimeStep.SCALE.YEAR:this.current.setFullYear(this.step*Math.floor(this.current.getFullYear()/this.step)),this.current.setMonth(0);case TimeStep.SCALE.MONTH:this.current.setDate(1);case TimeStep.SCALE.DAY:case TimeStep.SCALE.WEEKDAY:this.current.setHours(0);case TimeStep.SCALE.HOUR:this.current.setMinutes(0);case TimeStep.SCALE.MINUTE:this.current.setSeconds(0);case TimeStep.SCALE.SECOND:this.current.setMilliseconds(0)}if(1!=this.step)switch(this.scale){case TimeStep.SCALE.MILLISECOND:this.current.setMilliseconds(this.current.getMilliseconds()-this.current.getMilliseconds()%this.step);break;case TimeStep.SCALE.SECOND:this.current.setSeconds(this.current.getSeconds()-this.current.getSeconds()%this.step);break;case TimeStep.SCALE.MINUTE:this.current.setMinutes(this.current.getMinutes()-this.current.getMinutes()%this.step);break;case TimeStep.SCALE.HOUR:this.current.setHours(this.current.getHours()-this.current.getHours()%this.step);break;case TimeStep.SCALE.WEEKDAY:case TimeStep.SCALE.DAY:this.current.setDate(this.current.getDate()-1-(this.current.getDate()-1)%this.step+1);break;case TimeStep.SCALE.MONTH:this.current.setMonth(this.current.getMonth()-this.current.getMonth()%this.step);break;case TimeStep.SCALE.YEAR:this.current.setFullYear(this.current.getFullYear()-this.current.getFullYear()%this.step)}},TimeStep.prototype.hasNext=function(){return this.current.valueOf()<=this._end.valueOf()},TimeStep.prototype.next=function(){var t=this.current.valueOf();if(this.current.getMonth()<6)switch(this.scale){case TimeStep.SCALE.MILLISECOND:this.current=new Date(this.current.valueOf()+this.step);break;case TimeStep.SCALE.SECOND:this.current=new Date(this.current.valueOf()+1e3*this.step);break;case TimeStep.SCALE.MINUTE:this.current=new Date(this.current.valueOf()+1e3*this.step*60);break;case TimeStep.SCALE.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 TimeStep.SCALE.WEEKDAY:case TimeStep.SCALE.DAY:this.current.setDate(this.current.getDate()+this.step);break;case TimeStep.SCALE.MONTH:this.current.setMonth(this.current.getMonth()+this.step);break;case TimeStep.SCALE.YEAR:this.current.setFullYear(this.current.getFullYear()+this.step)}else switch(this.scale){case TimeStep.SCALE.MILLISECOND:this.current=new Date(this.current.valueOf()+this.step);break;case TimeStep.SCALE.SECOND:this.current.setSeconds(this.current.getSeconds()+this.step);break;case TimeStep.SCALE.MINUTE:this.current.setMinutes(this.current.getMinutes()+this.step);break;case TimeStep.SCALE.HOUR:this.current.setHours(this.current.getHours()+this.step);break;case TimeStep.SCALE.WEEKDAY:case TimeStep.SCALE.DAY:this.current.setDate(this.current.getDate()+this.step);break;case TimeStep.SCALE.MONTH:this.current.setMonth(this.current.getMonth()+this.step);break;case TimeStep.SCALE.YEAR:this.current.setFullYear(this.current.getFullYear()+this.step)}if(1!=this.step)switch(this.scale){case TimeStep.SCALE.MILLISECOND:this.current.getMilliseconds()0&&(this.step=e),this.autoScale=!1},TimeStep.prototype.setAutoScale=function(t){this.autoScale=t},TimeStep.prototype.setMinimumStep=function(t){if(void 0!=t){var e=31104e6,i=2592e6,s=864e5,n=36e5,o=6e4,a=1e3,r=1;1e3*e>t&&(this.scale=TimeStep.SCALE.YEAR,this.step=1e3),500*e>t&&(this.scale=TimeStep.SCALE.YEAR,this.step=500),100*e>t&&(this.scale=TimeStep.SCALE.YEAR,this.step=100),50*e>t&&(this.scale=TimeStep.SCALE.YEAR,this.step=50),10*e>t&&(this.scale=TimeStep.SCALE.YEAR,this.step=10),5*e>t&&(this.scale=TimeStep.SCALE.YEAR,this.step=5),e>t&&(this.scale=TimeStep.SCALE.YEAR,this.step=1),3*i>t&&(this.scale=TimeStep.SCALE.MONTH,this.step=3),i>t&&(this.scale=TimeStep.SCALE.MONTH,this.step=1),5*s>t&&(this.scale=TimeStep.SCALE.DAY,this.step=5),2*s>t&&(this.scale=TimeStep.SCALE.DAY,this.step=2),s>t&&(this.scale=TimeStep.SCALE.DAY,this.step=1),s/2>t&&(this.scale=TimeStep.SCALE.WEEKDAY,this.step=1),4*n>t&&(this.scale=TimeStep.SCALE.HOUR,this.step=4),n>t&&(this.scale=TimeStep.SCALE.HOUR,this.step=1),15*o>t&&(this.scale=TimeStep.SCALE.MINUTE,this.step=15),10*o>t&&(this.scale=TimeStep.SCALE.MINUTE,this.step=10),5*o>t&&(this.scale=TimeStep.SCALE.MINUTE,this.step=5),o>t&&(this.scale=TimeStep.SCALE.MINUTE,this.step=1),15*a>t&&(this.scale=TimeStep.SCALE.SECOND,this.step=15),10*a>t&&(this.scale=TimeStep.SCALE.SECOND,this.step=10),5*a>t&&(this.scale=TimeStep.SCALE.SECOND,this.step=5),a>t&&(this.scale=TimeStep.SCALE.SECOND,this.step=1),200*r>t&&(this.scale=TimeStep.SCALE.MILLISECOND,this.step=200),100*r>t&&(this.scale=TimeStep.SCALE.MILLISECOND,this.step=100),50*r>t&&(this.scale=TimeStep.SCALE.MILLISECOND,this.step=50),10*r>t&&(this.scale=TimeStep.SCALE.MILLISECOND,this.step=10),5*r>t&&(this.scale=TimeStep.SCALE.MILLISECOND,this.step=5),r>t&&(this.scale=TimeStep.SCALE.MILLISECOND,this.step=1)}},TimeStep.prototype.snap=function(t){var e=new Date(t.valueOf());if(this.scale==TimeStep.SCALE.YEAR){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(this.scale==TimeStep.SCALE.MONTH)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(this.scale==TimeStep.SCALE.DAY){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(this.scale==TimeStep.SCALE.WEEKDAY){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(this.scale==TimeStep.SCALE.HOUR){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(this.scale==TimeStep.SCALE.MINUTE){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(this.scale==TimeStep.SCALE.SECOND)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(this.scale==TimeStep.SCALE.MILLISECOND){var s=this.step>5?this.step/2:1;e.setMilliseconds(Math.round(e.getMilliseconds()/s)*s)}return e},TimeStep.prototype.isMajor=function(){switch(this.scale){case TimeStep.SCALE.MILLISECOND:return 0==this.current.getMilliseconds();case TimeStep.SCALE.SECOND:return 0==this.current.getSeconds();case TimeStep.SCALE.MINUTE:return 0==this.current.getHours()&&0==this.current.getMinutes();case TimeStep.SCALE.HOUR:return 0==this.current.getHours();case TimeStep.SCALE.WEEKDAY:case TimeStep.SCALE.DAY:return 1==this.current.getDate();case TimeStep.SCALE.MONTH:return 0==this.current.getMonth();case TimeStep.SCALE.YEAR:return!1;default:return!1}},TimeStep.prototype.getLabelMinor=function(t){switch(void 0==t&&(t=this.current),this.scale){case TimeStep.SCALE.MILLISECOND:return moment(t).format("SSS");case TimeStep.SCALE.SECOND:return moment(t).format("s");case TimeStep.SCALE.MINUTE:return moment(t).format("HH:mm");case TimeStep.SCALE.HOUR:return moment(t).format("HH:mm");case TimeStep.SCALE.WEEKDAY:return moment(t).format("ddd D");case TimeStep.SCALE.DAY:return moment(t).format("D");case TimeStep.SCALE.MONTH:return moment(t).format("MMM");case TimeStep.SCALE.YEAR:return moment(t).format("YYYY");default:return""}},TimeStep.prototype.getLabelMajor=function(t){switch(void 0==t&&(t=this.current),this.scale){case TimeStep.SCALE.MILLISECOND:return moment(t).format("HH:mm:ss");case TimeStep.SCALE.SECOND:return moment(t).format("D MMMM HH:mm");case TimeStep.SCALE.MINUTE:case TimeStep.SCALE.HOUR:return moment(t).format("ddd D MMMM");case TimeStep.SCALE.WEEKDAY:case TimeStep.SCALE.DAY:return moment(t).format("MMMM YYYY");case TimeStep.SCALE.MONTH:return moment(t).format("YYYY");case TimeStep.SCALE.YEAR:return"";default:return""}},Emitter(Range.prototype),Range.prototype.setOptions=function(t){util.extend(this.options,t),null!==this.start&&null!==this.end&&this.setRange(this.start,this.end)},Range.prototype.setRange=function(t,e){var i=this._applyRange(t,e);if(i){var s={start:new Date(this.start),end:new Date(this.end)};this.emit("rangechange",s),this.emit("rangechanged",s)}},Range.prototype._applyRange=function(t,e){var i,s=null!=t?util.convert(t,"Date").valueOf():this.start,n=null!=e?util.convert(e,"Date").valueOf():this.end,o=null!=this.options.max?util.convert(this.options.max,"Date").valueOf():null,a=null!=this.options.min?util.convert(this.options.min,"Date").valueOf():null;if(isNaN(s)||null===s)throw new Error('Invalid start "'+t+'"');if(isNaN(n)||null===n)throw new Error('Invalid end "'+e+'"');if(s>n&&(n=s),null!==a&&a>s&&(i=a-s,s+=i,n+=i,null!=o&&n>o&&(n=o)),null!==o&&n>o&&(i=n-o,s-=i,n-=i,null!=a&&a>s&&(s=a)),null!==this.options.zoomMin){var r=parseFloat(this.options.zoomMin);0>r&&(r=0),r>n-s&&(this.end-this.start===r?(s=this.start,n=this.end):(i=r-(n-s),s-=i/2,n+=i/2))}if(null!==this.options.zoomMax){var h=parseFloat(this.options.zoomMax);0>h&&(h=0),n-s>h&&(this.end-this.start===h?(s=this.start,n=this.end):(i=n-s-h,s+=i/2,n-=i/2))}var d=this.start!=s||this.end!=n;return this.start=s,this.end=n,d},Range.prototype.getRange=function(){return{start:this.start,end:this.end}},Range.prototype.conversion=function(t){return Range.conversion(this.start,this.end,t)},Range.conversion=function(t,e,i){return 0!=i&&e-t!=0?{offset:t,scale:i/(e-t)}:{offset:0,scale:1}};var touchParams={};Range.prototype._onDragStart=function(){if(!touchParams.ignore){touchParams.start=this.start,touchParams.end=this.end;var t=this.parent.frame;t&&(t.style.cursor="move")}},Range.prototype._onDrag=function(t){var e=this.options.direction;if(validateDirection(e),!touchParams.ignore){var i="horizontal"==e?t.gesture.deltaX:t.gesture.deltaY,s=touchParams.end-touchParams.start,n="horizontal"==e?this.parent.width:this.parent.height,o=-i/n*s;this._applyRange(touchParams.start+o,touchParams.end+o),this.emit("rangechange",{start:new Date(this.start),end:new Date(this.end)})}},Range.prototype._onDragEnd=function(){touchParams.ignore||(this.parent.frame&&(this.parent.frame.style.cursor="auto"),this.emit("rangechanged",{start:new Date(this.start),end:new Date(this.end)}))},Range.prototype._onMouseWheel=function(t){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=util.fakeGesture(this,t),n=getPointer(s.center,this.parent.frame),o=this._pointerToDate(n);this.zoom(i,o)}t.preventDefault()},Range.prototype._onTouch=function(t){touchParams.start=this.start,touchParams.end=this.end,touchParams.ignore=!1,touchParams.center=null;var e=ItemSet.itemFromTarget(t);e&&e.selected&&this.options.editable&&(touchParams.ignore=!0)},Range.prototype._onHold=function(){touchParams.ignore=!0},Range.prototype._onPinch=function(t){this.options.direction;if(touchParams.ignore=!0,t.gesture.touches.length>1){touchParams.center||(touchParams.center=getPointer(t.gesture.center,this.parent.frame));var e=1/t.gesture.scale,i=this._pointerToDate(touchParams.center),s=getPointer(t.gesture.center,this.parent.frame),n=(this._pointerToDate(this.parent,s),parseInt(i+(touchParams.start-i)*e)),o=parseInt(i+(touchParams.end-i)*e);this.setRange(n,o)}},Range.prototype._pointerToDate=function(t){var e,i=this.options.direction;if(validateDirection(i),"horizontal"==i){var s=this.parent.width;return e=this.conversion(s),t.x/e.scale+e.offset}var n=this.parent.height;return e=this.conversion(n),t.y/e.scale+e.offset},Range.prototype.zoom=function(t,e){null==e&&(e=(this.start+this.end)/2);var i=e+(this.start-e)*t,s=e+(this.end-e)*t;this.setRange(i,s)},Range.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},Range.prototype.moveTo=function(t){var e=(this.start+this.end)/2,i=e-t,s=this.start-i,n=this.end-i;this.setRange(s,n)},Emitter(Component.prototype),Component.prototype.setOptions=function(t){t&&(util.extend(this.options,t),this.repaint())},Component.prototype.getOption=function(t){var e;return this.options&&(e=this.options[t]),void 0===e&&this.defaultOptions&&(e=this.defaultOptions[t]),e},Component.prototype.getFrame=function(){return null},Component.prototype.repaint=function(){return!1},Component.prototype._isResized=function(){var t=this._previousWidth!==this.width||this._previousHeight!==this.height;return this._previousWidth=this.width,this._previousHeight=this.height,t},Panel.prototype=new Component,Panel.prototype.setOptions=Component.prototype.setOptions,Panel.prototype.getFrame=function(){return this.frame},Panel.prototype.appendChild=function(t){this.childs.push(t),t.parent=this;var e=t.getFrame();e&&(e.parentNode&&e.parentNode.removeChild(e),this.frame.appendChild(e))},Panel.prototype.insertBefore=function(t,e){var i=this.childs.indexOf(e);if(-1!=i){this.childs.splice(i,0,t),t.parent=this;var s=t.getFrame();
-if(s){s.parentNode&&s.parentNode.removeChild(s);var n=e.getFrame();n?this.frame.insertBefore(s,n):this.frame.appendChild(s)}}},Panel.prototype.removeChild=function(t){var e=this.childs.indexOf(t);if(-1!=e){this.childs.splice(e,1),t.parent=null;var i=t.getFrame();i&&i.parentNode&&this.frame.removeChild(i)}},Panel.prototype.hasChild=function(t){var e=this.childs.indexOf(t);return-1!=e},Panel.prototype.repaint=function(){var t=util.option.asString,e=this.options,i=this.getFrame();i.className="vpanel"+(e.className?" "+t(e.className):"");var s=this._repaintChilds();return this._updateSize(),this._isResized()||s},Panel.prototype._repaintChilds=function(){for(var t=!1,e=0,i=this.childs.length;i>e;e++)t=this.childs[e].repaint()||t;return t},Panel.prototype._updateSize=function(){this.frame.style.top=util.option.asSize(this.options.top),this.frame.style.bottom=util.option.asSize(this.options.bottom),this.frame.style.left=util.option.asSize(this.options.left),this.frame.style.right=util.option.asSize(this.options.right),this.frame.style.width=util.option.asSize(this.options.width,"100%"),this.frame.style.height=util.option.asSize(this.options.height,""),this.top=this.frame.offsetTop,this.left=this.frame.offsetLeft,this.width=this.frame.offsetWidth,this.height=this.frame.offsetHeight},RootPanel.prototype=new Panel,RootPanel.prototype._create=function(){this.frame=document.createElement("div"),this.hammer=Hammer(this.frame,{prevent_default:!0}),this.listeners={};var t=this,e=["touch","pinch","tap","doubletap","hold","dragstart","drag","dragend","mousewheel","DOMMouseScroll"];e.forEach(function(e){var i=function(){var i=[e].concat(Array.prototype.slice.call(arguments,0));t.emit.apply(t,i)};t.hammer.on(e,i),t.listeners[e]=i})},RootPanel.prototype.setOptions=function(t){t&&(util.extend(this.options,t),this.repaint(),this._initWatch())},RootPanel.prototype.getFrame=function(){return this.frame},RootPanel.prototype.repaint=function(){var t=this.options,e=t.editable.updateTime||t.editable.updateGroup,i="vis timeline rootpanel "+t.orientation+(e?" editable":"");t.className&&(i+=" "+util.option.asString(i)),this.frame.className=i;var s=this._repaintChilds();this.frame.style.maxHeight=util.option.asSize(this.options.maxHeight,""),this.frame.style.minHeight=util.option.asSize(this.options.minHeight,""),this._updateSize();var n=this._isResized()||s;n&&setTimeout(this.repaint.bind(this),0)},RootPanel.prototype._initWatch=function(){var t=this.getOption("autoResize");t?this._watch():this._unwatch()},RootPanel.prototype._watch=function(){var t=this;this._unwatch();var e=function(){var e=t.getOption("autoResize");return e?void(t.frame&&(t.frame.clientWidth!=t.lastWidth||t.frame.clientHeight!=t.lastHeight)&&(t.lastWidth=t.frame.clientWidth,t.lastHeight=t.frame.clientHeight,t.repaint())):void t._unwatch()};util.addEventListener(window,"resize",e),this.watchTimer=setInterval(e,1e3)},RootPanel.prototype._unwatch=function(){this.watchTimer&&(clearInterval(this.watchTimer),this.watchTimer=void 0)},TimeAxis.prototype=new Component,TimeAxis.prototype.setOptions=Component.prototype.setOptions,TimeAxis.prototype._create=function(){this.frame=document.createElement("div")},TimeAxis.prototype.setRange=function(t){if(!(t instanceof Range||t&&t.start&&t.end))throw new TypeError("Range must be an instance of Range, or an object containing start and end.");this.range=t},TimeAxis.prototype.getFrame=function(){return this.frame},TimeAxis.prototype.repaint=function(){var t=util.option.asSize,e=this.options,i=this.props,s=this.frame;s.className="timeaxis";var n=s.parentNode;if(n){this._calculateCharSize();var o=this.getOption("orientation"),a=this.getOption("showMinorLabels"),r=this.getOption("showMajorLabels"),h=this.parent.height;i.minorLabelHeight=a?i.minorCharHeight:0,i.majorLabelHeight=r?i.majorCharHeight:0,this.height=i.minorLabelHeight+i.majorLabelHeight,this.width=s.offsetWidth,i.minorLineHeight=h+i.minorLabelHeight,i.minorLineWidth=1,i.majorLineHeight=h+this.height,i.majorLineWidth=1;var d=s.nextSibling;n.removeChild(s),"top"==o?(s.style.top="0",s.style.left="0",s.style.bottom="",s.style.width=t(e.width,"100%"),s.style.height=this.height+"px"):(s.style.top="",s.style.bottom="0",s.style.left="0",s.style.width=t(e.width,"100%"),s.style.height=this.height+"px"),this._repaintLabels(),this._repaintLine(),d?n.insertBefore(s,d):n.appendChild(s)}return this._isResized()},TimeAxis.prototype._repaintLabels=function(){var t=this.getOption("orientation"),e=util.convert(this.range.start,"Number"),i=util.convert(this.range.end,"Number"),s=this.options.toTime(7*(this.props.minorCharWidth||10)).valueOf()-this.options.toTime(0).valueOf(),n=new TimeStep(new Date(e),new Date(i),s);this.step=n;var o=this.dom;o.redundant.majorLines=o.majorLines,o.redundant.majorTexts=o.majorTexts,o.redundant.minorLines=o.minorLines,o.redundant.minorTexts=o.minorTexts,o.majorLines=[],o.majorTexts=[],o.minorLines=[],o.minorTexts=[],n.first();for(var a=void 0,r=0;n.hasNext()&&1e3>r;){r++;var h=n.getCurrent(),d=this.options.toScreen(h),l=n.isMajor();this.getOption("showMinorLabels")&&this._repaintMinorText(d,n.getLabelMinor(),t),l&&this.getOption("showMajorLabels")?(d>0&&(void 0==a&&(a=d),this._repaintMajorText(d,n.getLabelMajor(),t)),this._repaintMajorLine(d,t)):this._repaintMinorLine(d,t),n.next()}if(this.getOption("showMajorLabels")){var c=this.options.toTime(0),u=n.getLabelMajor(c),p=u.length*(this.props.majorCharWidth||10)+10;(void 0==a||a>p)&&this._repaintMajorText(0,u,t)}util.forEach(this.dom.redundant,function(t){for(;t.length;){var e=t.pop();e&&e.parentNode&&e.parentNode.removeChild(e)}})},TimeAxis.prototype._repaintMinorText=function(t,e,i){var s=this.dom.redundant.minorTexts.shift();if(!s){var n=document.createTextNode("");s=document.createElement("div"),s.appendChild(n),s.className="text minor",this.frame.appendChild(s)}this.dom.minorTexts.push(s),s.childNodes[0].nodeValue=e,"top"==i?(s.style.top=this.props.majorLabelHeight+"px",s.style.bottom=""):(s.style.top="",s.style.bottom=this.props.majorLabelHeight+"px"),s.style.left=t+"px"},TimeAxis.prototype._repaintMajorText=function(t,e,i){var s=this.dom.redundant.majorTexts.shift();if(!s){var n=document.createTextNode(e);s=document.createElement("div"),s.className="text major",s.appendChild(n),this.frame.appendChild(s)}this.dom.majorTexts.push(s),s.childNodes[0].nodeValue=e,"top"==i?(s.style.top="0px",s.style.bottom=""):(s.style.top="",s.style.bottom="0px"),s.style.left=t+"px"},TimeAxis.prototype._repaintMinorLine=function(t,e){var i=this.dom.redundant.minorLines.shift();i||(i=document.createElement("div"),i.className="grid vertical minor",this.frame.appendChild(i)),this.dom.minorLines.push(i);var s=this.props;"top"==e?(i.style.top=this.props.majorLabelHeight+"px",i.style.bottom=""):(i.style.top="",i.style.bottom=this.props.majorLabelHeight+"px"),i.style.height=s.minorLineHeight+"px",i.style.left=t-s.minorLineWidth/2+"px"},TimeAxis.prototype._repaintMajorLine=function(t,e){var i=this.dom.redundant.majorLines.shift();i||(i=document.createElement("DIV"),i.className="grid vertical major",this.frame.appendChild(i)),this.dom.majorLines.push(i);var s=this.props;"top"==e?(i.style.top="0px",i.style.bottom=""):(i.style.top="",i.style.bottom="0px"),i.style.left=t-s.majorLineWidth/2+"px",i.style.height=s.majorLineHeight+"px"},TimeAxis.prototype._repaintLine=function(){var t=this.dom.line,e=this.frame,i=this.getOption("orientation");this.getOption("showMinorLabels")||this.getOption("showMajorLabels")?(t?(e.removeChild(t),e.appendChild(t)):(t=document.createElement("div"),t.className="grid horizontal major",e.appendChild(t),this.dom.line=t),"top"==i?(t.style.top=this.height+"px",t.style.bottom=""):(t.style.top="",t.style.bottom=this.height+"px")):t&&t.parentNode&&(t.parentNode.removeChild(t),delete this.dom.line)},TimeAxis.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.frame.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 minor measure",this.dom.measureCharMajor.style.position="absolute",this.dom.measureCharMajor.appendChild(document.createTextNode("0")),this.frame.appendChild(this.dom.measureCharMajor)),this.props.majorCharHeight=this.dom.measureCharMajor.clientHeight,this.props.majorCharWidth=this.dom.measureCharMajor.clientWidth},TimeAxis.prototype.snap=function(t){return this.step.snap(t)},CurrentTime.prototype=new Component,CurrentTime.prototype.setOptions=Component.prototype.setOptions,CurrentTime.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},CurrentTime.prototype.getFrame=function(){return this.bar},CurrentTime.prototype.repaint=function(){var t=(this.parent,new Date),e=this.options.toScreen(t);return this.bar.style.left=e+"px",this.bar.title="Current time: "+t,!1},CurrentTime.prototype.start=function(){function t(){e.stop();var i=e.range.conversion(e.parent.width).scale,s=1/i/10;30>s&&(s=30),s>1e3&&(s=1e3),e.repaint(),e.currentTimeTimer=setTimeout(t,s)}var e=this;t()},CurrentTime.prototype.stop=function(){void 0!==this.currentTimeTimer&&(clearTimeout(this.currentTimeTimer),delete this.currentTimeTimer)},CustomTime.prototype=new Component,CustomTime.prototype.setOptions=Component.prototype.setOptions,CustomTime.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=Hammer(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))},CustomTime.prototype.getFrame=function(){return this.bar},CustomTime.prototype.repaint=function(){var t=this.options.toScreen(this.customTime);return this.bar.style.left=t+"px",this.bar.title="Time: "+this.customTime,!1},CustomTime.prototype.setCustomTime=function(t){this.customTime=new Date(t.valueOf()),this.repaint()},CustomTime.prototype.getCustomTime=function(){return new Date(this.customTime.valueOf())},CustomTime.prototype._onDragStart=function(t){this.eventParams.dragging=!0,this.eventParams.customTime=this.customTime,t.stopPropagation(),t.preventDefault()},CustomTime.prototype._onDrag=function(t){if(this.eventParams.dragging){var e=t.gesture.deltaX,i=this.options.toScreen(this.eventParams.customTime)+e,s=this.options.toTime(i);this.setCustomTime(s),this.emit("timechange",{time:new Date(this.customTime.valueOf())}),t.stopPropagation(),t.preventDefault()}},CustomTime.prototype._onDragEnd=function(t){this.eventParams.dragging&&(this.emit("timechanged",{time:new Date(this.customTime.valueOf())}),t.stopPropagation(),t.preventDefault())};var UNGROUPED="__ungrouped__";ItemSet.prototype=new Panel,ItemSet.types={box:ItemBox,range:ItemRange,rangeoverflow:ItemRangeOverflow,point:ItemPoint},ItemSet.prototype._create=function(){var t=document.createElement("div");t["timeline-itemset"]=this,this.frame=t;var e=document.createElement("div");e.className="background",this.backgroundPanel.frame.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,this.axisPanel.frame.appendChild(s);var n=document.createElement("div");n.className="labelset",this.dom.labelSet=n,this.sidePanel.frame.appendChild(n),this._updateUngrouped(),this.hammer=Hammer(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))},ItemSet.prototype.setOptions=function(t){Component.prototype.setOptions.call(this,t)},ItemSet.prototype.markDirty=function(){this.groupIds=[],this.stackDirty=!0},ItemSet.prototype.hide=function(){this.dom.axis.parentNode&&this.dom.axis.parentNode.removeChild(this.dom.axis),this.dom.background.parentNode&&this.dom.background.parentNode.removeChild(this.dom.background),this.dom.labelSet.parentNode&&this.dom.labelSet.parentNode.removeChild(this.dom.labelSet)},ItemSet.prototype.show=function(){this.dom.axis.parentNode||this.axisPanel.frame.appendChild(this.dom.axis),this.dom.background.parentNode||this.backgroundPanel.frame.appendChild(this.dom.background),this.dom.labelSet.parentNode||this.sidePanel.frame.appendChild(this.dom.labelSet)},ItemSet.prototype.setRange=function(t){if(!(t instanceof Range||t&&t.start&&t.end))throw new TypeError("Range must be an instance of Range, or an object containing start and end.");this.range=t},ItemSet.prototype.setSelection=function(t){var e,i,s,n;if(t){if(!Array.isArray(t))throw new TypeError("Array expected");for(e=0,i=this.selection.length;i>e;e++)s=this.selection[e],n=this.items[s],n&&n.unselect();for(this.selection=[],e=0,i=t.length;i>e;e++)s=t[e],n=this.items[s],n&&(this.selection.push(s),n.select())}},ItemSet.prototype.getSelection=function(){return this.selection.concat([])},ItemSet.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}},ItemSet.prototype.getFrame=function(){return this.frame},ItemSet.prototype.repaint=function(){var t=this.options.margin,e=this.range,i=util.option.asSize,s=util.option.asString,n=this.options,o=this.getOption("orientation"),a=!1,r=this.frame;"number"==typeof t&&(t={item:t,axis:t}),r.className="itemset"+(n.className?" "+s(n.className):""),a=this._orderGroups()||a;var h=this.range.end-this.range.start,d=h!=this.lastVisibleInterval||this.width!=this.lastWidth;d&&(this.stackDirty=!0),this.lastVisibleInterval=h,this.lastWidth=this.width;var l=this.stackDirty,c=this._firstGroup(),u={item:t.item,axis:t.axis},p={item:t.item,axis:t.item/2},m=0,g=t.axis+t.item;return util.forEach(this.groups,function(t){var i=t==c?u:p;a=t.repaint(e,i,l)||a,m+=t.height}),m=Math.max(m,g),this.stackDirty=!1,r.style.left=i(n.left,""),r.style.right=i(n.right,""),r.style.top=i("top"==o?"0":""),r.style.bottom=i("top"==o?"":"0"),r.style.width=i(n.width,"100%"),r.style.height=i(m),this.top=r.offsetTop,this.left=r.offsetLeft,this.width=r.offsetWidth,this.height=m,this.dom.axis.style.left=i(n.left,"0"),this.dom.axis.style.right=i(n.right,""),this.dom.axis.style.width=i(n.width,"100%"),this.dom.axis.style.height=i(0),this.dom.axis.style.top=i("top"==o?"0":""),this.dom.axis.style.bottom=i("top"==o?"":"0"),a=this._isResized()||a},ItemSet.prototype._firstGroup=function(){var t="top"==this.options.orientation?0:this.groupIds.length-1,e=this.groupIds[t],i=this.groups[e]||this.groups[UNGROUPED];return i||null},ItemSet.prototype._updateUngrouped=function(){var t=this.groups[UNGROUPED];if(this.groupsData)t&&(t.hide(),delete this.groups[UNGROUPED]);else if(!t){var e=null,i=null;t=new Group(e,i,this),this.groups[UNGROUPED]=t;for(var s in this.items)this.items.hasOwnProperty(s)&&t.add(this.items[s]);t.show()}},ItemSet.prototype.getForeground=function(){return this.dom.foreground},ItemSet.prototype.getBackground=function(){return this.dom.background},ItemSet.prototype.getAxis=function(){return this.dom.axis},ItemSet.prototype.getLabelSet=function(){return this.dom.labelSet},ItemSet.prototype.setItems=function(t){var e,i=this,s=this.itemsData;if(t){if(!(t instanceof DataSet||t instanceof DataView))throw new TypeError("Data must be an instance of DataSet or DataView");this.itemsData=t}else this.itemsData=null;if(s&&(util.forEach(this.itemListeners,function(t,e){s.unsubscribe(e,t)}),e=s.getIds(),this._onRemove(e)),this.itemsData){var n=this.id;util.forEach(this.itemListeners,function(t,e){i.itemsData.on(e,t,n)}),e=this.itemsData.getIds(),this._onAdd(e),this._updateUngrouped()}},ItemSet.prototype.getItems=function(){return this.itemsData},ItemSet.prototype.setGroups=function(t){var e,i=this;if(this.groupsData&&(util.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 DataSet||t instanceof DataView))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;util.forEach(this.groupListeners,function(t,e){i.groupsData.on(e,t,s)}),e=this.groupsData.getIds(),this._onAddGroups(e)}this._updateUngrouped(),this._order(),this.emit("change")},ItemSet.prototype.getGroups=function(){return this.groupsData},ItemSet.prototype.removeItem=function(t){var e=this.itemsData.get(t),i=this._myDataSet();e&&this.options.onRemove(e,function(e){e&&i.remove(t)})},ItemSet.prototype._onUpdate=function(t){var e=this,i=this.items,s=this.itemOptions;t.forEach(function(t){var n=e.itemsData.get(t),o=i[t],a=n.type||n.start&&n.end&&"range"||e.options.type||"box",r=ItemSet.types[a];if(o&&(r&&o instanceof r?e._updateItem(o,n):(e._removeItem(o),o=null)),!o){if(!r)throw new TypeError('Unknown item type "'+a+'"');o=new r(n,e.options,s),o.id=t,e._addItem(o)}}),this._order(),this.stackDirty=!0,this.emit("change")},ItemSet.prototype._onAdd=ItemSet.prototype._onUpdate,ItemSet.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.emit("change"))},ItemSet.prototype._order=function(){util.forEach(this.groups,function(t){t.order()})},ItemSet.prototype._onUpdateGroups=function(t){this._onAddGroups(t)},ItemSet.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==UNGROUPED)throw new Error("Illegal group id. "+t+" is a reserved id.");var n=Object.create(e.options);util.extend(n,{height:null}),s=new Group(t,i,e),e.groups[t]=s;for(var o in e.items)if(e.items.hasOwnProperty(o)){var a=e.items[o];a.data.group==t&&s.add(a)}s.order(),s.show()}}),this.emit("change")},ItemSet.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.emit("change")},ItemSet.prototype._orderGroups=function(){if(this.groupsData){var t=this.groupsData.getIds({order:this.options.groupOrder}),e=!util.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},ItemSet.prototype._addItem=function(t){this.items[t.id]=t;var e=this.groupsData?t.data.group:UNGROUPED,i=this.groups[e];i&&i.add(t)},ItemSet.prototype._updateItem=function(t,e){var i=t.data.group;if(t.data=e,t.displayed&&t.repaint(),i!=t.data.group){var s=this.groups[i];s&&s.remove(t);var n=this.groupsData?t.data.group:UNGROUPED,o=this.groups[n];o&&o.add(t)}},ItemSet.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);var i=this.groupsData?t.data.group:UNGROUPED,s=this.groups[i];s&&s.remove(t)},ItemSet.prototype._constructByEndArray=function(t){for(var e=[],i=0;it.start-e&&this.data.startt.start-e&&this.data.startt.start},ItemRange.prototype.repaint=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.parent)throw new Error("Cannot repaint item: no parent attached");if(!t.box.parentNode){var e=this.parent.getForeground();if(!e)throw new Error("Cannot repaint time axis: parent has no foreground container element");e.appendChild(t.box)}if(this.displayed=!0,this.data.content!=this.content){if(this.content=this.data.content,this.content instanceof Element)t.content.innerHTML="",t.content.appendChild(this.content);else{if(void 0==this.data.content)throw new Error('Property "content" missing in item '+this.data.id);t.content.innerHTML=this.content}this.dirty=!0}var i=(this.data.className?" "+this.data.className:"")+(this.selected?" selected":"");this.className!=i&&(this.className=i,t.box.className=this.baseClassName+i,this.dirty=!0),this.dirty&&(this.props.content.width=this.dom.content.offsetWidth,this.height=this.dom.box.offsetHeight,this.dirty=!1),this._repaintDeleteButton(t.box),this._repaintDragLeft(),this._repaintDragRight()},ItemRange.prototype.show=function(){this.displayed||this.repaint()},ItemRange.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}},ItemRange.prototype.repositionX=function(){var t,e=this.props,i=this.parent.width,s=this.defaultOptions.toScreen(this.data.start),n=this.defaultOptions.toScreen(this.data.end),o="padding"in this.options?this.options.padding:this.defaultOptions.padding;-i>s&&(s=-i),n>2*i&&(n=2*i),t=0>s?Math.min(-s,n-s-e.content.width-2*o):0,this.left=s,this.width=Math.max(n-s,1),this.dom.box.style.left=this.left+"px",this.dom.box.style.width=this.width+"px",this.dom.content.style.left=t+"px"},ItemRange.prototype.repositionY=function(){var t=this.options.orientation||this.defaultOptions.orientation,e=this.dom.box;"top"==t?(e.style.top=this.top+"px",e.style.bottom=""):(e.style.top="",e.style.bottom=this.top+"px")},ItemRange.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,Hammer(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)},ItemRange.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,Hammer(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)},ItemRangeOverflow.prototype=new ItemRange(null),ItemRangeOverflow.prototype.baseClassName="item rangeoverflow",ItemRangeOverflow.prototype.repositionX=function(){{var t,e=this.parent.width,i=this.defaultOptions.toScreen(this.data.start),s=this.defaultOptions.toScreen(this.data.end);"padding"in this.options?this.options.padding:this.defaultOptions.padding}-e>i&&(i=-e),s>2*e&&(s=2*e),t=Math.max(-i,0),this.left=i;var n=Math.max(s-i,1);this.width=n+this.props.content.width,this.dom.box.style.left=this.left+"px",this.dom.box.style.width=n+"px",this.dom.content.style.left=t+"px"},Group.prototype._create=function(){var t=document.createElement("div");t.className="vlabel",this.dom.label=t;var e=document.createElement("div");e.className="inner",t.appendChild(e),this.dom.inner=e;var i=document.createElement("div");i.className="group",i["timeline-group"]=this,this.dom.foreground=i,this.dom.background=document.createElement("div"),this.dom.axis=document.createElement("div"),this.dom.marker=document.createElement("div"),this.dom.marker.style.visibility="hidden",this.dom.marker.innerHTML="?",this.dom.background.appendChild(this.dom.marker)},Group.prototype.setData=function(t){var e=t&&t.content;e instanceof Element?this.dom.inner.appendChild(e):this.dom.inner.innerHTML=void 0!=e?e:this.groupId;var i=t&&t.className;i&&util.addClassName(this.dom.label,i)},Group.prototype.getForeground=function(){return this.dom.foreground},Group.prototype.getBackground=function(){return this.dom.background},Group.prototype.getAxis=function(){return this.dom.axis},Group.prototype.getLabelWidth=function(){return this.props.label.width},Group.prototype.repaint=function(t,e,i){var s=!1;this.visibleItems=this._updateVisibleItems(this.orderedItems,this.visibleItems,t);var n=this.dom.marker.clientHeight;n!=this.lastMarkerHeight&&(this.lastMarkerHeight=n,util.forEach(this.items,function(t){t.dirty=!0,t.displayed&&t.repaint()}),i=!0),this.itemSet.options.stack?stack.stack(this.visibleItems,e,i):stack.nostack(this.visibleItems,e);for(var o=0,a=this.visibleItems.length;a>o;o++){var r=this.visibleItems[o];r.repositionY()}var h,d=this.visibleItems;if(d.length){var l=d[0].top,c=d[0].top+d[0].height;util.forEach(d,function(t){l=Math.min(l,t.top),c=Math.max(c,t.top+t.height)}),h=c-l+e.axis+e.item}else h=e.axis+e.item;h=Math.max(h,this.props.label.height);var u=this.dom.foreground;return this.top=u.offsetTop,this.left=u.offsetLeft,this.width=u.offsetWidth,s=util.updateProperty(this,"height",h)||s,s=util.updateProperty(this.props.label,"width",this.dom.inner.clientWidth)||s,s=util.updateProperty(this.props.label,"height",this.dom.inner.clientHeight)||s,u.style.height=h+"px",this.dom.label.style.height=h+"px",s},Group.prototype.show=function(){this.dom.label.parentNode||this.itemSet.getLabelSet().appendChild(this.dom.label),this.dom.foreground.parentNode||this.itemSet.getForeground().appendChild(this.dom.foreground),this.dom.background.parentNode||this.itemSet.getBackground().appendChild(this.dom.background),this.dom.axis.parentNode||this.itemSet.getAxis().appendChild(this.dom.axis)},Group.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)},Group.prototype.add=function(t){if(this.items[t.id]=t,t.setParent(this),t instanceof ItemRange&&-1==this.visibleItems.indexOf(t)){var e=this.itemSet.range;this._checkIfVisible(t,this.visibleItems,e)}},Group.prototype.remove=function(t){delete this.items[t.id],t.setParent(this.itemSet);var e=this.visibleItems.indexOf(t);-1!=e&&this.visibleItems.splice(e,1)},Group.prototype.removeFromDataSet=function(t){this.itemSet.removeItem(t.id)},Group.prototype.order=function(){var t=util.toArray(this.items);this.orderedItems.byStart=t,this.orderedItems.byEnd=this._constructByEndArray(t),stack.orderByStart(this.orderedItems.byStart),stack.orderByEnd(this.orderedItems.byEnd)},Group.prototype._constructByEndArray=function(t){for(var e=[],i=0;i0)for(n=0;n=0&&!this._checkIfInvisible(t.byStart[n],o,i);n--);for(n=s+1;n=0&&!this._checkIfInvisible(t.byEnd[n],o,i);n--);for(n=a+1;ne.start-a&&s[l].data[n]e.start-a&&s[l].data[n]=s&&(s=864e5),e=new Date(e.valueOf()-.05*s),i=new Date(i.valueOf()+.05*s)}(null!==e||null!==i)&&this.range.setRange(e,i)},Timeline.prototype.getItemRange=function(){var t=this.itemsData,e=null,i=null;if(t){var s=t.min("start");e=s?s.start.valueOf():null;var n=t.max("start");n&&(i=n.start.valueOf());var o=t.max("end");o&&(i=null==i?o.end.valueOf():Math.max(i,o.end.valueOf()))}return{min:null!=e?new Date(e):null,max:null!=i?new Date(i):null}},Timeline.prototype.setSelection=function(t){this.itemSet.setSelection(t)},Timeline.prototype.getSelection=function(){return this.itemSet.getSelection()},Timeline.prototype.setWindow=function(t,e){if(1==arguments.length){var i=arguments[0];this.range.setRange(i.start,i.end)}else this.range.setRange(t,e)},Timeline.prototype.getWindow=function(){var t=this.range.getRange();return{start:new Date(t.start),end:new Date(t.end)}},Timeline.prototype.redraw=function(){this.rootPanel.repaint()},Timeline.prototype.repaint=function(){throw new Error("Function repaint is deprecated. Use redraw instead.")},Timeline.prototype._onSelectItem=function(t){if(this.options.selectable){var e=t.gesture.srcEvent&&t.gesture.srcEvent.ctrlKey,i=t.gesture.srcEvent&&t.gesture.srcEvent.shiftKey;if(e||i)return void this._onMultiSelectItem(t);var s=this.getSelection(),n=ItemSet.itemFromTarget(t),o=n?[n.id]:[];this.setSelection(o);var a=this.getSelection();(a.length>0||s.length>0)&&this.emit("select",{items:this.getSelection()}),t.stopPropagation()}},Timeline.prototype._onAddItem=function(t){if(this.options.selectable&&this.options.editable.add){var e=this,i=ItemSet.itemFromTarget(t);if(i){var s=e.itemsData.get(i.id);this.options.onUpdate(s,function(t){t&&e.itemsData.update(t)})}else{var n=vis.util.getAbsoluteLeft(this.contentPanel.frame),o=t.gesture.center.pageX-n,a={start:this.timeAxis.snap(this._toTime(o)),content:"new item"};("range"===this.options.type||"rangeoverflow"==this.options.type)&&(a.end=this.timeAxis.snap(this._toTime(o+this.rootPanel.width/5)));var r=util.randomUUID();a[this.itemsData.fieldId]=r;var h=ItemSet.groupFromTarget(t);h&&(a.group=h.groupId),this.options.onAdd(a,function(t){t&&e.itemsData.add(a)})}}},Timeline.prototype._onMultiSelectItem=function(t){if(this.options.selectable){var e,i=ItemSet.itemFromTarget(t);if(i){e=this.getSelection();var s=e.indexOf(i.id);-1==s?e.push(i.id):e.splice(s,1),this.setSelection(e),this.emit("select",{items:this.getSelection()}),t.stopPropagation()}}},Timeline.prototype._toTime=function(t){var e=this.range.conversion(this.mainPanel.width);return new Date(t/e.scale+e.offset)},Timeline.prototype._toScreen=function(t){var e=this.range.conversion(this.mainPanel.width);return(t.valueOf()-e.offset)*e.scale},function(t){function e(t){return M=t,u()}function i(){D=0,C=M.charAt(0)}function s(){D++,C=M.charAt(D)}function n(){return M.charAt(D+1)}function o(t){return P.test(t)}function a(t,e){if(t||(t={}),e)for(var i in e)e.hasOwnProperty(i)&&(t[i]=e[i]);return t}function r(t,e,i){for(var s=e.split("."),n=t;s.length;){var o=s.shift();s.length?(n[o]||(n[o]={}),n=n[o]):n[o]=i}}function h(t,e){for(var i,s,n=null,o=[t],r=t;r.parent;)o.push(r.parent),r=r.parent;if(r.nodes)for(i=0,s=r.nodes.length;s>i;i++)if(e.id===r.nodes[i].id){n=r.nodes[i];break}for(n||(n={id:e.id},t.node&&(n.attr=a(n.attr,t.node))),i=o.length-1;i>=0;i--){var h=o[i];h.nodes||(h.nodes=[]),-1==h.nodes.indexOf(n)&&h.nodes.push(n)}e.attr&&(n.attr=a(n.attr,e.attr))}function d(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 l(t,e,i,s,n){var o={from:e,to:i,type:s};return t.edge&&(o.attr=a({},t.edge)),o.attr=a(o.attr||{},n),o}function c(){for(O=T.NULL,I="";" "==C||" "==C||"\n"==C||"\r"==C;)s();do{var t=!1;if("#"==C){for(var e=D-1;" "==M.charAt(e)||" "==M.charAt(e);)e--;if("\n"==M.charAt(e)||""==M.charAt(e)){for(;""!=C&&"\n"!=C;)s();t=!0}}if("/"==C&&"/"==n()){for(;""!=C&&"\n"!=C;)s();t=!0}if("/"==C&&"*"==n()){for(;""!=C;){if("*"==C&&"/"==n()){s(),s();break}s()}t=!0}for(;" "==C||" "==C||"\n"==C||"\r"==C;)s()}while(t);if(""==C)return void(O=T.DELIMITER);var i=C+n();if(E[i])return O=T.DELIMITER,I=i,s(),void s();if(E[C])return O=T.DELIMITER,I=C,void s();if(o(C)||"-"==C){for(I+=C,s();o(C);)I+=C,s();return"false"==I?I=!1:"true"==I?I=!0:isNaN(Number(I))||(I=Number(I)),void(O=T.IDENTIFIER)}if('"'==C){for(s();""!=C&&('"'!=C||'"'==C&&'"'==n());)I+=C,'"'==C&&s(),s();if('"'!=C)throw b('End of string " expected');return s(),void(O=T.IDENTIFIER)}for(O=T.UNKNOWN;""!=C;)I+=C,s();throw new SyntaxError('Syntax error in part "'+x(I,30)+'"')}function u(){var t={};if(i(),c(),"strict"==I&&(t.strict=!0,c()),("graph"==I||"digraph"==I)&&(t.type=I,c()),O==T.IDENTIFIER&&(t.id=I,c()),"{"!=I)throw b("Angle bracket { expected");if(c(),p(t),"}"!=I)throw b("Angle bracket } expected");if(c(),""!==I)throw b("End of file expected");return c(),delete t.node,delete t.edge,delete t.graph,t}function p(t){for(;""!==I&&"}"!=I;)m(t),";"==I&&c()}function m(t){var e=g(t);if(e)return void y(t,e);var i=f(t);if(!i){if(O!=T.IDENTIFIER)throw b("Identifier expected");var s=I;if(c(),"="==I){if(c(),O!=T.IDENTIFIER)throw b("Identifier expected");t[s]=I,c()}else v(t,s)}}function g(t){var e=null;if("subgraph"==I&&(e={},e.type="subgraph",c(),O==T.IDENTIFIER&&(e.id=I,c())),"{"==I){if(c(),e||(e={}),e.parent=t,e.node=t.node,e.edge=t.edge,e.graph=t.graph,p(e),"}"!=I)throw b("Angle bracket } expected");c(),delete e.node,delete e.edge,delete e.graph,delete e.parent,t.subgraphs||(t.subgraphs=[]),t.subgraphs.push(e)}return e}function f(t){return"node"==I?(c(),t.node=_(),"node"):"edge"==I?(c(),t.edge=_(),"edge"):"graph"==I?(c(),t.graph=_(),"graph"):null}function v(t,e){var i={id:e},s=_();s&&(i.attr=s),h(t,i),y(t,e)}function y(t,e){for(;"->"==I||"--"==I;){var i,s=I;c();var n=g(t);if(n)i=n;else{if(O!=T.IDENTIFIER)throw b("Identifier or subgraph expected");i=I,h(t,{id:i}),c()}var o=_(),a=l(t,e,i,s,o);d(t,a),e=i}}function _(){for(var t=null;"["==I;){for(c(),t={};""!==I&&"]"!=I;){if(O!=T.IDENTIFIER)throw b("Attribute name expected");var e=I;if(c(),"="!=I)throw b("Equal sign = expected");if(c(),O!=T.IDENTIFIER)throw b("Attribute value expected");var i=I;r(t,e,i),c(),","==I&&c()}if("]"!=I)throw b("Bracket ] expected");c()}return t}function b(t){return new SyntaxError(t+', got "'+x(I,30)+'" (char '+D+")")}function x(t,e){return t.length<=e?t:t.substr(0,27)+"..."}function w(t,e,i){t instanceof Array?t.forEach(function(t){e instanceof Array?e.forEach(function(e){i(t,e)}):i(t,e)}):e instanceof Array?e.forEach(function(e){i(t,e)}):i(t,e)}function S(t){function i(t){var e={from:t.from,to:t.to};return a(e,t.attr),e.style="->"==t.type?"arrow":"line",e}var s=e(t),n={nodes:[],edges:[],options:{}};return s.nodes&&s.nodes.forEach(function(t){var e={id:t.id,label:String(t.label||t.id)};a(e,t.attr),e.image&&(e.shape="image"),n.nodes.push(e)}),s.edges&&s.edges.forEach(function(t){var e,s;e=t.from instanceof Object?t.from.nodes:{id:t.from},s=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=i(t);n.edges.push(e)}),w(e,s,function(e,s){var o=l(n,e.id,s.id,t.type,t.attr),a=i(o);n.edges.push(a)}),t.to instanceof Object&&t.to.edges&&t.to.edges.forEach(function(t){var e=i(t);n.edges.push(e)})}),s.attr&&(n.options=s.attr),n}var T={NULL:0,DELIMITER:1,IDENTIFIER:2,UNKNOWN:3},E={"{":!0,"}":!0,"[":!0,"]":!0,";":!0,"=":!0,",":!0,"->":!0,"--":!0},M="",D=0,C="",I="",O=T.NULL,P=/[a-zA-Z_0-9.:#]/;t.parseDOT=e,t.DOTToGraph=S}("undefined"!=typeof util?util:exports),"undefined"!=typeof CanvasRenderingContext2D&&(CanvasRenderingContext2D.prototype.circle=function(t,e,i){this.beginPath(),this.arc(t,e,i,0,2*Math.PI,!1)},CanvasRenderingContext2D.prototype.square=function(t,e,i){this.beginPath(),this.rect(t-i,e-i,2*i,2*i)},CanvasRenderingContext2D.prototype.triangle=function(t,e,i){this.beginPath();var s=2*i,n=s/2,o=Math.sqrt(3)/6*s,a=Math.sqrt(s*s-n*n);this.moveTo(t,e-(a-o)),this.lineTo(t+n,e+o),this.lineTo(t-n,e+o),this.lineTo(t,e-(a-o)),this.closePath()},CanvasRenderingContext2D.prototype.triangleDown=function(t,e,i){this.beginPath();var s=2*i,n=s/2,o=Math.sqrt(3)/6*s,a=Math.sqrt(s*s-n*n);this.moveTo(t,e+(a-o)),this.lineTo(t+n,e-o),this.lineTo(t-n,e-o),this.lineTo(t,e+(a-o)),this.closePath()},CanvasRenderingContext2D.prototype.star=function(t,e,i){this.beginPath();for(var s=0;10>s;s++){var n=s%2===0?1.3*i:.5*i;this.lineTo(t+n*Math.sin(2*s*Math.PI/10),e-n*Math.cos(2*s*Math.PI/10))}this.closePath()},CanvasRenderingContext2D.prototype.roundRect=function(t,e,i,s,n){var o=Math.PI/180;0>i-2*n&&(n=i/2),0>s-2*n&&(n=s/2),this.beginPath(),this.moveTo(t+n,e),this.lineTo(t+i-n,e),this.arc(t+i-n,e+n,n,270*o,360*o,!1),this.lineTo(t+i,e+s-n),this.arc(t+i-n,e+s-n,n,0,90*o,!1),this.lineTo(t+n,e+s),this.arc(t+n,e+s-n,n,90*o,180*o,!1),this.lineTo(t,e+n),this.arc(t+n,e+n,n,180*o,270*o,!1)},CanvasRenderingContext2D.prototype.ellipse=function(t,e,i,s){var n=.5522848,o=i/2*n,a=s/2*n,r=t+i,h=e+s,d=t+i/2,l=e+s/2;this.beginPath(),this.moveTo(t,l),this.bezierCurveTo(t,l-a,d-o,e,d,e),this.bezierCurveTo(d+o,e,r,l-a,r,l),this.bezierCurveTo(r,l+a,d+o,h,d,h),this.bezierCurveTo(d-o,h,t,l+a,t,l)},CanvasRenderingContext2D.prototype.database=function(t,e,i,s){var n=1/3,o=i,a=s*n,r=.5522848,h=o/2*r,d=a/2*r,l=t+o,c=e+a,u=t+o/2,p=e+a/2,m=e+(s-a/2),g=e+s;this.beginPath(),this.moveTo(l,p),this.bezierCurveTo(l,p+d,u+h,c,u,c),this.bezierCurveTo(u-h,c,t,p+d,t,p),this.bezierCurveTo(t,p-d,u-h,e,u,e),this.bezierCurveTo(u+h,e,l,p-d,l,p),this.lineTo(l,m),this.bezierCurveTo(l,m+d,u+h,g,u,g),this.bezierCurveTo(u-h,g,t,m+d,t,m),this.lineTo(t,p)},CanvasRenderingContext2D.prototype.arrow=function(t,e,i,s){var n=t-s*Math.cos(i),o=e-s*Math.sin(i),a=t-.9*s*Math.cos(i),r=e-.9*s*Math.sin(i),h=n+s/3*Math.cos(i+.5*Math.PI),d=o+s/3*Math.sin(i+.5*Math.PI),l=n+s/3*Math.cos(i-.5*Math.PI),c=o+s/3*Math.sin(i-.5*Math.PI);this.beginPath(),this.moveTo(t,e),this.lineTo(h,d),this.lineTo(a,r),this.lineTo(l,c),this.closePath()},CanvasRenderingContext2D.prototype.dashedLine=function(t,e,i,s,n){n||(n=[10,5]),0==u&&(u=.001);var o=n.length;this.moveTo(t,e);for(var a=i-t,r=s-e,h=r/a,d=Math.sqrt(a*a+r*r),l=0,c=!0;d>=.1;){var u=n[l++%o];u>d&&(u=d);var p=Math.sqrt(u*u/(1+h*h));0>a&&(p=-p),t+=p,e+=h*p,this[c?"lineTo":"moveTo"](t,e),d-=u,c=!c}}),Node.prototype.resetCluster=function(){this.formationScale=void 0,this.clusterSize=1,this.containedNodes={},this.containedEdges={},this.clusterSessions=[]},Node.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},Node.prototype.detachEdge=function(t){var e=this.edges.indexOf(t);-1!=e&&(this.edges.splice(e,1),this.dynamicEdges.splice(e,1)),this.dynamicEdgesLength=this.dynamicEdges.length},Node.prototype.setProperties=function(t,e){if(t){if(this.originalLabel=void 0,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.group&&(this.group=t.group),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.mass&&(this.mass=t.mass),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(this.group){var i=this.grouplist.get(this.group);for(var s in i)i.hasOwnProperty(s)&&(this[s]=i[s])}if(void 0!==t.shape&&(this.shape=t.shape),void 0!==t.image&&(this.image=t.image),void 0!==t.radius&&(this.radius=t.radius),void 0!==t.color&&(this.color=util.parseColor(t.color)),void 0!==t.fontColor&&(this.fontColor=t.fontColor),void 0!==t.fontSize&&(this.fontSize=t.fontSize),void 0!==t.fontFace&&(this.fontFace=t.fontFace),void 0!==this.image&&""!=this.image){if(!this.imagelist)throw"No imagelist provided";this.imageObj=this.imagelist.load(this.image)}switch(this.xFixed=this.xFixed||void 0!==t.x&&!t.allowedToMoveX,this.yFixed=this.yFixed||void 0!==t.y&&!t.allowedToMoveY,this.radiusFixed=this.radiusFixed||void 0!==t.radius,"image"==this.shape&&(this.radiusMin=e.nodes.widthMin,this.radiusMax=e.nodes.widthMax),this.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"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()}},Node.prototype.select=function(){this.selected=!0,this._reset()},Node.prototype.unselect=function(){this.selected=!1,this._reset()},Node.prototype.clearSizeCache=function(){this._reset()},Node.prototype._reset=function(){this.width=void 0,this.height=void 0},Node.prototype.getTitle=function(){return"function"==typeof this.title?this.title():this.title},Node.prototype.distanceToBorder=function(t,e){var i=1;switch(this.width||this.resize(t),this.shape){case"circle":case"dot":return this.radius+i;case"ellipse":var s=this.width/2,n=this.height/2,o=Math.sin(e)*s,a=Math.cos(e)*n;return s*n/Math.sqrt(o*o+a*a);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}},Node.prototype._setForce=function(t,e){this.fx=t,this.fy=e},Node.prototype._addForce=function(t,e){this.fx+=t,this.fy+=e},Node.prototype.discreteStep=function(t){if(!this.xFixed){var e=this.damping*this.vx,i=(this.fx-e)/this.mass;this.vx+=i*t,this.x+=this.vx*t}if(!this.yFixed){var s=this.damping*this.vy,n=(this.fy-s)/this.mass;this.vy+=n*t,this.y+=this.vy*t}},Node.prototype.discreteStepLimited=function(t,e){if(this.xFixed)this.fx=0;else{var i=this.damping*this.vx,s=(this.fx-i)/this.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;else{var n=this.damping*this.vy,o=(this.fy-n)/this.mass;this.vy+=o*t,this.vy=Math.abs(this.vy)>e?this.vy>0?e:-e:this.vy,this.y+=this.vy*t}},Node.prototype.isFixed=function(){return this.xFixed&&this.yFixed},Node.prototype.isMoving=function(t){return Math.abs(this.vx)>t||Math.abs(this.vy)>t},Node.prototype.isSelected=function(){return this.selected},Node.prototype.getValue=function(){return this.value},Node.prototype.getDistance=function(t,e){var i=this.x-t,s=this.y-e;return Math.sqrt(i*i+s*s)},Node.prototype.setValueRange=function(t,e){if(!this.radiusFixed&&void 0!==this.value)if(e==t)this.radius=(this.radiusMin+this.radiusMax)/2;else{var i=(this.radiusMax-this.radiusMin)/(e-t);this.radius=(this.value-t)*i+this.radiusMin}this.baseRadiusValue=this.radius},Node.prototype.draw=function(){throw"Draw method not initialized for node"},Node.prototype.resize=function(){throw"Resize method not initialized for node"},Node.prototype.isOverlappingWith=function(t){return this.leftt.left&&this.topt.top},Node.prototype._resizeImage=function(){if(!this.width||!this.height){var t,e;if(this.value){this.radius=this.baseRadiusValue;var i=this.imageObj.height/this.imageObj.width;void 0!==i?(t=this.radius||this.imageObj.width,e=this.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.radius+=Math.min(this.clusterSize-1,this.maxNodeSizeIncrements)*this.clusterSizeRadiusFactor,this.growthIndicator=this.width-t)}},Node.prototype._drawImage=function(t){this._resizeImage(t),this.left=this.x-this.width/2,this.top=this.y-this.height/2;var e;if(0!=this.imageObj.width){if(this.clusterSize>1){var i=this.clusterSize>1?10:0;i*=this.graphScaleInv,i=Math.min(.2*this.width,i),t.globalAlpha=.5,t.drawImage(this.imageObj,this.left-i,this.top-i,this.width+2*i,this.height+2*i)}t.globalAlpha=1,t.drawImage(this.imageObj,this.left,this.top,this.width,this.height),e=this.y+this.height/2}else e=this.y;this._label(t,this.label,this.x,e,void 0,"top")},Node.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)}},Node.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=2;t.strokeStyle=this.selected?this.color.highlight.border:this.hover?this.color.hover.border:this.color.border,this.clusterSize>1&&(t.lineWidth=(this.selected?i:1)+(this.clusterSize>1?e:0),t.lineWidth*=this.graphScaleInv,t.lineWidth=Math.min(.1*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.radius),t.stroke()),t.lineWidth=(this.selected?i:1)+(this.clusterSize>1?e:0),t.lineWidth*=this.graphScaleInv,t.lineWidth=Math.min(.1*this.width,t.lineWidth),t.fillStyle=this.selected?this.color.highlight.background:this.color.background,t.roundRect(this.left,this.top,this.width,this.height,this.radius),t.fill(),t.stroke(),this._label(t,this.label,this.x,this.y)},Node.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.radius+=Math.min(this.clusterSize-1,this.maxNodeSizeIncrements)*this.clusterSizeRadiusFactor,this.growthIndicator=this.width-s}},Node.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=2;t.strokeStyle=this.selected?this.color.highlight.border:this.hover?this.color.hover.border:this.color.border,this.clusterSize>1&&(t.lineWidth=(this.selected?i:1)+(this.clusterSize>1?e:0),t.lineWidth*=this.graphScaleInv,t.lineWidth=Math.min(.1*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?i:1)+(this.clusterSize>1?e:0),t.lineWidth*=this.graphScaleInv,t.lineWidth=Math.min(.1*this.width,t.lineWidth),t.fillStyle=this.selected?this.color.highlight.background:this.hover?this.color.hover.background:this.color.background,t.database(this.x-this.width/2,this.y-.5*this.height,this.width,this.height),t.fill(),t.stroke(),this._label(t,this.label,this.x,this.y)},Node.prototype._resizeCircle=function(t){if(!this.width){var e=5,i=this.getTextSize(t),s=Math.max(i.width,i.height)+2*e;this.radius=s/2,this.width=s,this.height=s,this.radius+=.5*Math.min(this.clusterSize-1,this.maxNodeSizeIncrements)*this.clusterSizeRadiusFactor,this.growthIndicator=this.radius-.5*s}},Node.prototype._drawCircle=function(t){this._resizeCircle(t),this.left=this.x-this.width/2,this.top=this.y-this.height/2;var e=2.5,i=2;t.strokeStyle=this.selected?this.color.highlight.border:this.hover?this.color.hover.border:this.color.border,this.clusterSize>1&&(t.lineWidth=(this.selected?i:1)+(this.clusterSize>1?e:0),t.lineWidth*=this.graphScaleInv,t.lineWidth=Math.min(.1*this.width,t.lineWidth),t.circle(this.x,this.y,this.radius+2*t.lineWidth),t.stroke()),t.lineWidth=(this.selected?i:1)+(this.clusterSize>1?e:0),t.lineWidth*=this.graphScaleInv,t.lineWidth=Math.min(.1*this.width,t.lineWidth),t.fillStyle=this.selected?this.color.highlight.background:this.hover?this.color.hover.background:this.color.background,t.circle(this.x,this.y,this.radius),t.fill(),t.stroke(),this._label(t,this.label,this.x,this.y)},Node.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?i:1)+(this.clusterSize>1?e:0),t.lineWidth*=this.graphScaleInv,t.lineWidth=Math.min(.1*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?i:1)+(this.clusterSize>1?e:0),t.lineWidth*=this.graphScaleInv,t.lineWidth=Math.min(.1*this.width,t.lineWidth),t.fillStyle=this.selected?this.color.highlight.background:this.hover?this.color.hover.background:this.color.background,t.ellipse(this.left,this.top,this.width,this.height),t.fill(),t.stroke(),this._label(t,this.label,this.x,this.y)},Node.prototype._drawDot=function(t){this._drawShape(t,"circle")},Node.prototype._drawTriangle=function(t){this._drawShape(t,"triangle")},Node.prototype._drawTriangleDown=function(t){this._drawShape(t,"triangleDown")},Node.prototype._drawSquare=function(t){this._drawShape(t,"square")},Node.prototype._drawStar=function(t){this._drawShape(t,"star")},Node.prototype._resizeShape=function(){if(!this.width){this.radius=this.baseRadiusValue;var t=2*this.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.radius+=.5*Math.min(this.clusterSize-1,this.maxNodeSizeIncrements)*this.clusterSizeRadiusFactor,this.growthIndicator=this.width-t}},Node.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=2,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.color.highlight.border:this.hover?this.color.hover.border:this.color.border,this.clusterSize>1&&(t.lineWidth=(this.selected?s:1)+(this.clusterSize>1?i:0),t.lineWidth*=this.graphScaleInv,t.lineWidth=Math.min(.1*this.width,t.lineWidth),t[e](this.x,this.y,this.radius+n*t.lineWidth),t.stroke()),t.lineWidth=(this.selected?s:1)+(this.clusterSize>1?i:0),t.lineWidth*=this.graphScaleInv,t.lineWidth=Math.min(.1*this.width,t.lineWidth),t.fillStyle=this.selected?this.color.highlight.background:this.hover?this.color.hover.background:this.color.background,t[e](this.x,this.y,this.radius),t.fill(),t.stroke(),this.label&&this._label(t,this.label,this.x,this.y+this.height/2,void 0,"top")
-},Node.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.radius+=Math.min(this.clusterSize-1,this.maxNodeSizeIncrements)*this.clusterSizeRadiusFactor,this.growthIndicator=this.width-(i.width+2*e)}},Node.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)},Node.prototype._label=function(t,e,i,s,n,o){if(e&&this.fontSize*this.graphScale>this.fontDrawThreshold){t.font=(this.selected?"bold ":"")+this.fontSize+"px "+this.fontFace,t.fillStyle=this.fontColor||"black",t.textAlign=n||"center",t.textBaseline=o||"middle";for(var a=e.split("\n"),r=a.length,h=this.fontSize+4,d=s+(1-r)/2*h,l=0;r>l;l++)t.fillText(a[l],i,d),d+=h}},Node.prototype.getTextSize=function(t){if(void 0!==this.label){t.font=(this.selected?"bold ":"")+this.fontSize+"px "+this.fontFace;for(var e=this.label.split("\n"),i=(this.fontSize+4)*e.length,s=0,n=0,o=e.length;o>n;n++)s=Math.max(s,t.measureText(e[n]).width);return{width:s,height:i}}return{width:0,height:0}},Node.prototype.inArea=function(){return void 0!==this.width?this.x+this.width*this.graphScaleInv>=this.canvasTopLeft.x&&this.x-this.width*this.graphScaleInv=this.canvasTopLeft.y&&this.y-this.height*this.graphScaleInv=this.canvasTopLeft.x&&this.x=this.canvasTopLeft.y&&this.yh}return!1},Edge.prototype._drawLine=function(t){if(t.strokeStyle=1==this.selected?this.color.highlight:1==this.hover?this.color.hover:this.color.color,t.lineWidth=this._getLineWidth(),this.from!=this.to){this._line(t);var e;if(this.label){if(1==this.smooth){var i=.5*(.5*(this.from.x+this.via.x)+.5*(this.to.x+this.via.x)),s=.5*(.5*(this.from.y+this.via.y)+.5*(this.to.y+this.via.y));e={x:i,y:s}}else e=this._pointOnLine(.5);this._label(t,this.label,e.x,e.y)}}else{var n,o,a=this.length/4,r=this.from;r.width||r.resize(t),r.width>r.height?(n=r.x+r.width/2,o=r.y-a):(n=r.x+a,o=r.y-r.height/2),this._circle(t,n,o,a),e=this._pointOnCircle(n,o,a,.5),this._label(t,this.label,e.x,e.y)}},Edge.prototype._getLineWidth=function(){return 1==this.selected?Math.min(2*this.width,this.widthMax)*this.graphScaleInv:1==this.hover?Math.min(this.hoverWidth,this.widthMax)*this.graphScaleInv:this.width*this.graphScaleInv},Edge.prototype._line=function(t){t.beginPath(),t.moveTo(this.from.x,this.from.y),1==this.smooth?t.quadraticCurveTo(this.via.x,this.via.y,this.to.x,this.to.y):t.lineTo(this.to.x,this.to.y),t.stroke()},Edge.prototype._circle=function(t,e,i,s){t.beginPath(),t.arc(e,i,s,0,2*Math.PI,!1),t.stroke()},Edge.prototype._label=function(t,e,i,s){if(e){t.font=(this.from.selected||this.to.selected?"bold ":"")+this.fontSize+"px "+this.fontFace,t.fillStyle=this.fontFill;var n=t.measureText(e).width,o=this.fontSize,a=i-n/2,r=s-o/2;t.fillRect(a,r,n,o),t.fillStyle=this.fontColor||"black",t.textAlign="left",t.textBaseline="top",t.fillText(e,a,r)}},Edge.prototype._drawDashLine=function(t){if(t.strokeStyle=1==this.selected?this.color.highlight:1==this.hover?this.color.hover:this.color.color,t.lineWidth=this._getLineWidth(),void 0!==t.mozDash||void 0!==t.setLineDash){t.beginPath(),t.moveTo(this.from.x,this.from.y);var e=[0];e=void 0!==this.dash.length&&void 0!==this.dash.gap?[this.dash.length,this.dash.gap]:[5,5],"undefined"!=typeof t.setLineDash?(t.setLineDash(e),t.lineDashOffset=0):(t.mozDash=e,t.mozDashOffset=0),1==this.smooth?t.quadraticCurveTo(this.via.x,this.via.y,this.to.x,this.to.y):t.lineTo(this.to.x,this.to.y),t.stroke(),"undefined"!=typeof t.setLineDash?(t.setLineDash([0]),t.lineDashOffset=0):(t.mozDash=[0],t.mozDashOffset=0)}else t.beginPath(),t.lineCap="round",void 0!==this.dash.altLength?t.dashedLine(this.from.x,this.from.y,this.to.x,this.to.y,[this.dash.length,this.dash.gap,this.dash.altLength,this.dash.gap]):void 0!==this.dash.length&&void 0!==this.dash.gap?t.dashedLine(this.from.x,this.from.y,this.to.x,this.to.y,[this.dash.length,this.dash.gap]):(t.moveTo(this.from.x,this.from.y),t.lineTo(this.to.x,this.to.y)),t.stroke();if(this.label){var i;if(1==this.smooth){var s=.5*(.5*(this.from.x+this.via.x)+.5*(this.to.x+this.via.x)),n=.5*(.5*(this.from.y+this.via.y)+.5*(this.to.y+this.via.y));i={x:s,y:n}}else i=this._pointOnLine(.5);this._label(t,this.label,i.x,i.y)}},Edge.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}},Edge.prototype._pointOnCircle=function(t,e,i,s){var n=2*(s-3/8)*Math.PI;return{x:t+i*Math.cos(n),y:e-i*Math.sin(n)}},Edge.prototype._drawArrowCenter=function(t){var e;if(1==this.selected?(t.strokeStyle=this.color.highlight,t.fillStyle=this.color.highlight):1==this.hover?(t.strokeStyle=this.color.hover,t.fillStyle=this.color.hover):(t.strokeStyle=this.color.color,t.fillStyle=this.color.color),t.lineWidth=this._getLineWidth(),this.from!=this.to){this._line(t);var i=Math.atan2(this.to.y-this.from.y,this.to.x-this.from.x),s=(10+5*this.width)*this.arrowScaleFactor;if(1==this.smooth){var n=.5*(.5*(this.from.x+this.via.x)+.5*(this.to.x+this.via.x)),o=.5*(.5*(this.from.y+this.via.y)+.5*(this.to.y+this.via.y));e={x:n,y:o}}else e=this._pointOnLine(.5);t.arrow(e.x,e.y,i,s),t.fill(),t.stroke(),this.label&&this._label(t,this.label,e.x,e.y)}else{var a,r,h=.25*Math.max(100,this.length),d=this.from;d.width||d.resize(t),d.width>d.height?(a=d.x+.5*d.width,r=d.y-h):(a=d.x+h,r=d.y-.5*d.height),this._circle(t,a,r,h);var i=.2*Math.PI,s=(10+5*this.width)*this.arrowScaleFactor;e=this._pointOnCircle(a,r,h,.5),t.arrow(e.x,e.y,i,s),t.fill(),t.stroke(),this.label&&(e=this._pointOnCircle(a,r,h,.5),this._label(t,this.label,e.x,e.y))}},Edge.prototype._drawArrow=function(t){1==this.selected?(t.strokeStyle=this.color.highlight,t.fillStyle=this.color.highlight):1==this.hover?(t.strokeStyle=this.color.hover,t.fillStyle=this.color.hover):(t.strokeStyle=this.color.color,t.fillStyle=this.color.color),t.lineWidth=this._getLineWidth();var e,i;if(this.from!=this.to){e=Math.atan2(this.to.y-this.from.y,this.to.x-this.from.x);var s=this.to.x-this.from.x,n=this.to.y-this.from.y,o=Math.sqrt(s*s+n*n),a=this.from.distanceToBorder(t,e+Math.PI),r=(o-a)/o,h=r*this.from.x+(1-r)*this.to.x,d=r*this.from.y+(1-r)*this.to.y;1==this.smooth&&(e=Math.atan2(this.to.y-this.via.y,this.to.x-this.via.x),s=this.to.x-this.via.x,n=this.to.y-this.via.y,o=Math.sqrt(s*s+n*n));var l,c,u=this.to.distanceToBorder(t,e),p=(o-u)/o;if(1==this.smooth?(l=(1-p)*this.via.x+p*this.to.x,c=(1-p)*this.via.y+p*this.to.y):(l=(1-p)*this.from.x+p*this.to.x,c=(1-p)*this.from.y+p*this.to.y),t.beginPath(),t.moveTo(h,d),1==this.smooth?t.quadraticCurveTo(this.via.x,this.via.y,l,c):t.lineTo(l,c),t.stroke(),i=(10+5*this.width)*this.arrowScaleFactor,t.arrow(l,c,e,i),t.fill(),t.stroke(),this.label){var m;if(1==this.smooth){var g=.5*(.5*(this.from.x+this.via.x)+.5*(this.to.x+this.via.x)),f=.5*(.5*(this.from.y+this.via.y)+.5*(this.to.y+this.via.y));m={x:g,y:f}}else m=this._pointOnLine(.5);this._label(t,this.label,m.x,m.y)}}else{var v,y,_,b=this.from,x=.25*Math.max(100,this.length);b.width||b.resize(t),b.width>b.height?(v=b.x+.5*b.width,y=b.y-x,_={x:v,y:b.y,angle:.9*Math.PI}):(v=b.x+x,y=b.y-.5*b.height,_={x:b.x,y:y,angle:.6*Math.PI}),t.beginPath(),t.arc(v,y,x,0,2*Math.PI,!1),t.stroke();var i=(10+5*this.width)*this.arrowScaleFactor;t.arrow(_.x,_.y,_.angle,i),t.fill(),t.stroke(),this.label&&(m=this._pointOnCircle(v,y,x,.5),this._label(t,this.label,m.x,m.y))}},Edge.prototype._getDistanceToEdge=function(t,e,i,s,n,o){if(1==this.smooth){var a,r,h,d,l,c,u=1e9;for(a=0;10>a;a++)r=.1*a,h=Math.pow(1-r,2)*t+2*r*(1-r)*this.via.x+Math.pow(r,2)*i,d=Math.pow(1-r,2)*e+2*r*(1-r)*this.via.y+Math.pow(r,2)*s,l=Math.abs(n-h),c=Math.abs(o-d),u=Math.min(u,Math.sqrt(l*l+c*c));return u}var p=i-t,m=s-e,g=p*p+m*m,f=((n-t)*p+(o-e)*m)/g;f>1?f=1:0>f&&(f=0);var h=t+f*p,d=e+f*m,l=h-n,c=d-o;return Math.sqrt(l*l+c*c)},Edge.prototype.setScale=function(t){this.graphScaleInv=1/t},Edge.prototype.select=function(){this.selected=!0},Edge.prototype.unselect=function(){this.selected=!1},Edge.prototype.positionBezierNode=function(){null!==this.via&&(this.via.x=.5*(this.from.x+this.to.x),this.via.y=.5*(this.from.y+this.to.y))},Popup.prototype.setPosition=function(t,e){this.x=parseInt(t),this.y=parseInt(e)},Popup.prototype.setText=function(t){this.frame.innerHTML=t},Popup.prototype.show=function(t){if(void 0===t&&(t=!0),t){var e=this.frame.clientHeight,i=this.frame.clientWidth,s=this.frame.parentNode.clientHeight,n=this.frame.parentNode.clientWidth,o=this.y-e;o+e+this.padding>s&&(o=s-e-this.padding),on&&(a=n-i-this.padding),athis.constants.clustering.clusterThreshold&&1==this.constants.clustering.enabled&&this.clusterToFit(this.constants.clustering.reduceToNodes,!1),this._calculateForces())},_calculateForces:function(){this._calculateGravitationalForces(),this._calculateNodeForces(),1==this.constants.smoothCurves?this._calculateSpringForcesWithSupport():this._calculateSpringForces()},_updateCalculationNodes:function(){if(1==this.constants.smoothCurves){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},_calculateGravitationalForces:function(){var t,e,i,s,n,o=this.calculationNodes,a=this.constants.physics.centralGravity,r=0;for(n=0;n
Simulation Mode:
Barnes Hut
Repulsion
Hierarchical
Barnes Hut
gravitationalConstant
0
-20000
centralGravity
0
3
springLength
0
500
springConstant
0
0.5
damping
0
0.3
Repulsion
nodeDistance
0
300
centralGravity
0
3
springLength
0
500
springConstant
0
0.5
damping
0
0.3
Hierarchical
nodeDistance
0
300
centralGravity
0
3
springLength
0
500
springConstant
0
0.5
damping
0
0.3
direction
1
4
levelSeparation
1
500
nodeSpacing
1
500
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=showValueOfRange.bind(this,"graph_BH_gc",-1,"physics_barnesHut_gravitationalConstant"),e=document.getElementById("graph_BH_cg"),e.onchange=showValueOfRange.bind(this,"graph_BH_cg",1,"physics_centralGravity"),e=document.getElementById("graph_BH_sc"),e.onchange=showValueOfRange.bind(this,"graph_BH_sc",1,"physics_springConstant"),e=document.getElementById("graph_BH_sl"),e.onchange=showValueOfRange.bind(this,"graph_BH_sl",1,"physics_springLength"),e=document.getElementById("graph_BH_damp"),e.onchange=showValueOfRange.bind(this,"graph_BH_damp",1,"physics_damping"),e=document.getElementById("graph_R_nd"),e.onchange=showValueOfRange.bind(this,"graph_R_nd",1,"physics_repulsion_nodeDistance"),e=document.getElementById("graph_R_cg"),e.onchange=showValueOfRange.bind(this,"graph_R_cg",1,"physics_centralGravity"),e=document.getElementById("graph_R_sc"),e.onchange=showValueOfRange.bind(this,"graph_R_sc",1,"physics_springConstant"),e=document.getElementById("graph_R_sl"),e.onchange=showValueOfRange.bind(this,"graph_R_sl",1,"physics_springLength"),e=document.getElementById("graph_R_damp"),e.onchange=showValueOfRange.bind(this,"graph_R_damp",1,"physics_damping"),e=document.getElementById("graph_H_nd"),e.onchange=showValueOfRange.bind(this,"graph_H_nd",1,"physics_hierarchicalRepulsion_nodeDistance"),e=document.getElementById("graph_H_cg"),e.onchange=showValueOfRange.bind(this,"graph_H_cg",1,"physics_centralGravity"),e=document.getElementById("graph_H_sc"),e.onchange=showValueOfRange.bind(this,"graph_H_sc",1,"physics_springConstant"),e=document.getElementById("graph_H_sl"),e.onchange=showValueOfRange.bind(this,"graph_H_sl",1,"physics_springLength"),e=document.getElementById("graph_H_damp"),e.onchange=showValueOfRange.bind(this,"graph_H_damp",1,"physics_damping"),e=document.getElementById("graph_H_direction"),e.onchange=showValueOfRange.bind(this,"graph_H_direction",t,"hierarchicalLayout_direction"),e=document.getElementById("graph_H_levsep"),e.onchange=showValueOfRange.bind(this,"graph_H_levsep",1,"hierarchicalLayout_levelSeparation"),e=document.getElementById("graph_H_nspac"),e.onchange=showValueOfRange.bind(this,"graph_H_nspac",1,"hierarchicalLayout_nodeSpacing");var i=document.getElementById("graph_physicsMethod1"),s=document.getElementById("graph_physicsMethod2"),n=document.getElementById("graph_physicsMethod3");s.checked=!0,this.constants.physics.barnesHut.enabled&&(i.checked=!0),this.constants.hierarchicalLayout.enabled&&(n.checked=!0);var o=document.getElementById("graph_toggleSmooth"),a=document.getElementById("graph_repositionNodes"),r=document.getElementById("graph_generateOptions");o.onclick=graphToggleSmoothCurves.bind(this),a.onclick=graphRepositionNodes.bind(this),r.onclick=graphGenerateOptions.bind(this),o.style.background=1==this.constants.smoothCurves?"#A4FF56":"#FF8532",switchConfigurations.apply(this),i.onchange=switchConfigurations.bind(this),s.onchange=switchConfigurations.bind(this),n.onchange=switchConfigurations.bind(this)}},_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)}},hierarchalRepulsionMixin={_calculateNodeForces:function(){var t,e,i,s,n,o,a,r,h,d,l=this.calculationNodes,c=this.calculationNodeIndices,u=5,p=.5*-u,m=this.constants.physics.hierarchicalRepulsion.nodeDistance,g=m;for(h=0;hi&&(o=f*i+u,0==i?i=.01:o/=i,s=t*o,n=e*o,a.fx-=s,a.fy-=n,r.fx+=s,r.fy+=n)}}},barnesHutMixin={_calculateNodeForces:function(){if(0!=this.constants.physics.barnesHut.gravitationalConstant){var t,e=this.calculationNodes,i=this.calculationNodeIndices,s=i.length;this._formBarnesHutTree(e,i);for(var n=this.barnesHutTree,o=0;s>o;o++)t=e[i[o]],this._getForceContribution(n.root.children.NW,t),this._getForceContribution(n.root.children.NE,t),this._getForceContribution(n.root.children.SW,t),this._getForceContribution(n.root.children.SE,t)}},_getForceContribution:function(t,e){if(t.childrenCount>0){var i,s,n;if(i=t.centerOfMass.x-e.x,s=t.centerOfMass.y-e.y,n=Math.sqrt(i*i+s*s),n*t.calcSize>this.constants.physics.barnesHut.theta){0==n&&(n=.1*Math.random(),i=n);var o=this.constants.physics.barnesHut.gravitationalConstant*t.mass*e.mass/(n*n*n),a=i*o,r=s*o;e.fx+=a,e.fy+=r}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==n&&(n=.5*Math.random(),i=n);var o=this.constants.physics.barnesHut.gravitationalConstant*t.mass*e.mass/(n*n*n),a=i*o,r=s*o;e.fx+=a,e.fy+=r}}},_formBarnesHutTree:function(t,e){for(var i,s=e.length,n=Number.MAX_VALUE,o=Number.MAX_VALUE,a=-Number.MAX_VALUE,r=-Number.MAX_VALUE,h=0;s>h;h++){var d=t[e[h]].x,l=t[e[h]].y;n>d&&(n=d),d>a&&(a=d),o>l&&(o=l),l>r&&(r=l)}var c=Math.abs(a-n)-Math.abs(r-o);c>0?(o-=.5*c,r+=.5*c):(n+=.5*c,a-=.5*c);var u=1e-5,p=Math.max(u,Math.abs(a-n)),m=.5*p,g=.5*(n+a),f=.5*(o+r),v={root:{centerOfMass:{x:0,y:0},mass:0,range:{minX:g-m,maxX:g+m,minY:f-m,maxY:f+m},size:p,calcSize:1/p,children:{data:null},maxWidth:0,level:0,childrenCount:4}};for(this._splitBranch(v.root),h=0;s>h;h++)i=t[e[h]],this._placeInTree(v.root,i);this.barnesHutTree=v},_updateBranchMass:function(t,e){var i=t.mass+e.mass,s=1/i;t.centerOfMass.x=t.centerOfMass.x*t.mass+e.x*e.mass,t.centerOfMass.x*=s,t.centerOfMass.y=t.centerOfMass.y*t.mass+e.y*e.mass,t.centerOfMass.y*=s,t.mass=i;var n=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")},_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)}},_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)},_insertRegion:function(t,e){var i,s,n,o,a=.5*t.size;switch(e){case"NW":i=t.range.minX,s=t.range.minX+a,n=t.range.minY,o=t.range.minY+a;break;case"NE":i=t.range.minX+a,s=t.range.maxX,n=t.range.minY,o=t.range.minY+a;break;case"SW":i=t.range.minX,s=t.range.minX+a,n=t.range.minY+a,o=t.range.maxY;break;case"SE":i=t.range.minX+a,s=t.range.maxX,n=t.range.minY+a,o=t.range.maxY}t.children[e]={centerOfMass:{x:0,y:0},mass:0,range:{minX:i,maxX:s,minY:n,maxY:o},size:.5*t.size,calcSize:2*t.calcSize,children:{data:null},maxWidth:0,level:t.level+1,childrenCount:0}},_drawTree:function(t,e){void 0!==this.barnesHutTree&&(t.lineWidth=1,this._drawBranch(this.barnesHutTree.root,t,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()}},repulsionMixin={_calculateNodeForces:function(){var t,e,i,s,n,o,a,r,h,d,l,c=this.calculationNodes,u=this.calculationNodeIndices,p=-2/3,m=4/3,g=this.constants.physics.repulsion.nodeDistance,f=g;for(d=0;di&&(a=.5*f>i?1:v*i+m,a*=0==o?1:1+o*this.constants.clustering.forceAmplification,a/=i,s=t*a,n=e*a,r.fx-=s,r.fy-=n,h.fx+=s,h.fy+=n)}}},HierarchicalLayoutMixin={_resetLevels:function(){for(var t in this.nodes)if(this.nodes.hasOwnProperty(t)){var e=this.nodes[t];0==e.preassignedLevel&&(e.level=-1)}},_setupHierarchicalLayout:function(){if(1==this.constants.hierarchicalLayout.enabled&&this.nodeIndices.length>0){"RL"==this.constants.hierarchicalLayout.direction||"DU"==this.constants.hierarchicalLayout.direction?this.constants.hierarchicalLayout.levelSeparation*=-1:this.constants.hierarchicalLayout.levelSeparation=Math.abs(this.constants.hierarchicalLayout.levelSeparation);var t,e,i=0,s=!1,n=!1;for(e in this.nodes)this.nodes.hasOwnProperty(e)&&(t=this.nodes[e],-1!=t.level?s=!0:n=!0,is&&(o.xFixed=!1,o.x=i[o.level].minPos,a=!0):o.yFixed&&o.level>s&&(o.yFixed=!1,o.y=i[o.level].minPos,a=!0),1==a&&(i[o.level].minPos+=i[o.level].nodeSpacing,o.edges.length>1&&this._placeBranchNodes(o.edges,o.id,i,o.level))}},_setLevel:function(t,e,i){for(var s=0;st)&&(n.level=t,e.length>1&&this._setLevel(t+1,n.edges,n.id))}},_restoreNodes:function(){for(nodeId in this.nodes)this.nodes.hasOwnProperty(nodeId)&&(this.nodes[nodeId].xFixed=!1,this.nodes[nodeId].yFixed=!1)}},manipulationMixin={_clearManipulatorBar:function(){for(;this.manipulationDiv.hasChildNodes();)this.manipulationDiv.removeChild(this.manipulationDiv.firstChild)},_restoreOverloadedFunctions:function(){for(var t in this.cachedFunctions)this.cachedFunctions.hasOwnProperty(t)&&(this[t]=this.cachedFunctions[t])},_toggleEditMode:function(){this.editMode=!this.editMode;var t=document.getElementById("graph-manipulationDiv"),e=document.getElementById("graph-manipulation-closeDiv"),i=document.getElementById("graph-manipulation-editMode");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()},_createManipulatorBar:function(){if(this.boundFunction&&this.off("select",this.boundFunction),this._restoreOverloadedFunctions(),this.freezeSimulation=!1,this.blockConnectingEdgeSelection=!1,this.forceAppendSelection=!1,1==this.editMode){for(;this.manipulationDiv.hasChildNodes();)this.manipulationDiv.removeChild(this.manipulationDiv.firstChild);this.manipulationDiv.innerHTML=""+this.constants.labels.add+""+this.constants.labels.link+"",1==this._getSelectedNodeCount()&&this.triggerFunctions.edit&&(this.manipulationDiv.innerHTML+=""+this.constants.labels.editNode+""),0==this._selectionIsEmpty()&&(this.manipulationDiv.innerHTML+=""+this.constants.labels.del+"");var t=document.getElementById("graph-manipulate-addNode");t.onclick=this._createAddNodeToolbar.bind(this);var e=document.getElementById("graph-manipulate-connectNode");if(e.onclick=this._createAddEdgeToolbar.bind(this),1==this._getSelectedNodeCount()&&this.triggerFunctions.edit){var i=document.getElementById("graph-manipulate-editNode");i.onclick=this._editNode.bind(this)}if(0==this._selectionIsEmpty()){var s=document.getElementById("graph-manipulate-delete");s.onclick=this._deleteSelected.bind(this)}var n=document.getElementById("graph-manipulation-closeDiv");n.onclick=this._toggleEditMode.bind(this),this.boundFunction=this._createManipulatorBar.bind(this),this.on("select",this.boundFunction)}else{this.editModeDiv.innerHTML=""+this.constants.labels.edit+"";var o=document.getElementById("graph-manipulate-editModeButton");o.onclick=this._toggleEditMode.bind(this)}},_createAddNodeToolbar:function(){this._clearManipulatorBar(),this.boundFunction&&this.off("select",this.boundFunction),this.manipulationDiv.innerHTML=""+this.constants.labels.back+" "+this.constants.labels.addDescription+"";var t=document.getElementById("graph-manipulate-back");t.onclick=this._createManipulatorBar.bind(this),this.boundFunction=this._addNode.bind(this),this.on("select",this.boundFunction)},_createAddEdgeToolbar:function(){this._clearManipulatorBar(),this._unselectAll(!0),this.freezeSimulation=!0,this.boundFunction&&this.off("select",this.boundFunction),this._unselectAll(),this.forceAppendSelection=!1,this.blockConnectingEdgeSelection=!0,this.manipulationDiv.innerHTML=""+this.constants.labels.back+" "+this.constants.labels.linkDescription+"";var t=document.getElementById("graph-manipulate-back");t.onclick=this._createManipulatorBar.bind(this),this.boundFunction=this._handleConnect.bind(this),this.on("select",this.boundFunction),this.cachedFunctions._handleTouch=this._handleTouch,this.cachedFunctions._handleOnRelease=this._handleOnRelease,this._handleTouch=this._handleConnect,this._handleOnRelease=this._finishConnect,this._redraw()},_handleConnect:function(t){if(0==this._getSelectedNodeCount()){var e=this._getNodeAt(t);null!=e&&(e.clusterSize>1?alert("Cannot create edges to a cluster."):(this._selectObject(e,!1),this.sectors.support.nodes.targetNode=new Node({id:"targetNode"},{},{},this.constants),this.sectors.support.nodes.targetNode.x=e.x,this.sectors.support.nodes.targetNode.y=e.y,this.sectors.support.nodes.targetViaNode=new Node({id:"targetViaNode"},{},{},this.constants),this.sectors.support.nodes.targetViaNode.x=e.x,this.sectors.support.nodes.targetViaNode.y=e.y,this.sectors.support.nodes.targetViaNode.parentEdgeId="connectionEdge",this.edges.connectionEdge=new Edge({id:"connectionEdge",from:e.id,to:this.sectors.support.nodes.targetNode.id},this,this.constants),this.edges.connectionEdge.from=e,this.edges.connectionEdge.connected=!0,this.edges.connectionEdge.smooth=!0,this.edges.connectionEdge.selected=!0,this.edges.connectionEdge.to=this.sectors.support.nodes.targetNode,this.edges.connectionEdge.via=this.sectors.support.nodes.targetViaNode,this.cachedFunctions._handleOnDrag=this._handleOnDrag,this._handleOnDrag=function(t){var e=this._getPointer(t.gesture.center);this.sectors.support.nodes.targetNode.x=this._XconvertDOMtoCanvas(e.x),this.sectors.support.nodes.targetNode.y=this._YconvertDOMtoCanvas(e.y),this.sectors.support.nodes.targetViaNode.x=.5*(this._XconvertDOMtoCanvas(e.x)+this.edges.connectionEdge.from.x),this.sectors.support.nodes.targetViaNode.y=this._YconvertDOMtoCanvas(e.y)},this.moving=!0,this.start()))}},_finishConnect:function(t){if(1==this._getSelectedNodeCount()){this._handleOnDrag=this.cachedFunctions._handleOnDrag,delete this.cachedFunctions._handleOnDrag;var e=this.edges.connectionEdge.fromId;delete this.edges.connectionEdge,delete this.sectors.support.nodes.targetNode,delete this.sectors.support.nodes.targetViaNode;var i=this._getNodeAt(t);null!=i&&(i.clusterSize>1?alert("Cannot create edges to a cluster."):(this._createEdge(e,i.id),this._createManipulatorBar())),this._unselectAll()}},_addNode:function(){if(this._selectionIsEmpty()&&1==this.editMode){var t=this._pointerToPositionObject(this.pointerPosition),e={id:util.randomUUID(),x:t.left,y:t.top,label:"new",allowedToMoveX:!0,allowedToMoveY:!0};if(this.triggerFunctions.add)if(2==this.triggerFunctions.add.length){var i=this;this.triggerFunctions.add(e,function(t){i.nodesData.add(t),i._createManipulatorBar(),i.moving=!0,i.start()})}else alert(this.constants.labels.addError),this._createManipulatorBar(),this.moving=!0,this.start();else this.nodesData.add(e),this._createManipulatorBar(),this.moving=!0,this.start()}},_createEdge:function(t,e){if(1==this.editMode){var i={from:t,to:e};if(this.triggerFunctions.connect)if(2==this.triggerFunctions.connect.length){var s=this;this.triggerFunctions.connect(i,function(t){s.edgesData.add(t),s.moving=!0,s.start()})}else alert(this.constants.labels.linkError),this.moving=!0,this.start();else this.edgesData.add(i),this.moving=!0,this.start()}},_editNode:function(){if(this.triggerFunctions.edit&&1==this.editMode){var t=this._getSelectedNode(),e={id:t.id,label:t.label,group:t.group,shape:t.shape,color:{background:t.color.background,border:t.color.border,highlight:{background:t.color.highlight.background,border:t.color.highlight.border}}};if(2==this.triggerFunctions.edit.length){var i=this;this.triggerFunctions.edit(e,function(t){i.nodesData.update(t),i._createManipulatorBar(),i.moving=!0,i.start()})}else alert(this.constants.labels.editError)}else alert(this.constants.labels.editBoundError)},_deleteSelected:function(){if(!this._selectionIsEmpty()&&1==this.editMode)if(this._clusterInSelection())alert(this.constants.labels.deleteClusterError);else{var t=this.getSelectedNodes(),e=this.getSelectedEdges();if(this.triggerFunctions.del){var i=this,s={nodes:t,edges:e};(this.triggerFunctions.del.length=2)?this.triggerFunctions.del(s,function(t){i.edgesData.remove(t.edges),i.nodesData.remove(t.nodes),i._unselectAll(),i.moving=!0,i.start()}):alert(this.constants.labels.deleteError)}else this.edgesData.remove(e),this.nodesData.remove(t),this._unselectAll(),this.moving=!0,this.start()}}},SectorMixin={_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},_switchToSector:function(t,e){void 0===e||"active"==e?this._switchToActiveSector(t):this._switchToFrozenSector(t)},_switchToActiveSector:function(t){this.nodeIndices=this.sectors.active[t].nodeIndices,this.nodes=this.sectors.active[t].nodes,this.edges=this.sectors.active[t].edges},_switchToSupportSector:function(){this.nodeIndices=this.sectors.support.nodeIndices,this.nodes=this.sectors.support.nodes,this.edges=this.sectors.support.edges},_switchToFrozenSector:function(t){this.nodeIndices=this.sectors.frozen[t].nodeIndices,this.nodes=this.sectors.frozen[t].nodes,this.edges=this.sectors.frozen[t].edges},_loadLatestSector:function(){this._switchToSector(this._sector())},_sector:function(){return this.activeSector[this.activeSector.length-1]},_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.")},_setActiveSector:function(t){this.activeSector.push(t)},_forgetLastSector:function(){this.activeSector.pop()},_createNewSector:function(t){this.sectors.active[t]={nodes:{},edges:{},nodeIndices:[],formationScale:this.scale,drawingNode:void 0},this.sectors.active[t].drawingNode=new Node({id:t,color:{background:"#eaefef",border:"495c5e"}},{},{},this.constants),this.sectors.active[t].drawingNode.clusterSize=2},_deleteActiveSector:function(t){delete this.sectors.active[t]},_deleteFrozenSector:function(t){delete this.sectors.frozen[t]},_freezeSector:function(t){this.sectors.frozen[t]=this.sectors.active[t],this._deleteActiveSector(t)},_activateSector:function(t){this.sectors.active[t]=this.sectors.frozen[t],this._deleteFrozenSector(t)},_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](s[0],s[1]):this[t](e)}this._loadLatestSector()},_doInSupportSector:function(t,e){if(void 0===e)this._switchToSupportSector(),this[t]();else{this._switchToSupportSector();var i=Array.prototype.splice.call(arguments,1);i.length>1?this[t](i[0],i[1]):this[t](e)}this._loadLatestSector()},_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()},_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))},_clearNodeIndexList:function(){var t=this._sector();this.sectors.active[t].nodeIndices=[],this.nodeIndices=this.sectors.active[t].nodeIndices},_drawSectorNodes:function(t,e){var i,s=1e9,n=-1e9,o=1e9,a=-1e9;for(var r in this.sectors[e])if(this.sectors[e].hasOwnProperty(r)&&void 0!==this.sectors[e][r].drawingNode){this._switchToSector(r,e),s=1e9,n=-1e9,o=1e9,a=-1e9;for(var h in this.nodes)this.nodes.hasOwnProperty(h)&&(i=this.nodes[h],i.resize(t),o>i.x-.5*i.width&&(o=i.x-.5*i.width),ai.y-.5*i.height&&(s=i.y-.5*i.height),nt&&s>n;)n%3==0?(this.forceAggregateHubs(!0),this.normalizeClusterLevels()):this.increaseClusterLevel(),i=this.nodeIndices.length,n+=1;n>0&&1==e&&this.repositionNodes(),this._updateCalculationNodes()},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()},updateClustersDefault:function(){1==this.constants.clustering.enabled&&this.updateClusters(0,!1,!1)},increaseClusterLevel:function(){this.updateClusters(-1,!1,!0)},decreaseClusterLevel:function(){this.updateClusters(1,!1,!0)},updateClusters:function(t,e,i,s){var n=this.moving,o=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)},_aggregateHubs:function(t){this._getHubSize(),this._formClustersByHub(t,!1)},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()},_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)}},_openClusters:function(t,e){for(var i=0;i1&&(t.clusterSizei)){var a=o.from,r=o.to;o.to.mass>o.from.mass&&(a=o.to,r=o.from),1==r.dynamicEdgesLength?this._addToCluster(a,r,!1):1==a.dynamicEdgesLength&&this._addToCluster(r,a,!1)}}},_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.mass>e.mass?this._addToCluster(s,e,!0):this._addToCluster(e,s,!0))}}},_clusterToSmallestNeighbour:function(t){for(var e=-1,i=null,s=0;sn.clusterSessions.length&&(e=n.clusterSessions.length,i=n)}null!=n&&void 0!==this.nodes[n.id]&&this._addToCluster(n,t,!0)},_formClustersByHub:function(t,e){for(var i in this.nodes)this.nodes.hasOwnProperty(i)&&this._formClusterFromHub(this.nodes[i],t,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 n,o,a,r=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 u=this.edges[d[c]];if(void 0!==u&&u.connected&&u.toId!=u.fromId&&(n=u.to.x-u.from.x,o=u.to.y-u.from.y,a=Math.sqrt(n*n+o*o),r>a)){h=!0;break}}if(!e&&h||e)for(c=0;l>c;c++)if(u=this.edges[d[c]],void 0!==u){var p=this.nodes[u.fromId==t.id?u.toId:u.fromId];p.dynamicEdges.length<=this.hubThreshold+s&&p.id!=t.id&&this._addToCluster(t,p,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)))},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 n=this.nodeIndices.length,o=e-this.constants.clustering.clusterLevelDifference;for(t in this.nodes)this.nodes.hasOwnProperty(t)&&this.nodes[t].clusterSessions.lengths&&(s=o.dynamicEdgesLength),t+=o.dynamicEdgesLength,e+=Math.pow(o.dynamicEdgesLength,2),i+=1}t/=i,e/=i;var a=e-Math.pow(t,2),r=Math.sqrt(a);this.hubThreshold=Math.floor(t+2*r),this.hubThreshold>s&&(this.hubThreshold=s)},_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)},_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}},SelectionMixin={_getNodesOverlappingWith:function(t,e){var i=this.nodes;for(var s in i)i.hasOwnProperty(s)&&i[s].isOverlappingWith(t)&&e.push(s)},_getAllNodesOverlappingWith:function(t){var e=[];return this._doInAllActiveSectors("_getNodesOverlappingWith",t,e),e},_pointerToPositionObject:function(t){var e=this._XconvertDOMtoCanvas(t.x),i=this._YconvertDOMtoCanvas(t.y);return{left:e,top:i,right:e,bottom:i}},_getNodeAt:function(t){var e=this._pointerToPositionObject(t),i=this._getAllNodesOverlappingWith(e);return i.length>0?this.nodes[i[i.length-1]]:null},_getEdgesOverlappingWith:function(t,e){var i=this.edges;for(var s in i)i.hasOwnProperty(s)&&i[s].isOverlappingWith(t)&&e.push(s)},_getAllEdgesOverlappingWith:function(t){var e=[];return this._doInAllActiveSectors("_getEdgesOverlappingWith",t,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},_addToSelection:function(t){t instanceof Node?this.selectionObj.nodes[t.id]=t:this.selectionObj.edges[t.id]=t},_addToHover:function(t){t instanceof Node?this.hoverObj.nodes[t.id]=t:this.hoverObj.edges[t.id]=t},_removeFromSelection:function(t){t instanceof Node?delete this.selectionObj.nodes[t.id]:delete this.selectionObj.edges[t.id]},_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())},_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())},_getSelectedNodeCount:function(){var t=0;for(var e in this.selectionObj.nodes)this.selectionObj.nodes.hasOwnProperty(e)&&(t+=1);return t},_getSelectedNode:function(){for(var t in this.selectionObj.nodes)if(this.selectionObj.nodes.hasOwnProperty(t))return this.selectionObj.nodes[t];return null},_getSelectedEdgeCount:function(){var t=0;for(var e in this.selectionObj.edges)this.selectionObj.edges.hasOwnProperty(e)&&(t+=1);return t},_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},_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},_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},_selectConnectedEdges:function(t){for(var e=0;ee;e++){s=t[e];var n=this.nodes[s];if(!n)throw new RangeError('Node with id "'+s+'" not found');this._selectObject(n,!0,!0)}this.redraw()},_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])}},NavigationMixin={_cleanNavigation:function(){var t=document.getElementById("graph-navigation_wrapper");null!=t&&this.containerElement.removeChild(t),document.onmouseup=null},_loadNavigationElements:function(){this._cleanNavigation(),this.navigationDivs={};var t=["up","down","left","right","zoomIn","zoomOut","zoomExtends"],e=["_moveUp","_moveDown","_moveLeft","_moveRight","_zoomIn","_zoomOut","zoomExtent"];this.navigationDivs.wrapper=document.createElement("div"),this.navigationDivs.wrapper.id="graph-navigation_wrapper",this.navigationDivs.wrapper.style.position="absolute",this.navigationDivs.wrapper.style.width=this.frame.canvas.clientWidth+"px",this.navigationDivs.wrapper.style.height=this.frame.canvas.clientHeight+"px",this.containerElement.insertBefore(this.navigationDivs.wrapper,this.frame);for(var i=0;it.x&&(s=t.x),nt.y&&(e=t.y),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 o=Math.min(this.frame.canvas.clientWidth/600,this.frame.canvas.clientHeight/600);i*=o}else{var a=1.1*(Math.abs(s.minX)+Math.abs(s.maxX)),r=1.1*(Math.abs(s.minY)+Math.abs(s.maxY)),h=this.frame.canvas.clientWidth/a,d=this.frame.canvas.clientHeight/r;i=d>=h?h:d}i>1&&(i=1),this._setScale(i),this._centerGraph(s),0==e&&(this.moving=!0,this.start())},Graph.prototype._updateNodeIndexList=function(){this._clearNodeIndexList();for(var t in this.nodes)this.nodes.hasOwnProperty(t)&&this.nodeIndices.push(t)},Graph.prototype.setData=function(t,e){if(void 0===e&&(e=!1),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(this.setOptions(t&&t.options),t&&t.dot){if(t&&t.dot){var i=vis.util.DOTToGraph(t.dot);return void this.setData(i)}}else this._setNodes(t&&t.nodes),this._setEdges(t&&t.edges);if(this._putDataInSector(),!e)if(this.stabilize){var s=this;setTimeout(function(){s._stabilize(),s.start()},0)}else this.start()},Graph.prototype.setOptions=function(t){if(t){var e;if(void 0!==t.width&&(this.width=t.width),void 0!==t.height&&(this.height=t.height),void 0!==t.stabilize&&(this.stabilize=t.stabilize),void 0!==t.selectable&&(this.selectable=t.selectable),void 0!==t.smoothCurves&&(this.constants.smoothCurves=t.smoothCurves),void 0!==t.freezeForStabilization&&(this.constants.freezeForStabilization=t.freezeForStabilization),void 0!==t.configurePhysics&&(this.constants.configurePhysics=t.configurePhysics),void 0!==t.stabilizationIterations&&(this.constants.stabilizationIterations=t.stabilizationIterations),void 0!==t.moveable&&(this.constants.moveable=t.moveable),void 0!==t.zoomable&&(this.constants.zoomable=t.zoomable),void 0!==t.hover&&(this.constants.hover=t.hover),void 0!==t.labels)for(e in t.labels)t.labels.hasOwnProperty(e)&&(this.constants.labels[e]=t.labels[e]);if(t.onAdd&&(this.triggerFunctions.add=t.onAdd),t.onEdit&&(this.triggerFunctions.edit=t.onEdit),t.onConnect&&(this.triggerFunctions.connect=t.onConnect),t.onDelete&&(this.triggerFunctions.del=t.onDelete),t.physics){if(t.physics.barnesHut){this.constants.physics.barnesHut.enabled=!0;for(e in t.physics.barnesHut)t.physics.barnesHut.hasOwnProperty(e)&&(this.constants.physics.barnesHut[e]=t.physics.barnesHut[e])}if(t.physics.repulsion){this.constants.physics.barnesHut.enabled=!1;for(e in t.physics.repulsion)t.physics.repulsion.hasOwnProperty(e)&&(this.constants.physics.repulsion[e]=t.physics.repulsion[e])}if(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.hierarchicalLayout){this.constants.hierarchicalLayout.enabled=!0;for(e in t.hierarchicalLayout)t.hierarchicalLayout.hasOwnProperty(e)&&(this.constants.hierarchicalLayout[e]=t.hierarchicalLayout[e])}else void 0!==t.hierarchicalLayout&&(this.constants.hierarchicalLayout.enabled=!1);if(t.clustering){this.constants.clustering.enabled=!0;for(e in t.clustering)t.clustering.hasOwnProperty(e)&&(this.constants.clustering[e]=t.clustering[e])}else void 0!==t.clustering&&(this.constants.clustering.enabled=!1);if(t.navigation){this.constants.navigation.enabled=!0;for(e in t.navigation)t.navigation.hasOwnProperty(e)&&(this.constants.navigation[e]=t.navigation[e])}else void 0!==t.navigation&&(this.constants.navigation.enabled=!1);if(t.keyboard){this.constants.keyboard.enabled=!0;for(e in t.keyboard)t.keyboard.hasOwnProperty(e)&&(this.constants.keyboard[e]=t.keyboard[e])}else void 0!==t.keyboard&&(this.constants.keyboard.enabled=!1);if(t.dataManipulation){this.constants.dataManipulation.enabled=!0;for(e in t.dataManipulation)t.dataManipulation.hasOwnProperty(e)&&(this.constants.dataManipulation[e]=t.dataManipulation[e])}else void 0!==t.dataManipulation&&(this.constants.dataManipulation.enabled=!1);if(t.edges){for(e in t.edges)t.edges.hasOwnProperty(e)&&"object"!=typeof t.edges[e]&&(this.constants.edges[e]=t.edges[e]);void 0!==t.edges.color&&(util.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))),t.edges.fontColor||void 0!==t.edges.color&&(util.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.edges.dash&&(void 0!==t.edges.dash.length&&(this.constants.edges.dash.length=t.edges.dash.length),void 0!==t.edges.dash.gap&&(this.constants.edges.dash.gap=t.edges.dash.gap),void 0!==t.edges.dash.altLength&&(this.constants.edges.dash.altLength=t.edges.dash.altLength))}if(t.nodes){for(e in t.nodes)t.nodes.hasOwnProperty(e)&&(this.constants.nodes[e]=t.nodes[e]);t.nodes.color&&(this.constants.nodes.color=util.parseColor(t.nodes.color))}if(t.groups)for(var i in t.groups)if(t.groups.hasOwnProperty(i)){var s=t.groups[i];this.groups.add(i,s)}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=util.parseColor(t.tooltip.color))}}this._loadPhysicsSystem(),this._loadNavigationControls(),this._loadManipulationSystem(),this._configureSmoothCurves(),this._createKeyBinds(),this.setSize(this.width,this.height),this.moving=!0,this.start()},Graph.prototype._create=function(){for(;this.containerElement.hasChildNodes();)this.containerElement.removeChild(this.containerElement.firstChild);if(this.frame=document.createElement("div"),this.frame.className="graph-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=document.createElement("DIV");t.style.color="red",t.style.fontWeight="bold",t.style.padding="10px",t.innerHTML="Error: your browser does not support HTML canvas",this.frame.canvas.appendChild(t)}var e=this;this.drag={},this.pinch={},this.hammer=Hammer(this.frame.canvas,{prevent_default:!0}),this.hammer.on("tap",e._onTap.bind(e)),this.hammer.on("doubletap",e._onDoubleTap.bind(e)),this.hammer.on("hold",e._onHold.bind(e)),this.hammer.on("pinch",e._onPinch.bind(e)),this.hammer.on("touch",e._onTouch.bind(e)),this.hammer.on("dragstart",e._onDragStart.bind(e)),this.hammer.on("drag",e._onDrag.bind(e)),this.hammer.on("dragend",e._onDragEnd.bind(e)),this.hammer.on("release",e._onRelease.bind(e)),this.hammer.on("mousewheel",e._onMouseWheel.bind(e)),this.hammer.on("DOMMouseScroll",e._onMouseWheel.bind(e)),this.hammer.on("mousemove",e._onMouseMoveTitle.bind(e)),this.containerElement.appendChild(this.frame)},Graph.prototype._createKeyBinds=function(){var t=this;this.mousetrap=mousetrap,this.mousetrap.reset(),1==this.constants.keyboard.enabled&&(this.mousetrap.bind("up",this._moveUp.bind(t),"keydown"),this.mousetrap.bind("up",this._yStopMoving.bind(t),"keyup"),this.mousetrap.bind("down",this._moveDown.bind(t),"keydown"),this.mousetrap.bind("down",this._yStopMoving.bind(t),"keyup"),this.mousetrap.bind("left",this._moveLeft.bind(t),"keydown"),this.mousetrap.bind("left",this._xStopMoving.bind(t),"keyup"),this.mousetrap.bind("right",this._moveRight.bind(t),"keydown"),this.mousetrap.bind("right",this._xStopMoving.bind(t),"keyup"),this.mousetrap.bind("=",this._zoomIn.bind(t),"keydown"),this.mousetrap.bind("=",this._stopZoom.bind(t),"keyup"),this.mousetrap.bind("-",this._zoomOut.bind(t),"keydown"),this.mousetrap.bind("-",this._stopZoom.bind(t),"keyup"),this.mousetrap.bind("[",this._zoomIn.bind(t),"keydown"),this.mousetrap.bind("[",this._stopZoom.bind(t),"keyup"),this.mousetrap.bind("]",this._zoomOut.bind(t),"keydown"),this.mousetrap.bind("]",this._stopZoom.bind(t),"keyup"),this.mousetrap.bind("pageup",this._zoomIn.bind(t),"keydown"),this.mousetrap.bind("pageup",this._stopZoom.bind(t),"keyup"),this.mousetrap.bind("pagedown",this._zoomOut.bind(t),"keydown"),this.mousetrap.bind("pagedown",this._stopZoom.bind(t),"keyup")),1==this.constants.dataManipulation.enabled&&(this.mousetrap.bind("escape",this._createManipulatorBar.bind(t)),this.mousetrap.bind("del",this._deleteSelected.bind(t)))},Graph.prototype._getPointer=function(t){return{x:t.pageX-vis.util.getAbsoluteLeft(this.frame.canvas),y:t.pageY-vis.util.getAbsoluteTop(this.frame.canvas)}},Graph.prototype._onTouch=function(t){this.drag.pointer=this._getPointer(t.gesture.center),this.drag.pinched=!1,this.pinch.scale=this._getScale(),this._handleTouch(this.drag.pointer)},Graph.prototype._onDragStart=function(){this._handleDragStart()},Graph.prototype._handleDragStart=function(){var t=this.drag,e=this._getNodeAt(t.pointer);if(t.dragging=!0,t.selection=[],t.translation=this._getTranslation(),t.nodeId=null,null!=e){t.nodeId=e.id,e.isSelected()||this._selectObject(e,!1);for(var i in this.selectionObj.nodes)if(this.selectionObj.nodes.hasOwnProperty(i)){var s=this.selectionObj.nodes[i],n={id:s.id,node:s,x:s.x,y:s.y,xFixed:s.xFixed,yFixed:s.yFixed};s.xFixed=!0,s.yFixed=!0,t.selection.push(n)}}},Graph.prototype._onDrag=function(t){this._handleOnDrag(t)},Graph.prototype._handleOnDrag=function(t){if(!this.drag.pinched){var e=this._getPointer(t.gesture.center),i=this,s=this.drag,n=s.selection;if(n&&n.length){var o=e.x-s.pointer.x,a=e.y-s.pointer.y;n.forEach(function(t){var e=t.node;t.xFixed||(e.x=i._XconvertDOMtoCanvas(i._XconvertCanvasToDOM(t.x)+o)),t.yFixed||(e.y=i._YconvertDOMtoCanvas(i._YconvertCanvasToDOM(t.y)+a))}),this.moving||(this.moving=!0,this.start())}else if(1==this.constants.moveable){var r=e.x-this.drag.pointer.x,h=e.y-this.drag.pointer.y;this._setTranslation(this.drag.translation.x+r,this.drag.translation.y+h),this._redraw(),this.moving=!0,this.start()}}},Graph.prototype._onDragEnd=function(){this.drag.dragging=!1;var t=this.drag.selection;t&&t.forEach(function(t){t.node.xFixed=t.xFixed,t.node.yFixed=t.yFixed})},Graph.prototype._onTap=function(t){var e=this._getPointer(t.gesture.center);this.pointerPosition=e,this._handleTap(e)},Graph.prototype._onDoubleTap=function(t){var e=this._getPointer(t.gesture.center);this._handleDoubleTap(e)},Graph.prototype._onHold=function(t){var e=this._getPointer(t.gesture.center);this.pointerPosition=e,this._handleOnHold(e)},Graph.prototype._onRelease=function(t){var e=this._getPointer(t.gesture.center);this._handleOnRelease(e)},Graph.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)},Graph.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=this._getTranslation(),n=t/i,o=(1-n)*e.x+s.x*n,a=(1-n)*e.y+s.y*n;return this.areaCenter={x:this._XconvertDOMtoCanvas(e.x),y:this._YconvertDOMtoCanvas(e.y)},this._setScale(t),this._setTranslation(o,a),this.updateClustersDefault(),this._redraw(),t>i?this.emit("zoom",{direction:"+"}):this.emit("zoom",{direction:"-"}),t}},Graph.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 n=util.fakeGesture(this,t),o=this._getPointer(n.center);this._zoom(i,o)}t.preventDefault()},Graph.prototype._onMouseMoveTitle=function(t){var e=util.fakeGesture(this,t),i=this._getPointer(e.center);this.popupObj&&this._checkHidePopup(i);var s=this,n=function(){s._checkShowPopup(i)};if(this.popupTimer&&clearInterval(this.popupTimer),this.drag.dragging||(this.popupTimer=setTimeout(n,this.constants.tooltip.delay)),1==this.constants.hover){for(var o in this.hoverObj.edges)this.hoverObj.edges.hasOwnProperty(o)&&(this.hoverObj.edges[o].hover=!1,delete this.hoverObj.edges[o]);var a=this._getNodeAt(i);null==a&&(a=this._getEdgeAt(i)),null!=a&&this._hoverObject(a);for(var r in this.hoverObj.nodes)this.hoverObj.nodes.hasOwnProperty(r)&&(a instanceof Node&&a.id!=r||a instanceof Edge||null==a)&&(this._blurObject(this.hoverObj.nodes[r]),delete this.hoverObj.nodes[r]);this.redraw()}},Graph.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;if(void 0==this.popupObj){var n=this.nodes;for(e in n)if(n.hasOwnProperty(e)){var o=n[e];if(void 0!==o.getTitle()&&o.isOverlappingWith(i)){this.popupObj=o;break}}}if(void 0===this.popupObj){var a=this.edges;for(e in a)if(a.hasOwnProperty(e)){var r=a[e];if(r.connected&&void 0!==r.getTitle()&&r.isOverlappingWith(i)){this.popupObj=r;break}}}if(this.popupObj){if(this.popupObj!=s){var h=this;h.popup||(h.popup=new Popup(h.frame,h.constants.tooltip)),h.popup.setPosition(t.x-3,t.y-3),h.popup.setText(h.popupObj.getTitle()),h.popup.show()}}else this.popup&&this.popup.hide()},Graph.prototype._checkHidePopup=function(t){this.popupObj&&this._getNodeAt(t)||(this.popupObj=void 0,this.popup&&this.popup.hide())},Graph.prototype.setSize=function(t,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.frame.canvas.height=this.frame.canvas.clientHeight,void 0!==this.manipulationDiv&&(this.manipulationDiv.style.width=this.frame.canvas.clientWidth+"px"),void 0!==this.navigationDivs&&void 0!==this.navigationDivs.wrapper&&(this.navigationDivs.wrapper.style.width=this.frame.canvas.clientWidth+"px",this.navigationDivs.wrapper.style.height=this.frame.canvas.clientHeight+"px"),this.emit("resize",{width:this.frame.canvas.width,height:this.frame.canvas.height})},Graph.prototype._setNodes=function(t){var e=this.nodesData;if(t instanceof DataSet||t instanceof DataView)this.nodesData=t;else if(t instanceof Array)this.nodesData=new DataSet,this.nodesData.add(t);else{if(t)throw new TypeError("Array or DataSet expected");this.nodesData=new DataSet}if(e&&util.forEach(this.nodesListeners,function(t,i){e.off(i,t)}),this.nodes={},this.nodesData){var i=this;util.forEach(this.nodesListeners,function(t,e){i.nodesData.on(e,t)});var s=this.nodesData.getIds();this._addNodes(s)}this._updateSelection()},Graph.prototype._addNodes=function(t){for(var e,i=0,s=t.length;s>i;i++){e=t[i];var n=this.nodesData.get(e),o=new Node(n,this.images,this.groups,this.constants);if(this.nodes[e]=o,!(0!=o.xFixed&&0!=o.yFixed||null!==o.x&&null!==o.y)){var a=1*t.length,r=2*Math.PI*Math.random();0==o.xFixed&&(o.x=a*Math.cos(r)),0==o.yFixed&&(o.y=a*Math.sin(r))}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()},Graph.prototype._updateNodes=function(t){for(var e=this.nodes,i=this.nodesData,s=0,n=t.length;n>s;s++){var o=t[s],a=e[o],r=i.get(o);a?a.setProperties(r,this.constants):(a=new Node(properties,this.images,this.groups,this.constants),e[o]=a)}this.moving=!0,1==this.constants.hierarchicalLayout.enabled&&0==this.initializing&&(this._resetLevels(),this._setupHierarchicalLayout()),this._updateNodeIndexList(),this._reconnectEdges(),this._updateValueRange(e)},Graph.prototype._removeNodes=function(t){for(var e=this.nodes,i=0,s=t.length;s>i;i++){var n=t[i];delete e[n]}this._updateNodeIndexList(),1==this.constants.hierarchicalLayout.enabled&&0==this.initializing&&(this._resetLevels(),this._setupHierarchicalLayout()),this._updateCalculationNodes(),this._reconnectEdges(),this._updateSelection(),this._updateValueRange(e)},Graph.prototype._setEdges=function(t){var e=this.edgesData;if(t instanceof DataSet||t instanceof DataView)this.edgesData=t;else if(t instanceof Array)this.edgesData=new DataSet,this.edgesData.add(t);else{if(t)throw new TypeError("Array or DataSet expected");this.edgesData=new DataSet}if(e&&util.forEach(this.edgesListeners,function(t,i){e.off(i,t)}),this.edges={},this.edgesData){var i=this;util.forEach(this.edgesListeners,function(t,e){i.edgesData.on(e,t)});var s=this.edgesData.getIds();this._addEdges(s)}this._reconnectEdges()},Graph.prototype._addEdges=function(t){for(var e=this.edges,i=this.edgesData,s=0,n=t.length;n>s;s++){var o=t[s],a=e[o];a&&a.disconnect();var r=i.get(o,{showInternalIds:!0});e[o]=new Edge(r,this,this.constants)}this.moving=!0,this._updateValueRange(e),this._createBezierNodes(),1==this.constants.hierarchicalLayout.enabled&&0==this.initializing&&(this._resetLevels(),this._setupHierarchicalLayout()),this._updateCalculationNodes()},Graph.prototype._updateEdges=function(t){for(var e=this.edges,i=this.edgesData,s=0,n=t.length;n>s;s++){var o=t[s],a=i.get(o),r=e[o];r?(r.disconnect(),r.setProperties(a,this.constants),r.connect()):(r=new Edge(a,this,this.constants),this.edges[o]=r)}this._createBezierNodes(),1==this.constants.hierarchicalLayout.enabled&&0==this.initializing&&(this._resetLevels(),this._setupHierarchicalLayout()),this.moving=!0,this._updateValueRange(e)},Graph.prototype._removeEdges=function(t){for(var e=this.edges,i=0,s=t.length;s>i;i++){var n=t[i],o=e[n];o&&(null!=o.via&&delete this.sectors.support.nodes[o.via.id],o.disconnect(),delete e[n])}this.moving=!0,this._updateValueRange(e),1==this.constants.hierarchicalLayout.enabled&&0==this.initializing&&(this._resetLevels(),this._setupHierarchicalLayout()),this._updateCalculationNodes()},Graph.prototype._reconnectEdges=function(){var t,e=this.nodes,i=this.edges;for(t in e)e.hasOwnProperty(t)&&(e[t].edges=[]);for(t in i)if(i.hasOwnProperty(t)){var s=i[t];s.from=null,s.to=null,s.connect()}},Graph.prototype._updateValueRange=function(t){var e,i=void 0,s=void 0;for(e in t)if(t.hasOwnProperty(e)){var n=t[e].getValue();void 0!==n&&(i=void 0===i?n:Math.min(n,i),s=void 0===s?n:Math.max(n,s))}if(void 0!==i&&void 0!==s)for(e in t)t.hasOwnProperty(e)&&t[e].setValueRange(i,s)},Graph.prototype.redraw=function(){this.setSize(this.width,this.height),this._redraw()},Graph.prototype._redraw=function(){var t=this.frame.canvas.getContext("2d"),e=this.frame.canvas.width,i=this.frame.canvas.height;t.clearRect(0,0,e,i),t.save(),t.translate(this.translation.x,this.translation.y),t.scale(this.scale,this.scale),this.canvasTopLeft={x:this._XconvertDOMtoCanvas(0),y:this._YconvertDOMtoCanvas(0)},this.canvasBottomRight={x:this._XconvertDOMtoCanvas(this.frame.canvas.clientWidth),y:this._YconvertDOMtoCanvas(this.frame.canvas.clientHeight)},this._doInAllSectors("_drawAllSectorNodes",t),this._doInAllSectors("_drawEdges",t),this._doInAllSectors("_drawNodes",t,!1),t.restore()
-},Graph.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")},Graph.prototype._getTranslation=function(){return{x:this.translation.x,y:this.translation.y}},Graph.prototype._setScale=function(t){this.scale=t},Graph.prototype._getScale=function(){return this.scale},Graph.prototype._XconvertDOMtoCanvas=function(t){return(t-this.translation.x)/this.scale},Graph.prototype._XconvertCanvasToDOM=function(t){return t*this.scale+this.translation.x},Graph.prototype._YconvertDOMtoCanvas=function(t){return(t-this.translation.y)/this.scale},Graph.prototype._YconvertCanvasToDOM=function(t){return t*this.scale+this.translation.y},Graph.prototype.canvasToDOM=function(t){return{x:this._XconvertCanvasToDOM(t.x),y:this._YconvertCanvasToDOM(t.y)}},Graph.prototype.DOMtoCanvas=function(t){return{x:this._XconvertDOMtoCanvas(t.x),y:this._YconvertDOMtoCanvas(t.y)}},Graph.prototype._drawNodes=function(t,e){void 0===e&&(e=!1);var i=this.nodes,s=[];for(var n in i)i.hasOwnProperty(n)&&(i[n].setScaleAndPos(this.scale,this.canvasTopLeft,this.canvasBottomRight),i[n].isSelected()?s.push(n):(i[n].inArea()||e)&&i[n].draw(t));for(var o=0,a=s.length;a>o;o++)(i[s[o]].inArea()||e)&&i[s[o]].draw(t)},Graph.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)}},Graph.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 n=this.constants.minVelocity/Math.max(this.scale,.05);this.moving=n>.5*this.constants.maxVelocity?!0:this._isMoving(n)}},Graph.prototype._physicsTick=function(){this.freezeSimulation||this.moving&&(this._doInAllActiveSectors("_initializeForceCalculation"),this._doInAllActiveSectors("_discreteStepNodes"),this.constants.smoothCurves&&this._doInSupportSector("_discreteStepNodes"),this._findCenter(this._getRange()))},Graph.prototype._animationStep=function(){this.timer=void 0,this._handleNavigation(),this.start();var t=Date.now(),e=1;this._physicsTick();for(var i=Date.now()-t;i.5*Math.PI&&(this.armRotation.vertical=.5*Math.PI)),(void 0!==t||void 0!==e)&&this.calculateCameraOrientation()},Graph3d.Camera.prototype.getArmRotation=function(){var t={};return t.horizontal=this.armRotation.horizontal,t.vertical=this.armRotation.vertical,t},Graph3d.Camera.prototype.setArmLength=function(t){void 0!==t&&(this.armLength=t,this.armLength<.71&&(this.armLength=.71),this.armLength>5&&(this.armLength=5),this.calculateCameraOrientation())},Graph3d.Camera.prototype.getArmLength=function(){return this.armLength},Graph3d.Camera.prototype.getCameraLocation=function(){return this.cameraLocation},Graph3d.Camera.prototype.getCameraRotation=function(){return this.cameraRotation},Graph3d.Camera.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},Graph3d.prototype._setScale=function(){this.scale=new Point3d(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!==Graph3d.STYLE.DOTCOLOR&&this.style!==Graph3d.STYLE.DOTSIZE&&this.style!==Graph3d.STYLE.BARCOLOR&&this.style!==Graph3d.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)}},Graph3d.prototype.getNumberOfRows=function(t){return t.length},Graph3d.prototype.getNumberOfColumns=function(t){var e=0;for(var i in t[0])t[0].hasOwnProperty(i)&&e++;return e},Graph3d.prototype.getDistinctValues=function(t,e){for(var i=[],s=0;st[s][e]&&(i.min=t[s][e]),i.maxt;t++){var p=(t-c)/(u-c),m=240*p,g=this._hsv2rgb(m,1,1);l.strokeStyle=g,l.beginPath(),l.moveTo(r,o+t),l.lineTo(a,o+t),l.stroke()}l.strokeStyle=this.colorAxis,l.strokeRect(r,o,i,n)}if(this.style===Graph3d.STYLE.DOTSIZE&&(l.strokeStyle=this.colorAxis,l.fillStyle=this.colorDot,l.beginPath(),l.moveTo(r,o),l.lineTo(a,o),l.lineTo(a-i+e,h),l.lineTo(r,h),l.closePath(),l.fill(),l.stroke()),this.style===Graph3d.STYLE.DOTCOLOR||this.style===Graph3d.STYLE.DOTSIZE){var f=5,v=new StepNumber(this.valueMin,this.valueMax,(this.valueMax-this.valueMin)/5,!0);for(v.start(),v.getCurrent()0?this.yMin:this.yMax,n=this._convert3Dto2D(new Point3d(_,a,this.zMin)),Math.cos(2*y)>0?(m.textAlign="center",m.textBaseline="top",n.y+=v):Math.sin(2*y)<0?(m.textAlign="right",m.textBaseline="middle"):(m.textAlign="left",m.textBaseline="middle"),m.fillStyle=this.colorAxis,m.fillText(" "+i.getCurrent()+" ",n.x,n.y),i.next()}for(m.lineWidth=1,s=void 0===this.defaultYStep,i=new StepNumber(this.yMin,this.yMax,this.yStep,s),i.start(),i.getCurrent()0?this.xMin:this.xMax,n=this._convert3Dto2D(new Point3d(o,i.getCurrent(),this.zMin)),Math.cos(2*y)<0?(m.textAlign="center",m.textBaseline="top",n.y+=v):Math.sin(2*y)>0?(m.textAlign="right",m.textBaseline="middle"):(m.textAlign="left",m.textBaseline="middle"),m.fillStyle=this.colorAxis,m.fillText(" "+i.getCurrent()+" ",n.x,n.y),i.next();for(m.lineWidth=1,s=void 0===this.defaultZStep,i=new StepNumber(this.zMin,this.zMax,this.zStep,s),i.start(),i.getCurrent()0?this.xMin:this.xMax,a=Math.sin(y)<0?this.yMin:this.yMax;!i.end();)t=this._convert3Dto2D(new Point3d(o,a,i.getCurrent())),m.strokeStyle=this.colorAxis,m.beginPath(),m.moveTo(t.x,t.y),m.lineTo(t.x-v,t.y),m.stroke(),m.textAlign="right",m.textBaseline="middle",m.fillStyle=this.colorAxis,m.fillText(i.getCurrent()+" ",t.x-5,t.y),i.next();m.lineWidth=1,t=this._convert3Dto2D(new Point3d(o,a,this.zMin)),e=this._convert3Dto2D(new Point3d(o,a,this.zMax)),m.strokeStyle=this.colorAxis,m.beginPath(),m.moveTo(t.x,t.y),m.lineTo(e.x,e.y),m.stroke(),m.lineWidth=1,c=this._convert3Dto2D(new Point3d(this.xMin,this.yMin,this.zMin)),u=this._convert3Dto2D(new Point3d(this.xMax,this.yMin,this.zMin)),m.strokeStyle=this.colorAxis,m.beginPath(),m.moveTo(c.x,c.y),m.lineTo(u.x,u.y),m.stroke(),c=this._convert3Dto2D(new Point3d(this.xMin,this.yMax,this.zMin)),u=this._convert3Dto2D(new Point3d(this.xMax,this.yMax,this.zMin)),m.strokeStyle=this.colorAxis,m.beginPath(),m.moveTo(c.x,c.y),m.lineTo(u.x,u.y),m.stroke(),m.lineWidth=1,t=this._convert3Dto2D(new Point3d(this.xMin,this.yMin,this.zMin)),e=this._convert3Dto2D(new Point3d(this.xMin,this.yMax,this.zMin)),m.strokeStyle=this.colorAxis,m.beginPath(),m.moveTo(t.x,t.y),m.lineTo(e.x,e.y),m.stroke(),t=this._convert3Dto2D(new Point3d(this.xMax,this.yMin,this.zMin)),e=this._convert3Dto2D(new Point3d(this.xMax,this.yMax,this.zMin)),m.strokeStyle=this.colorAxis,m.beginPath(),m.moveTo(t.x,t.y),m.lineTo(e.x,e.y),m.stroke();var b=this.xLabel;b.length>0&&(l=.1/this.scale.y,o=(this.xMin+this.xMax)/2,a=Math.cos(y)>0?this.yMin-l:this.yMax+l,n=this._convert3Dto2D(new Point3d(o,a,this.zMin)),Math.cos(2*y)>0?(m.textAlign="center",m.textBaseline="top"):Math.sin(2*y)<0?(m.textAlign="right",m.textBaseline="middle"):(m.textAlign="left",m.textBaseline="middle"),m.fillStyle=this.colorAxis,m.fillText(b,n.x,n.y));var x=this.yLabel;x.length>0&&(d=.1/this.scale.x,o=Math.sin(y)>0?this.xMin-d:this.xMax+d,a=(this.yMin+this.yMax)/2,n=this._convert3Dto2D(new Point3d(o,a,this.zMin)),Math.cos(2*y)<0?(m.textAlign="center",m.textBaseline="top"):Math.sin(2*y)>0?(m.textAlign="right",m.textBaseline="middle"):(m.textAlign="left",m.textBaseline="middle"),m.fillStyle=this.colorAxis,m.fillText(x,n.x,n.y));var w=this.zLabel;w.length>0&&(h=30,o=Math.cos(y)>0?this.xMin:this.xMax,a=Math.sin(y)<0?this.yMin:this.yMax,r=(this.zMin+this.zMax)/2,n=this._convert3Dto2D(new Point3d(o,a,r)),m.textAlign="right",m.textBaseline="middle",m.fillStyle=this.colorAxis,m.fillText(w,n.x-h,n.y))},Graph3d.prototype._hsv2rgb=function(t,e,i){var s,n,o,a,r,h;switch(a=i*e,r=Math.floor(t/60),h=a*(1-Math.abs(t/60%2-1)),r){case 0:s=a,n=h,o=0;break;case 1:s=h,n=a,o=0;break;case 2:s=0,n=a,o=h;break;case 3:s=0,n=h,o=a;break;case 4:s=h,n=0,o=a;break;case 5:s=a,n=0,o=h;break;default:s=0,n=0,o=0}return"RGB("+parseInt(255*s)+","+parseInt(255*n)+","+parseInt(255*o)+")"},Graph3d.prototype._redrawDataGrid=function(){var t,e,i,s,n,o,a,r,h,d,l,c,u,p=this.frame.canvas,m=p.getContext("2d");if(!(void 0===this.dataPoints||this.dataPoints.length<=0)){for(n=0;n0}else o=!0;o?(u=(t.point.z+e.point.z+i.point.z+s.point.z)/4,d=240*(1-(u-this.zMin)*this.scale.z/this.verticalRatio),l=1,this.showShadow?(c=Math.min(1+x.x/w/2,1),a=this._hsv2rgb(d,l,c),r=a):(c=1,a=this._hsv2rgb(d,l,c),r=this.colorAxis)):(a="gray",r=this.colorAxis),h=.5,m.lineWidth=h,m.fillStyle=a,m.strokeStyle=r,m.beginPath(),m.moveTo(t.screen.x,t.screen.y),m.lineTo(e.screen.x,e.screen.y),m.lineTo(s.screen.x,s.screen.y),m.lineTo(i.screen.x,i.screen.y),m.closePath(),m.fill(),m.stroke()}}else for(n=0;nc&&(c=0);var u,p,m;this.style===Graph3d.STYLE.DOTCOLOR?(u=240*(1-(h.point.value-this.valueMin)*this.scale.value),p=this._hsv2rgb(u,1,1),m=this._hsv2rgb(u,1,.8)):this.style===Graph3d.STYLE.DOTSIZE?(p=this.colorDot,m=this.colorDotBorder):(u=240*(1-(h.point.z-this.zMin)*this.scale.z/this.verticalRatio),p=this._hsv2rgb(u,1,1),m=this._hsv2rgb(u,1,.8)),i.lineWidth=1,i.strokeStyle=m,i.fillStyle=p,i.beginPath(),i.arc(h.screen.x,h.screen.y,c,0,2*Math.PI,!0),i.fill(),i.stroke()}}},Graph3d.prototype._redrawDataBar=function(){var t,e,i,s,n=this.frame.canvas,o=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()}},Graph3d.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=getMouseX(t),this.startMouseY=getMouseY(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)},G3DaddEventListener(document,"mousemove",e.onmousemove),G3DaddEventListener(document,"mouseup",e.onmouseup),G3DpreventDefault(t)}},Graph3d.prototype._onMouseMove=function(t){t=t||window.event;var e=parseFloat(getMouseX(t))-this.startMouseX,i=parseFloat(getMouseY(t))-this.startMouseY,s=this.startArmRotation.horizontal+e/200,n=this.startArmRotation.vertical+i/200,o=4,a=Math.sin(o/360*2*Math.PI);Math.abs(Math.sin(s))0?1:0>t?-1:0}var s=e[0],n=e[1],o=e[2],a=i((n.x-s.x)*(t.y-s.y)-(n.y-s.y)*(t.x-s.x)),r=i((o.x-n.x)*(t.y-n.y)-(o.y-n.y)*(t.x-n.x)),h=i((s.x-o.x)*(t.y-o.y)-(s.y-o.y)*(t.x-o.x));return!(0!=a&&0!=r&&a!=r||0!=r&&0!=h&&r!=h||0!=a&&0!=h&&a!=h)},Graph3d.prototype._dataPointFromXY=function(t,e){var i,s=100,n=null,o=null,a=null,r=new Point2d(t,e);if(this.style===Graph3d.STYLE.BAR||this.style===Graph3d.STYLE.BARCOLOR||this.style===Graph3d.STYLE.BARSIZE)for(i=this.dataPoints.length-1;i>=0;i--){n=this.dataPoints[i];var h=n.surfaces;if(h)for(var d=h.length-1;d>=0;d--){var l=h[d],c=l.corners,u=[c[0].screen,c[1].screen,c[2].screen],p=[c[2].screen,c[3].screen,c[0].screen];if(this._insideTriangle(r,u)||this._insideTriangle(r,p))return n}}else for(i=0;iv)&&s>v&&(a=v,o=n)}}return o},Graph3d.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):"
An object containing field names as key, and data types as value.
@@ -700,7 +700,7 @@ data.add([
// retrieve formatted items
var items = data.get({
fields: ['id', 'date', 'group'], // output the specified fields only
- convert: {
+ type: {
date: 'Date', // convert the date fields to Date objects
group: 'String' // convert the group fields to Strings
}
diff --git a/docs/graph.html b/docs/graph.html
index 20e96ed2..23d49b6a 100644
--- a/docs/graph.html
+++ b/docs/graph.html
@@ -1593,6 +1593,16 @@ var options: {
// all fields normally accepted by a node can be used.
callback(newData); // call the callback with the new data to edit the node.
}
+ onEditEdge: function(data,callback) {
+ /** data = {id: edgeID,
+ * from: nodeId1,
+ * to: nodeId2,
+ * };
+ */
+ var newData = {..}; // alter the data as you want, except for the ID.
+ // all fields normally accepted by an edge can be used.
+ callback(newData); // call the callback with the new data to edit the edge.
+ }
onConnect: function(data,callback) {
// data = {from: nodeId1, to: nodeId2};
var newData = {..}; // check or alter data as you see fit.
@@ -1951,10 +1961,12 @@ var options: {
link:"Add Link",
del:"Delete selected",
editNode:"Edit Node",
+ editEdge:"Edit Edge",
back:"Back",
addDescription:"Click in an empty space to place a new node.",
linkDescription:"Click on a node and drag the edge to another
node to connect them.",
+ editEdgeDescription:"Click on either one of the control points and drag them to another node to connect to it.".
addError:"The function for add does not support two arguments
(data,callback).",
linkError:"The function for connect does not support two arguments
diff --git a/docs/img/vis_overview.odg b/docs/img/vis_overview.odg
index 0be797e9..ec94baea 100644
Binary files a/docs/img/vis_overview.odg and b/docs/img/vis_overview.odg differ
diff --git a/docs/img/vis_overview.png b/docs/img/vis_overview.png
index d2de2359..588d5840 100644
Binary files a/docs/img/vis_overview.png and b/docs/img/vis_overview.png differ
diff --git a/docs/index.html b/docs/index.html
index 48eff665..1d6e3bd8 100644
--- a/docs/index.html
+++ b/docs/index.html
@@ -34,6 +34,13 @@
Vis.js contains of the following components:
+
@@ -163,16 +162,23 @@ var timeline = new vis.Timeline(container, data, options);
<div id="visualization"></div>
<script type="text/javascript">
+ // DOM element where the Timeline will be attached
var container = document.getElementById('visualization');
- var data = [
+
+ // Create a DataSet (allows two way data-binding)
+ var data = new vis.DataSet([
{id: 1, content: 'item 1', start: '2013-04-20'},
{id: 2, content: 'item 2', start: '2013-04-14'},
{id: 3, content: 'item 3', start: '2013-04-18'},
{id: 4, content: 'item 4', start: '2013-04-16', end: '2013-04-19'},
{id: 5, content: 'item 5', start: '2013-04-25'},
{id: 6, content: 'item 6', start: '2013-04-27'}
- ];
+ ]);
+
+ // Configuration for the Timeline
var options = {};
+
+ // Create a Timeline
var timeline = new vis.Timeline(container, data, options);
</script>
</body>
diff --git a/docs/timeline.html b/docs/timeline.html
index 179d11a1..6907887d 100644
--- a/docs/timeline.html
+++ b/docs/timeline.html
@@ -68,16 +68,23 @@
<div id="visualization"></div>
<script type="text/javascript">
+ // DOM element where the Timeline will be attached
var container = document.getElementById('visualization');
- var items = [
+
+ // Create a DataSet (allows two way data-binding)
+ var items = new vis.DataSet([
{id: 1, content: 'item 1', start: '2013-04-20'},
{id: 2, content: 'item 2', start: '2013-04-14'},
{id: 3, content: 'item 3', start: '2013-04-18'},
{id: 4, content: 'item 4', start: '2013-04-16', end: '2013-04-19'},
{id: 5, content: 'item 5', start: '2013-04-25'},
{id: 6, content: 'item 6', start: '2013-04-27'}
- ];
+ ]);
+
+ // Configuration for the Timeline
var options = {};
+
+ // Create a Timeline
var timeline = new vis.Timeline(container, items, options);
</script>
</body>
@@ -458,6 +465,16 @@ var options = {
Specifies the minimum height for the Timeline. Can be a number in pixels or a string like "300px".
+
+
moveable
+
Boolean
+
true
+
+ Specifies whether the Timeline can be moved and zoomed by dragging the window.
+ See also option zoomable.
+
+
+
onAdd
Function
@@ -533,7 +550,7 @@ var options = {
showCurrentTime
boolean
-
false
+
true
Show a vertical bar at the current time.
@@ -599,6 +616,16 @@ var options = {
The width of the timeline in pixels or as a percentage.
+
+
zoomable
+
Boolean
+
true
+
+ Specifies whether the Timeline can be zoomed by pinching or scrolling in the window.
+ Only applicable when option moveable is set true.
+
+
+
zoomMax
Number
@@ -646,6 +673,13 @@ timeline.clear({options: true}); // clear options only
+
+
destroy()
+
none
+
Destroy the Timeline. The timeline is removed from memory. all DOM elements and event listeners are cleaned up.
+
+
+
fit()
none
@@ -895,7 +929,7 @@ var options = {
item: the item being manipulated
-
callback: a callback function which must be invoked to report back. The callback must be invoked as callback(item | null). Here, item can contain changes to the passed item. When invoked as callback(null), the action will be cancelled.
+
callback: a callback function which must be invoked to report back. The callback must be invoked as callback(item | null). Here, item can contain changes to the passed item. Parameter `item` typically contains fields `content`, `start`, and optionally `end`. The type of `start` and `end` is determined by the DataSet type configuration and is `Date` by default. When invoked as callback(null), the action will be cancelled.
diff --git a/download/vis.zip b/download/vis.zip
index 7bedaba2..f260afb7 100644
Binary files a/download/vis.zip and b/download/vis.zip differ
diff --git a/examples/timeline/01_basic.html b/examples/timeline/01_basic.html
index 5dc030e0..289555f7 100644
--- a/examples/timeline/01_basic.html
+++ b/examples/timeline/01_basic.html
@@ -16,16 +16,23 @@
+
+
Serialization and deserialization
+
+
This example shows how to serialize and deserialize JSON data, and load this in the Timeline via a DataSet. Serialization and deserialization is needed when loading or saving data from a server.